<commit_before>import lonlat from '@conveyal/lonlat'
import React, { Component, PropTypes } from 'react'
import ReactDOM from 'react-dom'
import { FormGroup, FormControl, InputGroup, DropdownButton, MenuItem } from 'react-bootstrap'
import { connect } from 'react-redux'
import { autocomplete } from 'isomorphic-mapzen-search'

import { setLocation, setLocationToCurrent, clearLocation } from '../../actions/map'
import { addLocationSearch, getCurrentPosition } from '../../actions/location'
import { distanceStringImperial } from '../../util/distance'

class LocationField extends Component {
  static propTypes = {
    config: PropTypes.object,
    currentPosition: PropTypes.object,
    hideExistingValue: PropTypes.bool,
    location: PropTypes.object,
    label: PropTypes.string,
    nearbyStops: PropTypes.array,
    sessionSearches: PropTypes.array,
    showClearButton: PropTypes.bool,
    static: PropTypes.bool, // show autocomplete options as fixed/inline element rather than dropdown
    stopsIndex: PropTypes.object,
    type: PropTypes.string, // replace with locationType?

    // callbacks
    onClick: PropTypes.func,
    onLocationSelected: PropTypes.func,

    // dispatch
    addLocationSearch: PropTypes.func,
    clearLocation: PropTypes.func,
    setLocation: PropTypes.func,
    setLocationToCurrent: PropTypes.func
  }

  static defaultProps = {
    showClearButton: true
  }

  constructor (props) {
    super(props)
    this.state = {
      value: props.location !== null && !props.hideExistingValue
        ? props.location.name
        : '',
      menuVisible: false,
      geocodedFeatures: []
    }
  }

  componentWillReceiveProps (nextProps) {
    if (this.props.location !== nextProps.location) {
      this.setState({
        value: nextProps.location !== null ? nextProps.location.name : '',
        geocodedFeatures: []
      })
    }
  }

  componentDidMount () {
    if (this.props.static) {
      ReactDOM.findDOMNode(this.refs['formControl']).focus()
    }
  }

  _geocode (text) {
    const {MAPZEN_KEY, baseUrl, boundary, focusPoint} = this.props.config.geocoder
    autocomplete({
      apiKey: MAPZEN_KEY,
      boundary,
      focusPoint,
      text,
      url: baseUrl ? `${baseUrl}/autocomplete` : null
    }).then((result) => {
      console.log(result)
      this.setState({ geocodedFeatures: result.features })
    }).catch((err) => {
      console.error(err)
    })
  }

  _getFormControlClassname () {
    return this.props.type + '-form-control'
  }

  _onClearButtonClick = () => {
    this.props.clearLocation(this.props.type)
    this.setState({
      value: '',
      geocodedFeatures: []
    })
  }

  _onDropdownToggle = (v, e) => {
    // if clicked on input form control, keep dropdown open; otherwise, toggle
    const targetIsInput =
      e.target.className.indexOf(this._getFormControlClassname()) !== -1
    this.setState({ menuVisible: targetIsInput ? true : !this.state.menuVisible })
  }

  _onTextInputChange = (evt) => {
    this.setState({ value: evt.target.value })
    this._geocode(evt.target.value)
  }

  _onTextInputClick = () => {
    if (typeof this.props.onClick === 'function') this.props.onClick()
    this.setState({ menuVisible: true })
  }

  _setLocation (location) {
    if (typeof this.props.onLocationSelected === 'function') {
      this.props.onLocationSelected(location)
    }
    this.props.setLocation(this.props.type, location)
  }

  _useCurrentLocation = () => {
    const {currentPosition, getCurrentPosition, setLocationToCurrent, type} = this.props
    if (currentPosition.coords) {
      // We already have geolocation coordinates
      setLocationToCurrent(type)
    } else {
      // Call geolocation.getCurrentPosition and set as from/to type
      this.setState({fetchingLocation: true})
      getCurrentPosition(type)
    }
  }

  /**
   * Provide alert to user with reason for geolocation error
   */
  _geolocationAlert = () => {
    window.alert(
      `Geolocation either has been disabled for ${window.location.host} or is not available in your browser.\n\nReason: ${this.props.currentPosition.error.message || 'Unknown reason'}`
    )
  }

  render () {
    const {
      currentPosition,
      label,
      showClearButton,
      static: isStatic,
      type
    } = this.props

    const currentLocationOption = !currentPosition.error
      ? createOption('location-arrow', 'Use Current Location', this._useCurrentLocation)
      : createOption('ban', 'Current location not available', this._geolocationAlert)

    let geocodedFeatures = this.state.geocodedFeatures
    if (geocodedFeatures.length > 5) geocodedFeatures = geocodedFeatures.slice(0, 5)

    let nearbyStops = this.props.nearbyStops
    if (nearbyStops.length > 4) nearbyStops = nearbyStops.slice(0, 3)

    let sessionSearches = this.props.sessionSearches
    if (sessionSearches.length > 5) sessionSearches = sessionSearches.slice(0, 5)

    // assemble menu contents, to be displayed either as dropdown or static panel
    let menuItems = []
    menuItems.push(currentLocationOption)

    // geocode search result option(s)
    if (geocodedFeatures.length > 0) {
      menuItems.push(<MenuItem header key='sr-header'>Search Results</MenuItem>)
      menuItems = menuItems.concat(geocodedFeatures.map(feature => {
        return createOption('map-pin', feature.properties.label, () => {
          const location = lonlat.fromCoordinates(feature.geometry.coordinates)
          location.name = feature.properties.label

          // set the current location
          this._setLocation(location)

          // add to the location search history
          this.props.addLocationSearch(location)
        })
      }))
    }

    // nearby transit stop options
    if (nearbyStops.length > 0) {
      menuItems.push(<MenuItem header key='ns-header'>Nearby Stops</MenuItem>)
      menuItems = menuItems.concat(nearbyStops.map(stopId => {
        const stop = this.props.stopsIndex[stopId]
        return createTransitStopOption(stop, () => {
          this._setLocation({
            name: stop.name,
            lat: stop.lat,
            lon: stop.lon
          })
        })
      }))
    }

    // recent search history options
    if (sessionSearches.length > 0) {
      menuItems.push(<MenuItem header key='ss-header'>Recently Searched</MenuItem>)
      menuItems = menuItems.concat(sessionSearches.map(location => {
        return createOption('search', location.name, () => {
          // set the current location
          this._setLocation(location)
        })
      }))
    }

    /** the text input element **/
    const placeholder = currentPosition.fetching === type
      ? 'Fetching location...'
      : label || type
    const textControl = <FormControl ref='formControl'
      className={this._getFormControlClassname()}
      type='text'
      value={this.state.value}
      placeholder={placeholder}
      onChange={this._onTextInputChange}
      onClick={this._onTextInputClick}
    />

    /** the clear ('X') button add-on */
    const clearButton = <InputGroup.Addon onClick={this._onClearButtonClick}>

    </InputGroup.Addon>

    if (isStatic) { // 'static' mode (menu is displayed alongside input)
      return (

          <form>
            <FormGroup>
              <InputGroup>
                <InputGroup.Addon>
                  <i className={`fa fa-${type === 'from' ? 'star' : 'map-marker'}`} />
                </InputGroup.Addon>
                {textControl}
                {showClearButton && clearButton}
              </InputGroup>
            </FormGroup>
          </form>

            {menuItems}

      )
    } else { // default display mode with dropdown menu
      return (
        <form>
          <FormGroup className='location-field'>
            <InputGroup>
              {/* location field icon -- also serves as dropdown anchor */}
              <DropdownButton
                componentClass={InputGroup.Button}
                open={this.state.menuVisible}
                onToggle={this._onDropdownToggle}
                id='location-dropdown'
                title={<i className={`fa fa-${type === 'from' ? 'star' : 'map-marker'}`} />}
                noCaret
              >
                {menuItems}
              </DropdownButton>
              {textControl}
              {showClearButton && clearButton}
            </InputGroup>
          </FormGroup>
        </form>
      )
    }
  }
}

// helper functions for dropdown options

let itemKey = 0

function createOption (icon, title, onSelect) {
  return <MenuItem className='location-option' onSelect={onSelect} key={itemKey++}>

<i className={`fa fa-${icon}`} />

{title}

  </MenuItem>
}

function createTransitStopOption (stop, onSelect) {
  return <MenuItem className='location-option' onSelect={onSelect} key={itemKey++}>

{distanceStringImperial(stop.dist, true)}

{stop.name} ({stop.code})

          {(stop.routes || []).map((route, i) => {
            const name = route.shortName || route.longName
            return (
              <span key={`route-${i}`} className='route'>
                {name}

            )
          })}

  </MenuItem>
}

// connect to redux store

const mapStateToProps = (state, ownProps) => {
  return {
    config: state.otp.config,
    location: state.otp.currentQuery[ownProps.type],
    currentPosition: state.otp.location.currentPosition,
    sessionSearches: state.otp.location.sessionSearches,
    nearbyStops: state.otp.location.nearbyStops,
    stopsIndex: state.otp.transitIndex.stops
  }
}

const mapDispatchToProps = (dispatch, ownProps) => {
  return {
    addLocationSearch: (location) => { dispatch(addLocationSearch({ location })) },
    getCurrentPosition: (type) => dispatch(getCurrentPosition(type)),
    setLocation: (type, location) => { dispatch(setLocation({ type, location })) },
    setLocationToCurrent: (type) => { dispatch(setLocationToCurrent({ type })) },
    clearLocation: (type) => { dispatch(clearLocation({ type })) }
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(LocationField)
<commit_msg>feat(form): Extend LocationField to handle up/down/enter keystrokes<commit_after>import lonlat from '@conveyal/lonlat'
import React, { Component, PropTypes } from 'react'
import ReactDOM from 'react-dom'
import { FormGroup, FormControl, InputGroup, DropdownButton, MenuItem } from 'react-bootstrap'
import { connect } from 'react-redux'
import { autocomplete } from 'isomorphic-mapzen-search'

import { setLocation, setLocationToCurrent, clearLocation } from '../../actions/map'
import { addLocationSearch, getCurrentPosition } from '../../actions/location'
import { distanceStringImperial } from '../../util/distance'

class LocationField extends Component {
  static propTypes = {
    config: PropTypes.object,
    currentPosition: PropTypes.object,
    hideExistingValue: PropTypes.bool,
    location: PropTypes.object,
    label: PropTypes.string,
    nearbyStops: PropTypes.array,
    sessionSearches: PropTypes.array,
    showClearButton: PropTypes.bool,
    static: PropTypes.bool, // show autocomplete options as fixed/inline element rather than dropdown
    stopsIndex: PropTypes.object,
    type: PropTypes.string, // replace with locationType?

    // callbacks
    onClick: PropTypes.func,
    onLocationSelected: PropTypes.func,

    // dispatch
    addLocationSearch: PropTypes.func,
    clearLocation: PropTypes.func,
    setLocation: PropTypes.func,
    setLocationToCurrent: PropTypes.func
  }

  static defaultProps = {
    showClearButton: true
  }

  constructor (props) {
    super(props)
    this.state = {
      value: props.location !== null && !props.hideExistingValue
        ? props.location.name
        : '',
      menuVisible: false,
      geocodedFeatures: [],
      activeIndex: null
    }
  }

  componentWillReceiveProps (nextProps) {
    if (this.props.location !== nextProps.location) {
      this.setState({
        value: nextProps.location !== null ? nextProps.location.name : '',
        geocodedFeatures: []
      })
    }
  }

  componentDidMount () {
    if (this.props.static) {
      ReactDOM.findDOMNode(this.refs['formControl']).focus()
    }
  }

  _geocode (text) {
    const {MAPZEN_KEY, baseUrl, boundary, focusPoint} = this.props.config.geocoder
    autocomplete({
      apiKey: MAPZEN_KEY,
      boundary,
      focusPoint,
      text,
      url: baseUrl ? `${baseUrl}/autocomplete` : null
    }).then((result) => {
      console.log(result)
      this.setState({ geocodedFeatures: result.features })
    }).catch((err) => {
      console.error(err)
    })
  }

  _getFormControlClassname () {
    return this.props.type + '-form-control'
  }

  _onClearButtonClick = () => {
    this.props.clearLocation(this.props.type)
    this.setState({
      value: '',
      geocodedFeatures: []
    })
  }

  _onDropdownToggle = (v, e) => {
    // if clicked on input form control, keep dropdown open; otherwise, toggle
    const targetIsInput =
      e.target.className.indexOf(this._getFormControlClassname()) !== -1
    this.setState({ menuVisible: targetIsInput ? true : !this.state.menuVisible })
  }

  _onTextInputChange = (evt) => {
    this.setState({ value: evt.target.value })
    this._geocode(evt.target.value)
  }

  _onTextInputClick = () => {
    if (typeof this.props.onClick === 'function') this.props.onClick()
    this.setState({ menuVisible: true })
  }

  _onKeyDown = (evt) => {
    const { activeIndex } = this.state

    switch (evt.key) {
      // 'Down' arrow key pressed: move selected menu item down by one position
      case 'ArrowDown':
        if (activeIndex === this.menuItemCount - 1) {
          this.setState({ activeIndex: null })
          break
        }
        this.setState({
          activeIndex: activeIndex === null
            ? 0
            : activeIndex + 1
        })
        break

      // 'Up' arrow key pressed: move selection up by one position
      case 'ArrowUp':
        if (activeIndex === 0) {
          this.setState({ activeIndex: null })
          break
        }
        this.setState({
          activeIndex: activeIndex === null
            ? this.menuItemCount - 1
            : activeIndex - 1
        })
        break

      // Enter pressed: apply the location associated with current selected menu item
      case 'Enter':

        // Retrieve location selection handler from lookup object and invoke
        const locationSelected = this.locationSelectedLookup[activeIndex]
        if (locationSelected) locationSelected()

        // Clear selection & hide the menu
        this.setState({
          menuVisible: false,
          activeIndex: null
        })

        // Suppress default 'Enter' behavior which causes page to reload
        evt.preventDefault()
        break

      // Any other key pressed: clear active selection
      default:
        this.setState({ activeIndex: null })
    }
  }

  _setLocation (location) {
    if (typeof this.props.onLocationSelected === 'function') {
      this.props.onLocationSelected(location)
    }
    this.props.setLocation(this.props.type, location)
  }

  _useCurrentLocation = () => {
    const {currentPosition, getCurrentPosition, setLocationToCurrent, type} = this.props
    if (currentPosition.coords) {
      // We already have geolocation coordinates
      setLocationToCurrent(type)
    } else {
      // Call geolocation.getCurrentPosition and set as from/to type
      this.setState({fetchingLocation: true})
      getCurrentPosition(type)
    }
  }

  /**
   * Provide alert to user with reason for geolocation error
   */
  _geolocationAlert = () => {
    window.alert(
      `Geolocation either has been disabled for ${window.location.host} or is not available in your browser.\n\nReason: ${this.props.currentPosition.error.message || 'Unknown reason'}`
    )
  }

  render () {
    const {
      currentPosition,
      label,
      showClearButton,
      static: isStatic,
      type
    } = this.props

    const { activeIndex } = this.state

    let geocodedFeatures = this.state.geocodedFeatures
    if (geocodedFeatures.length > 5) geocodedFeatures = geocodedFeatures.slice(0, 5)

    let nearbyStops = this.props.nearbyStops
    if (nearbyStops.length > 4) nearbyStops = nearbyStops.slice(0, 3)

    let sessionSearches = this.props.sessionSearches
    if (sessionSearches.length > 5) sessionSearches = sessionSearches.slice(0, 5)

    // Assemble menu contents, to be displayed either as dropdown or static panel.
    // Menu items are created in four phases: (1) the current location, (2) any
    // geocoder search results; (3) nearby transit stops; and (4) saved searches

    let menuItems = [] // array of menu items for display (may include non-selectable items e.g. dividers/headings)
    let itemIndex = 0 // the index of the current location-associated menu item (excluding non-selectable items)
    this.locationSelectedLookup = {} // maps itemIndex to a location selection handler (for use by the _onKeyDown method)

    /* 1) Process the current location */
    let locationSelected, optionIcon, optionTitle

    if (!currentPosition.error) { // current position detected successfully
      locationSelected = this._useCurrentLocation
      optionIcon = 'location-arrow'
      optionTitle = 'Use Current Location'
    } else { // error detecting current position
      locationSelected = this._geolocationAlert
      optionIcon = 'ban'
      optionTitle = 'Current location not available'
    }

    // Add to the selection handler lookup (for use in _onKeyDown)
    this.locationSelectedLookup[itemIndex] = locationSelected

    // Create and add the option item to the menu items array
    const currentLocationOption = createOption(optionIcon, optionTitle, locationSelected, itemIndex === activeIndex)
    menuItems.push(currentLocationOption)
    itemIndex++

    /* 2) Process geocode search result option(s) */
    if (geocodedFeatures.length > 0) {
      // Add the menu sub-heading (not a selectable item)
      menuItems.push(<MenuItem header key='sr-header'>Search Results</MenuItem>)

      // Iterate through the geocoder results
      menuItems = menuItems.concat(geocodedFeatures.map(feature => {
        // Create the selection handler
        const locationSelected = () => {
          // Construct the location
          const location = lonlat.fromCoordinates(feature.geometry.coordinates)
          location.name = feature.properties.label
          // Set the current location
          this._setLocation(location)
          // Add to the location search history
          this.props.addLocationSearch(location)
        }

        // Add to the selection handler lookup (for use in _onKeyDown)
        this.locationSelectedLookup[itemIndex] = locationSelected

        // Create and return the option menu item
        const option = createOption('map-pin', feature.properties.label, locationSelected, itemIndex === activeIndex)
        itemIndex++
        return option
      }))
    }

    /* 3) Process nearby transit stop options */
    if (nearbyStops.length > 0) {
      // Add the menu sub-heading (not a selectable item)
      menuItems.push(<MenuItem header key='ns-header'>Nearby Stops</MenuItem>)

      // Iterate through the found nearby stops
      menuItems = menuItems.concat(nearbyStops.map(stopId => {
        // Constuct the location
        const stop = this.props.stopsIndex[stopId]
        const location = {
          name: stop.name,
          lat: stop.lat,
          lon: stop.lon
        }

        // Create the location selected handler
        const locationSelected = () => { this._setLocation(location) }

        // Add to the selection handler lookup (for use in _onKeyDown)
        this.locationSelectedLookup[itemIndex] = locationSelected

        // Create and return the option menu item
        const option = createTransitStopOption(stop, locationSelected, itemIndex === activeIndex)
        itemIndex++
        return option
      }))
    }

    /* 4) Process recent search history options */
    if (sessionSearches.length > 0) {
      // Add the menu sub-heading (not a selectable item)
      menuItems.push(<MenuItem header key='ss-header'>Recently Searched</MenuItem>)

      // Iterate through any saved locations
      menuItems = menuItems.concat(sessionSearches.map(location => {
        // Create the location-selected handler
        const locationSelected = () => { this._setLocation(location) }

        // Add to the selection handler lookup (for use in _onKeyDown)
        this.locationSelectedLookup[itemIndex] = locationSelected

        // Create and return the option menu item
        const option = createOption('search', location.name, locationSelected, itemIndex === activeIndex)
        itemIndex++
        return option
      }))
    }

    // Store the number of location-associated items for reference in the _onKeyDown method
    this.menuItemCount = itemIndex

    /** the text input element **/
    const placeholder = currentPosition.fetching === type
      ? 'Fetching location...'
      : label || type
    const textControl = <FormControl ref='formControl'
      className={this._getFormControlClassname()}
      type='text'
      value={this.state.value}
      placeholder={placeholder}
      onChange={this._onTextInputChange}
      onClick={this._onTextInputClick}
      onKeyDown={this._onKeyDown}
    />

    /** the clear ('X') button add-on */
    const clearButton = <InputGroup.Addon onClick={this._onClearButtonClick}>

    </InputGroup.Addon>

    if (isStatic) { // 'static' mode (menu is displayed alongside input)
      return (

          <form>
            <FormGroup>
              <InputGroup>
                <InputGroup.Addon>
                  <i className={`fa fa-${type === 'from' ? 'star' : 'map-marker'}`} />
                </InputGroup.Addon>
                {textControl}
                {showClearButton && clearButton}
              </InputGroup>
            </FormGroup>
          </form>

            {menuItems}

      )
    } else { // default display mode with dropdown menu
      return (
        <form>
          <FormGroup className='location-field'>
            <InputGroup>
              {/* location field icon -- also serves as dropdown anchor */}
              <DropdownButton
                componentClass={InputGroup.Button}
                open={this.state.menuVisible}
                onToggle={this._onDropdownToggle}
                id='location-dropdown'
                title={<i className={`fa fa-${type === 'from' ? 'star' : 'map-marker'}`} />}
                noCaret
              >
                {menuItems}
              </DropdownButton>
              {textControl}
              {showClearButton && clearButton}
            </InputGroup>
          </FormGroup>
        </form>
      )
    }
  }
}

// helper functions for dropdown options

let itemKey = 0

function createOption (icon, title, onSelect, isActive) {
  return <MenuItem className='location-option' onSelect={onSelect} key={itemKey++} active={isActive}>

<i className={`fa fa-${icon}`} />

{title}

  </MenuItem>
}

function createTransitStopOption (stop, onSelect, isActive) {
  return <MenuItem className='location-option' onSelect={onSelect} key={itemKey++} active={isActive}>

{distanceStringImperial(stop.dist, true)}

{stop.name} ({stop.code})

          {(stop.routes || []).map((route, i) => {
            const name = route.shortName || route.longName
            return (
              <span key={`route-${i}`} className='route'>
                {name}

            )
          })}

  </MenuItem>
}

// connect to redux store

const mapStateToProps = (state, ownProps) => {
  return {
    config: state.otp.config,
    location: state.otp.currentQuery[ownProps.type],
    currentPosition: state.otp.location.currentPosition,
    sessionSearches: state.otp.location.sessionSearches,
    nearbyStops: state.otp.location.nearbyStops,
    stopsIndex: state.otp.transitIndex.stops
  }
}

const mapDispatchToProps = (dispatch, ownProps) => {
  return {
    addLocationSearch: (location) => { dispatch(addLocationSearch({ location })) },
    getCurrentPosition: (type) => dispatch(getCurrentPosition(type)),
    setLocation: (type, location) => { dispatch(setLocation({ type, location })) },
    setLocationToCurrent: (type) => { dispatch(setLocationToCurrent({ type })) },
    clearLocation: (type) => { dispatch(clearLocation({ type })) }
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(LocationField)
<|endoftext|>


<commit_before>/* @flow */

import React from "react";
import { toJS } from "mobx";
import { observer } from "mobx-react";
import { Output } from "@nteract/display-area";
import { richestMimetype } from "@nteract/transforms";
import Slider from "react-rangeslider";

import { transforms, displayOrder } from "./transforms";

import type OutputStore from "../../store/output";

const counterStyle = {
  position: "absolute",
  pointerEvents: "none",
  left: "50%",
  top: "50%",
  transform: "translate(-50%, -50%)"
};

const History = observer(({ store }: { store: OutputStore }) => {
  const output = store.outputs[store.index];
  return output ? (

        <Slider
          min={0}
          max={store.outputs.length - 1}
          value={store.index}
          onChange={store.setIndex}
          tooltip={false}
        />

          {store.index + 1}/{store.outputs.length}

      <div
        className="multiline-container native-key-bindings"
        tabIndex="-1"
        style={{
          fontSize: atom.config.get(`Hydrogen.outputAreaFontSize`) || "inherit"
        }}
      >

          <Output
            output={toJS(output)}
            displayOrder={displayOrder}
            transforms={transforms}
            theme="light"
            models={{}}
            expanded
          />

  ) : null;
});

export default History;
<commit_msg>Revert "added div to maintain atom default css for markdown output"<commit_after>/* @flow */

import React from "react";
import { toJS } from "mobx";
import { observer } from "mobx-react";
import { Output } from "@nteract/display-area";
import { richestMimetype } from "@nteract/transforms";
import Slider from "react-rangeslider";

import { transforms, displayOrder } from "./transforms";

import type OutputStore from "../../store/output";

const counterStyle = {
  position: "absolute",
  pointerEvents: "none",
  left: "50%",
  top: "50%",
  transform: "translate(-50%, -50%)"
};

const History = observer(({ store }: { store: OutputStore }) => {
  const output = store.outputs[store.index];
  return output ? (

        <Slider
          min={0}
          max={store.outputs.length - 1}
          value={store.index}
          onChange={store.setIndex}
          tooltip={false}
        />

          {store.index + 1}/{store.outputs.length}

      <div
        className="multiline-container native-key-bindings"
        tabIndex="-1"
        style={{
          fontSize: atom.config.get(`Hydrogen.outputAreaFontSize`) || "inherit"
        }}
      >
        <Output
          output={toJS(output)}
          displayOrder={displayOrder}
          transforms={transforms}
          theme="light"
          models={{}}
          expanded
        />

  ) : null;
});

export default History;
<|endoftext|>


<commit_before>'use strict';

const Loki = require('lokijs'),
    BbPromise = require('bluebird'),
    fs = require("fs-extra"),
    fsn = BbPromise.promisifyAll(require("fs")),
    AzuriteTableResponse = require('./../../model/table/AzuriteTableResponse'),
    TableProxy = require('./../../model/table/TableProxy'),
    EntityProxy = require('./../../model/table/EntityProxy'),
    EntityGenerator = require('./../../model/table/EntityGenerator'),
    Tables = require('./../Constants').TableStorageTables,
    env = require('./../../core/env');

class TableStorageManager {
    constructor() {
    }

    init() {
        this.db = BbPromise.promisifyAll(new Loki(env.azuriteDBPathTable, { autosave: true, autosaveInterval: 5000 }));
        return fsn.statAsync(env.azuriteDBPathTable)
            .then((stat) => {
                return this.db.loadDatabaseAsync({});
            })
            .then((data) => {
                if (!this.db.getCollection(Tables.Tables)) {
                    this.db.addCollection(Tables.Tables);
                }
                return this.db.saveDatabaseAsync();
            })
            .catch((e) => {
                if (e.code === 'ENOENT') {
                    // No DB has been persisted / initialized yet.
                    this.db.addCollection(Tables.Tables);
                    return this.db.saveDatabaseAsync();
                }
                // This should never happen!
                console.error(`Failed to initialize database at "${env.azuriteDBPathTable}"`);
                throw e;
            });
    }

    createTable(request) {
        this.db.addCollection(request.tableName);
        const coll = this.db.getCollection(Tables.Tables);
        const tableEntity = EntityGenerator.generateTable(request.tableName);
        const proxy = new TableProxy(coll.insert(tableEntity));
        return BbPromise.resolve(new AzuriteTableResponse({ proxy: proxy }));
    }

    insertEntity(request) {
        const proxy = this._createOrUpdateEntity(request.tableName, request.payload);
        return BbPromise.resolve(new AzuriteTableResponse({ proxy: proxy }));
    }

    deleteTable(request) {
        const coll = this.db.getCollection(Tables.Tables);
        coll.chain().find({ name: { '$eq': request.tableName } }).remove();
        this.db.removeCollection(request.tableName);
        return BbPromise.resolve(new AzuriteTableResponse({}));
    }

    deleteEntity(request) {
        this._deleteEntity(request.tableName, request.partitionKey, request.rowKey);
        return BbPromise.resolve(new AzuriteTableResponse({}));
    }

    queryTable(request) {
        const coll = this.db.getCollection(Tables.Tables);
        const payload = [];
        if (request.tableName !== undefined) {
            const result = coll.chain()
                .find({ name: request.tableName })
                .limit(request.top)
                .data();
            // there must be a table since we are validating its existence in validation pipeline
            payload.push(new TableProxy(result[0]));
            return BbPromise.resolve(new AzuriteTableResponse({ payload: payload }));
        }

        let result;
        if (request.filter !== undefined) {
            result = coll.chain()
                .where((item) => {
                    return eval(request.filter)
                })
                .limit(request.top)
                .data();
        } else { // Returning all tables
            result = coll.chain()
                .find({})
                .limit(request.top)
                .data();
        }
        for (const table of result) {
            payload.push(new TableProxy(table));
        }
        return BbPromise.resolve(new AzuriteTableResponse({ payload: payload }));
    }

    queryEntities(request) {
        const coll = this.db.getCollection(request.tableName);
        const findExpr = {};
        if (request.partitionKey) {
            findExpr['partitionKey'] = partitionKey;
        }
        if (request.rowKey) {
            findExpr['rowKey'] = rowKey;
        }

        const chain = coll.chain().find(findExpr);
        if (request.filter) {
            chain.where((item) => {
                return eval(request.filter)
            });
        }
        const result = chain.limit(request.top).data();
        const payload = [];
        for (const item of result) {
            payload.push(new EntityProxy(item));
        }

        return BbPromise.resolve(new AzuriteTableResponse({ payload: payload }));
    }

    updateEntity(request) {
        return BbPromise.resolve(new AzuriteTableResponse({}));
    }

    _getTable(name) {
        const coll = this.db.getCollection(Tables.Tables);
        const result = coll.chain()
            .find({ name: name })
            .data();
        return (result.length === 0) ? undefined : new TableProxy(result[0]);
    }

    _deleteEntity(tableName, partitionKey, rowKey) {
        const coll = this.db.getCollection(tableName),
            result = coll.chain()
                .find({
                    '$and':
                        [
                            {
                                partitionKey: { '$eq': partitionKey }
                            },
                            {
                                rowKey: { '$eq': rowKey }
                            }
                        ]
                })
                .remove();
    }

    _getEntity(tableName, partitionKey, rowKey) {
        const coll = this.db.getCollection(tableName);
        if (coll === null) {
            return undefined;
        }
        const result = coll.chain()
            .find({
                '$and':
                    [
                        {
                            partitionKey: { '$eq': partitionKey }
                        },
                        {
                            rowKey: { '$eq': rowKey }
                        }
                    ]
            })
            .data();
        return (result.length === 0) ? undefined : new EntityProxy(result[0]);
    }

    _createOrUpdateEntity(tableName, rawEntity) {
        const coll = this.db.getCollection(tableName),
            entity = EntityGenerator.generateEntity(rawEntity, tableName),
            res = coll.findOne({ partitionKey: entity.partitionKey, rowKey: entity.rowKey });

        if (res !== null) {
            res.partitionKey = entity.partitionKey;
            res.rowKey = entity.rowKey;
            res.attribs = entity.attribs;
            res.odata = entity.odata;
            coll.update(res);
            return new EntityProxy(res);
        }
        const entityProxy = new EntityProxy(coll.insert(entity));
        return entityProxy;
    }
}

module.exports = new TableStorageManager();<commit_msg>implementation #136<commit_after>'use strict';

const Loki = require('lokijs'),
    BbPromise = require('bluebird'),
    fs = require("fs-extra"),
    fsn = BbPromise.promisifyAll(require("fs")),
    AzuriteTableResponse = require('./../../model/table/AzuriteTableResponse'),
    TableProxy = require('./../../model/table/TableProxy'),
    EntityProxy = require('./../../model/table/EntityProxy'),
    EntityGenerator = require('./../../model/table/EntityGenerator'),
    Tables = require('./../Constants').TableStorageTables,
    env = require('./../../core/env');

class TableStorageManager {
    constructor() {
    }

    init() {
        this.db = BbPromise.promisifyAll(new Loki(env.azuriteDBPathTable, { autosave: true, autosaveInterval: 5000 }));
        return fsn.statAsync(env.azuriteDBPathTable)
            .then((stat) => {
                return this.db.loadDatabaseAsync({});
            })
            .then((data) => {
                if (!this.db.getCollection(Tables.Tables)) {
                    this.db.addCollection(Tables.Tables);
                }
                return this.db.saveDatabaseAsync();
            })
            .catch((e) => {
                if (e.code === 'ENOENT') {
                    // No DB has been persisted / initialized yet.
                    this.db.addCollection(Tables.Tables);
                    return this.db.saveDatabaseAsync();
                }
                // This should never happen!
                console.error(`Failed to initialize database at "${env.azuriteDBPathTable}"`);
                throw e;
            });
    }

    createTable(request) {
        this.db.addCollection(request.tableName);
        const coll = this.db.getCollection(Tables.Tables);
        const tableEntity = EntityGenerator.generateTable(request.tableName);
        const proxy = new TableProxy(coll.insert(tableEntity));
        return BbPromise.resolve(new AzuriteTableResponse({ proxy: proxy }));
    }

    insertEntity(request) {
        const proxy = this._createOrUpdateEntity(request.partitionKey, request.rowKey, request.tableName, request.payload);
        return BbPromise.resolve(new AzuriteTableResponse({ proxy: proxy }));
    }

    deleteTable(request) {
        const coll = this.db.getCollection(Tables.Tables);
        coll.chain().find({ name: { '$eq': request.tableName } }).remove();
        this.db.removeCollection(request.tableName);
        return BbPromise.resolve(new AzuriteTableResponse({}));
    }

    deleteEntity(request) {
        this._deleteEntity(request.tableName, request.partitionKey, request.rowKey);
        return BbPromise.resolve(new AzuriteTableResponse({}));
    }

    queryTable(request) {
        const coll = this.db.getCollection(Tables.Tables);
        const payload = [];
        if (request.tableName !== undefined) {
            const result = coll.chain()
                .find({ name: request.tableName })
                .limit(request.top)
                .data();
            // there must be a table since we are validating its existence in validation pipeline
            payload.push(new TableProxy(result[0]));
            return BbPromise.resolve(new AzuriteTableResponse({ payload: payload }));
        }

        let result;
        if (request.filter !== undefined) {
            result = coll.chain()
                .where((item) => {
                    return eval(request.filter)
                })
                .limit(request.top)
                .data();
        } else { // Returning all tables
            result = coll.chain()
                .find({})
                .limit(request.top)
                .data();
        }
        for (const table of result) {
            payload.push(new TableProxy(table));
        }
        return BbPromise.resolve(new AzuriteTableResponse({ payload: payload }));
    }

    queryEntities(request) {
        const coll = this.db.getCollection(request.tableName);
        const findExpr = {};
        if (request.partitionKey) {
            findExpr['partitionKey'] = partitionKey;
        }
        if (request.rowKey) {
            findExpr['rowKey'] = rowKey;
        }

        const chain = coll.chain().find(findExpr);
        if (request.filter) {
            chain.where((item) => {
                return eval(request.filter)
            });
        }
        const result = chain.limit(request.top).data();
        const payload = [];
        for (const item of result) {
            payload.push(new EntityProxy(item));
        }

        return BbPromise.resolve(new AzuriteTableResponse({ payload: payload }));
    }

    updateEntity(request) {
        const proxy = this._createOrUpdateEntity(request.partitionKey, request.rowKey, request.tableName, request.payload);
        return BbPromise.resolve(new AzuriteTableResponse({ proxy: proxy }));
    }

    _getTable(name) {
        const coll = this.db.getCollection(Tables.Tables);
        const result = coll.chain()
            .find({ name: name })
            .data();
        return (result.length === 0) ? undefined : new TableProxy(result[0]);
    }

    _deleteEntity(tableName, partitionKey, rowKey) {
        const coll = this.db.getCollection(tableName),
            result = coll.chain()
                .find({
                    '$and':
                        [
                            {
                                partitionKey: { '$eq': partitionKey }
                            },
                            {
                                rowKey: { '$eq': rowKey }
                            }
                        ]
                })
                .remove();
    }

    _getEntity(tableName, partitionKey, rowKey) {
        const coll = this.db.getCollection(tableName);
        if (coll === null) {
            return undefined;
        }
        const result = coll.chain()
            .find({
                '$and':
                    [
                        {
                            partitionKey: { '$eq': partitionKey }
                        },
                        {
                            rowKey: { '$eq': rowKey }
                        }
                    ]
            })
            .data();
        return (result.length === 0) ? undefined : new EntityProxy(result[0]);
    }

    _createOrUpdateEntity(partitionKey, rowKey, tableName, rawEntity) {
        const coll = this.db.getCollection(tableName),
            entity = EntityGenerator.generateEntity(rawEntity, tableName),
            res = coll.findOne({ partitionKey: partitionKey, rowKey: rowKey });

        if (res !== null) {
            res.attribs = entity.attribs;
            res.odata = entity.odata;
            coll.update(res);
            return new EntityProxy(res);
        }
        const entityProxy = new EntityProxy(coll.insert(entity));
        return entityProxy;
    }
}

module.exports = new TableStorageManager();<|endoftext|>


<commit_before>var wallabyWebpack = require('wallaby-webpack');
var webpackConfig = require('./webpack.config');

module.exports = function (wallaby) {
  webpackConfig.module = {}
  return {
    compilers: {
      "scripts/dash_banner/**/*.js": wallaby.compilers.babel(),
      "spec/**/*.js": wallaby.compilers.babel()
    },

    files: [
      {pattern: "bower_components/underscore/underscore.js", load: true},
      {pattern: "bower_components/jquery/dist/jquery.js", load: true},
      {pattern: "bower_components/backbone/backbone.js", load: true},
      {pattern: "scripts/dash_banner/foo.js", load: true}
    ],

    postprocessor: wallabyWebpack(webpackConfig),

    setup: function () {
      window.__moduleBundler.loadTests();
    },

    tests: [
      {pattern: "spec/dash_banner/foo_spec.js", load: false}
    ]
  };

};
<commit_msg>Fix Wallaby<commit_after>var wallabyWebpack = require('wallaby-webpack');
var webpackConfig = require('./webpack.config');
var path = require("path");

const PROJECT_ROOT = path.resolve(__dirname);

module.exports = function (wallaby) {
  webpackConfig.devtool = "eval-source-map"
  return {
    compilers: {
      "scripts/**/*.js": wallaby.compilers.babel(),
      "spec/**/*.js": wallaby.compilers.babel()
    },

    files: [
      {pattern: "bower_components/jquery/dist/jquery.js", load: true},
      {pattern: "bower_components/jasmine-jquery/lib/jasmine-jquery.js", load: true},
      {pattern: "scripts/**/*.ejs", load: false},
      {pattern: "spec/dash_banner/view_spec.js", ignore: true}
    ],

    postprocessor: wallabyWebpack(webpackConfig),

    setup: function () {
      window.__moduleBundler.loadTests();
    },

    tests: [
      {pattern: "spec/dash_banner/view_spec.js", load: false}
    ]
  };

};
<|endoftext|>


<commit_before>angular.module( 'app' )
.run( function( $rootScope, $timeout ) {
    (function connect() {
        var url = 'ws://localhost:3000'
        var connection = new WebSocket( url )

        connection.onclose = function( e ) {
            console.log(' WebSocket closed. Reconnecting...' )
            $timeout( connect, 10*1000)
        }

        connection.onopen = function() {
            console.log( 'WebSocket connected' )
        }

        connection.onmessage = function( e ) {
            var payload = JSON.parse( e.data )
            $rootScope.$broadcast( 'ws:' + payload.topic, payload.data )
        }
    })()
})<commit_msg>Add dynamic web address for websockets<commit_after>angular.module( 'app' )
.run( function( $rootScope, $window ) {
    (function connect() {
        var url = 'ws://' + $window.location.host
        var connection = new WebSocket( url )

        connection.onclose = function( e ) {
            console.log(' WebSocket closed. Reconnecting...' )
            $timeout( connect, 10*1000)
        }

        connection.onopen = function() {
            console.log( 'WebSocket connected' )
        }

        connection.onmessage = function( e ) {
            var payload = JSON.parse( e.data )
            $rootScope.$broadcast( 'ws:' + payload.topic, payload.data )
        }
    })()
})<|endoftext|>


<commit_before>#!/usr/bin/env node

var _     = require('lodash'),
    fs    = require('fs'),
    util  = require('util'),
    path  = require('path'),
    PEG   = require('pegjs'),
    cli   = require('commander');

cli
  .version('0.0.1')
  .description('Compile your whenever files by passing the path to the file')
  .parse(process.argv);

// Find the files
var file = fs.readFileSync(cli.args[0]);

// Use the grammar to make a parser

var grammar = fs.readFileSync(__dirname + '/lib/grammar.txt').toString(),
    parser  = PEG.buildParser(grammar),
    bag     = parser.parse(file.toString());

// console.log(bag);

// Built-in whenever funcs

var master = {},
    sumStatements = 0,
    addedCurrStatement,
    currStatement;

function getFuncFromString(str) {
  return master[str].fn;
}

function convertPredicate(pred){
  // return _.isString(pred) ? _.includes(workingArr, pred) : pred;
  return _.isString(pred) ? master[pred].numCopies > 0 : pred;
}

function convertFn(fn){
  return _.isString(fn) ? getFuncFromString(fn) : fn;
}

function addCurrStatement(){
  if (!currStatement) {
    return console.log('ERROR: current statement unknown...');
  }

  if (!addedCurrStatement) {
    addedCurrStatement = true;
    master[currStatement].numCopies++;
    sumStatements++;
    // workingArr.push(currStatement);
  }
}

function getStatement(num){

  var keys = Object.keys(master),
      index = 0,
      counter = master[keys[index]].numCopies;

  while (counter < num) {
    counter += master[keys[index]].numCopies;
    index++;
  }

  return master[keys[index]].fn;

}

function add(fnName, times){
  var times = times || 1;
  master[fnName].numCopies += times;
  sumStatements += times;

  // _.times(times, function(){
  //   workingArr.push(fnName);
  // });
}

function remove(fnName, times){
  var times = times || 1;
  master[fnName].numCopies -= times;
  sumStatements -= times;
  // _.times(times, function(){
  //   var idx = _.findIndex(workingArr, function(el){
  //     return el === fnName;
  //   });

  //   workingArr.splice(idx, 1);
  // });
}

function defer(predicate, fn) {
  var fn        = convertFn(fn),
      predicate = convertPredicate(predicate);

  if (!predicate) {
    fn();
  } else {
    addCurrStatement();
  }
}

function again(predicate, fnName){
  var predicate = convertPredicate(predicate);

  if (predicate){
    addCurrStatement();
  }
}

function N(fnName) {
  return master[fnName].numCopies;
}

// Start whenevering!

function deStringifyAndRun(arr) {
  var cleaned = _.pull(arr, 'comment');

  sumStatements = cleaned.length;

  _.forEach(cleaned, function(el){
    eval('var moo = ' + el);
    master[moo.name] = {
      fn: moo,
      timesCalled: 0,
      numCopies: 1
    };
  });

  run();
}

function run() {
  while(sumStatements > 0) {
    console.log('sumStatements', sumStatements)
    var randIndex = Math.floor(Math.random() * sumStatements),
        randFn = getStatement(randIndex);

    console.log('randIndex', randIndex);
    console.log('randFn', randFn.name);

    // globals used during execution
    currStatement = randFn.name;
    addedCurrStatement = false;

    randFn();

    master[currStatement].numCopies--;
    sumStatements--;
    master[currStatement].timesCalled++;
  }

  console.log('FIN: THE BAG IS EMPTY');
}

// ACTION

deStringifyAndRun(bag);
<commit_msg>One more bug left; program runs, nums are off<commit_after>#!/usr/bin/env node

var _     = require('lodash'),
    fs    = require('fs'),
    util  = require('util'),
    path  = require('path'),
    PEG   = require('pegjs'),
    cli   = require('commander');

cli
  .version('0.0.1')
  .description('Compile your whenever files by passing the path to the file')
  .parse(process.argv);

// Find the files
var file = fs.readFileSync(cli.args[0]);

// Use the grammar to make a parser

var grammar = fs.readFileSync(__dirname + '/lib/grammar.txt').toString(),
    parser  = PEG.buildParser(grammar),
    bag     = parser.parse(file.toString());

// console.log(bag);

// Built-in whenever funcs

var master = {},
    sumStatements = 0,
    addedCurrStatement,
    currStatement;

function getFuncFromString(str) {
  return master[str].fn;
}

function convertPredicate(pred){
  return _.isString(pred) ? master[pred].numCopies > 0 : pred;
}

function convertFn(fn){
  return _.isString(fn) ? getFuncFromString(fn) : fn;
}

function addCurrStatement(){
  if (!currStatement) {
    return console.log('ERROR: current statement unknown...');
  }

  if (!addedCurrStatement) {
    addedCurrStatement = true;
    master[currStatement].numCopies++;
    sumStatements++;
  }
}

function getStatement(num){

  var keys = Object.keys(master),
      index = 0,
      counter = master[keys[index]].numCopies;

  while (counter <= num) {
    index++;
    counter += master[keys[index]].numCopies;
  }

  return master[keys[index]].fn;

}

function add(fnName, times){
  var times = times || 1;
  master[fnName].numCopies += times;
  sumStatements += times;
}

function remove(fnName, times){
  var times = times || 1;
  master[fnName].numCopies -= times;
  sumStatements -= times;
}

function defer(predicate, fn) {
  var fn        = convertFn(fn),
      predicate = convertPredicate(predicate);

  if (!predicate) {
    fn();
  } else {
    addCurrStatement();
  }
}

function again(predicate, fnName){
  var predicate = convertPredicate(predicate);

  if (predicate){
    addCurrStatement();
  }
}

function N(fnName) {
  return master[fnName].numCopies;
}

// Start whenevering!

function deStringifyAndRun(arr) {
  var cleaned = _.pull(arr, 'comment');

  sumStatements = cleaned.length;

  _.forEach(cleaned, function(el){
    eval('var moo = ' + el);
    master[moo.name] = {
      fn: moo,
      timesCalled: 0,
      numCopies: 1
    };
  });

  run();
}

function run() {
  while(sumStatements > 0) {

    var randIndex = Math.floor(Math.random() * sumStatements),
        randFn = getStatement(randIndex);

    // globals used during execution
    currStatement = randFn.name;
    addedCurrStatement = false;

    randFn();

    // update counting properties
    master[currStatement].numCopies--;
    sumStatements--;
    master[currStatement].timesCalled++;

  }

  console.log('FIN: THE BAG IS EMPTY');
}

// ACTION

deStringifyAndRun(bag);
<|endoftext|>


<commit_before>// @flow

import fs from 'fs';
import os from 'os';
import path from 'path';

import FileUtils from '../FileUtils';

import findPackageDependencies from '../findPackageDependencies';

const coreModules = [
  'meteor/accounts-base',
  'meteor/blaze',
  'meteor/check',
  'meteor/ddp-client',
  'meteor/ddp-rate-limiter',
  'meteor/ejson',
  'meteor/email',
  'meteor/http',
  'meteor/check',
  'meteor/meteor',
  'meteor/mongo',
  'meteor/random',
  'meteor/reactive-var',
  'meteor/session',
  'meteor/templating',
  'meteor/tracker',
];

const coreNamedExports = {
  'meteor/accounts-base': ['AccountsClient', 'Accounts', 'AccountsServer'],
  'meteor/blaze': ['Blaze'],
  'meteor/check': ['check', 'Match'],
  'meteor/ddp-client': ['DDP'],
  'meteor/ddp-rate-limiter': ['DDPRateLimiter'],
  'meteor/ejson': ['EJSON'],
  'meteor/email': ['Email'],
  'meteor/http': ['HTTP'],
  'meteor/meteor': ['Meteor'],
  'meteor/mongo': ['Mongo'],
  'meteor/random': ['Random'],
  'meteor/reactive-var': ['ReactiveVar'],
  'meteor/session': ['Session'],
  'meteor/templating': ['Template'],
  'meteor/tracker': ['Tracker'],
};

function meteorPackageDependencies(
  { config }: Object
): Array<string> {
  const meteorPackagesPath = path.join(config.workingDirectory, '.meteor/packages');
  if (!fs.existsSync(meteorPackagesPath)) {
    return [];
  }
  // Meteor is an "app" framework. As such, it has both apps and packages. When working with a
  // module that is part of an app, the list of Meteor packages that a module may import is found in
  // '.meteor/packages'. This file is actually called a ProjectConstraintsFile in the meteor code.
  // The internal meteor routine that parses it may be found at
  // https://github.com/meteor/meteor/blob/f8b1bba606678303ea4e82b8da7c8ae0683b0b7c/tools/project-context.js#L841.
  // After reverse engineering ProjectConstraintsFile.prototype._readfile at that location, the
  // following appears to be the pertinent facts for any parser.
  //  - the only true information within the file is a list of constraints
  //  - a constraint may not span a line
  //  - only one constraint may appear on a line
  //  - a constraint consists of a package name and an optional version constraint separated by
  //    the '@' symbol
  //  - white space may not appear within a constraint
  //  - the '#' symbol signifies that the rest of the line is a comment
  //  - all white space is ignored
  //  - package names
  //      - are allowed to contain [a-z0-9:.\-]
  //      - must have at least one lowercase letter
  //      - may not begin or end with a dot or colon
  //      - may not begin with a hyphen
  //      - may not contain two consecutive dots
  //
  // This routine is only interested in extracting the package names and has no concern for
  // precisely validating them. An assumption is made that the file is basically valid.
  // Thus, they may be extracted with a simple global, multiline match of characters allowed
  // to be in a package name that are at the beginning of a line, possibly following white space.
  const packages: Array<string> = (fs.readFileSync(meteorPackagesPath, 'utf8')
    // extract an array of package names (possibly with preceding whitespace) from the packages file
    .match(/^\s*[a-z0-9:.\-]+/gm) || [])
    // add 'meteor/' to the start of each name per Meteor convention
    .map((pkg: string): string => `meteor/${pkg.trimLeft()}`)
    // eliminate those packages that are considered to be core
    .filter((pkg: string): boolean => coreModules.indexOf(pkg) === -1);
  return packages;
}

function meteorPackageVersions(projectRootDir: string): ?Map<string, string> {
  // This function processes all of the package versions found in .meteor/versions and returns
  // a map containing them.

  const meteorVersionsPath = path.join(projectRootDir, '.meteor/versions');
  if (!fs.existsSync(meteorVersionsPath)) {
    // If we're even in an application directory, it must be broken. In any case, we can't
    // find the packages without their versions. Return null to indicate that an issue
    // occurred as opposed to processing an empty .meteor/versions.
    return null;
  }
  const pkgVersions = new Map();
  const pkgVersionPairs: Array<string> = fs.readFileSync(meteorVersionsPath, 'utf8')
    .match(/^[^@\s]+@[^\s]+$/gm) || [];
  for (const pkgVersionPair of pkgVersionPairs) {
    const [pkg, version] = pkgVersionPair.split('@');
    pkgVersions.set(pkg, version);
  }
  return pkgVersions;
}

function extractExportsFromMeteorPackage(
  projectRootDir: string,
  pkg: string,
  pkgVersion: string
): ?Array<string> {
  // This function extracts the named exports from the package specified by pkg and pkgVersion
  // and returns them as an array of strings. If null is returned, a problem was encountered
  // in processing. If an empty array is returned, no named exports were found.

  // Meteor packages are deployed as "isopacks". These can usually be found within the meteor
  // warehouse directory on the system at ~/.meteor/packages. isopacks for local Meteor packages,
  // i.e. those whose source is in the <project-root>/packages directory, can usually be found in
  // at <project-root>/.meteor/local/isopacks.
  //
  // These isopacks do not exactly contain the original package source. In particular, they are
  // always missing a "package.js" file. This is important to us because it is the one that
  // specifies the interface. They do however contain build products that resulted from that
  // specification.
  //
  // Meteor is an isomorphic environment. An isopack contains multiple builds, one for each
  // targeted platform. Typically, this will include at least a client and a server build.  An
  // isopack contains an isopack.json file that details which platforms are represented and
  // leads us to other platform specific isopack.json files that contain the interface details
  // needed by this routine.
  //
  // This routine will find all interfaces from all build platforms and create namedExport
  // entries for them.
  //
  // So, for example, if a project includes version 1.5.3 of package "aldeed:simple-schema", the
  // isopack for that package will usually be located at
  //    ~/.meteor/packages/aldeed_simple-schema/1.5.3/
  // Within that directory, we will find the following important files
  //
  //    isopack.json
  //    os.json
  //    web.browser.json
  //    web.cordova.json
  //
  // isopack.json will point us to the others which all represent specific platforms.
  //
  // There are two ways that meteor packages expose their exported interface, pre-ES6 and post-ES6.
  //
  // The pre-ES6 modules method is to call api.exports within the package.js file for each variable
  // exported. These calls all result in declaredExports entries in the individual platform's json
  // files. Within aldeed:simple-schema's web.browser.json for example, we find
  //
  //  "declaredExports": [
  //      {
  //        "name": "SimpleSchema",
  //        "testOnly": false
  //      },
  //      {
  //        "name": "MongoObject",
  //        "testOnly": false
  //      },
  //      {
  //        "name": "humanize",
  //        "testOnly": true
  //      }
  //    ],
  //
  // From this, we can determine that the namedExports entry should be
  //
  //   'aldeed:simple-schema': ['SimpleSchema', 'MongoObject']
  //
  // "humanize" was left off because it is testOnly. We may try to get smarter and include that for
  // some modules in the future.
  //
  // Post-ES6 modules, things become a bit more difficult. The package.js file in a post-ES6 world
  // specifies a mainModule. Each build will have no more than one mainModule and they may be
  // different. The mainModule's are denoted in the same build json files that we process to find
  // the pre-ES6 declaredExports. They are in the resources section which looks like
  //
  //   "resources": [
  //       {
  //         "type": "source",
  //         "extension": "js",
  //         "file": "web.browser/client_main.js",
  //         "length": 752,
  //         "offset": 0,
  //         "path": "client_main.js",
  //         "hash": "cf1eeaf24f21f7755a7cb3fe1e247d56b5e97acf",
  //         "fileOptions": {
  //           "mainModule": true
  //         }
  //       },
  //
  // We are interested only in the one resource that has fileOptions.mainModule = true.
  //
  // The exports are defined by the export statements in the build's mainModule file,
  // "web.browser/client_main.js" in the above example. So, we'll have to parse the file and
  // identify the exports. If the file uses ES6 module export syntax, we should be able to
  // achieve this. If its using CommonJS, we may have to punt and let the user define their
  // own namedExports for that package.

  // The isopack's for 3rd party packages are usually found at
  //   ~/.meteor/packages/<pkg>/<pkgVersion>
  // where pkg is the meteorPkg without the 'meteor/' prefix and colons are replaced with
  //       underlines.
  let isopackRoot = path.join(os.homedir(), '.meteor/packages', pkg.replace(':', '_'), pkgVersion);
  let isopackPath = path.join(isopackRoot, 'isopack.json');
  let isopack = FileUtils.readJsonFile(isopackPath);
  if (!isopack) {
    // It is possible that this is a local package as opposed to a 3rd party package. If so, it's
    // isopack may be in <project-root>/.meteor/isopacks/<pkg>.
    isopackRoot = path.join(
      projectRootDir, '.meteor', 'local', 'isopacks', pkg.replace(':', '_'));
    isopackPath = path.join(isopackRoot, 'isopack.json');
    isopack = FileUtils.readJsonFile(isopackPath);
    if (!isopack) {
      // Can't get anywhere without the main isopack.json.
      return null;
    }
  }

  // isopack.json often contains separate sections for every version of the isopacks that
  // has ever been made available. We're only interested in isopack-2 or isopack-1 at this
  // time. Prefer the newer isopack-2 specification if available
  const isopackVer = isopack['isopack-2'] || isopack['isopack-1'];
  if (!isopackVer || !isopackVer.builds) {
    // If we didn't find an isopack version we understand or documented builds within it,
    // we're done.
    return null;
  }
  // We can't guess which build the current module is being included in. So, we'll find all
  // declaredExports from all builds and combine them into one namedExports specification.
  const declaredExports: Array<string> = [];
  for (const build of isopackVer.builds) {
    const buildIsopackPath = path.join(isopackRoot, build.path);
    const buildIsopack = FileUtils.readJsonFile(buildIsopackPath);
    if (!buildIsopack || !buildIsopack.declaredExports) {
      // This build is missing, corrupted, or has no declaredExports. Try the next one.
      continue;
    }
    for (const declaredExport of buildIsopack.declaredExports) {
      // If this declaredExport is not a "testOnly" export and is not already amongst those we've
      // gathered, push it into our collection.
      if (!declaredExport.testOnly && declaredExports.indexOf(declaredExport.name) === -1) {
        declaredExports.push(declaredExport.name);
      }
    }

    // TODO: If the "resources" section of the buildIsopack specifies a mainModule, we need to
    // attempt to scan it to find exports.
  }
  return declaredExports;
}

function meteorPackageNamedExports({ config }: Object): Object {
  // This function seeks to extract the named exports from all non-core, 3rd party or local meteor
  // packages being utilized in the application. The meteorPackageDependencies function identifies
  // that list of packages.

  // Retrieve the versions of all Meteor packages. Note that local packages aren't absolutely
  // required to have versions.
  const pkgVersions = meteorPackageVersions(config.workingDirectory) || new Map();

  // Try to identify the exports of all packages identified by meteorPackageDependencies
  const namedExports = {};
  for (const meteorPkg of meteorPackageDependencies({ config })) {
    const pkg = meteorPkg.slice(7);
    const pkgVersion = pkgVersions.get(pkg) || '';
    const extractedExports = extractExportsFromMeteorPackage(
      config.workingDirectory, pkg, pkgVersion);

    // If we found declared exports, create a namedExports entry for them.
    if (extractedExports && extractedExports.length) {
      namedExports[meteorPkg] = extractedExports;
    }
  }
  return namedExports;
}

export default {
  coreModules,

  moduleNameFormatter: ({ moduleName, pathToImportedModule }: Object): string => {
    // If the module being imported is a Meteor package, it will begin with 'meteor/' and should
    // not be altered.
    if (moduleName.startsWith('meteor/')) {
      return moduleName;
    }
    // If the module being imported is an npm package, the path to the module will start with
    // 'node_modules/' and the moduleName should not be altered.
    // not be altered.
    if (pathToImportedModule.startsWith('node_modules/')) {
      return moduleName;
    }
    // If the moduleName does not start with a '.', then import-js is trying to reference it via
    // an absolute path. In this case, Meteor wants it to start with a '/' and will interpret it
    // as relative to the project directory root.
    if (!moduleName.startsWith('.')) {
      return `/${moduleName}`;
    }
    // Otherwise, return the moduleName unchanged.
    return moduleName;
  },

  moduleSideEffectImports: ({ pathToCurrentFile, config }: Object): Array<string> => {
    let basePath = '';
    if (pathToCurrentFile.endsWith('.js')) {
      basePath = pathToCurrentFile.slice(0, -3);
    } else if (pathToCurrentFile.endsWith('.jsx')) {
      basePath = pathToCurrentFile.slice(0, -4);
    } else {
      return [];
    }
    const moduleSpecifiers = [];
    ['.html', '.css'].forEach((ext: string) => {
      const moduleSpecifier = `${basePath}${ext}`;
      if (fs.existsSync(path.join(config.workingDirectory, moduleSpecifier))) {
        if (config.get('useRelativePaths')) {
          moduleSpecifiers.push('.'.concat(path.sep, path.basename(moduleSpecifier)));
        } else {
          // Strip the leading '.' off of the moduleSpecifier to turn it into a Meteor compliant
          // absolute path.
          moduleSpecifiers.push(moduleSpecifier.slice(1));
        }
      }
    });
    return moduleSpecifiers;
  },

  namedExports: ({ config }: Object): Object => {
    const allNamedExports = coreNamedExports;
    // There are no worries about this overwriting the definitions of core
    // namedExports. meteorPackageNamedExports skips core packages. Even if
    // it did not, it would presumably find correct definitions.
    Object.assign(allNamedExports, meteorPackageNamedExports({ config }));
    return allNamedExports;
  },

  packageDependencies: ({ config }: Object): Array<string> =>
    meteorPackageDependencies({ config })
      // add NPM packages to the list
      .concat(findPackageDependencies(
        config.workingDirectory,
        config.get('importDevDependencies'))),
};
<commit_msg>Reformat some comments in meteorEnvironment.js<commit_after>// @flow

import fs from 'fs';
import os from 'os';
import path from 'path';

import FileUtils from '../FileUtils';

import findPackageDependencies from '../findPackageDependencies';

const coreModules = [
  'meteor/accounts-base',
  'meteor/blaze',
  'meteor/check',
  'meteor/ddp-client',
  'meteor/ddp-rate-limiter',
  'meteor/ejson',
  'meteor/email',
  'meteor/http',
  'meteor/check',
  'meteor/meteor',
  'meteor/mongo',
  'meteor/random',
  'meteor/reactive-var',
  'meteor/session',
  'meteor/templating',
  'meteor/tracker',
];

const coreNamedExports = {
  'meteor/accounts-base': ['AccountsClient', 'Accounts', 'AccountsServer'],
  'meteor/blaze': ['Blaze'],
  'meteor/check': ['check', 'Match'],
  'meteor/ddp-client': ['DDP'],
  'meteor/ddp-rate-limiter': ['DDPRateLimiter'],
  'meteor/ejson': ['EJSON'],
  'meteor/email': ['Email'],
  'meteor/http': ['HTTP'],
  'meteor/meteor': ['Meteor'],
  'meteor/mongo': ['Mongo'],
  'meteor/random': ['Random'],
  'meteor/reactive-var': ['ReactiveVar'],
  'meteor/session': ['Session'],
  'meteor/templating': ['Template'],
  'meteor/tracker': ['Tracker'],
};

function meteorPackageDependencies(
  { config }: Object
): Array<string> {
  const meteorPackagesPath = path.join(config.workingDirectory, '.meteor/packages');

  if (!fs.existsSync(meteorPackagesPath)) {
    return [];
  }

  // Meteor is an "app" framework. As such, it has both apps and packages. When
  // working with a module that is part of an app, the list of Meteor packages
  // that a module may import is found in '.meteor/packages'. This file is
  // actually called a ProjectConstraintsFile in the meteor code.
  // The internal meteor routine that parses it may be found at
  // https://github.com/meteor/meteor/blob/f8b1bba6/tools/project-context.js#L841.
  //
  // After reverse engineering ProjectConstraintsFile.prototype._readfile at
  // that location, the following appears to be the pertinent facts for any
  // parser.
  //
  //  - the only true information within the file is a list of constraints
  //  - a constraint may not span a line
  //  - only one constraint may appear on a line
  //  - a constraint consists of a package name and an optional version
  //    constraint separated by the '@' symbol
  //  - white space may not appear within a constraint
  //  - the '#' symbol signifies that the rest of the line is a comment
  //  - all white space is ignored
  //  - package names
  //      - are allowed to contain [a-z0-9:.\-]
  //      - must have at least one lowercase letter
  //      - may not begin or end with a dot or colon
  //      - may not begin with a hyphen
  //      - may not contain two consecutive dots
  //
  // This routine is only interested in extracting the package names and has no
  // concern for precisely validating them. An assumption is made that the file
  // is basically valid. Thus, they may be extracted with a simple global,
  // multiline match of characters allowed to be in a package name that are at
  // the beginning of a line, possibly following white space.
  const packages: Array<string> = (fs.readFileSync(meteorPackagesPath, 'utf8')
    // extract an array of package names (possibly with preceding whitespace)
    // from the packages file
    .match(/^\s*[a-z0-9:.\-]+/gm) || [])
    // add 'meteor/' to the start of each name per Meteor convention
    .map((pkg: string): string => `meteor/${pkg.trimLeft()}`)
    // eliminate those packages that are considered to be core
    .filter((pkg: string): boolean => coreModules.indexOf(pkg) === -1);
  return packages;
}

function meteorPackageVersions(projectRootDir: string): ?Map<string, string> {
  // This function processes all of the package versions found in
  // .meteor/versions and returns a map containing them.

  const meteorVersionsPath = path.join(projectRootDir, '.meteor/versions');
  if (!fs.existsSync(meteorVersionsPath)) {
    // If we're even in an application directory, it must be broken. In any
    // case, we can't find the packages without their versions. Return null to
    // indicate that an issue occurred as opposed to processing an empty
    // .meteor/versions.
    return null;
  }

  const pkgVersions = new Map();
  const pkgVersionPairs: Array<string> = fs.readFileSync(meteorVersionsPath, 'utf8')
    .match(/^[^@\s]+@[^\s]+$/gm) || [];
  for (const pkgVersionPair of pkgVersionPairs) {
    const [pkg, version] = pkgVersionPair.split('@');
    pkgVersions.set(pkg, version);
  }
  return pkgVersions;
}

function extractExportsFromMeteorPackage(
  projectRootDir: string,
  pkg: string,
  pkgVersion: string
): ?Array<string> {
  // This function extracts the named exports from the package specified by pkg
  // and pkgVersion and returns them as an array of strings. If null is
  // returned, a problem was encountered in processing. If an empty array is
  // returned, no named exports were found.

  // Meteor packages are deployed as "isopacks". These can usually be found
  // within the meteor warehouse directory on the system at ~/.meteor/packages.
  // isopacks for local Meteor packages, i.e. those whose source is in the
  // <project-root>/packages directory, can usually be found in at
  // <project-root>/.meteor/local/isopacks.
  //
  // These isopacks do not exactly contain the original package source. In
  // particular, they are always missing a "package.js" file. This is important
  // to us because it is the one that specifies the interface. They do however
  // contain build products that resulted from that specification.
  //
  // Meteor is an isomorphic environment. An isopack contains multiple builds,
  // one for each targeted platform. Typically, this will include at least a
  // client and a server build.  An isopack contains an isopack.json file that
  // details which platforms are represented and leads us to other platform
  // specific isopack.json files that contain the interface details needed by
  // this routine.
  //
  // This routine will find all interfaces from all build platforms and create
  // namedExport entries for them.
  //
  // So, for example, if a project includes version 1.5.3 of package
  // "aldeed:simple-schema", the isopack for that package will usually be
  // located at
  //    ~/.meteor/packages/aldeed_simple-schema/1.5.3/
  // Within that directory, we will find the following important files
  //
  //    isopack.json
  //    os.json
  //    web.browser.json
  //    web.cordova.json
  //
  // isopack.json will point us to the others which all represent specific
  // platforms.
  //
  // There are two ways that meteor packages expose their exported interface,
  // pre-ES6 and post-ES6.
  //
  // The pre-ES6 modules method is to call api.exports within the package.js
  // file for each variable exported. These calls all result in declaredExports
  // entries in the individual platform's json files. Within
  // aldeed:simple-schema's web.browser.json for example, we find
  //
  //  "declaredExports": [
  //      {
  //        "name": "SimpleSchema",
  //        "testOnly": false
  //      },
  //      {
  //        "name": "MongoObject",
  //        "testOnly": false
  //      },
  //      {
  //        "name": "humanize",
  //        "testOnly": true
  //      }
  //    ],
  //
  // From this, we can determine that the namedExports entry should be
  //
  //   'aldeed:simple-schema': ['SimpleSchema', 'MongoObject']
  //
  // "humanize" was left off because it is testOnly. We may try to get smarter
  // and include that for some modules in the future.
  //
  // Post-ES6 modules, things become a bit more difficult. The package.js file
  // in a post-ES6 world specifies a mainModule. Each build will have no more
  // than one mainModule and they may be different. The mainModule's are denoted
  // in the same build json files that we process to find the pre-ES6
  // declaredExports. They are in the resources section which looks like
  //
  //   "resources": [
  //       {
  //         "type": "source",
  //         "extension": "js",
  //         "file": "web.browser/client_main.js",
  //         "length": 752,
  //         "offset": 0,
  //         "path": "client_main.js",
  //         "hash": "cf1eeaf24f21f7755a7cb3fe1e247d56b5e97acf",
  //         "fileOptions": {
  //           "mainModule": true
  //         }
  //       },
  //
  // We are interested only in the one resource that has fileOptions.mainModule
  // = true.
  //
  // The exports are defined by the export statements in the build's mainModule
  // file, "web.browser/client_main.js" in the above example. So, we'll have to
  // parse the file and identify the exports. If the file uses ES6 module export
  // syntax, we should be able to achieve this. If its using CommonJS, we may
  // have to punt and let the user define their own namedExports for that
  // package.

  // The isopack's for 3rd party packages are usually found at
  //   ~/.meteor/packages/<pkg>/<pkgVersion>
  // where pkg is the meteorPkg without the 'meteor/' prefix and colons are
  // replaced with underlines.
  let isopackRoot = path.join(os.homedir(), '.meteor/packages', pkg.replace(':', '_'), pkgVersion);
  let isopackPath = path.join(isopackRoot, 'isopack.json');
  let isopack = FileUtils.readJsonFile(isopackPath);

  if (!isopack) {
    // It is possible that this is a local package as opposed to a 3rd party
    // package. If so, it's isopack may be in
    // <project-root>/.meteor/isopacks/<pkg>.
    isopackRoot = path.join(
      projectRootDir, '.meteor', 'local', 'isopacks', pkg.replace(':', '_'));
    isopackPath = path.join(isopackRoot, 'isopack.json');
    isopack = FileUtils.readJsonFile(isopackPath);

    if (!isopack) {
      // Can't get anywhere without the main isopack.json.
      return null;
    }
  }

  // isopack.json often contains separate sections for every version of the
  // isopacks that has ever been made available. We're only interested in
  // isopack-2 or isopack-1 at this time. Prefer the newer isopack-2
  // specification if available
  const isopackVer = isopack['isopack-2'] || isopack['isopack-1'];
  if (!isopackVer || !isopackVer.builds) {
    // If we didn't find an isopack version we understand or documented builds
    // within it, we're done.
    return null;
  }

  // We can't guess which build the current module is being included in. So,
  // we'll find all declaredExports from all builds and combine them into one
  // namedExports specification.
  const declaredExports: Array<string> = [];
  for (const build of isopackVer.builds) {
    const buildIsopackPath = path.join(isopackRoot, build.path);
    const buildIsopack = FileUtils.readJsonFile(buildIsopackPath);

    if (!buildIsopack || !buildIsopack.declaredExports) {
      // This build is missing, corrupted, or has no declaredExports. Try the
      // next one.
      continue;
    }
    for (const declaredExport of buildIsopack.declaredExports) {
      // If this declaredExport is not a "testOnly" export and is not already
      // amongst those we've gathered, push it into our collection.
      if (!declaredExport.testOnly && declaredExports.indexOf(declaredExport.name) === -1) {
        declaredExports.push(declaredExport.name);
      }
    }

    // TODO: If the "resources" section of the buildIsopack specifies a
    // mainModule, we need to attempt to scan it to find exports.
  }
  return declaredExports;
}

function meteorPackageNamedExports({ config }: Object): Object {
  // This function seeks to extract the named exports from all non-core, 3rd
  // party or local meteor packages being utilized in the application. The
  // meteorPackageDependencies function identifies that list of packages.

  // Retrieve the versions of all Meteor packages. Note that local packages
  // aren't absolutely required to have versions.
  const pkgVersions = meteorPackageVersions(config.workingDirectory) || new Map();

  // Try to identify the exports of all packages identified by
  // meteorPackageDependencies
  const namedExports = {};
  for (const meteorPkg of meteorPackageDependencies({ config })) {
    const pkg = meteorPkg.slice(7);
    const pkgVersion = pkgVersions.get(pkg) || '';
    const extractedExports = extractExportsFromMeteorPackage(
      config.workingDirectory, pkg, pkgVersion);

    // If we found declared exports, create a namedExports entry for them.
    if (extractedExports && extractedExports.length) {
      namedExports[meteorPkg] = extractedExports;
    }
  }
  return namedExports;
}

export default {
  coreModules,

  moduleNameFormatter: ({ moduleName, pathToImportedModule }: Object): string => {
    // If the module being imported is a Meteor package, it will begin with
    // 'meteor/' and should not be altered.
    if (moduleName.startsWith('meteor/')) {
      return moduleName;
    }
    // If the module being imported is an npm package, the path to the module
    // will start with 'node_modules/' and the moduleName should not be altered.
    // not be altered.
    if (pathToImportedModule.startsWith('node_modules/')) {
      return moduleName;
    }
    // If the moduleName does not start with a '.', then import-js is trying to
    // reference it via an absolute path. In this case, Meteor wants it to start
    // with a '/' and will interpret it as relative to the project directory
    // root.
    if (!moduleName.startsWith('.')) {
      return `/${moduleName}`;
    }
    // Otherwise, return the moduleName unchanged.
    return moduleName;
  },

  moduleSideEffectImports: ({ pathToCurrentFile, config }: Object): Array<string> => {
    let basePath = '';
    if (pathToCurrentFile.endsWith('.js')) {
      basePath = pathToCurrentFile.slice(0, -3);
    } else if (pathToCurrentFile.endsWith('.jsx')) {
      basePath = pathToCurrentFile.slice(0, -4);
    } else {
      return [];
    }
    const moduleSpecifiers = [];
    ['.html', '.css'].forEach((ext: string) => {
      const moduleSpecifier = `${basePath}${ext}`;
      if (fs.existsSync(path.join(config.workingDirectory, moduleSpecifier))) {
        if (config.get('useRelativePaths')) {
          moduleSpecifiers.push('.'.concat(path.sep, path.basename(moduleSpecifier)));
        } else {
          // Strip the leading '.' off of the moduleSpecifier to turn it into a
          // Meteor compliant absolute path.
          moduleSpecifiers.push(moduleSpecifier.slice(1));
        }
      }
    });
    return moduleSpecifiers;
  },

  namedExports: ({ config }: Object): Object => {
    const allNamedExports = coreNamedExports;
    // There are no worries about this overwriting the definitions of core
    // namedExports. meteorPackageNamedExports skips core packages. Even if it
    // did not, it would presumably find correct definitions.
    Object.assign(allNamedExports, meteorPackageNamedExports({ config }));
    return allNamedExports;
  },

  packageDependencies: ({ config }: Object): Array<string> =>
    meteorPackageDependencies({ config })
      // add NPM packages to the list
      .concat(findPackageDependencies(
        config.workingDirectory,
        config.get('importDevDependencies'))),
};
<|endoftext|>


<commit_before>var httpNative   = require('http'),
    httpsNative  = require('https'),
    web_o  = require('./web-outgoing'),
    common = require('../common'),
    followRedirects = require('follow-redirects');

web_o = Object.keys(web_o).map(function(pass) {
  return web_o[pass];
});

var nativeAgents = { http: httpNative, https: httpsNative };

/*!
 * Array of passes.
 *
 * A `pass` is just a function that is executed on `req, res, options`
 * so that you can easily add new checks while still keeping the base
 * flexible.
 */

module.exports = {

  /**
   * Sets `content-length` to '0' if request is of DELETE type.
   *
   * @param {ClientRequest} Req Request object
   * @param {IncomingMessage} Res Response object
   * @param {Object} Options Config object passed to the proxy
   *
   * @api private
   */

  deleteLength: function deleteLength(req, res, options) {
    if((req.method === 'DELETE' || req.method === 'OPTIONS')
       && !req.headers['content-length']) {
      req.headers['content-length'] = '0';
      delete req.headers['transfer-encoding'];
    }
  },

  /**
   * Sets timeout in request socket if it was specified in options.
   *
   * @param {ClientRequest} Req Request object
   * @param {IncomingMessage} Res Response object
   * @param {Object} Options Config object passed to the proxy
   *
   * @api private
   */

  timeout: function timeout(req, res, options) {
    if(options.timeout) {
      req.socket.setTimeout(options.timeout);
    }
  },

  /**
   * Sets `x-forwarded-*` headers if specified in config.
   *
   * @param {ClientRequest} Req Request object
   * @param {IncomingMessage} Res Response object
   * @param {Object} Options Config object passed to the proxy
   *
   * @api private
   */

  XHeaders: function XHeaders(req, res, options) {
    if(!options.xfwd) return;

    var encrypted = req.isSpdy || common.hasEncryptedConnection(req);
    var values = {
      for  : req.connection.remoteAddress || req.socket.remoteAddress,
      port : common.getPort(req),
      proto: encrypted ? 'https' : 'http'
    };

    ['for', 'port', 'proto'].forEach(function(header) {
      req.headers['x-forwarded-' + header] =
        (req.headers['x-forwarded-' + header] || '') +
        (req.headers['x-forwarded-' + header] ? ',' : '') +
        values[header];
    });

    req.headers['x-forwarded-host'] = req.headers['host'] || '';
  },

  /**
   * Does the actual proxying. If `forward` is enabled fires up
   * a ForwardStream, same happens for ProxyStream. The request
   * just dies otherwise.
   *
   * @param {ClientRequest} Req Request object
   * @param {IncomingMessage} Res Response object
   * @param {Object} Options Config object passed to the proxy
   *
   * @api private
   */

  stream: function stream(req, res, options, _, server, clb) {

    // And we begin!
    server.emit('start', req, res, options.target || options.forward);

    var agents = options.followRedirects ? followRedirects : nativeAgents;
    var http = agents.http;
    var https = agents.https;

    if(options.forward) {
      // If forward enable, so just pipe the request
      var forwardReq = (options.forward.protocol === 'https:' ? https : http).request(
        common.setupOutgoing(options.ssl || {}, options, req, 'forward')
      );

      // error handler (e.g. ECONNRESET, ECONNREFUSED)
      // Handle errors on incoming request as well as it makes sense to
      var forwardError = createErrorHandler(forwardReq, options.forward);
      req.on('error', forwardError);
      forwardReq.on('error', forwardError);

      (options.buffer || req).pipe(forwardReq);
      if(!options.target) { return res.end(); }
    }

    // Request initalization
    var proxyReq = (options.target.protocol === 'https:' ? https : http).request(
      common.setupOutgoing(options.ssl || {}, options, req)
    );

    // Enable developers to modify the proxyReq before headers are sent
    proxyReq.on('socket', function(socket) {
      if(server) { server.emit('proxyReq', proxyReq, req, res, options); }
    });

    // allow outgoing socket to timeout so that we could
    // show an error page at the initial request
    if(options.proxyTimeout) {
      proxyReq.setTimeout(options.proxyTimeout, function() {
         proxyReq.abort();
      });
    }

    // Ensure we abort proxy if request is aborted
    req.on('aborted', function () {
      proxyReq.abort();
    });

    // handle errors in proxy and incoming request, just like for forward proxy
    var proxyError = createErrorHandler(proxyReq, options.target);
    req.on('error', proxyError);
    proxyReq.on('error', proxyError);

    function createErrorHandler(proxyReq, url) {
      return function proxyError(err) {
        if (req.socket.destroyed && err.code === 'ECONNRESET') {
          server.emit('econnreset', err, req, res, url);
          return proxyReq.abort();
        }

        if (clb) {
          clb(err, req, res, url);
        } else {
          server.emit('error', err, req, res, url);
        }
      }
    }

    (options.buffer || req).pipe(proxyReq);

    proxyReq.on('response', function(proxyRes) {
      if(server) { server.emit('proxyRes', proxyRes, req, res); }

      if(!res.headersSent && !options.selfHandleResponse) {
        for(var i=0; i < web_o.length; i++) {
          if(web_o[i](req, res, proxyRes, options)) { break; }
        }
      }

      if (!res.finished) {
        // Allow us to listen when the proxy has completed
        proxyRes.on('end', function () {
          if (server) server.emit('end', req, res, proxyRes);
        });
        // We pipe to the response unless its expected to be handled by the user
        if (!options.selfHandleResponse) proxyRes.pipe(res);
      } else {
        if (server) server.emit('end', req, res, proxyRes);
      }
    });
  }

};
<commit_msg>x-forwarded-host overwrite for mutli level proxies (#1267)<commit_after>var httpNative   = require('http'),
    httpsNative  = require('https'),
    web_o  = require('./web-outgoing'),
    common = require('../common'),
    followRedirects = require('follow-redirects');

web_o = Object.keys(web_o).map(function(pass) {
  return web_o[pass];
});

var nativeAgents = { http: httpNative, https: httpsNative };

/*!
 * Array of passes.
 *
 * A `pass` is just a function that is executed on `req, res, options`
 * so that you can easily add new checks while still keeping the base
 * flexible.
 */

module.exports = {

  /**
   * Sets `content-length` to '0' if request is of DELETE type.
   *
   * @param {ClientRequest} Req Request object
   * @param {IncomingMessage} Res Response object
   * @param {Object} Options Config object passed to the proxy
   *
   * @api private
   */

  deleteLength: function deleteLength(req, res, options) {
    if((req.method === 'DELETE' || req.method === 'OPTIONS')
       && !req.headers['content-length']) {
      req.headers['content-length'] = '0';
      delete req.headers['transfer-encoding'];
    }
  },

  /**
   * Sets timeout in request socket if it was specified in options.
   *
   * @param {ClientRequest} Req Request object
   * @param {IncomingMessage} Res Response object
   * @param {Object} Options Config object passed to the proxy
   *
   * @api private
   */

  timeout: function timeout(req, res, options) {
    if(options.timeout) {
      req.socket.setTimeout(options.timeout);
    }
  },

  /**
   * Sets `x-forwarded-*` headers if specified in config.
   *
   * @param {ClientRequest} Req Request object
   * @param {IncomingMessage} Res Response object
   * @param {Object} Options Config object passed to the proxy
   *
   * @api private
   */

  XHeaders: function XHeaders(req, res, options) {
    if(!options.xfwd) return;

    var encrypted = req.isSpdy || common.hasEncryptedConnection(req);
    var values = {
      for  : req.connection.remoteAddress || req.socket.remoteAddress,
      port : common.getPort(req),
      proto: encrypted ? 'https' : 'http'
    };

    ['for', 'port', 'proto'].forEach(function(header) {
      req.headers['x-forwarded-' + header] =
        (req.headers['x-forwarded-' + header] || '') +
        (req.headers['x-forwarded-' + header] ? ',' : '') +
        values[header];
    });

    req.headers['x-forwarded-host'] = req.headers['x-forwarded-host'] || req.headers['host'] || '';
  },

  /**
   * Does the actual proxying. If `forward` is enabled fires up
   * a ForwardStream, same happens for ProxyStream. The request
   * just dies otherwise.
   *
   * @param {ClientRequest} Req Request object
   * @param {IncomingMessage} Res Response object
   * @param {Object} Options Config object passed to the proxy
   *
   * @api private
   */

  stream: function stream(req, res, options, _, server, clb) {

    // And we begin!
    server.emit('start', req, res, options.target || options.forward);

    var agents = options.followRedirects ? followRedirects : nativeAgents;
    var http = agents.http;
    var https = agents.https;

    if(options.forward) {
      // If forward enable, so just pipe the request
      var forwardReq = (options.forward.protocol === 'https:' ? https : http).request(
        common.setupOutgoing(options.ssl || {}, options, req, 'forward')
      );

      // error handler (e.g. ECONNRESET, ECONNREFUSED)
      // Handle errors on incoming request as well as it makes sense to
      var forwardError = createErrorHandler(forwardReq, options.forward);
      req.on('error', forwardError);
      forwardReq.on('error', forwardError);

      (options.buffer || req).pipe(forwardReq);
      if(!options.target) { return res.end(); }
    }

    // Request initalization
    var proxyReq = (options.target.protocol === 'https:' ? https : http).request(
      common.setupOutgoing(options.ssl || {}, options, req)
    );

    // Enable developers to modify the proxyReq before headers are sent
    proxyReq.on('socket', function(socket) {
      if(server) { server.emit('proxyReq', proxyReq, req, res, options); }
    });

    // allow outgoing socket to timeout so that we could
    // show an error page at the initial request
    if(options.proxyTimeout) {
      proxyReq.setTimeout(options.proxyTimeout, function() {
         proxyReq.abort();
      });
    }

    // Ensure we abort proxy if request is aborted
    req.on('aborted', function () {
      proxyReq.abort();
    });

    // handle errors in proxy and incoming request, just like for forward proxy
    var proxyError = createErrorHandler(proxyReq, options.target);
    req.on('error', proxyError);
    proxyReq.on('error', proxyError);

    function createErrorHandler(proxyReq, url) {
      return function proxyError(err) {
        if (req.socket.destroyed && err.code === 'ECONNRESET') {
          server.emit('econnreset', err, req, res, url);
          return proxyReq.abort();
        }

        if (clb) {
          clb(err, req, res, url);
        } else {
          server.emit('error', err, req, res, url);
        }
      }
    }

    (options.buffer || req).pipe(proxyReq);

    proxyReq.on('response', function(proxyRes) {
      if(server) { server.emit('proxyRes', proxyRes, req, res); }

      if(!res.headersSent && !options.selfHandleResponse) {
        for(var i=0; i < web_o.length; i++) {
          if(web_o[i](req, res, proxyRes, options)) { break; }
        }
      }

      if (!res.finished) {
        // Allow us to listen when the proxy has completed
        proxyRes.on('end', function () {
          if (server) server.emit('end', req, res, proxyRes);
        });
        // We pipe to the response unless its expected to be handled by the user
        if (!options.selfHandleResponse) proxyRes.pipe(res);
      } else {
        if (server) server.emit('end', req, res, proxyRes);
      }
    });
  }

};
<|endoftext|>


<commit_before>import Head from 'components/head';
import feather from 'feather-icons';
import { useEffect } from 'react';

function App({ Component, pageProps }) {
  const { post } = pageProps;

  useEffect(() => {
    feather.replace();
  }, []);

  return (
    <>
      <Head>
        <title>Reaper | Full Stack Developer</title>
        <meta property="og:site_name" content="Reaper | Full Stack Developer" />

        <meta
          property="og:title"
          content={
            post && post.meta
              ? post.meta.title
              : 'Reaper | Full Stack Developer'
          }
        />
        <meta name="twitter:card" content="summary_large_image" />
        <meta name="twitter:site" content="@_barelyhuman" />
        <meta
          property="og:image"
          content="https://reaper.im/wallpapers/reaper-dark.png"
        />
      </Head>
      <Component {...pageProps} />
    </>
  );
}

export default App;
<commit_msg>🤖 Formatted!<commit_after>import Head from 'components/head'
import feather from 'feather-icons'
import { useEffect } from 'react'

function App ({ Component, pageProps }) {
  const { post } = pageProps

  useEffect(() => {
    feather.replace()
  }, [])

  return (
    <>
      <Head>
        <title>Reaper | Full Stack Developer</title>
        <meta property='og:site_name' content='Reaper | Full Stack Developer' />

        <meta
          property='og:title'
          content={
            post && post.meta
              ? post.meta.title
              : 'Reaper | Full Stack Developer'
          }
        />
        <meta name='twitter:card' content='summary_large_image' />
        <meta name='twitter:site' content='@_barelyhuman' />
        <meta
          property='og:image'
          content='https://reaper.im/wallpapers/reaper-dark.png'
        />
      </Head>
      <Component {...pageProps} />
    </>
  )
}

export default App
<|endoftext|>


<commit_before>/* global APP */

import algoliasearch from 'algoliasearch/lite'
import Layout from 'components/Layout'
import { Component } from 'react'
import { Provider } from 'rebass'
import App from 'components/App'
import theme from 'config/theme'

const algoliaClient = algoliasearch(APP.algolia.appId, APP.algolia.apiKey)
const algoliaIndex = algoliaClient.initIndex(APP.algolia.indexName)

export default class extends Component {
  static async getInitialProps ({ req, asPath, url, ...props }) {
    const isServer = !!req

    const { query: { id: objectID } } = props

    const item = isServer
      ? await algoliaIndex.getObject(objectID)
      : JSON.parse(window.sessionStorage.getItem('hit'))

    const layout = {
      url: item.link,
      title: `${item.title} | Windtoday`,
      ogImage: item.image,
      description: `See all the information related with ${item.brand} ${item.model}. Compare with the rest of brands, search the best price and choose real deals in our windsurfing marketplace.`
    }

    return { isServer, url, layout, item }
  }

  render () {
    return (
      <Layout {...this.props.layout}>
        <Provider theme={theme}>
          <App item={this.props.item} isServer={this.props.isServer} />
        </Provider>
      </Layout>
    )
  }
}
<commit_msg>Use schema description<commit_after>/* global APP */

import algoliasearch from 'algoliasearch/lite'
import Layout from 'components/Layout'
import { Component } from 'react'
import { Provider } from 'rebass'
import App from 'components/App'
import theme from 'config/theme'

const algoliaClient = algoliasearch(APP.algolia.appId, APP.algolia.apiKey)
const algoliaIndex = algoliaClient.initIndex(APP.algolia.indexName)

const getFallbackDescription = ({ brand, model }) => `
See all the information related with ${brand} ${model}. Compare with the rest of brands, search the best price and choose real deals in our windsurfing marketplace.
`

export default class extends Component {
  static async getInitialProps ({ req, asPath, url, ...props }) {
    const isServer = !!req

    const { query: { id: objectID } } = props

    const item = isServer
      ? await algoliaIndex.getObject(objectID)
      : JSON.parse(window.sessionStorage.getItem('hit'))

    const layout = {
      url: item.link,
      title: `${item.title} | Windtoday`,
      ogImage: item.image,
      description: item.description || getFallbackDescription(item)
    }

    return { isServer, url, layout, item }
  }

  render () {
    return (
      <Layout {...this.props.layout}>
        <Provider theme={theme}>
          <App item={this.props.item} isServer={this.props.isServer} />
        </Provider>
      </Layout>
    )
  }
}
<|endoftext|>