<commit_before>/*global process, require, global, __dirname*/

var isNode = System.get("@system-env").node
var GLOBAL = typeof window !== "undefined" ? window : (typeof Global !== "undefined" ? Global : global);
var debug = false;
// var debug = true;

// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
// utils
import * as path from "path";
import * as lang from "lively.lang";
var join = path.join || lang.string.joinPath,
    isAbsolute = path.isAbsolute ||  (p => !!p.match(/^(\/|[^\/]+:\/\/)/)),
    relative = path.relative || ((base, path) => path),
    dirname = path.dirname || (path => path.replace(/\/[^\/]+$/, ""));

// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
// node interface
import { Module } from "module";

var nodeModules = isNode ? {
  get cache() { return Module._cache; },
  get require() { return (name, parent) => Module._load(name, parent); },
  get resolve() { return (name, parent) => Module._resolveFilename(name, parent); },
} : {
  get cache() {
    console.warn("[lively.vm cjs] module cache accessor used on non-node system");
    return {};
  },
  get require() {
    return (name, parent) => {
      console.warn("[lively.vm cjs] require used on non-node system");
      return undefined;
    };
  },
  get resolve() {
    return (name, parent) => {
      console.warn("[lively.vm cjs] resolveFilename used on non-node system");
      return name;
    };
  }
}

var __dirname = System.normalizeSync("lively.vm/lib/").replace(/^[^:]+:\/\//, '');
var __filename = isNode ?
  join(__dirname, "commonjs-interface.js") :
  System.normalizeSync("lively.vm/lib/commonjs-interface.js").replace(/^[^:]+:\/\//, '');

// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
// other deps
import callsite from "callsite";
import * as fs from "fs";
import * as evaluator from "./evaluator";

// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
// helper
function resolve(file, parent) {
  if (typeof parent === "string") parent = {id: parent};

  // normal resolve
  try {
    return nodeModules.resolve(file, parent);
  } catch (e) {}

  // manual lookup using path
  if (!isAbsolute(file) && parent) {
    if (!file.match(/\.js$/)) file += ".js";
    try {
      return nodeModules.resolve(join(parent.id, "..", file));
    } catch (e) {}
  }

  // manual lookup using callstack
  var frames = callsite(), frame;
  for (var i = 2; i < frames.length; i++) {
    frame = frames[i];
    var frameFile = frame.getFileName();
    if (!frameFile) continue;
    var dir = dirname(frameFile),
        full = join(dir, file);
    try { return nodeModules.resolve(full, parent); } catch (e) {}
  }

  // last resort: current working directory
  try {
    return nodeModules.resolve(join(process.cwd(), file), parent);
  } catch (e) {}

  return file;
}

function sourceOf(moduleName, parent) {
  var read = lang.promise.convertCallbackFun(fs.readFile);
  return read(resolve(moduleName, parent)).then(String);
}

// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
// module wrapping + loading

// maps filenames to envs = {isLoaded: BOOL, loadError: ERROR, recorder: OBJECT}
var loadedModules = {},
    requireMap = {},
    originalCompile = null, originalLoad = null,
    exceptions = [
      // (id) => console.log(!!id.match(/lib\/commonjs-interface\.js$/)) || id.match(/lib\/commonjs-interface\.js$/),
      (id) => id.indexOf("commonjs-interface.js") > -1,
      (id) => console.log(id) || ["lively.lang/node_modules", "lively.vm/node_modules", "code-pump/node_modules"]
              .some(ignore => id.indexOf(ignore) > -1)
    ],
    // exceptions = [module.filename],
    // exceptions = [],
    scratchModule = join(__dirname, "commonjs-scratch.js"); // fallback eval target

function instrumentedFiles() { return Object.keys(loadedModules); }
function isLoaded(fileName) { return nodeModules.cache[fileName] && fileName in loadedModules; }
function ensureRecorder(fullName) { return ensureEnv(fullName).recorder; }

function ensureEnv(fullName) {
  return loadedModules[fullName]
    || (loadedModules[fullName] = {
      isInstrumented: false,
      loadError: undefined,
      // recorderName: "eval_rec_" + path.basename(fullName).replace(/[^a-z]/gi, "_"),
      // recorderName: "eval_rec_" + fullName.replace(/[^a-z]/gi, "_"),
      recorderName: "__lv_rec__",
      recorder: Object.create(GLOBAL)
    });
}

function prepareCodeForCustomCompile(source, filename, env) {
  source = String(source);
  var magicVars = ["exports", "require", "module", "__filename", "__dirname"],
      tfmOptions = {
        topLevelVarRecorder: env.recorder,
        varRecorderName: env.recorderName,
        dontTransform: [env.recorderName, "global"].concat(magicVars),
        recordGlobals: true
      },
      header = `var __cjs = System.get('file://${__filename}'),\n    ${env.recorderName} = __cjs.envFor('${filename}').recorder;\n`
             + magicVars.map(varName => `${env.recorderName}.${varName} = ${varName};`).join("\n");

  try {
    return (header + "\n"
          + ";(function() {\n"
          + evaluator.evalCodeTransform(source, tfmOptions)
          + "\n})();");
  } catch (e) { return e; }
}

var loadDepth = 0;
function customCompile(content, filename) {
  // wraps Module.prototype._compile to capture top-level module definitions
  if (exceptions.some(exc => exc(filename)) || isLoaded(filename))
    return originalCompile.call(this, content, filename);

  debug && console.log("[lively.vm customCompile] %s", filename);
  // console.log(lang.string.indent("[lively.vm commonjs] loads %s", " ", loadDepth), filename);

  // if cache was cleared also reset our recorded state
  if (!nodeModules.cache[filename] && loadedModules[filename])
    delete loadedModules[filename];

  var env = ensureEnv(filename),
      _ = env.isInstrumented = true,
      tfmedContent = prepareCodeForCustomCompile(content, filename, env);

  if (tfmedContent instanceof Error) {
    console.warn("Cannot compile module %s:", filename, tfmedContent);
    env.loadError = tfmedContent;
    var result = originalCompile.call(this, content, filename);
    return result;
  }

  GLOBAL[env.recorderName] = env.recorder;
  loadDepth++;
  try {
    var result = originalCompile.call(this, tfmedContent, filename);
    env.loadError = undefined;
    return result;
  } catch (e) {
    console.log("-=-=-=-=-=-=-=-");
    console.error("[lively.vm commonjs] evaluator error loading module: ", e.stack || e);
    console.log("-=-=-=-=-=-=-=-");
    console.log(tfmedContent);
    console.log("-=-=-=-=-=-=-=-");
    env.loadError = e; throw e;
  } finally {
    loadDepth--;
    // console.log(lang.string.indent("[lively.vm commonjs] done loading %s", " ", loadDepth), filename);
    // delete global[env.recorderName];
  }
}

function customLoad(request, parent, isMain) {
  var id = resolve(request, parent),
      parentId = resolve(parent.id);
  if (exceptions.some(exc => exc(id)) || exceptions.some(exc => exc(parentId)))
    return originalLoad.call(this, request, parent, isMain);

  if (debug) {
    var parentRel = relative(process.cwd(), parentId);
    console.log(lang.string.indent("[lively.vm cjs dependency] %s -> %s", " ", loadDepth), parentRel, request);
    // console.log(id);
  }

  if (!requireMap[parent.id]) requireMap[parent.id] = [id];
  else requireMap[parent.id].push(id);
  return originalLoad.call(this, request, parent, isMain);
}

function wrapModuleLoad() {
  if (!originalCompile)
    originalCompile = Module.prototype._compile;
  Module.prototype._compile = customCompile;
  if (!originalLoad)
    originalLoad = Module._load;
  Module._load = customLoad;
}

function unwrapModuleLoad() {
  if (originalCompile)
    Module.prototype._compile = originalCompile;
  if (originalLoad)
    Module._load = originalLoad;
}

function envFor(moduleName) {
  // var fullName = require.resolve(moduleName);
  var fullName = resolve(moduleName);
  return ensureEnv(fullName);
}

// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
// instrumented code evaluation

function runEval(code, options) {
  options = lang.obj.merge({targetModule: null, parentModule: null}, options);

  return Promise.resolve().then(() => {
    if (!options.targetModule) {
      options.targetModule = scratchModule;
    } else {
      options.targetModule = resolve(options.targetModule, options.parentModule);
    }

    var fullName = resolve(options.targetModule);
    if (!nodeModules.cache[fullName]) {
      try {
        nodeModules.require(fullName);
      } catch (e) {
        throw new Error(`Cannot load module ${options.targetModule} (tried as ${fullName})\noriginal load error: ${e.stack}`)
      }
    }

    var m = nodeModules.cache[fullName],
        env = envFor(fullName),
        rec = env.recorder,
        recName = env.recorderName;
    rec.__filename = m.filename;
    var d = rec.__dirname = dirname(m.filename);
    // rec.require = function(fname) {
    //   if (!path.isAbsolute(fname))
    //     fname = path.join(d, fname);
    //   return Module._load(fname, m);
    // };
    rec.exports = m.exports;
    rec.module = m;
    GLOBAL[recName] = rec;
    options = lang.obj.merge(options, {
      recordGlobals: true,
      dontTransform: [recName, "global"],
      varRecorderName: recName,
      topLevelVarRecorder: rec,
      sourceURL: options.targetModule,
      context: rec.exports || {}
    });

    return evaluator.runEval(code, options);
  });
}

function importCjsModule(name) {
  return new Promise(ok => ok(nodeModules.require(resolve(name))));
}

// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
// module runtime status

function status(thenDo) {
  var files = Object.keys(loadedModules),
      envs = files.reduce((envs, fn) => {
        envs[fn] = {
          loadError:         loadedModules[fn].loadError,
          isLoaded:          isLoaded(fn),
          recorderName:      loadedModules[fn].recorderName,
          isInstrumented:    loadedModules[fn].isInstrumented,
          recordedVariables: Object.keys(loadedModules[fn].recorder)
        }
        return envs;
      }, {});
  if (typeof thenDo === "function") thenDo(null, envs);
  return Promise.resolve(envs);
}

function statusForPrinted(moduleName, options, thenDo) {
  options = lang.obj.merge({depth: 3}, options);
  var env = envFor(moduleName);

  var state = {
    loadError:         env.loadError,
    recorderName:      env.recorderName,
    recordedVariables: env.recorder
  }

  thenDo(null, lang.obj.inspect(state, {maxDepth: options.depth}));
}

// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
// module reloading

function _invalidateCacheForModules(fullModuleIds, moduleMap) {
  fullModuleIds.forEach(id => {
    delete moduleMap[id];
    delete loadedModules[id];
  });
}

function forgetModule(moduleName, parent) {
  var id = resolve(moduleName, parent),
      deps = findDependentsOf(id);
  _invalidateCacheForModules([id].concat(deps), Module._cache);
  return id;
}

function forgetModuleDeps(moduleName, parent) {
  var id = resolve(moduleName, parent),
      deps = findDependentsOf(id);
  _invalidateCacheForModules(deps, Module._cache);
  return id;
}

function reloadModule(moduleName, parent) {
  var id = forgetModule(moduleName, parent);
  return nodeModules.require(id);
}

// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
// module dependencies

function findDependentsOf(id) {
  // which modules (module ids) are (in)directly required by module with id
  // Let's say you have
  // module1: exports.x = 23;
  // module2: exports.y = require("./module1").x + 1;
  // module3: exports.z = require("./module2").y + 1;
  // `findDependentsOf` gives you an answer what modules are "stale" when you
  // change module1
  return lang.graph.hull(lang.graph.invert(requireMap), resolve(id));
}

function findRequirementsOf(id) {
  // which modules (module ids) are (in)directly required by module with id
  // Let's say you have
  // module1: exports.x = 23;
  // module2: exports.y = require("./module1").x + 1;
  // module3: exports.z = require("./module2").y + 1;
  // `findRequirementsOf("./module3")` will report ./module2 and ./module1
  return lang.graph.hull(requireMap, resolve(id));
}

// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

export {

  // internals
  requireMap as _requireMap,
  loadedModules as _loadedModules,
  instrumentedFiles,
  prepareCodeForCustomCompile as _prepareCodeForCustomCompile,

  // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
  // accessing
  sourceOf,
  envFor,
  status,
  statusForPrinted,

  // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
  // eval + changes
  runEval,
  // sourceChange,

  // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
  // loading + dependencies
  // reloadModule,
  resolve,
  importCjsModule as import,
  reloadModule,
  forgetModule,
  forgetModuleDeps,
  findRequirementsOf,
  findDependentsOf,

  // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
  // instrumentation
  wrapModuleLoad,
  unwrapModuleLoad
}
<commit_msg>cjs use System._nodeRequire<commit_after>/*global process, require, global, __dirname*/

var isNode = System.get("@system-env").node
var GLOBAL = typeof window !== "undefined" ? window : (typeof Global !== "undefined" ? Global : global);
var debug = false;
// var debug = true;

// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
// utils
import * as path from "path";
import * as lang from "lively.lang";
var join = path.join || lang.string.joinPath,
    isAbsolute = path.isAbsolute ||  (p => !!p.match(/^(\/|[^\/]+:\/\/)/)),
    relative = path.relative || ((base, path) => path),
    dirname = path.dirname || (path => path.replace(/\/[^\/]+$/, ""));

// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
// node interface
import { Module } from "module";

var nodeModules = isNode ? {
  get cache() { return Module._cache; },
  get require() { return (name, parent) => System._nodeRequire(name, parent); },
  get resolve() { return (name, parent) => Module._resolveFilename(name, parent); },
} : {
  get cache() {
    console.warn("[lively.vm cjs] module cache accessor used on non-node system");
    return {};
  },
  get require() {
    return (name, parent) => {
      console.warn("[lively.vm cjs] require used on non-node system");
      return undefined;
    };
  },
  get resolve() {
    return (name, parent) => {
      console.warn("[lively.vm cjs] resolveFilename used on non-node system");
      return name;
    };
  }
}

var __dirname = System.normalizeSync("lively.vm/lib/").replace(/^[^:]+:\/\//, '');
var __filename = isNode ?
  join(__dirname, "commonjs-interface.js") :
  System.normalizeSync("lively.vm/lib/commonjs-interface.js").replace(/^[^:]+:\/\//, '');

// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
// other deps
import callsite from "callsite";
import * as fs from "fs";
import * as evaluator from "./evaluator";

// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
// helper
function resolve(file, parent) {
  if (typeof parent === "string") parent = {id: parent};

  // normal resolve
  try {
    return nodeModules.resolve(file, parent);
  } catch (e) {}

  // manual lookup using path
  if (!isAbsolute(file) && parent) {
    if (!file.match(/\.js$/)) file += ".js";
    try {
      return nodeModules.resolve(join(parent.id, "..", file));
    } catch (e) {}
  }

  // manual lookup using callstack
  var frames = callsite(), frame;
  for (var i = 2; i < frames.length; i++) {
    frame = frames[i];
    var frameFile = frame.getFileName();
    if (!frameFile) continue;
    var dir = dirname(frameFile),
        full = join(dir, file);
    try { return nodeModules.resolve(full, parent); } catch (e) {}
  }

  // last resort: current working directory
  try {
    return nodeModules.resolve(join(process.cwd(), file), parent);
  } catch (e) {}

  return file;
}

function sourceOf(moduleName, parent) {
  var read = lang.promise.convertCallbackFun(fs.readFile);
  return read(resolve(moduleName, parent)).then(String);
}

// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
// module wrapping + loading

// maps filenames to envs = {isLoaded: BOOL, loadError: ERROR, recorder: OBJECT}
var loadedModules = {},
    requireMap = {},
    originalCompile = null, originalLoad = null,
    exceptions = [
      // (id) => console.log(!!id.match(/lib\/commonjs-interface\.js$/)) || id.match(/lib\/commonjs-interface\.js$/),
      (id) => id.indexOf("commonjs-interface.js") > -1,
      (id) => console.log(id) || ["lively.lang/node_modules", "lively.vm/node_modules", "code-pump/node_modules"]
              .some(ignore => id.indexOf(ignore) > -1)
    ],
    // exceptions = [module.filename],
    // exceptions = [],
    scratchModule = join(__dirname, "commonjs-scratch.js"); // fallback eval target

function instrumentedFiles() { return Object.keys(loadedModules); }
function isLoaded(fileName) { return nodeModules.cache[fileName] && fileName in loadedModules; }
function ensureRecorder(fullName) { return ensureEnv(fullName).recorder; }

function ensureEnv(fullName) {
  return loadedModules[fullName]
    || (loadedModules[fullName] = {
      isInstrumented: false,
      loadError: undefined,
      // recorderName: "eval_rec_" + path.basename(fullName).replace(/[^a-z]/gi, "_"),
      // recorderName: "eval_rec_" + fullName.replace(/[^a-z]/gi, "_"),
      recorderName: "__lv_rec__",
      recorder: Object.create(GLOBAL)
    });
}

function prepareCodeForCustomCompile(source, filename, env) {
  source = String(source);
  var magicVars = ["exports", "require", "module", "__filename", "__dirname"],
      tfmOptions = {
        topLevelVarRecorder: env.recorder,
        varRecorderName: env.recorderName,
        dontTransform: [env.recorderName, "global"].concat(magicVars),
        recordGlobals: true
      },
      header = `var __cjs = System.get('file://${__filename}'),\n    ${env.recorderName} = __cjs.envFor('${filename}').recorder;\n`
             + magicVars.map(varName => `${env.recorderName}.${varName} = ${varName};`).join("\n");

  try {
    return (header + "\n"
          + ";(function() {\n"
          + evaluator.evalCodeTransform(source, tfmOptions)
          + "\n})();");
  } catch (e) { return e; }
}

var loadDepth = 0;
function customCompile(content, filename) {
  // wraps Module.prototype._compile to capture top-level module definitions
  if (exceptions.some(exc => exc(filename)) || isLoaded(filename))
    return originalCompile.call(this, content, filename);

  debug && console.log("[lively.vm customCompile] %s", filename);
  // console.log(lang.string.indent("[lively.vm commonjs] loads %s", " ", loadDepth), filename);

  // if cache was cleared also reset our recorded state
  if (!nodeModules.cache[filename] && loadedModules[filename])
    delete loadedModules[filename];

  var env = ensureEnv(filename),
      _ = env.isInstrumented = true,
      tfmedContent = prepareCodeForCustomCompile(content, filename, env);

  if (tfmedContent instanceof Error) {
    console.warn("Cannot compile module %s:", filename, tfmedContent);
    env.loadError = tfmedContent;
    var result = originalCompile.call(this, content, filename);
    return result;
  }

  GLOBAL[env.recorderName] = env.recorder;
  loadDepth++;
  try {
    var result = originalCompile.call(this, tfmedContent, filename);
    env.loadError = undefined;
    return result;
  } catch (e) {
    console.log("-=-=-=-=-=-=-=-");
    console.error("[lively.vm commonjs] evaluator error loading module: ", e.stack || e);
    console.log("-=-=-=-=-=-=-=-");
    console.log(tfmedContent);
    console.log("-=-=-=-=-=-=-=-");
    env.loadError = e; throw e;
  } finally {
    loadDepth--;
    // console.log(lang.string.indent("[lively.vm commonjs] done loading %s", " ", loadDepth), filename);
    // delete global[env.recorderName];
  }
}

function customLoad(request, parent, isMain) {
  var id = resolve(request, parent),
      parentId = resolve(parent.id);
  if (exceptions.some(exc => exc(id)) || exceptions.some(exc => exc(parentId)))
    return originalLoad.call(this, request, parent, isMain);

  if (debug) {
    var parentRel = relative(process.cwd(), parentId);
    console.log(lang.string.indent("[lively.vm cjs dependency] %s -> %s", " ", loadDepth), parentRel, request);
    // console.log(id);
  }

  if (!requireMap[parent.id]) requireMap[parent.id] = [id];
  else requireMap[parent.id].push(id);
  return originalLoad.call(this, request, parent, isMain);
}

function wrapModuleLoad() {
  if (!originalCompile)
    originalCompile = Module.prototype._compile;
  Module.prototype._compile = customCompile;
  if (!originalLoad)
    originalLoad = Module._load;
  Module._load = customLoad;
}

function unwrapModuleLoad() {
  if (originalCompile)
    Module.prototype._compile = originalCompile;
  if (originalLoad)
    Module._load = originalLoad;
}

function envFor(moduleName) {
  // var fullName = require.resolve(moduleName);
  var fullName = resolve(moduleName);
  return ensureEnv(fullName);
}

// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
// instrumented code evaluation

function runEval(code, options) {
  options = lang.obj.merge({targetModule: null, parentModule: null}, options);

  return Promise.resolve().then(() => {
    if (!options.targetModule) {
      options.targetModule = scratchModule;
    } else {
      options.targetModule = resolve(options.targetModule, options.parentModule);
    }

    var fullName = resolve(options.targetModule);
    if (!nodeModules.cache[fullName]) {
      try {
        nodeModules.require(fullName);
      } catch (e) {
        throw new Error(`Cannot load module ${options.targetModule} (tried as ${fullName})\noriginal load error: ${e.stack}`)
      }
    }

    var m = nodeModules.cache[fullName],
        env = envFor(fullName),
        rec = env.recorder,
        recName = env.recorderName;
    rec.__filename = m.filename;
    var d = rec.__dirname = dirname(m.filename);
    // rec.require = function(fname) {
    //   if (!path.isAbsolute(fname))
    //     fname = path.join(d, fname);
    //   return Module._load(fname, m);
    // };
    rec.exports = m.exports;
    rec.module = m;
    GLOBAL[recName] = rec;
    options = lang.obj.merge(options, {
      recordGlobals: true,
      dontTransform: [recName, "global"],
      varRecorderName: recName,
      topLevelVarRecorder: rec,
      sourceURL: options.targetModule,
      context: rec.exports || {}
    });

    return evaluator.runEval(code, options);
  });
}

function importCjsModule(name) {
  return new Promise(ok => ok(nodeModules.require(resolve(name))));
}

// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
// module runtime status

function status(thenDo) {
  var files = Object.keys(loadedModules),
      envs = files.reduce((envs, fn) => {
        envs[fn] = {
          loadError:         loadedModules[fn].loadError,
          isLoaded:          isLoaded(fn),
          recorderName:      loadedModules[fn].recorderName,
          isInstrumented:    loadedModules[fn].isInstrumented,
          recordedVariables: Object.keys(loadedModules[fn].recorder)
        }
        return envs;
      }, {});
  if (typeof thenDo === "function") thenDo(null, envs);
  return Promise.resolve(envs);
}

function statusForPrinted(moduleName, options, thenDo) {
  options = lang.obj.merge({depth: 3}, options);
  var env = envFor(moduleName);

  var state = {
    loadError:         env.loadError,
    recorderName:      env.recorderName,
    recordedVariables: env.recorder
  }

  thenDo(null, lang.obj.inspect(state, {maxDepth: options.depth}));
}

// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
// module reloading

function _invalidateCacheForModules(fullModuleIds, moduleMap) {
  fullModuleIds.forEach(id => {
    delete moduleMap[id];
    delete loadedModules[id];
  });
}

function forgetModule(moduleName, parent) {
  var id = resolve(moduleName, parent),
      deps = findDependentsOf(id);
  _invalidateCacheForModules([id].concat(deps), Module._cache);
  return id;
}

function forgetModuleDeps(moduleName, parent) {
  var id = resolve(moduleName, parent),
      deps = findDependentsOf(id);
  _invalidateCacheForModules(deps, Module._cache);
  return id;
}

function reloadModule(moduleName, parent) {
  var id = forgetModule(moduleName, parent);
  return nodeModules.require(id);
}

// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
// module dependencies

function findDependentsOf(id) {
  // which modules (module ids) are (in)directly required by module with id
  // Let's say you have
  // module1: exports.x = 23;
  // module2: exports.y = require("./module1").x + 1;
  // module3: exports.z = require("./module2").y + 1;
  // `findDependentsOf` gives you an answer what modules are "stale" when you
  // change module1
  return lang.graph.hull(lang.graph.invert(requireMap), resolve(id));
}

function findRequirementsOf(id) {
  // which modules (module ids) are (in)directly required by module with id
  // Let's say you have
  // module1: exports.x = 23;
  // module2: exports.y = require("./module1").x + 1;
  // module3: exports.z = require("./module2").y + 1;
  // `findRequirementsOf("./module3")` will report ./module2 and ./module1
  return lang.graph.hull(requireMap, resolve(id));
}

// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

export {

  // internals
  requireMap as _requireMap,
  loadedModules as _loadedModules,
  instrumentedFiles,
  prepareCodeForCustomCompile as _prepareCodeForCustomCompile,

  // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
  // accessing
  sourceOf,
  envFor,
  status,
  statusForPrinted,

  // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
  // eval + changes
  runEval,
  // sourceChange,

  // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
  // loading + dependencies
  // reloadModule,
  resolve,
  importCjsModule as import,
  reloadModule,
  forgetModule,
  forgetModuleDeps,
  findRequirementsOf,
  findDependentsOf,

  // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
  // instrumentation
  wrapModuleLoad,
  unwrapModuleLoad
}
<|endoftext|>


<commit_before>const html = require('choo/html')
const component = require('fun-component')
const createPopup = require('./popup')
const { loader } = require('../icons')
const { __ } = require('../../locale')
const { getEnergyClass, getPerformance, load, distance } = require('../utils')

const CLUSTER_THRESHOLD = 12
const POPUP_OFFSET = {
  'top': [0, -15],
  'top-left': [0, -15],
  'top-right': [0, -15],
  'bottom': [0, -36],
  'bottom-left': [0, -36],
  'bottom-right': [0, -36],
  'left': [6, -26],
  'right': [-6, -26]
}

module.exports = component({
  name: 'map',
  cache: true,
  isInitialized: false,
  hasLoaded: false,

  update (element, args, prev) {
    const [ cooperatives, center ] = args

    if (!this.map && this.mapboxgl && center) {
      this.init(element, ...args)
    } else if (shouldUpdate(args, prev) && this.map) {
      // Update map source
      this.map.getSource('cooperatives').setData({
        type: 'FeatureCollection',
        features: asFeatures(cooperatives)
      })

      if (coordinatesDiff(center, prev[1])) {
        // Recalculate bounds
        const bounds = this.getBounds(cooperatives, getLngLat(center))

        if (center.precission === 'exact') {
          if (!this.position) {
            // Create position marker for exact position
            this.position = new this.mapboxgl.Marker(myLocation())
          }

          // Update position coordinates
          this.position.setLngLat(getLngLat(center)).addTo(this.map)

          // Ensure that exact position is included in bounds
          bounds.extend(getLngLat(center))
        }

        // Fit new bounds in map
        this.map.fitBounds(bounds, { padding: element.offsetWidth * 0.1 })
      }
    }

    return false
  },

  unload () {
    if (this.popup && this.popup.isOpen()) {
      this.popup.remove()
    }
  },

  load (element, cooperatives, center, emit) {
    if (this.hasLoaded) { return }
    this.hasLoaded = true

    load([
      'mapbox-gl',
      'https://api.mapbox.com/mapbox-gl-js/v0.34.0/mapbox-gl.css'
    ]).then(([ mapboxgl ]) => {
      // Stash mapbox api in scoped variable
      this.mapboxgl = mapboxgl

      // Init if there a center location
      if (center && !this.isInitialized) {
        window.requestAnimationFrame(() => {
          this.init(element, cooperatives, center, emit)
        })
      }
    })
  },

  init (element, cooperatives, center, emit) {
    this.isInitialized = true

    /**
     * Unset loading state and empty out container
     */

    element.classList.remove('is-loading')
    element.innerHTML = ''

    this.mapboxgl.accessToken = process.env.MAPBOX_ACCESS_TOKEN
    const map = this.map = new this.mapboxgl.Map({
      container: element,
      style: process.env.MAPBOX_STYLE,
      maxZoom: 17
    })

    /**
     * Try and fit center and a couple cooperatives in map
     */

    if (cooperatives.length) {
      map.fitBounds(this.getBounds(cooperatives, getLngLat(center)), {
        padding: element.offsetWidth * 0.1,
        animate: false
      })
    }

    if (center.precission === 'exact') {
      // Create a marker for exact position
      this.position = new this.mapboxgl.Marker(myLocation())
        .setLngLat(getLngLat(center))
        .addTo(map)
    }

    map.on('load', () => {
      /**
       * Add cooperatives as source
       */

      map.addSource('cooperatives', {
        type: 'geojson',
        data: {
          type: 'FeatureCollection',
          features: asFeatures(cooperatives)
        },
        cluster: true,
        clusterMaxZoom: CLUSTER_THRESHOLD
      })

      /**
       * Add all individual (unclustered cooperatives)
       */

      map.addLayer({
        id: 'cooperative-markers',
        type: 'symbol',
        source: 'cooperatives',
        filter: ['!has', 'point_count'],
        layout: {
          'icon-allow-overlap': true,
          'icon-image': 'marker-{energyClass}',
          'icon-offset': [0, -21]
        }
      })

      /**
       * Add clusters
       */

      map.addLayer({
        id: 'cooperative-clusters',
        type: 'symbol',
        source: 'cooperatives',
        filter: ['has', 'point_count'],
        layout: {
          'icon-allow-overlap': true,
          'icon-image': 'marker-cluster',
          'text-field': '{point_count}',
          'text-font': [ 'Lato Bold' ],
          'text-size': 14,
          'text-offset': [ 0, 0.85 ]
        },
        paint: {
          'text-color': '#fff'
        }
      })

      /**
       * Zoom out if center posiiton is unprecise
       */

      if (center.precission !== 'exact') {
        map.setZoom(11)
      }

      /**
       * Handle pointer
       */

      map.on('mousemove', event => {
        const features = map.queryRenderedFeatures(event.point, {
          layers: ['cooperative-markers', 'cooperative-clusters']
        })

        map.getCanvas().style.cursor = features.length ? 'pointer' : ''
      })

      /**
       * Handle clikcing on the map
       */

      map.on('click', event => {
        // Figure out which (if any) layers has been clicked
        const features = map.queryRenderedFeatures(event.point, {
          layers: ['cooperative-markers', 'cooperative-clusters']
        })

        if (!features.length) {
          return
        }

        const feature = features[0]

        if (feature.properties.cluster) {
          // Reveal all cooperatives in cluster
          map.flyTo({
            center: feature.geometry.coordinates,
            zoom: CLUSTER_THRESHOLD + 1
          })
        } else {
          // Show cooperative popup
          this.popup = new this.mapboxgl.Popup({ closeButton: false, offset: POPUP_OFFSET })
          this.popup
            .setLngLat(feature.geometry.coordinates)
            .setDOMContent(createPopup(feature))
            .addTo(map)

          emit('track', {
            event_name: 'inspect',
            event_category: 'map',
            event_label: feature.properties.name,
            id: feature.properties._id
          })
        }
      })
    })
  },

  render () {
    return html`

${loader()}

    `
  },

  /**
   * Calculate bounds for given coopeartives and center coordinates
   * @param  {Array}      cooperatives List of cooperatives
   * @param  {LatLngLike} center       Coordinates to base positioning on
   * @return {mapbox.LngLatBounds}
   */

  getBounds (cooperatives, center) {
    let include
    const bounds = new this.mapboxgl.LngLatBounds()
    const closest = cooperatives.map(cooperative => {
      return distance(center, getLngLat(cooperative))
    }).sort()[0]

    if (closest < 200) {
      // Include center
      bounds.extend(center)

      // Include the closest five cooperatives if within reasonable distance
      include = cooperatives.slice()
        .sort((a, b) => {
          const aDistance = distance(center, getLngLat(a))
          const bDistance = distance(center, getLngLat(b))
          return aDistance > bDistance ? 1 : -1
        })
        .filter((item, index) => index < 5)
    } else {
      // Include all cooperatives if center is too far off
      include = cooperatives
    }

    include.forEach(cooperative => bounds.extend(getLngLat(cooperative)))

    return bounds
  }
})

/**
 * Determin whether map should update
 * @param {any} [cooperatives, center]
 * @param {any} [prevCooperatives, prevCenter]
 * @returns {boolean}
 */

function shouldUpdate ([cooperatives, center], [prevCooperatives, prevCenter]) {
  // Check if number of cooperatives has changed
  if (cooperatives.length !== prevCooperatives.length) {
    return true
  }

  // Check if center has changed
  if (!center || !prevCenter || coordinatesDiff(center, prevCenter)) {
    return true
  }

  return false
}

/**
 * Generic "You are here"-location marker
 */

function myLocation () {
  return html`

${__('You are here')}

  `
}

/**
 * Extract coordinates as LngLatLike object from object
 * @param  {Object} props Object with some kind of lat/lng properties
 * @return {Array}        LatLngLike (Mapbox compatible)
 */

function getLngLat (props) {
  return [
    props.longitude || props.lng,
    props.latitude || props.lat
  ]
}

function asFeatures (cooperatives) {
  return cooperatives.map(cooperative => {
    const { value: performance } = getPerformance(cooperative) || {}

    return {
      type: 'Feature',
      geometry: {
        type: 'Point',
        coordinates: [ cooperative.lng, cooperative.lat ]
      },
      properties: Object.assign({
        id: cooperative._id,
        performance: performance,
        energyClass: (getEnergyClass(cooperative) || 'unknown').toLowerCase()
      }, cooperative)
    }
  })
}

function coordinatesDiff (coordsA, coordsB) {
  return ['latitude', 'longitude'].reduce((changed, key) => {
    return changed || coordsA[key] !== coordsB[key]
  }, false)
}
<commit_msg>Fix map init race condition<commit_after>const html = require('choo/html')
const component = require('fun-component')
const createPopup = require('./popup')
const { loader } = require('../icons')
const { __ } = require('../../locale')
const { getEnergyClass, getPerformance, load, distance } = require('../utils')

const CLUSTER_THRESHOLD = 12
const POPUP_OFFSET = {
  'top': [0, -15],
  'top-left': [0, -15],
  'top-right': [0, -15],
  'bottom': [0, -36],
  'bottom-left': [0, -36],
  'bottom-right': [0, -36],
  'left': [6, -26],
  'right': [-6, -26]
}

module.exports = component({
  name: 'map',
  cache: true,
  isInitialized: false,
  hasLoaded: false,

  update (element, args, prev) {
    const [ cooperatives, center, emit ] = args

    if (!this.map && this.mapboxgl && center) {
      this.init(element, ...args)
    } else if (shouldUpdate(args, prev) && cooperatives.length && this.map) {
      if (!this.map.getSource('cooperatives')) {
        this.addSource(cooperatives, center, emit)
      } else {
        // Update map source
        this.map.getSource('cooperatives').setData({
          type: 'FeatureCollection',
          features: asFeatures(cooperatives)
        })

        if (coordinatesDiff(center, prev[1])) {
          // Recalculate bounds
          const bounds = this.getBounds(cooperatives, getLngLat(center))

          if (center.precission === 'exact') {
            if (!this.position) {
              // Create position marker for exact position
              this.position = new this.mapboxgl.Marker(myLocation())
            }

            // Update position coordinates
            this.position.setLngLat(getLngLat(center)).addTo(this.map)

            // Ensure that exact position is included in bounds
            bounds.extend(getLngLat(center))
          }

          // Fit new bounds in map
          this.map.fitBounds(bounds, { padding: element.offsetWidth * 0.1 })
        }
      }
    }

    return false
  },

  unload () {
    if (this.popup && this.popup.isOpen()) {
      this.popup.remove()
    }
  },

  load (element, cooperatives, center, emit) {
    if (this.hasLoaded) { return }
    this.hasLoaded = true

    load([
      'mapbox-gl',
      'https://api.mapbox.com/mapbox-gl-js/v0.34.0/mapbox-gl.css'
    ]).then(([ mapboxgl ]) => {
      // Stash mapbox api in scoped variable
      this.mapboxgl = mapboxgl

      // Init if there a center location
      if (center && !this.isInitialized) {
        window.requestAnimationFrame(() => {
          this.init(element, cooperatives, center, emit)
        })
      }
    })
  },

  init (element, cooperatives, center, emit) {
    this.isInitialized = true

    /**
     * Unset loading state and empty out container
     */

    element.classList.remove('is-loading')
    element.innerHTML = ''

    this.mapboxgl.accessToken = process.env.MAPBOX_ACCESS_TOKEN
    const map = this.map = new this.mapboxgl.Map({
      container: element,
      style: process.env.MAPBOX_STYLE,
      maxZoom: 17
    })

    /**
     * Try and fit center and a couple cooperatives in map
     */

    if (cooperatives.length) {
      map.fitBounds(this.getBounds(cooperatives, getLngLat(center)), {
        padding: element.offsetWidth * 0.1,
        animate: false
      })
    }

    if (center.precission === 'exact') {
      // Create a marker for exact position
      this.position = new this.mapboxgl.Marker(myLocation())
        .setLngLat(getLngLat(center))
        .addTo(map)
    }

    map.on('load', () => {
      if (!this.map.getSource('cooperatives') && cooperatives.length) {
        this.addSource(cooperatives, center, emit)
      }
    })
  },

  addSource (cooperatives, center, emit) {
    /**
     * Add cooperatives as source
     */

    this.map.addSource('cooperatives', {
      type: 'geojson',
      data: {
        type: 'FeatureCollection',
        features: asFeatures(cooperatives)
      },
      cluster: true,
      clusterMaxZoom: CLUSTER_THRESHOLD
    })

    /**
     * Add all individual (unclustered cooperatives)
     */

    this.map.addLayer({
      id: 'cooperative-markers',
      type: 'symbol',
      source: 'cooperatives',
      filter: ['!has', 'point_count'],
      layout: {
        'icon-allow-overlap': true,
        'icon-image': 'marker-{energyClass}',
        'icon-offset': [0, -21]
      }
    })

    /**
     * Add clusters
     */

    this.map.addLayer({
      id: 'cooperative-clusters',
      type: 'symbol',
      source: 'cooperatives',
      filter: ['has', 'point_count'],
      layout: {
        'icon-allow-overlap': true,
        'icon-image': 'marker-cluster',
        'text-field': '{point_count}',
        'text-font': [ 'Lato Bold' ],
        'text-size': 14,
        'text-offset': [ 0, 0.85 ]
      },
      paint: {
        'text-color': '#fff'
      }
    })

    /**
     * Zoom out if center posiiton is unprecise
     */

    if (center.precission !== 'exact') {
      this.map.setZoom(11)
    }

    /**
     * Handle pointer
     */

    this.map.on('mousemove', event => {
      const features = this.map.queryRenderedFeatures(event.point, {
        layers: ['cooperative-markers', 'cooperative-clusters']
      })

      this.map.getCanvas().style.cursor = features.length ? 'pointer' : ''
    })

    /**
     * Handle clikcing on the map
     */

    this.map.on('click', event => {
      // Figure out which (if any) layers has been clicked
      const features = this.map.queryRenderedFeatures(event.point, {
        layers: ['cooperative-markers', 'cooperative-clusters']
      })

      if (!features.length) {
        return
      }

      const feature = features[0]

      if (feature.properties.cluster) {
        // Reveal all cooperatives in cluster
        this.map.flyTo({
          center: feature.geometry.coordinates,
          zoom: CLUSTER_THRESHOLD + 1
        })
      } else {
        // Show cooperative popup
        this.popup = new this.mapboxgl.Popup({ closeButton: false, offset: POPUP_OFFSET })
        this.popup
          .setLngLat(feature.geometry.coordinates)
          .setDOMContent(createPopup(feature))
          .addTo(this.map)

        emit('track', {
          event_name: 'inspect',
          event_category: 'map',
          event_label: feature.properties.name,
          id: feature.properties._id
        })
      }
    })
  },

  render () {
    return html`

${loader()}

    `
  },

  /**
   * Calculate bounds for given coopeartives and center coordinates
   * @param  {Array}      cooperatives List of cooperatives
   * @param  {LatLngLike} center       Coordinates to base positioning on
   * @return {mapbox.LngLatBounds}
   */

  getBounds (cooperatives, center) {
    let include
    const bounds = new this.mapboxgl.LngLatBounds()
    const closest = cooperatives.map(cooperative => {
      return distance(center, getLngLat(cooperative))
    }).sort()[0]

    if (closest < 200) {
      // Include center
      bounds.extend(center)

      // Include the closest five cooperatives if within reasonable distance
      include = cooperatives.slice()
        .sort((a, b) => {
          const aDistance = distance(center, getLngLat(a))
          const bDistance = distance(center, getLngLat(b))
          return aDistance > bDistance ? 1 : -1
        })
        .filter((item, index) => index < 5)
    } else {
      // Include all cooperatives if center is too far off
      include = cooperatives
    }

    include.forEach(cooperative => bounds.extend(getLngLat(cooperative)))

    return bounds
  }
})

/**
 * Determin whether map should update
 * @param {any} [cooperatives, center]
 * @param {any} [prevCooperatives, prevCenter]
 * @returns {boolean}
 */

function shouldUpdate ([cooperatives, center], [prevCooperatives, prevCenter]) {
  // Check if number of cooperatives has changed
  if (cooperatives.length !== prevCooperatives.length) {
    return true
  }

  // Check if center has changed
  if (!center || !prevCenter || coordinatesDiff(center, prevCenter)) {
    return true
  }

  return false
}

/**
 * Generic "You are here"-location marker
 */

function myLocation () {
  return html`

${__('You are here')}

  `
}

/**
 * Extract coordinates as LngLatLike object from object
 * @param  {Object} props Object with some kind of lat/lng properties
 * @return {Array}        LatLngLike (Mapbox compatible)
 */

function getLngLat (props) {
  return [
    props.longitude || props.lng,
    props.latitude || props.lat
  ]
}

function asFeatures (cooperatives) {
  return cooperatives.map(cooperative => {
    const { value: performance } = getPerformance(cooperative) || {}

    return {
      type: 'Feature',
      geometry: {
        type: 'Point',
        coordinates: [ cooperative.lng, cooperative.lat ]
      },
      properties: Object.assign({
        id: cooperative._id,
        performance: performance,
        energyClass: (getEnergyClass(cooperative) || 'unknown').toLowerCase()
      }, cooperative)
    }
  })
}

function coordinatesDiff (coordsA, coordsB) {
  return ['latitude', 'longitude'].reduce((changed, key) => {
    return changed || coordsA[key] !== coordsB[key]
  }, false)
}
<|endoftext|>


<commit_before>var ComputeBreakdowns = {
  stock_details: {},
  breakdown_totals: {},
  total_portfolio_value: 0.0,
  total_weighted_cost: 0.0,

  init: function(stock_summaries, stocks) {
    this.stock_details = {};
    this.breakdown_totals = {};
    this.total_portfolio_value = 0.0;
    this.total_weighted_cost = 0.0;

    for (var i = 0; i < stocks.length; i++) {
      var stock = stocks[i];
      this.stock_details[stock.symbol] = stock;
    }

    for (var symbol in stock_summaries) {
      matching_keys = this.getMatchingKeys(this.stock_details[symbol])
      this.total_portfolio_value += stock_summaries[symbol].total_value;
      this.total_weighted_cost += stock_summaries[symbol].weighted_cost;
      for (var i = 0; i < matching_keys.length; i++) {
        key = matching_keys[i];
        this.addToKey(key, stock_summaries[symbol]);
      }
    }

    for (var symbol in this.breakdown_totals) {
      this.breakdown_totals[symbol].portion = this.breakdown_totals[symbol].total_value * 100.0 / this.total_portfolio_value;
    }

    this.breakdown_totals[Constants.STOCK_ALL].weighted_cost = this.total_weighted_cost;
  },

  getMatchingKeys: function(stock) {
    var keys = [];
    if (stock.is_us) {
      keys.push("US");
    } else {
      keys.push("INT");
    }

    keys.push(stock.stock_type);
    keys.push(keys[0] + keys[1]);
    keys.push(Constants.STOCK_ALL);

    return keys;
  },

  addToKey: function(key, stock_summary) {
      if (key in this.breakdown_totals) {
        this.breakdown_totals[key].total_value += stock_summary.total_value;
        this.breakdown_totals[key].unrealized_pl += stock_summary.unrealized_pl;
        if(!isNaN(stock_summary.realized_pl)){
          if(("realized_pl" in this.breakdown_totals[key])){
            this.breakdown_totals[key].realized_pl += stock_summary.realized_pl;
          } else if(!("realized_pl" in this.breakdown_totals[key])) {
            this.breakdown_totals[key].realized_pl = stock_summary.realized_pl;
          }
        }
      } else {
        this.breakdown_totals[key] = {
          total_value: stock_summary.total_value,
          unrealized_pl: stock_summary.unrealized_pl,
        };
        if(!isNaN(stock_summary.realized_pl)){
          this.breakdown_totals[key].realized_pl = stock_summary.realized_pl;
        }
      }
  },

  getStockBreakdowns: function() {
    return this.breakdown_totals;
  }
};
<commit_msg>Adding the requested changes to the code<commit_after>var ComputeBreakdowns = {
  stock_details: {},
  breakdown_totals: {},
  total_portfolio_value: 0.0,
  total_weighted_cost: 0.0,

  init: function(stock_summaries, stocks) {
    this.stock_details = {};
    this.breakdown_totals = {};
    this.total_portfolio_value = 0.0;
    this.total_weighted_cost = 0.0;

    for (var i = 0; i < stocks.length; i++) {
      var stock = stocks[i];
      this.stock_details[stock.symbol] = stock;
    }

    for (var symbol in stock_summaries) {
      matching_keys = this.getMatchingKeys(this.stock_details[symbol])
      this.total_portfolio_value += stock_summaries[symbol].total_value;
      this.total_weighted_cost += stock_summaries[symbol].weighted_cost;
      for (var i = 0; i < matching_keys.length; i++) {
        key = matching_keys[i];
        this.addToKey(key, stock_summaries[symbol]);
      }
    }

    for (var symbol in this.breakdown_totals) {
      this.breakdown_totals[symbol].portion = this.breakdown_totals[symbol].total_value * 100.0 / this.total_portfolio_value;
    }

    this.breakdown_totals[Constants.STOCK_ALL].weighted_cost = this.total_weighted_cost;
  },

  getMatchingKeys: function(stock) {
    var keys = [];
    if (stock.is_us) {
      keys.push("US");
    } else {
      keys.push("INT");
    }

    keys.push(stock.stock_type);
    keys.push(keys[0] + keys[1]);
    keys.push(Constants.STOCK_ALL);

    return keys;
  },

  addToKey: function(key, stock_summary) {
      if (key in this.breakdown_totals) {
        this.breakdown_totals[key].total_value += stock_summary.total_value;
        this.breakdown_totals[key].unrealized_pl += stock_summary.unrealized_pl;
        if (!isNaN(stock_summary.realized_pl)){
          if (("realized_pl" in this.breakdown_totals[key])){
            this.breakdown_totals[key].realized_pl += stock_summary.realized_pl;
          } else {
            this.breakdown_totals[key].realized_pl = stock_summary.realized_pl;
          }
        }
      } else {
        this.breakdown_totals[key] = {
          total_value: stock_summary.total_value,
          unrealized_pl: stock_summary.unrealized_pl,
        };
        if (!isNaN(stock_summary.realized_pl)){
          this.breakdown_totals[key].realized_pl = stock_summary.realized_pl;
        }
      }
  },

  getStockBreakdowns: function() {
    return this.breakdown_totals;
  }
};
<|endoftext|>


<commit_before>/****************************************************************************
 The MIT License (MIT)

 Copyright (c) 2015 Apigee Corporation

 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
 in the Software without restriction, including without limitation the rights
 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 copies of the Software, and to permit persons to whom the Software is
 furnished to do so, subject to the following conditions:

 The above copyright notice and this permission notice shall be included in
 all copies or substantial portions of the Software.

 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 THE SOFTWARE.
 ****************************************************************************/
'use strict';

var debug = require('debug')('swagger');
var Url = require('url');
var CORS = require('cors');

module.exports = init;

function init(runner) {
  return new Middleware(runner);
}

function Middleware(runner) {
  this.runner = runner;
  this.config = runner.config.swaggerNode;

  this.metadata = function metadata() {

    return this.runner.swaggerTools.swaggerMetadata();
  };

  this.security = function security() {

    debug('swagger security config: %j', this.config.securityHandlers);
    return this.runner.swaggerTools.swaggerSecurity(this.config.securityHandlers);
  };

  this.validator = function validator() {

    var validatorConfig = {
      validateResponse: !!this.config.validateResponse
    };
    debug('validator config: %j', validatorConfig);
    return this.runner.swaggerTools.swaggerValidator(validatorConfig);
  };

  this.router = function router() {

    var controllers = this.config.mockMode ? this.config.mockControllersDirs : this.config.controllersDirs;

    var routerConfig = {
      useStubs: !!this.config.mockMode,
      controllers: controllers
    };
    debug('swaggerTools router config: %j', routerConfig);
    return this.runner.swaggerTools.swaggerRouter(routerConfig);
  };

  this.jsonErrorHandler = function() {

    debug('jsonErrorHandler configured');
    return function jsonErrorHandler(err, req, res, next) {
      if (err && typeof err === 'object') {
        Object.defineProperty(err, 'message', {enumerable: true}); // include message property in response
        res.end(JSON.stringify(err));
      }
      next(err);
    };
  };

  this.expressCompatibilityMW = function expressCompatibilityMW() {
    debug('express compatibility middleware configured');
    return expressCompatibility;
  };

  this.swaggerDocs = function swaggerDocs() {

    debug('swagger docs functionality not yet implemented');
    // todo: this needs to be implemented
    return function(req, res, next) {
      next();
    };

    //var config = {
    //  apiDocs: '/api-docs'
    //};
    //return this.runner.swaggerTools.swaggerUi(config);
  };

  // enables request.swaggerNode.config
  this.helpers = function helpers() {

    debug('swaggerNode.config will be attached to requests');
    var swaggerNode = {
      config: this.runner.config
    };
    return function(req, res, next) {
      req.swaggerNode = swaggerNode;
      next();
    };
  };

  // config options: https://www.npmjs.com/package/cors
  this.cors = function cors() {

    debug('cors config: %j', this.config.corsOptions);
    //return CORS(Object.keys(this.config.corsOptions).length ? this.config.corsOptions : undefined);
    return CORS(this.config.corsOptions);
  };

  this.chain = function chain(options) {

    options = options || {};

    var middlewares = [
      this.metadata(),
      this.security(),
      this.validator(),
      this.helpers(),
      this.expressCompatibilityMW(),
      this.router(),
      this.swaggerDocs()
    ];

    if (this.config.corsOptions) {
      middlewares.unshift(this.cors());
    }

    if (options.mapErrorsToJson) {
      middlewares.push(this.jsonErrorHandler());
    }

    return createChain(middlewares);
  };
}

function createChain(middlewares) {

  if (!middlewares || middlewares.length < 1) {
    return function noOp(req, res, next) { next(); };
  }

  return function middlewareChain(req, res, next) {
    function createNext(middleware, index) {
      return function runNext(err) {
        if (err) { return next(err); }

        var nextIndex = index + 1;
        var nextMiddleware = middlewares[nextIndex] ? createNext(middlewares[nextIndex], nextIndex) : next;
        middleware(req, res, nextMiddleware);
      };
    }
    return createNext(middlewares[0], 0)();
  };
}

function expressCompatibility(req, res, next) {

  if (!req.query) {
    req.query = Url.parse(req.url, true).query;
  }

  res.json = function(obj) {
    if (!res.getHeader('Content-Type')) {
      res.setHeader('Content-Type', 'application/json');
    }
    res.end(JSON.stringify(obj));
  };

  if (!req.get) req.get = function(name) {
    return this.headers[name];
  };

  if (!res.set) { res.set = res.setHeader; }
  if (!res.get) { res.get = res.getHeader; }
  if (!res.status) {
    res.status = function(status) {
      res.statusCode = status;
    };
  }

  next();
}
<commit_msg>add swaggerDoc serving function<commit_after>/****************************************************************************
 The MIT License (MIT)

 Copyright (c) 2015 Apigee Corporation

 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
 in the Software without restriction, including without limitation the rights
 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 copies of the Software, and to permit persons to whom the Software is
 furnished to do so, subject to the following conditions:

 The above copyright notice and this permission notice shall be included in
 all copies or substantial portions of the Software.

 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 THE SOFTWARE.
 ****************************************************************************/
'use strict';

var debug = require('debug')('swagger');
var Url = require('url');
var CORS = require('cors');
var YAML = require('yamljs');

module.exports = init;

function init(runner) {
  return new Middleware(runner);
}

function Middleware(runner) {
  this.runner = runner;
  this.config = runner.config.swaggerNode;

  this.metadata = function metadata() {

    return this.runner.swaggerTools.swaggerMetadata();
  };

  this.security = function security() {

    debug('swagger security config: %j', this.config.securityHandlers);
    return this.runner.swaggerTools.swaggerSecurity(this.config.securityHandlers);
  };

  this.validator = function validator() {

    var validatorConfig = {
      validateResponse: !!this.config.validateResponse
    };
    debug('validator config: %j', validatorConfig);
    return this.runner.swaggerTools.swaggerValidator(validatorConfig);
  };

  this.router = function router() {

    var controllers = this.config.mockMode ? this.config.mockControllersDirs : this.config.controllersDirs;

    var routerConfig = {
      useStubs: !!this.config.mockMode,
      controllers: controllers
    };
    debug('swaggerTools router config: %j', routerConfig);
    return this.runner.swaggerTools.swaggerRouter(routerConfig);
  };

  this.jsonErrorHandler = function() {

    debug('jsonErrorHandler configured');
    return function jsonErrorHandler(err, req, res, next) {
      if (err && typeof err === 'object') {
        Object.defineProperty(err, 'message', {enumerable: true}); // include message property in response
        res.end(JSON.stringify(err));
      }
      next(err);
    };
  };

  this.expressCompatibilityMW = function expressCompatibilityMW() {
    debug('express compatibility middleware configured');
    return expressCompatibility;
  };

  this.swaggerDoc = function swaggerDoc() {

    debug('swagger doc path: %s', this.config.swaggerDocPath);

    // should this just be based on accept type?
    var swaggerDocPath = this.config.swaggerDocPath;
    var yaml = YAML.stringify(this.runner.swagger, 99, 2);
    var json = JSON.stringify(this.runner.swagger, null, 2);

    return function swaggerDoc(req, res, next) {
      if (swaggerDocPath) {
        if (!req.path) {
          req.path = Url.parse(req.url).path
        }
        if (swaggerDocPath.indexOf(req.path) != -1) {
          if (req.headers['accept'].indexOf('yaml') != -1) {
            res.setHeader('Content-Type', 'application/yaml');
            return res.end(yaml);
          } else {
            res.setHeader('Content-Type', 'application/json');
            return res.end(json);
          }
        }
      }
      next();
    };

  };

  // enables request.swaggerNode.config
  this.helpers = function helpers() {

    debug('swaggerNode.config will be attached to requests');
    var swaggerNode = {
      config: this.runner.config
    };
    return function(req, res, next) {
      req.swaggerNode = swaggerNode;
      next();
    };
  };

  // config options: https://www.npmjs.com/package/cors
  this.cors = function cors() {

    debug('cors config: %j', this.config.corsOptions);
    return CORS(this.config.corsOptions);
  };

  this.chain = function chain(options) {

    options = options || {};

    var middlewares = [
      this.metadata(),
      this.security(),
      this.validator(),
      this.helpers(),
      this.expressCompatibilityMW(),
      this.router()
    ];

    if (this.config.corsOptions) {
      middlewares.unshift(this.cors());
    }

    if (this.config.swaggerDocPath) {
      middlewares.push(this.swaggerDoc());
    }

    if (options.mapErrorsToJson) {
      middlewares.push(this.jsonErrorHandler());
    }

    return createChain(middlewares);
  };
}

function createChain(middlewares) {

  if (!middlewares || middlewares.length < 1) {
    return function noOp(req, res, next) { next(); };
  }

  return function middlewareChain(req, res, next) {
    function createNext(middleware, index) {
      return function runNext(err) {
        if (err) { return next(err); }

        var nextIndex = index + 1;
        var nextMiddleware = middlewares[nextIndex] ? createNext(middlewares[nextIndex], nextIndex) : next;
        middleware(req, res, nextMiddleware);
      };
    }
    return createNext(middlewares[0], 0)();
  };
}

function expressCompatibility(req, res, next) {

  if (!req.query || !req.path) {
    var url = Url.parse(req.url, !req.query);
    req.path = url.path;
    req.query = url.query;
  }

  res.json = function(obj) {
    if (!res.getHeader('Content-Type')) {
      res.setHeader('Content-Type', 'application/json');
    }
    res.end(JSON.stringify(obj));
  };

  if (!req.get) req.get = function(name) {
    return this.headers[name];
  };

  if (!res.set) { res.set = res.setHeader; }
  if (!res.get) { res.get = res.getHeader; }
  if (!res.status) {
    res.status = function(status) {
      res.statusCode = status;
    };
  }

  next();
}
<|endoftext|>


<commit_before>// Imported from json.js
var querystring = require('querystring');
var cip = require('../services/natmus-cip');
var config = require('../config/config');
var asset_player = require('../asset-player');
var helpers = {};

var page_size = 10;

helpers.catalog_url = function catalog_url(result) {
  return '/' + result.catalog;
};

helpers.asset_url = function asset_url(result) {
  return '/' + result.catalog + '/' + result.id;
};

helpers.thumbnail_url = function thumbnail_url(result) {
  return '/' + result.catalog + '/' + result.id + '/thumbnail';
};

helpers.find_catalog = function find_catalog(req, alias) {
  var catalog = cip.findCatalog(req.app.get('cip_catalogs'), alias);
  if(!catalog) {
      return null;
  }
  return catalog.name;
};

helpers.thousandsSeparator = function thousandsSeparator(number) {
  return number.toString().replace(/(\d)(?=(\d\d\d)+(?!\d))/g, "$1.");
};

function prepareFilters(req) {
  var freetext = req.query.q || '';
  var params = {};

  if('geotagged' in req.query) {
    params.geotagged = req.query.geotagged === 'true';
  } else {
    params.geotagged = null;
  }

  if('catalog' in req.params && req.params.catalog !== 'null') {
    params.catalog = req.params.catalog;
  }

  if('cat' in req.query && req.query.cat !== '') {
    params.category = parseInt(req.query.cat, 10);
  }

  var offset;
  if('p' in req.query) {
    var p = parseInt(req.query.p, 10);
    // Offset by page size
    offset = (p - 1 ) * page_size;
  } else {
    offset = 0;
  }

  return {
    freetext: freetext,
    offset: offset,
    params: params
  };
}

function search(es_client, params, freetext, offset) {
  var query = {
    index: config.es_assets_index,
    size: page_size,
    from: offset,
    body: {}
  };

  query.body = {
      sort: [
          {'modification_time': 'desc'}
      ],
      filter: {
          and: [],
      }
  };

  // Add catalog query parameter
  if ('catalog' in params) {
    query.body.filter.and.push({
      query: {match: {"catalog": params.catalog}}
    });
  }

  // Add category query parameter
  if ('category' in params) {
    query.body.filter.and.push({
      query: {match: {"categories_int": params.category} }
    });
  }

  // Make sure all results are reachable through a search.
  query.body.filter.and.push({
    query: { match: {"is_searchable": true} }
  });

  if('geotagged' in params && params.geotagged !== null) {
    var geotaggingQuery;
    if(params.geotagged) {
      query.body.filter.and.push({
        or: [
          {exists: {field: 'google_maps_coordinates'}},
          {exists: {field: 'google_maps_coordinates_crowd'}}
        ]
      });
    } else {
      query.body.filter.and.push({
        and: [
          {missing: {field: 'google_maps_coordinates'}},
          {missing: {field: 'google_maps_coordinates_crowd'}}
        ]
      });
    }
  }

  if(freetext !== undefined && freetext.length > 0) {
    freetext = freetext.replace('-', ' ');
    var freetext_arr = freetext.trim().split(/ +/);

    for(var i=0; i < freetext_arr.length; i++) {
        freetext_arr[i] = '"' + freetext_arr[i] + '"';
    }

    query.body.filter.and.push({
        query: {
            query_string: {
                query: "_all:" + freetext_arr.join(' AND ')
            }
        }
    });
  }

  if(query.body.filter.and.length === 0) {
      delete query.body.filter.and;
  }

  return es_client.search(query).then(function (resp) {
    var results = [];
    var temp_results = resp.hits.hits;
    var total_results = resp.hits.total;

    for(var i=0; i < temp_results.length; ++i) {
      results.push(temp_results[i]._source);
    }
    return [results, total_results];
  });
}

exports.result = function index(req, res) {
  var es_client = req.app.get('es_client');

  // Filter parameters
  var filters = prepareFilters(req);

  // Get the intial assets
  return search(es_client, filters.params, filters.freetext, filters.offset)
  .then(function(search_result) {
    var results = search_result[0];
    var total_results = search_result[1];

    var final_results = {
      offset: filters.offset,
      total_results_raw: total_results,
      total_results: helpers.thousandsSeparator(total_results),
      results: []
    };

    var real_results = [];
    for(var i=0; i < results.length; ++i) {
      real_results.push({
        title: results[i].short_title,
        url: helpers.asset_url(results[i]),
        thumbnail_url: helpers.thumbnail_url(results[i]),
        catalog_url: helpers.catalog_url(results[i]),
        description: results[i].description,
        catalog: (helpers.find_catalog(req, results[i].catalog) || ''),
        type: asset_player.determine_player( results[i] )
      });
    }

    final_results.results = real_results;

    // Heading for either front page or search page
    if('q' in req.query) {
      final_results.is_search = true;
    } else {
      final_results.is_front = true;
    }
    return final_results;
  }).then(function(results) {
    var qs = {
      p: 2,
      q: filters.freetext
    };
    if(filters.params.geotagged !== null) {
      qs.geotagged = filters.params.geotagged;
    }

    qs = '?'+querystring.stringify(qs);

    res.render('search', {
      req: req,
      infinitepath: 'null/infinite'+qs,
      results: results,
      filters: filters
    });
    res.end();
  });
};

// Catalog page
exports.catalog = function catalog(req, res, next) {
  var es_client = req.app.get('es_client');
  var query_params = {};

  var filters = prepareFilters(req);

  var catalogs = req.app.get('cip_catalogs');
  var catalog = catalogs.reduce(function(result, c) {
    if(result) {
      return result;
    } else if(c.alias === req.params.catalog) {
      return {
        name: c.name,
        alias: c.alias,
        children: categories[c.alias].tree
      };
    }
  }, undefined);

  if(!catalog) {
    var err = new Error('Catalog does not exist!');
    err.status = 404;
    next(err);
  } else {
    // Get the intial assets
    search(es_client, filters.params, filters.freetext, filters.offset)
    .then(function(search_result) {

      var results = search_result[0];
      var total_results = search_result[1];

      // Heading for either catalog, cat page - first grab some meta data
      var categories = req.app.get('cip_categories');

      var final_results = {
        offset: filters.offset,
        total_results_raw: total_results,
        total_results: helpers.thousandsSeparator(total_results),
        results: []
      };

      var real_results = [];
      for(var i=0; i < results.length; ++i) {
        real_results.push({
          title: results[i].short_title,
          url: helpers.asset_url(results[i]),
          thumbnail_url: helpers.thumbnail_url(results[i]),
          catalog_url: helpers.catalog_url(results[i]),
          description: results[i].description,
          catalog: (helpers.find_catalog(req, results[i].catalog) || ''),
          type: asset_player.determine_player( results[i] )
        });
      }

      final_results.results = real_results;

      var category = filters.params.category;
      var breadcrumbs;
      if (req.params.catalog && category) {
        // Retrieve the breadcrumbs
        var path = categories[req.params.catalog].get_path(category);
        breadcrumbs = {
          first:  path.length > 1 ? path[1] : null,
          second: path.length > 2 ? path[2] : null,
          last:   path[path.length - 1]
        };

        final_results.is_category = true;
      } else {
        final_results.is_catalog = true;
      }

      var qs = {
        p: 2,
        q: filters.freetext
      };
      if(filters.params.geotagged !== null) {
        qs.geotagged = filters.params.geotagged;
      }
      if(category) {
        qs.cat = category;
      }

      qs = '?'+querystring.stringify(qs);

      res.render('search', {
        req: req,
        infinitepath: req.params.catalog + '/infinite' + qs,
        results: final_results,
        breadcrumbs: breadcrumbs,
        catalog: catalog,
        filters: filters
      });
    }, next);
  }
};

// Infinite scroll for both catalog pages, front page and search pages
exports.infinite = function(req, res) {
  var es_client = req.app.get('es_client');

  var filters = prepareFilters(req);

  search(es_client, filters.params, filters.freetext, filters.offset)
  .then(function(search_result) {
    var results = search_result[0];
    var total_results = search_result[1];

    var parsedResults = [];
    for(var i=0; i < results.length; ++i) {
        parsedResults.push({
          title: results[i].short_title,
          url: helpers.asset_url(results[i]),
          thumbnail_url: helpers.thumbnail_url(results[i]),
          catalog_url: helpers.catalog_url(results[i]),
          description: results[i].description,
          catalog: (helpers.find_catalog(req, results[i].catalog) || ''),
          type: asset_player.determine_player( results[i] )
      });
    }

    res.render('infinite-search', {
      req: req,
      results: parsedResults
    });
    }
  );
};

// Retrieve the main menu async & render server side
exports.mainmenu = function(req, res) {
  // Grab categories, i.e. second level in the menu
  var categories = req.app.get('cip_categories');

  // Grab the catalogs, i.e. first level in the menu
  var catalogs = req.app.get('cip_catalogs');
  var catalogsToRender = [];

  catalogs = catalogs.sort(function(x,y) {
    return x.name.localeCompare(y.name);
  });

  for(var c in catalogs) {
    var catalog = catalogs[c];
    if(catalog.alias === undefined) {
      continue;
    }
    // If fetching the catalogs categories failed.
    if(!categories[catalog.alias] || !categories[catalog.alias].tree) {
      continue;
    }

    // The count of assets in a catalog is the sum of assets in it's categories.
    var count = categories[catalog.alias].tree.children.reduce(function(r, c) {
      return r + c.count;
    }, 0);

    catalogsToRender.push({
      name: catalog.name,
      alias: catalog.alias,
      categoryRoot: categories[catalog.alias].tree,
      count: count
    });
  }
  // res.json(results);
  res.render('main-menu', {
    catalogs: catalogsToRender
  });
};
<commit_msg>Now throwing an error if the catalogs category does not exist<commit_after>// Imported from json.js
var querystring = require('querystring');
var cip = require('../services/natmus-cip');
var config = require('../config/config');
var asset_player = require('../asset-player');
var helpers = {};

var page_size = 10;

helpers.catalog_url = function catalog_url(result) {
  return '/' + result.catalog;
};

helpers.asset_url = function asset_url(result) {
  return '/' + result.catalog + '/' + result.id;
};

helpers.thumbnail_url = function thumbnail_url(result) {
  return '/' + result.catalog + '/' + result.id + '/thumbnail';
};

helpers.find_catalog = function find_catalog(req, alias) {
  var catalog = cip.findCatalog(req.app.get('cip_catalogs'), alias);
  if(!catalog) {
      return null;
  }
  return catalog.name;
};

helpers.thousandsSeparator = function thousandsSeparator(number) {
  return number.toString().replace(/(\d)(?=(\d\d\d)+(?!\d))/g, "$1.");
};

function prepareFilters(req) {
  var freetext = req.query.q || '';
  var params = {};

  if('geotagged' in req.query) {
    params.geotagged = req.query.geotagged === 'true';
  } else {
    params.geotagged = null;
  }

  if('catalog' in req.params && req.params.catalog !== 'null') {
    params.catalog = req.params.catalog;
  }

  if('cat' in req.query && req.query.cat !== '') {
    params.category = parseInt(req.query.cat, 10);
  }

  var offset;
  if('p' in req.query) {
    var p = parseInt(req.query.p, 10);
    // Offset by page size
    offset = (p - 1 ) * page_size;
  } else {
    offset = 0;
  }

  return {
    freetext: freetext,
    offset: offset,
    params: params
  };
}

function search(es_client, params, freetext, offset) {
  var query = {
    index: config.es_assets_index,
    size: page_size,
    from: offset,
    body: {}
  };

  query.body = {
      sort: [
          {'modification_time': 'desc'}
      ],
      filter: {
          and: [],
      }
  };

  // Add catalog query parameter
  if ('catalog' in params) {
    query.body.filter.and.push({
      query: {match: {"catalog": params.catalog}}
    });
  }

  // Add category query parameter
  if ('category' in params) {
    query.body.filter.and.push({
      query: {match: {"categories_int": params.category} }
    });
  }

  // Make sure all results are reachable through a search.
  query.body.filter.and.push({
    query: { match: {"is_searchable": true} }
  });

  if('geotagged' in params && params.geotagged !== null) {
    var geotaggingQuery;
    if(params.geotagged) {
      query.body.filter.and.push({
        or: [
          {exists: {field: 'google_maps_coordinates'}},
          {exists: {field: 'google_maps_coordinates_crowd'}}
        ]
      });
    } else {
      query.body.filter.and.push({
        and: [
          {missing: {field: 'google_maps_coordinates'}},
          {missing: {field: 'google_maps_coordinates_crowd'}}
        ]
      });
    }
  }

  if(freetext !== undefined && freetext.length > 0) {
    freetext = freetext.replace('-', ' ');
    var freetext_arr = freetext.trim().split(/ +/);

    for(var i=0; i < freetext_arr.length; i++) {
        freetext_arr[i] = '"' + freetext_arr[i] + '"';
    }

    query.body.filter.and.push({
        query: {
            query_string: {
                query: "_all:" + freetext_arr.join(' AND ')
            }
        }
    });
  }

  if(query.body.filter.and.length === 0) {
      delete query.body.filter.and;
  }

  return es_client.search(query).then(function (resp) {
    var results = [];
    var temp_results = resp.hits.hits;
    var total_results = resp.hits.total;

    for(var i=0; i < temp_results.length; ++i) {
      results.push(temp_results[i]._source);
    }
    return [results, total_results];
  });
}

exports.result = function index(req, res) {
  var es_client = req.app.get('es_client');

  // Filter parameters
  var filters = prepareFilters(req);

  // Get the intial assets
  return search(es_client, filters.params, filters.freetext, filters.offset)
  .then(function(search_result) {
    var results = search_result[0];
    var total_results = search_result[1];

    var final_results = {
      offset: filters.offset,
      total_results_raw: total_results,
      total_results: helpers.thousandsSeparator(total_results),
      results: []
    };

    var real_results = [];
    for(var i=0; i < results.length; ++i) {
      real_results.push({
        title: results[i].short_title,
        url: helpers.asset_url(results[i]),
        thumbnail_url: helpers.thumbnail_url(results[i]),
        catalog_url: helpers.catalog_url(results[i]),
        description: results[i].description,
        catalog: (helpers.find_catalog(req, results[i].catalog) || ''),
        type: asset_player.determine_player( results[i] )
      });
    }

    final_results.results = real_results;

    // Heading for either front page or search page
    if('q' in req.query) {
      final_results.is_search = true;
    } else {
      final_results.is_front = true;
    }
    return final_results;
  }).then(function(results) {
    var qs = {
      p: 2,
      q: filters.freetext
    };
    if(filters.params.geotagged !== null) {
      qs.geotagged = filters.params.geotagged;
    }

    qs = '?'+querystring.stringify(qs);

    res.render('search', {
      req: req,
      infinitepath: 'null/infinite'+qs,
      results: results,
      filters: filters
    });
    res.end();
  });
};

// Catalog page
exports.catalog = function catalog(req, res, next) {
  var es_client = req.app.get('es_client');
  var query_params = {};

  var filters = prepareFilters(req);

  var categories = req.app.get('cip_categories');
  var catalogs = req.app.get('cip_catalogs');
  var catalog = catalogs.reduce(function(result, c) {
    if(result) {
      return result;
    } else if(c.alias === req.params.catalog) {
      return {
        name: c.name,
        alias: c.alias,
        children: categories[c.alias].tree
      };
    }
  }, undefined);

  if(!catalog) {
    var err = new Error('Catalog does not exist!');
    err.status = 404;
    next(err);
  } else {
    // Get the intial assets
    search(es_client, filters.params, filters.freetext, filters.offset)
    .then(function(search_result) {

      var results = search_result[0];
      var total_results = search_result[1];

      var final_results = {
        offset: filters.offset,
        total_results_raw: total_results,
        total_results: helpers.thousandsSeparator(total_results),
        results: []
      };

      var real_results = [];
      for(var i=0; i < results.length; ++i) {
        real_results.push({
          title: results[i].short_title,
          url: helpers.asset_url(results[i]),
          thumbnail_url: helpers.thumbnail_url(results[i]),
          catalog_url: helpers.catalog_url(results[i]),
          description: results[i].description,
          catalog: (helpers.find_catalog(req, results[i].catalog) || ''),
          type: asset_player.determine_player( results[i] )
        });
      }

      final_results.results = real_results;

      var category = filters.params.category;
      var breadcrumbs;
      if(category) {
        // Retrieve the breadcrumbs
        var path = categories[catalog.alias].get_path(category);
        if(!path) {
          var err = new Error('Category does not exist!');
          err.status = 404;
          next(err);
          return;
        } else {
          breadcrumbs = {
            first:  path.length > 1 ? path[1] : null,
            second: path.length > 2 ? path[2] : null,
            last:   path[path.length - 1]
          };
          final_results.is_category = true;
        }
      } else {
        final_results.is_catalog = true;
      }

      var qs = {
        p: 2,
        q: filters.freetext
      };
      if(filters.params.geotagged !== null) {
        qs.geotagged = filters.params.geotagged;
      }
      if(category) {
        qs.cat = category;
      }

      qs = '?'+querystring.stringify(qs);

      res.render('search', {
        req: req,
        infinitepath: req.params.catalog + '/infinite' + qs,
        results: final_results,
        breadcrumbs: breadcrumbs,
        catalog: catalog,
        filters: filters
      });
    }, next);
  }
};

// Infinite scroll for both catalog pages, front page and search pages
exports.infinite = function(req, res) {
  var es_client = req.app.get('es_client');

  var filters = prepareFilters(req);

  search(es_client, filters.params, filters.freetext, filters.offset)
  .then(function(search_result) {
    var results = search_result[0];
    var total_results = search_result[1];

    var parsedResults = [];
    for(var i=0; i < results.length; ++i) {
        parsedResults.push({
          title: results[i].short_title,
          url: helpers.asset_url(results[i]),
          thumbnail_url: helpers.thumbnail_url(results[i]),
          catalog_url: helpers.catalog_url(results[i]),
          description: results[i].description,
          catalog: (helpers.find_catalog(req, results[i].catalog) || ''),
          type: asset_player.determine_player( results[i] )
      });
    }

    res.render('infinite-search', {
      req: req,
      results: parsedResults
    });
    }
  );
};

// Retrieve the main menu async & render server side
exports.mainmenu = function(req, res) {
  // Grab categories, i.e. second level in the menu
  var categories = req.app.get('cip_categories');

  // Grab the catalogs, i.e. first level in the menu
  var catalogs = req.app.get('cip_catalogs');
  var catalogsToRender = [];

  catalogs = catalogs.sort(function(x,y) {
    return x.name.localeCompare(y.name);
  });

  for(var c in catalogs) {
    var catalog = catalogs[c];
    if(catalog.alias === undefined) {
      continue;
    }
    // If fetching the catalogs categories failed.
    if(!categories[catalog.alias] || !categories[catalog.alias].tree) {
      continue;
    }

    // The count of assets in a catalog is the sum of assets in it's categories.
    var count = categories[catalog.alias].tree.children.reduce(function(r, c) {
      return r + c.count;
    }, 0);

    catalogsToRender.push({
      name: catalog.name,
      alias: catalog.alias,
      categoryRoot: categories[catalog.alias].tree,
      count: count
    });
  }
  // res.json(results);
  res.render('main-menu', {
    catalogs: catalogsToRender
  });
};
<|endoftext|>


<commit_before>var URL = require('url'),
    express = require('express'),
    request = require('request');

module.exports = function createLiveStyleApp(options) {
    var app = express.createServer(),
        proxyUrl;

    if (options.proxy) {
        if (!/\/$/.test(options.proxy)) {
            options.proxy += '/';
        }
        proxyUrl = URL.parse(options.proxy);
    }

    app.configure(function () {
        if (options.debug) {
            app.use(express.logger('dev'));
        }

        app
            .use(require('./middleware/bufferDataEventsUntilFirstListener')())
            .use(require('./middleware/injectLiveStyleScriptIncludeIntoHtml')(options));

        if (options.mappings) {
            app
                .use(function (req, res, next) {
                    Object.keys(options.mappings).forEach(function (url) {
                        if (req.url.indexOf(url) === 0) {
                            req.url = req.url.replace(url, options.mappings[url]);
                        }
                    });
                    next();
                });
        }

        if (options.documentRoot) {
            var staticProvider = express['static'](options.documentRoot);
            app.use(function (req, res, next) {
                if (!proxyUrl || req.accepts('css') || req.accepts('text/x-less')) {
                    staticProvider(req, res, next);
                } else {
                    next();
                }
            });
        }

        if (proxyUrl) {
            app.use(function (req, res, next) {
                var targetUrl = proxyUrl.protocol + '//' + proxyUrl.host + req.url,
                    upstreamRequest = request({
                        method: req.method,
                        url: targetUrl,
                        onResponse: true
                    }, function (err, response) {
                        if (options.debug) {
                            console.warn('Proxying ' + req.url + ' to ' + targetUrl + ' => ' + (err ? err.toString() : response.statusCode));
                        }
                        if (err) {
                            res.writeHead(502, {});
                            res.end('Error connecting to upstream server: ' + err.stack);
                        } else {
                            res.writeHead(response.statusCode, response.headers);
                            response.pipe(res);
                        }
                    });
                req.pipe(upstreamRequest);
            });
        }

        if (options.documentRoot) {
            require('../lib/installLiveCssFileWatcherInServer')(app, {
                debug: options.debug,
                documentRoot: options.documentRoot,
                mappings: options.mappings
            }, require('socket.io'));
        }

        app.use(express.errorHandler({dumpExceptions: options.debug, showStack: options.debug}));
    });

    return app;
};
<commit_msg>createLiveStyleApp, proxy mode: Tell request to not follow redirects. Fixes #16.<commit_after>var URL = require('url'),
    express = require('express'),
    request = require('request');

module.exports = function createLiveStyleApp(options) {
    var app = express.createServer(),
        proxyUrl;

    if (options.proxy) {
        if (!/\/$/.test(options.proxy)) {
            options.proxy += '/';
        }
        proxyUrl = URL.parse(options.proxy);
    }

    app.configure(function () {
        if (options.debug) {
            app.use(express.logger('dev'));
        }

        app
            .use(require('./middleware/bufferDataEventsUntilFirstListener')())
            .use(require('./middleware/injectLiveStyleScriptIncludeIntoHtml')(options));

        if (options.mappings) {
            app
                .use(function (req, res, next) {
                    Object.keys(options.mappings).forEach(function (url) {
                        if (req.url.indexOf(url) === 0) {
                            req.url = req.url.replace(url, options.mappings[url]);
                        }
                    });
                    next();
                });
        }

        if (options.documentRoot) {
            var staticProvider = express['static'](options.documentRoot);
            app.use(function (req, res, next) {
                if (!proxyUrl || req.accepts('css') || req.accepts('text/x-less')) {
                    staticProvider(req, res, next);
                } else {
                    next();
                }
            });
        }

        if (proxyUrl) {
            app.use(function (req, res, next) {
                var targetUrl = proxyUrl.protocol + '//' + proxyUrl.host + req.url,
                    upstreamRequest = request({
                        followRedirect: false,
                        method: req.method,
                        url: targetUrl,
                        onResponse: true
                    }, function (err, response) {
                        if (options.debug) {
                            console.warn('Proxying ' + req.url + ' to ' + targetUrl + ' => ' + (err ? err.toString() : response.statusCode));
                        }
                        if (err) {
                            res.writeHead(502, {});
                            res.end('Error connecting to upstream server: ' + err.stack);
                        } else {
                            res.writeHead(response.statusCode, response.headers);
                            response.pipe(res);
                        }
                    });
                req.pipe(upstreamRequest);
            });
        }

        if (options.documentRoot) {
            require('../lib/installLiveCssFileWatcherInServer')(app, {
                debug: options.debug,
                documentRoot: options.documentRoot,
                mappings: options.mappings
            }, require('socket.io'));
        }

        app.use(express.errorHandler({dumpExceptions: options.debug, showStack: options.debug}));
    });

    return app;
};
<|endoftext|>


<commit_before>var URL = require('url'),
    express = require('express'),
    request = require('request');

module.exports = function createLiveStyleApp(options) {
    var app = express.createServer(),
        proxyUrl;

    if (options.proxy) {
        if (!/\/$/.test(options.proxy)) {
            options.proxy += '/';
        }
        proxyUrl = URL.parse(options.proxy);
    }

    app.configure(function () {
        if (options.debug) {
            app.use(express.logger('dev'));
        }

        app
            .use(require('./middleware/bufferDataEventsUntilFirstListener')())
            .use(require('./middleware/injectLiveStyleScriptIncludeIntoHtml')(options));

        if (options.mappings) {
            app
                .use(function (req, res, next) {
                    Object.keys(options.mappings).forEach(function (url) {
                        if (req.url.indexOf(url) === 0) {
                            req.url = req.url.replace(url, options.mappings[url]);
                        }
                    });
                    next();
                });
        }

        if (options.documentRoot) {
            var staticProvider = express['static'](options.documentRoot);
            app.use(function (req, res, next) {
                if (!proxyUrl || req.accepts('css') || req.accepts('text/x-less')) {
                    staticProvider(req, res, next);
                } else {
                    next();
                }
            });
        }

        if (proxyUrl) {
            var selfRedirectUrlRegExp = new RegExp('^' + proxyUrl.protocol + '//' + proxyUrl.host.replace(/[\.\+\*\?\{\}\[\]\(\)]/g, '\\$&') + '/');
            app.use(function (req, res, next) {
                var targetUrl = proxyUrl.protocol + '//' + proxyUrl.host + req.url,
                    upstreamRequest = request({
                        followRedirect: false,
                        method: req.method,
                        url: targetUrl,
                        onResponse: true
                    }, function (err, response) {
                        if (options.debug) {
                            console.warn('Proxying ' + req.url + ' to ' + targetUrl + ' => ' + (err ? err.toString() : response.statusCode));
                        }
                        if (err) {
                            res.writeHead(502, {});
                            res.end('Error connecting to upstream server: ' + err.stack);
                        } else {
                            if (response.headers.location && /^https?:/.test(response.headers.location)) {
                                // HTTP redirect. If it's an absolute url pointing at the upstream server, rewrite it to relative one
                                // so it points at the LiveStyle proxy. Technically this is a violation of the HTTP spec, but getting the
                                // LiveStyle servers host/port is a bit hard from here, and browsers understand it fine:
                                var rewrittenRedirect = response.headers.location.replace(selfRedirectUrlRegExp, '/');
                                if (rewrittenRedirect !== response.headers.location) {
                                    if (options.debug) {
                                        console.warn("Rewriting redirect from upstream server: " + response.headers.location + " => " + rewrittenRedirect);
                                    }
                                    response.headers.location = rewrittenRedirect;
                                }
                            }
                            res.writeHead(response.statusCode, response.headers);
                            response.pipe(res);
                        }
                    });
                req.pipe(upstreamRequest);
            });
        }

        if (options.documentRoot) {
            require('../lib/installLiveCssFileWatcherInServer')(app, {
                debug: options.debug,
                documentRoot: options.documentRoot,
                mappings: options.mappings
            }, require('socket.io'));
        }

        app.use(express.errorHandler({dumpExceptions: options.debug, showStack: options.debug}));
    });

    return app;
};
<commit_msg>Fixed typo in comment.<commit_after>var URL = require('url'),
    express = require('express'),
    request = require('request');

module.exports = function createLiveStyleApp(options) {
    var app = express.createServer(),
        proxyUrl;

    if (options.proxy) {
        if (!/\/$/.test(options.proxy)) {
            options.proxy += '/';
        }
        proxyUrl = URL.parse(options.proxy);
    }

    app.configure(function () {
        if (options.debug) {
            app.use(express.logger('dev'));
        }

        app
            .use(require('./middleware/bufferDataEventsUntilFirstListener')())
            .use(require('./middleware/injectLiveStyleScriptIncludeIntoHtml')(options));

        if (options.mappings) {
            app
                .use(function (req, res, next) {
                    Object.keys(options.mappings).forEach(function (url) {
                        if (req.url.indexOf(url) === 0) {
                            req.url = req.url.replace(url, options.mappings[url]);
                        }
                    });
                    next();
                });
        }

        if (options.documentRoot) {
            var staticProvider = express['static'](options.documentRoot);
            app.use(function (req, res, next) {
                if (!proxyUrl || req.accepts('css') || req.accepts('text/x-less')) {
                    staticProvider(req, res, next);
                } else {
                    next();
                }
            });
        }

        if (proxyUrl) {
            var selfRedirectUrlRegExp = new RegExp('^' + proxyUrl.protocol + '//' + proxyUrl.host.replace(/[\.\+\*\?\{\}\[\]\(\)]/g, '\\$&') + '/');
            app.use(function (req, res, next) {
                var targetUrl = proxyUrl.protocol + '//' + proxyUrl.host + req.url,
                    upstreamRequest = request({
                        followRedirect: false,
                        method: req.method,
                        url: targetUrl,
                        onResponse: true
                    }, function (err, response) {
                        if (options.debug) {
                            console.warn('Proxying ' + req.url + ' to ' + targetUrl + ' => ' + (err ? err.toString() : response.statusCode));
                        }
                        if (err) {
                            res.writeHead(502, {});
                            res.end('Error connecting to upstream server: ' + err.stack);
                        } else {
                            if (response.headers.location && /^https?:/.test(response.headers.location)) {
                                // HTTP redirect. If it's an absolute url pointing at the upstream server, rewrite it to relative one
                                // so it points at the LiveStyle proxy. Technically this is a violation of the HTTP spec, but getting the
                                // LiveStyle server's host/port is a bit hard from here, and browsers understand it fine:
                                var rewrittenRedirect = response.headers.location.replace(selfRedirectUrlRegExp, '/');
                                if (rewrittenRedirect !== response.headers.location) {
                                    if (options.debug) {
                                        console.warn("Rewriting redirect from upstream server: " + response.headers.location + " => " + rewrittenRedirect);
                                    }
                                    response.headers.location = rewrittenRedirect;
                                }
                            }
                            res.writeHead(response.statusCode, response.headers);
                            response.pipe(res);
                        }
                    });
                req.pipe(upstreamRequest);
            });
        }

        if (options.documentRoot) {
            require('../lib/installLiveCssFileWatcherInServer')(app, {
                debug: options.debug,
                documentRoot: options.documentRoot,
                mappings: options.mappings
            }, require('socket.io'));
        }

        app.use(express.errorHandler({dumpExceptions: options.debug, showStack: options.debug}));
    });

    return app;
};
<|endoftext|>


<commit_before>'use strict';

const rp = require('request-promise');
const defaultTransitKey = Config.get('acs:transit_key');

const VAULT_CLIENT = new Vaulted({
  vault_host: Config.get('vault:host'),
  vault_port: Config.get('vault:port'),
  vault_ssl: Config.get('vault:ssh')
});

const getToken = function() {
  return rp({
    uri: 'http://localhost:4500/v1/token/default',
    json: true}
  ).then((body) => body.token);
};

module.exports = function generateCiphertext(secret) {
  return getToken().then((token) => {
    t = token;

    return VAULT_CLIENT.prepare(token);
  }).then((client) => client.encryptTransitPlainText({
    id: defaultTransitKey,
    body: {
      plaintext: secret
    },
    token: t
  })).then((response) => response.data.ciphertext);
};
<commit_msg>Fixed rebase.<commit_after>'use strict';

const rp = require('request-promise');
const defaultTransitKey = Config.get('acs:transit_key');

const TOKEND_ENDPOINT = 'http://localhost:4500/v1/token/default';
const VAULT_ENDPOINT = `http${Config.get('vault:ssl') ? 's' : ''}://${Config.get('vault:host')}:${Config.get('vault:port')}`;

const getToken = () => rp({uri: TOKEND_ENDPOINT, json: true}).then((body) => body.token);

module.exports = function generateCiphertext(secret) {
  return getToken().then((token) => {
    const options = {
      method: 'POST',
      uri: `${VAULT_ENDPOINT}/v1/transit/encrypt/${defaultTransitKey}`,
      body: {
        plaintext: secret
      },
      headers: {
        'X-Vault-Token': token
      },
      json: true
    };

    return rp(options);
  }).then((resp) => resp.data.ciphertext);
};
<|endoftext|>