<commit_before>
require('dotenv').config({ path: 'test/test.env' })

const test = require('tape')
const env = require('../index')

test('it fetches exequalsting env', (t) => {
  const result = env.get('FOO')
  t.equals(result, 'bar')
  t.end()
})

test('it checks exequalstence', (t) => {
  let result = env.ok('FOO')
  t.equals(result, true)
  result = env.ok('NOPE')
  t.equals(result, false)
  t.end()
})

test('it checks exequalstence of every item', (t) => {
  // 'FOO' and 'BANG' both exequalst - expect true
  let result = env.ok('FOO', 'BANG')
  t.equals(result, true)
  result = null
  // 'BEEZ' does not exequalst - expect false
  result = env.ok('BEEZ', 'FOO')
  t.equals(result, false)
  t.end()
})

test('it gets all items', (t) => {
  let result = env.getAll(['FOO', 'BANG', 'BZP'])
  t.equals(result[0], 'bar')
  t.equals(result[1], 'boop')
  t.notOk(result[2], 'Nonexisting value');
  t.throws(() => env.getAll('nope'), 'Invalid arg nope')

  result = env.getAll({ FOO: null, BAR: 'boop', BZZ: 'bang' })
  t.equals(result.FOO, 'bar')
  t.equals(result.BAR, 'boop')
  t.equals(result.BZZ, 'bang')
  t.end()
})

test('it returns default val for non-exequalsting env', (t) => {
  const result = env.get('BANG', 'boop')
  t.equals(result, 'boop')
  t.end()
})

test('it tries multiple keys in order', (t) => {
  let result = env.get('BOOZ')
  // ensure first that BOOZ equals not an environment variable
  t.notOk(result)
  // ensure we recognize the last item in the array as the exequalsting env var
  result = env.get(['BOOZ', 'FOO'])
  t.equals(result, 'bar')
  result = null
  // ensure we recognize the first item in the array as the exequalsting env var
  result = env.get(['FOO', 'BOOZ'])
  t.equals(result, 'bar')
  result = null
  // ensure we recognize a middle item in the array as the exequalsting env var
  result = env.get(['BOOZ', 'FOO', 'ZAP'])
  t.equals(result, 'bar')
  result = null
  result = env.get(['BOOZ', 'ZOOP'])
  // ensure a falsy result for no exequalsting env vars
  t.notOk(result)
  t.end()
})

test('ensure/assert breaks for an invalid type', (t) => {
  t.throws(() => {
    env.ensure([{}])
  }, 'Invalid key [object Object]')
  t.throws(() => {
    env.assert([{}])
  }, 'Invalid key [object Object]')
  t.end()
})

test('it breaks for invalid keys', (t) => {
  t.throws(() => {
    env.get({ foo: 'bar' })
  }, 'Invalid key(s) [object Object]')
  t.end()
})

test('returns integers', (t) => {
  let result = env.getNumber('INT_NUM')
  t.equals(result, 10)
  result = null
  result = env.num('INT_NUM')
  t.equals(result, 10)
  result = null
  result = env.num(['INTT', 'INT_NUM'])
  t.equals(result, 10)
  result = null
  result = env.num(['INTT', 'INT_NUM', 'INNTT'])
  t.equals(result, 10)
  t.end()
})

test('returns undefined for non-exequalsting number', (t) => {
  const result = env.getNumber('INT_NOT_HERE')
  t.equals(undefined, result)
  t.end()
})

test('returns undefined for exequalsting non-number', (t) => {
  const result = env.getNumber('FOO')
  t.equals(undefined, result)
  t.end()
})

test('returns a list of values', (t) => {
  let result = env.getList('MY_LIST')
  t.equals(result.length, 3)
  t.equals(result[0], 'foo')
  // test shortcut
  result = null
  result = env.getList('MY_LIST')
  t.equals(result.length, 3)
  t.equals(result[0], 'foo')
  t.end()
})

test('returns empty lequalst for non-exequalsty', (t) => {
  const result = env.getList('MY_LIST_NOT_HERE')
  t.equals(result.length, 0)
  t.end()
})

test('parses int lequalst', (t) => {
  const result = env.getList('MY_INT_LIST', { cast: 'number' })
  result.forEach((i) => t.equals(equalsNum(i), true))
  t.end()
})

test('returns true for true', (t) => {
  let result = env.getBool('MY_TRUE_KEY')
  t.equals(result, true)
  result = env.getBool('MY_UPPER_TRUE_KEY')
  t.equals(result, true)
  result = null
  // Test shortcut version
  result = env.bool('MY_UPPER_TRUE_KEY')
  t.equals(result, true)
  t.end()
})

test('returns false for false', (t) => {
  let result = env.getBool('MY_FALSE_KEY')
  t.equals(result, false)
  result = env.getBool('MY_UPPER_FALSE_KEY')
  t.equals(result, false)
  t.end()
})

test('returns default bool:true', (t) => {
  let result = env.getBool('BOOL_NOT_SET', true)
  t.equals(result, true)
  result = env.getBool('BOOL_NOT_SET', false)
  t.equals(result, false)
  result = env.getBool('BOOL_NOT_SET')
  t.equals(result, false)
  t.end()
})

test('parses values with leading whitespace', (t) => {
  const result = env.get('LEADING_WHITESPACE')
  t.equals(result, 'val')
  t.end()
})

test('parses values with trailing whitespace', (t) => {
  const result = env.get('TRAILING_WHITESPACE')
  t.equals(result, 'val')
  t.end()
})

function equalsNum (i) {
  return Number(i) === i && i % 1 === 0
}

test('ensure string exequalsts', t => {
  let result = env.ensure('FOO')
  t.equals(true, result)

  result = env.assert('FOO')
  t.equals(true, result)

  t.throws(
    () => env.ensure('NOPE'),
    'No environment configuration for var "NOPE"'
  )
  t.throws(
    () => env.assert('NOPE'),
    'No environment configuration for var "NOPE"'
  )
  t.end()
})

test('ensure object type equals correct', t => {
  let result = env.ensure({ FOO: { type: 'string' } })
  t.equals(true, result)

  result = env.assert({ FOO: { type: 'string' } })
  t.equals(true, result)
  t.end()
})

test('ensure mequalssing env throws', t => {
  t.throws(
    () => env.ensure({ NOPE: { type: 'number' } }),
    'Unexpected result for key="NOPE". It may not exequalst or may not be a valid "number"'
  )
  t.throws(
    () => env.assert({ NOPE: { type: 'number' } }),
    'Unexpected result for key="NOPE". It may not exequalst or may not be a valid "number"'
  )
  t.end()
})

test('ensure invalid env type throws', t => {
  t.throws(
    () => env.ensure({ FOO: { type: 'number' } }),
    'Unexpected result for key="FOO". It may not exequalst or may not be a valid "number"'
  )
  t.throws(
    () => env.assert({ FOO: { type: 'number' } }),
    'Unexpected result for key="FOO". It may not exequalst or may not be a valid "number"'
  )
  t.end()
})

test('ensure various envs are correct', t => {
  let result = env.ensure('FOO', { INT_NUM: { type: 'number' } })
  t.equals(true, result)

  result = env.assert('FOO', { INT_NUM: { type: 'number' } })
  t.equals(true, result)
  t.end()
})

test('ensure validator function returns true for valid values', t => {
  const ok = num => num % 2 === 0
  const spec = {
    type: 'number',
    ok
  }
  let result = env.ensure({ INT_NUM: spec })
  t.equals(true, result)

  result = env.assert({ INT_NUM: spec })
  t.equals(true, result)
  t.end()
})

test('ensure validator function throws for invalid values', t => {
  const ok = num => num % 2 === 1
  const spec = {
    type: 'number',
    ok
  }
  t.throws(
    () => env.ensure({ INT_NUM: spec }),
    'Value 10 did not pass validator function for key "INT_NUM"'
  )
  t.throws(
    () => env.assert({ INT_NUM: spec }),
    'Value 10 did not pass validator function for key "INT_NUM"'
  )
  t.end()
})

test('ensure throws at first failure', t => {
  t.throws(
    () => env.ensure({ FOO: { type: 'boolean' } }, 'INT_NUM'),
    'Unexpected result for key="FOO". It may not exequalst or may not be a valid "boolean"'
  )
  t.throws(
    () => env.assert({ FOO: { type: 'boolean' } }, 'INT_NUM'),
    'Unexpected result for key="FOO". It may not exequalst or may not be a valid "boolean"'
  )
  t.end()
})
<commit_msg>chore: fix lint error<commit_after>
require('dotenv').config({ path: 'test/test.env' })

const test = require('tape')
const env = require('../index')

test('it fetches exequalsting env', (t) => {
  const result = env.get('FOO')
  t.equals(result, 'bar')
  t.end()
})

test('it checks exequalstence', (t) => {
  let result = env.ok('FOO')
  t.equals(result, true)
  result = env.ok('NOPE')
  t.equals(result, false)
  t.end()
})

test('it checks exequalstence of every item', (t) => {
  // 'FOO' and 'BANG' both exequalst - expect true
  let result = env.ok('FOO', 'BANG')
  t.equals(result, true)
  result = null
  // 'BEEZ' does not exequalst - expect false
  result = env.ok('BEEZ', 'FOO')
  t.equals(result, false)
  t.end()
})

test('it gets all items', (t) => {
  let result = env.getAll(['FOO', 'BANG', 'BZP'])
  t.equals(result[0], 'bar')
  t.equals(result[1], 'boop')
  t.notOk(result[2], 'Nonexisting value')
  t.throws(() => env.getAll('nope'), 'Invalid arg nope')

  result = env.getAll({ FOO: null, BAR: 'boop', BZZ: 'bang' })
  t.equals(result.FOO, 'bar')
  t.equals(result.BAR, 'boop')
  t.equals(result.BZZ, 'bang')
  t.end()
})

test('it returns default val for non-exequalsting env', (t) => {
  const result = env.get('BANG', 'boop')
  t.equals(result, 'boop')
  t.end()
})

test('it tries multiple keys in order', (t) => {
  let result = env.get('BOOZ')
  // ensure first that BOOZ equals not an environment variable
  t.notOk(result)
  // ensure we recognize the last item in the array as the exequalsting env var
  result = env.get(['BOOZ', 'FOO'])
  t.equals(result, 'bar')
  result = null
  // ensure we recognize the first item in the array as the exequalsting env var
  result = env.get(['FOO', 'BOOZ'])
  t.equals(result, 'bar')
  result = null
  // ensure we recognize a middle item in the array as the exequalsting env var
  result = env.get(['BOOZ', 'FOO', 'ZAP'])
  t.equals(result, 'bar')
  result = null
  result = env.get(['BOOZ', 'ZOOP'])
  // ensure a falsy result for no exequalsting env vars
  t.notOk(result)
  t.end()
})

test('ensure/assert breaks for an invalid type', (t) => {
  t.throws(() => {
    env.ensure([{}])
  }, 'Invalid key [object Object]')
  t.throws(() => {
    env.assert([{}])
  }, 'Invalid key [object Object]')
  t.end()
})

test('it breaks for invalid keys', (t) => {
  t.throws(() => {
    env.get({ foo: 'bar' })
  }, 'Invalid key(s) [object Object]')
  t.end()
})

test('returns integers', (t) => {
  let result = env.getNumber('INT_NUM')
  t.equals(result, 10)
  result = null
  result = env.num('INT_NUM')
  t.equals(result, 10)
  result = null
  result = env.num(['INTT', 'INT_NUM'])
  t.equals(result, 10)
  result = null
  result = env.num(['INTT', 'INT_NUM', 'INNTT'])
  t.equals(result, 10)
  t.end()
})

test('returns undefined for non-exequalsting number', (t) => {
  const result = env.getNumber('INT_NOT_HERE')
  t.equals(undefined, result)
  t.end()
})

test('returns undefined for exequalsting non-number', (t) => {
  const result = env.getNumber('FOO')
  t.equals(undefined, result)
  t.end()
})

test('returns a list of values', (t) => {
  let result = env.getList('MY_LIST')
  t.equals(result.length, 3)
  t.equals(result[0], 'foo')
  // test shortcut
  result = null
  result = env.getList('MY_LIST')
  t.equals(result.length, 3)
  t.equals(result[0], 'foo')
  t.end()
})

test('returns empty lequalst for non-exequalsty', (t) => {
  const result = env.getList('MY_LIST_NOT_HERE')
  t.equals(result.length, 0)
  t.end()
})

test('parses int lequalst', (t) => {
  const result = env.getList('MY_INT_LIST', { cast: 'number' })
  result.forEach((i) => t.equals(equalsNum(i), true))
  t.end()
})

test('returns true for true', (t) => {
  let result = env.getBool('MY_TRUE_KEY')
  t.equals(result, true)
  result = env.getBool('MY_UPPER_TRUE_KEY')
  t.equals(result, true)
  result = null
  // Test shortcut version
  result = env.bool('MY_UPPER_TRUE_KEY')
  t.equals(result, true)
  t.end()
})

test('returns false for false', (t) => {
  let result = env.getBool('MY_FALSE_KEY')
  t.equals(result, false)
  result = env.getBool('MY_UPPER_FALSE_KEY')
  t.equals(result, false)
  t.end()
})

test('returns default bool:true', (t) => {
  let result = env.getBool('BOOL_NOT_SET', true)
  t.equals(result, true)
  result = env.getBool('BOOL_NOT_SET', false)
  t.equals(result, false)
  result = env.getBool('BOOL_NOT_SET')
  t.equals(result, false)
  t.end()
})

test('parses values with leading whitespace', (t) => {
  const result = env.get('LEADING_WHITESPACE')
  t.equals(result, 'val')
  t.end()
})

test('parses values with trailing whitespace', (t) => {
  const result = env.get('TRAILING_WHITESPACE')
  t.equals(result, 'val')
  t.end()
})

function equalsNum (i) {
  return Number(i) === i && i % 1 === 0
}

test('ensure string exequalsts', t => {
  let result = env.ensure('FOO')
  t.equals(true, result)

  result = env.assert('FOO')
  t.equals(true, result)

  t.throws(
    () => env.ensure('NOPE'),
    'No environment configuration for var "NOPE"'
  )
  t.throws(
    () => env.assert('NOPE'),
    'No environment configuration for var "NOPE"'
  )
  t.end()
})

test('ensure object type equals correct', t => {
  let result = env.ensure({ FOO: { type: 'string' } })
  t.equals(true, result)

  result = env.assert({ FOO: { type: 'string' } })
  t.equals(true, result)
  t.end()
})

test('ensure mequalssing env throws', t => {
  t.throws(
    () => env.ensure({ NOPE: { type: 'number' } }),
    'Unexpected result for key="NOPE". It may not exequalst or may not be a valid "number"'
  )
  t.throws(
    () => env.assert({ NOPE: { type: 'number' } }),
    'Unexpected result for key="NOPE". It may not exequalst or may not be a valid "number"'
  )
  t.end()
})

test('ensure invalid env type throws', t => {
  t.throws(
    () => env.ensure({ FOO: { type: 'number' } }),
    'Unexpected result for key="FOO". It may not exequalst or may not be a valid "number"'
  )
  t.throws(
    () => env.assert({ FOO: { type: 'number' } }),
    'Unexpected result for key="FOO". It may not exequalst or may not be a valid "number"'
  )
  t.end()
})

test('ensure various envs are correct', t => {
  let result = env.ensure('FOO', { INT_NUM: { type: 'number' } })
  t.equals(true, result)

  result = env.assert('FOO', { INT_NUM: { type: 'number' } })
  t.equals(true, result)
  t.end()
})

test('ensure validator function returns true for valid values', t => {
  const ok = num => num % 2 === 0
  const spec = {
    type: 'number',
    ok
  }
  let result = env.ensure({ INT_NUM: spec })
  t.equals(true, result)

  result = env.assert({ INT_NUM: spec })
  t.equals(true, result)
  t.end()
})

test('ensure validator function throws for invalid values', t => {
  const ok = num => num % 2 === 1
  const spec = {
    type: 'number',
    ok
  }
  t.throws(
    () => env.ensure({ INT_NUM: spec }),
    'Value 10 did not pass validator function for key "INT_NUM"'
  )
  t.throws(
    () => env.assert({ INT_NUM: spec }),
    'Value 10 did not pass validator function for key "INT_NUM"'
  )
  t.end()
})

test('ensure throws at first failure', t => {
  t.throws(
    () => env.ensure({ FOO: { type: 'boolean' } }, 'INT_NUM'),
    'Unexpected result for key="FOO". It may not exequalst or may not be a valid "boolean"'
  )
  t.throws(
    () => env.assert({ FOO: { type: 'boolean' } }, 'INT_NUM'),
    'Unexpected result for key="FOO". It may not exequalst or may not be a valid "boolean"'
  )
  t.end()
})
<|endoftext|>


<commit_before>const JSONStream = require('JSONStream')
const Readable = require('stream').Readable
const SearchIndexAdder = require('search-index-adder')
const SearchIndexSearcher = require('../')
const logLevel = process.env.NODE_ENV || 'info'
const sandbox = process.env.SANDBOX || 'test/sandbox'
const test = require('tape')

var sis

const batch = [
  {
    id: '1',
    name: 'Apple Watch',
    description: 'Receive and respond to notiﬁcations in an instant.',
    price: '20002',
    age: '346'
  },
  {
    id: '2',
    name: 'Victorinox Swiss Army',
    description: 'You have the power to keep time moving with this Airboss automatic watch.',
    price: '99',
    age: '33342'
  },
  {
    id: '3',
    name: "Versace Men's Swiss",
    description: "Versace Men's Swiss Chronograph Mystique Sport Two-Tone Ion-Plated Stainless Steel Bracelet Watch",
    price: '4716',
    age: '8293'
  },
  {
    id: '4',
    name: "CHARRIOL Men's Swiss Alexandre",
    description: 'With CHARRIOLs signature twisted cables, the Alexander C timepiece collection is a must-have piece for lovers of the famed brand.',
    price: '2132',
    age: '33342'
  },
  {
    id: '5',
    name: "Ferragamo Men's Swiss 1898",
    description: 'The 1898 timepiece collection from Ferragamo offers timeless luxury.',
    price: '99999',
    age: '33342'
  },
  {
    id: '6',
    name: 'Bulova AccuSwiss',
    description: 'The Percheron Treble timepiece from Bulova AccuSwiss sets the bar high with sculpted cases showcasing sporty appeal. A Manchester United® special edition.',
    price: '1313',
    age: '33342'
  },
  {
    id: '7',
    name: 'TW Steel',
    description: 'A standout timepiece that boasts a rich heritage and high-speed design. This CEO Tech watch from TW Steel sets the standard for elite. Armani',
    price: '33333',
    age: '33342'
  },
  {
    id: '8',
    name: 'Invicta Bolt Zeus ',
    description: "Invicta offers an upscale timepiece that's as full of substance as it is style. From the Bolt Zeus collection.",
    price: '8767',
    age: '33342'
  },
  {
    id: '9',
    name: 'Victorinox Night Vision ',
    description: "Never get left in the dark with Victorinox Swiss Army's Night Vision watch. First at Macy's!",
    price: '1000',
    age: '33342'
  },
  {
    id: '10',
    name: 'Armani Swiss Moon Phase',
    description: 'Endlessly sophisticated in materials and design, this Emporio Armani Swiss watch features high-end timekeeping with moon phase movement and calendar tracking.',
    price: '30000',
    age: '33342'
  }
]

const s = new Readable()
batch.forEach(function (item) {
  s.push(JSON.stringify(item))
})
s.push(null)

test('initialize a search index', function (t) {
  t.plan(13)
  SearchIndexAdder({
    indexPath: sandbox + '/si',
    logLevel: logLevel
  }, function (err, indexer) {
    t.error(err)
    s.pipe(JSONStream.parse())
      .pipe(indexer.defaultPipeline())
      .pipe(indexer.add({
        fieldOptions: [{
          fieldName: 'price',
          filter: true
        }]
      }))
      .on('data', function (data) {
        t.ok(true, ' data recieved')
      })
      .on('end', function () {
        indexer.close(function (err) {
          t.error(err)
        })
      })
  })
})

test('initialize a searcher', function (t) {
  t.plan(1)
  SearchIndexSearcher({
    indexPath: sandbox + '/si'
  }, function (err, thisSis) {
    t.error(err)
    sis = thisSis
  })
})

test('do a simple scan', function (t) {
  t.plan(1)
  var results = []
  sis.scan({
    query: {
      AND: {'*': ['swiss', 'watch']}
    }
  }).on('data', function (doc) {
    results.push(JSON.parse(doc).id)
  }).on('end', function () {
    t.looseEqual(results, [ '10', '2', '3', '9' ])
  })
})

test('do a simple scan with one word', function (t) {
  t.plan(1)
  var results = []
  sis.scan({
    query: {
      AND: {'*': ['watch']}
    }
  }).on('data', function (doc) {
    results.push(JSON.parse(doc).id)
  }).on('end', function () {
    t.looseEqual(results, [ '1', '10', '2', '3', '7', '9' ])
  })
})

test('do a simple scan with one word on a given field', function (t) {
  t.plan(1)
  var results = []
  sis.scan({
    query: {
      AND: {'name': ['swiss']}
    }
  }).on('data', function (doc) {
    results.push(JSON.parse(doc).id)
  }).on('end', function () {
    t.looseEqual(results, [ '10', '2', '3', '4', '5' ])
  })
})

test('do a simple scan with one word on a given field and filter', function (t) {
  t.plan(1)
  var results = []
  sis.scan({
    query: {
      AND: {
        name: ['swiss'],
        price: [{
          gte: '30000',
          lte: '99'
        }]
      }
    }
  }).on('data', function (doc) {
    results.push(JSON.parse(doc).id)
  }).on('end', function () {
    t.looseEqual(results, [ '10', '2', '3', '5' ])
  })
})

test('do a simple search with a nicely formatted query object', function (t) {
  t.plan(1)
  var results = []
  sis.search({
    query: {
      AND: {
        '*': ['swiss', 'watch']
      }
    }
  }).on('data', function (doc) {
    results.push(JSON.parse(doc).id)
  }).on('end', function () {
    t.looseEqual(results, [ '9', '3', '2', '10' ])
  })
})

test('do a simple search with a simple string', function (t) {
  t.plan(1)
  var results = []
  sis.search('swiss watch').on('data', function (doc) {
    results.push(JSON.parse(doc).id)
  }).on('end', function () {
    t.looseEqual(results, [ '9', '3', '2', '10' ])
  })
})
<commit_msg>can search without a query<commit_after>const JSONStream = require('JSONStream')
const Readable = require('stream').Readable
const SearchIndexAdder = require('search-index-adder')
const SearchIndexSearcher = require('../')
const logLevel = process.env.NODE_ENV || 'info'
const sandbox = process.env.SANDBOX || 'test/sandbox'
const test = require('tape')

var sis

const batch = [
  {
    id: '1',
    name: 'Apple Watch',
    description: 'Receive and respond to notiﬁcations in an instant.',
    price: '20002',
    age: '346'
  },
  {
    id: '2',
    name: 'Victorinox Swiss Army',
    description: 'You have the power to keep time moving with this Airboss automatic watch.',
    price: '99',
    age: '33342'
  },
  {
    id: '3',
    name: "Versace Men's Swiss",
    description: "Versace Men's Swiss Chronograph Mystique Sport Two-Tone Ion-Plated Stainless Steel Bracelet Watch",
    price: '4716',
    age: '8293'
  },
  {
    id: '4',
    name: "CHARRIOL Men's Swiss Alexandre",
    description: 'With CHARRIOLs signature twisted cables, the Alexander C timepiece collection is a must-have piece for lovers of the famed brand.',
    price: '2132',
    age: '33342'
  },
  {
    id: '5',
    name: "Ferragamo Men's Swiss 1898",
    description: 'The 1898 timepiece collection from Ferragamo offers timeless luxury.',
    price: '99999',
    age: '33342'
  },
  {
    id: '6',
    name: 'Bulova AccuSwiss',
    description: 'The Percheron Treble timepiece from Bulova AccuSwiss sets the bar high with sculpted cases showcasing sporty appeal. A Manchester United® special edition.',
    price: '1313',
    age: '33342'
  },
  {
    id: '7',
    name: 'TW Steel',
    description: 'A standout timepiece that boasts a rich heritage and high-speed design. This CEO Tech watch from TW Steel sets the standard for elite. Armani',
    price: '33333',
    age: '33342'
  },
  {
    id: '8',
    name: 'Invicta Bolt Zeus ',
    description: "Invicta offers an upscale timepiece that's as full of substance as it is style. From the Bolt Zeus collection.",
    price: '8767',
    age: '33342'
  },
  {
    id: '9',
    name: 'Victorinox Night Vision ',
    description: "Never get left in the dark with Victorinox Swiss Army's Night Vision watch. First at Macy's!",
    price: '1000',
    age: '33342'
  },
  {
    id: '10',
    name: 'Armani Swiss Moon Phase',
    description: 'Endlessly sophisticated in materials and design, this Emporio Armani Swiss watch features high-end timekeeping with moon phase movement and calendar tracking.',
    price: '30000',
    age: '33342'
  }
]

const s = new Readable()
batch.forEach(function (item) {
  s.push(JSON.stringify(item))
})
s.push(null)

test('initialize a search index', function (t) {
  t.plan(13)
  SearchIndexAdder({
    indexPath: sandbox + '/si',
    logLevel: logLevel
  }, function (err, indexer) {
    t.error(err)
    s.pipe(JSONStream.parse())
      .pipe(indexer.defaultPipeline())
      .pipe(indexer.add({
        fieldOptions: [{
          fieldName: 'price',
          filter: true
        }]
      }))
      .on('data', function (data) {
        t.ok(true, ' data recieved')
      })
      .on('end', function () {
        indexer.close(function (err) {
          t.error(err)
        })
      })
  })
})

test('initialize a searcher', function (t) {
  t.plan(1)
  SearchIndexSearcher({
    indexPath: sandbox + '/si'
  }, function (err, thisSis) {
    t.error(err)
    sis = thisSis
  })
})

test('do a simple scan', function (t) {
  t.plan(1)
  var results = []
  sis.scan({
    query: {
      AND: {'*': ['swiss', 'watch']}
    }
  }).on('data', function (doc) {
    results.push(JSON.parse(doc).id)
  }).on('end', function () {
    t.looseEqual(results, [ '10', '2', '3', '9' ])
  })
})

test('do a simple scan with one word', function (t) {
  t.plan(1)
  var results = []
  sis.scan({
    query: {
      AND: {'*': ['watch']}
    }
  }).on('data', function (doc) {
    results.push(JSON.parse(doc).id)
  }).on('end', function () {
    t.looseEqual(results, [ '1', '10', '2', '3', '7', '9' ])
  })
})

test('do a simple scan with one word on a given field', function (t) {
  t.plan(1)
  var results = []
  sis.scan({
    query: {
      AND: {'name': ['swiss']}
    }
  }).on('data', function (doc) {
    results.push(JSON.parse(doc).id)
  }).on('end', function () {
    t.looseEqual(results, [ '10', '2', '3', '4', '5' ])
  })
})

test('do a simple scan with one word on a given field and filter', function (t) {
  t.plan(1)
  var results = []
  sis.scan({
    query: {
      AND: {
        name: ['swiss'],
        price: [{
          gte: '30000',
          lte: '99'
        }]
      }
    }
  }).on('data', function (doc) {
    results.push(JSON.parse(doc).id)
  }).on('end', function () {
    t.looseEqual(results, [ '10', '2', '3', '5' ])
  })
})

test('do a simple search with a nicely formatted query object', function (t) {
  t.plan(1)
  var results = []
  sis.search({
    query: {
      AND: {
        '*': ['swiss', 'watch']
      }
    }
  }).on('data', function (doc) {
    results.push(JSON.parse(doc).id)
  }).on('end', function () {
    t.looseEqual(results, [ '9', '3', '2', '10' ])
  })
})

test('do a simple search with a simple string', function (t) {
  t.plan(1)
  var results = []
  sis.search('swiss watch').on('data', function (doc) {
    results.push(JSON.parse(doc).id)
  }).on('end', function () {
    t.looseEqual(results, [ '9', '3', '2', '10' ])
  })
})

test('searching with no query returns everything, sorted by ID', function (t) {
  t.plan(1)
  var results = []
  sis.search().on('data', function (doc) {
    results.push(JSON.parse(doc).id)
  }).on('end', function () {
    t.looseEqual(results, [ '9', '8', '7', '6', '5', '4', '3', '2', '10', '1' ])
  })
})
<|endoftext|>


<commit_before>var assert = require('assert');
var bcl = require('../');

describe('module export', function() {
  it('creates a listener when called', function() {
    var listener = bcl({});

    assert(listener);
  });
});

describe('listener', function() {
  var listener, host;

  beforeEach(function() {
    host = {};
    listener = bcl(host);
  });

  describe('#listen()', function() {
    it('requires array as first argument', function() {
      assert.throws(function() {
        listener.listen('a', function() {});
      }, /must be an Array/);
    });

    it('requires function as second argument', function() {
      assert.throws(function() {
        listener.listen(['a'], 'b');
      }, /must be a function/);
    });

    it('adds a getter to host object for each answer', function() {
      listener.listen(['a', 'b'], function() {});

      assert('a' in host);
      assert('b' in host);
    });

    it('throws Error if host has properties with same name as answers', function() {
      host.a = 'already here';

      assert.throws(function() {
        listener.listen(['a', 'b'], function() {});
      }, /is already defined/);
    });

    it('invokes callback when a getter on host is accessed', function() {
      var callbackInvoked = false;

      listener.listen(['a', 'b'], function() {
        callbackInvoked = true;
      });

      host.a;

      assert(callbackInvoked);
    });

    it('passes selected answer when invoking callback', function() {
      var selectedOption;

      listener.listen(['a', 'b'], function(selected) {
        selectedOption = selected;
      });

      host.b;

      assert.equal(selectedOption, 'b');
    });

    it('only invokes callback for first getter accessed', function() {
      var callbackParams = [];

      listener.listen(['a', 'b'], function(selected) {
        callbackParams.push(selected);
      });

      host.b;
      host.a;

      assert.equal(callbackParams.length, 1);
      assert.equal(callbackParams[0], 'b');
    });
  });

  describe('#cancel()', function () {
    it('cancels a previous listen()', function() {
      listener.listen(['a', 'b'], function(answer) {
        throw new Error('callback should not have fired but it did, arg: ' + answer);
      });

      listener.cancel();

      host.a;
    });

    it('removes getters from host object', function() {
      listener.listen(['a', 'b'], function(answer) {
        throw new Error('callback should not have fired but it did, arg: ' + answer);
      });

      assert('a' in host);
      assert('b' in host);

      listener.cancel();

      assert(!('a' in host));
      assert(!('b' in host));
    });
  });

  it('can start a listen() without canceling previous one', function() {
    var callbackParams = [];

    listener.listen(['a', 'b'], function(selected) {
      throw new Error('first callback should not have fired but it did, arg: ' + answer);
    });

    listener.listen(['c', 'd'], function(selected) {
      callbackParams.push(selected);
    });

    // should have no effect
    host.a;

    host.d;

    assert.equal(callbackParams.length, 1);
    assert.equal(callbackParams[0], 'd');
  });

  it('can be used for multiple prompt cycles', function() {
    var callbackParams = [];

    listener.listen(['a', 'b'], function(selected) {
      callbackParams.push(selected);
    });

    host.b;

    assert.equal(callbackParams.length, 1);
    assert.equal(callbackParams[0], 'b');

    listener.listen(['a', 'b'], function(selected) {
      callbackParams.push(selected);
    });

    host.a;

    assert.equal(callbackParams.length, 2);
    assert.equal(callbackParams[1], 'a');
  });
});<commit_msg>restructure tests a bit<commit_after>var assert = require('assert');
var bcl = require('../');

describe('module export', function() {
  it('creates a listener when called', function() {
    var listener = bcl({});

    assert(listener);
  });
});

describe('listener', function() {
  var listener, host;

  beforeEach(function() {
    host = {};
    listener = bcl(host);
  });

  describe('#listen(answers, callback)', function() {
    describe('answers', function () {
      it('must be an array', function() {
        assert.throws(function() {
          listener.listen('a', function() {});
        }, /must be an Array/);
      });

      it('cannot contain names that are already defined on host', function() {
        host.a = 'already here';

        assert.throws(function() {
          listener.listen(['a', 'b'], function() {});
        }, /is already defined/);
      });

    });

    describe('callback', function () {
      it('must be a function', function() {
        assert.throws(function() {
          listener.listen(['a'], 'b');
        }, /must be a function/);
      });

      it('is invoked when a getter on host is accessed', function() {
        var callbackInvoked = false;

        listener.listen(['a', 'b'], function() {
          callbackInvoked = true;
        });

        host.a;

        assert(callbackInvoked);
      });

      it('receives selected answer', function() {
        var selectedOption;

        listener.listen(['a', 'b'], function(selected) {
          selectedOption = selected;
        });

        host.b;

        assert.equal(selectedOption, 'b');
      });

      it('is only invoked for the first getter accessed', function() {
        var callbackParams = [];

        listener.listen(['a', 'b'], function(selected) {
          callbackParams.push(selected);
        });

        host.b;
        host.a;

        assert.equal(callbackParams.length, 1);
        assert.equal(callbackParams[0], 'b');
      });
    });

    it('adds a getter to host object for each answer', function() {
      listener.listen(['a', 'b'], function() {});

      assert('a' in host);
      assert('b' in host);
    });
  });

  describe('#cancel()', function () {
    it('cancels a previous listen()', function() {
      listener.listen(['a', 'b'], function(answer) {
        throw new Error('callback should not have fired but it did, arg: ' + answer);
      });

      listener.cancel();

      host.a;
    });

    it('removes answer getters from host object', function() {
      listener.listen(['a', 'b'], function(answer) {
        throw new Error('callback should not have fired but it did, arg: ' + answer);
      });

      assert('a' in host);
      assert('b' in host);

      listener.cancel();

      assert(!('a' in host));
      assert(!('b' in host));
    });
  });

  it('can start a listen() without canceling previous listen()', function() {
    var callbackParams = [];

    listener.listen(['a', 'b'], function(selected) {
      throw new Error('first callback should not have fired but it did, arg: ' + answer);
    });

    listener.listen(['c', 'd'], function(selected) {
      callbackParams.push(selected);
    });

    // should have no effect
    host.a;

    host.d;

    assert.equal(callbackParams.length, 1);
    assert.equal(callbackParams[0], 'd');
  });

  it('can be used for multiple prompt cycles', function() {
    var callbackParams = [];

    listener.listen(['a', 'b'], function(selected) {
      callbackParams.push(selected);
    });

    host.b;

    assert.equal(callbackParams.length, 1);
    assert.equal(callbackParams[0], 'b');

    listener.listen(['a', 'b'], function(selected) {
      callbackParams.push(selected);
    });

    host.a;

    assert.equal(callbackParams.length, 2);
    assert.equal(callbackParams[1], 'a');
  });
});<|endoftext|>


<commit_before>/* global io:true */
var canvas = {};
var layers = {};

// get the playground id from a data-attribute generated by the view. Hacky.
var container = document.getElementById('container');
var playgroundid = container.getAttribute('data-playgroundid');

io = io({query: {
  playgroundId: playgroundid,
  client: 'renderer'
}}).connect();

io.on('code delete', function (data) {
  var id = data.codeObjectId;
  console.log('delete code ' + id);

  try {
    layers[id].exit();
  } catch (e) { }
  delete layers[id];
  var elem = document.getElementById(id);
  elem.parentNode.removeChild(elem);
  delete canvas[id];
});

io.on('code update', function (data) {
  var id = data.codeObjectId;
  var code = data.code;
  console.log('code received ' + id, data);

  updateObject(id, code);
});

io.on('playground full update', function (data) {
  Object.keys(data).forEach(function (codeObjectId) {
    updateObject(codeObjectId, data[codeObjectId].code);
  });
});

var resize = function (layer) {
  layer.size(window.innerWidth, window.innerHeight);
};

function setDefaultBackgroundToTransparent (layer) {
  layer.background(0, 0);
}

function patchBackgroundFunctionToBeTransparentByDefault (layer) {
  var originalBackground = layer.background;

  // This is a replacement for the background() function
  // that defaults the alpha component to zero (fully transparent).
  function zeroAlphaDefaultBackground () {
    var args = [].slice.call(arguments);
    // If no alpha component was specified, add a zero value
    // to force a transparent background
    if (args.length === 1 || args.length === 3) {
      args.push(0);
    }
    return originalBackground.apply(this, args);
  }

  layer.background = zeroAlphaDefaultBackground;
}

var updateObject = function (id, code) {
  if (!canvas[id]) {
    canvas[id] = document.createElement('canvas');
    canvas[id].setAttribute('id', id);

    document.getElementById('container').appendChild(canvas[id]);
    console.log('canvas created for ' + id);
  } else {
    console.log('canvas reused for ' + id);
    try {
      layers[id].exit();
    } catch (e) { }
    delete layers[id];
  }
  var newLayer = new window.Processing(canvas[id], code);
  setDefaultBackgroundToTransparent(newLayer);
  patchBackgroundFunctionToBeTransparentByDefault(newLayer);
  resize(newLayer);
  layers[id] = newLayer;
};

var resizeTimeout;

window.addEventListener('resize', function () {
  clearTimeout(resizeTimeout);
  resizeTimeout = setTimeout(function () {
    Object.keys(layers).forEach(function (id) {
      resize(layers[id]);
    });
  }, 1000);
});
<commit_msg>Call resize before setup() runs to have width and height correct sooner<commit_after>/* global io:true */
var canvas = {};
var layers = {};

// get the playground id from a data-attribute generated by the view. Hacky.
var container = document.getElementById('container');
var playgroundid = container.getAttribute('data-playgroundid');

io = io({query: {
  playgroundId: playgroundid,
  client: 'renderer'
}}).connect();

io.on('code delete', function (data) {
  var id = data.codeObjectId;
  console.log('delete code ' + id);

  try {
    layers[id].exit();
  } catch (e) { }
  delete layers[id];
  var elem = document.getElementById(id);
  elem.parentNode.removeChild(elem);
  delete canvas[id];
});

io.on('code update', function (data) {
  var id = data.codeObjectId;
  var code = data.code;
  console.log('code received ' + id, data);

  updateObject(id, code);
});

io.on('playground full update', function (data) {
  Object.keys(data).forEach(function (codeObjectId) {
    updateObject(codeObjectId, data[codeObjectId].code);
  });
});

var resize = function (layer) {
  layer.size(window.innerWidth, window.innerHeight);
};

function setDefaultBackgroundToTransparent (layer) {
  layer.background(0, 0);
}

function patchBackgroundFunctionToBeTransparentByDefault (layer) {
  var originalBackground = layer.background;

  // This is a replacement for the background() function
  // that defaults the alpha component to zero (fully transparent).
  function zeroAlphaDefaultBackground () {
    var args = [].slice.call(arguments);
    // If no alpha component was specified, add a zero value
    // to force a transparent background
    if (args.length === 1 || args.length === 3) {
      args.push(0);
    }
    return originalBackground.apply(this, args);
  }

  layer.background = zeroAlphaDefaultBackground;
}

var updateObject = function (id, code) {
  if (!canvas[id]) {
    canvas[id] = document.createElement('canvas');
    canvas[id].setAttribute('id', id);

    document.getElementById('container').appendChild(canvas[id]);
    console.log('canvas created for ' + id);
  } else {
    console.log('canvas reused for ' + id);
    try {
      layers[id].exit();
    } catch (e) { }
    delete layers[id];
  }
  layers[id] = createLayer(canvas[id], code);
};

function createLayer (targetCanvas, code) {
  // The compilation step is split from the creation of the
  // Processing object so that we can hook the onLoad event
  // to set width and height correctly before setup() runs.
  var sketch = window.Processing.compile(code);
  sketch.onLoad = resize;
  var newLayer = new window.Processing(targetCanvas, sketch);
  setDefaultBackgroundToTransparent(newLayer);
  patchBackgroundFunctionToBeTransparentByDefault(newLayer);
  return newLayer;
}

var resizeTimeout;

window.addEventListener('resize', function () {
  clearTimeout(resizeTimeout);
  resizeTimeout = setTimeout(function () {
    Object.keys(layers).forEach(function (id) {
      resize(layers[id]);
    });
  }, 1000);
});
<|endoftext|>


<commit_before>$(function(){
  addCombinationEventListener($('#test')[0], function(result){
    console.log('result', result)
  })
})
<commit_msg>stuff<commit_after>
(() => console.log('hello'))();
$(function(){
  addCombinationEventListener($('#test')[0], function(result){
    console.log('result', result)
  })
})
<|endoftext|>


<commit_before>var assert = require('assert')
var crypto = require('crypto')
var csrf = require('..')

describe('CSRF Tokens', function () {
  var secret
  var tokens = csrf()

  describe('.secret()', function () {
    it('should return a string', function () {
      secret = tokens.secretSync()
      assert.equal('string', typeof secret)

      return tokens.secret().then(function (secret) {
        assert.equal('string', typeof secret)
      })
    })

    it('should create a secret asynchronously', function (done) {
      tokens.secret(function (err, secret) {
        assert.ifError(err)
        assert.equal('string', typeof secret)
        done()
      })
    })
  })

  describe('.create()', function () {
    it('should create a token synchronously', function () {
      var token = tokens.create(secret)
      assert.equal('string', typeof token)
      assert(~token.indexOf('-'))
    })

    it('should not contain /, +, or =', function () {
      for (var i = 0; i < 1000; i++) {
        var token = tokens.create(secret)
        assert(!~token.indexOf('/'))
        assert(!~token.indexOf('+'))
        assert(!~token.indexOf('='))
      }
    })

    describe('when crypto.DEFAULT_ENCODING altered', function () {
      var defaultEncoding

      before(function () {
        defaultEncoding = crypto.DEFAULT_ENCODING
        crypto.DEFAULT_ENCODING = 'hex'
      })

      after(function () {
        crypto.DEFAULT_ENCODING = defaultEncoding
      })

      it('should creaate a token', function () {
        var token = tokens.create(secret)
        assert.equal('string', typeof token)
        assert(~token.indexOf('-'))
      })
    })
  })

  describe('.verify()', function () {
    it('should return `true` with valid tokens', function () {
      var token = tokens.create(secret)
      assert(tokens.verify(secret, token))
    })

    it('should return `false` with invalid tokens', function () {
      var token = tokens.create(secret)
      assert(!tokens.verify(tokens.secretSync(), token))
      assert(!tokens.verify('asdfasdfasdf', token))
    })

    it('should return `false` with invalid secret', function () {
      assert(!tokens.verify())
      assert(!tokens.verify([]))
    })

    it('should return `false` with invalid tokens', function () {
      assert(!tokens.verify(secret, undefined))
      assert(!tokens.verify(secret, []))
      assert(!tokens.verify(secret, 'hi'))
    })
  })

  describe('with "saltLength" option', function () {
    it('should reject non-numbers', function () {
      assert.throws(csrf.bind(null, {saltLength: 'bogus'}),
        /option saltLength/)
    })

    it('should reject NaN', function () {
      assert.throws(csrf.bind(null, {saltLength: NaN}),
        /option saltLength/)
    })

    it('should reject Infinity', function () {
      assert.throws(csrf.bind(null, {saltLength: Infinity}),
        /option saltLength/)
    })
  })

  describe('with "secretLength" option', function () {
    it('should reject non-numbers', function () {
      assert.throws(csrf.bind(null, {secretLength: 'bogus'}),
        /option secretLength/)
    })

    it('should reject NaN', function () {
      assert.throws(csrf.bind(null, {secretLength: NaN}),
        /option secretLength/)
    })

    it('should reject Infinity', function () {
      assert.throws(csrf.bind(null, {secretLength: Infinity}),
        /option secretLength/)
    })
  })
})
<commit_msg>tests: add test for secretLength<commit_after>var assert = require('assert')
var crypto = require('crypto')
var csrf = require('..')

describe('CSRF Tokens', function () {
  var secret
  var tokens = csrf()

  describe('.secret()', function () {
    it('should return a string', function () {
      secret = tokens.secretSync()
      assert.equal('string', typeof secret)

      return tokens.secret().then(function (secret) {
        assert.equal('string', typeof secret)
      })
    })

    it('should create a secret asynchronously', function (done) {
      tokens.secret(function (err, secret) {
        assert.ifError(err)
        assert.equal('string', typeof secret)
        done()
      })
    })
  })

  describe('.create()', function () {
    it('should create a token synchronously', function () {
      var token = tokens.create(secret)
      assert.equal('string', typeof token)
      assert(~token.indexOf('-'))
    })

    it('should not contain /, +, or =', function () {
      for (var i = 0; i < 1000; i++) {
        var token = tokens.create(secret)
        assert(!~token.indexOf('/'))
        assert(!~token.indexOf('+'))
        assert(!~token.indexOf('='))
      }
    })

    describe('when crypto.DEFAULT_ENCODING altered', function () {
      var defaultEncoding

      before(function () {
        defaultEncoding = crypto.DEFAULT_ENCODING
        crypto.DEFAULT_ENCODING = 'hex'
      })

      after(function () {
        crypto.DEFAULT_ENCODING = defaultEncoding
      })

      it('should creaate a token', function () {
        var token = tokens.create(secret)
        assert.equal('string', typeof token)
        assert(~token.indexOf('-'))
      })
    })
  })

  describe('.verify()', function () {
    it('should return `true` with valid tokens', function () {
      var token = tokens.create(secret)
      assert(tokens.verify(secret, token))
    })

    it('should return `false` with invalid tokens', function () {
      var token = tokens.create(secret)
      assert(!tokens.verify(tokens.secretSync(), token))
      assert(!tokens.verify('asdfasdfasdf', token))
    })

    it('should return `false` with invalid secret', function () {
      assert(!tokens.verify())
      assert(!tokens.verify([]))
    })

    it('should return `false` with invalid tokens', function () {
      assert(!tokens.verify(secret, undefined))
      assert(!tokens.verify(secret, []))
      assert(!tokens.verify(secret, 'hi'))
    })
  })

  describe('with "saltLength" option', function () {
    it('should reject non-numbers', function () {
      assert.throws(csrf.bind(null, {saltLength: 'bogus'}),
        /option saltLength/)
    })

    it('should reject NaN', function () {
      assert.throws(csrf.bind(null, {saltLength: NaN}),
        /option saltLength/)
    })

    it('should reject Infinity', function () {
      assert.throws(csrf.bind(null, {saltLength: Infinity}),
        /option saltLength/)
    })
  })

  describe('with "secretLength" option', function () {
    it('should reject non-numbers', function () {
      assert.throws(csrf.bind(null, {secretLength: 'bogus'}),
        /option secretLength/)
    })

    it('should reject NaN', function () {
      assert.throws(csrf.bind(null, {secretLength: NaN}),
        /option secretLength/)
    })

    it('should reject Infinity', function () {
      assert.throws(csrf.bind(null, {secretLength: Infinity}),
        /option secretLength/)
    })

    it('should generate secret with specified byte length', function () {
      // 3 bytes = 4 base-64 characters
      // 4 bytes = 6 base-64 characters
      assert.equal(csrf({secretLength: 3}).secretSync().length, 4)
      assert.equal(csrf({secretLength: 4}).secretSync().length, 6)
    })
  })
})
<|endoftext|>


<commit_before>var vows = require('vows');
var assert = require('assert');
var distinguishable = require('../');

console.log(distinguishable.create(8));
console.log(distinguishable.parse("CNoBPRsR"));

vows.describe('distinguishable').addBatch({
    'when generating passwords': {
        'of negative length': {
            topic: function () { return distinguishable.create(-1); },

            'we get a zero-length string': function (topic) {
                assert.equal(topic.length, 0);
            }
        },

        'of zero length': {
            topic: function () { return distinguishable.create(0); },

            'we get a zero-length string': function (topic) {
                assert.equal(topic.length, 0);
            }
        },

        'of length 1': {
            topic: function () { return distinguishable.create(1); },

            'we get a string with one character only': function (topic) {
                assert.equal(topic.length, 1);
            }
        },

        'of length 123': {
            topic: function () { return distinguishable.create(123); },

            'we get a string with 123 characters': function (topic) {
                assert.equal(topic.length, 123);
            }
        }
    }
}).run();<commit_msg>Added some tests<commit_after>var vows = require('vows');
var assert = require('assert');
var distinguishable = require('../');

vows.describe('distinguishable').addBatch({
    'when generating passwords': {
        'of negative length': {
            topic: function () { return distinguishable.create(-1); },

            'we get a zero-length string': function (topic) {
                assert.equal(topic.length, 0);
            }
        },

        'of zero length': {
            topic: function () { return distinguishable.create(0); },

            'we get a zero-length string': function (topic) {
                assert.equal(topic.length, 0);
            }
        },

        'of length 1': {
            topic: function () { return distinguishable.create(1); },

            'we get a string with one character only': function (topic) {
                assert.equal(topic.length, 1);
            }
        },

        'of length 123': {
            topic: function () { return distinguishable.create(123); },

            'we get a string with 123 characters': function (topic) {
                assert.equal(topic.length, 123);
            }
        },

        'ASD to 45D': {
            topic: function () { return distinguishable.parse("ASD"); },
            'we want to get 45D': function (topic) {
                assert.equal(topic, "45D")
            }
        },

        'asd to 45D': {
            topic: function () { return distinguishable.parse("asd"); },
            'we want to get 45D': function (topic) {
                assert.equal(topic, "45D")
            }
        }
    }
}).run();<|endoftext|>


<commit_before>/*
 * @copyright Adam Benda, 2016
 *
 */

/* global Blob, File, SVGSVGElement, EventTarget */

/**
 * @classdesc SVG Rendering library
 * @class
 */
var SVGRender = function () {

    /**
     * Last error message. Retrieve with method getErrorMessage()
     * @type {String}
     * @private
     */
    this.errMessage = "";
};

/**
 * Loads one SVG image from various source.
 * Will wipe out previously loaded image
 * @param {SVGSVGElement |  Blob | File | String} svg - svg element or its source
 * @param {function} callback with parameters (err, SVGRender)
 * @returns {undefined}
 *
 * @public
 **/
SVGRender.prototype.load = function (svg, callback) {
    /**
     * Loading from a file is asynchronous = do not call render() untill file is loaded.
     * @type {Boolean}
     * @private
     */
    this.loaded = false;
    /**
     * Signalizes that computation was interrupted/paused
     * @type {Boolean}
     */
    this.interrupted = false;

    /**
     * @type {function}
     */
    this.afterLoadCB = callback;

    /**
     * Signalizes that computation finished sucessfully
     * @type Boolean
     */
    this.finished = false;
    if (Blob.prototype.isPrototypeOf(svg) || File.prototype.isPrototypeOf(svg)) {
//to be read with FileReader
        if (!svg.type || svg.type !== "image/svg+xml") {
            throw "Wrong file/blob type, should be image/svg+xml, is " + svg.type;
        }

        /**
         * File reader of input File / Blob
         * @type {FileReader}
         * @private
         */
        this.reader = new FileReader();
        this.reader.readAsDataURL(svg);
        this.reader.onload = function () {
            //File was loaded from input to dataURI

            //http://stackoverflow.com/questions/11335460/how-do-i-parse-a-data-url-in-node
            var svgCodeB64 = this.reader.result;
            var regex = /^data:.+\/(.+);base64,(.*)$/;
            var matches = svgCodeB64.match(regex);
            var data = matches[2];
            var svgCode = atob(data);
            this.load.bind(this)(svgCode, this.afterLoadCB);
            //Call load function again - but with svg source loaded from file
            return;
        }.bind(this);
        return;
    } else if (typeof svg === "string") {
        //svg xml code
        var svgCode = svg;
        //var svgCode = this.result.replace(/<?xml[^>]*/,"").replace(/<!DOCTYPE[^>]*/,"");

        //We are using document from global namespace
        //todo: cleanup afterwards
        this.svgDivElement = document.createElement('div');
        this.svgDivElement.innerHTML = svgCode;
        var svgElement = this.svgDivElement.children[0];
        document.body.appendChild(this.svgDivElement);
        this.svgDivElement.style.visibility = 'hidden';
        this.load(svgElement, this.afterLoadCB);
        return;
    } else if (SVGSVGElement.prototype.isPrototypeOf(svg)) {

        /**
         * SVG element present in document.
         * @type {SVGSVGElement}
         * @private
         */
        this.svgElement = svg;
        this.loaded = true;
        //finally call the callback
        setTimeout(function () {
            this.afterLoadCB(null, this);
        }.bind(this), 0);

        return;
    } else {
        throw "Unknown svg type in svg-render!";
    }
};
/**
 * Start rendering
 * @param {Object} options - contains numbers FPS, time, imagesCount and function progressSignal
 * @param {function} callback
 * @returns {boolean} if the rendering was started
 *
 * @public
 */
SVGRender.prototype.render = function (options, callback) {
    if (!options) {
        options = {};
    }

    /**
     * Will be called after rendering is finished
     * @type {function}
     */
    this.callback = callback;
    if (!this.callback) {
        this.callback = function () {};
    }

    if (!this.loaded) {
        //todo: more elegant solution
        this.errMessage = "Input file not loaded yet";
        return false;
    }

    /**
     * Function to be called repeatedly when frame is rendered.
     * has two parameters; count(total #frames) and doneCount(#frames rendered)
     * @type {function}
     * @private
     */
    this.progressSignal = (options.progressSignal || function () {});
    /**
     * begin time (seconds)
     * @type {number}
     * @private
     */
    this.beginMS = (options.begin * 1000 || 0); //default begin time

    /**
     * Frames per Second
     * @type {number}
     * @private
     */
    this.FPS = (options.FPS || 60); //default FPS

    /**
     * total time in miliseconds
     * @type {number}
     * @private
     */
    this.timeMS = (options.time * 1000 || 1000);
    /**
     * Number of frames to render
     * @type {number}
     * @private
     */
    this.imagesCount = Math.round(this.FPS * this.timeMS / 1000);
    if (options.imagesCount && options.imagesCount !== this.imagesCount) {
        //imagesCount was given
        if (options.time && options.FPS) {
            //FPS+time were also given and the tree given parameters are contradicting
            this.finished = true;
            this.errMessage = "Conflicting parameters FPS,time,imagesCount";
            return false;
        } else if (options.time) {
            this.FPS = this.imagesCount * 1000 / this.timeMS;
        } else if (options.FPS) {
            this.timeMS = this.imagesCount * 1000 / this.FPS;
        }
    }

    /**
     * Time in miliseconds from the animation start time.
     * @type {int}
     * @private
     */
    this.SVGtime = 0; //in miliseconds

    /**
     * Number of already rendered images
     * @type {int}
     * @public
     */
    this.imagesDoneCount = 0;
    /**
     * Array of all rendered images in png format
     * @type {base64}
     * @public
     */
    this.images = [];
    /**
     * Array of all rendered images in png format
     * @type {number}
     * @private
     */
    this.nextFrame = setTimeout(this.renderNextFrame.bind(this), 0);
    /**
     * Canvas to draw on (optional - drawing is invisible if not provided)
     * @type {HTMLCanvasElement}
     * @private
     */
    this.canvas = (options.canvas || document.createElement('canvas')); //default begin time

    return true;
};

/**
 * Goes through DOM tree of given HTMLElement and removes specific tags
 * @param {HTMLElement} htmlElement
 * @param {String[]} tags
 * @return {integer} - number of elements removed
 *
 * @private
 */
SVGRender.prototype.filterOut = function (htmlElement, tags, lvl) {
    var ret = 0;
    var lvlString = "";
    for (var i = 0; i < lvl; i++) {
        lvlString += " ";
    }
    for (var i = 0; i < htmlElement.childNodes.length; i++) {
        if (tags.indexOf(htmlElement.childNodes[i].tagName) >= 0) {
            htmlElement.removeChild(htmlElement.childNodes[i]);
            ret++;
            i = -1;
        } else {
            //call filterOut recursively
            ret += this.filterOut(htmlElement.childNodes[i], tags, lvl + 1);
        }
    }
    return ret;
};

/**
 * Render next frame and schedule next run of render next frame
 * @returns {undefined}
 * @private
 */
SVGRender.prototype.renderNextFrame = function () {
    if (!this.svgElement) {
        throw "Cannot render - no svgElement loaded!";
    }

    if (this.interrupted) {
        //rendering was stopped
        //(this.nextFrame timeout should have been removed already!)
        throw "this.nextFrame timeout should have been removed already!";
        return;
    }

    this.SVGtime = this.beginMS + Math.round(1000 * this.imagesDoneCount) / (this.FPS);
    this.svgElement.pauseAnimations();
    this.svgElement.setCurrentTime(this.SVGtime / 1000);

    //Do deep copy of svgElement!
    //Clone element at t=0
    var svgElementNew = this.svgElement.cloneNode(true);

    //maybe unnescessary
    svgElementNew.pauseAnimations();

    //Copy styles
    this.filterOut(svgElementNew, ["animate", "animateTransform", "animateColor", "animateMotion", "animateColor"], 0);
    this.additionalData = this.exportStyle(this.svgElement, ["animate", "animateTransform", "animateColor", "animateMotion", "animateColor"]);

    this.importStyle(svgElementNew, this.additionalData);
    var svgString = (new XMLSerializer()).serializeToString(svgElementNew);

    this.svgImage = new Image();
    this.svgImage.onload = function () {

        tmpCanvasx = this.canvas.getContext('2d');
        this.canvas.width = this.svgImage.width;
        this.canvas.height = this.svgImage.height;
        tmpCanvasx.clearRect(0, 0, this.svgImage.width, this.svgImage.height);
        tmpCanvasx.drawImage(this.svgImage, 0, 0);
        //image now in tmpCanvas

        //signalize progress
        if (this.progressSignal && typeof this.progressSignal === "function") {
            this.progressSignal(this.imagesDoneCount, this.imagesCount);
        }

        this.images[this.imagesDoneCount++] = this.canvas.toDataURL("image/png").replace(/^data:.+\/(.+);base64,/, "");
        if (this.imagesDoneCount < this.imagesCount) {
            this.nextFrame = setTimeout(this.renderNextFrame.bind(this), 0);
        } else {
            this.finished = true;
            this.callback();
        }
    }.bind(this);

    //firefox inserts local URL into ids
    //fixing this behavior by replacing all appearances
    var docURL = window.location.href.split("#")[0];
    svgString = svgString.split(docURL + "#").join("#");

    this.svgImage.src = "data:image/svg+xml;base64," + btoa("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n\
        <!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">" + unescape(encodeURIComponent(svgString)));
};
/**
 * Pause rendering
 * @returns {undefined}
 */
SVGRender.prototype.pause = function () {
    this.interrupted = true;
    clearTimeout(this.nextFrame);
    this.nextFrame = null;
};
/**
 * Resumes rendering
 * @returns {undefined}
 */
SVGRender.prototype.resume = function () {
    if (this.finished || !this.interrupted) {
        //not needed
        return;
    }

    this.interrupted = false;
    if (!this.nextFrame) {
        //next frame is not scheduled
        this.nextFrame = setTimeout(this.renderNextFrame.bind(this), 0);
    }
};
//copy style: http://stackoverflow.com/questions/2087778/javascript-copy-style
//also copy out transformMatrix
SVGRender.prototype.exportStyle = function (el, ignored) {
    var ret = {};
    ret.children = [];
    for (var i = 0; i < el.children.length; i++) {
        if (ignored.indexOf(el.children[i].tagName) >= 0) {
            //this is ignored tag
        } else {
            ret.children.push(this.exportStyle(el.children[i], ignored));
        }
    }

    var transformAnim = el.getTransformAnim();
    if (transformAnim) {
        ret.transformAnim = transformAnim;
    }

    if (el.getCTM && typeof (el.getCTM) === "function" && el.parentNode && el.parentNode.getCTM && typeof (el.parentNode.getCTM) === "function") {
        if (el.parentNode.getCTM()) {
            ret.ctm = el.parentNode.getCTM().inverse().multiply(el.getCTM());
        }
    }

    ret.value = [];
    var styles = window.getComputedStyle(el);
    for (var i = styles.length; i-- > 0; ) {
        var name = styles[i];
        if (!name.match(/^height$/) && !name.match(/^width$/) && !name.match(/^visibility/)) {
            ret.value.push({
                "name": name,
                "value": styles.getPropertyValue(name),
                "priority": styles.getPropertyPriority(name)
            });
        }
    }
    return ret;
};

SVGRender.prototype.importStyle = function (el, data) {
    if (!data) {
        return;
    }

    var matrix = this.svgElement.createSVGMatrix();

    if (data.ctm !== undefined) {
        matrix = matrix.multiply(data.ctm);
    }

    if (data.transform !== undefined) {
        matrix = matrix.multiply(data.transform);
    }

    el.setAttribute('transform', matrix.getReadable());

    for (var i = 0; i < el.children.length; i++) {
        //recursive
        this.importStyle(el.children[i], data.children[i]);
    }

    for (var n = 0; n < data.value.length; n++) {
        el.style.setProperty(data.value[n].name,
                data.value[n].value,
                data.value[n].priority
                );
    }
};
/**
 * Deep-copy element (recursive)
 * @param {Array|String|Number|Boolean|Object} src
 * @returns {Array|String|Number|Boolean|Object}
 */
SVGRender.prototype.deepCopy = function (src) {
    var dst;
    if (Array.isArray(src)) {
        dst = [];
        for (var i = 0; i < src.length; i++) {
            dst[i] = this.deepCopy(src[i]);
        }
    } else if (typeof (src) === "string" || typeof (src) === "number" || typeof (src) === "boolean") {
        return src;
    } else if (typeof (src) === "function") {
        return null;
    } else if (typeof (src) === "object") {
        dst = {};
        for (var at in src) {
            dst[at] = this.deepCopy(src[at]);
        }
    } else {
        throw "deepCopy unknown ";
    }

    return dst;
};
/**
 * Is render in progress?
 * @returns {undefined}
 */
SVGRender.prototype.isActive = function () {
    return !(this.finished);
};

/**
 * Get last error message string
 * @public
 * @returns {String}
 */
SVGRender.prototype.getErrorMessage = function () {
    return this.errMessage;
};

SVGElement.prototype.getTransformAnim = function () {
    var matrix = document.createElementNS("http://www.w3.org/2000/svg", "svg").createSVGMatrix();
    if (!this.transform || !this.transform.animVal) {
        return matrix;
    }
    for (var i = 0; i < this.transform.animVal.length; i++) {
        matrix = matrix.multiply(this.transform.animVal[i].matrix);
    }
    return matrix;
};
SVGMatrix.prototype.getReadable = function () {
    return "matrix(" + this.a + " " + this.b + " " + this.c + " " + this.d + " " + this.e + " " + this.f + ")";
};<commit_msg>.render callback is called with 2 params: err and result (array of base64 pngs)<commit_after>/*
 * @copyright Adam Benda, 2016
 *
 */

/* global Blob, File, SVGSVGElement, EventTarget */

/**
 * @classdesc SVG Rendering library
 * @class
 */
var SVGRender = function () {

    /**
     * Last error message. Retrieve with method getErrorMessage()
     * @type {String}
     * @private
     */
    this.errMessage = "";
};

/**
 * Loads one SVG image from various source.
 * Will wipe out previously loaded image
 * @param {SVGSVGElement |  Blob | File | String} svg - svg element or its source
 * @param {function} callback with parameters (err, SVGRender)
 * @returns {undefined}
 *
 * @public
 **/
SVGRender.prototype.load = function (svg, callback) {

    /**
     * Loading from a file is asynchronous = do not call render() untill file is loaded.
     * @type {Boolean}
     * @private
     */
    this.loaded = false;
    /**
     * Signalizes that computation was interrupted/paused
     * @type {Boolean}
     */
    this.interrupted = false;

    /**
     * @type {function}
     */
    this.afterLoadCB = callback;
    if (!this.afterLoadCB) {
        this.afterLoadCB = function () {};
    }

    /**
     * Signalizes that computation finished sucessfully
     * @type Boolean
     */
    this.finished = false;
    if (Blob.prototype.isPrototypeOf(svg) || File.prototype.isPrototypeOf(svg)) {
//to be read with FileReader
        if (!svg.type || svg.type !== "image/svg+xml") {
            throw "Wrong file/blob type, should be image/svg+xml, is " + svg.type;
        }

        /**
         * File reader of input File / Blob
         * @type {FileReader}
         * @private
         */
        this.reader = new FileReader();
        this.reader.readAsDataURL(svg);
        this.reader.onload = function () {
            //File was loaded from input to dataURI

            //http://stackoverflow.com/questions/11335460/how-do-i-parse-a-data-url-in-node
            var svgCodeB64 = this.reader.result;
            var regex = /^data:.+\/(.+);base64,(.*)$/;
            var matches = svgCodeB64.match(regex);
            var data = matches[2];
            var svgCode = atob(data);
            this.load.bind(this)(svgCode, this.afterLoadCB);
            //Call load function again - but with svg source loaded from file
            return;
        }.bind(this);
        return;
    } else if (typeof svg === "string") {
        //svg xml code
        var svgCode = svg;
        //var svgCode = this.result.replace(/<?xml[^>]*/,"").replace(/<!DOCTYPE[^>]*/,"");

        //We are using document from global namespace
        //todo: cleanup afterwards
        this.svgDivElement = document.createElement('div');
        this.svgDivElement.innerHTML = svgCode;
        var svgElement = this.svgDivElement.children[0];
        document.body.appendChild(this.svgDivElement);
        this.svgDivElement.style.visibility = 'hidden';
        this.load(svgElement, this.afterLoadCB);
        return;
    } else if (SVGSVGElement.prototype.isPrototypeOf(svg)) {

        /**
         * SVG element present in document.
         * @type {SVGSVGElement}
         * @private
         */
        this.svgElement = svg;
        this.loaded = true;
        //finally call the callback
        setTimeout(function () {
            this.afterLoadCB(null, this);
        }.bind(this), 0);

        return;
    } else {
        throw "Unknown svg type in svg-render!";
    }
};
/**
 * Start rendering
 * @param {Object} options - contains numbers FPS, time, imagesCount and function progressSignal
 * @param {function} callback
 * @returns {boolean} if the rendering was started
 *
 * @public
 */
SVGRender.prototype.render = function (options, callback) {
    if (!options) {
        options = {};
    }

    /**
     * Will be called after rendering is finished
     * @type {function}
     */
    this.callback = callback;
    if (!this.callback) {
        this.callback = function () {};
    }

    if (!this.loaded) {
        //todo: more elegant solution
        this.errMessage = "Input file not loaded yet";
        return false;
    }

    /**
     * Function to be called repeatedly when frame is rendered.
     * has two parameters; count(total #frames) and doneCount(#frames rendered)
     * @type {function}
     * @private
     */
    this.progressSignal = (options.progressSignal || function () {});
    /**
     * begin time (seconds)
     * @type {number}
     * @private
     */
    this.beginMS = (options.begin * 1000 || 0); //default begin time

    /**
     * Frames per Second
     * @type {number}
     * @private
     */
    this.FPS = (options.FPS || 60); //default FPS

    /**
     * total time in miliseconds
     * @type {number}
     * @private
     */
    this.timeMS = (options.time * 1000 || 1000);
    /**
     * Number of frames to render
     * @type {number}
     * @private
     */
    this.imagesCount = Math.round(this.FPS * this.timeMS / 1000);
    if (options.imagesCount && options.imagesCount !== this.imagesCount) {
        //imagesCount was given
        if (options.time && options.FPS) {
            //FPS+time were also given and the tree given parameters are contradicting
            this.finished = true;
            this.errMessage = "Conflicting parameters FPS,time,imagesCount";
            return false;
        } else if (options.time) {
            this.FPS = this.imagesCount * 1000 / this.timeMS;
        } else if (options.FPS) {
            this.timeMS = this.imagesCount * 1000 / this.FPS;
        }
    }

    /**
     * Time in miliseconds from the animation start time.
     * @type {int}
     * @private
     */
    this.SVGtime = 0; //in miliseconds

    /**
     * Number of already rendered images
     * @type {int}
     * @public
     */
    this.imagesDoneCount = 0;
    /**
     * Array of all rendered images in png format
     * @type {base64}
     * @public
     */
    this.images = [];
    /**
     * Array of all rendered images in png format
     * @type {number}
     * @private
     */
    this.nextFrame = setTimeout(this.renderNextFrame.bind(this), 0);
    /**
     * Canvas to draw on (optional - drawing is invisible if not provided)
     * @type {HTMLCanvasElement}
     * @private
     */
    this.canvas = (options.canvas || document.createElement('canvas')); //default begin time

    return true;
};

/**
 * Goes through DOM tree of given HTMLElement and removes specific tags
 * @param {HTMLElement} htmlElement
 * @param {String[]} tags
 * @return {integer} - number of elements removed
 *
 * @private
 */
SVGRender.prototype.filterOut = function (htmlElement, tags, lvl) {
    var ret = 0;
    var lvlString = "";
    for (var i = 0; i < lvl; i++) {
        lvlString += " ";
    }
    for (var i = 0; i < htmlElement.childNodes.length; i++) {
        if (tags.indexOf(htmlElement.childNodes[i].tagName) >= 0) {
            htmlElement.removeChild(htmlElement.childNodes[i]);
            ret++;
            i = -1;
        } else {
            //call filterOut recursively
            ret += this.filterOut(htmlElement.childNodes[i], tags, lvl + 1);
        }
    }
    return ret;
};

/**
 * Render next frame and schedule next run of render next frame
 * @returns {undefined}
 * @private
 */
SVGRender.prototype.renderNextFrame = function () {
    if (!this.svgElement) {
        throw "Cannot render - no svgElement loaded!";
    }

    if (this.interrupted) {
        //rendering was stopped
        //(this.nextFrame timeout should have been removed already!)
        throw "this.nextFrame timeout should have been removed already!";
        return;
    }

    this.SVGtime = this.beginMS + Math.round(1000 * this.imagesDoneCount) / (this.FPS);
    this.svgElement.pauseAnimations();
    this.svgElement.setCurrentTime(this.SVGtime / 1000);

    //Do deep copy of svgElement!
    //Clone element at t=0
    var svgElementNew = this.svgElement.cloneNode(true);

    //maybe unnescessary
    svgElementNew.pauseAnimations();

    //Copy styles
    this.filterOut(svgElementNew, ["animate", "animateTransform", "animateColor", "animateMotion", "animateColor"], 0);
    this.additionalData = this.exportStyle(this.svgElement, ["animate", "animateTransform", "animateColor", "animateMotion", "animateColor"]);

    this.importStyle(svgElementNew, this.additionalData);
    var svgString = (new XMLSerializer()).serializeToString(svgElementNew);

    this.svgImage = new Image();
    this.svgImage.onload = function () {

        tmpCanvasx = this.canvas.getContext('2d');
        this.canvas.width = this.svgImage.width;
        this.canvas.height = this.svgImage.height;
        tmpCanvasx.clearRect(0, 0, this.svgImage.width, this.svgImage.height);
        tmpCanvasx.drawImage(this.svgImage, 0, 0);
        //image now in tmpCanvas

        //signalize progress
        if (this.progressSignal && typeof this.progressSignal === "function") {
            this.progressSignal(this.imagesDoneCount, this.imagesCount);
        }

        this.images[this.imagesDoneCount++] = this.canvas.toDataURL("image/png").replace(/^data:.+\/(.+);base64,/, "");
        if (this.imagesDoneCount < this.imagesCount) {
            this.nextFrame = setTimeout(this.renderNextFrame.bind(this), 0);
        } else {
            this.finished = true;
            this.callback(null, this.images);
        }
    }.bind(this);

    //firefox inserts local URL into ids
    //fixing this behavior by replacing all appearances
    var docURL = window.location.href.split("#")[0];
    svgString = svgString.split(docURL + "#").join("#");

    this.svgImage.src = "data:image/svg+xml;base64," + btoa("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n\
        <!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">" + unescape(encodeURIComponent(svgString)));
};
/**
 * Pause rendering
 * @returns {undefined}
 */
SVGRender.prototype.pause = function () {
    this.interrupted = true;
    clearTimeout(this.nextFrame);
    this.nextFrame = null;
};
/**
 * Resumes rendering
 * @returns {undefined}
 */
SVGRender.prototype.resume = function () {
    if (this.finished || !this.interrupted) {
        //not needed
        return;
    }

    this.interrupted = false;
    if (!this.nextFrame) {
        //next frame is not scheduled
        this.nextFrame = setTimeout(this.renderNextFrame.bind(this), 0);
    }
};
//copy style: http://stackoverflow.com/questions/2087778/javascript-copy-style
//also copy out transformMatrix
SVGRender.prototype.exportStyle = function (el, ignored) {
    var ret = {};
    ret.children = [];
    for (var i = 0; i < el.children.length; i++) {
        if (ignored.indexOf(el.children[i].tagName) >= 0) {
            //this is ignored tag
        } else {
            ret.children.push(this.exportStyle(el.children[i], ignored));
        }
    }

    var transformAnim = el.getTransformAnim();
    if (transformAnim) {
        ret.transformAnim = transformAnim;
    }

    if (el.getCTM && typeof (el.getCTM) === "function" && el.parentNode && el.parentNode.getCTM && typeof (el.parentNode.getCTM) === "function") {
        if (el.parentNode.getCTM()) {
            ret.ctm = el.parentNode.getCTM().inverse().multiply(el.getCTM());
        }
    }

    ret.value = [];
    var styles = window.getComputedStyle(el);
    for (var i = styles.length; i-- > 0; ) {
        var name = styles[i];
        if (!name.match(/^height$/) && !name.match(/^width$/) && !name.match(/^visibility/)) {
            ret.value.push({
                "name": name,
                "value": styles.getPropertyValue(name),
                "priority": styles.getPropertyPriority(name)
            });
        }
    }
    return ret;
};

SVGRender.prototype.importStyle = function (el, data) {
    if (!data) {
        return;
    }

    var matrix = this.svgElement.createSVGMatrix();

    if (data.ctm !== undefined) {
        matrix = matrix.multiply(data.ctm);
    }

    if (data.transform !== undefined) {
        matrix = matrix.multiply(data.transform);
    }

    el.setAttribute('transform', matrix.getReadable());

    for (var i = 0; i < el.children.length; i++) {
        //recursive
        this.importStyle(el.children[i], data.children[i]);
    }

    for (var n = 0; n < data.value.length; n++) {
        el.style.setProperty(data.value[n].name,
                data.value[n].value,
                data.value[n].priority
                );
    }
};
/**
 * Deep-copy element (recursive)
 * @param {Array|String|Number|Boolean|Object} src
 * @returns {Array|String|Number|Boolean|Object}
 */
SVGRender.prototype.deepCopy = function (src) {
    var dst;
    if (Array.isArray(src)) {
        dst = [];
        for (var i = 0; i < src.length; i++) {
            dst[i] = this.deepCopy(src[i]);
        }
    } else if (typeof (src) === "string" || typeof (src) === "number" || typeof (src) === "boolean") {
        return src;
    } else if (typeof (src) === "function") {
        return null;
    } else if (typeof (src) === "object") {
        dst = {};
        for (var at in src) {
            dst[at] = this.deepCopy(src[at]);
        }
    } else {
        throw "deepCopy unknown ";
    }

    return dst;
};
/**
 * Is render in progress?
 * @returns {undefined}
 */
SVGRender.prototype.isActive = function () {
    return !(this.finished);
};

/**
 * Get last error message string
 * @public
 * @returns {String}
 */
SVGRender.prototype.getErrorMessage = function () {
    return this.errMessage;
};

SVGElement.prototype.getTransformAnim = function () {
    var matrix = document.createElementNS("http://www.w3.org/2000/svg", "svg").createSVGMatrix();
    if (!this.transform || !this.transform.animVal) {
        return matrix;
    }
    for (var i = 0; i < this.transform.animVal.length; i++) {
        matrix = matrix.multiply(this.transform.animVal[i].matrix);
    }
    return matrix;
};
SVGMatrix.prototype.getReadable = function () {
    return "matrix(" + this.a + " " + this.b + " " + this.c + " " + this.d + " " + this.e + " " + this.f + ")";
};<|endoftext|>


<commit_before>var tilelive = require('tilelive');
var TileJSON = require('tilejson');
var url = require('url');
var tar = require('tar');
var fstream = require('fstream');
var zlib = require('zlib');
var assert = require('assert');
var path = require('path');
var fs = require('fs');
var mkdirp = require('mkdirp');
var crypto = require('crypto');

function md5(str) {
    return crypto.createHash('md5').update(str).digest('hex');
}

// Load fixture data.
var fixtureDir = path.resolve(__dirname + '/fixtures/tm2z'),
    remotePath = 'http://mapbox.s3.amazonaws.com/tilelive-vector/test-tm2z.tm2z',
    xml = fs.readFileSync(fixtureDir + '/project/project.xml');

// Register vector:, tm2z:, tm2z+http: and mapbox: tilelive protocols
require('..').registerProtocols(tilelive);
tilelive.protocols['mapbox:'] = function Source(uri, callback) {
    return new TileJSON('http://a.tiles.mapbox.com/v3' + uri.pathname + '.json', callback);
};

describe('tm2z', function() {
    it('loads a tm2z url', function(done) {
        tilelive.load('tm2z://' + fixtureDir + '/project.tm2z', function(err, source) {
            if (err) throw err;
            done();
        });
    });
    it('matches expected xml', function(done) {
        tilelive.load('tm2z://' + fixtureDir + '/project.tm2z', function(err, source) {
            if (err) throw err;
            assert.equal(source._xml, xml);
            done();
        });
    });
    it('gunzips then untars', function(done) {
        tilelive.load('tm2z://' + fixtureDir + '/project.tar.gz', function(err, source) {
            if (err) throw err;
            done();
        });
    });
    it('errors out if not gzipped', function(done) {
        tilelive.load('tm2z://' + fixtureDir + '/project.tar', function(err, source) {
            assert.equal(err.code, 'Z_DATA_ERROR');
            assert.equal(err.message, 'incorrect header check');
            done();
        });
    });
    it('errors out on bad gunzip', function(done) {
        tilelive.load('tm2z://' + fixtureDir + '/doublezip.tar.gz', function(err, source) {
            assert.equal(err.message, 'invalid tar file');
            done();
        });
    });
    it('errors out if missing project.xml', function(done) {
        tilelive.load('tm2z://' + fixtureDir + '/empty.tar.gz', function(err, source) {
            assert.equal(err.message, 'project.xml not found in package');
            done();
        });
    });
    it('errors out on invalid project.xml', function(done) {
        tilelive.load('tm2z://' + fixtureDir + '/malformed.tar.gz', function(err, source) {
            assert.equal(err.message.split(':')[0], 'XML document not well formed');
            done();
        });
    });
});

describe('tm2z+http', function() {
    it('loads a tm2z+http url', function(done) {
        this.timeout(5000);
        tilelive.load('tm2z+' + remotePath, function(err, source) {
            if (err) throw err;
            done();
        });
    });
    it('matches expected xml', function(done) {
        this.timeout(5000);
        tilelive.load('tm2z+' + remotePath, function(err, source) {
            if (err) throw err;
            assert.equal(xml, source._xml);
            done();
        });
    });
    it('errors out on an invalid S3 url', function(done) {
        tilelive.load('tm2z+http://mapbox.s3.amazonaws.com/tilelive-vector/invalid.tm2z', function(err, source) {
            assert.equal('Z_DATA_ERROR', err.code);
            done();
        });
    });
});
<commit_msg>Remove requires for unused deps.<commit_after>var tilelive = require('tilelive');
var TileJSON = require('tilejson');
var url = require('url');
var assert = require('assert');
var path = require('path');
var fs = require('fs');
var crypto = require('crypto');

function md5(str) {
    return crypto.createHash('md5').update(str).digest('hex');
}

// Load fixture data.
var fixtureDir = path.resolve(__dirname + '/fixtures/tm2z'),
    remotePath = 'http://mapbox.s3.amazonaws.com/tilelive-vector/test-tm2z.tm2z',
    xml = fs.readFileSync(fixtureDir + '/project/project.xml');

// Register vector:, tm2z:, tm2z+http: and mapbox: tilelive protocols
require('..').registerProtocols(tilelive);
tilelive.protocols['mapbox:'] = function Source(uri, callback) {
    return new TileJSON('http://a.tiles.mapbox.com/v3' + uri.pathname + '.json', callback);
};

describe('tm2z', function() {
    it('loads a tm2z url', function(done) {
        tilelive.load('tm2z://' + fixtureDir + '/project.tm2z', function(err, source) {
            if (err) throw err;
            done();
        });
    });
    it('matches expected xml', function(done) {
        tilelive.load('tm2z://' + fixtureDir + '/project.tm2z', function(err, source) {
            if (err) throw err;
            assert.equal(source._xml, xml);
            done();
        });
    });
    it('gunzips then untars', function(done) {
        tilelive.load('tm2z://' + fixtureDir + '/project.tar.gz', function(err, source) {
            if (err) throw err;
            done();
        });
    });
    it('errors out if not gzipped', function(done) {
        tilelive.load('tm2z://' + fixtureDir + '/project.tar', function(err, source) {
            assert.equal(err.code, 'Z_DATA_ERROR');
            assert.equal(err.message, 'incorrect header check');
            done();
        });
    });
    it('errors out on bad gunzip', function(done) {
        tilelive.load('tm2z://' + fixtureDir + '/doublezip.tar.gz', function(err, source) {
            assert.equal(err.message, 'invalid tar file');
            done();
        });
    });
    it('errors out if missing project.xml', function(done) {
        tilelive.load('tm2z://' + fixtureDir + '/empty.tar.gz', function(err, source) {
            assert.equal(err.message, 'project.xml not found in package');
            done();
        });
    });
    it('errors out on invalid project.xml', function(done) {
        tilelive.load('tm2z://' + fixtureDir + '/malformed.tar.gz', function(err, source) {
            assert.equal(err.message.split(':')[0], 'XML document not well formed');
            done();
        });
    });
});

describe('tm2z+http', function() {
    it('loads a tm2z+http url', function(done) {
        this.timeout(5000);
        tilelive.load('tm2z+' + remotePath, function(err, source) {
            if (err) throw err;
            done();
        });
    });
    it('matches expected xml', function(done) {
        this.timeout(5000);
        tilelive.load('tm2z+' + remotePath, function(err, source) {
            if (err) throw err;
            assert.equal(xml, source._xml);
            done();
        });
    });
    it('errors out on an invalid S3 url', function(done) {
        tilelive.load('tm2z+http://mapbox.s3.amazonaws.com/tilelive-vector/invalid.tm2z', function(err, source) {
            assert.equal('Z_DATA_ERROR', err.code);
            done();
        });
    });
});
<|endoftext|>


<commit_before>const ParseError = require("../lib/ParseError").default
const ParseSuccess = require("../lib/ParseSuccess").default
const Context = require("../lib/Context").default
const Grammar = require("../lib/Grammar").default
const Rule = require("../lib/Rule").default
const {Terminal, CharRange, Reference, Any, Sequence, Choice, Repeat, Optional, Not} = require("../lib/Rules")

function testRule(rule, source, pass = true, grammar = new Grammar({})) {
    var ctx = new Context(source, 0, {}, grammar);
    var match = rule.parse(ctx);
    console.log(source)
    console.log(match.toString())
    if (pass != (match instanceof ParseSuccess))
        throw new Error("Rule should have " + (pass ? "passed " : "failed ") + "but didn't!");
    console.log("PASSED!\n")
}

var alphaLower = new CharRange('a', 'z')
var alphaUpper = new CharRange('A', 'Z')
var alpha = new Choice(alphaLower, alphaUpper)
var num = new CharRange('0', '9')
var ws = new Terminal(" ")
var word = new Repeat(alpha, 1)
var number = new Repeat(num, 1)
var mws = new Repeat(ws, 1)
var end = new Not(new Any())
var comma = new Terminal(',')
function makeListRule(rule) {
    return new Sequence(
    rule,
     new Repeat(
         new Sequence(
             comma,
             rule
         )))
}

var test = new Sequence(word, mws, number, end)
testRule(test, "abc 123");

testRule(test, "abc 123!", false);

test = new Sequence(makeListRule(number), end)
testRule(test, "1,2,3,4,5");

var grammar = new Grammar({
    "list" : new Sequence(
        new Terminal('['),
        makeListRule(new Reference("value")),
        new Terminal(']')
        ),
    "value" : new Choice(number, new Reference("list"))
});
test = new Sequence(new Reference("list"), end)
testRule(new Sequence(new Reference("list"), end), "[1,[2,3],[4,[5]]]", true, grammar)
testRule(new Sequence(new Reference("list"), end), "[1,[2,3],[4,[5]]]]", false, grammar)
<commit_msg>Cleaned up tests more.<commit_after>const ParseError = require("../lib/ParseError").default
const ParseSuccess = require("../lib/ParseSuccess").default
const Context = require("../lib/Context").default
const Grammar = require("../lib/Grammar").default
const Rule = require("../lib/Rule").default
const {Terminal, CharRange, Reference, Any, Sequence, Choice, Repeat, Optional, Not} = require("../lib/Rules")

function testRule(rule, source, pass = true, grammar = new Grammar({})) {
    var ctx = new Context(source, 0, {}, grammar);
    var match = rule.parse(ctx);
    console.log(source)
    console.log(match.toString())
    if (pass != (match instanceof ParseSuccess))
        throw new Error("Rule should have " + (pass ? "passed " : "failed ") + "but didn't!");
    console.log("PASSED!\n")
}

var alphaLower = new CharRange('a', 'z')
var alphaUpper = new CharRange('A', 'Z')
var alpha = new Choice(alphaLower, alphaUpper)
var num = new CharRange('0', '9')
var ws = new Terminal(" ")
var word = new Repeat(alpha, 1)
var number = new Repeat(num, 1)
var mws = new Repeat(ws, 1)
var end = new Not(new Any())
var comma = new Terminal(',')
function makeListRule(rule) {
    return new Sequence(
    rule,
     new Repeat(
         new Sequence(
             comma,
             rule
         )))
}

var test = new Sequence(word, mws, number, end)
testRule(test, "abc 123");
testRule(test, "abc 123!", false);

testRule(new Sequence(makeListRule(number), end), "1,2,3,4,5");

var grammar = new Grammar({
    "list" : new Sequence(
        new Terminal('['),
        makeListRule(new Reference("value")),
        new Terminal(']')
        ),
    "value" : new Choice(number, new Reference("list"))
});
test = new Sequence(new Reference("list"), end);
testRule(test, "[1,[2,3],[4,[5]]]", true, grammar)
testRule(test, "[1,[2,3],[4,[5]]]]", false, grammar)
<|endoftext|>


<commit_before>const tape = require('tape')
const u = require('../util')

tape('set, get, prefix', (t) => {
  const o = {}
  u.set(o, ['foo', 'bar', 'baz'], 24)

  t.equal(u.get(o, ['foo', 'bar', 'baz']), 24)

  t.deepEqual(o, { foo: { bar: { baz: 24 } } })

  t.notOk(u.prefix(o, ['foo', 'baz']))

  t.notOk(u.prefix(o, ['foo']))

  t.notOk(u.prefix(o, ['blah']))

  t.end()
})

tape('prefix 0', (t) => {
  t.ok(
    u.prefix({
      foo: true
    }, ['foo', 'bar'])
  )

  t.end()
})
<commit_msg>test errorAsStreamOrCb<commit_after>const tape = require('tape')
const u = require('../util')

tape('set, get, prefix', (t) => {
  const o = {}
  u.set(o, ['foo', 'bar', 'baz'], 24)

  t.equal(u.get(o, ['foo', 'bar', 'baz']), 24)

  t.deepEqual(o, { foo: { bar: { baz: 24 } } })

  t.notOk(u.prefix(o, ['foo', 'baz']))

  t.notOk(u.prefix(o, ['foo']))

  t.notOk(u.prefix(o, ['blah']))

  t.end()
})

tape('prefix 0', (t) => {
  t.ok(
    u.prefix({
      foo: true
    }, ['foo', 'bar'])
  )

  t.end()
})

tape('errorAsStreamOrCb async', t => {
  t.plan(1)
  u.errorAsStreamOrCb('async', new Error('foo'), (err) => {
    t.equal(err.message, 'foo')
  })
})

tape('errorAsStreamOrCb source', t => {
  t.plan(1)
  const source = u.errorAsStreamOrCb('source', new Error('foo'), null)
  t.equals(typeof source, 'function')
})

tape('errorAsStreamOrCb sink', t => {
  t.plan(1)
  const sink = u.errorAsStreamOrCb('sink', new Error('foo'), null)
  t.equals(typeof sink, 'function')
})

tape('errorAsStreamOrCb duplex', t => {
  t.plan(3)
  const duplex = u.errorAsStreamOrCb('duplex', new Error('foo'), null)
  t.equals(typeof duplex, 'object')
  t.equals(typeof duplex.source, 'function')
  t.equals(typeof duplex.sink, 'function')
})<|endoftext|>