text:
var should = require('should');
var sinon = require('sinon');
var _ = require('lodash');
var path = require('path');

var replayerUtil = require('../src/util');
var fs = require('fs');

describe('utils.js', function() {
  beforeEach(function() {
    replayerUtil.reset();
  });

  describe('#addSubstitution', function() {
    const addSubstitution = replayerUtil.addSubstitution;

    it('should store a list of substitutions', function() {
      var valfn1 = function() { return 'realvalue1'; };
      var valfn2 = function() {return 'realvalue2'; };
      addSubstitution('<OPAQUE1>', valfn1);
      addSubstitution('<OPAQUE2>', valfn2);

      var substitutions = replayerUtil.internal.globalOptions.substitutions;
      substitutions[0].opaqueKey.should.equal('<OPAQUE1>');
      substitutions[0].actualValueFn().should.equal('realvalue1');
      substitutions[1].opaqueKey.should.equal('<OPAQUE2>');
      substitutions[1].actualValueFn().should.equal('realvalue2');
    });
  });

  describe('#substituteWithOpaqueKeys', function() {
    const substituteWithOpaqueKeys = replayerUtil.substituteWithOpaqueKeys;
    const addSubstitution = replayerUtil.addSubstitution;

    it('should substitute real values with opaque keys', function () {
      var valfn1 = function() {return 'realvalue1'; };
      var valfn2 = function() {return 'realvalue2'; };
      addSubstitution('<OPAQUE1>', valfn1);
      addSubstitution('<OPAQUE2>', valfn2);

      var text = substituteWithOpaqueKeys('A:<OPAQUE1>:B:<OPAQUE2>:C:realvalue1:D:realvalue2:E:realvalue1:F:realvalue2');
      text.should.equal('A:<OPAQUE1>:B:<OPAQUE2>:C:<OPAQUE1>:D:<OPAQUE2>:E:<OPAQUE1>:F:<OPAQUE2>');
    });
  });

  describe('#substituteWithRealValues', function() {
    const substituteWithRealValues= replayerUtil.substituteWithRealValues;
    const addSubstitution = replayerUtil.addSubstitution;

    it('should substitute opaque keys with real values', function () {
      var valfn1 = function() {return 'realvalue1'; };
      var valfn2 = function() {return 'realvalue2'; };
      addSubstitution('<OPAQUE1>', valfn1);
      addSubstitution('<OPAQUE2>', valfn2);

      var text = substituteWithRealValues('A:<OPAQUE1>:B:<OPAQUE2>:C:realvalue1:D:realvalue2:E:<OPAQUE1>:F:<OPAQUE2>');
      text.should.equal('A:realvalue1:B:realvalue2:C:realvalue1:D:realvalue2:E:realvalue1:F:realvalue2');
    });
  });

  describe('#addFilter', function() {
    const addFilter = replayerUtil.addFilter;

    it('retains all recognized properties', function() {
      addFilter({
        url: /my-regex/,
        urlFilter: function(url) {
          return 'url-filtered: ' + url;
        },
        bodyFilter: function(body) {
          return 'body-filtered: ' + body;
        },
        forceLive: true,
        global: true
      });

      var filters = replayerUtil.internal.globalOptions.filenameFilters;
      filters[0].url.source.should.equal('my-regex');
      filters[0].forceLive.should.equal(true);
      filters[0].global.should.equal(true);
      filters[0].urlFilter('my-url').should.equal('url-filtered: my-url');
      filters[0].bodyFilter('my-body').should.equal('body-filtered: my-body');
    });

    it('discards unknown properties', function() {
      addFilter({
        url: /filter-with-invalid-property/,
        invalid: 'invalid-property'
      });

      var filters = replayerUtil.internal.globalOptions.filenameFilters;
      filters[0].url.source.should.equal('filter-with-invalid-property');
      filters[0].should.have.keys([
        'url',
        'urlFilter',
        'bodyFilter',
        'forceLive',
        'global'
      ]);
    });

    it('fills in defaults for missing properties', function() {
      addFilter({});

      var filters = replayerUtil.internal.globalOptions.filenameFilters;
      filters[0].url.source.should.equal('.*');
      filters[0].forceLive.should.equal(false);
      filters[0].global.should.equal(false);
      filters[0].urlFilter('my-url').should.equal('my-url');
      filters[0].bodyFilter('my-body').should.equal('my-body');
    });
  });

  describe('#mkdirpSync', function() {
    const mkdirpSync = replayerUtil.internal.mkdirpSync;

    beforeEach(function() {
      sinon.stub(fs, 'mkdirSync'); // mkdirSync becomes a noop
    });

    afterEach(function() {
      fs.mkdirSync.restore();
      fs.existsSync.restore();
    });

    it('creates each of the non-existent parent directories', function() {
      sinon.stub(fs, 'existsSync');
      fs.existsSync.withArgs('a/b/c/d').returns(false);
      fs.existsSync.withArgs('a/b/c').returns(false);
      fs.existsSync.withArgs('a/b').returns(true);
      fs.existsSync.withArgs('a').returns(true);

      mkdirpSync('a/b/c/d');
      fs.mkdirSync.callCount.should.equal(2);
      fs.mkdirSync.args[0][0].should.equal('a/b/c');
      fs.mkdirSync.args[1][0].should.equal('a/b/c/d');
    });
  });

  describe('#filterByWhitelist', function() {
    const filterByWhitelist = replayerUtil.internal.filterByWhitelist;
    const original = ['a', 'b', 'c'];

    it('does not filter if the whitelist is empty', function() {
      var filtered = filterByWhitelist(original, []);
      filtered.should.eql(original);
    });

    it('only retains elements present in the whitelist', function() {
      var filtered = filterByWhitelist(original, ['a', 'c', 'd']);
      filtered.should.eql(['a', 'c']);
    });
  });

  describe('#removeInternalHeaders', function() {
    const removeInternalHeaders = replayerUtil.removeInternalHeaders;

    it('returns undefined if the input is undefined', function() {
      var filtered = removeInternalHeaders();
      should.not.exist(filtered);
    });

    it('filters out only internal headers', function() {
      var filtered = removeInternalHeaders({
        a: 1,
        'x-replayer-internal': 2,
        b: 3
      });

      filtered.should.eql({
        a: 1,
        b: 3
      });
    });

    it('does not modify the original object', function() {
      var original = {
        a: 1,
        'x-replayer-internal': 2,
        b: 3
      };

      var input = _.cloneDeep(original);

      removeInternalHeaders(input);
      input.should.eql(original);
    });
  });

  describe('#usesGlobalFixtures', function() {
    const usesGlobalFixtures = replayerUtil.internal.usesGlobalFixtures;

    it('returns false if there are no filters', function() {
      usesGlobalFixtures('my-url').should.equal(false);
    });

    it('returns false if there are no matching filters', function() {
      replayerUtil.addFilter({
        url: /other-url/,
        global: true
      });

      replayerUtil.addFilter({
        url: /another-url/,
        global: true
      });

      usesGlobalFixtures('my-url').should.equal(false);
    });

    it('returns false if no matching filters specify global', function() {
      replayerUtil.addFilter({ url: /my/ });
      replayerUtil.addFilter({ url: /url/ });

      usesGlobalFixtures('my-url').should.equal(false);
    });

    it('returns true if any matching filter specifies global', function() {
      replayerUtil.addFilter({
        url: /my/,
        global: false
      });

      replayerUtil.addFilter({
        url: /url/,
        global: true
      });

      replayerUtil.addFilter({
        url: /my-url/,
        global: false
      });

      usesGlobalFixtures('my-url').should.equal(true);
    });
  });

  describe('#applyMatchingFilters', function() {
    const applyMatchingFilters = replayerUtil.internal.applyMatchingFilters;

    function addTestFilter(pattern) {
      replayerUtil.addFilter({
        url: new RegExp(pattern),
        urlFilter: function(url) {
          return pattern + '-url-filter: ' + url;
        },
        bodyFilter: function(body) {
          return pattern + '-body-filter: ' + body;
        }
      });
    }

    it('applies only filters that match', function() {
      addTestFilter('a');
      addTestFilter('b');

      var filtered = applyMatchingFilters('a', 'my-body');
      filtered.should.eql({
        filteredUrl: 'a-url-filter: a',
        filteredBody: 'a-body-filter: my-body'
      });
    });

    it('applies all matching the filters in order', function() {
      addTestFilter('d');
      addTestFilter('c');
      addTestFilter('b');
      addTestFilter('a');

      var filtered = applyMatchingFilters('abd', 'my-body');
      filtered.should.eql({
        filteredUrl: 'a-url-filter: b-url-filter: d-url-filter: abd',
        filteredBody: 'a-body-filter: b-body-filter: d-body-filter: my-body'
      });
    });

    it('applies multiple filters only if the original url matches',
      function() {
        replayerUtil.addFilter({
          url: /a/,
          urlFilter: function() { return 'b'; }
        });

        replayerUtil.addFilter({
          url: /b/,
          urlFilter: function() { return 'c'; }
        });

        var filtered = applyMatchingFilters('a', 'my-body');
        filtered.should.eql({
          filteredUrl: 'b',
          filteredBody: 'my-body'
        });
      });

    it('returns unfiltered values if there are no filters', function() {
      var filtered = applyMatchingFilters('my-url', 'my-body');
      filtered.should.eql({
        filteredUrl: 'my-url',
        filteredBody: 'my-body'
      });
    });
  });

  describe('#touchOnHit', function() {
    const touchOnHit = replayerUtil.internal.touchOnHit;

    beforeEach(function() {
      sinon.stub(Date, 'now').returns(10000);
      sinon.stub(fs, 'utimesSync'); // utimesSync becomes a noop
    });

    afterEach(function() {
      Date.now.restore();
      fs.utimesSync.restore();
      fs.existsSync.restore();
    });

    it('modifies the timestamp of the fixture file if it exists', function() {
      sinon.stub(fs, 'existsSync').returns(false);
      fs.existsSync.withArgs('my-filename.headers').returns(true);

      touchOnHit('my-filename');
      fs.utimesSync.calledWithExactly('my-filename.headers', 10, 10)
        .should.equal(true);
    });

    it('does nothing if the fixture file does not exist', function() {
      sinon.stub(fs, 'existsSync').returns(true);
      fs.existsSync.withArgs('my-filename.headers').returns(false);

      touchOnHit('my-filename');
      fs.utimesSync.called.should.equal(false);
    });

    it('does nothing if not configured touch on hits', function() {
      replayerUtil.configure({ touchHits: false });
      sinon.stub(fs, 'existsSync').returns(true);

      touchOnHit('my-filename');
      fs.existsSync.called.should.equal(false);
      fs.utimesSync.called.should.equal(false);
    });
  });

  describe('#log', function() {
    const log = replayerUtil.internal.log;

    beforeEach(function() {
      sinon.stub(console, 'log'); // console.log becomes a noop
    });

    afterEach(function() {
      console.log.restore();
    });

    it('logs the given message in the given color', function() {
      replayerUtil.configure({ verbose: true });

      log('\033[31m', ['arg1', 'arg2']);
      console.log.calledWithExactly('\033[31m', 'arg1', 'arg2', '\033[0m')
        .should.equal(true);
    });

    it('does not log anything if in non-verbose mode', function() {
      replayerUtil.configure({ verbose: false });

      log('\033[31m', 'arg1', 'arg2');
      console.log.called.should.equal(false);
    });
  });

  describe('#logFixtureStatus', function() {
    const logFixtureStatus = replayerUtil.internal.logFixtureStatus;

    beforeEach(function() {
      sinon.stub(console, 'log'); // console.log becomes a noop
    });

    afterEach(function() {
      console.log.restore();
      fs.existsSync.restore();
    });

    it('logs a cache hit if the fixture exists', function() {
      replayerUtil.configure({ verbose: true });
      sinon.stub(fs, 'existsSync').returns(false);
      fs.existsSync.withArgs('my-filename.headers').returns(true);

      logFixtureStatus('my-filename', 'my-hash-body');
      console.log.calledOnce.should.equal(true);
      console.log.args[0][0].should.equal('\033[1;32m');
      console.log.args[0][1].should.include('cache hit');
      console.log.args[0].should.include('my-filename.headers');
      console.log.args[0].should.include('my-hash-body');
    });

    it('logs a cache miss if the fixture does not exist', function() {
      replayerUtil.configure({ verbose: true });
      sinon.stub(fs, 'existsSync').returns(true);
      fs.existsSync.withArgs('my-filename.headers').returns(false);

      logFixtureStatus('my-filename', 'my-hash-body');
      console.log.calledOnce.should.equal(true);
      console.log.args[0][0].should.equal('\033[1;31m');
      console.log.args[0][1].should.include('cache miss');
      console.log.args[0].should.include('my-filename.headers');
      console.log.args[0].should.include('my-hash-body');
    });

    it('does not log anything if in non-verbose mode', function() {
      replayerUtil.configure({ verbose: false });
      sinon.stub(fs, 'existsSync').returns(true);

      logFixtureStatus('filename', 'my-hash-body');
      fs.existsSync.called.should.equal(false);
      console.log.called.should.equal(false);
    });
  });

  describe('#logFixtureDebugStatus', function() {
    const logFixtureDebugStatus = replayerUtil.internal.logFixtureDebugStatus;

    beforeEach(function() {
      sinon.stub(console, 'log'); // console.log becomes a noop
    });

    afterEach(function() {
      console.log.restore();
      fs.existsSync.restore();
    });

    it('does not log anything if in non-verbose mode', function() {
      replayerUtil.configure({ verbose: false });
      sinon.stub(fs, 'existsSync').returns(true);

      logFixtureDebugStatus('my-missing-filename', 'my-best-matching-fixture',
        'my-best-matching-fixture-file-hash');
      fs.existsSync.called.should.equal(false);
      console.log.called.should.equal(false);
    });

    it('logs a best matching fixture when verbose mode is true', function() {
      replayerUtil.configure({ verbose: true });
      sinon.stub(fs, 'existsSync').returns(true);
      fs.existsSync.withArgs('my-filename.headers').returns(false);

      logFixtureDebugStatus('my-missing-filename', 'my-best-matching-fixture',
        'my-best-matching-fixture-file-hash');
      console.log.calledOnce.should.equal(true);
      console.log.args[0][0].should.equal('\033[1;34m');
      console.log.args[0][1].should.include('==== Best matching Fixture ====');
      console.log.args[0].should.include('my-missing-filename');
      console.log.args[0].should.include('my-best-matching-fixture');
      console.log.args[0].should.include('my-best-matching-fixture-file-hash');
    });
  });

  describe('#parseCookiesNames', function() {
    const parseCookiesNames = replayerUtil.internal.parseCookiesNames;

    it('returns an empty list when there are no cookies', function() {
      parseCookiesNames().should.eql([]);
      parseCookiesNames('').should.eql([]);
    });

    it('parses out all cookie names when there is no whitelist', function() {
      parseCookiesNames('name1=value1; name2="value2"').should.eql([
        'name1',
        'name2'
      ]);
    });

    it('alphabetizes the cookie names', function() {
      parseCookiesNames('b=1; a=2; c=3').should.eql(['a', 'b', 'c']);
    });

    it('lower cases the cookie names', function() {
      parseCookiesNames('A=1; B=2; C=3').should.eql(['a', 'b', 'c']);
    });

    it('ignores invalid cookies', function() {
      parseCookiesNames('A=1; ; B=2; =3').should.eql(['a', 'b']);
    });

    it('filters by the whitelist if there is one', function() {
      replayerUtil.configure({
        cookieWhitelist: ['a', 'b']
      });

      parseCookiesNames('a=1; b=2; c=3').should.eql(['a', 'b']);
    });
  });

  describe('#parseHeaders', function() {
    const parseHeaders = replayerUtil.internal.parseHeaders;

    it('returns an empty list when there are no headers', function() {
      parseHeaders().should.eql([]);
      parseHeaders({}).should.eql([]);
    });

    it('parses out all header names when there is no whitelist', function() {
      parseHeaders({
        name1: 'value1',
        name2: 'value2'
      }).should.eql([
        'name1',
        'name2'
      ]);
    });

    it('alphabetizes the header names', function() {
      parseHeaders({
        b: 1,
        c: 2,
        a: 3
      }).should.eql(['a', 'b', 'c']);
    });

    it('lower cases the header names', function() {
      parseHeaders({
        A: 1,
        B: 2,
        C: 3
      }).should.eql(['a', 'b', 'c']);
    });

    it('filters by the whitelist if there is one', function() {
      replayerUtil.configure({
        headerWhitelist: ['a', 'b']
      });

      parseHeaders({
        b: 1,
        c: 2,
        a: 3
      }).should.eql(['a', 'b']);
    });

    it('includes header values if requested', function () {
      replayerUtil.configure({
        includeHeaderValues: true
      });

      parseHeaders({
        b: 1,
        c: 2,
        a: 3
      }).should.eql(['a:3', 'b:1', 'c:2']);
    });

    it('includes header values if requested and filters by whitelist case insensitively', function () {
      replayerUtil.configure({
        includeHeaderValues: true,
        headerWhitelist: ['A', 'B']
      });

      parseHeaders({
        b: 1,
        c: 2,
        a: 3
      }).should.eql(['a:3', 'b:1']);
    });

    it('filters out replayer headers', function() {
      parseHeaders({
        b: 1,
        'x-replayer-internal-header': 2,
        a: 3
      }).should.eql(['a', 'b']);
    });
  });

  describe('#gatherFilenameHashParts', function() {
    const gatherFilenameHashParts = replayerUtil.internal.gatherFilenameHashParts;

    const headers = {
      header1: 'value1',
      header2: 'value2',
      cookie: 'name3=value3; name4=value4'
    };

    it('gathers all non-header parts if not including headers', function() {
      replayerUtil.configure({
        includeHeaderNames: false,
        includeCookieNames: false
      });

      var parts = gatherFilenameHashParts('post', 'my-url', 'my-body',
        headers);

      parts.should.eql([
        ['method', 'post'],
        ['url', 'my-url'],
        ['body', 'my-body'],
        ['headerNames', []],
        ['cookieNames', []]
      ]);
    });

    it('includes header names if requested', function() {
      replayerUtil.configure({
        includeHeaderNames: true,
        includeCookieNames: false
      });

      var parts = gatherFilenameHashParts('post', 'my-url', 'my-body',
        headers);

      parts.should.eql([
        ['method', 'post'],
        ['url', 'my-url'],
        ['body', 'my-body'],
        ['headerNames', ['cookie', 'header1', 'header2']],
        ['cookieNames', []]
      ]);
    });

    it('includes cookie names if requested', function() {
      replayerUtil.configure({
        includeHeaderNames: false,
        includeCookieNames: true
      });

      var parts = gatherFilenameHashParts('post', 'my-url', 'my-body',
        headers);

      parts.should.eql([
        ['method', 'post'],
        ['url', 'my-url'],
        ['body', 'my-body'],
        ['headerNames', []],
        ['cookieNames', ['name3', 'name4']]
      ]);
    });

    it('applies all filtering', function() {
      replayerUtil.addFilter({
        urlFilter: function(url) {
          return 'url-filtered: ' + url;
        },
        bodyFilter: function(body) {
          return 'body-filtered: ' + body;
        }
      });

      var parts = gatherFilenameHashParts('post', 'my-url', 'my-body',
        headers);
      parts[1][1].should.equal('url-filtered: my-url');
      parts[2][1].should.equal('body-filtered: my-body');
    });

    it('defaults the method to "GET" if missing', function() {
      var parts = gatherFilenameHashParts(undefined, 'my-url', 'my-body',
        headers);
      parts[0][1].should.equal('get');

      parts = gatherFilenameHashParts('', 'my-url', 'my-body', headers);
      parts[0][1].should.equal('get');
    });

    it('lowercases the method', function() {
      var parts = gatherFilenameHashParts('POST', 'my-url', 'my-body',
        headers);
      parts[0][1].should.equal('post');
    });

    it('gracefully handles missing headers', function() {
      var parts = gatherFilenameHashParts('POST', 'my-url', 'my-body');

      parts.should.eql([
        ['method', 'post'],
        ['url', 'my-url'],
        ['body', 'my-body'],
        ['headerNames', []],
        ['cookieNames', []]
      ]);
    });
  });

  describe('#constructAndCreateFixtureFolder', function() {
    const constructAndCreateFixtureFolder =
      replayerUtil.internal.constructAndCreateFixtureFolder;

    beforeEach(function() {
      sinon.stub(fs, 'mkdirSync'); // mkdirSync becomes a noop
      sinon.stub(fs, 'existsSync').returns(true);
    });

    afterEach(function() {
      fs.mkdirSync.restore();
      fs.existsSync.restore();
    });

    it('constructs a folder from all necessary information', function() {
      replayerUtil.setFixtureDir('/global/fixture/dir');
      replayerUtil.setTestOptions({ testName: 'test/name' });

      var folder = constructAndCreateFixtureFolder('my-url', {
        'accept-language': 'en-US'
      });

      folder.should.equal('/global/fixture/dir/en-US/test/name');
    });

    it('uses the "x-replayer-test-name" header as the test name', function() {
      replayerUtil.setFixtureDir('/global/fixture/dir');
      // don't set the test name globally

      var folder = constructAndCreateFixtureFolder('my-url', {
        'accept-language': 'en-US',
        'x-replayer-test-name': 'test/name'
      });

      folder.should.equal('/global/fixture/dir/en-US/test/name');
    });

    it('favors the "x-replayer-test-name" header as the test name', function() {
      replayerUtil.setFixtureDir('/global/fixture/dir');
      replayerUtil.setTestOptions({ testName: 'global/test/name' });

      var folder = constructAndCreateFixtureFolder('my-url', {
        'accept-language': 'en-US',
        'x-replayer-test-name': 'test/name'
      });

      folder.should.equal('/global/fixture/dir/en-US/test/name');
    });

    it('ignores the test name if it should use global fixtures', function() {
      replayerUtil.setFixtureDir('/global/fixture/dir');
      replayerUtil.setTestOptions({ testName: 'test/name' });

      replayerUtil.addFilter({
        url: /my-url/,
        global: true
      });

      var folder = constructAndCreateFixtureFolder('my-url', {
        'accept-language': 'en-US'
      });

      folder.should.equal('/global/fixture/dir/en-US');
    });

    it('picks out the first entry in the accept-language header', function() {
      replayerUtil.setFixtureDir('/global/fixture/dir');
      replayerUtil.setTestOptions({ testName: 'test/name' });

      var folder = constructAndCreateFixtureFolder('my-url', {
        'accept-language': 'de-DE,en-US'
      });

      folder.should.equal('/global/fixture/dir/de-DE/test/name');
    });

    it('gracefully handles no test name', function() {
      replayerUtil.setFixtureDir('/global/fixture/dir');

      var folder = constructAndCreateFixtureFolder('my-url', {
        'accept-language': 'en-US'
      });

      folder.should.equal('/global/fixture/dir/en-US');
    });

    it('gracefully handles an invalid/missing accept-language header',
      function() {
        replayerUtil.setFixtureDir('/global/fixture/dir');
        replayerUtil.setTestOptions({ testName: 'test/name' });

        var folder = constructAndCreateFixtureFolder('my-url');
        folder.should.equal('/global/fixture/dir/test/name');

        folder = constructAndCreateFixtureFolder('my-url', {});
        folder.should.equal('/global/fixture/dir/test/name');

        folder = constructAndCreateFixtureFolder('my-url', {
          'accept-language': ''
        });
        folder.should.equal('/global/fixture/dir/test/name');
      });

    it('creates the constructed folder', function() {
      replayerUtil.setFixtureDir('global/fixture/dir');
      replayerUtil.setTestOptions({ testName: 'test/name' });

      var folder = constructAndCreateFixtureFolder('my-url', {
        'accept-language': 'en-US'
      });

      fs.existsSync.calledWith(folder).should.equal(true);
    });
  });

  describe('#constructFilename', function() {
    // The fixture filenames in these tests are computed by hand for the
    // purposes of verification.
    const constructFilename = replayerUtil.constructFilename;

    beforeEach(function() {
      sinon.stub(fs, 'mkdirSync'); // mkdirSync becomes a noop
      sinon.stub(fs, 'utimesSync'); // utimesSync becomes a noop
      sinon.stub(fs, 'existsSync').returns(true);
    });

    afterEach(function() {
      fs.mkdirSync.restore();
      fs.utimesSync.restore();
      fs.existsSync.restore();
    });

    it('constructs using all the available information using default values', function() {
      replayerUtil.setFixtureDir('/global/fixture/dir');
      replayerUtil.setTestOptions({ testName: 'test/name', });

      var filename = constructFilename('get', 'my-url', 'my-body', {
        'accept-language': 'en-US',
        header1: 'value1',
        cookie: 'cookie1=value1'
      });

      filename.should.equal('/global/fixture/dir/en-US/test/name/' +
        '32772f774a3f187d465d47a526b80e6f');
    });

  });

  describe('#urlFromHttpRequestOptions', function() {
    const urlFromHttpRequestOptions = replayerUtil.urlFromHttpRequestOptions;

    it('returns a formatted url based on the options', function() {
      urlFromHttpRequestOptions(
        {
          hostname: 'my-hostname',
          auth: 'my-user:my-pass',
          port: '1234',
          path: '/my/path'
        },
        'https'
      ).should.equal('https://my-user:my-pass@my-hostname:1234/my/path');
    });

    it('accepts "host" instead of "hostname"', function() {
      urlFromHttpRequestOptions(
        {
          host: 'my-hostname',
          path: '/my/path'
        },
        'https'
      ).should.equal('https://my-hostname/my/path');
    });

    it('parses ? properly"', function() {
      urlFromHttpRequestOptions(
        {
          host: 'my-hostname',
          path: '/my/path?foo=bar'
        },
        'https'
      ).should.equal('https://my-hostname/my/path?foo=bar');
    });
  });

  describe('#shouldForceLive', function() {
    const shouldForceLive = replayerUtil.shouldForceLive;

    it('returns false if there are no filters', function() {
      shouldForceLive('my-url').should.equal(false);
    });

    it('returns false if there are no matching filters', function() {
      replayerUtil.addFilter({
        url: /other-url/,
        forceLive: true
      });

      replayerUtil.addFilter({
        url: /another-url/,
        forceLive: true
      });

      shouldForceLive('my-url').should.equal(false);
    });

    it('returns false if no matching filters specify forceLive', function() {
      replayerUtil.addFilter({ url: /my/ });
      replayerUtil.addFilter({ url: /url/ });

      shouldForceLive('my-url').should.equal(false);
    });

    it('returns true if any matching filter specifies forceLive', function() {
      replayerUtil.addFilter({
        url: /my/,
        forceLive: false
      });

      replayerUtil.addFilter({
        url: /url/,
        forceLive: true
      });

      replayerUtil.addFilter({
        url: /my-url/,
        forceLive: false
      });

      shouldForceLive('my-url').should.equal(true);
    });
  });

  describe('#findTheBestMatchingFixture', function() {

    beforeEach(function() {
      sinon.stub(fs, 'readFileSync');
      sinon.stub(fs, 'readdirSync');
    });

    it('finds the best matching fixture', function() {
      var missingFile = 'missingFile.missing';
      var missingFileData = JSON.stringify({
        url: 'http://mysite.com',
        method: 'POST',
        body: 'missing file'
      });
      var RequestOneFileData = JSON.stringify({
        url: 'http://mysite.com',
        method: 'POST',
        body: 'Some different request'
      });
      var RequestTwoFileData = JSON.stringify({
        url: 'http://mysite.com',
        method: 'POST',
        body: 'missing file 0123'
      });
      var filesArr = ['requestOne.request', 'requestTwo.request'] ;

      fs.readFileSync.withArgs(path.resolve('missingFile.missing'))
        .returns(missingFileData);
      fs.readFileSync.withArgs(path.resolve('requestOne.request'))
        .returns(RequestOneFileData);
      fs.readFileSync.withArgs(path.resolve('requestTwo.request'))
        .returns(RequestTwoFileData);
      fs.readdirSync.returns(filesArr);

      var fixture = replayerUtil.findTheBestMatchingFixture(missingFile);
      fixture.should.include('requestTwo.request');
    });

    it('finds the best matching fixture, with the same host only', function() {
      var missingFile = 'missingFile.missing';
      var missingFileData = JSON.stringify({
        url: 'http://mysite.com',
        method: 'POST',
        body: 'missing file'
      });
      var RequestOneFileData = JSON.stringify({
        url: 'http://mysite.com',
        method: 'POST',
        body: 'Some different request'
      });
      var RequestTwoFileData = JSON.stringify({
        url: 'http://mysite2.com',
        method: 'POST',
        body: 'missing file'
      });
      var filesArr = ['requestOne.request', 'requestTwo.request'] ;

      fs.readFileSync.withArgs(path.resolve('missingFile.missing'))
        .returns(missingFileData);
      fs.readFileSync.withArgs(path.resolve('requestOne.request'))
        .returns(RequestOneFileData);
      fs.readFileSync.withArgs(path.resolve('requestTwo.request'))
        .returns(RequestTwoFileData);
      fs.readdirSync.returns(filesArr);

      var fixture = replayerUtil.findTheBestMatchingFixture(missingFile);
      fixture.should.include('requestOne.request');
    });

    it('returns null when there is no match', function() {
      var missingFile = 'missingFile.missing';
      var missingFileData = JSON.stringify({
        url: 'http://mysite.com',
        method: 'POST',
        body: 'missing file'
      });
      var RequestOneFileData = JSON.stringify({
        url: 'http://mysite1.com',
        method: 'POST',
        body: 'Some different request'
      });
      var RequestTwoFileData = JSON.stringify({
        url: 'http://mysite2.com',
        method: 'POST',
        body: 'missing file'
      });
      var filesArr = ['requestOne.request', 'requestTwo.request'] ;

      fs.readFileSync.withArgs(path.resolve('missingFile.missing'))
        .returns(missingFileData);
      fs.readFileSync.withArgs(path.resolve('requestOne.request'))
        .returns(RequestOneFileData);
      fs.readFileSync.withArgs(path.resolve('requestTwo.request'))
        .returns(RequestTwoFileData);
      fs.readdirSync.returns(filesArr);

      var fixture = replayerUtil.findTheBestMatchingFixture(missingFile);
      should.not.exist(fixture);
    });

    afterEach(function() {
      fs.readFileSync.restore();
      fs.readdirSync.restore();
    });

  });

  describe('#testAgainstUrlRegex', function() {
    it('should return true if the routes in test match the regex. False otherwise', function() {
      replayerUtil.internal.globalOptions.urlBlacklist = [/test/,/boop/];
      var shouldIgnoreRoute1 = replayerUtil.testUrlAgainstRegex('http://www.test.com/true');
      var shouldIgnoreRoute2 = replayerUtil.testUrlAgainstRegex('http://www.beep.com/boop');
      var shouldIgnoreRoute3 = replayerUtil.testUrlAgainstRegex('http://www.foo.com/bar');

      shouldIgnoreRoute1.should.equal(true);
      shouldIgnoreRoute2.should.equal(true);
      shouldIgnoreRoute3.should.equal(false);
    });
  });
});


text:
<?php

namespace RabbitMQBundle\Services\RabbitMQ;

use JMS\Serializer\Serializer;
use PhpAmqpLib\Message\AMQPMessage;
use OldSound\RabbitMqBundle\RabbitMq\ConsumerInterface;
use Symfony\Bundle\TwigBundle\TwigEngine;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Class PDFGeneratorConsumer
 * @package RabbitMQBundle\Services\RabbitMQ
 * @author Alfonso M. García Astorga <me@alfon.io>
 */
class PDFGeneratorConsumer implements ConsumerInterface
{
    /**
     * @var ContainerInterface
     */
    protected $container;

    /**
     * @var TwigEngine
     */
    protected $templating;

    /**
     * @var Serializer
     */
    protected $serializer;

    /**
     * PDFGeneratorConsumer constructor.
     * @param ContainerInterface $container
     * @param TwigEngine $templating
     * @param Serializer $serializer
     */
    public function __construct(ContainerInterface $container, TwigEngine $templating, Serializer $serializer)
    {
        $this->container = $container;
        $this->templating = $templating;
        $this->serializer = $serializer;
    }

    /**
     * @param AMQPMessage $msg
     * @return bool
     */
    public function execute(AMQPMessage $msg)
    {
        $post = $this->serializer->deserialize($msg->body, 'AppBundle\Entity\Post', 'json');

        $targetPath = $this->container->get('kernel')->getRootDir() . '/../web/downloads/pdf/' . $post->getPdfName() . '.pdf';

        $this->container->get('knp_snappy.pdf')->generateFromHtml(
            $this->templating->render(
                'RabbitMQBundle::pdf_post_view.html.twig',
                array(
                    'post' => $post
                )
            ),
            $targetPath
        );

        if (file_exists($targetPath)) {
            return true;
        } else {
            return false;
        }
    }
}

redpajama_set_name: RedPajamaGithub


text:
<?php

use_stylesheet('admin.dataTable');
use_stylesheet('admin.log');
use_stylesheet('dmSepPlugin.log');
use_javascript('dmSepPlugin.log');

echo _tag('div.dm_box.log.'.$logKey.'_log',
  _tag('div.title',
    _link('@dm_sep_result')->set('.s16block.s16_arrow_up_right')->textTitle(__('Expanded view')).
    _tag('h2', __('Search Engine Position'))
  ).
  _tag('div.dm_box_inner.dm_data', '

')
);


text:
CREATE TABLE Dishes(
	Name VARCHAR(24) PRIMARY KEY,
	TotalAmount VARCHAR(24) NOT NULL,
	Price SMALLMONEY NOT NULL
);

CREATE TABLE Ingredients(
	Name VARCHAR(24) PRIMARY KEY
);

CREATE TABLE Recipes(
	Id INT PRIMARY KEY IDENTITY,
	Dish_name VARCHAR(24) NOT NULL,
	Ingredient_name VARCHAR(24) NOT NULL,
	Amount VARCHAR(24) NOT NULL,

	FOREIGN KEY (Dish_name) REFERENCES Dishes(Name),
	FOREIGN KEY (Ingredient_name) REFERENCES Ingredients(Name)
);


text:
#include "config.h"
#include "web/WebInputEventConversion.h"

#include "core/dom/Touch.h"
#include "core/dom/TouchList.h"
#include "core/events/GestureEvent.h"
#include "core/events/KeyboardEvent.h"
#include "core/events/MouseEvent.h"
#include "core/events/TouchEvent.h"
#include "core/events/WheelEvent.h"
#include "core/frame/FrameHost.h"
#include "core/frame/FrameView.h"
#include "core/frame/PinchViewport.h"
#include "core/layout/LayoutObject.h"
#include "core/page/Page.h"
#include "platform/KeyboardCodes.h"
#include "platform/Widget.h"
#include "public/platform/Platform.h"

namespace blink {

static const double millisPerSecond = 1000.0;

static float scaleDeltaToWindow(const Widget* widget, float delta)
{
    float scale = 1;
    if (widget) {
        FrameView* rootView = toFrameView(widget->root());
        if (rootView)
            scale = rootView->inputEventsScaleFactor();
    }
    return delta / scale;
}

static FloatSize scaleSizeToWindow(const Widget* widget, FloatSize size)
{
    return FloatSize(scaleDeltaToWindow(widget, size.width()), scaleDeltaToWindow(widget, size.height()));
}

static FloatPoint convertHitPointToWindow(const Widget* widget, FloatPoint point)
{
    float scale = 1;
    IntSize offset;
    IntPoint pinchViewport;
    FloatSize overscrollOffset;
    if (widget) {
        FrameView* rootView = toFrameView(widget->root());
        if (rootView) {
            scale = rootView->inputEventsScaleFactor();
            offset = rootView->inputEventsOffsetForEmulation();
            pinchViewport = flooredIntPoint(rootView->page()->frameHost().pinchViewport().visibleRect().location());
            overscrollOffset = rootView->elasticOverscroll();
        }
    }
    return FloatPoint(
        (point.x() - offset.width()) / scale + pinchViewport.x() + overscrollOffset.width(),
        (point.y() - offset.height()) / scale + pinchViewport.y() + overscrollOffset.height());
}

static unsigned toPlatformEventModifiers(int webModifiers)
{
    unsigned newModifiers = 0;
    if (webModifiers & WebInputEvent::ShiftKey)
        newModifiers |= PlatformEvent::ShiftKey;
    if (webModifiers & WebInputEvent::ControlKey)
        newModifiers |= PlatformEvent::CtrlKey;
    if (webModifiers & WebInputEvent::AltKey)
        newModifiers |= PlatformEvent::AltKey;
    if (webModifiers & WebInputEvent::MetaKey)
        newModifiers |= PlatformEvent::MetaKey;
    return newModifiers;
}

unsigned toPlatformMouseEventModifiers(int webModifiers)
{
    unsigned newModifiers = toPlatformEventModifiers(webModifiers);
    if (webModifiers & WebInputEvent::LeftButtonDown)
        newModifiers |= PlatformEvent::LeftButtonDown;
    if (webModifiers & WebInputEvent::MiddleButtonDown)
        newModifiers |= PlatformEvent::MiddleButtonDown;
    if (webModifiers & WebInputEvent::RightButtonDown)
        newModifiers |= PlatformEvent::RightButtonDown;
    return newModifiers;
}

static unsigned toPlatformModifierFrom(WebMouseEvent::Button button)
{
    if (button == WebMouseEvent::ButtonNone)
        return 0;

    unsigned webMouseButtonToPlatformModifier[] = {
        PlatformEvent::LeftButtonDown,
        PlatformEvent::MiddleButtonDown,
        PlatformEvent::RightButtonDown
    };

    return webMouseButtonToPlatformModifier[button];
}

// MakePlatformMouseEvent -----------------------------------------------------

PlatformMouseEventBuilder::PlatformMouseEventBuilder(Widget* widget, const WebMouseEvent& e)
{
    // FIXME: Widget is always toplevel, unless it's a popup. We may be able
    // to get rid of this once we abstract popups into a WebKit API.
    m_position = widget->convertFromContainingWindow(flooredIntPoint(convertHitPointToWindow(widget, IntPoint(e.x, e.y))));
    m_globalPosition = IntPoint(e.globalX, e.globalY);
    m_movementDelta = IntPoint(scaleDeltaToWindow(widget, e.movementX), scaleDeltaToWindow(widget, e.movementY));
    m_button = static_cast<MouseButton>(e.button);
    m_modifiers = toPlatformMouseEventModifiers(e.modifiers);

    m_timestamp = e.timeStampSeconds;
    m_clickCount = e.clickCount;

    switch (e.type) {
    case WebInputEvent::MouseMove:
    case WebInputEvent::MouseLeave:  // synthesize a move event
        m_type = PlatformEvent::MouseMoved;
        break;

    case WebInputEvent::MouseDown:
        m_type = PlatformEvent::MousePressed;
        break;

    case WebInputEvent::MouseUp:
        m_type = PlatformEvent::MouseReleased;

        // The MouseEvent spec requires that buttons indicates the state
        // immediately after the event takes place. To ensure consistency
        // between platforms here, we explicitly clear the button that is
        // in the process of being released.
        m_modifiers &= ~toPlatformModifierFrom(e.button);
        break;

    default:
        ASSERT_NOT_REACHED();
    }
}

// PlatformWheelEventBuilder --------------------------------------------------

PlatformWheelEventBuilder::PlatformWheelEventBuilder(Widget* widget, const WebMouseWheelEvent& e)
{
    m_position = widget->convertFromContainingWindow(flooredIntPoint(convertHitPointToWindow(widget, FloatPoint(e.x, e.y))));
    m_globalPosition = IntPoint(e.globalX, e.globalY);
    m_deltaX = e.deltaX;
    m_deltaY = e.deltaY;
    m_wheelTicksX = e.wheelTicksX;
    m_wheelTicksY = e.wheelTicksY;
    m_granularity = e.scrollByPage ?
        ScrollByPageWheelEvent : ScrollByPixelWheelEvent;

    m_type = PlatformEvent::Wheel;

    m_modifiers = toPlatformMouseEventModifiers(e.modifiers);

    m_hasPreciseScrollingDeltas = e.hasPreciseScrollingDeltas;
    m_canScroll = e.canScroll;
    m_railsMode = static_cast<PlatformEvent::RailsMode>(e.railsMode);
#if OS(MACOSX)
    m_phase = static_cast<PlatformWheelEventPhase>(e.phase);
    m_momentumPhase = static_cast<PlatformWheelEventPhase>(e.momentumPhase);
    m_timestamp = e.timeStampSeconds;
    m_canRubberbandLeft = e.canRubberbandLeft;
    m_canRubberbandRight = e.canRubberbandRight;
#endif
}

// PlatformGestureEventBuilder --------------------------------------------------

PlatformGestureEventBuilder::PlatformGestureEventBuilder(Widget* widget, const WebGestureEvent& e)
{
    switch (e.type) {
    case WebInputEvent::GestureScrollBegin:
        m_type = PlatformEvent::GestureScrollBegin;
        break;
    case WebInputEvent::GestureScrollEnd:
        m_type = PlatformEvent::GestureScrollEnd;
        break;
    case WebInputEvent::GestureFlingStart:
        m_type = PlatformEvent::GestureFlingStart;
        break;
    case WebInputEvent::GestureScrollUpdate:
        m_type = PlatformEvent::GestureScrollUpdate;
        m_data.m_scroll.m_deltaX = scaleDeltaToWindow(widget, e.data.scrollUpdate.deltaX);
        m_data.m_scroll.m_deltaY = scaleDeltaToWindow(widget, e.data.scrollUpdate.deltaY);
        m_data.m_scroll.m_velocityX = e.data.scrollUpdate.velocityX;
        m_data.m_scroll.m_velocityY = e.data.scrollUpdate.velocityY;
        m_data.m_scroll.m_preventPropagation = e.data.scrollUpdate.preventPropagation;
        m_data.m_scroll.m_inertial = e.data.scrollUpdate.inertial;
        break;
    case WebInputEvent::GestureTap:
        m_type = PlatformEvent::GestureTap;
        m_area = expandedIntSize(scaleSizeToWindow(widget, FloatSize(e.data.tap.width, e.data.tap.height)));
        m_data.m_tap.m_tapCount = e.data.tap.tapCount;
        break;
    case WebInputEvent::GestureTapUnconfirmed:
        m_type = PlatformEvent::GestureTapUnconfirmed;
        m_area = expandedIntSize(scaleSizeToWindow(widget, FloatSize(e.data.tap.width, e.data.tap.height)));
        break;
    case WebInputEvent::GestureTapDown:
        m_type = PlatformEvent::GestureTapDown;
        m_area = expandedIntSize(scaleSizeToWindow(widget, FloatSize(e.data.tapDown.width, e.data.tapDown.height)));
        break;
    case WebInputEvent::GestureShowPress:
        m_type = PlatformEvent::GestureShowPress;
        m_area = expandedIntSize(scaleSizeToWindow(widget, FloatSize(e.data.showPress.width, e.data.showPress.height)));
        break;
    case WebInputEvent::GestureTapCancel:
        m_type = PlatformEvent::GestureTapDownCancel;
        break;
    case WebInputEvent::GestureDoubleTap:
        // DoubleTap gesture is now handled as PlatformEvent::GestureTap with tap_count = 2. So no
        // need to convert to a Platfrom DoubleTap gesture. But in WebViewImpl::handleGestureEvent
        // all WebGestureEvent are converted to PlatformGestureEvent, for completeness and not reach
        // the ASSERT_NOT_REACHED() at the end, convert the DoubleTap to a NoType.
        m_type = PlatformEvent::NoType;
        break;
    case WebInputEvent::GestureTwoFingerTap:
        m_type = PlatformEvent::GestureTwoFingerTap;
        m_area = expandedIntSize(scaleSizeToWindow(widget, FloatSize(e.data.twoFingerTap.firstFingerWidth, e.data.twoFingerTap.firstFingerHeight)));
        break;
    case WebInputEvent::GestureLongPress:
        m_type = PlatformEvent::GestureLongPress;
        m_area = expandedIntSize(scaleSizeToWindow(widget, FloatSize(e.data.longPress.width, e.data.longPress.height)));
        break;
    case WebInputEvent::GestureLongTap:
        m_type = PlatformEvent::GestureLongTap;
        m_area = expandedIntSize(scaleSizeToWindow(widget, FloatSize(e.data.longPress.width, e.data.longPress.height)));
        break;
    case WebInputEvent::GesturePinchBegin:
        m_type = PlatformEvent::GesturePinchBegin;
        break;
    case WebInputEvent::GesturePinchEnd:
        m_type = PlatformEvent::GesturePinchEnd;
        break;
    case WebInputEvent::GesturePinchUpdate:
        m_type = PlatformEvent::GesturePinchUpdate;
        m_data.m_pinchUpdate.m_scale = e.data.pinchUpdate.scale;
        break;
    default:
        ASSERT_NOT_REACHED();
    }
    m_position = widget->convertFromContainingWindow(flooredIntPoint(convertHitPointToWindow(widget, FloatPoint(e.x, e.y))));
    m_globalPosition = IntPoint(e.globalX, e.globalY);
    m_timestamp = e.timeStampSeconds;
    m_modifiers = toPlatformEventModifiers(e.modifiers);
}

// MakePlatformKeyboardEvent --------------------------------------------------

inline PlatformEvent::Type toPlatformKeyboardEventType(WebInputEvent::Type type)
{
    switch (type) {
    case WebInputEvent::KeyUp:
        return PlatformEvent::KeyUp;
    case WebInputEvent::KeyDown:
        return PlatformEvent::KeyDown;
    case WebInputEvent::RawKeyDown:
        return PlatformEvent::RawKeyDown;
    case WebInputEvent::Char:
        return PlatformEvent::Char;
    default:
        ASSERT_NOT_REACHED();
    }
    return PlatformEvent::KeyDown;
}

PlatformKeyboardEventBuilder::PlatformKeyboardEventBuilder(const WebKeyboardEvent& e)
{
    m_type = toPlatformKeyboardEventType(e.type);
    m_text = String(e.text);
    m_unmodifiedText = String(e.unmodifiedText);
    m_keyIdentifier = String(e.keyIdentifier);
    m_autoRepeat = (e.modifiers & WebInputEvent::IsAutoRepeat);
    m_nativeVirtualKeyCode = e.nativeKeyCode;
    m_isKeypad = (e.modifiers & WebInputEvent::IsKeyPad);
    m_isSystemKey = e.isSystemKey;
    // TODO: BUG482880 Fix this initialization to lazy initialization.
    m_code = Platform::current()->domCodeStringFromEnum(e.domCode);
    m_key = Platform::current()->domKeyStringFromEnum(e.domKey);

    m_modifiers = toPlatformEventModifiers(e.modifiers);

    // FIXME: PlatformKeyboardEvents expect a locational version of the keycode (e.g. VK_LSHIFT
    // instead of VK_SHIFT). This should be changed so the location/keycode are stored separately,
    // as in other places in the code.
    m_windowsVirtualKeyCode = e.windowsKeyCode;
    if (e.windowsKeyCode == VK_SHIFT) {
        if (e.modifiers & WebInputEvent::IsLeft)
            m_windowsVirtualKeyCode = VK_LSHIFT;
        else if (e.modifiers & WebInputEvent::IsRight)
            m_windowsVirtualKeyCode = VK_RSHIFT;
    } else if (e.windowsKeyCode == VK_CONTROL) {
        if (e.modifiers & WebInputEvent::IsLeft)
            m_windowsVirtualKeyCode = VK_LCONTROL;
        else if (e.modifiers & WebInputEvent::IsRight)
            m_windowsVirtualKeyCode = VK_RCONTROL;
    } else if (e.windowsKeyCode == VK_MENU) {
        if (e.modifiers & WebInputEvent::IsLeft)
            m_windowsVirtualKeyCode = VK_LMENU;
        else if (e.modifiers & WebInputEvent::IsRight)
            m_windowsVirtualKeyCode = VK_RMENU;
    }

}

void PlatformKeyboardEventBuilder::setKeyType(Type type)
{
    // According to the behavior of Webkit in Windows platform,
    // we need to convert KeyDown to RawKeydown and Char events
    // See WebKit/WebKit/Win/WebView.cpp
    ASSERT(m_type == KeyDown);
    ASSERT(type == RawKeyDown || type == Char);
    m_type = type;

    if (type == RawKeyDown) {
        m_text = String();
        m_unmodifiedText = String();
    } else {
        m_keyIdentifier = String();
        m_windowsVirtualKeyCode = 0;
    }
}

// Please refer to bug http://b/issue?id=961192, which talks about Webkit
// keyboard event handling changes. It also mentions the list of keys
// which don't have associated character events.
bool PlatformKeyboardEventBuilder::isCharacterKey() const
{
    switch (windowsVirtualKeyCode()) {
    case VKEY_BACK:
    case VKEY_ESCAPE:
        return false;
    }
    return true;
}

inline PlatformEvent::Type toPlatformTouchEventType(const WebInputEvent::Type type)
{
    switch (type) {
    case WebInputEvent::TouchStart:
        return PlatformEvent::TouchStart;
    case WebInputEvent::TouchMove:
        return PlatformEvent::TouchMove;
    case WebInputEvent::TouchEnd:
        return PlatformEvent::TouchEnd;
    case WebInputEvent::TouchCancel:
        return PlatformEvent::TouchCancel;
    default:
        ASSERT_NOT_REACHED();
    }
    return PlatformEvent::TouchStart;
}

inline PlatformTouchPoint::State toPlatformTouchPointState(const WebTouchPoint::State state)
{
    switch (state) {
    case WebTouchPoint::StateReleased:
        return PlatformTouchPoint::TouchReleased;
    case WebTouchPoint::StatePressed:
        return PlatformTouchPoint::TouchPressed;
    case WebTouchPoint::StateMoved:
        return PlatformTouchPoint::TouchMoved;
    case WebTouchPoint::StateStationary:
        return PlatformTouchPoint::TouchStationary;
    case WebTouchPoint::StateCancelled:
        return PlatformTouchPoint::TouchCancelled;
    case WebTouchPoint::StateUndefined:
        ASSERT_NOT_REACHED();
    }
    return PlatformTouchPoint::TouchReleased;
}

inline WebTouchPoint::State toWebTouchPointState(const AtomicString& type)
{
    if (type == EventTypeNames::touchend)
        return WebTouchPoint::StateReleased;
    if (type == EventTypeNames::touchcancel)
        return WebTouchPoint::StateCancelled;
    if (type == EventTypeNames::touchstart)
        return WebTouchPoint::StatePressed;
    if (type == EventTypeNames::touchmove)
        return WebTouchPoint::StateMoved;
    return WebTouchPoint::StateUndefined;
}

PlatformTouchPointBuilder::PlatformTouchPointBuilder(Widget* widget, const WebTouchPoint& point)
{
    m_id = point.id;
    m_state = toPlatformTouchPointState(point.state);

    // This assumes convertFromContainingWindow does only translations, not scales.
    FloatPoint floatPos = convertHitPointToWindow(widget, point.position);
    IntPoint flooredPoint = flooredIntPoint(floatPos);
    m_pos = widget->convertFromContainingWindow(flooredPoint) + (floatPos - flooredPoint);

    m_screenPos = FloatPoint(point.screenPosition.x, point.screenPosition.y);
    m_radius = scaleSizeToWindow(widget, FloatSize(point.radiusX, point.radiusY));
    m_rotationAngle = point.rotationAngle;
    m_force = point.force;
}

PlatformTouchEventBuilder::PlatformTouchEventBuilder(Widget* widget, const WebTouchEvent& event)
{
    m_type = toPlatformTouchEventType(event.type);
    m_modifiers = toPlatformEventModifiers(event.modifiers);
    m_timestamp = event.timeStampSeconds;
    m_causesScrollingIfUncanceled = event.causesScrollingIfUncanceled;

    for (unsigned i = 0; i < event.touchesLength; ++i)
        m_touchPoints.append(PlatformTouchPointBuilder(widget, event.touches[i]));

    m_cancelable = event.cancelable;
}

static int getWebInputModifiers(const UIEventWithKeyState& event)
{
    int modifiers = 0;
    if (event.ctrlKey())
        modifiers |= WebInputEvent::ControlKey;
    if (event.shiftKey())
        modifiers |= WebInputEvent::ShiftKey;
    if (event.altKey())
        modifiers |= WebInputEvent::AltKey;
    if (event.metaKey())
        modifiers |= WebInputEvent::MetaKey;
    return modifiers;
}

static FloatPoint convertAbsoluteLocationForLayoutObjectFloat(const LayoutPoint& location, const LayoutObject& layoutObject)
{
    return layoutObject.absoluteToLocal(FloatPoint(location), UseTransforms);
}

static IntPoint convertAbsoluteLocationForLayoutObject(const LayoutPoint& location, const LayoutObject& layoutObject)
{
    return roundedIntPoint(convertAbsoluteLocationForLayoutObjectFloat(location, layoutObject));
}

// FIXME: Change |widget| to const Widget& after RemoteFrames get
// RemoteFrameViews.
static void updateWebMouseEventFromCoreMouseEvent(const MouseRelatedEvent& event, const Widget* widget, const LayoutObject& layoutObject, WebMouseEvent& webEvent)
{
    webEvent.timeStampSeconds = event.timeStamp() / millisPerSecond;
    webEvent.modifiers = getWebInputModifiers(event);

    FrameView* view = widget ? toFrameView(widget->parent()) : 0;
    // FIXME: If view == nullptr, pointInRootFrame will really be pointInRootContent.
    IntPoint pointInRootFrame = IntPoint(event.absoluteLocation().x(), event.absoluteLocation().y());
    if (view)
        pointInRootFrame = view->contentsToRootFrame(pointInRootFrame);
    webEvent.globalX = event.screenX();
    webEvent.globalY = event.screenY();
    webEvent.windowX = pointInRootFrame.x();
    webEvent.windowY = pointInRootFrame.y();
    IntPoint localPoint = convertAbsoluteLocationForLayoutObject(event.absoluteLocation(), layoutObject);
    webEvent.x = localPoint.x();
    webEvent.y = localPoint.y();
}

WebMouseEventBuilder::WebMouseEventBuilder(const Widget* widget, const LayoutObject* layoutObject, const MouseEvent& event)
{
    if (event.type() == EventTypeNames::mousemove)
        type = WebInputEvent::MouseMove;
    else if (event.type() == EventTypeNames::mouseout)
        type = WebInputEvent::MouseLeave;
    else if (event.type() == EventTypeNames::mouseover)
        type = WebInputEvent::MouseEnter;
    else if (event.type() == EventTypeNames::mousedown)
        type = WebInputEvent::MouseDown;
    else if (event.type() == EventTypeNames::mouseup)
        type = WebInputEvent::MouseUp;
    else if (event.type() == EventTypeNames::contextmenu)
        type = WebInputEvent::ContextMenu;
    else
        return; // Skip all other mouse events.

    updateWebMouseEventFromCoreMouseEvent(event, widget, *layoutObject, *this);

    switch (event.button()) {
    case LeftButton:
        button = WebMouseEvent::ButtonLeft;
        break;
    case MiddleButton:
        button = WebMouseEvent::ButtonMiddle;
        break;
    case RightButton:
        button = WebMouseEvent::ButtonRight;
        break;
    }
    if (event.buttonDown()) {
        switch (event.button()) {
        case LeftButton:
            modifiers |= WebInputEvent::LeftButtonDown;
            break;
        case MiddleButton:
            modifiers |= WebInputEvent::MiddleButtonDown;
            break;
        case RightButton:
            modifiers |= WebInputEvent::RightButtonDown;
            break;
        }
    } else
        button = WebMouseEvent::ButtonNone;
    movementX = event.movementX();
    movementY = event.movementY();
    clickCount = event.detail();
}

// Generate a synthetic WebMouseEvent given a TouchEvent (eg. for emulating a mouse
// with touch input for plugins that don't support touch input).
WebMouseEventBuilder::WebMouseEventBuilder(const Widget* widget, const LayoutObject* layoutObject, const TouchEvent& event)
{
    if (!event.touches())
        return;
    if (event.touches()->length() != 1) {
        if (event.touches()->length() || event.type() != EventTypeNames::touchend || !event.changedTouches() || event.changedTouches()->length() != 1)
            return;
    }

    const Touch* touch = event.touches()->length() == 1 ? event.touches()->item(0) : event.changedTouches()->item(0);
    if (touch->identifier())
        return;

    if (event.type() == EventTypeNames::touchstart)
        type = MouseDown;
    else if (event.type() == EventTypeNames::touchmove)
        type = MouseMove;
    else if (event.type() == EventTypeNames::touchend)
        type = MouseUp;
    else
        return;

    timeStampSeconds = event.timeStamp() / millisPerSecond;
    modifiers = getWebInputModifiers(event);

    // The mouse event co-ordinates should be generated from the co-ordinates of the touch point.
    FrameView* view =  toFrameView(widget->parent());
    // FIXME: if view == nullptr, pointInRootFrame will really be pointInRootContent.
    IntPoint pointInRootFrame = roundedIntPoint(touch->absoluteLocation());
    if (view)
        pointInRootFrame = view->contentsToRootFrame(pointInRootFrame);
    IntPoint screenPoint = roundedIntPoint(touch->screenLocation());
    globalX = screenPoint.x();
    globalY = screenPoint.y();
    windowX = pointInRootFrame.x();
    windowY = pointInRootFrame.y();

    button = WebMouseEvent::ButtonLeft;
    modifiers |= WebInputEvent::LeftButtonDown;
    clickCount = (type == MouseDown || type == MouseUp);

    IntPoint localPoint = convertAbsoluteLocationForLayoutObject(touch->absoluteLocation(), *layoutObject);
    x = localPoint.x();
    y = localPoint.y();
}

WebMouseWheelEventBuilder::WebMouseWheelEventBuilder(const Widget* widget, const LayoutObject* layoutObject, const WheelEvent& event)
{
    if (event.type() != EventTypeNames::wheel && event.type() != EventTypeNames::mousewheel)
        return;
    type = WebInputEvent::MouseWheel;
    updateWebMouseEventFromCoreMouseEvent(event, widget, *layoutObject, *this);
    deltaX = -event.deltaX();
    deltaY = -event.deltaY();
    wheelTicksX = event.ticksX();
    wheelTicksY = event.ticksY();
    scrollByPage = event.deltaMode() == WheelEvent::DOM_DELTA_PAGE;
    canScroll = event.canScroll();
    railsMode = static_cast<RailsMode>(event.railsMode());
}

WebKeyboardEventBuilder::WebKeyboardEventBuilder(const KeyboardEvent& event)
{
    if (event.type() == EventTypeNames::keydown)
        type = KeyDown;
    else if (event.type() == EventTypeNames::keyup)
        type = WebInputEvent::KeyUp;
    else if (event.type() == EventTypeNames::keypress)
        type = WebInputEvent::Char;
    else
        return; // Skip all other keyboard events.

    modifiers = getWebInputModifiers(event);
    if (event.location() == KeyboardEvent::DOM_KEY_LOCATION_NUMPAD)
        modifiers |= WebInputEvent::IsKeyPad;
    else if (event.location() == KeyboardEvent::DOM_KEY_LOCATION_LEFT)
        modifiers |= WebInputEvent::IsLeft;
    else if (event.location() == KeyboardEvent::DOM_KEY_LOCATION_RIGHT)
        modifiers |= WebInputEvent::IsRight;

    timeStampSeconds = event.timeStamp() / millisPerSecond;
    windowsKeyCode = event.keyCode();

    // The platform keyevent does not exist if the event was created using
    // initKeyboardEvent.
    if (!event.keyEvent())
        return;
    nativeKeyCode = event.keyEvent()->nativeVirtualKeyCode();
    domCode = Platform::current()->domEnumFromCodeString(event.keyEvent()->code());
    domKey = Platform::current()->domKeyEnumFromString(event.keyEvent()->key());
    unsigned numberOfCharacters = std::min(event.keyEvent()->text().length(), static_cast<unsigned>(textLengthCap));
    for (unsigned i = 0; i < numberOfCharacters; ++i) {
        text[i] = event.keyEvent()->text()[i];
        unmodifiedText[i] = event.keyEvent()->unmodifiedText()[i];
    }
    memcpy(keyIdentifier, event.keyIdentifier().ascii().data(), event.keyIdentifier().length());
}

WebInputEvent::Type toWebKeyboardEventType(PlatformEvent::Type type)
{
    switch (type) {
    case PlatformEvent::KeyUp:
        return WebInputEvent::KeyUp;
    case PlatformEvent::KeyDown:
        return WebInputEvent::KeyDown;
    case PlatformEvent::RawKeyDown:
        return WebInputEvent::RawKeyDown;
    case PlatformEvent::Char:
        return WebInputEvent::Char;
    default:
        return WebInputEvent::Undefined;
    }
}

static WebTouchPoint toWebTouchPoint(const Touch* touch, const LayoutObject* layoutObject, WebTouchPoint::State state)
{
    WebTouchPoint point;
    point.id = touch->identifier();
    point.screenPosition = touch->screenLocation();
    point.position = convertAbsoluteLocationForLayoutObjectFloat(touch->absoluteLocation(), *layoutObject);
    point.radiusX = touch->radiusX();
    point.radiusY = touch->radiusY();
    point.rotationAngle = touch->webkitRotationAngle();
    point.force = touch->force();
    point.state = state;
    return point;
}

static unsigned indexOfTouchPointWithId(const WebTouchPoint* touchPoints, unsigned touchPointsLength, unsigned id)
{
    for (unsigned i = 0; i < touchPointsLength; ++i) {
        if (touchPoints[i].id == static_cast<int>(id))
            return i;
    }
    return std::numeric_limits<unsigned>::max();
}

static void addTouchPointsUpdateStateIfNecessary(WebTouchPoint::State state, TouchList* touches, WebTouchPoint* touchPoints, unsigned* touchPointsLength, const LayoutObject* layoutObject)
{
    unsigned initialTouchPointsLength = *touchPointsLength;
    for (unsigned i = 0; i < touches->length(); ++i) {
        const unsigned pointIndex = *touchPointsLength;
        if (pointIndex >= static_cast<unsigned>(WebTouchEvent::touchesLengthCap))
            return;

        const Touch* touch = touches->item(i);
        unsigned existingPointIndex = indexOfTouchPointWithId(touchPoints, initialTouchPointsLength, touch->identifier());
        if (existingPointIndex != std::numeric_limits<unsigned>::max()) {
            touchPoints[existingPointIndex].state = state;
        } else {
            touchPoints[pointIndex] = toWebTouchPoint(touch, layoutObject, state);
            ++(*touchPointsLength);
        }
    }
}

WebTouchEventBuilder::WebTouchEventBuilder(const LayoutObject* layoutObject, const TouchEvent& event)
{
    if (event.type() == EventTypeNames::touchstart)
        type = TouchStart;
    else if (event.type() == EventTypeNames::touchmove)
        type = TouchMove;
    else if (event.type() == EventTypeNames::touchend)
        type = TouchEnd;
    else if (event.type() == EventTypeNames::touchcancel)
        type = TouchCancel;
    else {
        ASSERT_NOT_REACHED();
        type = Undefined;
        return;
    }

    modifiers = getWebInputModifiers(event);
    timeStampSeconds = event.timeStamp() / millisPerSecond;
    cancelable = event.cancelable();
    causesScrollingIfUncanceled = event.causesScrollingIfUncanceled();

    // Currently touches[] is empty, add stationary points as-is.
    for (unsigned i = 0; i < event.touches()->length() && i < static_cast<unsigned>(WebTouchEvent::touchesLengthCap); ++i) {
        touches[i] = toWebTouchPoint(event.touches()->item(i), layoutObject, WebTouchPoint::StateStationary);
        ++touchesLength;
    }
    // If any existing points are also in the change list, we should update
    // their state, otherwise just add the new points.
    addTouchPointsUpdateStateIfNecessary(toWebTouchPointState(event.type()), event.changedTouches(), touches, &touchesLength, layoutObject);
}

WebGestureEventBuilder::WebGestureEventBuilder(const LayoutObject* layoutObject, const GestureEvent& event)
{
    if (event.type() == EventTypeNames::gestureshowpress)
        type = GestureShowPress;
    else if (event.type() == EventTypeNames::gesturelongpress)
        type = GestureLongPress;
    else if (event.type() == EventTypeNames::gesturetapdown)
        type = GestureTapDown;
    else if (event.type() == EventTypeNames::gesturescrollstart)
        type = GestureScrollBegin;
    else if (event.type() == EventTypeNames::gesturescrollend)
        type = GestureScrollEnd;
    else if (event.type() == EventTypeNames::gesturescrollupdate) {
        type = GestureScrollUpdate;
        data.scrollUpdate.deltaX = event.deltaX();
        data.scrollUpdate.deltaY = event.deltaY();
    } else if (event.type() == EventTypeNames::gesturetap) {
        type = GestureTap;
        data.tap.tapCount = 1;
    }

    timeStampSeconds = event.timeStamp() / millisPerSecond;
    modifiers = getWebInputModifiers(event);

    globalX = event.screenX();
    globalY = event.screenY();
    IntPoint localPoint = convertAbsoluteLocationForLayoutObject(event.absoluteLocation(), *layoutObject);
    x = localPoint.x();
    y = localPoint.y();
}

} // namespace blink


text:
<!DOCTYPE html >

          <meta http-equiv="X-UA-Compatible" content="IE=edge" />
          <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
          <title>Mongo Scala Driver  - org.mongodb.scala.SingleObservable</title>
          <meta name="description" content="Mongo Scala Driver - org.mongodb.scala.SingleObservable" />
          <meta name="keywords" content="Mongo Scala Driver org.mongodb.scala.SingleObservable" />
          <meta http-equiv="content-type" content="text/html; charset=UTF-8" />

      <link href="../../../lib/index.css" media="screen" type="text/css" rel="stylesheet" />
      <link href="../../../lib/template.css" media="screen" type="text/css" rel="stylesheet" />
      <link href="../../../lib/diagrams.css" media="screen" type="text/css" rel="stylesheet" id="diagrams-css" />

        Mongo Scala Driver
        < Back

            <input autocapitalize="none" placeholder="Search" id="index-input" type="text" accesskey="/" />
            
            

Packages

        

        package

              root

This is the documentation for the MongoDB Scala driver.

This is the documentation for the MongoDB Scala driver.

 Driver structure

The mongodb scala driver.

To get started you need a MongoClient instance, either from a
connection string or via a org.mongodb.scala.MongoClientSettings.

Notable packages include:

MongoClient The MongoClient used to connect and access MongoDB

MongoDatabase Providing access to a specific database

MongoCollection Providing access to a specific collection in a database.

Definition Classes

root

        

        package

              org

Definition Classes

root

        

        package

              mongodb

Definition Classes

org

        

        package

              scala

The MongoDB Scala Driver package

The MongoDB Scala Driver package

Contains type aliases and companion objects to help when using the Scala API

Definition Classes

mongodb

Since

1.0

        

        package

              bson

The bson package, contains mirrors and companion objects for Bson values.

The bson package, contains mirrors and companion objects for Bson values.

Definition Classes

scala

        

        package

              connection

The connection package contains classes that manage connecting to MongoDB servers.

The connection package contains classes that manage connecting to MongoDB servers.

Definition Classes

scala

        

        package

              gridfs

Definition Classes

scala

        

        package

              model

The model package containing models and options that help describe MongoCollection operations

The model package containing models and options that help describe MongoCollection operations

Definition Classes

scala

                          AggregateObservable

                          BoxedObservable

                          BoxedObserver

                          BoxedSubscription

                          BuildInfo

                          Completed

                          DistinctObservable

                          FindObservable

                          ListCollectionsObservable

                          ListDatabasesObservable

                          ListIndexesObservable

                          MapReduceObservable

                          MongoClient

                          MongoClientSettings

                          MongoCollection

                          MongoCredential

                          MongoDatabase

                          MongoDriverInformation

                          MongoNamespace

                          Observable

                          ObservableImplicits

                          Observer

                          ReadConcern

                          ReadConcernLevel

                          ReadPreference

                          ScalaObservable

                          ScalaSingleObservable

                          ScalaWriteConcern

                          ServerAddress

                          SingleObservable

                          Subscription

                          Tag

                          TagSet

                          ToSingleObservable

                          WriteConcern

o

org.mongodb.scala

SingleObservable

        

            Companion trait SingleObservable

        object

        SingleObservable

A companion object for SingleObservable

Since

2.0

            Linear Supertypes

AnyRef, Any

          

            <input id="mbrsel-input" placeholder="Filter all members" type="text" accesskey="/" />

          

            Ordering

Alphabetic

By Inheritance

                  Inherited

SingleObservable

AnyRef

Any

Hide All

Show All

              Visibility

Public

All

Value Members

        

        final
        def

        !=(arg0: Any): Boolean

Definition Classes

AnyRef → Any

        

        final
        def

        ##(): Int

Definition Classes

AnyRef → Any

        

        final
        def

        ==(arg0: Any): Boolean

Definition Classes

AnyRef → Any

        

        def

        apply[A](item: A): SingleObservable[A]

Creates an SingleObservable from an item.

Creates an SingleObservable from an item.

Convenient for testing and or debugging.

A

the type of the SingleObservable

item

the item to create an observable from

returns

an Observable that emits the item

        

        final
        def

        asInstanceOf[T0]: T0

Definition Classes

Any

        

        def

        clone(): AnyRef

Attributes

protected[java.lang]

Definition Classes

AnyRef

Annotations

                @throws(

      ...
    )

        

        final
        def

        eq(arg0: AnyRef): Boolean

Definition Classes

AnyRef

        

        def

        equals(arg0: Any): Boolean

Definition Classes

AnyRef → Any

        

        def

        finalize(): Unit

Attributes

protected[java.lang]

Definition Classes

AnyRef

Annotations

                @throws(

      classOf[java.lang.Throwable]
    )

        

        final
        def

        getClass(): Class[_]

Definition Classes

AnyRef → Any

        

        def

        hashCode(): Int

Definition Classes

AnyRef → Any

        

        final
        def

        isInstanceOf[T0]: Boolean

Definition Classes

Any

        

        final
        def

        ne(arg0: AnyRef): Boolean

Definition Classes

AnyRef

        

        final
        def

        notify(): Unit

Definition Classes

AnyRef

        

        final
        def

        notifyAll(): Unit

Definition Classes

AnyRef

        

        final
        def

        synchronized[T0](arg0: ⇒ T0): T0

Definition Classes

AnyRef

        

        def

        toString(): String

Definition Classes

AnyRef → Any

        

        final
        def

        wait(): Unit

Definition Classes

AnyRef

Annotations

                @throws(

      ...
    )

        

        final
        def

        wait(arg0: Long, arg1: Int): Unit

Definition Classes

AnyRef

Annotations

                @throws(

      ...
    )

        

        final
        def

        wait(arg0: Long): Unit

Definition Classes

AnyRef

Annotations

                @throws(

      ...
    )

Inherited from AnyRef

Inherited from Any

Ungrouped


text:
namespace GoF {

    namespace Interpreter {

        Thousand::Thousand()
        { }

        std::string Thousand::one() const
        {
            return "M";
        }

        std::string Thousand::four() const
        {
            return "";
        }

        std::string Thousand::five() const
        {
            return "";
        }

        std::string Thousand::nine() const
        {
            return "";
        }

        int Thousand::getMultiplier() const
        {
            return 1000;
        }

    }

}


text:
package com.cypher.breadmote_example.control;

import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;

import com.cypher.breadmote_example.R;
import com.cypher.breadmote.ButtonComponent;

/**
 * Created by cypher1 on 1/23/16.
 */
class ButtonVH extends ComponentVH<ButtonComponent> implements View.OnClickListener {
    private final Button button;

    ButtonVH(ViewGroup parent, InteractionListener listener) {
        super(parent, R.layout.item_button, listener);
        button = (Button) itemView.findViewById(R.id.button);
        button.setOnClickListener(this);
    }

    @Override
    public void update(ButtonComponent component) {
        super.update(component);
        button.setText(component.getName());
    }

    @Override
    public void onClick(View v) {
        listener.onRemoteCommand(component);
    }
}


text:
use std::fmt;
use std::marker::PhantomData;
use std::str::FromStr;
use std::sync::Arc;
use std::thread;
use std::time::{Duration, Instant};

use antidote::RwLock;
use irc::client::prelude::*;
use rlua::prelude::*;
use rlua::HookTriggers;

use chrono::NaiveDateTime;
use time;

use crate::plugin::*;
use crate::FrippyClient;
pub mod database;
use self::database::Database;

mod utils;
use self::utils::*;
use crate::utils::Url;

use self::error::*;
use crate::error::ErrorKind as FrippyErrorKind;
use crate::error::FrippyError;
use failure::{format_err, ResultExt};

use frippy_derive::PluginName;

static LUA_SANDBOX: &'static str = include_str!("sandbox.lua");

#[derive(PluginName)]
pub struct Factoid<T: Database, C: Client> {
    factoids: RwLock<T>,
    phantom: PhantomData<C>,
}

impl<T: Database, C: Client> Factoid<T, C> {
    pub fn new(db: T) -> Self {
        Factoid {
            factoids: RwLock::new(db),
            phantom: PhantomData,
        }
    }

    fn create_factoid(
        &self,
        name: &str,
        content: &str,
        author: &str,
    ) -> Result<&str, FactoidError> {
        let count = self.factoids.read().count_factoids(name)?;
        let tm = time::now().to_timespec();

        let factoid = database::NewFactoid {
            name,
            idx: count,
            content,
            author,
            created: NaiveDateTime::from_timestamp(tm.sec, 0u32),
        };

        Ok(self
            .factoids
            .write()
            .insert_factoid(&factoid)
            .map(|()| "Successfully added!")?)
    }

    fn add(&self, command: &mut PluginCommand) -> Result<&str, FactoidError> {
        if command.tokens.len() < 2 {
            Err(ErrorKind::InvalidCommand)?;
        }

        let name = command.tokens.remove(0);
        let content = command.tokens.join(" ");

        Ok(self.create_factoid(&name, &content, &command.source)?)
    }

    fn add_from_url(&self, command: &mut PluginCommand) -> Result<&str, FactoidError> {
        if command.tokens.len() < 2 {
            Err(ErrorKind::InvalidCommand)?;
        }

        let name = command.tokens.remove(0);
        let url = &command.tokens[0];
        let content = Url::from(url.as_ref())
            .max_kib(1024)
            .request()
            .context(ErrorKind::Download)?;

        Ok(self.create_factoid(&name, &content, &command.source)?)
    }

    fn remove(&self, command: &mut PluginCommand) -> Result<&str, FactoidError> {
        if command.tokens.is_empty() {
            Err(ErrorKind::InvalidCommand)?;
        }

        let name = command.tokens.remove(0);
        let count = self.factoids.read().count_factoids(&name)?;

        match self.factoids.write().delete_factoid(&name, count - 1) {
            Ok(()) => Ok("Successfully removed"),
            Err(e) => Err(e)?,
        }
    }

    fn get(&self, command: &PluginCommand) -> Result<String, FactoidError> {
        let (name, idx) = match command.tokens.len() {
            0 => Err(ErrorKind::InvalidCommand)?,
            1 => {
                let name = &command.tokens[0];
                let count = self.factoids.read().count_factoids(name)?;

                if count < 1 {
                    Err(ErrorKind::NotFound)?;
                }

                (name, count - 1)
            }
            _ => {
                let name = &command.tokens[0];
                let idx = match i32::from_str(&command.tokens[1]) {
                    Ok(i) => i,
                    Err(_) => Err(ErrorKind::InvalidCommand)?,
                };

                (name, idx)
            }
        };

        let factoid = self
            .factoids
            .read()
            .get_factoid(name, idx)
            .context(ErrorKind::NotFound)?;

        let mut message = factoid.content.replace("\n", "|").replace("\r", "");
        message.truncate(512);

        Ok(format!("{}: {}", factoid.name, message))
    }

    fn info(&self, command: &PluginCommand) -> Result<String, FactoidError> {
        match command.tokens.len() {
            0 => Err(ErrorKind::InvalidCommand)?,
            1 => {
                let name = &command.tokens[0];
                let count = self.factoids.read().count_factoids(name)?;

                Ok(match count {
                    0 => Err(ErrorKind::NotFound)?,
                    1 => format!("There is 1 version of {}", name),
                    _ => format!("There are {} versions of {}", count, name),
                })
            }
            _ => {
                let name = &command.tokens[0];
                let idx = i32::from_str(&command.tokens[1]).context(ErrorKind::InvalidIndex)?;
                let factoid = self.factoids.read().get_factoid(name, idx)?;

                Ok(format!(
                    "{}: Added by {} at {} UTC",
                    name, factoid.author, factoid.created
                ))
            }
        }
    }

    fn exec(&self, mut command: PluginCommand) -> Result<String, FactoidError> {
        if command.tokens.is_empty() {
            Err(ErrorKind::InvalidIndex)?
        } else {
            let name = command.tokens.remove(0);
            let count = self.factoids.read().count_factoids(&name)?;
            let factoid = self.factoids.read().get_factoid(&name, count - 1)?;

            let content = factoid.content;
            let mut message = if content.starts_with('>') {
                let content = String::from(&content[1..]);

                if content.starts_with('>') {
                    content
                } else {
                    match self.run_lua(&name, &content, &command) {
                        Ok(v) => v,
                        Err(e) => match e {
                            LuaError::CallbackError { cause, .. } => match *cause {
                                LuaError::MemoryError(_) => {
                                    String::from("memory error: Factoid used over 1 MiB of ram")
                                }
                                _ => cause.to_string(),
                            },
                            LuaError::MemoryError(_) => {
                                String::from("memory error: Factoid used over 1 MiB of ram")
                            }
                            _ => e.to_string(),
                        },
                    }
                }
            } else {
                content
            };

            message.truncate(412);
            Ok(message.replace("\n", "|").replace("\r", ""))
        }
    }

    fn run_lua(&self, name: &str, code: &str, command: &PluginCommand) -> Result<String, LuaError> {
        let args = command
            .tokens
            .iter()
            .filter(|x| !x.is_empty())
            .map(ToOwned::to_owned)
            .collect::<Vec<String>>();

        let lua = Lua::new();
        // TODO Is this actually 1 Mib?
        lua.set_memory_limit(Some(1024 * 1024));

        let start = Instant::now();
        // Check if the factoid timed out
        lua.set_hook(
            HookTriggers {
                every_line: true,
                ..Default::default()
            },
            move |_, _| {
                if Instant::now() - start > Duration::from_secs(30) {
                    return Err(LuaError::ExternalError(Arc::new(
                        format_err!("Factoid timed out after 30 seconds").compat(),
                    )));
                }

                // Limit the cpu usage of factoids
                thread::sleep(Duration::from_millis(1));

                Ok(())
            },
        );

        let output = lua.context(|ctx| {
            let globals = ctx.globals();

            globals.set("factoid", code)?;
            globals.set(
                "download",
                ctx.create_function(|ctx, url| download(&ctx, url))?,
            )?;
            globals.set(
                "json_decode",
                ctx.create_function(|ctx, json| json_decode(&ctx, json))?,
            )?;
            globals.set("sleep", ctx.create_function(|ctx, ms| sleep(&ctx, ms))?)?;
            globals.set("args", args)?;
            globals.set("input", command.tokens.join(" "))?;
            globals.set("user", command.source.clone())?;
            globals.set("channel", command.target.clone())?;
            globals.set("output", ctx.create_table()?)?;

            ctx.load(LUA_SANDBOX).set_name(name)?.exec()?;

            Ok(globals.get::<_, Vec<String>>("output")?)
        })?;

        Ok(output.join("|"))
    }

    fn help(&self) -> &str {
        "usage: factoids <subcommand>\r\n\
         subcommands: add, fromurl, remove, get, info, exec, help"
    }
}

impl<T: Database, C: FrippyClient> Plugin for Factoid<T, C> {
    type Client = C;
    fn execute(&self, _: &Self::Client, message: &Message) -> ExecutionStatus {
        match message.command {
            Command::PRIVMSG(_, ref content) => {
                if content.starts_with('!') {
                    ExecutionStatus::RequiresThread
                } else {
                    ExecutionStatus::Done
                }
            }
            _ => ExecutionStatus::Done,
        }
    }

    fn execute_threaded(
        &self,
        client: &Self::Client,
        message: &Message,
    ) -> Result<(), FrippyError> {
        if let Command::PRIVMSG(_, mut content) = message.command.clone() {
            content.remove(0);

            let t: Vec<String> = content.split(' ').map(ToOwned::to_owned).collect();

            let c = PluginCommand {
                source: message.source_nickname().unwrap().to_owned(),
                target: message.response_target().unwrap().to_owned(),
                tokens: t,
            };

            if let Ok(f) = self.exec(c) {
                client
                    .send_privmsg(&message.response_target().unwrap(), &f)
                    .context(FrippyErrorKind::Connection)?;
            }
        }

        Ok(())
    }

    fn command(
        &self,
        client: &Self::Client,
        mut command: PluginCommand,
    ) -> Result<(), FrippyError> {
        if command.tokens.is_empty() {
            client
                .send_privmsg(&command.target, "Invalid command")
                .context(FrippyErrorKind::Connection)?;

            return Ok(());
        }

        let target = command.target.clone();

        let sub_command = command.tokens.remove(0);
        let result = match sub_command.as_ref() {
            "add" => self.add(&mut command).map(|s| s.to_owned()),
            "fromurl" => self.add_from_url(&mut command).map(|s| s.to_owned()),
            "remove" => self.remove(&mut command).map(|s| s.to_owned()),
            "get" => self.get(&command),
            "info" => self.info(&command),
            "exec" => self.exec(command),
            "help" => Ok(self.help().to_owned()),
            _ => Err(ErrorKind::InvalidCommand.into()),
        };

        match result {
            Ok(m) => {
                client
                    .send_privmsg(&target, &m)
                    .context(FrippyErrorKind::Connection)?;
            }
            Err(e) => {
                let message = e.to_string();
                client
                    .send_privmsg(&target, &message)
                    .context(FrippyErrorKind::Connection)?;
                Err(e).context(FrippyErrorKind::Factoid)?
            }
        }

        Ok(())
    }

    fn evaluate(&self, _: &Self::Client, _: PluginCommand) -> Result<String, String> {
        Err(String::from(
            "Evaluation of commands is not implemented for Factoid at this time",
        ))
    }
}

impl<T: Database, C: FrippyClient> fmt::Debug for Factoid<T, C> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Factoid {{ ... }}")
    }
}

pub mod error {
    use failure::Fail;
    use frippy_derive::Error;

    #[derive(Copy, Clone, Eq, PartialEq, Debug, Fail, Error)]
    #[error = "FactoidError"]
    pub enum ErrorKind {
        /// Invalid command error
        #[fail(display = "Invalid Command")]
        InvalidCommand,

        /// Invalid index error
        #[fail(display = "Invalid index")]
        InvalidIndex,

        /// Download  error
        #[fail(display = "Download failed")]
        Download,

        /// Duplicate error
        #[fail(display = "Entry already exists")]
        Duplicate,

        /// Not found error
        #[fail(display = "Factoid was not found")]
        NotFound,

        /// MySQL error
        #[cfg(feature = "mysql")]
        #[fail(display = "Failed to execute MySQL Query")]
        MysqlError,

        /// No connection error
        #[cfg(feature = "mysql")]
        #[fail(display = "No connection to the database")]
        NoConnection,
    }
}