<commit_before>"use babel"
// @flow

import path from "path"
import fs from "fs"
import type { CompositeDisposable, TextEditor } from "atom"
import type { Info } from "./types"
import { parseCode } from "./core"
import makeDebug from "debug"
import makeRequire from "./require-if-trusted"

const debug = makeDebug("js-hyperclick:make-cache")

function findFile(basedir, filenames) {
  for (let i = 0; i < filenames.length; i++) {
    try {
      const absolutePath = path.resolve(basedir, filenames[i])
      fs.accessSync(absolutePath)
      return absolutePath
    } catch (e) {
      // Skip this file, it probably doesn't exist
    }
  }

  const parent = path.resolve(basedir, "../")
  if (parent != basedir) {
    return findFile(parent, filenames)
  }
  return undefined
}

export default function cachedParser(subscriptions: CompositeDisposable) {
  const editors = new WeakMap()
  const data: WeakMap<TextEditor, Info> = new WeakMap()
  const configCache: WeakMap<TextEditor, ?Object> = new WeakMap()

  function findConfigFile(editor: TextEditor) {
    const basedir = path.dirname(editor.getPath())
    // "New in Babel 7.x, Babel has as concept of a "root" directory, which
    // defaults to the current working directory"
    // https://babeljs.io/docs/en/config-files#project-wide-configuration
    //
    // The current working directory doesn't work well for `js-hyperclick`
    // because you might launch Atom from anywhere and you can add multiple
    // projects at once.
    let configPath = findFile(basedir, ["babel.config.js"])
    if (configPath == null) {
      configPath = findFile(basedir, [".babelrc", ".babelrc.js"])
    }

    return configPath
  }

  function loadBabelConfig(editor) {
    if (!configCache.has(editor)) {
      let babelConfig: ?Object = undefined
      const configPath = findConfigFile(editor)
      if (configPath != null) {
        debug("loadBabelConfig", configPath)
        try {
          if (path.extname(configPath) === ".js") {
            const dontCache = {}

            const requireIfTrusted = makeRequire(trusted => {
              if (trusted === false) {
                return undefined
              }
              if (trusted === true) {
                data.delete(editor)
                configCache.delete(editor)
              }

              return dontCache
            })

            // $FlowExpectError
            const cfg = requireIfTrusted(configPath)
            debug("cfg", cfg)

            babelConfig = typeof cfg === "function" ? cfg() : cfg
            if (babelConfig === dontCache) {
              return undefined
            }
          } else {
            babelConfig = JSON.parse(String(fs.readFileSync(configPath)))
          }

          if (babelConfig) {
            babelConfig.cwd = babelConfig.cwd || path.dirname(configPath)
            const babel = require("@babel/core")
            babelConfig = babel.loadOptions(babelConfig)
          }
        } catch (e) {
          debug("loadBabelConfig error", e)
        }
        debug("babel config", babelConfig)
      }
      configCache.set(editor, babelConfig)
    }
    return configCache.get(editor)
  }

  function watchEditor(editor) {
    if (!editors.has(editor)) {
      editors.set(editor, null)
      subscriptions.add(
        editor.onDidStopChanging(() => {
          data.delete(editor)
        }),
      )
    }
  }

  return {
    get(editor: TextEditor): Info {
      watchEditor(editor)
      if (!data.has(editor)) {
        data.set(editor, parseCode(editor.getText(), loadBabelConfig(editor)))
      }

      // $FlowExpectError - Flow thinks it might return null here
      return data.get(editor)
    },
  }
}
<commit_msg>Reject custom Babel6 configs<commit_after>"use babel"
// @flow

import path from "path"
import fs from "fs"
import type { CompositeDisposable, TextEditor } from "atom"
import type { Info } from "./types"
import { parseCode } from "./core"
import makeDebug from "debug"
import makeRequire from "./require-if-trusted"

const debug = makeDebug("js-hyperclick:make-cache")

function findFile(basedir, filenames) {
  for (let i = 0; i < filenames.length; i++) {
    try {
      const absolutePath = path.resolve(basedir, filenames[i])
      fs.accessSync(absolutePath)
      return absolutePath
    } catch (e) {
      // Skip this file, it probably doesn't exist
    }
  }

  const parent = path.resolve(basedir, "../")
  if (parent != basedir) {
    return findFile(parent, filenames)
  }
  return undefined
}

export default function cachedParser(subscriptions: CompositeDisposable) {
  const editors = new WeakMap()
  const data: WeakMap<TextEditor, Info> = new WeakMap()
  const configCache: WeakMap<TextEditor, ?Object> = new WeakMap()

  function findConfigFile(editor: TextEditor) {
    const basedir = path.dirname(editor.getPath())
    // "New in Babel 7.x, Babel has as concept of a "root" directory, which
    // defaults to the current working directory"
    // https://babeljs.io/docs/en/config-files#project-wide-configuration
    //
    // The current working directory doesn't work well for `js-hyperclick`
    // because you might launch Atom from anywhere and you can add multiple
    // projects at once.
    let configPath = findFile(basedir, ["babel.config.js"])
    if (configPath == null) {
      configPath = findFile(basedir, [".babelrc", ".babelrc.js"])
    }

    if (configPath != null) {
      const basedir = path.dirname(configPath)
      const packagePath = findFile(basedir, ["package.json"])
      if (packagePath != null) {
        let packageJson
        try {
          packageJson = JSON.parse(String(fs.readFileSync(packagePath)))
        } catch (e) {
          // do nothing
        }
        if (packageJson != null) {
          const dependencies = {
            ...packageJson.dependencies,
            ...packageJson.devDependencies,
          }
          const babelVersion =
            dependencies["babel-core"] || dependencies["@babel/core"]
          if (babelVersion) {
            // Strep anything that's not a digit, I only need the firt number
            const major = babelVersion.replace(/\D/, "")
            if (major[0] === "6") {
              debug("Skipping Babel 6 config")
              // Do not accept a config from a Babel6 project
              configPath = null
            }
          }
        }
      }
    }

    return configPath
  }

  function loadBabelConfig(editor) {
    if (!configCache.has(editor)) {
      let babelConfig: ?Object = undefined
      const configPath = findConfigFile(editor)
      if (configPath != null) {
        debug("loadBabelConfig", configPath)
        try {
          if (path.extname(configPath) === ".js") {
            const dontCache = {}

            const requireIfTrusted = makeRequire(trusted => {
              if (trusted === false) {
                return undefined
              }
              if (trusted === true) {
                data.delete(editor)
                configCache.delete(editor)
              }

              return dontCache
            })

            // $FlowExpectError
            const cfg = requireIfTrusted(configPath)
            debug("cfg", cfg)

            babelConfig = typeof cfg === "function" ? cfg() : cfg
            if (babelConfig === dontCache) {
              return undefined
            }
          } else {
            babelConfig = JSON.parse(String(fs.readFileSync(configPath)))
          }

          if (babelConfig) {
            babelConfig.cwd = babelConfig.cwd || path.dirname(configPath)
            const babel = require("@babel/core")
            babelConfig = babel.loadOptions(babelConfig)
          }
        } catch (e) {
          debug("loadBabelConfig error", e)
        }
        debug("babel config", babelConfig)
      }
      configCache.set(editor, babelConfig)
    }
    return configCache.get(editor)
  }

  function watchEditor(editor) {
    if (!editors.has(editor)) {
      editors.set(editor, null)
      subscriptions.add(
        editor.onDidStopChanging(() => {
          data.delete(editor)
        }),
      )
    }
  }

  return {
    get(editor: TextEditor): Info {
      watchEditor(editor)
      if (!data.has(editor)) {
        data.set(editor, parseCode(editor.getText(), loadBabelConfig(editor)))
      }

      // $FlowExpectError - Flow thinks it might return null here
      return data.get(editor)
    },
  }
}
<|endoftext|>


<commit_before>var utils = require('./utils')
var oa = utils.oa
var isPromise = utils.isPromise

/**
* Create Redux middleware for handling async actions
* @arg {object} creators - A {type} -> {action creator} hash
* @return {function} - Redux middleware function
*/
module.exports = function makeMiddleware(creators) {
    return function factionServiceMiddleware(store) {
        var dispatch = store.dispatch

        // Takes an action (or array of them); verifies, and dispatches them
        function handleFollowUp(action, contextName) {
            var isMulti = Array.isArray(action)
            var actionList = (isMulti ? action : [action])
            actionList.forEach(function(act) {
                var isValid = (act && act.type && act.meta)
                if (!isValid) {
                    throw new Error(contextName + ' callback should return action(s)')
                }
                dispatch(act)
            })
        }

        return function(next) {
            return function handleMiddlewareAction(action) {
                // Don't handle non-faction actions:
                if (!action.meta || !action.meta.factionAsync) return next(action)

                // Don't re-process dispatched actions from this middleware:
                if (action.meta.isPending || action.payload) return next(action)

                var inputs = action.meta.inputs
                action.payload = action.meta._deferredHandler(inputs, store, creators)
                delete action.meta._deferredHandler

                // TODO: should flag an error here if service didn't return a Promise:
                if (!isPromise(action.payload)) return next(action)

                // Read the optional success/error callbacks from action.meta
                var _successCb = action.meta._successCb
                var _errorCb = action.meta._errorCb
                delete action.meta._successCb
                delete action.meta._errorCb

                // Dispatch a "pending" version of the action, so reducers
                // can set up any state related to loading:
                var pendingAction = oa(action, {
                    payload: null,
                    meta: oa(action.meta, { isPending: true })
                })
                dispatch(utils.addTimestamp(pendingAction))

                // When the Promise payload resolves, dispatch the success/error
                // action, and fire any follow up actions that were registered:
                action.payload.then(function(result) {
                    var successAction = oa(action, { payload: result })
                    dispatch(utils.addTimestamp(successAction))

                    if (typeof _successCb === 'function') {
                        handleFollowUp(_successCb(creators, successAction), 'chain')
                    }
                }, function(error) {
                    var errorAction = oa(action, { payload: error, error: true })
                    dispatch(utils.addTimestamp(errorAction))

                    if (typeof _errorCb === 'function') {
                        handleFollowUp(_errorCb(creators, errorAction), 'onError')
                    }
                }).catch(function(err) {
                    /* eslint-disable no-console */
                    // TODO: properly handle this (can happen if follow-up action fails)
                    console.log('Error while processing action:', err)
                    throw err
                })
            }
        }
    }
}
<commit_msg>Support conditional follow-up actions<commit_after>var utils = require('./utils')
var oa = utils.oa
var isPromise = utils.isPromise

/**
* Create Redux middleware for handling async actions
* @arg {object} creators - A {type} -> {action creator} hash
* @return {function} - Redux middleware function
*/
module.exports = function makeMiddleware(creators) {
    return function factionServiceMiddleware(store) {
        var dispatch = store.dispatch

        // Takes an action (or array of them); verifies, and dispatches them
        function handleFollowUp(action, contextName) {
            if (!action) return         // Follow up action may have been conditional
            var isMulti = Array.isArray(action)
            var actionList = (isMulti ? action : [action])
            actionList.forEach(function(act) {
                var isValid = (act && act.type && act.meta)
                if (!isValid) {
                    throw new Error(contextName + ' callback should return action(s)')
                }
                dispatch(act)
            })
        }

        return function(next) {
            return function handleMiddlewareAction(action) {
                // Don't handle non-faction actions:
                if (!action.meta || !action.meta.factionAsync) return next(action)

                // Don't re-process dispatched actions from this middleware:
                if (action.meta.isPending || action.payload) return next(action)

                var inputs = action.meta.inputs
                action.payload = action.meta._deferredHandler(inputs, store, creators)
                delete action.meta._deferredHandler

                // TODO: should flag an error here if service didn't return a Promise:
                if (!isPromise(action.payload)) return next(action)

                // Read the optional success/error callbacks from action.meta
                var _successCb = action.meta._successCb
                var _errorCb = action.meta._errorCb
                delete action.meta._successCb
                delete action.meta._errorCb

                // Dispatch a "pending" version of the action, so reducers
                // can set up any state related to loading:
                var pendingAction = oa(action, {
                    payload: null,
                    meta: oa(action.meta, { isPending: true })
                })
                dispatch(utils.addTimestamp(pendingAction))

                // When the Promise payload resolves, dispatch the success/error
                // action, and fire any follow up actions that were registered:
                action.payload.then(function(result) {
                    var successAction = oa(action, { payload: result })
                    dispatch(utils.addTimestamp(successAction))

                    if (typeof _successCb === 'function') {
                        handleFollowUp(_successCb(creators, successAction), 'chain')
                    }
                }, function(error) {
                    var errorAction = oa(action, { payload: error, error: true })
                    dispatch(utils.addTimestamp(errorAction))

                    if (typeof _errorCb === 'function') {
                        handleFollowUp(_errorCb(creators, errorAction), 'onError')
                    }
                }).catch(function(err) {
                    /* eslint-disable no-console */
                    // TODO: properly handle this (can happen if follow-up action fails)
                    console.log('Error while processing action:', err)
                    throw err
                })
            }
        }
    }
}
<|endoftext|>


<commit_before>/**
  Resource Middleware
  @description main definition of middleware
  @exports @default {function} remoteResourceMiddleware
  @exports {function} cacheLookup
  @exports {function} buildHeaders
  @exports {function} parseBody
**/

/* eslint no-unused-vars:0 */
require('core-js/library/fn/object/assign');
import fetch from 'isomorphic-fetch';
import RemoteResource from './RemoteResource';
import { APIError, CallProcessingError } from './errors';
import * as utils from './utils';

/**
  @name remoteResourceMiddleware
  @desc provides a function that can be used to configure the middleware's
    behavior, returning valid Redux middleware
  @param {object} conf
  @param {object} injectedHeaders headers to be injected on every outgoing req
  @param {object} statusActions HTTP Status codes w/ corresponding types
    or functions that will be emitted when the status code is received
  @returns {function} ReduxMiddleware
**/
export default function remoteResourceMiddleware(_conf) {
  const conf = Object.assign({
    injectedHeaders: {},
    statusActions: {}
  }, _conf || {});

  return store => next => action => {
    // if we don't find our symbol don't even bother
    if (action.hasOwnProperty && !action.hasOwnProperty(RemoteResource))
      return next(action);

    const callOpts = action[RemoteResource];

    const validationPipeline = [
      cacheLookup(store.getState(), callOpts),
      buildHeaders(store.getState(), callOpts, conf),
      parseBody(store.getState(), callOpts.body)
    ];

    return Promise.all(validationPipeline)

    // TODO: fill in w/ lifecycle method calls & HTTP request
    .then(() => console.log("We're validated!"))

    // TODO: obviously this is temporary,
    // dispatch the error lifecycle w/ the error
    .catch(error => { throw error; });
  };
}

/**
  @private
  @name cacheLookup
  @desc consults the given state cache mapping to see if we already have a value
    note that this will *not* run when using actionable HTTP methods like POST
  @param {string} method HTTP verb, used to determine if we should cache
  @param {function} cacheMapping
  @param {boolean} nocache
  @param {function} emitSuccessOnCacheHit
  @returns {Promise<boolean>} result

  @TODO allow `cacheMapping` to return a value that will be supplied to the
    success event if `emitSuccessOnCacheHit` is true after the whole thing
    short circuits
**/
export function cacheLookup(state, { method, cacheMapping, nocache }) {
  const stepName = '#cacheLookup';
  if (!utils.isCacheableRequest(method) || !cacheMapping || nocache)
    return Promise.resolve(false);

  try {
    return Promise.resolve(cacheMapping(state));
  } catch (error) {
    const msg = 'Error thrown while evaluating cache mapping';
    return Promise.reject(new CallProcessingError(msg, stepName, error));
  }
}

/**
  @private
  @name buildHeaders
  @desc builds a flat header map by evaluating each k/v in the given headers
    object, optionally injecting any headers from the top level configuration,
    will evaluate promises & functions passing in the state.
    If a header val is a function that returns false the header will be skipped
    NOTE: headers given in the action *will* overwrite globally injected headers
  @param {object} state
  @param {object} headers flat map w/ a mix of values, functions, or promises
  @param {object|string} body used to determine if we'll be sending JSON
  @param {object} injectedHeaders optional
  @returns {Promise<object|CallProcessingError}
**/
export function buildHeaders(state, { headers, body }, { injectedHeaders }) {
  const stepName = '#buildHeaders';
  let headerMap = Object.assign({}, injectedHeaders || {}, actionHeaders);

  if (
    body &&
    typeof body === 'object' &&
    !headerMap.hasOwnProperty('Content-Type')
  ) headerMap['Content-Type'] = 'application/json';

  let ps = Object.keys(headerMap).map(header => {
    const headerVal = headerMap[header];

    if (typeof headerVal === 'function')
      try {
        return { key: header, value: headerVal(state) };
      } catch (error) {
        const msg = `Error thrown when evaluating header function ${header}`;
        return Promise.reject(new CallProcessingError(msg, stepName, error));
      }
    else if (utils.isPromise(headerVal))
      return headerVal.then(v => ({ key: header, value: v }));
    // if you're not a promise but still an object we don't want you
    else if (typeof headerVal === 'object')
      return Promise.reject(
        new CallProcessingError(`Object given for header ${header}`, stepName)
      );
    // I guess it's just a plain value
    else return { key: header, value: headerVal };
  });

  return Promise.all(ps).then(p => p
    .filter(pair => pair.value !== false)
    .reduce((acc, pair) => { acc[pair.key] = pair.value; return acc; }, {})
  );
}

/**
  @private
  @name parseBody
  @desc detects if the request body is an object, if so will attempt to parse
    it as JSON, otherwise just returning it; if body is a function it will be
    called with the state passed in - supports promises
  @param {object} state
  @param {any} body
  @returns {Promise<string>} parsedBody
**/
export function parseBody(state, body) {
  const stepName = '#parseBody';
  if (utils.isPromise(body))
    return body.then(realBody => parseBody(realBody));
  else if (typeof body === 'object')
    try {
      // TODO: can this even fail?
      return Promise.resolve(JSON.stringify(body));
    } catch (error) {
      return Promise.reject(new CallProcessingError(
        'Error parsing body', stepName, error
      ));
    }
  else if (typeof body === 'function')
    try {
      return Promise.resolve(body(state));
    } catch (error) {
      return Promise.reject(new CallProcessingError(
        'Error occurred while evaluating body function', stepName, error
      ));
    }
}
<commit_msg>Provide else clause to parseBody<commit_after>/**
  Resource Middleware
  @description main definition of middleware
  @exports @default {function} remoteResourceMiddleware
  @exports {function} cacheLookup
  @exports {function} buildHeaders
  @exports {function} parseBody
**/

/* eslint no-unused-vars:0 */
require('core-js/library/fn/object/assign');
import fetch from 'isomorphic-fetch';
import RemoteResource from './RemoteResource';
import { APIError, CallProcessingError } from './errors';
import * as utils from './utils';

/**
  @name remoteResourceMiddleware
  @desc provides a function that can be used to configure the middleware's
    behavior, returning valid Redux middleware
  @param {object} conf
  @param {object} injectedHeaders headers to be injected on every outgoing req
  @param {object} statusActions HTTP Status codes w/ corresponding types
    or functions that will be emitted when the status code is received
  @returns {function} ReduxMiddleware
**/
export default function remoteResourceMiddleware(_conf) {
  const conf = Object.assign({
    injectedHeaders: {},
    statusActions: {}
  }, _conf || {});

  return store => next => action => {
    // if we don't find our symbol don't even bother
    if (action.hasOwnProperty && !action.hasOwnProperty(RemoteResource))
      return next(action);

    const callOpts = action[RemoteResource];

    const validationPipeline = [
      cacheLookup(store.getState(), callOpts),
      buildHeaders(store.getState(), callOpts, conf),
      parseBody(store.getState(), callOpts.body)
    ];

    return Promise.all(validationPipeline)

    // TODO: fill in w/ lifecycle method calls & HTTP request
    .then(() => console.log("We're validated!"))

    // TODO: obviously this is temporary,
    // dispatch the error lifecycle w/ the error
    .catch(error => { throw error; });
  };
}

/**
  @private
  @name cacheLookup
  @desc consults the given state cache mapping to see if we already have a value
    note that this will *not* run when using actionable HTTP methods like POST
  @param {string} method HTTP verb, used to determine if we should cache
  @param {function} cacheMapping
  @param {boolean} nocache
  @param {function} emitSuccessOnCacheHit
  @returns {Promise<boolean>} result

  @TODO allow `cacheMapping` to return a value that will be supplied to the
    success event if `emitSuccessOnCacheHit` is true after the whole thing
    short circuits
**/
export function cacheLookup(state, { method, cacheMapping, nocache }) {
  const stepName = '#cacheLookup';
  if (!utils.isCacheableRequest(method) || !cacheMapping || nocache)
    return Promise.resolve(false);

  try {
    return Promise.resolve(cacheMapping(state));
  } catch (error) {
    const msg = 'Error thrown while evaluating cache mapping';
    return Promise.reject(new CallProcessingError(msg, stepName, error));
  }
}

/**
  @private
  @name buildHeaders
  @desc builds a flat header map by evaluating each k/v in the given headers
    object, optionally injecting any headers from the top level configuration,
    will evaluate promises & functions passing in the state.
    If a header val is a function that returns false the header will be skipped
    NOTE: headers given in the action *will* overwrite globally injected headers
  @param {object} state
  @param {object} headers flat map w/ a mix of values, functions, or promises
  @param {object|string} body used to determine if we'll be sending JSON
  @param {object} injectedHeaders optional
  @returns {Promise<object|CallProcessingError}
**/
export function buildHeaders(state, { headers, body }, { injectedHeaders }) {
  const stepName = '#buildHeaders';
  let headerMap = Object.assign({}, injectedHeaders || {}, actionHeaders);

  if (
    body &&
    typeof body === 'object' &&
    !headerMap.hasOwnProperty('Content-Type')
  ) headerMap['Content-Type'] = 'application/json';

  let ps = Object.keys(headerMap).map(header => {
    const headerVal = headerMap[header];

    if (typeof headerVal === 'function')
      try {
        return { key: header, value: headerVal(state) };
      } catch (error) {
        const msg = `Error thrown when evaluating header function ${header}`;
        return Promise.reject(new CallProcessingError(msg, stepName, error));
      }
    else if (utils.isPromise(headerVal))
      return headerVal.then(v => ({ key: header, value: v }));
    // if you're not a promise but still an object we don't want you
    else if (typeof headerVal === 'object')
      return Promise.reject(
        new CallProcessingError(`Object given for header ${header}`, stepName)
      );
    // I guess it's just a plain value
    else return { key: header, value: headerVal };
  });

  return Promise.all(ps).then(p => p
    .filter(pair => pair.value !== false)
    .reduce((acc, pair) => { acc[pair.key] = pair.value; return acc; }, {})
  );
}

/**
  @private
  @name parseBody
  @desc detects if the request body is an object, if so will attempt to parse
    it as JSON, otherwise just returning it; if body is a function it will be
    called with the state passed in - supports promises
  @param {object} state
  @param {any} body
  @returns {Promise<string>} parsedBody
**/
export function parseBody(state, body) {
  const stepName = '#parseBody';
  if (utils.isPromise(body))
    return body.then(realBody => parseBody(realBody));
  else if (typeof body === 'object')
    try {
      // TODO: can this even fail?
      return Promise.resolve(JSON.stringify(body));
    } catch (error) {
      return Promise.reject(new CallProcessingError(
        'Error parsing body', stepName, error
      ));
    }
  else if (typeof body === 'function')
    try {
      return Promise.resolve(body(state));
    } catch (error) {
      return Promise.reject(new CallProcessingError(
        'Error occurred while evaluating body function', stepName, error
      ));
    }
  else return Promise.resolve(body);
}
<|endoftext|>


<commit_before>'use strict';
var utils   = require('./utils'),
    errors  = require('./errors'),
    connect = require('express/node_modules/connect'),
    models  = require('./models'),
    config  = require('./config'),
    features = require('./features'),
    parse = require('url').parse;

// Custom middleware used by the application.
module.exports = {
  // Transparently handle JSONP. Although this does require wrapping the
  // Request#send() method which is a bit nasty. Other alternative is to
  // always pass the response on and include this middleware after the
  // routes.
  jsonp: function () {
    return function (req, res, next) {
      var _send = res.send;
      res.send = function (body) {
        var callback = req.param('callback'),
            isJSONP  = res.get('Content-Type') === 'application/json' && callback;

        if (body && req.method !== 'HEAD' && isJSONP) {
          res.contentType('js');
          body = callback + '(' + body.toString().trim() + ');';
        }
        _send.call(this, body);
      };
      next();
    };
  },
  // attach the requesting IP
  // Check for ajax requests and set the Request/Response#ajax flag to true.
  ajax: function () {
    return function (req, res, next) {
      req.ajax = res.ajax = utils.isAjax(req);

      // if ((req.get('X-Stream') || '').toLowerCase() === 'true') {
      //   req.ajax = false;
      // }

      next();
    };
  },
  // Add relevant CORS headers for the application.
  cors: function () {
    return function (req, res, next) {
      var headers = req.header('Access-Control-Request-Headers');
      var origin = req.header('Origin');

      // TODO should this check if the request is via the API?
      if (req.method === 'OPTIONS' || (req.method === 'GET' && utils.isAjax(req)) || (req.method === 'GET' && req.headers.origin)) {
        res.header({
          'Access-Control-Allow-Origin':  origin,
          'Access-Control-Allow-Headers': headers
        });
        req.cors = true;
      }

      if (req.method === 'OPTIONS') {
        res.send(204);
      } else {
        next();
      }
    };
  },
  flash: function () {
    function flash(key, message) {
      // `this` refers to the request object
      // jshint validthis:true
      var cache = this.flashCache = this.flashCache || {},
          value;

      // We store the flash on the session whenever we can
      // However, if we send a flash message before sessions exist
      // i.e in run.js, we store on the req object.
      // the cache object starts on the request, and if it is empty
      // and a session exists we then move it to teh session.
      if (Object.keys(cache).length === 0 && this.session) {
        if (!this.session.flashCache) {
          this.session.flashCache = {};
        }
        cache = this.session.flashCache;
      }

      if (arguments.length === 2) {
        cache[key] = message;
        return this;
      } else if (arguments.length === 1) {
        value = cache[key];
        delete cache[key];
        return value;
      }

      this.flashCache = this.session.flashCache = {};
      return cache;
    }

    return function (req, res, next) {
      req.flash = res.flash = flash.bind(req);

      req.flash.INFO = 'info';
      req.flash.ERROR = 'error';
      req.flash.NOTIFICATION = 'notification';

      next();
    };
  },
  // Redirect urls with trailing slashes to their non-trailing counterpart,
  // requires the "strict routing" setting to be false.
  noslashes: function () {
    return function (req, res, next) {
      if (req.path !== '/' && req.originalUrl.slice(-1) === '/') {
        return res.redirect(301, req.originalUrl.slice(0, -1));
      }
      next();
    };
  },
  // monkey patch for express' own csrf method, so we can ignore specific url patterns
  csrf: function (options) {
    var ignore = options.ignore || [],
        csrf = connect.csrf(options),
        always = {OPTIONS: 1, GET: 1, HEAD: 1};

    return function (req, res, next) {
      if (always[req.method]) {
        return csrf(req, res, next);
      } else {
        var url = parse(req.url);
        var skipCSRF = false;
        ignore.forEach(function(matcher) {
          if (typeof matcher === 'string') {
            if (matcher === url.pathname) {
              skipCSRF = true;
            }
          } else {
            // regular expression matcher
            if (url.pathname.match(matcher)) {
              skipCSRF = true;
            }
          }
        });

        if (skipCSRF) {
          next();
        } else {
          return csrf(req, res, next);
        }
      }
    };
  },
  // Checks for a subdomain in the current url, if found it sets the
  // req.subdomain property. This supports existing behaviour that allows
  // subdomains to load custom config files.
  subdomain: function (app) {
    return function (req, res, next) {
      var apphost = app.set('url host').split(':')[0],
          host = req.header('Host', ''),
          offset = host.indexOf(apphost);

      if (offset) {
        // Slice the host from the subdomain and subtract 1 for
        // trailing . on the subdomain.
        req.subdomain = host.slice(0, offset - 1);
      }
      next();
    };
  },

  // Limit the file size that can be uploaded.
  limitContentLength: function (options) {
    // Parse a string representing a file size and convert it into bytes.
    // A number on it's own will be assumed to be bytes. A multiple such as
    // "k" or "m" can be appended to the string to handle larger numbers. This
    // is case insensitive and uses powers of 1024 rather than (1000).
    // So both 1kB and 1kb == 1024.
    function parseLimit(string) {
      var matches = ('' + string).toLowerCase().match(regexp),
          bytes = null, power;

      if (matches) {
        bytes = parseFloat(matches[1]);
        power = powers[matches[2]];

        if (bytes && power) {
          bytes = Math.pow(bytes * 1024, power);
        }
      }

      return bytes || null;
    }

    var powers = { k: 1, m: 2, g: 3, t: 4 },
        regexp = /^(\d+(?:.\d+)?)\s*([kmgt]?)b?$/,
        limit  = options && parseLimit(options.limit);

    return function (req, res, next) {
      if (limit) {
        var contentLength = parseInt(req.header('Content-Length', 0), 10),
            message = 'Sorry, the content you have uploaded is larger than JS Bin can handle. Max size is ' + options.limit;

        if (limit && contentLength > limit) {
          return next(new errors.RequestEntityTooLarge(message));
        }
      }
      next();
    };
  },

  // detect if this is an API request and add flag isApi to the request object
  api: function(options) {
    return function (req, res, next) {
      var apiKey,
          userModel = models.user;

      if (req.url.indexOf('/api') === 0) {
        req.isApi = true;

        // Make the API requests stateless by removin the cookie set by middleware cookieSession
        res.on('header', function() {
          res.removeHeader('Set-Cookie');
        });

        if (config.api.requireSSL) {
          if (!req.secure && (String(req.headers['x-forwarded-proto']).toLowerCase() !== "https") ) {
            res.status(403); // forbidden
            res.json({ error: 'All API requests must be made over SSL/TLS' });
            return;
          }
        }

        if (req.query.api_key) {
          req.apiKey = req.query.api_key;
        } else if (req.headers.authorization) {
          req.apiKey = req.headers.authorization.replace(/token\s/,'');
        }

        var validateApiRequest = function() {
          if (config.api.allowAnonymousReadWrite || (config.api.allowAnonymousRead && req.method === 'GET')) {
            next();
          } else {
            if (!req.apiKey) {
              res.status(403); // forbidden
              res.json({ error: 'You need to provide a valid API key when using this API' });
            } else {
              next();
            }
          }
        };

        if (req.apiKey) {
          userModel.loadByApiKey(req.apiKey, function (err, user) {
            if (err) {
              return next(err);
            }
            if (user) {
              req.session.user = user;
              validateApiRequest();
            } else {
              res.status(403); // forbidden
              res.json({ error: 'The API key you provided is not valid' });
            }
          });
        } else {
          validateApiRequest();
        }
      } else {
        next();
      }
    };
  },
  protocolCheck: function(req, res, next) {
    // this is a bit of a hack, because the protocolCheck should be supported
    // no matter what, but since we're using feature flags, this gets very
    // messy when it goes live without the feature flag test in place.
    if (!features('sslLogin', req)) {
      return next();
    }
    var pathname = parse(req.url).pathname;

    if (!config.url.ssl) {
      if (req.secure) {
        return res.redirect('http://' + req.headers.host.replace(/:.*/, '') + req.url);
      }
    } else {
      if (!req.secure) {
        if (config.url.ssl.paths.indexOf(pathname) > -1) {
          return res.redirect('https://' + req.headers.host.replace(/:.*/, '') + req.url);
        }
      } else {
        if (config.url.ssl.paths.indexOf(pathname) === -1) {
          return res.redirect('http://' + req.headers.host.replace(/:.*/, '') + req.url);
        }
      }
    }
    next();
  },
  notices: function () {
    var notices = require('../public/notices');
    var version = require('../package').version;
    var versionTest = require('./session-version');

    return function (req, res, next) {
      var cookieVersion = versionTest(req);

      var matches = notices.filter(function (notice) {
        return cookieVersion.lt(notice.version);
      });

      /* TODO: return latest error messages as the priority */
      // if (matches.length > 1) {
      //   // find the latest error
      //   var errors = matches.filter(function (m) {
      //     return m.type === req.flash.ERROR;
      //   });

      //   if (errors.length) {
      //     // TODO
      //   }
      // }

      req.session.version = version;

      // TODO make this support multiple messages...one day
      if (matches.length) {
        var match = matches[0];
        // we only bump their version
        req.flash(match.type || req.flash.NOTIFICATION, match.message);
      }

      next();
    };
  },
};
<commit_msg>Use !== rather than > in condition<commit_after>'use strict';
var utils   = require('./utils'),
    errors  = require('./errors'),
    connect = require('express/node_modules/connect'),
    models  = require('./models'),
    config  = require('./config'),
    features = require('./features'),
    parse = require('url').parse;

// Custom middleware used by the application.
module.exports = {
  // Transparently handle JSONP. Although this does require wrapping the
  // Request#send() method which is a bit nasty. Other alternative is to
  // always pass the response on and include this middleware after the
  // routes.
  jsonp: function () {
    return function (req, res, next) {
      var _send = res.send;
      res.send = function (body) {
        var callback = req.param('callback'),
            isJSONP  = res.get('Content-Type') === 'application/json' && callback;

        if (body && req.method !== 'HEAD' && isJSONP) {
          res.contentType('js');
          body = callback + '(' + body.toString().trim() + ');';
        }
        _send.call(this, body);
      };
      next();
    };
  },
  // attach the requesting IP
  // Check for ajax requests and set the Request/Response#ajax flag to true.
  ajax: function () {
    return function (req, res, next) {
      req.ajax = res.ajax = utils.isAjax(req);

      // if ((req.get('X-Stream') || '').toLowerCase() === 'true') {
      //   req.ajax = false;
      // }

      next();
    };
  },
  // Add relevant CORS headers for the application.
  cors: function () {
    return function (req, res, next) {
      var headers = req.header('Access-Control-Request-Headers');
      var origin = req.header('Origin');

      // TODO should this check if the request is via the API?
      if (req.method === 'OPTIONS' || (req.method === 'GET' && utils.isAjax(req)) || (req.method === 'GET' && req.headers.origin)) {
        res.header({
          'Access-Control-Allow-Origin':  origin,
          'Access-Control-Allow-Headers': headers
        });
        req.cors = true;
      }

      if (req.method === 'OPTIONS') {
        res.send(204);
      } else {
        next();
      }
    };
  },
  flash: function () {
    function flash(key, message) {
      // `this` refers to the request object
      // jshint validthis:true
      var cache = this.flashCache = this.flashCache || {},
          value;

      // We store the flash on the session whenever we can
      // However, if we send a flash message before sessions exist
      // i.e in run.js, we store on the req object.
      // the cache object starts on the request, and if it is empty
      // and a session exists we then move it to teh session.
      if (Object.keys(cache).length === 0 && this.session) {
        if (!this.session.flashCache) {
          this.session.flashCache = {};
        }
        cache = this.session.flashCache;
      }

      if (arguments.length === 2) {
        cache[key] = message;
        return this;
      } else if (arguments.length === 1) {
        value = cache[key];
        delete cache[key];
        return value;
      }

      this.flashCache = this.session.flashCache = {};
      return cache;
    }

    return function (req, res, next) {
      req.flash = res.flash = flash.bind(req);

      req.flash.INFO = 'info';
      req.flash.ERROR = 'error';
      req.flash.NOTIFICATION = 'notification';

      next();
    };
  },
  // Redirect urls with trailing slashes to their non-trailing counterpart,
  // requires the "strict routing" setting to be false.
  noslashes: function () {
    return function (req, res, next) {
      if (req.path !== '/' && req.originalUrl.slice(-1) === '/') {
        return res.redirect(301, req.originalUrl.slice(0, -1));
      }
      next();
    };
  },
  // monkey patch for express' own csrf method, so we can ignore specific url patterns
  csrf: function (options) {
    var ignore = options.ignore || [],
        csrf = connect.csrf(options),
        always = {OPTIONS: 1, GET: 1, HEAD: 1};

    return function (req, res, next) {
      if (always[req.method]) {
        return csrf(req, res, next);
      } else {
        var url = parse(req.url);
        var skipCSRF = false;
        ignore.forEach(function(matcher) {
          if (typeof matcher === 'string') {
            if (matcher === url.pathname) {
              skipCSRF = true;
            }
          } else {
            // regular expression matcher
            if (url.pathname.match(matcher)) {
              skipCSRF = true;
            }
          }
        });

        if (skipCSRF) {
          next();
        } else {
          return csrf(req, res, next);
        }
      }
    };
  },
  // Checks for a subdomain in the current url, if found it sets the
  // req.subdomain property. This supports existing behaviour that allows
  // subdomains to load custom config files.
  subdomain: function (app) {
    return function (req, res, next) {
      var apphost = app.set('url host').split(':')[0],
          host = req.header('Host', ''),
          offset = host.indexOf(apphost);

      if (offset) {
        // Slice the host from the subdomain and subtract 1 for
        // trailing . on the subdomain.
        req.subdomain = host.slice(0, offset - 1);
      }
      next();
    };
  },

  // Limit the file size that can be uploaded.
  limitContentLength: function (options) {
    // Parse a string representing a file size and convert it into bytes.
    // A number on it's own will be assumed to be bytes. A multiple such as
    // "k" or "m" can be appended to the string to handle larger numbers. This
    // is case insensitive and uses powers of 1024 rather than (1000).
    // So both 1kB and 1kb == 1024.
    function parseLimit(string) {
      var matches = ('' + string).toLowerCase().match(regexp),
          bytes = null, power;

      if (matches) {
        bytes = parseFloat(matches[1]);
        power = powers[matches[2]];

        if (bytes && power) {
          bytes = Math.pow(bytes * 1024, power);
        }
      }

      return bytes || null;
    }

    var powers = { k: 1, m: 2, g: 3, t: 4 },
        regexp = /^(\d+(?:.\d+)?)\s*([kmgt]?)b?$/,
        limit  = options && parseLimit(options.limit);

    return function (req, res, next) {
      if (limit) {
        var contentLength = parseInt(req.header('Content-Length', 0), 10),
            message = 'Sorry, the content you have uploaded is larger than JS Bin can handle. Max size is ' + options.limit;

        if (limit && contentLength > limit) {
          return next(new errors.RequestEntityTooLarge(message));
        }
      }
      next();
    };
  },

  // detect if this is an API request and add flag isApi to the request object
  api: function(options) {
    return function (req, res, next) {
      var apiKey,
          userModel = models.user;

      if (req.url.indexOf('/api') === 0) {
        req.isApi = true;

        // Make the API requests stateless by removin the cookie set by middleware cookieSession
        res.on('header', function() {
          res.removeHeader('Set-Cookie');
        });

        if (config.api.requireSSL) {
          if (!req.secure && (String(req.headers['x-forwarded-proto']).toLowerCase() !== "https") ) {
            res.status(403); // forbidden
            res.json({ error: 'All API requests must be made over SSL/TLS' });
            return;
          }
        }

        if (req.query.api_key) {
          req.apiKey = req.query.api_key;
        } else if (req.headers.authorization) {
          req.apiKey = req.headers.authorization.replace(/token\s/,'');
        }

        var validateApiRequest = function() {
          if (config.api.allowAnonymousReadWrite || (config.api.allowAnonymousRead && req.method === 'GET')) {
            next();
          } else {
            if (!req.apiKey) {
              res.status(403); // forbidden
              res.json({ error: 'You need to provide a valid API key when using this API' });
            } else {
              next();
            }
          }
        };

        if (req.apiKey) {
          userModel.loadByApiKey(req.apiKey, function (err, user) {
            if (err) {
              return next(err);
            }
            if (user) {
              req.session.user = user;
              validateApiRequest();
            } else {
              res.status(403); // forbidden
              res.json({ error: 'The API key you provided is not valid' });
            }
          });
        } else {
          validateApiRequest();
        }
      } else {
        next();
      }
    };
  },
  protocolCheck: function(req, res, next) {
    // this is a bit of a hack, because the protocolCheck should be supported
    // no matter what, but since we're using feature flags, this gets very
    // messy when it goes live without the feature flag test in place.
    if (!features('sslLogin', req)) {
      return next();
    }
    var pathname = parse(req.url).pathname;

    if (!config.url.ssl) {
      if (req.secure) {
        return res.redirect('http://' + req.headers.host.replace(/:.*/, '') + req.url);
      }
    } else {
      if (!req.secure) {
        if (config.url.ssl.paths.indexOf(pathname) !== -1) {
          return res.redirect('https://' + req.headers.host.replace(/:.*/, '') + req.url);
        }
      } else {
        if (config.url.ssl.paths.indexOf(pathname) === -1) {
          return res.redirect('http://' + req.headers.host.replace(/:.*/, '') + req.url);
        }
      }
    }
    next();
  },
  notices: function () {
    var notices = require('../public/notices');
    var version = require('../package').version;
    var versionTest = require('./session-version');

    return function (req, res, next) {
      var cookieVersion = versionTest(req);

      var matches = notices.filter(function (notice) {
        return cookieVersion.lt(notice.version);
      });

      /* TODO: return latest error messages as the priority */
      // if (matches.length > 1) {
      //   // find the latest error
      //   var errors = matches.filter(function (m) {
      //     return m.type === req.flash.ERROR;
      //   });

      //   if (errors.length) {
      //     // TODO
      //   }
      // }

      req.session.version = version;

      // TODO make this support multiple messages...one day
      if (matches.length) {
        var match = matches[0];
        // we only bump their version
        req.flash(match.type || req.flash.NOTIFICATION, match.message);
      }

      next();
    };
  },
};
<|endoftext|>


<commit_before>var stream = require('stream');
var inherits = require('util').inherits;

function MockSTDIN(restoreTarget) {
  stream.Readable.call(this, {
    highWaterMark: 0,
    readable: true,
    writable: false
  });
  Object.defineProperties(this, {
    target: {
      enumerable: false,
      writable: false,
      configurable: false,
      value: restoreTarget
    },
    isMock: {
      enumerable: false,
      writable: false,
      configurable: false,
      value: true
    },
    _mockData: {
      enumerable: false,
      writable: false,
      configurable: false,
      value: []
    },
    _flags: {
      enumerable: false,
      writable: false,
      configurable: false,
      value: {
        emittedData: false,
        lastChunk: null
      }
    }
  });
}

inherits(MockSTDIN, stream.Readable);

function MockData(chunk) {
  Object.defineProperties(this, {
    data: {
      value: chunk,
      writable: false,
      configurable: false,
      enumerable: false
    },
    length: {
      get: function() {
        if (Buffer.isBuffer(chunk)) {
          return chunk.length;
        } else if (typeof chunk === 'string') {
          return chunk.length;
        }
        return 0;
      },
      configurable: false,
      enumerable: false
    },
    pos: {
      writable: true,
      value: 0,
      configurable: false,
      enumerable: false
    },
    done: {
      writable: true,
      value: false,
      configurable: false,
      enumerable: false
    }
  });
}

MockData.prototype.chunk = function(length) {
  if (this.pos <= this.length) {
    if (Buffer.isBuffer(this.data) || typeof this.data === 'string') {
      var value = this.data.slice(this.pos, this.pos + length);
      this.pos += length;
      if (this.pos >= this.length) {
        this.done = true;
      }
      return value;
    }
  }

  this.done = true;
  return null;
}

var Readable$emit = stream.Readable.prototype.emit;
MockSTDIN.prototype.emit = function MockSTDINEmit(name) {
  if (name === 'data') {
    this._flags.emittedData = true;
    this._flags.lastChunk = null;
  }
  return Readable$emit.apply(this, arguments);
};

MockSTDIN.prototype.send = function MockSTDINWrite(text) {
  if (Array.isArray(text)) text = text.join('\n');
  if (Buffer.isBuffer(text) || typeof text === 'string' || text === null) {
    var data = new MockData(text);
    this._mockData.push(data);
    this._read();
    if (!this._flags.emittedData && this._flags.lastChunk) {
      this.emit('data', this._flags.lastChunk);
      this._flags.emittedData = false;
    }
    if (text === null) {
      // Trigger an end event synchronously...
      endReadable(this);
    }
  }
  return this;
};

MockSTDIN.prototype.end = function MockSTDINEnd() {
  this.send(null);
  return this;
};

MockSTDIN.prototype.restore = function MockSTDINRestore() {
  Object.defineProperty(process, 'stdin', {
    value: this.target,
    configurable: true,
    writable: false
  });
  return this;
};

MockSTDIN.prototype.reset = function MockSTDINReset() {
  var state = this._readableState;
  state.ended = false;
  return this;
};

MockSTDIN.prototype._read = function MockSTDINRead(size) {
  if (size === void 0) size = Infinity;
  var count = 0;
  var read = true;
  while (read && this._mockData.length && count < size) {
    var item = this._mockData[0];
    var leftInChunk = item.length - item.pos;
    var remaining = size === Infinity ? leftInChunk : size - count;
    var toProcess = Math.min(leftInChunk, remaining);

    if (!this.push(this._flags.lastChunk = item.chunk(toProcess))) {
      read = false;
    }

    if (item.done) {
      this._mockData.shift();
    }

    count += toProcess;
  }
};

function endReadable(stream) {
  // Synchronously emit an end event, if possible.
  var state = stream._readableState;

  if (!state.length) {
    state.ended = true;
    state.endEmitted = true;
    stream.readable = false;
    stream.emit('end');
  }
}

function mock() {
  var mock = new MockSTDIN(process.stdin);
  Object.defineProperty(process, 'stdin', {
    value: mock,
    configurable: true,
    writable: false
  });
  return mock;
}

mock.Class = MockSTDIN;

module.exports = mock;
<commit_msg>fix(MockSTDIN): emit synchronous data better<commit_after>var stream = require('stream');
var inherits = require('util').inherits;

function MockSTDIN(restoreTarget) {
  stream.Readable.call(this, {
    highWaterMark: 0,
    readable: true,
    writable: false
  });
  Object.defineProperties(this, {
    target: {
      enumerable: false,
      writable: false,
      configurable: false,
      value: restoreTarget
    },
    isMock: {
      enumerable: false,
      writable: false,
      configurable: false,
      value: true
    },
    _mockData: {
      enumerable: false,
      writable: false,
      configurable: false,
      value: []
    },
    _flags: {
      enumerable: false,
      writable: false,
      configurable: false,
      value: {
        emittedData: false,
        lastChunk: null
      }
    }
  });
}

inherits(MockSTDIN, stream.Readable);

function MockData(chunk) {
  Object.defineProperties(this, {
    data: {
      value: chunk,
      writable: false,
      configurable: false,
      enumerable: false
    },
    length: {
      get: function() {
        if (Buffer.isBuffer(chunk)) {
          return chunk.length;
        } else if (typeof chunk === 'string') {
          return chunk.length;
        }
        return 0;
      },
      configurable: false,
      enumerable: false
    },
    pos: {
      writable: true,
      value: 0,
      configurable: false,
      enumerable: false
    },
    done: {
      writable: true,
      value: false,
      configurable: false,
      enumerable: false
    }
  });
}

MockData.prototype.chunk = function(length) {
  if (this.pos <= this.length) {
    if (Buffer.isBuffer(this.data) || typeof this.data === 'string') {
      var value = this.data.slice(this.pos, this.pos + length);
      this.pos += length;
      if (this.pos >= this.length) {
        this.done = true;
      }
      return value;
    }
  }

  this.done = true;
  return null;
}

var Readable$emit = stream.Readable.prototype.emit;
MockSTDIN.prototype.emit = function MockSTDINEmit(name) {
  if (name === 'data') {
    this._flags.emittedData = true;
    this._flags.lastChunk = null;
  }
  return Readable$emit.apply(this, arguments);
};

MockSTDIN.prototype.send = function MockSTDINWrite(text) {
  if (Array.isArray(text)) text = text.join('\n');
  if (Buffer.isBuffer(text) || typeof text === 'string' || text === null) {
    var data = new MockData(text);
    this._mockData.push(data);
    this._read();
    if (!this._flags.emittedData && this._readableState.length) {
      drainData(this);
    }
    if (text === null) {
      // Trigger an end event synchronously...
      endReadable(this);
    }
  }
  return this;
};

MockSTDIN.prototype.end = function MockSTDINEnd() {
  this.send(null);
  return this;
};

MockSTDIN.prototype.restore = function MockSTDINRestore() {
  Object.defineProperty(process, 'stdin', {
    value: this.target,
    configurable: true,
    writable: false
  });
  return this;
};

MockSTDIN.prototype.reset = function MockSTDINReset() {
  var state = this._readableState;
  state.ended = false;
  return this;
};

MockSTDIN.prototype._read = function MockSTDINRead(size) {
  if (size === void 0) size = Infinity;
  var count = 0;
  var read = true;
  while (read && this._mockData.length && count < size) {
    var item = this._mockData[0];
    var leftInChunk = item.length - item.pos;
    var remaining = size === Infinity ? leftInChunk : size - count;
    var toProcess = Math.min(leftInChunk, remaining);

    if (!this.push(this._flags.lastChunk = item.chunk(toProcess))) {
      read = false;
    }

    if (item.done) {
      this._mockData.shift();
    }

    count += toProcess;
  }
};

function endReadable(stream) {
  // Synchronously emit an end event, if possible.
  var state = stream._readableState;

  if (!state.length) {
    state.ended = true;
    state.endEmitted = true;
    stream.readable = false;
    stream.emit('end');
  }
}

function drainData(stream) {
  var state = stream._readableState;
  var buffer = state.buffer;
  while (buffer.length) {
    var chunk = buffer.shift();
    if (chunk !== null) {
      state.length -= chunk.length;
      stream.emit('data', chunk);
      stream._flags.emittedData = false;
    }
  }
}

function mock() {
  var mock = new MockSTDIN(process.stdin);
  Object.defineProperty(process, 'stdin', {
    value: mock,
    configurable: true,
    writable: false
  });
  return mock;
}

mock.Class = MockSTDIN;

module.exports = mock;
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2013 Vanderbilt University, All rights reserved.
 *
 * Author: Robert Kereskenyi
 */

"use strict";

define(['js/DragDrop/DragSource',
    'js/DragDrop/DragHelper',
    './DiagramDesignerWidget.Constants'], function (dragSource,
                                                    dragHelper,
                                                    DiagramDesignerWidgetConstants) {

    var DiagramDesignerWidgetDraggable,
        DRAG_HELPER_CLASS = 'diagram-designer-drag-outline';

    DiagramDesignerWidgetDraggable = function () {
    };

    DiagramDesignerWidgetDraggable.prototype._makeDraggable = function (item) {
        var self = this;

        dragSource.makeDraggable(item.$el, {
            'helper': function (event, dragInfo) {
                return self._dragHelper(this, event, dragInfo);
            },
            'dragItems': function (el) {
                return self.getDragItems(self.selectionManager.getSelectedElements());
            },
            'dragEffects': function (el, event) {
                return self.getDragEffects(self.selectionManager.getSelectedElements(), event);
            },
            'dragParams': function (el, event) {
                return self.getDragParams(self.selectionManager.getSelectedElements(), event);
            },
            'start': function (event) {
                var ret = false;
                //enable drag mode only in
                //- DESIGN MODE
                //- if the mousedown is not on a connection drawing start point
                if (self.mode === self.OPERATING_MODES.DESIGN) {
                    //we need to check if the target element is SVGElement or not
                    //because jQuery does not work well on SVGElements
                    if (event.originalEvent.target instanceof SVGElement) {
                        ret = event.originalEvent.target.getAttribute("class").split(' ').indexOf(DiagramDesignerWidgetConstants.CONNECTOR_CLASS) === -1;
                    } else {
                        ret = !$(event.originalEvent.target).hasClass(DiagramDesignerWidgetConstants.CONNECTOR_CLASS);
                    }
                }
                return ret;
            }
        });
    };

    DiagramDesignerWidgetDraggable.prototype._destroyDraggable = function (item) {
        dragSource.destroyDraggable(item.$el);
    };

    /* OVERWRITE DragSource.prototype.dragHelper */
    DiagramDesignerWidgetDraggable.prototype._dragHelper = function (el, event, dragInfo) {
        var helperEl = $('
', {'class': DRAG_HELPER_CLASS}),
            selectionBBox = this.selectionManager._getSelectionBoundingBox(),
            mousePos = this.getAdjustedMousePos(event),
            dragEffects = dragHelper.getDragEffects(dragInfo);

        this.logger.debug("_dragHelper's dragInfo: " + JSON.stringify(dragInfo));

        if (selectionBBox) {
            helperEl.css({'width': (selectionBBox.x2 - selectionBBox.x) * this._zoomRatio,
                'height': (selectionBBox.y2 - selectionBBox.y) * this._zoomRatio,
                'line-height': ((selectionBBox.y2 - selectionBBox.y) * this._zoomRatio) + "px",
                'text-align': 'center',
                'border': '2px dashed #666',
                'background-color': 'rgba(100, 100, 100, 0.1)',
                'margin-top': (selectionBBox.y - mousePos.mY + dragSource.DEFAULT_CURSOR_AT.top) * this._zoomRatio,
                'margin-left': (selectionBBox.x - mousePos.mX + dragSource.DEFAULT_CURSOR_AT.left) * this._zoomRatio});

            if (dragEffects.length === 1) {
                if (dragEffects[0] === dragSource.DRAG_EFFECTS.DRAG_MOVE) {
                    helperEl.append($('')).append(' Move...');
                } else if (dragEffects[0] === dragSource.DRAG_EFFECTS.DRAG_COPY) {
                    helperEl.append($('')).append(' Copy...');
                }
            }
        }

        return helperEl;
    };

    DiagramDesignerWidgetDraggable.prototype.getDragItems = function (selectedElements) {
        this.logger.warning("DiagramDesignerWidgetDraggable.getDragItems is not overridden in the controller!!! selectedElements: " + selectedElements);
        return [];
    };

    DiagramDesignerWidgetDraggable.prototype.getDragEffects = function (selectedElements, event) {
        var ctrlKey = event.ctrlKey || event.metaKey,
            effects = [dragSource.DRAG_EFFECTS.DRAG_MOVE];

        //by default the drag is a MOVE

        //CTRL key --> copy
        if (ctrlKey) {
            effects = [dragSource.DRAG_EFFECTS.DRAG_COPY];
        }

        return effects;
    };

    DiagramDesignerWidgetDraggable.prototype.getDragParams = function (selectedElements, event) {
        var params = { 'positions': {}},
            i = selectedElements.length,
            itemID,
            selectionBBox = this.selectionManager._getSelectionBoundingBox(),
            mousePos = this.getAdjustedMousePos(event);

        while (i--) {
            itemID = selectedElements[i];
            if (this.itemIds.indexOf(itemID) !== -1) {
                params.positions[itemID] = {'x': this.items[itemID].positionX - mousePos.mX,
                                     'y': this.items[itemID].positionY - mousePos.mY};
            }
        }

        return params;
    };

    DiagramDesignerWidgetDraggable.prototype.DRAG_EFFECTS = dragSource.DRAG_EFFECTS;

    return DiagramDesignerWidgetDraggable;
});
<commit_msg>bugfix<commit_after>/*
 * Copyright (C) 2013 Vanderbilt University, All rights reserved.
 *
 * Author: Robert Kereskenyi
 */

"use strict";

define(['js/DragDrop/DragSource',
    'js/DragDrop/DragHelper',
    './DiagramDesignerWidget.Constants'], function (dragSource,
                                                    dragHelper,
                                                    DiagramDesignerWidgetConstants) {

    var DiagramDesignerWidgetDraggable,
        DRAG_HELPER_CLASS = 'diagram-designer-drag-outline';

    DiagramDesignerWidgetDraggable = function () {
    };

    DiagramDesignerWidgetDraggable.prototype._makeDraggable = function (item) {
        var self = this;

        dragSource.makeDraggable(item.$el, {
            'helper': function (event, dragInfo) {
                return self._dragHelper(this, event, dragInfo);
            },
            'dragItems': function (el) {
                return self.getDragItems(self.selectionManager.getSelectedElements());
            },
            'dragEffects': function (el, event) {
                return self.getDragEffects(self.selectionManager.getSelectedElements(), event);
            },
            'dragParams': function (el, event) {
                return self.getDragParams(self.selectionManager.getSelectedElements(), event);
            },
            'start': function (event) {
                var ret = false;
                //enable drag mode only in
                //- DESIGN MODE
                //- if the mousedown is not on a connection drawing start point
                if (self.mode === self.OPERATING_MODES.DESIGN) {
                    //we need to check if the target element is SVGElement or not
                    //because jQuery does not work well on SVGElements
                    if (event.originalEvent.target instanceof SVGElement) {
                        var classDef = event.originalEvent.target.getAttribute("class");
                        if (classDef) {
                            ret = classDef.split(' ').indexOf(DiagramDesignerWidgetConstants.CONNECTOR_CLASS) === -1;
                        } else {
                            ret = true;
                        }
                    } else {
                        ret = !$(event.originalEvent.target).hasClass(DiagramDesignerWidgetConstants.CONNECTOR_CLASS);
                    }
                }
                return ret;
            }
        });
    };

    DiagramDesignerWidgetDraggable.prototype._destroyDraggable = function (item) {
        dragSource.destroyDraggable(item.$el);
    };

    /* OVERWRITE DragSource.prototype.dragHelper */
    DiagramDesignerWidgetDraggable.prototype._dragHelper = function (el, event, dragInfo) {
        var helperEl = $('
', {'class': DRAG_HELPER_CLASS}),
            selectionBBox = this.selectionManager._getSelectionBoundingBox(),
            mousePos = this.getAdjustedMousePos(event),
            dragEffects = dragHelper.getDragEffects(dragInfo);

        this.logger.debug("_dragHelper's dragInfo: " + JSON.stringify(dragInfo));

        if (selectionBBox) {
            helperEl.css({'width': (selectionBBox.x2 - selectionBBox.x) * this._zoomRatio,
                'height': (selectionBBox.y2 - selectionBBox.y) * this._zoomRatio,
                'line-height': ((selectionBBox.y2 - selectionBBox.y) * this._zoomRatio) + "px",
                'text-align': 'center',
                'border': '2px dashed #666',
                'background-color': 'rgba(100, 100, 100, 0.1)',
                'margin-top': (selectionBBox.y - mousePos.mY + dragSource.DEFAULT_CURSOR_AT.top) * this._zoomRatio,
                'margin-left': (selectionBBox.x - mousePos.mX + dragSource.DEFAULT_CURSOR_AT.left) * this._zoomRatio});

            if (dragEffects.length === 1) {
                if (dragEffects[0] === dragSource.DRAG_EFFECTS.DRAG_MOVE) {
                    helperEl.append($('')).append(' Move...');
                } else if (dragEffects[0] === dragSource.DRAG_EFFECTS.DRAG_COPY) {
                    helperEl.append($('')).append(' Copy...');
                }
            }
        }

        return helperEl;
    };

    DiagramDesignerWidgetDraggable.prototype.getDragItems = function (selectedElements) {
        this.logger.warning("DiagramDesignerWidgetDraggable.getDragItems is not overridden in the controller!!! selectedElements: " + selectedElements);
        return [];
    };

    DiagramDesignerWidgetDraggable.prototype.getDragEffects = function (selectedElements, event) {
        var ctrlKey = event.ctrlKey || event.metaKey,
            effects = [dragSource.DRAG_EFFECTS.DRAG_MOVE];

        //by default the drag is a MOVE

        //CTRL key --> copy
        if (ctrlKey) {
            effects = [dragSource.DRAG_EFFECTS.DRAG_COPY];
        }

        return effects;
    };

    DiagramDesignerWidgetDraggable.prototype.getDragParams = function (selectedElements, event) {
        var params = { 'positions': {}},
            i = selectedElements.length,
            itemID,
            selectionBBox = this.selectionManager._getSelectionBoundingBox(),
            mousePos = this.getAdjustedMousePos(event);

        while (i--) {
            itemID = selectedElements[i];
            if (this.itemIds.indexOf(itemID) !== -1) {
                params.positions[itemID] = {'x': this.items[itemID].positionX - mousePos.mX,
                                     'y': this.items[itemID].positionY - mousePos.mY};
            }
        }

        return params;
    };

    DiagramDesignerWidgetDraggable.prototype.DRAG_EFFECTS = dragSource.DRAG_EFFECTS;

    return DiagramDesignerWidgetDraggable;
});
<|endoftext|>


<commit_before>const Jaryn = function() { return(this === window) ? new Jaryn() : this; };

Jaryn.prototype.error = Object.freeze({
  "printError": ((error) => console.log(error))
});

Jaryn.prototype.permissions = Object.freeze({
  "create": { "create": true },
  "notCreate": { "create": false }
});

// Virtual file system layer to google drive.
Jaryn.prototype.vfs = Object.freeze({
  /**
   * Convenience function to get the current data file.
   * @param function cb Call back to execute once data is loaded.
   */
  "getThisMonthsJSON": (cb) => {
    const date = new Date();
    const month = date.getMonth()+1;
    const year = date.getFullYear();
    const name = month + "-" + year + ".json";

    Jaryn.prototype.vfs.readJSON(name, cb);
  },

  /**
   * This function takes a date, parses the month and year from it
   * and reads the JSON from that months <year>-<month>.json file.
   * The supplied callback is then passed the data and executed.
   *
   * @param Date date
   * @param function cb Callback to execute when name generated.
   */
  "getJSONForDate": (date, cb) => {
    const year = date.getFullYear();
    const month = (date.getMonth() + 1 < 10) ? "0" + (date.getMonth() + 1) : date.getMonth() + 1;

    if(cb !== undefined) cb();
  },

  /**
   * getDataFile gets the data file for the given filename and executes the
   * given callback with the data stored in the file passed in as an argument.
   *
   * @param String fn File name to open and read.
   * @param function cb Callback to execute when file is read.
   */
  "readJSON": (fn, cb) => {
    chrome.syncFileSystem.requestFileSystem((fs) => {
      const perms = Jaryn.prototype.permissions.notCreate;
      const err = Jaryn.prototype.error.printError;

      fs.root.getFile(fn,
        perms,
        (fileEntry) => {
          fileEntry.file((file) => {
            const fileReader = new FileReader();
            fileReader.onload = (e) => {
              if(cb !== undefined) cb(JSON.parse(fileReader.result));
            };
            fileReader.readAsText(file);
          });
        },
        err);
    });
  },

  /**
   * This writes data to the specified file, creating it if it does not exist.
   * Ensure data is a JSON object!
   *
   * @param String fn File name to write to.
   * @param Object json JSON to  stringify and write.
   * @param cb Callback to execute on write.
   */
  "writeJSON": (fn, json, cb) => {
    const ops =  Jaryn.prototype.permissions.create;
    const err = Jaryn.prototype.error.printError;

    chrome.syncFileSystem.requestFileSystem((fs) => {
      fs.root.getFile(fn,
      ops,
      (file) => {
        file.createWriter((writer) => {
          const blob = new Blob([JSON.stringify(json)], { "type": "text/plain" });
          writer.write(blob);
          if(cb !== undefined) cb(JSON.stringify(json));
        });
      },
      err);
    });
  },

  /**
   * @param String fn File name to write to.
   * @param Object day The data for the day.
   * @param function cb the Callback function to execute.
   */
  "updateJSON": (fn, day, cb) => {
    Jaryn.prototype.vfs.readJSON(fn, (data) => {
      data.push(day);
      Jaryn.prototype.vfs.writeJSON(fn, data, cb);
    });
  }
});<commit_msg>Cleaned up the Jaryn api a bit.<commit_after>const Jaryn = function() { return(this === window) ? new Jaryn() : this; };

// Virtual file system layer to google drive.
Jaryn.prototype.vfs = Object.freeze({
  /**
   * Convenience function to get the current data file.
   * @param function cb Call back to execute once data is loaded.
   */
  "getThisMonthsJSON": (cb) => {
    const date = new Date();
    const month = date.getMonth()+1;
    const year = date.getFullYear();
    const name = month + "-" + year + ".json";

    Jaryn.prototype.vfs.readJSON(name, cb);
  },

  /**
   * This function takes a date, parses the month and year from it
   * and reads the JSON from that months <month>-<year>.json file.
   * The supplied callback is then passed the data and executed.
   *
   * @param Date date
   * @param function cb Callback to execute when name generated.
   */
  "getJSONForDate": (date, cb) => {
    const year = date.getFullYear();
    const month = (date.getMonth() + 1 < 10) ? "0" + (date.getMonth() + 1) : date.getMonth() + 1;

    Jaryn.prototype.vfs.readJSON(year + "-" + month + ".json", cb);
  },

  /**
   * getDataFile gets the data file for the given filename and executes the
   * given callback with the data stored in the file passed in as an argument.
   *
   * readJSON does NOT create a file if it doesn't already exist.
   *
   * @param String fn File name to open and read.
   * @param function cb Callback to execute when file is read.
   */
  "readJSON": (fn, cb) => {
    chrome.syncFileSystem.requestFileSystem((fs) => {

      fs.root.getFile(fn,
        { "create": false },
        (fileEntry) => {
          fileEntry.file((file) => {
            const fileReader = new FileReader();
            fileReader.onload = (e) => {
              if(cb !== undefined) cb(JSON.parse(fileReader.result));
            };
            fileReader.readAsText(file);
          });
        },
        (err) => console.log(err));
    });
  },

  /**
   * createJSON creates a new json file with the given file name.
   *
   * @param String fn File name to create.
   * @param Function fn Callback to execute when file is created.
   */
  "createJSON": (fn, cb) => {
    chrome.syncFileSystem.requestFileSystem((fs) => {
      fs.root.getFile(fn,
      { "create": true },
      () => {
        if(cb !== undefined) cb();
      },
      (err) => console.log(err));
    });
  },

  /**
   * This writes data to the specified file, creating it if it does not exist.
   * Ensure data is a JSON object!
   *
   * @param String fn File name to write to.
   * @param Object json JSON to  stringify and write.
   * @param cb Callback to execute on write.
   */
  "writeJSON": (fn, json, cb) => {
    chrome.syncFileSystem.requestFileSystem((fs) => {
      fs.root.getFile(fn,
      { "create": true },
      (file) => {
        file.createWriter((writer) => {
          const blob = new Blob([JSON.stringify(json)], { "type": "text/plain" });
          writer.write(blob);
          if(cb !== undefined) cb(JSON.stringify(json));
        });
      },
      (err) => console.log(err));
    });
  },

  /**
   * @param String fn File name to write to.
   * @param Object day The data for the day.
   * @param function cb the Callback function to execute.
   */
  "updateJSON": (fn, day, cb) => {
    Jaryn.prototype.vfs.readJSON(fn, (data) => {
      data.push(day);
      Jaryn.prototype.vfs.writeJSON(fn, data, cb);
    });
  }
});<|endoftext|>


<commit_before><commit_msg>changed search results page<commit_after>import React from "react";

constr searchresults_doginfo = () =>

<|endoftext|>


<commit_before>import React from 'react'
import echarts from 'echarts'
import _ from 'lodash'
import fetch from 'isomorphic-fetch'
import styles from './Main.css'
import Loading from '../Loading'

import { autobind } from 'react-decoration'
import { Select } from 'antd'
import { checkStatus, apiAddress } from '../../util'

const refName = 'Compare'
const Option = Select.Option

const get5MaCalculator = (values) => {
  const result = []
  for (let i = 0, len = values.length; i < len; i++) {
    if (i < 5) {
      result.push('-')
      continue
    }
    let sum = 0
    for (let j = 0; j < 5; j++) {
      sum += values[i - j]
    }
    result.push(sum / 5)
  }
  return result
}

const getOption = ({ topic, totTopicAmountList, ldaTopicAmountList, selectedTopic }) => ({
  tooltip: {
    trigger: 'axis',
    axisPointer: {
      type: 'cross'
    }
  },
  legend: {
    data: ['tot', 'lda']
  },
  grid: {
    left: '10%',
    right: '10%',
    bottom: '15%'
  },
  xAxis: {
    type: 'category',
    data: totTopicAmountList.date,
    scale: true,
    boundaryGap: false,
    axisLine: {onZero: false},
    splitLine: {show: false},
    splitNumber: 20,
    min: 'dataMin',
    max: 'dataMax'
  },
  yAxis: {
    scale: true,
    splitArea: {
      show: true
    }
  },
  dataZoom: [
    {
      type: 'inside',
      start: 50,
      end: 100
    },
    {
      show: true,
      type: 'slider',
      y: '90%',
      start: 50,
      end: 100
    }
  ],
  series: [{
    name: 'tot',
    type: 'line',
    data: get5MaCalculator(totTopicAmountList.data[selectedTopic]),
    smooth: true,
    lineStyle: {
      normal: {opacity: 0.5}
    }
  }, {
    name: 'lda',
    type: 'line',
    data: get5MaCalculator(ldaTopicAmountList.data[selectedTopic]),
    smooth: true,
    lineStyle: {
      normal: {opacity: 0.5}
    }
  }]
})

const transformData = (topicAbsAmountList) => {
  const totalAmount = _.zip(...topicAbsAmountList.data).map(_.sum)
  return {
    data: topicAbsAmountList.data.map(topicAbsAmount => (
      topicAbsAmount.map((amount, index) => amount / totalAmount[index])
    )),
    date: topicAbsAmountList.date
  }
}

export default class RelativeTrend extends React.Component {
  constructor () {
    super()
    this.state = {
      topic: [],
      selectedTopic: 0,
      totTopicAmountList: [],
      ldaTopicAmountList: []
    }
    fetch(`${apiAddress}/tot-topic`, { method: 'GET' })
      .then(checkStatus)
      .then((res) => (res.json()))
      .then((topic) => this.setState({...this.state, topic}))
    fetch(`${apiAddress}/tot-topic-amount`, { method: 'GET' })
      .then(checkStatus)
      .then((res) => (res.json()))
      .then((topicAbsAmountList) => this.setState({
        ...this.state,
        totTopicAmountList: transformData(topicAbsAmountList)
      }))
    fetch(`${apiAddress}/lda-topic-amount`, { method: 'GET' })
      .then(checkStatus)
      .then((res) => (res.json()))
      .then((topicAbsAmountList) => this.setState({
        ...this.state,
        ldaTopicAmountList: transformData(topicAbsAmountList)
      }))
  }

  @autobind
  isDataLoaded () {
    return !_.isEmpty(this.state.topic) &&
      !_.isEmpty(this.state.ldaTopicAmountList) &&
      !_.isEmpty(this.state.totTopicAmountList)
  }

  componentDidMount () {
    this.myChart = echarts.init(this.refs[refName])
  }

  componentDidUpdate () {
    if (this.isDataLoaded()) {
      const option = getOption(this.state)
      console.log(option)
      this.myChart.setOption(option)
    }
  }

  @autobind
  handleChange (value) {
    this.setState({
      ...this.state,
      selectedTopic: value
    })
  }

  render () {
    return

      {this.isDataLoaded()
        ?

请选择主题进行对比，主题名称是对关键词的人工解释

          <Select defaultValue={this.state.topic[0]} style={{ width: 200 }} onChange={this.handleChange}>
            {
                this.state.topic.map((topic, index) => (
                  <Option value={index}>{topic}</Option>
                ))
              }
          </Select>

        : <Loading />}

  }
}
<commit_msg>finish<commit_after>import React from 'react'
import echarts from 'echarts'
import _ from 'lodash'
import fetch from 'isomorphic-fetch'
import styles from './Main.css'
import Loading from '../Loading'

import { autobind } from 'react-decoration'
import { Select } from 'antd'
import { checkStatus, apiAddress } from '../../util'

const refName = 'Compare'
const Option = Select.Option

const get3MaCalculator = (values) => {
  const result = []
  for (let i = 0, len = values.length; i < len; i++) {
    if (i < 3) {
      result.push('-')
      continue
    }
    let sum = 0
    for (let j = 0; j < 3; j++) {
      sum += values[i - j]
    }
    result.push(sum / 3)
  }
  return result
}

const getOption = ({ topic, totTopicAmountList, ldaTopicAmountList, selectedTopic }) => ({
  tooltip: {
    trigger: 'axis',
    axisPointer: {
      type: 'cross'
    }
  },
  legend: {
    data: ['tot', 'lda']
  },
  grid: {
    left: '10%',
    right: '10%',
    bottom: '15%'
  },
  xAxis: {
    type: 'category',
    data: totTopicAmountList.date,
    scale: true,
    boundaryGap: false,
    axisLine: {onZero: false},
    splitLine: {show: false},
    splitNumber: 20,
    min: 'dataMin',
    max: 'dataMax'
  },
  yAxis: {
    scale: true,
    splitArea: {
      show: true
    }
  },
  dataZoom: [
    {
      type: 'inside',
      start: 0,
      end: 100
    },
    {
      show: true,
      type: 'slider',
      y: '90%',
      start: 0,
      end: 100
    }
  ],
  series: [{
    name: 'tot',
    type: 'line',
    data: get3MaCalculator(totTopicAmountList.data[selectedTopic]),
    smooth: true,
    lineStyle: {
      normal: {opacity: 0.5}
    }
  }, {
    name: 'lda',
    type: 'line',
    data: get3MaCalculator(ldaTopicAmountList.data[selectedTopic]),
    smooth: true,
    lineStyle: {
      normal: {opacity: 0.5}
    }
  }]
})

const transformData = (topicAbsAmountList) => {
  const totalAmount = _.zip(...topicAbsAmountList.data).map(_.sum)
  return {
    data: topicAbsAmountList.data.map(topicAbsAmount => (
      topicAbsAmount.map((amount, index) => amount / totalAmount[index])
    )),
    date: topicAbsAmountList.date
  }
}

export default class RelativeTrend extends React.Component {
  constructor () {
    super()
    this.state = {
      topic: [],
      selectedTopic: 0,
      totTopicAmountList: [],
      ldaTopicAmountList: []
    }
    fetch(`${apiAddress}/tot-topic`, { method: 'GET' })
      .then(checkStatus)
      .then((res) => (res.json()))
      .then((topic) => this.setState({...this.state, topic}))
    fetch(`${apiAddress}/tot-topic-amount`, { method: 'GET' })
      .then(checkStatus)
      .then((res) => (res.json()))
      .then((topicAbsAmountList) => this.setState({
        ...this.state,
        totTopicAmountList: transformData(topicAbsAmountList)
      }))
    fetch(`${apiAddress}/lda-topic-amount`, { method: 'GET' })
      .then(checkStatus)
      .then((res) => (res.json()))
      .then((topicAbsAmountList) => this.setState({
        ...this.state,
        ldaTopicAmountList: transformData(topicAbsAmountList)
      }))
  }

  @autobind
  isDataLoaded () {
    return !_.isEmpty(this.state.topic) &&
      !_.isEmpty(this.state.ldaTopicAmountList) &&
      !_.isEmpty(this.state.totTopicAmountList)
  }

  componentDidMount () {
    this.myChart = echarts.init(this.refs[refName])
  }

  componentDidUpdate () {
    if (this.isDataLoaded()) {
      const option = getOption(this.state)
      console.log(option)
      this.myChart.setOption(option)
    }
  }

  @autobind
  handleChange (value) {
    this.setState({
      ...this.state,
      selectedTopic: value
    })
  }

  render () {
    return

      {this.isDataLoaded()
        ?

请选择主题进行对比，主题名称是对关键词的人工解释

          <Select defaultValue={this.state.topic[0]} style={{ width: 200 }} onChange={this.handleChange}>
            {
                this.state.topic.map((topic, index) => (
                  <Option value={index}>{topic}</Option>
                ))
              }
          </Select>

        : <Loading />}

  }
}
<|endoftext|>


<commit_before>/**
 * @author: @AngularClass
 */

require('ts-node/register');
var helpers = require('./helpers');

exports.config = {
    baseUrl: 'http://localhost:3000/',

    // use `npm run e2e`
    specs: [
        helpers.root('src/**/**.e2e.ts'),
        helpers.root('src/**/*.e2e.ts')
    ],
    exclude: [],

    framework: 'jasmine2',

    allScriptsTimeout: 110000,

    jasmineNodeOpts: {
        showTiming: true,
        showColors: true,
        isVerbose: false,
        includeStackTrace: false,
        defaultTimeoutInterval: 400000
    },
    // directConnect: false,

    capabilities: {
        'browserName': 'chrome',
        'chromeOptions': {
            'args': ['show-fps-counter=true']
        }
    },

    onPrepare: function() {
        browser.ignoreSynchronization = true;
    },

    /**
     * Angular 2 configuration
     *
     * useAllAngular2AppRoots: tells Protractor to wait for any angular2 apps on the page instead of just the one matching
     * `rootEl`
     */
    useAllAngular2AppRoots: true
};<commit_msg>cleanup<commit_after><|endoftext|>


<commit_before>// @flow
import type {
  IncomingMessage,
  ServerResponse,
} from 'http'
import type {
  RequestData,
  ResponseData,
  PhenylRunner,
} from 'phenyl-interfaces'

import url from 'url'
import {
  decodeRequest,
  encodeResponse,
  getStatusCode,
} from 'phenyl-http-rules/jsnext'
import {
  createErrorResult,
} from 'phenyl-utils/jsnext'

export default class PhenylHttpServer {
  server: net$Server
  phenylCore: PhenylRunner

  constructor(server: net$Server, phenylCore: PhenylRunner) {
    this.server = server
    this.phenylCore = phenylCore
  }

  /**
   *
   */
  listen(port: number, hostname?: string, backlog?: number, callback?: Function) {
    this.server.on('request', this.onRequest.bind(this))
    this.server.listen(port, hostname, backlog, callback)
  }

  getRequestBody(request: IncomingMessage): Promise<string> {
    return new Promise((resolve, reject) => {
      const read = () => {
        try {
          // https://nodejs.org/api/stream.html#stream_readable_read_size
          const chunks = []
          let chunk
          let totalLength = 0
          while (null !== (chunk = request.read())) {
            // $FlowIssue(chunk-is-always-Buffer)
            chunks.push(chunk)
            // $FlowIssue(chunk-is-always-Buffer)
            totalLength += chunk.length
          }
          resolve(Buffer.concat(chunks, totalLength).toString('utf8'))
        } catch (err) {
          reject(err)
        }
      }

      request.once('error', err => {
        request.removeListener('readable', read)
        reject(err)
      })

      request.once('readable', read)
    })
  }

  /**
   * @private
   */
  async onRequest(request: IncomingMessage, response: ServerResponse) {
    const requestUrl = url.parse(request.url, true)
    let responseData

    try {
      // 1. Decoding Request
      // $FlowIssue(request.method-is-always-compatible)
      const [requestData, sessionId] = decodeRequest({
        method: request.method,
        path: requestUrl.pathname,
        body: await this.getRequestBody(request),
        headers: request.headers,
        queryString: requestUrl.query,
      })
      // 2. Invoking PhenylCore
      responseData = await this.phenylCore.run(requestData, sessionId)
    }
    catch (err) {
      responseData = { error: createErrorResult(err) }
    }

    // 3. Encoding Response
    const statusCode = getStatusCode(responseData)
    const body = JSON.stringify(encodeResponse(responseData))
    const headers = {
      'Content-Length': Buffer.byteLength(body).toString(),
      'Content-Type': 'application/json',
    }
    response.writeHead(statusCode, headers)
    response.write(body)
    response.end()
  }
}
<commit_msg>[phenyl-http-server] fix bug: http server should parse querystring<commit_after>// @flow
import type {
  IncomingMessage,
  ServerResponse,
} from 'http'
import type {
  RequestData,
  ResponseData,
  PhenylRunner,
} from 'phenyl-interfaces'

import url from 'url'
import {
  decodeRequest,
  encodeResponse,
  getStatusCode,
} from 'phenyl-http-rules/jsnext'
import {
  createErrorResult,
} from 'phenyl-utils/jsnext'

export default class PhenylHttpServer {
  server: net$Server
  phenylCore: PhenylRunner

  constructor(server: net$Server, phenylCore: PhenylRunner) {
    this.server = server
    this.phenylCore = phenylCore
  }

  /**
   *
   */
  listen(port: number, hostname?: string, backlog?: number, callback?: Function) {
    this.server.on('request', this.onRequest.bind(this))
    this.server.listen(port, hostname, backlog, callback)
  }

  getRequestBody(request: IncomingMessage): Promise<string> {
    return new Promise((resolve, reject) => {
      const read = () => {
        try {
          // https://nodejs.org/api/stream.html#stream_readable_read_size
          const chunks = []
          let chunk
          let totalLength = 0
          while (null !== (chunk = request.read())) {
            // $FlowIssue(chunk-is-always-Buffer)
            chunks.push(chunk)
            // $FlowIssue(chunk-is-always-Buffer)
            totalLength += chunk.length
          }
          resolve(Buffer.concat(chunks, totalLength).toString('utf8'))
        } catch (err) {
          reject(err)
        }
      }

      request.once('error', err => {
        request.removeListener('readable', read)
        reject(err)
      })

      request.once('readable', read)
    })
  }

  /**
   * @private
   */
  async onRequest(request: IncomingMessage, response: ServerResponse) {
    const requestUrl = url.parse(request.url, true)
    let responseData

    try {
      // 1. Decoding Request
      // $FlowIssue(request.method-is-always-compatible)
      const [requestData, sessionId] = decodeRequest({
        method: request.method,
        path: requestUrl.pathname,
        body: await this.getRequestBody(request),
        headers: request.headers,
        qsParams: requestUrl.query,
      })
      // 2. Invoking PhenylCore
      responseData = await this.phenylCore.run(requestData, sessionId)
    }
    catch (err) {
      responseData = { error: createErrorResult(err) }
    }

    // 3. Encoding Response
    const statusCode = getStatusCode(responseData)
    const body = JSON.stringify(encodeResponse(responseData))
    const headers = {
      'Content-Length': Buffer.byteLength(body).toString(),
      'Content-Type': 'application/json',
    }
    response.writeHead(statusCode, headers)
    response.write(body)
    response.end()
  }
}
<|endoftext|>