text: 'use strict';
  
  function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
  
  var pouchdbErrors = require('pouchdb-errors');
  var pouchdbUtils = require('pouchdb-utils');
  var pouchdbSelectorCore = require('pouchdb-selector-core');
  var vm = _interopDefault(require('vm'));
  
  function evalFilter(input) {
    var code = '(function() {\n"use strict";\nreturn ' + input + '\n})()';
  
    return vm.runInNewContext(code);
  }
  
  function evalView(input) {
    var code = [
      '"use strict";',
      'var emitted = false;',
      'var emit = function (a, b) {',
      '  emitted = true;',
      '};',
      'var view = ' + input + ';',
      'view(doc);',
      'if (emitted) {',
      '  return true;',
      '}'
    ].join('\n');
  
    return vm.runInNewContext('(function(doc) {\n' + code + '\n})');
  }
  
  function validate(opts, callback) {
    if (opts.selector) {
      if (opts.filter && opts.filter !== '_selector') {
        var filterName = typeof opts.filter === 'string' ?
          opts.filter : 'function';
        return callback(new Error('selector invalid for filter "' + filterName + '"'));
      }
    }
    callback();
  }
  
  function normalize(opts) {
    if (opts.view && !opts.filter) {
      opts.filter = '_view';
    }
  
    if (opts.selector && !opts.filter) {
      opts.filter = '_selector';
    }
  
    if (opts.filter && typeof opts.filter === 'string') {
      if (opts.filter === '_view') {
        opts.view = pouchdbUtils.normalizeDdocFunctionName(opts.view);
      } else {
        opts.filter = pouchdbUtils.normalizeDdocFunctionName(opts.filter);
      }
    }
  }
  
  function shouldFilter(changesHandler, opts) {
    return opts.filter && typeof opts.filter === 'string' &&
      !opts.doc_ids && !pouchdbUtils.isRemote(changesHandler.db);
  }
  
  function filter(changesHandler, opts) {
    var callback = opts.complete;
    if (opts.filter === '_view') {
      if (!opts.view || typeof opts.view !== 'string') {
        var err = pouchdbErrors.createError(pouchdbErrors.BAD_REQUEST,
          '`view` filter parameter not found or invalid.');
        return callback(err);
      }
      // fetch a view from a design doc, make it behave like a filter
      var viewName = pouchdbUtils.parseDdocFunctionName(opts.view);
      changesHandler.db.get('_design/' + viewName[0], function (err, ddoc) {
        /* istanbul ignore if */
        if (changesHandler.isCancelled) {
          return callback(null, {status: 'cancelled'});
        }
        /* istanbul ignore next */
        if (err) {
          return callback(pouchdbErrors.generateErrorFromResponse(err));
        }
        var mapFun = ddoc && ddoc.views && ddoc.views[viewName[1]] &&
          ddoc.views[viewName[1]].map;
        if (!mapFun) {
          return callback(pouchdbErrors.createError(pouchdbErrors.MISSING_DOC,
            (ddoc.views ? 'missing json key: ' + viewName[1] :
              'missing json key: views')));
        }
        opts.filter = evalView(mapFun);
        changesHandler.doChanges(opts);
      });
    } else if (opts.selector) {
      opts.filter = function (doc) {
        return pouchdbSelectorCore.matchesSelector(doc, opts.selector);
      };
      changesHandler.doChanges(opts);
    } else {
      // fetch a filter from a design doc
      var filterName = pouchdbUtils.parseDdocFunctionName(opts.filter);
      changesHandler.db.get('_design/' + filterName[0], function (err, ddoc) {
        /* istanbul ignore if */
        if (changesHandler.isCancelled) {
          return callback(null, {status: 'cancelled'});
        }
        /* istanbul ignore next */
        if (err) {
          return callback(pouchdbErrors.generateErrorFromResponse(err));
        }
        var filterFun = ddoc && ddoc.filters && ddoc.filters[filterName[1]];
        if (!filterFun) {
          return callback(pouchdbErrors.createError(pouchdbErrors.MISSING_DOC,
            ((ddoc && ddoc.filters) ? 'missing json key: ' + filterName[1]
              : 'missing json key: filters')));
        }
        opts.filter = evalFilter(filterFun);
        changesHandler.doChanges(opts);
      });
    }
  }
  
  function applyChangesFilterPlugin(PouchDB) {
    PouchDB._changesFilterPlugin = {
      validate: validate,
      normalize: normalize,
      shouldFilter: shouldFilter,
      filter: filter
    };
  }
  
  module.exports = applyChangesFilterPlugin;
  