(function () {
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.
//
// Based on the normalizeFunction which can be
// found here:
//
//  https://github.com/dmunch/couch-chakra/blob/master/js/normalizeFunction.js

function rewriteFunInt(source) {
    return source;
}
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.

var Dreyfus = (function() {

  var index_results = []; // holds temporary emitted values during index

  function handleIndexError(err, doc) {
    if (err == "fatal_error") {
      throw(["error", "map_runtime_error", "function raised 'fatal_error'"]);
    } else if (err[0] == "fatal") {
      throw(err);
    }
    var message = "function raised exception " + errstr(err);
    if (doc) message += " with doc._id " + doc._id;
    log(message);
  };

  return {
    index: function(name, value, options) {
      if (typeof name !== 'string') {
        throw({name: 'TypeError', message: 'name must be a string not ' + typeof name});
      }
      if (name.substring(0, 1) === '_') {
        throw({name: 'ReservedName', message: 'name must not start with an underscore'});
      }
      if (typeof value !== 'string' && typeof value !== 'number' && typeof value !== 'boolean') {
        throw({name: 'TypeError', message: 'value must be a string, a number or boolean not ' + typeof value});
      }
      if (options && typeof options !== 'object') {
        throw({name: 'TypeError', message: 'options must be an object not ' + typeof options});
      }
      index_results.push([name, value, options || {}]);
    },

    indexDoc: function(doc) {
      Couch.recursivelySeal(doc);
      var buf = [];
      for (var fun in State.funs) {
        index_results = [];
        try {
          State.funs[fun](doc);
          buf.push(index_results);
        } catch (err) {
          handleIndexError(err, doc);
          buf.push([]);
        }
      }
      print(JSON.stringify(buf));
    }

  }
})();
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.

var Nouveau = (function () {

  var index_results = []; // holds temporary emitted values during index

  function handleIndexError(err, doc) {
    if (err == "fatal_error") {
      throw (["error", "map_runtime_error", "function raised 'fatal_error'"]);
    } else if (err[0] == "fatal") {
      throw (err);
    }
    var message = "function raised exception " + err.toSource();
    if (doc) message += " with doc._id " + doc._id;
    log(message);
  };

  function assertType(name, expected, actual) {
    if (typeof actual !== expected) {
      throw ({ name: 'TypeError', message: 'type of ' + name + ' must be a ' + expected + ' not ' + typeof actual });
    }
  };

  function rejectReservedName(name) {
    if (name.substring(0, 1) === '_') {
      throw ({ name: 'ReservedName', message: 'name must not start with an underscore' });
    }
  };

  return {
    index: function (doc) {
      var type = arguments[0];
      var name = arguments[1];

      assertType('type', 'string', type);
      assertType('name', 'string', name);

      rejectReservedName(name);

      switch (type) {
        case 'double':
        case 'string':
          var value = arguments[2];
          var options = arguments[3] || {};
          assertType('value', type == 'double' ? 'number' : 'string', value);
          index_results.push({
            '@type': type,
            'name': name,
            'value': value,
            'store': options.store,
            'facet': options.facet
          });
          break;
        case 'text':
          var value = arguments[2];
          var options = arguments[3] || {};
          assertType('value', 'string', value);
          index_results.push({
            '@type': type,
            'name': name,
            'value': value,
            'store': options.store
          });
          break;
        case 'stored':
          var value = arguments[2];
          if (typeof value != 'number' && typeof value != 'string') {
            throw ({ name: 'TypeError', message: 'type of ' + value + ' must be a string or number' });
          }
          index_results.push({
            '@type': type,
            'name': name,
            'value': value
          });
          break;
        default:
          throw ({ name: 'TypeError', message: type + ' not supported' });
      }
    },

    indexDoc: function (doc) {
      Couch.recursivelySeal(doc);
      var buf = [];
      for (var fun in State.funs) {
        index_results = [];
        try {
          State.funs[fun](doc);
          buf.push(index_results);
        } catch (err) {
          handleIndexError(err, doc);
          buf.push([]);
        }
      }
      print(JSON.stringify(buf));
    }

  }
})();
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.

var Filter = (function() {

  var view_emit = false;

  return {
      emit : function(key, value) {
        view_emit = true;
      },
      filter : function(fun, ddoc, args) {
        var results = [];
        var docs = args[0];
        var req = args[1];
        for (var i=0; i < docs.length; i++) {
          results.push((fun.apply(ddoc, [docs[i], req]) && true) || false);
        };
        respond([true, results]);
      },
      filter_view : function(fun, ddoc, args) {
        var results = [];
        var docs = args[0];
        for (var i=0; i < docs.length; i++) {
          view_emit = false;
          fun(docs[i]);
          results.push((view_emit && true) || false);
        };
        respond([true, results]);
      }
    }
})();
// mimeparse.js
//
// This module provides basic functions for handling mime-types. It can
// handle matching mime-types against a list of media-ranges. See section
// 14.1 of the HTTP specification [RFC 2616] for a complete explanation.
//
//   http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1
//
// A port to JavaScript of Joe Gregorio's MIME-Type Parser:
//
//   http://code.google.com/p/mimeparse/
//
// Ported by J. Chris Anderson <jchris@apache.org>, targeting the Spidermonkey runtime.
//
// To run the tests, open mimeparse-js-test.html in a browser.
// Ported from version 0.1.2
// Comments are mostly excerpted from the original.

var Mimeparse = (function() {
  // private helpers
  function strip(string) {
    return string.replace(/^\s+/, '').replace(/\s+$/, '');
  };

  function parseRanges(ranges) {
    var parsedRanges = [], rangeParts = ranges.split(",");
    for (var i=0; i < rangeParts.length; i++) {
      parsedRanges.push(publicMethods.parseMediaRange(rangeParts[i]));
    };
    return parsedRanges;
  };

  var publicMethods = {
    // Carves up a mime-type and returns an Array of the
    //  [type, subtype, params] where "params" is a Hash of all
    //  the parameters for the media range.
    //
    // For example, the media range "application/xhtml;q=0.5" would
    //  get parsed into:
    //
    // ["application", "xhtml", { "q" : "0.5" }]
    parseMimeType : function(mimeType) {
      var fullType, typeParts, params = {}, parts = mimeType.split(';');
      for (var i=0; i < parts.length; i++) {
        var p = parts[i].split('=');
        if (p.length == 2) {
          params[strip(p[0])] = strip(p[1]);
        }
      };
      fullType = parts[0].replace(/^\s+/, '').replace(/\s+$/, '');
      if (fullType == '*') fullType = '*/*';
      typeParts = fullType.split('/');
      return [typeParts[0], typeParts[1], params];
    },

    // Carves up a media range and returns an Array of the
    //  [type, subtype, params] where "params" is a Object with
    //  all the parameters for the media range.
    //
    // For example, the media range "application/*;q=0.5" would
    //  get parsed into:
    //
    // ["application", "*", { "q" : "0.5" }]
    //
    // In addition this function also guarantees that there
    //  is a value for "q" in the params dictionary, filling it
    //  in with a proper default if necessary.
    parseMediaRange : function(range) {
      var q, parsedType = this.parseMimeType(range);
      if (!parsedType[2]['q']) {
        parsedType[2]['q'] = '1';
      } else {
        q = parseFloat(parsedType[2]['q']);
        if (isNaN(q)) {
          parsedType[2]['q'] = '1';
        } else if (q > 1 || q < 0) {
          parsedType[2]['q'] = '1';
        }
      }
      return parsedType;
    },

    // Find the best match for a given mime-type against
    // a list of media_ranges that have already been
    // parsed by parseMediaRange(). Returns an array of
    // the fitness value and the value of the 'q' quality
    // parameter of the best match, or (-1, 0) if no match
    // was found. Just as for qualityParsed(), 'parsed_ranges'
    // must be a list of parsed media ranges.
    fitnessAndQualityParsed : function(mimeType, parsedRanges) {
      var bestFitness = -1, bestFitQ = 0, target = this.parseMediaRange(mimeType);
      var targetType = target[0], targetSubtype = target[1], targetParams = target[2];

      for (var i=0; i < parsedRanges.length; i++) {
        var parsed = parsedRanges[i];
        var type = parsed[0], subtype = parsed[1], params = parsed[2];
        if ((type == targetType || type == "*" || targetType == "*") &&
          (subtype == targetSubtype || subtype == "*" || targetSubtype == "*")) {
          var matchCount = 0;
          for (var param in targetParams) {
            if (param != 'q' && params[param] && params[param] == targetParams[param]) {
              matchCount += 1;
            }
          }

          var fitness = (type == targetType) ? 100 : 0;
          fitness += (subtype == targetSubtype) ? 10 : 0;
          fitness += matchCount;

          if (fitness > bestFitness) {
            bestFitness = fitness;
            bestFitQ = params["q"];
          }
        }
      };
      return [bestFitness, parseFloat(bestFitQ)];
    },

    // Find the best match for a given mime-type against
    // a list of media_ranges that have already been
    // parsed by parseMediaRange(). Returns the
    // 'q' quality parameter of the best match, 0 if no
    // match was found. This function bahaves the same as quality()
    // except that 'parsedRanges' must be a list of
    // parsed media ranges.
    qualityParsed : function(mimeType, parsedRanges) {
      return this.fitnessAndQualityParsed(mimeType, parsedRanges)[1];
    },

    // Returns the quality 'q' of a mime-type when compared
    // against the media-ranges in ranges. For example:
    //
    // >>> Mimeparse.quality('text/html','text/*;q=0.3, text/html;q=0.7, text/html;level=1, text/html;level=2;q=0.4, */*;q=0.5')
    // 0.7
    quality : function(mimeType, ranges) {
      return this.qualityParsed(mimeType, parseRanges(ranges));
    },

    // Takes a list of supported mime-types and finds the best
    // match for all the media-ranges listed in header. The value of
    // header must be a string that conforms to the format of the
    // HTTP Accept: header. The value of 'supported' is a list of
    // mime-types.
    //
    // >>> bestMatch(['application/xbel+xml', 'text/xml'], 'text/*;q=0.5,*/*; q=0.1')
    // 'text/xml'
    bestMatch : function(supported, header) {
      var parsedHeader = parseRanges(header);
      var weighted = [];
      for (var i=0; i < supported.length; i++) {
        weighted.push([publicMethods.fitnessAndQualityParsed(supported[i], parsedHeader), i, supported[i]]);
      };
      weighted.sort();
      return weighted[weighted.length-1][0][1] ? weighted[weighted.length-1][2] : '';
    }
  };
  return publicMethods;
})();
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.


var Mime = (function() {
  // registerType(name, mime-type, mime-type, ...)
  // 
  // Available in query server sandbox. TODO: The list is cleared on reset.
  // This registers a particular name with the set of mimetypes it can handle.
  // Whoever registers last wins.
  // 
  // Example: 
  // registerType("html", "text/html; charset=utf-8");

  var mimesByKey = {};
  var keysByMime = {};
  function registerType() {
    var mimes = [], key = arguments[0];
    for (var i=1; i < arguments.length; i++) {
      mimes.push(arguments[i]);
    };
    mimesByKey[key] = mimes;
    for (var i=0; i < mimes.length; i++) {
      keysByMime[mimes[i]] = key;
    };
  }

  // Some default types
  // Ported from Ruby on Rails
  // Build list of Mime types for HTTP responses
  // http://www.iana.org/assignments/media-types/
  // http://dev.rubyonrails.org/svn/rails/trunk/actionpack/lib/action_controller/mime_types.rb

  registerType("all", "*/*");
  registerType("text", "text/plain; charset=utf-8", "txt");
  registerType("html", "text/html; charset=utf-8");
  registerType("xhtml", "application/xhtml+xml", "xhtml");
  registerType("xml", "application/xml", "text/xml", "application/x-xml");
  registerType("js", "text/javascript", "application/javascript", "application/x-javascript");
  registerType("css", "text/css");
  registerType("ics", "text/calendar");
  registerType("csv", "text/csv");
  registerType("rss", "application/rss+xml");
  registerType("atom", "application/atom+xml");
  registerType("yaml", "application/x-yaml", "text/yaml");
  // just like Rails
  registerType("multipart_form", "multipart/form-data");
  registerType("url_encoded_form", "application/x-www-form-urlencoded");
  // http://www.ietf.org/rfc/rfc4627.txt
  registerType("json", "application/json", "text/x-json");


  var providesUsed = false;
  var mimeFuns = [];
  var responseContentType = null;

  function provides(type, fun) {
    providesUsed = true;
    mimeFuns.push([type, fun]);
  };

  function resetProvides() {
    // set globals
    providesUsed = false;
    mimeFuns = [];
    responseContentType = null;
  };

  function runProvides(req, ddoc) {
    var supportedMimes = [], bestFun, bestKey = null, accept = req.headers["Accept"];
    if (req.query && req.query.format) {
      bestKey = req.query.format;
      responseContentType = mimesByKey[bestKey][0];
    } else if (accept) {
      // log("using accept header: "+accept);
      mimeFuns.reverse().forEach(function(mimeFun) {
        var mimeKey = mimeFun[0];
        if (mimesByKey[mimeKey]) {
          supportedMimes = supportedMimes.concat(mimesByKey[mimeKey]);
        }
      });
      responseContentType = Mimeparse.bestMatch(supportedMimes, accept);
      bestKey = keysByMime[responseContentType];
    } else {
      // just do the first one
      bestKey = mimeFuns[0][0];
      responseContentType = mimesByKey[bestKey][0];
    }

    if (bestKey) {
      for (var i=0; i < mimeFuns.length; i++) {
        if (mimeFuns[i][0] == bestKey) {
          bestFun = mimeFuns[i][1];
          break;
        }
      };
    };

    if (bestFun) {
      return bestFun.call(ddoc);
    } else {
      var supportedTypes = mimeFuns.map(function(mf) {
        return mimesByKey[mf[0]].join(', ') || mf[0];
      });
      throw(["error","not_acceptable",
        "Content-Type "+(accept||bestKey)+" not supported, try one of: "+supportedTypes.join(', ')]);
    }
  };


  return {
    registerType : registerType,
    provides : provides,
    resetProvides : resetProvides,
    runProvides : runProvides,
    providesUsed : function () {
      return providesUsed;
    },
    responseContentType : function () {
      return responseContentType;
    }
  };
})();




////
////  Render dispatcher
////
////
////
////

var Render = (function() {
  var new_header = false;
  var chunks = [];
  
  
  //  Start chunks
  var startResp = {};
  function start(resp) {
    startResp = resp || {};
    new_header = true;
  };

  function sendStart() {
    startResp = applyContentType((startResp || {}), Mime.responseContentType());
    respond(["start", chunks, startResp]);
    chunks = [];
    startResp = {};
    new_header = false;
  }

  function applyContentType(resp, responseContentType) {
    resp["headers"] = resp["headers"] || {};
    if (responseContentType) {
      resp["headers"]["Content-Type"] = resp["headers"]["Content-Type"] || responseContentType;
    }
    return resp;
  }

  function send(chunk) {
    chunks.push(chunk.toString());
  };

  function blowChunks(label) {
    if (new_header) {
      respond([label||"chunks", chunks, startResp]);
      new_header = false;
    }
    else {
      respond([label||"chunks", chunks]);
    }
    chunks = [];
  };

  var gotRow = false, lastRow = false;
  function getRow() {
    if (lastRow) return null;
    if (!gotRow) {
      gotRow = true;
      sendStart();
    } else {
      blowChunks();
    }
    var json = JSON.parse(readline());
    if (json[0] == "list_end") {
      lastRow = true;
      return null;
    }
    if (json[0] != "list_row") {
      throw(["fatal", "list_error", "not a row '" + json[0] + "'"]);
    }
    return json[1];
  };

  
  function maybeWrapResponse(resp) {
    var type = typeof resp;
    if ((type == "string") || (type == "xml")) {
      return {body:resp};
    } else {
      return resp;
    }
  };

  // from http://javascript.crockford.com/remedial.html
  function typeOf(value) {
    var s = typeof value;
    if (s === 'object') {
      if (value) {
        if (value instanceof Array) {
          s = 'array';
        }
      } else {
        s = 'null';
      }
    }
    return s;
  };

  function isDocRequestPath(info) {
    var path = info.path;
    return path.length > 5;
  };

  function runShow(fun, ddoc, args) {
    try {
      resetList();
      Mime.resetProvides();
      var resp = fun.apply(ddoc, args) || {};
      resp = maybeWrapResponse(resp);

      // handle list() style API
      if (chunks.length && chunks.length > 0) {
        resp.headers = resp.headers || {};
        for(var header in startResp) {
          resp.headers[header] = startResp[header];
        }
        resp.body = chunks.join("") + (resp.body || "");
        resetList();
      }

      if (Mime.providesUsed()) {
        var provided_resp = Mime.runProvides(args[1], ddoc) || {};
        provided_resp = maybeWrapResponse(provided_resp);
        resp.body = (resp.body || "") + chunks.join("");
        resp.body += provided_resp.body || "";
        resp = applyContentType(resp, Mime.responseContentType());
        resetList();
      }

      var type = typeOf(resp);
      if (type == 'object' || type == 'string') {
        respond(["resp", maybeWrapResponse(resp)]);
      } else {
        throw(["error", "render_error", "undefined response from show function"]);      
      }
    } catch(e) {
      if (args[0] === null && isDocRequestPath(args[1])) {
        throw(["error", "not_found", "document not found"]);
      } else {
        renderError(e, fun.toString());
      }
    }
  };

  function runUpdate(fun, ddoc, args) {
    try {
      var method = args[1].method;
      // for analytics logging applications you might want to remove the next line
      if (method == "GET") throw(["error","method_not_allowed","Update functions do not allow GET"]);
      var result = fun.apply(ddoc, args);
      var doc = result[0];
      var resp = result[1];
      var type = typeOf(resp);
      if (type == 'object' || type == 'string') {
        respond(["up", doc, maybeWrapResponse(resp)]);
      } else {
        throw(["error", "render_error", "undefined response from update function"]);      
      }
    } catch(e) {
      renderError(e, fun.toString());
    }
  };

  function resetList() {
    gotRow = false;
    lastRow = false;
    chunks = [];
    startResp = {};
    new_header = false;
  };

  function runList(listFun, ddoc, args) {
    try {
      Mime.resetProvides();
      resetList();
      var head = args[0];
      var req = args[1];
      var tail = listFun.apply(ddoc, args);

      if (Mime.providesUsed()) {
        tail = Mime.runProvides(req, ddoc);
      }
      if (!gotRow) getRow();
      if (typeof tail != "undefined") {
        chunks.push(tail);
      }
      blowChunks("end");
    } catch(e) {
      renderError(e, listFun.toString());
    }
  };

  function runRewrite(fun, ddoc, args) {
      var result;
      try {
        result = fun.apply(ddoc, args);
      } catch(error) {
        renderError(error, fun.toString(), "rewrite_error");
      }

      if (!result) {
        respond(["no_dispatch_rule"]);
        return;
      }

      if (typeof result === "string") {
        result = {path: result, method: args[0].method};
      }
      respond(["ok", result]);
  }

  function renderError(e, funSrc, errType) {
    if (e.error && e.reason || e[0] == "error" || e[0] == "fatal") {
      throw(e);
    } else {
      var logMessage = "function raised error: " +
                        errstr(e) + " \n" +
                       "stacktrace: " + e.stack;
      log(logMessage);
      throw(["error", errType || "render_error", logMessage]);
    }
  };

  function escapeHTML(string) {
    return string && string.replace(/&/g, "&amp;")
                 .replace(/</g, "&lt;")
                 .replace(/>/g, "&gt;");
  };

  
  return {
    start : start,
    send : send,
    getRow : getRow,
    show : function(fun, ddoc, args) {
      // var showFun = Couch.compileFunction(funSrc);
      runShow(fun, ddoc, args);
    },
    update : function(fun, ddoc, args) {
      // var upFun = Couch.compileFunction(funSrc);
      runUpdate(fun, ddoc, args);
    },
    list : function(fun, ddoc, args) {
      runList(fun, ddoc, args);
    },
    rewrite : function(fun, ddoc, args) {
      runRewrite(fun, ddoc, args);
    }
  };
})();

// send = Render.send;
// getRow = Render.getRow;
// start = Render.start;

// unused. this will be handled in the Erlang side of things.
// function htmlRenderError(e, funSrc) {
//   var msg = ["<html><body><h1>Render Error</h1>",
//     "<p>JavaScript function raised error: ",
//     e.toString(),
//     "</p><h2>Stacktrace:</h2><code><pre>",
//     escapeHTML(e.stack),
//     "</pre></code><h2>Function source:</h2><code><pre>",
//     escapeHTML(funSrc),
//     "</pre></code></body></html>"].join('');
//   return {body:msg};
// };
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.

var State = {
  reset : function(config) {
    // clear the globals and run gc
    State.funs = [];
    State.lib = null;
    State.query_config = config || {};
    gc();
    print("true"); // indicates success
  },
  addFun : function(newFun, option) {
    // Compile to a function and add it to funs array
    switch (option) {
      case 'nouveau':
        var sandbox = create_nouveau_sandbox();
        break;
      default:
        var sandbox = create_dreyfus_sandbox();
        break;
    }
    State.funs.push(Couch.compileFunction(newFun, {views : {lib : State.lib}}, undefined, sandbox));
    print("true");
  },
  addLib : function(lib) {
    State.lib = lib;
    print("true");
  }
};
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.

var resolveModule = function(names, mod, root) {
  if (names.length == 0) {
    if (typeof mod.current != "string") {
      throw ["error","invalid_require_path",
        'Must require a JavaScript string, not: '+(typeof mod.current)];
    }
    return {
      current : mod.current,
      parent : mod.parent,
      id : mod.id,
      exports : {}
    };
  }
  // we need to traverse the path
  var n = names.shift();
  if (n == '..') {
    if (!(mod.parent && mod.parent.parent)) {
      throw ["error", "invalid_require_path", 'Object has no parent '+JSON.stringify(mod.current)];
    }
    return resolveModule(names, {
      id : mod.id.slice(0, mod.id.lastIndexOf('/')),
      parent : mod.parent.parent,
      current : mod.parent.current
    });
  } else if (n == '.') {
    if (!mod.parent) {
      throw ["error", "invalid_require_path", 'Object has no parent '+JSON.stringify(mod.current)];
    }
    return resolveModule(names, {
      parent : mod.parent,
      current : mod.current,
      id : mod.id
    });
  } else if (root) {
    mod = {current : root};
  }
  if (mod.current[n] === undefined) {
    throw ["error", "invalid_require_path", 'Object has no property "'+n+'". '+JSON.stringify(mod.current)];
  }
  return resolveModule(names, {
    current : mod.current[n],
    parent : mod,
    id : mod.id ? mod.id + '/' + n : n
  });
};

var Couch = {
  // moving this away from global so we can move to json2.js later
  compileFunction : function(source, ddoc, name, sandbox) {
    if (!source) throw(["error","not_found","missing function"]);

    var functionObject = null;
    var sandbox = sandbox || create_sandbox();

    var require = function(name, module) {
      module = module || {};
      var newModule = resolveModule(name.split('/'), module.parent, ddoc);
      if (!ddoc._module_cache.hasOwnProperty(newModule.id)) {
        // create empty exports object before executing the module,
        // stops circular requires from filling the stack
        ddoc._module_cache[newModule.id] = {};
        var s = "(function (module, exports, require) { " + newModule.current + "\n });";
        try {
          var func = sandbox ? evalcx(s, sandbox, newModule.id) : eval(s);
          func.apply(sandbox, [newModule, newModule.exports, function(name) {
            return require(name, newModule);
          }]);
        } catch(e) {
          throw [
            "error",
            "compilation_error",
            "Module require('" +name+ "') raised error " + errstr(e)
          ];
        }
        ddoc._module_cache[newModule.id] = newModule.exports;
      }
      return ddoc._module_cache[newModule.id];
    };

    if (ddoc) {
      sandbox.require = require;
      if (!ddoc._module_cache) ddoc._module_cache = {};
    }

    try {
      if(typeof CoffeeScript === "undefined") {
        var rewrittenFun = rewriteFunInt(source);
        functionObject = evalcx(rewrittenFun, sandbox, name);
      } else {
        var transpiled = CoffeeScript.compile(source, {bare: true});
        functionObject = evalcx(transpiled, sandbox, name);
      }
    } catch (err) {
      throw([
        "error",
        "compilation_error",
        errstr(err) + " (" + source + ")"
      ]);
    };
    if (typeof(functionObject) == "function") {
      return functionObject;
    } else {
      throw(["error","compilation_error",
        "Expression does not eval to a function. (" + source.toString() + ")"]);
    };
  },
  recursivelySeal : function(obj) {
    seal(obj);
    for (var propname in obj) {
      if (typeof obj[propname] == "object") {
        arguments.callee(obj[propname]);
      }
    }
  }
};

function errstr(e) {
  // toSource() is a Spidermonkey "special"
  return (e.toSource ? e.toSource() : e.toString());
};

// prints the object as JSON, and rescues and logs any JSON.stringify() related errors
function respond(obj) {
  try {
    print(JSON.stringify(obj));
  } catch(e) {
    log("Error converting object to JSON: " + e.toString());
    log("error on obj: "+ obj.toString());
  }
};

function log(message) {
  // idea: query_server_config option for log level
  if (typeof message == "xml") {
    message = message.toXMLString();
  } else if (typeof message != "string") {
    message = JSON.stringify(message);
  }
  respond(["log", String(message)]);
};

function isArray(obj) {
  return toString.call(obj) === "[object Array]";
}
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.

var Validate = {
  validate : function(fun, ddoc, args) {
    try {
      fun.apply(ddoc, args);
      respond(1);
    } catch (error) {
      if (error.name && error.stack) {
        throw error;
      }
      respond(error);
    }
  }
};
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.



var Views = (function() {

  var map_results = []; // holds temporary emitted values during doc map

  function runReduce(reduceFuns, keys, values, rereduce) {
    var code_size = 0;
    for (var i in reduceFuns) {
      var fun_body =  reduceFuns[i];
      code_size += fun_body.length;
      reduceFuns[i] = Couch.compileFunction(fun_body);
    };
    var reductions = new Array(reduceFuns.length);
    for(var i = 0; i < reduceFuns.length; i++) {
      try {
        reductions[i] = reduceFuns[i](keys, values, rereduce);
      } catch (err) {
        handleViewError(err);
        // if the error is not fatal, ignore the results and continue
        reductions[i] = null;
      }
    };
    var reduce_line = JSON.stringify(reductions);
    var reduce_length = reduce_line.length;
    var input_length =  State.line_length - code_size
    // TODO make reduce_limit config into a number
    if (State.query_config && State.query_config.reduce_limit &&
          reduce_length > 4096 && ((reduce_length * 2) > input_length)) {
      var log_message = [
          "Reduce output must shrink more rapidly:",
          "input size:", input_length,
          "output size:", reduce_length
      ].join(" ");
      if (State.query_config.reduce_limit === "log") {
          log("reduce_overflow_error: " + log_message);
          print("[true," + reduce_line + "]");
      } else {
          throw(["error", "reduce_overflow_error", log_message]);
      };
    } else {
      print("[true," + reduce_line + "]");
    }
  };

  function handleViewError(err, doc) {
    if (err == "fatal_error") {
      // Only if it's a "fatal_error" do we exit. What's a fatal error?
      // That's for the query to decide.
      //
      // This will make it possible for queries to completely error out,
      // by catching their own local exception and rethrowing a
      // fatal_error. But by default if they don't do error handling we
      // just eat the exception and carry on.
      //
      // In this case we abort map processing but don't destroy the 
      // JavaScript process. If you need to destroy the JavaScript 
      // process, throw the error form matched by the block below.
      throw(["error", "map_runtime_error", "function raised 'fatal_error'"]);
    } else if (err[0] == "fatal") {
      // Throwing errors of the form ["fatal","error_key","reason"]
      // will kill the OS process. This is not normally what you want.
      throw(err);
    } else if (err.name == "InternalError") {
      throw(["fatal", err.name, err.message]);
    }
    var message = "function raised exception " + errstr(err);
    if (doc) message += " with doc._id " + doc._id;
    log(message);
  };

  return {
    // view helper functions
    emit : function(key, value) {
      map_results.push([key, value]);
    },
    sum : function(values) {
      var rv = 0;
      for (var i in values) {
        rv += values[i];
      }
      return rv;
    },
    reduce : function(reduceFuns, kvs) {
      var keys = new Array(kvs.length);
      var values = new Array(kvs.length);
      for(var i = 0; i < kvs.length; i++) {
          keys[i] = kvs[i][0];
          values[i] = kvs[i][1];
      }
      runReduce(reduceFuns, keys, values, false);
    },
    rereduce : function(reduceFuns, values) {
      runReduce(reduceFuns, null, values, true);
    },
    mapDoc : function(doc) {
      // Compute all the map functions against the document.
      //
      // Each function can output multiple key/value pairs for each document.
      //
      // Example output of map_doc after three functions set by add_fun cmds:
      // [
      //  [["Key","Value"]],                    <- fun 1 returned 1 key value
      //  [],                                   <- fun 2 returned 0 key values
      //  [["Key1","Value1"],["Key2","Value2"]] <- fun 3 returned 2 key values
      // ]
      //

      Couch.recursivelySeal(doc);

      var buf = [];
      for (var fun in State.funs) {
        map_results = [];
        try {
          State.funs[fun](doc);
          buf.push(map_results);
        } catch (err) {
          handleViewError(err, doc);
          // If the error is not fatal, we treat the doc as if it
          // did not emit anything, by buffering an empty array.
          buf.push([]);
        }
      }
      print(JSON.stringify(buf));
    }
  };
})();
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.

function create_sandbox() {
  var sandbox = {};
  sandbox.emit = Views.emit;
  sandbox.sum = Views.sum;
  sandbox.log = log;
  sandbox.toJSON = JSON.stringify;
  sandbox.JSON = JSON;
  sandbox.provides = Mime.provides;
  sandbox.registerType = Mime.registerType;
  sandbox.start = Render.start;
  sandbox.send = Render.send;
  sandbox.getRow = Render.getRow;
  sandbox.isArray = isArray;
  return sandbox;
};

function create_filter_sandbox() {
  var sandbox = create_sandbox();
  sandbox.emit = Filter.emit;
  return sandbox;
};

function create_dreyfus_sandbox() {
  var sandbox = create_sandbox();
  sandbox.index = Dreyfus.index;
  return sandbox;
};

function create_nouveau_sandbox() {
  var sandbox = create_sandbox();
  sandbox.index = Nouveau.index;
  return sandbox;
};

function seal(obj, flag) {
  Object.freeze(obj);
};

// This is a copy from loop.js
var DDoc = (function() {
  var ddoc_dispatch = {
    "lists"     : Render.list,
    "shows"    : Render.show,
    "filters"   : Filter.filter,
    "views"     : Filter.filter_view,
    "updates"  : Render.update,
    "validate_doc_update" : Validate.validate,
    "rewrites"  : Render.rewrite
  };
  var ddocs = {};
  return {
    ddoc : function() {
      var args = [];
      for (var i=0; i < arguments.length; i++) {
        args.push(arguments[i]);
      };
      var ddocId = args.shift();
      if (ddocId == "new") {
        // get the real ddocId.
        ddocId = args.shift();
        // store the ddoc, functions are lazily compiled.
        ddocs[ddocId] = args.shift();
        print("true");
      } else {
        // Couch makes sure we know this ddoc already.
        var ddoc = ddocs[ddocId];
        if (!ddoc) throw(["fatal", "query_protocol_error", "uncached design doc: "+ddocId]);
        var funPath = args.shift();
        var cmd = funPath[0];
        // the first member of the fun path determines the type of operation
        var funArgs = args.shift();
        if (ddoc_dispatch[cmd]) {
          // get the function, call the command with it
          var point = ddoc;
          for (var i=0; i < funPath.length; i++) {
            if (i+1 == funPath.length) {
              var fun = point[funPath[i]];
              if (!fun) {
                throw(["error","not_found",
                       "missing " + funPath[0] + " function " + funPath[i] +
                       " on design doc " + ddocId]);
              }
              if (typeof fun != "function") {
                // For filter_view we want the emit() function
                // to be overridden and just toggle a flag instead of
                // accumulating rows
                var sandbox = (cmd === "views") ? create_filter_sandbox() : create_sandbox();
                fun = Couch.compileFunction(fun, ddoc, funPath.join('.'), sandbox);
                // cache the compiled fun on the ddoc
                point[funPath[i]] = fun;
              };
            } else {
              point = point[funPath[i]];
            }
          };

          // run the correct responder with the cmd body
          ddoc_dispatch[cmd].apply(null, [fun, ddoc, funArgs]);
        } else {
          // unknown command, quit and hope the restarted version is better
          throw(["fatal", "unknown_command", "unknown ddoc command '" + cmd + "'"]);
        }
      }
    }
  };
})();

// This mostly a copy from loop.js handleError
function handleError(e) {
    if (e === null) {
      // internal error, another possibility when out of memory
      // we tell the client it was a fatal error by dying
      respond(["error", "internal_error", null]);
      return false;
    }
    const type = e[0];
    if (type == "fatal") {
      e[0] = "error"; // we tell the client it was a fatal error by dying
      respond(e);
      return false;
    } else if (type == "error") {
      respond(e);
      return true;
    } else if (e.name == "InternalError") {
      // If the internal error is caught by handleViewError it will be
      // re-thrown as a ["fatal", ...] error, and we already handle that above.
      // Here we handle the case when the error is thrown outside of
      // handleViewError, for instance when serializing the rows to be sent
      // back to the user
      respond(["error", e.name, e.message]);
      return false;
    } else if (e.error && e.reason) {
      // compatibility with old error format
      respond(["error", e.error, e.reason]);
      return true;
    } else if (e.name) {
      respond(["error", e.name, e]);
      return true;
    } else {
      respond(["error","unnamed_error", e.stack]);
      return true;
    }
  };

globalThis.dispatch = function(line) {
  const cmd = JSON.parse(line);
  State.line_length = line.length;
  try {
    switch (cmd.shift()) {
    case "ddoc":
      DDoc.ddoc.apply(null, cmd);
      break;
    case "reset":
      State.reset.apply(null, cmd);
      break;
    case "add_fun":
      State.addFun.apply(null, cmd);
      break;
    case "add_lib":
      State.addLib.apply(null, cmd);
      break;
    case "map_doc":
      Views.mapDoc.apply(null, cmd);
      break;
    case "index_doc":
      Dreyfus.indexDoc.apply(null, cmd);
      break;
    case "nouveau_index_doc":
      Nouveau.indexDoc.apply(null, cmd);
      break;
    case "reduce":
      Views.reduce.apply(null, cmd);
      break;
    case "rereduce":
      Views.rereduce.apply(null, cmd);
      break;
    default:
      // unknown command, quit and hope the restarted version is better
      throw(["fatal", "unknown_command", "unknown command '" + cmdkey + "'"]);
    }
  } catch(e) {
      return handleError(e);
  };
  return true;
};
})();
