<commit_before>import React, {PropTypes} from 'react'
import Faux from 'react-faux-dom'
import * as d3 from 'd3'
import styled from 'styled-components'
import _ from 'lodash'

const {arrayOf, array, string, number, func} = PropTypes

const BarChart = React.createClass({
  mixins: [Faux.mixins.core, Faux.mixins.anim],
  propTypes: {
    data: arrayOf(array),
    xDomain: array,
    colors: arrayOf(string),
    className: string,
    xLabel: string,
    yLabel: string,
    width: number,
    height: number,
    hover: string,
    setHover: func
  },
  getInitialState () {
    return {
      look: 'stacked',
      chart: 'loading...'
    }
  },
  componentDidMount () {
    this.renderD3(true)
  },
  componentDidUpdate (prevProps, prevState) {
    if (this.props !== prevProps) {
      this.renderD3(false)
    }
  },
  render () {
    return (

        <button onClick={this.toggle}>Toggle</button>
        {this.state.chart}

    )
  },
  toggle () {
    if (this.state.look === 'stacked') {
      this.setState({look: 'grouped'})
      this.transitionGrouped()
    } else {
      this.setState({look: 'stacked'})
      this.transitionStacked()
    }
  },
  renderD3 (firstRender) {
    const n = this.props.data.length // number of layers
    const stack = d3.layout.stack()
    const layers = stack(this.props.data)
    const yStackMax = d3.max(layers, layer => d3.max(layer, d => d.y0 + d.y))
    const margin = {top: 20, right: 10, bottom: 50, left: 50}
    const width = this.props.width - margin.left - margin.right
    const height = this.props.height - margin.top - margin.bottom
    const x = d3.scale.ordinal().domain(this.props.xDomain).rangeRoundBands([0, width], 0.08)
    const y = d3.scale.linear().domain([0, yStackMax]).range([height, 0])
    const color = d3.scale.linear().domain([0, n - 1]).range(this.props.colors)
    const xAxis = d3.svg.axis().scale(x).orient('bottom')
    const yAxis = d3.svg.axis().scale(y).orient('left')

    // create a faux div and store its virtual DOM in state.chart
    let faux = this.connectFauxDOM('div', 'chart')

    let svg = firstRender
      ? d3
          .select(faux)
          .append('svg')
          .attr('class', this.props.className)
          .attr('width', width + margin.left + margin.right)
          .attr('height', height + margin.top + margin.bottom)
          .append('g')
          .attr('transform', `translate(${margin.left}, ${margin.top})`)
      : d3.select(faux).select('svg').select('g')

    let layer = svg.selectAll('.layer').data(layers)
    layer.enter().append('g').attr('class', 'layer').style('fill', (d, i) => color(i))

    let rect = layer.selectAll('rect').data(d => d)
    rect.classed('disabled', d => this.props.hover && d.x !== this.props.hover)
    rect
      .enter()
      .append('rect')
      .attr('x', d => x(d.x))
      .attr('y', height)
      .attr('width', x.rangeBand())
      .attr('height', 0)
      .on('mouseover', d => this.props.setHover(d.x))
      .on('mouseout', d => this.props.setHover(null))
    if (this.state.look === 'stacked') {
      rect
        .transition()
        .delay((d, i) => i * 10)
        .attr('y', d => y(d.y0 + d.y))
        .attr('height', d => y(d.y0) - y(d.y0 + d.y))
    } else {
      rect.transition().delay((d, i) => i * 10).attr('y', d => y(d.y)).attr('height', d => height - y(d.y))
    }
    this.animateFauxDOM(800)

    let tooltip = firstRender
      ? d3
          .select(faux)
          .append('div')
          .attr('class', 'tooltip')
          .style('position', 'absolute')
          .style('z-index', '10')
          .style('display', 'inline-block')
          .style('border', 'solid grey 1px')
          .style('border-radius', '2px')
          .style('padding', '5px')
          .style('color', 'grey')
      : d3.select(faux).select('.tooltip')
    if (this.props.hover !== null) {
      const hoveredData = this.props.data.map(l => _.find(l, {x: this.props.hover}))
      const top = this.state.look === 'stacked' ? arr => y(_.sum(arr)) : arr => y(_.max(arr))
      tooltip
        .style('visibility', 'visible')
        .style('left', x(this.props.hover) + 40)
        .style('top', top(_.map(hoveredData, 'y')) + margin.top - 15)
        .html(`${this.props.hover}: ${_.map(hoveredData, 'y').join(', ')}`)
    } else {
      tooltip.style('visibility', 'hidden')
    }

    if (firstRender) {
      svg.append('g').attr('class', 'x axis').attr('transform', `translate(0, ${height})`).call(xAxis)
      svg
        .append('text')
        .attr('transform', `translate(${width / 2} ,${height + margin.bottom - 5})`)
        .style('text-anchor', 'middle')
        .text(this.props.xLabel)

      svg.append('g').attr('class', 'y axis').attr('transform', 'translate(0, 0)').call(yAxis)
      svg
        .append('text')
        .attr('transform', 'rotate(-90)')
        .attr('y', 0 - margin.left)
        .attr('x', 0 - height / 2)
        .attr('dy', '1em')
        .style('text-anchor', 'middle')
        .text(this.props.yLabel)
    } else {
      svg.select('g.x.axis').call(xAxis)
      svg.select('g.y.axis').call(yAxis)
    }

    this.transitionGrouped = () => {
      rect
        .transition()
        .duration(500)
        // .delay((d, i) => i * 10)
        .attr('x', (d, i, j) => x(d.x) + x.rangeBand() / n * j)
        .attr('width', x.rangeBand() / n)
        .transition()
        .attr('y', d => y(d.y))
        .attr('height', d => height - y(d.y))
      this.animateFauxDOM(2000)
    }

    this.transitionStacked = () => {
      rect
        .transition()
        .duration(500)
        // .delay((d, i) => i * 10)
        .attr('y', d => y(d.y0 + d.y))
        .attr('height', d => y(d.y0) - y(d.y0 + d.y))
        .transition()
        .attr('x', d => x(d.x))
        .attr('width', x.rangeBand())
      this.animateFauxDOM(2000)
    }
  }
})

const StyledBarChart = styled(BarChart)`
  .x.axis line,
  .x.axis path,
  .y.axis line,
  .y.axis path {
    fill: none;
    stroke: #000;
  }
  rect.disabled {
    opacity: 0.3;
  }
`

export default StyledBarChart
<commit_msg>Fix styled component class on chart + handle hover css<commit_after>import React, {PropTypes} from 'react'
import Faux from 'react-faux-dom'
import * as d3 from 'd3'
import styled from 'styled-components'
import _ from 'lodash'

const {arrayOf, array, string, number, func} = PropTypes

const BarChart = React.createClass({
  mixins: [Faux.mixins.core, Faux.mixins.anim],
  propTypes: {
    data: arrayOf(array),
    xDomain: array,
    colors: arrayOf(string),
    className: string,
    xLabel: string,
    yLabel: string,
    width: number,
    height: number,
    hover: string,
    setHover: func
  },
  getInitialState () {
    return {
      look: 'stacked',
      chart: 'loading...'
    }
  },
  componentDidMount () {
    this.renderD3(true)
  },
  componentDidUpdate (prevProps, prevState) {
    if (this.props !== prevProps) {
      this.renderD3(false)
    }
  },
  render () {
    return (
      <div className={`barchart ${this.props.className}`}>
        <button onClick={this.toggle}>Toggle</button>
        {this.state.chart}

    )
  },
  toggle () {
    if (this.state.look === 'stacked') {
      this.setState({look: 'grouped'})
      this.transitionGrouped()
    } else {
      this.setState({look: 'stacked'})
      this.transitionStacked()
    }
  },
  renderD3 (firstRender) {
    const n = this.props.data.length // number of layers
    const stack = d3.layout.stack()
    const layers = stack(this.props.data)
    const yStackMax = d3.max(layers, layer => d3.max(layer, d => d.y0 + d.y))
    const margin = {top: 20, right: 10, bottom: 50, left: 50}
    const width = this.props.width - margin.left - margin.right
    const height = this.props.height - margin.top - margin.bottom
    const x = d3.scale.ordinal().domain(this.props.xDomain).rangeRoundBands([0, width], 0.08)
    const y = d3.scale.linear().domain([0, yStackMax]).range([height, 0])
    const color = d3.scale.linear().domain([0, n - 1]).range(this.props.colors)
    const xAxis = d3.svg.axis().scale(x).orient('bottom')
    const yAxis = d3.svg.axis().scale(y).orient('left')

    // create a faux div and store its virtual DOM in state.chart
    let faux = this.connectFauxDOM('div', 'chart')

    let svg = firstRender
      ? d3
          .select(faux)
          .append('svg')
          .attr('width', width + margin.left + margin.right)
          .attr('height', height + margin.top + margin.bottom)
          .append('g')
          .attr('transform', `translate(${margin.left}, ${margin.top})`)
      : d3.select(faux).select('svg').select('g')

    let layer = svg.selectAll('.layer').data(layers)
    layer.enter().append('g').attr('class', 'layer').style('fill', (d, i) => color(i))

    let rect = layer.selectAll('rect').data(d => d)
    rect
      .enter()
      .append('rect')
      .attr('class', d => `data-${d.x}`)
      .attr('x', d => x(d.x))
      .attr('y', height)
      .attr('width', x.rangeBand())
      .attr('height', 0)
      .on('mouseover', d => this.props.setHover(d.x))
      .on('mouseout', d => this.props.setHover(null))
    if (this.state.look === 'stacked') {
      rect
        .transition()
        .delay((d, i) => i * 10)
        .attr('y', d => y(d.y0 + d.y))
        .attr('height', d => y(d.y0) - y(d.y0 + d.y))
    } else {
      rect.transition().delay((d, i) => i * 10).attr('y', d => y(d.y)).attr('height', d => height - y(d.y))
    }
    this.animateFauxDOM(800)

    let tooltip = firstRender
      ? d3.select(faux).append('div').attr('class', 'tooltip')
      : d3.select(faux).select('.tooltip')
    if (this.props.hover !== null) {
      const hoveredData = this.props.data.map(l => _.find(l, {x: this.props.hover}))
      const top = this.state.look === 'stacked' ? arr => y(_.sum(arr)) : arr => y(_.max(arr))
      tooltip
        .style('left', x(this.props.hover) + 40)
        .style('top', top(_.map(hoveredData, 'y')) + margin.top - 15)
        .html(`${this.props.hover}: ${_.map(hoveredData, 'y').join(', ')}`)
    }

    if (firstRender) {
      svg.append('g').attr('class', 'x axis').attr('transform', `translate(0, ${height})`).call(xAxis)
      svg
        .append('text')
        .attr('transform', `translate(${width / 2} ,${height + margin.bottom - 5})`)
        .style('text-anchor', 'middle')
        .text(this.props.xLabel)

      svg.append('g').attr('class', 'y axis').attr('transform', 'translate(0, 0)').call(yAxis)
      svg
        .append('text')
        .attr('transform', 'rotate(-90)')
        .attr('y', 0 - margin.left)
        .attr('x', 0 - height / 2)
        .attr('dy', '1em')
        .style('text-anchor', 'middle')
        .text(this.props.yLabel)
    } else {
      svg.select('g.x.axis').call(xAxis)
      svg.select('g.y.axis').call(yAxis)
    }

    this.transitionGrouped = () => {
      rect
        .transition()
        .duration(500)
        // .delay((d, i) => i * 10)
        .attr('x', (d, i, j) => x(d.x) + x.rangeBand() / n * j)
        .attr('width', x.rangeBand() / n)
        .transition()
        .attr('y', d => y(d.y))
        .attr('height', d => height - y(d.y))
      this.animateFauxDOM(2000)
    }

    this.transitionStacked = () => {
      rect
        .transition()
        .duration(500)
        // .delay((d, i) => i * 10)
        .attr('y', d => y(d.y0 + d.y))
        .attr('height', d => y(d.y0) - y(d.y0 + d.y))
        .transition()
        .attr('x', d => x(d.x))
        .attr('width', x.rangeBand())
      this.animateFauxDOM(2000)
    }
  }
})

const StyledBarChart = styled(BarChart)`
  .x.axis line,
  .x.axis path,
  .y.axis line,
  .y.axis path {
    fill: none;
    stroke: #000;
  }
  rect {
    opacity: ${props => props.hover ? 0.3 : 1};
  }
  rect.data-${props => props.hover} {
    opacity: 1;
  }
  position: relative;
  .tooltip {
    position: absolute;
    z-index: 10;
    display: inline-block;
    border: solid 1px grey;
    border-radius: 2px;
    padding: 5px;
    color: grey;
    visibility: ${props => props.hover ? 'visible' : 'hidden'};
  }
`

export default StyledBarChart
<|endoftext|>


<commit_before>import React, { Component } from 'react';

import PropTypes from 'prop-types';
import isString from 'lodash.isstring';
import result from 'lodash.result';
import ColorPicker from './Picker';

export default class DatColor extends Component {
  static propTypes = {
    data: PropTypes.object,
    path: PropTypes.string,
    label: PropTypes.string,
    labelWidth: PropTypes.number,
    liveUpdate: PropTypes.bool,
    onUpdate: PropTypes.func,
    _onUpdateValue: PropTypes.func
  };

  state = {
    value: this.getValue(),
    displayColorPicker: false
  };

  componentWillReceiveProps(nextProps) {
    this.setState({
      value: this.getValue(nextProps)
    });
  }

  getValue(props = this.props) {
    return result(props.data, props.path);
  }

  handleClick = () => {
    this.setState({
      displayColorPicker: !this.state.displayColorPicker
    });
  };

  handleClose = () => {
    this.setState({
      displayColorPicker: false
    });
  };

  handleChange = color => {
    const value = isString(color) ? color : color.hex;

    this.setState({ value }, () => {
      this.props.liveUpdate && this.update();
    });
  };

  update() {
    const { value } = this.state;

    this.props._onUpdateValue &&
      this.props._onUpdateValue(this.props.path, value);
    this.props.onUpdate && this.props.onUpdate(value);
  }

  renderPicker() {
    const { value, displayColorPicker } = this.state;

    return !displayColorPicker ? null : (

        <ColorPicker color={value} onChange={this.handleChange} />

    );
  }

  render() {
    const { path, label, labelWidth } = this.props;
    const { value } = this.state;
    const labelText = isString(label) ? label : path;

    return (
      <li className="cr color" style={{ borderLeftColor: `${value}` }}>

          <span className="label-text" style={{ width: `${labelWidth}%` }}>
            {labelText}

          <div
            style={{
              width: `${100 - labelWidth}%`,
              backgroundColor: `${value}`
            }}
          >

              {value}

            {this.renderPicker()}

    );
  }
}
<commit_msg>DatColor: Add default props, fix accessbility issues, implement getDerivedStateFromProps<commit_after>import React, { Component } from 'react';
import PropTypes from 'prop-types';
import isString from 'lodash.isstring';
import result from 'lodash.result';
import ColorPicker from './Picker';

export default class DatColor extends Component {
  static propTypes = {
    data: PropTypes.object,
    path: PropTypes.string,
    label: PropTypes.string,
    labelWidth: PropTypes.number,
    liveUpdate: PropTypes.bool,
    onUpdate: PropTypes.func,
    _onUpdateValue: PropTypes.func
  };

  static defaultProps = {
    data: null,
    path: null,
    label: null,
    labelWidth: null,
    liveUpdate: null,
    onUpdate: () => null,
    _onUpdateValue: () => null
  };

  constructor() {
    super();

    this.state = {
      value: null,
      displayColorPicker: false
    };
  }

  static getDerivedStateFromProps(nextProps, prevState) {
    const nextValue = result(nextProps.data, nextProps.path);

    return {
      ...prevState,
      value: nextValue
    };
  }

  handleClickColorPicker = () =>
    this.setState(prevState => ({
      ...prevState,
      displayColorPicker: !prevState.displayColorPicker
    }));

  handleCloseColorPicker = () =>
    this.setState({
      displayColorPicker: false
    });

  handleChangeColor = color => {
    const value = isString(color) ? color : color.hex;
    const { _onUpdateValue, onUpdate, path } = this.props;

    _onUpdateValue(path, value);
    onUpdate(value);
  };

  renderPicker() {
    const { value, displayColorPicker } = this.state;

    return !displayColorPicker ? null : (

        {/* eslint-disable-next-line jsx-a11y/control-has-associated-label */}

        <ColorPicker color={value} onChange={this.handleChangeColor} />

    );
  }

  render() {
    const { path, label, labelWidth } = this.props;
    const { value } = this.state;
    const labelText = isString(label) ? label : path;

    return (
      <li className="cr color" style={{ borderLeftColor: `${value}` }}>

          <span className="label-text" style={{ width: `${labelWidth}%` }}>
            {labelText}

          <div
            style={{
              width: `${100 - labelWidth}%`,
              backgroundColor: `${value}`
            }}
          >

              {value}

            {this.renderPicker()}

    );
  }
}
<|endoftext|>


<commit_before>import React from 'react'
import Link from './Link'

const style = {
  small: {
    fontSize: "80%"
  },
  quick: {
    cursor: "auto",
    position: "relative",
    overflow: "hidden",
    verticalAlign: "middle",
    outline: "none",
    fontSize: "100%"
  },
  list: {
    paddingLeft: 30
  },
  titleContainer: {
    display: "flex",
    alignItems: "baseline",
    justifyContent: "space-between"
  }
}

const renderList = linkList => accountList => onDelete => onClickGlobeCircle => {

  const _onDelete = (type, key) => () => onDelete(type, key)
  const _onClickGlobeCircle = (type, key) => () => onClickGlobeCircle(type, key)

  if (Object.keys(linkList).length === 0 && Object.keys(accountList).length === 0) {
    return (

        nothing to list: To add one, enter a gmail search and click "Add Quick Link" to create a quick list

    )
  } else {

    const linksArray = Object
      .keys(linkList)
      .map(key => {
        const { urlHash } = linkList[key]
        return (
          <Link
            key={key}
            type="global"
            name={key}
            urlHash={urlHash}
            onDelete={_onDelete('global', key)}
            onClickGlobeCircle={_onClickGlobeCircle('global', key)}
          />
        )
      })

      const accountArray = Object
        .keys(accountList)
        .map(key => {
          const { urlHash } = accountList[key]
          return (
            <Link
              key={key}
              type="account"
              name={key}
              urlHash={urlHash}
              onDelete={_onDelete('account', key)}
              onClickGlobeCircle={_onClickGlobeCircle('account', key)}
            />
          )
        })

    return (
      linksArray.concat(accountArray)
    )
  }
}

const displayHelp = () => {
  const message = `
To use Quick Links:

1) perform a gmail search
2) click on "Add Quick Link" to give a name for that search

By default, all quick links are specific to the account where they were created.  If you have multiple gmail accounts logged in simultaneously, creating a quick link would only be visible for that account.

You may choose to have quick links available for multiple gmail accounts by clicking on the yellow sphere which then toggles to a globe icon.  A quick link with a globe icon will now show up in every gmail account you are logged in.  Clicking on the globe again will change that quick link back to a specific account quick link.

If you do not have multiple gmail accounts, toggling between the yellow sphere and the globe does nothing.
`
  alert(message)
}

const LinkList = ({
  linkList = {},
  accountList = {},
  onAdd,
  onDelete,
  onClickGlobeCircle
}) => {
  return (

Quick Links

 onAdd(event)}>
          Add

            { renderList(linkList)(accountList)(onDelete)(onClickGlobeCircle) }

  )
}

export default LinkList
<commit_msg>add quick link is now just add<commit_after>import React from 'react'
import Link from './Link'

const style = {
  small: {
    fontSize: "80%"
  },
  quick: {
    cursor: "auto",
    position: "relative",
    overflow: "hidden",
    verticalAlign: "middle",
    outline: "none",
    fontSize: "100%"
  },
  list: {
    paddingLeft: 30
  },
  titleContainer: {
    display: "flex",
    alignItems: "baseline",
    justifyContent: "space-between"
  }
}

const renderList = linkList => accountList => onDelete => onClickGlobeCircle => {

  const _onDelete = (type, key) => () => onDelete(type, key)
  const _onClickGlobeCircle = (type, key) => () => onClickGlobeCircle(type, key)

  if (Object.keys(linkList).length === 0 && Object.keys(accountList).length === 0) {
    return (

        nothing to list: To add one, enter a gmail search and click "Add" to create a quick list

    )
  } else {

    const linksArray = Object
      .keys(linkList)
      .map(key => {
        const { urlHash } = linkList[key]
        return (
          <Link
            key={key}
            type="global"
            name={key}
            urlHash={urlHash}
            onDelete={_onDelete('global', key)}
            onClickGlobeCircle={_onClickGlobeCircle('global', key)}
          />
        )
      })

      const accountArray = Object
        .keys(accountList)
        .map(key => {
          const { urlHash } = accountList[key]
          return (
            <Link
              key={key}
              type="account"
              name={key}
              urlHash={urlHash}
              onDelete={_onDelete('account', key)}
              onClickGlobeCircle={_onClickGlobeCircle('account', key)}
            />
          )
        })

    return (
      linksArray.concat(accountArray)
    )
  }
}

const displayHelp = () => {
  const message = `
To use Quick Links:

1) perform a gmail search
2) click on "Add Quick Link" to give a name for that search

By default, all quick links are specific to the account where they were created.  If you have multiple gmail accounts logged in simultaneously, creating a quick link would only be visible for that account.

You may choose to have quick links available for multiple gmail accounts by clicking on the yellow sphere which then toggles to a globe icon.  A quick link with a globe icon will now show up in every gmail account you are logged in.  Clicking on the globe again will change that quick link back to a specific account quick link.

If you do not have multiple gmail accounts, toggling between the yellow sphere and the globe does nothing.
`
  alert(message)
}

const LinkList = ({
  linkList = {},
  accountList = {},
  onAdd,
  onDelete,
  onClickGlobeCircle
}) => {
  return (

Quick Links

 onAdd(event)}>
          Add

            { renderList(linkList)(accountList)(onDelete)(onClickGlobeCircle) }

  )
}

export default LinkList
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import { connect } from 'react-redux';
import { Link, browserHistory } from 'react-router';
import { fetchNewsItem } from '../actions';
import ScrollToTopOnMount from './ScrollToTopOnMount';

class NewsItem extends Component {
  componentDidMount() {
    this.props.fetchNewsItem(this.props.params.newsId);
  }

  componentDidUpdate(prevProps) {
    const oldId = prevProps.params.newsId;
    const newId = this.props.params.newsId;
    if (newId !== oldId) {
      this.props.fetchNewsItem(newId);
    }
  }

  render() {
    const news = this.props.news;
    if (this.props.hasErrored) {
      return
Sorry! There was an error loading the items
;
    }
    if (this.props.isLoading) {
      return
Loading…
;
    }
    return (

        <ScrollToTopOnMount />

          <Link to={`/news/${Number(news.id) + 1}`}>上一条</Link>
          {'      '}
          <Link to={`/news/${Number(news.id) - 1}`}>下一条</Link>
          {' '}

            <button
              type="button"
              className="btn btn-primary btn-sm"
              onClick={browserHistory.goBack}
            >
            返回之前页面
            </button>

{news.title}

日期:{news.create_date.substring(0, 10)}

话题:{news.category}

来源:{news.source}

        <Link to={`/news/${Number(news.id) + 1}`}>上一条</Link>
        {'      '}
        <Link to={`/news/${Number(news.id) - 1}`}>下一条</Link>
        {' '}

          <button
            type="button"
            className="btn btn-primary btn-sm"
            onClick={browserHistory.goBack}
          >
            返回之前页面
          </button>

    );
  }
}

const mapStateToProps = (state) => {
  const { isLoading, news, hasErrored } = state.newsItem;
  // console.log(news)
  return {
    isLoading,
    news,
    hasErrored,
  };
};

export default connect(mapStateToProps, { fetchNewsItem })(NewsItem);
<commit_msg>add news title<commit_after>import React, { Component } from 'react';
import { connect } from 'react-redux';
import { Link, browserHistory } from 'react-router';
import { fetchNewsItem } from '../actions';
import ScrollToTopOnMount from './ScrollToTopOnMount';

class NewsItem extends Component {
  componentDidMount() {
    this.props.fetchNewsItem(this.props.params.newsId);
  }

  componentDidUpdate(prevProps) {
    const oldId = prevProps.params.newsId;
    const newId = this.props.params.newsId;
    if (newId !== oldId) {
      this.props.fetchNewsItem(newId);
    }
  }
  componentWillUnmount() {
    // this.props.resetNewsList();
    document.title = 'News'
  }
  render() {
    const news = this.props.news;
    if (this.props.hasErrored) {
      return
Sorry! There was an error loading the items
;
    }
    if (this.props.isLoading) {
      return
Loading…
;
    }

    document.title = this.props.news.title;
    return (

        <ScrollToTopOnMount />

          <Link to={`/news/${Number(news.id) + 1}`}>上一条</Link>
          {'      '}
          <Link to={`/news/${Number(news.id) - 1}`}>下一条</Link>
          {' '}

            <button
              type="button"
              className="btn btn-primary btn-sm"
              onClick={browserHistory.goBack}
            >
            返回之前页面
            </button>

{news.title}

日期:{news.create_date.substring(0, 10)}

话题:{news.category}

来源:{news.source}

        <Link to={`/news/${Number(news.id) + 1}`}>上一条</Link>
        {'      '}
        <Link to={`/news/${Number(news.id) - 1}`}>下一条</Link>
        {' '}

          <button
            type="button"
            className="btn btn-primary btn-sm"
            onClick={browserHistory.goBack}
          >
            返回之前页面
          </button>

    );
  }
}

const mapStateToProps = (state) => {
  const { isLoading, news, hasErrored } = state.newsItem;
  // console.log(news)
  return {
    isLoading,
    news,
    hasErrored,
  };
};

export default connect(mapStateToProps, { fetchNewsItem })(NewsItem);
<|endoftext|>


<commit_before>import RecorderWorker from './RecorderWorker'

const RecorderState = {
  inactive: 'inactive',
  recording: 'recording'
};

const errorMessageToCodeMap = {
  PermissionDeniedError: 'permission-denied',
  NotAllowedError: 'permission-denied'
};

export default class Recorder extends H5P.EventDispatcher{
  // Record logic API goes here
  constructor() {
    super();

    this.config = {
      bufferLength: 4096,
      numChannels: 1
    }

    this.state = RecorderState.inactive;

    // Create a worker. This is normaløly done using a URL to the js-file
    const workerBlob = new Blob(
      [RecorderWorker.toString().replace(/^function .+\{?|\}$/g, '')],
      {type:'text/javascript'}
    );
    const workerBlobUrl = URL.createObjectURL(workerBlob);
    this.worker = new Worker(workerBlobUrl);

    const self = this;
    this.worker.onmessage = function (e) {
      self.trigger(e.data.command, e.data.blob);
    };
  }

  getWavURL() {
    this.stop();

    const promise = new Promise((resolve, reject) => {
      this.once('wav-delivered', (e) => {
        resolve(URL.createObjectURL(e.data));
      });
    });

    this.worker.postMessage({
      command: 'exportWAV',
      type: 'audio/wav'
    });

    return promise;
  }

  getMP3Url() {
    // TODO
  }

  init() {
    const self = this;
    this.userMedia = new Promise((resolve, reject) => {
      if (!this.supported()) {
        return reject('browser-unsupported');
      }

      this.audioContext = new AudioContext();
      this.scriptProcessorNode = this.audioContext.createScriptProcessor(
        this.config.bufferLength,
        this.config.numChannels,
        this.config.numChannels);
      this.scriptProcessorNode.onaudioprocess = function(e) {
        if (self.state === RecorderState.recording) {
          self.worker.postMessage({
            command: 'record',
            buffer: [e.inputBuffer.getChannelData(0)]
          });
        }
      };

      navigator.mediaDevices.getUserMedia({audio: true}).then((stream) => {
        this.sourceNode = this.audioContext.createMediaStreamSource(stream);

        this.worker.postMessage({
          command: 'init',
          config: {
            sampleRate: this.sourceNode.context.sampleRate,
            numChannels: this.config.numChannels
          }
        });

        this.sourceNode.connect(this.scriptProcessorNode);
        this.scriptProcessorNode.connect(this.audioContext.destination);

        resolve();
      }).catch((e) => {
        reject({
          code: this._errorToCode(e),
          error: e
        });
      });
    });
  }

  start() {
    if (!this.userMedia) {
      this.init();
    }

    this.userMedia
      .then(() => {
        this._setState(RecorderState.recording);
      })
      .catch((e) => {
        this.trigger('blocked');
      });
  }

  stop() {
    this._setState(RecorderState.inactive);
  }

  supported() {
    return AudioContext && navigator.mediaDevices.getUserMedia;
  }

  reset() {
    this._setState(RecorderState.inactive);
    this.worker.postMessage({
      command: 'clear'
    });
  }

  _errorToCode(e) {
    if (e.name && errorMessageToCodeMap[e.name]) {
      return errorMessageToCodeMap[e.name];
    }
    return 'unknown';
  }

  _setState(state) {
    this.state = state;
    this.trigger(this.state);
  }
}
<commit_msg>Added comments for Recorder<commit_after>import RecorderWorker from './RecorderWorker'

const RecorderState = {
  inactive: 'inactive',
  recording: 'recording'
};

const errorMessageToCodeMap = {
  PermissionDeniedError: 'permission-denied',
  NotAllowedError: 'permission-denied'
};

/**
 * Event triggered when recorder goes from recording to inactive
 *
 * @event Recorder#inactive
 * @type {Object}
 */

/**
 * Event triggered when recording is started
 *
 * @event Recorder#recording
 * @type {Object}
 */

/**
 * Event triggered when microphone access is denied
 *
 * @event Recorder#blocked
 * @type {Object}
 */

/**
 * The recorder class
 *
 * @fires Recorder#inactive
 * @fires Recorder#recording
 * @fires Recorder#blocked
 */
export default class Recorder extends H5P.EventDispatcher{

  /**
   * @constructor
   */
  constructor() {
    super();

    this.config = {
      bufferLength: 4096,
      numChannels: 1
    }

    this.state = RecorderState.inactive;

    // Create a worker. This is normally done using a URL to the js-file
    const workerBlob = new Blob(
      [RecorderWorker.toString().replace(/^function .+\{?|\}$/g, '')],
      {type:'text/javascript'}
    );
    const workerBlobUrl = URL.createObjectURL(workerBlob);
    this.worker = new Worker(workerBlobUrl);

    const self = this;
    this.worker.onmessage = function (e) {
      self.trigger(e.data.command, e.data.blob);
    };
  }

  /**
   * Creates a URL to a wav blob
   *
   * @return {Promise}
   */
  getWavURL() {
    this.stop();

    const promise = new Promise((resolve, reject) => {
      this.once('wav-delivered', (e) => {
        resolve(URL.createObjectURL(e.data));
      });
    });

    this.worker.postMessage({
      command: 'exportWAV',
      type: 'audio/wav'
    });

    return promise;
  }

  /**
   * Creates a URL to an mp3 blob
   *
   * @return {Promise}
   */
  getMP3Url() {
    // TODO
  }

  /**
   * Initialize microphone
   */
  init() {
    const self = this;
    this.userMedia = new Promise((resolve, reject) => {
      if (!this.supported()) {
        return reject('browser-unsupported');
      }

      this.audioContext = new AudioContext();
      this.scriptProcessorNode = this.audioContext.createScriptProcessor(
        this.config.bufferLength,
        this.config.numChannels,
        this.config.numChannels);
      this.scriptProcessorNode.onaudioprocess = function(e) {
        if (self.state === RecorderState.recording) {
          self.worker.postMessage({
            command: 'record',
            buffer: [e.inputBuffer.getChannelData(0)]
          });
        }
      };

      navigator.mediaDevices.getUserMedia({audio: true}).then((stream) => {
        this.sourceNode = this.audioContext.createMediaStreamSource(stream);

        this.worker.postMessage({
          command: 'init',
          config: {
            sampleRate: this.sourceNode.context.sampleRate,
            numChannels: this.config.numChannels
          }
        });

        this.sourceNode.connect(this.scriptProcessorNode);
        this.scriptProcessorNode.connect(this.audioContext.destination);

        resolve();
      }).catch((e) => {
        reject({
          code: this._errorToCode(e),
          error: e
        });
      });
    });
  }

  /**
   * Start or resume a recording
   */
  start() {
    if (!this.userMedia) {
      this.init();
    }

    this.userMedia
      .then(() => {
        this._setState(RecorderState.recording);
      })
      .catch((e) => {
        this.trigger('blocked');
      });
  }

  /**
   * Stop/pause a recording
   */
  stop() {
    this._setState(RecorderState.inactive);
  }

  /**
   * Check if browser supports recording
   * @return {boolean}
   */
  supported() {
    return AudioContext && navigator.mediaDevices.getUserMedia;
  }

  /**
   * Reset previous recording
   */
  reset() {
    this._setState(RecorderState.inactive);
    this.worker.postMessage({
      command: 'clear'
    });
  }

  /**
   * Maps different browsers error codes to a common one
   *
   * @param {Object} e Error object from browser
   * @private
   * @return {string}
   */
  _errorToCode(e) {
    if (e.name && errorMessageToCodeMap[e.name]) {
      return errorMessageToCodeMap[e.name];
    }
    return 'unknown';
  }

  /**
   * Set state
   *
   * @private
   * @param {string} state
   */
  _setState(state) {
    this.state = state;
    this.trigger(this.state);
  }
}
<|endoftext|>


<commit_before>import idMixin from '../../mixins/id'

export default {
  mixins: [idMixin],
  render (h) {
    let content = h(false)
    if (this.localActive || !this.computedLazy) {
      content = h(
        this.tag,
        {
          ref: 'panel',
          class: this.tabClasses,
          directives: [{ name: 'show', value: this.localActive }],
          attrs: {
            role: 'tabpanel',
            id: this.safeId(),
            'aria-hidden': this.localActive ? 'false' : 'true',
            'aria-expanded': this.localActive ? 'true' : 'false',
            'aria-lablelledby': this.controlledBy || null
          }
        },
        [this.$slots.default]
      )
    }
    return h(
      'transition',
      {
        props: { mode: 'out-in' },
        on: {
          beforeEnter: this.beforeEnter,
          beforeLeave: this.beforeLeave
        }
      },
      [content]
    )
  },
  methods: {
    beforeEnter () {
      // change opacity 1 frame after display
      // otherwise css transition won't happen
      window.requestAnimationFrame(() => { this.show = true })
    },
    beforeLeave () {
      this.show = false
    }
  },
  data () {
    return {
      localActive: this.active && !this.disabled,
      show: false
    }
  },
  mounted () {
    this.show = this.localActive
  },
  computed: {
    tabClasses () {
      return [
        'tab-pane',
        this.$parent && this.$parent.card && !this.noBody ? 'card-body' : '',
        this.show ? 'show' : '',
        this.computedFade ? 'fade' : '',
        this.disabled ? 'disabled' : '',
        this.localActive ? 'active' : ''
      ]
    },
    controlledBy () {
      return this.buttonId || this.safeId('__BV_tab_button__')
    },
    computedFade () {
      return this.$parent.fade
    },
    computedLazy () {
      return this.$parent.lazy
    },
    _isTab () {
      // For parent sniffing of child
      return true
    }
  },
  props: {
    active: {
      type: Boolean,
      default: false
    },
    tag: {
      type: String,
      default: 'div'
    },
    buttonId: {
      type: String,
      default: ''
    },
    title: {
      type: String,
      default: ''
    },
    titleItemClass: {
      // Sniffed by tabs.vue and added to nav 'li.nav-item'
      type: [String, Array, Object],
      default: null
    },
    titleLinkClass: {
      // Sniffed by tabs.vue and added to nav 'a.nav-link'
      type: [String, Array, Object],
      default: null
    },
    headHtml: {
      // Is this actually ever used?
      type: String,
      default: null
    },
    disabled: {
      type: Boolean,
      default: false
    },
    noBody: {
      type: Boolean,
      default: false
    },
    href: {
      type: String,
      default: '#'
    }
  }
}
<commit_msg>fix(tab): typo aria-lablelledby (#1959)<commit_after>import idMixin from '../../mixins/id'

export default {
  mixins: [idMixin],
  render (h) {
    let content = h(false)
    if (this.localActive || !this.computedLazy) {
      content = h(
        this.tag,
        {
          ref: 'panel',
          class: this.tabClasses,
          directives: [{ name: 'show', value: this.localActive }],
          attrs: {
            role: 'tabpanel',
            id: this.safeId(),
            'aria-hidden': this.localActive ? 'false' : 'true',
            'aria-expanded': this.localActive ? 'true' : 'false',
            'aria-labelledby': this.controlledBy || null
          }
        },
        [this.$slots.default]
      )
    }
    return h(
      'transition',
      {
        props: { mode: 'out-in' },
        on: {
          beforeEnter: this.beforeEnter,
          beforeLeave: this.beforeLeave
        }
      },
      [content]
    )
  },
  methods: {
    beforeEnter () {
      // change opacity 1 frame after display
      // otherwise css transition won't happen
      window.requestAnimationFrame(() => { this.show = true })
    },
    beforeLeave () {
      this.show = false
    }
  },
  data () {
    return {
      localActive: this.active && !this.disabled,
      show: false
    }
  },
  mounted () {
    this.show = this.localActive
  },
  computed: {
    tabClasses () {
      return [
        'tab-pane',
        this.$parent && this.$parent.card && !this.noBody ? 'card-body' : '',
        this.show ? 'show' : '',
        this.computedFade ? 'fade' : '',
        this.disabled ? 'disabled' : '',
        this.localActive ? 'active' : ''
      ]
    },
    controlledBy () {
      return this.buttonId || this.safeId('__BV_tab_button__')
    },
    computedFade () {
      return this.$parent.fade
    },
    computedLazy () {
      return this.$parent.lazy
    },
    _isTab () {
      // For parent sniffing of child
      return true
    }
  },
  props: {
    active: {
      type: Boolean,
      default: false
    },
    tag: {
      type: String,
      default: 'div'
    },
    buttonId: {
      type: String,
      default: ''
    },
    title: {
      type: String,
      default: ''
    },
    titleItemClass: {
      // Sniffed by tabs.vue and added to nav 'li.nav-item'
      type: [String, Array, Object],
      default: null
    },
    titleLinkClass: {
      // Sniffed by tabs.vue and added to nav 'a.nav-link'
      type: [String, Array, Object],
      default: null
    },
    headHtml: {
      // Is this actually ever used?
      type: String,
      default: null
    },
    disabled: {
      type: Boolean,
      default: false
    },
    noBody: {
      type: Boolean,
      default: false
    },
    href: {
      type: String,
      default: '#'
    }
  }
}
<|endoftext|>


<commit_before>import React                from 'react';
import PureRenderMixin      from 'react-addons-pure-render-mixin';
import { omit }             from 'timm';
import { bindAll }          from '../gral/helpers';

const NULL_VALUE = '';
function toInternalValue(val) { return val != null ? val : NULL_VALUE; }
function toExternalValue(val) { return val !== NULL_VALUE ? val : null; }

// ==========================================
// Component
// ==========================================
class Textarea extends React.Component {
  static propTypes = {
    value:                  React.PropTypes.any,
    errors:                 React.PropTypes.array,
    onChange:               React.PropTypes.func,
    onFocus:                React.PropTypes.func,
    onBlur:                 React.PropTypes.func,
  };
  static defaultProps = {
    errors:                 [],
  };

  constructor(props) {
    super(props);
    this.shouldComponentUpdate = PureRenderMixin.shouldComponentUpdate.bind(this);
    this.state = {
      curValue: toInternalValue(props.value),
      fFocused: false,
    };
    bindAll(this, [
      'onChange',
      'onFocus',
      'onBlur',
    ]);
  }

  componentWillReceiveProps(nextProps) {
    const { value } = nextProps;
    if (value !== this.props.value) {
      this.setState({ curValue: toInternalValue(value) });
    }
  }

  componentDidMount() { this.resizeTextarea(); }
  componentDidUpdate() { this.resizeTextarea(); }

  getValue() { return toExternalValue(this.state.curValue); }

  // ==========================================
  // Render
  // ==========================================
  render() {
    const { curValue } = this.state;
    const otherProps = omit(this.props, PROP_KEYS);
    return (

 { this._refTaPlaceholder = c; }}
          style={style.taPlaceholder}
        >
          {curValue || 'x'}

        <textarea ref={c => { this._refTaInput = c; }}
          value={curValue}
          onChange={this.onChange}
          onFocus={this.onFocus}
          onBlur={this.onBlur}
          style={style.taInput}
          {...otherProps}
        />

    );
  }

  // ==========================================
  // Handlers
  // ==========================================
  onChange(ev) {
    const { value } = ev.currentTarget;
    this.setState({ curValue: value });
    const { onChange } = this.props;
    if (onChange) onChange(ev, toExternalValue(value));
  }

  onFocus(ev) {
    this.setState({ fFocused: true });
    if (this.props.onFocus) this.props.onFocus(ev);
  }
  onBlur(ev) {
    this.setState({ fFocused: false });
    if (this.props.onBlur) this.props.onBlur(ev);
  }

  resizeTextarea() {
    const height = this._refTaPlaceholder.offsetHeight;
    // if (this.state.fFocused) height += 4;
    this._refTaInput.style.height = `${height}px`;
  }
}

// ==========================================
// Styles
// ==========================================
const style = {
  taWrapper: {
    position: 'relative',
  },
  taInput: {
    width: '100%',
    overflow: 'hidden',
    cursor: 'beam',
    fontFamily: 'inherit',
    fontSize: 'inherit',
    background: 'transparent',
    resize: 'none',
    padding: 2,
  },
  taPlaceholder: {
    position: 'absolute',
    top: 0,
    left: 0,
    width: '100%',
    opacity: 0,
    padding: 2,
    border: '1px solid red',
    color: 'red',
    cursor: 'beam',
    zIndex: -50,
  },
};

// ==========================================
// Miscellaneous
// ==========================================
const PROP_KEYS = Object.keys(Textarea.propTypes);

// ==========================================
// Public API
// ==========================================
export default Textarea;
<commit_msg>Allow Textarea customisation<commit_after>import React                from 'react';
import PureRenderMixin      from 'react-addons-pure-render-mixin';
import { omit, merge }      from 'timm';
import { bindAll }          from '../gral/helpers';

const NULL_VALUE = '';
function toInternalValue(val) { return val != null ? val : NULL_VALUE; }
function toExternalValue(val) { return val !== NULL_VALUE ? val : null; }

// ==========================================
// Component
// ==========================================
class Textarea extends React.Component {
  static propTypes = {
    value:                  React.PropTypes.any,
    errors:                 React.PropTypes.array,
    onChange:               React.PropTypes.func,
    onFocus:                React.PropTypes.func,
    onBlur:                 React.PropTypes.func,
    style:                  React.PropTypes.object,
  };
  static defaultProps = {
    errors:                 [],
  };

  constructor(props) {
    super(props);
    this.shouldComponentUpdate = PureRenderMixin.shouldComponentUpdate.bind(this);
    this.state = {
      curValue: toInternalValue(props.value),
      fFocused: false,
    };
    bindAll(this, [
      'onChange',
      'onFocus',
      'onBlur',
      'resize',
    ]);
  }

  componentWillReceiveProps(nextProps) {
    const { value } = nextProps;
    if (value !== this.props.value) {
      this.setState({ curValue: toInternalValue(value) });
    }
  }

  componentDidMount() {
    this.resize();
    window.addEventListener('resize', this.resize);
  }
  componentDidUpdate() { this.resize(); }
  componentWillUnmount() {
    window.removeEventListener('resize', this.resize);
  }

  // ==========================================
  // Imperative API
  // ==========================================
  getValue() { return toExternalValue(this.state.curValue); }
  setValue(val, cb) { this.setState({ curValue: toInternalValue(val) }, cb); }
  revert(cb) { this.setState({ curValue: toInternalValue(this.props.value) }, cb); }
  focus() { this._refTaInput.focus(); }
  blur() { this._refTaInput.blur(); }

  // ==========================================
  // Render
  // ==========================================
  render() {
    const { style: baseStyle } = this.props;
    const { curValue } = this.state;
    const otherProps = omit(this.props, PROP_KEYS);
    return (

 { this._refTaPlaceholder = c; }}
          style={merge(style.taPlaceholder, baseStyle)}
        >
          {curValue || 'x'}

        <textarea ref={c => { this._refTaInput = c; }}
          value={curValue}
          onChange={this.onChange}
          onFocus={this.onFocus}
          onBlur={this.onBlur}
          style={merge(style.taInput, baseStyle)}
          {...otherProps}
        />

    );
  }

  // ==========================================
  // Handlers
  // ==========================================
  onChange(ev) {
    const { value } = ev.currentTarget;
    this.setState({ curValue: value });
    const { onChange } = this.props;
    if (onChange) onChange(ev, toExternalValue(value));
  }

  onFocus(ev) {
    this.setState({ fFocused: true });
    if (this.props.onFocus) this.props.onFocus(ev);
  }
  onBlur(ev) {
    this.setState({ fFocused: false });
    if (this.props.onBlur) this.props.onBlur(ev);
  }

  resize() {
    const height = this._refTaPlaceholder.offsetHeight;
    this._refTaInput.style.height = `${height}px`;
  }
}

// ==========================================
// Styles
// ==========================================
const style = {
  taWrapper: {
    position: 'relative',
  },
  taInput: {
    width: '100%',
    overflow: 'hidden',
    cursor: 'beam',
    fontFamily: 'inherit',
    fontSize: 'inherit',
    background: 'transparent',
    resize: 'none',
    padding: 2,
  },
  taPlaceholder: {
    position: 'absolute',
    top: 0,
    left: 0,
    width: '100%',
    opacity: 0,
    padding: 2,
    border: '1px solid red',
    color: 'red',
    cursor: 'beam',
    zIndex: -50,
  },
};

// ==========================================
// Miscellaneous
// ==========================================
const PROP_KEYS = Object.keys(Textarea.propTypes);

// ==========================================
// Public API
// ==========================================
export default Textarea;
<|endoftext|>


<commit_before>import React from 'react'
import { renderToString } from 'react-dom/server'
import { Router, RoutingContext, match } from 'react-router'
import { createMemoryHistory } from 'history'
import { createStore, combineReducers, applyMiddleware } from 'redux'
import thunk from 'redux-thunk'
import reduxPromise from 'shared/utils/promiseMiddleware'
import * as reducers from 'shared/reducers'
import { Provider } from 'react-redux'
import routes from 'shared/routes'
import fs from 'fs'
import path from 'path'
import nunjucks from 'nunjucks'
import Helmet from 'react-helmet'
import * as AuthActions from 'shared/actions/AuthActions'
import * as LocaleActions from 'shared/actions/LocaleActions'
import url from 'url'
import AppContainer from 'shared/components/AppContainer'
import route from 'koa-route'
import ReduxUniversalResolver from 'shared/utils/redux-universal-resolver'
import { TranslatorInit } from './translator-helper'

nunjucks.configure('views', {
  autoescape: true
})

// thunkify stat
function stat (file) {
  return function (done) {
    fs.stat(file, done)
  }
}

export default function (app) {
  app.use(route.get('/sitemap.xml', function *(ctx, next) {
    try {
      const location = './sitemap.xml'
      const fstat = yield stat(location)
      if (fstat.isFile()) {
        this.type = path.extname(location)
        this.body = fs.createReadStream(location)
      }
    } catch (err) {
      this.status = 404
      this.body = '404'
    }
  }))

  app.use(route.get('/robots.txt', function *(ctx, next) {
    try {
      const location = './robots.txt'
      const fstat = yield stat(location)
      if (fstat.isFile()) {
        this.type = path.extname(location)
        this.body = fs.createReadStream(location)
      }
    } catch (err) {
      this.status = 404
      this.body = '404'
    }
  }))

  app.use(route.get('/uploads/*', function *(ctx, next) {
    let location = path.join(__dirname, '../../', decodeURI(this.path))
    try {
      const fstat = yield stat(location)
      if (fstat.isFile()) {
        this.type = path.extname(location)
        this.body = fs.createReadStream(location)
      }
    } catch (err) {
      this.status = 404
      this.body = '404'
    }
  }))

  app.use(function *() {
    let isCashed = this.cashed ? yield *this.cashed() : false
    if (!isCashed) {
      const reducer = combineReducers(reducers)
      const enhancer = applyMiddleware(
        thunk,
        reduxPromise
      )(createStore)
      const store = enhancer(reducer)
      const resolver = new ReduxUniversalResolver()
      store.resolver = resolver
      store.protocol = this.protocol
      store.host = this.host
      store.query = this.query

      const history = createMemoryHistory()
      const location = history.createLocation(this.url)

      // save session token to store
      if (this.session.token && this.session.token !== null) {
        store.dispatch(AuthActions.sync(this.session.token))
      }

      const { translator, lang } = TranslatorInit(this.getLocaleFromHeader())
      store.dispatch(LocaleActions.sync(lang || 'zh-hant-tw'))

      let appString
      let assets
      let head
      let siteUrl
      let pageUrl
      const { error, redirectLocation, renderProps }
      = yield new Promise((resolve) => {
        match({routes: routes(store), location},
          (_error, _redirectLocation, _renderProps) => {
            resolve({
              error: _error,
              redirectLocation: _redirectLocation,
              renderProps: _renderProps
            })
          })
      })

      if (redirectLocation) {
        this.redirect(redirectLocation.pathname + redirectLocation.search)
        return
      } else if (error) {
        this.redirect('/')
        return
      } else if (renderProps === null) {
        this.redirect('/')
        return
      }

      const elements = (
        <AppContainer translator={translator}>
          {()=>
          <Provider store={store}>
            <RoutingContext {...renderProps} />
          </Provider>
          }
        </AppContainer>
      )

      renderToString(elements)  // need this line to collect data
      yield resolver.dispatch(store)
      appString = renderToString(elements)
      head = Helmet.rewind()

      const env = process.env.NODE_ENV
      if (env === 'development') {
        assets = fs.readFileSync(
          path.resolve(__dirname, '../../storage/webpack-stats.json')
        )
        assets = JSON.parse(assets)
      } else {
        assets = require('storage/webpack-stats.json')
      }

      const serverState = renderToString(
        <script
          dangerouslySetInnerHTML=
            {{
              __html:
                `window.STATE_FROM_SERVER=${JSON.stringify(store.getState())};`
            }} />
      )

      siteUrl = 'https://' + this.host
      pageUrl = this.href.replace(/^http/, 'https')
      pageUrl = (location.pathname === '/sync/token') ? siteUrl : pageUrl

      this.body = nunjucks.render('index.html', {
        appString,
        assets,
        siteUrl,
        pageUrl,
        head,
        env: process.env,
        stateFromServer: serverState
      })

      resolver.clear()
    }
  })
}
<commit_msg>Remove decodeURI<commit_after>import React from "react";
import { renderToString } from "react-dom/server";
import { Router, RoutingContext, match } from "react-router";
import { createMemoryHistory } from "history";
import { createStore, combineReducers, applyMiddleware } from "redux";
import thunk from "redux-thunk";
import reduxPromise from "shared/utils/promiseMiddleware";
import * as reducers from "shared/reducers";
import { Provider } from "react-redux";
import routes from "shared/routes";
import fs from "fs";
import path from "path";
import nunjucks from "nunjucks";
import Helmet from "react-helmet";
import * as AuthActions from "shared/actions/AuthActions";
import * as LocaleActions from "shared/actions/LocaleActions";
import url from "url";
import AppContainer from "shared/components/AppContainer";
import route from "koa-route";
import ReduxUniversalResolver from "shared/utils/redux-universal-resolver";
import { TranslatorInit } from "./translator-helper";

nunjucks.configure("views", {
  autoescape: true
});

// thunkify stat
function stat(file) {
  return function(done) {
    fs.stat(file, done);
  };
}

export default function(app) {
  app.use(
    route.get("/sitemap.xml", function*(ctx, next) {
      try {
        const location = "./sitemap.xml";
        const fstat = yield stat(location);
        if (fstat.isFile()) {
          this.type = path.extname(location);
          this.body = fs.createReadStream(location);
        }
      } catch (err) {
        this.status = 404;
        this.body = "404";
      }
    })
  );

  app.use(
    route.get("/robots.txt", function*(ctx, next) {
      try {
        const location = "./robots.txt";
        const fstat = yield stat(location);
        if (fstat.isFile()) {
          this.type = path.extname(location);
          this.body = fs.createReadStream(location);
        }
      } catch (err) {
        this.status = 404;
        this.body = "404";
      }
    })
  );

  app.use(
    route.get("/uploads/*", function*(ctx, next) {
      let location = path.join(__dirname, "../../", this.path);
      try {
        const fstat = yield stat(location);
        if (fstat.isFile()) {
          this.type = path.extname(location);
          this.body = fs.createReadStream(location);
        }
      } catch (err) {
        this.status = 404;
        this.body = "404";
      }
    })
  );

  app.use(function*() {
    let isCashed = this.cashed ? yield* this.cashed() : false;
    if (!isCashed) {
      const reducer = combineReducers(reducers);
      const enhancer = applyMiddleware(thunk, reduxPromise)(createStore);
      const store = enhancer(reducer);
      const resolver = new ReduxUniversalResolver();
      store.resolver = resolver;
      store.protocol = this.protocol;
      store.host = this.host;
      store.query = this.query;

      const history = createMemoryHistory();
      const location = history.createLocation(this.url);

      // save session token to store
      if (this.session.token && this.session.token !== null) {
        store.dispatch(AuthActions.sync(this.session.token));
      }

      const { translator, lang } = TranslatorInit(this.getLocaleFromHeader());
      store.dispatch(LocaleActions.sync(lang || "zh-hant-tw"));

      let appString;
      let assets;
      let head;
      let siteUrl;
      let pageUrl;
      const { error, redirectLocation, renderProps } = yield new Promise(
        resolve => {
          match(
            { routes: routes(store), location },
            (_error, _redirectLocation, _renderProps) => {
              resolve({
                error: _error,
                redirectLocation: _redirectLocation,
                renderProps: _renderProps
              });
            }
          );
        }
      );

      if (redirectLocation) {
        this.redirect(redirectLocation.pathname + redirectLocation.search);
        return;
      } else if (error) {
        this.redirect("/");
        return;
      } else if (renderProps === null) {
        this.redirect("/");
        return;
      }

      const elements = (
        <AppContainer translator={translator}>
          {() => (
            <Provider store={store}>
              <RoutingContext {...renderProps} />
            </Provider>
          )}
        </AppContainer>
      );

      renderToString(elements); // need this line to collect data
      yield resolver.dispatch(store);
      appString = renderToString(elements);
      head = Helmet.rewind();

      const env = process.env.NODE_ENV;
      if (env === "development") {
        assets = fs.readFileSync(
          path.resolve(__dirname, "../../storage/webpack-stats.json")
        );
        assets = JSON.parse(assets);
      } else {
        assets = require("storage/webpack-stats.json");
      }

      const serverState = renderToString(
        <script
          dangerouslySetInnerHTML={{
            __html: `window.STATE_FROM_SERVER=${JSON.stringify(
              store.getState()
            )};`
          }}
        />
      );

      siteUrl = "https://" + this.host;
      pageUrl = this.href.replace(/^http/, "https");
      pageUrl = location.pathname === "/sync/token" ? siteUrl : pageUrl;

      this.body = nunjucks.render("index.html", {
        appString,
        assets,
        siteUrl,
        pageUrl,
        head,
        env: process.env,
        stateFromServer: serverState
      });

      resolver.clear();
    }
  });
}
<|endoftext|>


<commit_before>import express from "express"
import shrinkRay from "shrink-ray"
import uuid from "uuid"
import hpp from "hpp"
import helmet from "helmet"
import { resolve } from "path"

import {
  ABSOLUTE_CLIENT_OUTPUT_PATH,
  ABSOLUTE_PUBLIC_PATH
} from "./config"

export function addFallbackHandler(server) {
  /* eslint-disable no-magic-numbers */

  // Handle 404 errors.
  // Note: the react application middleware hands 404 paths, but it is good to
  // have this backup for paths not handled by the universal middleware. For
  // example you may bind a /api path to express.
  server.use((req, res, next) => {
    // eslint-disable-line no-unused-vars,max-len
    res.status(404).send("Sorry, that resource was not found.")
  })

  // Handle all other errors (i.e. 500).
  // Note: You must provide specify all 4 parameters on this callback function
  // even if they aren't used, otherwise it won't be used.
  server.use((err, req, res, next) => {
    // eslint-disable-line no-unused-vars,max-len
    if (err) {
      console.log(err)
      console.log(err.stack)
    }

    res.status(500).send("Sorry, an unexpected error occurred.")
  })
}

export function generateServer()
{
  // Create our express based server.
  const server = express()

  // Attach a unique "nonce" to every response.  This allows use to declare
  // inline scripts as being safe for execution against our content security policy.
  // @see https://helmetjs.github.io/docs/csp/
  server.use((req, res, next) => {
    res.locals.nonce = uuid() // eslint-disable-line no-param-reassign
    next()
  })

  // Don't expose any software information to hackers.
  server.disable("x-powered-by")

  // Prevent HTTP Parameter pollution.
  server.use(hpp())

  /*
  // Content Security Policy (CSP)
  //
  // If you are unfamiliar with CSPs then I highly recommend that you do some
  // reading on the subject:
  //  - https://content-security-policy.com/
  //  - https://developers.google.com/web/fundamentals/security/csp/
  //  - https://developer.mozilla.org/en/docs/Web/Security/CSP
  //  - https://helmetjs.github.io/docs/csp/
  //
  // If you are relying on scripts/styles/assets from other servers (internal or
  // external to your company) then you will need to explicitly configure the
  // CSP below to allow for this.  For example you can see I have had to add
  // the polyfill.io CDN in order to allow us to use the polyfill script.
  // It can be a pain to manage these, but it's a really great habit to get in
  // to.
  //
  // You may find CSPs annoying at first, but it is a great habit to build.
  // The CSP configuration is an optional item for helmet, however you should
  // not remove it without making a serious consideration that you do not require
  // the added security.
  const cspConfig = {
    directives: {
      defaultSrc: [ "'self'" ],

      scriptSrc:
      [
        // Allow scripts hosted from our application.
        "'self'",

        // Note: We will execution of any inline scripts that have the following
        // nonce identifier attached to them.
        // This is useful for guarding your application whilst allowing an inline
        // script to do data store rehydration (redux/mobx/apollo) for example.
        // @see https://helmetjs.github.io/docs/csp/
        (req, res) => `'nonce-${res.locals.nonce}'`,

        // FIXME: Required for eval-source-maps (devtool in webpack)
        process.env.NODE_ENV === "development" ? "'unsafe-eval'" : ""
      ].filter((value) => value !== ""),

      styleSrc: [ "'self'", "'unsafe-inline'", "blob:" ],
      imgSrc: [ "'self'", "data:" ],
      fontSrc: [ "'self'" ],

      // Note: Setting this to stricter than * breaks the service worker. :(
      // I can't figure out how to get around this, so if you know of a safer
      // implementation that is kinder to service workers please let me know.
      // ["'self'", 'ws:'],
      connectSrc: [ "*" ],

      // objectSrc: [ "'none'" ],
      // mediaSrc: [ "'none'" ],

      childSrc: [ "'self'" ]
    }
  }

  if (process.env.NODE_ENV === "development") {
    // When in development mode we need to add our secondary express server that
    // is used to host our client bundle to our csp config.
    Object.keys(cspConfig.directives).forEach((directive) =>
      cspConfig.directives[directive].push(`localhost:${process.env.CLIENT_DEVSERVER_PORT}`)
    )
  }

  server.use(helmet.contentSecurityPolicy(cspConfig))
  */

  // The xssFilter middleware sets the X-XSS-Protection header to prevent
  // reflected XSS attacks.
  // @see https://helmetjs.github.io/docs/xss-filter/
  server.use(helmet.xssFilter())

  // Frameguard mitigates clickjacking attacks by setting the X-Frame-Options header.
  // @see https://helmetjs.github.io/docs/frameguard/
  server.use(helmet.frameguard("deny"))

  // Sets the X-Download-Options to prevent Internet Explorer from executing
  // downloads in your site’s context.
  // @see https://helmetjs.github.io/docs/ienoopen/
  server.use(helmet.ieNoOpen())

  // Don’t Sniff Mimetype middleware, noSniff, helps prevent browsers from trying
  // to guess (“sniff”) the MIME type, which can have security implications. It
  // does this by setting the X-Content-Type-Options header to nosniff.
  // @see https://helmetjs.github.io/docs/dont-sniff-mimetype/
  server.use(helmet.noSniff())

  // Advanced response compression using a async zopfli/brotli combination
  // https://github.com/aickin/shrink-ray
  server.use(shrinkRay())

  // Configure static serving of our webpack bundled client files.
  server.use(
    process.env.CLIENT_BUNDLE_HTTP_PATH,
    express.static(ABSOLUTE_CLIENT_OUTPUT_PATH, {
      maxAge: process.env.CLIENT_BUNDLE_CACHE_MAXAGE
    })
  )

  // Configure static serving of our "public" root http path static files.
  server.use(express.static(ABSOLUTE_PUBLIC_PATH))

  // When in production mode, bind our service worker folder so that it can
  // be served.
  // Note: the service worker needs to be available at the http root of your
  // application for the offline support to work.
  if (process.env.NODE_ENV === "production") {
    server.use(express.static(resolve(ABSOLUTE_CLIENT_OUTPUT_PATH, "./serviceWorker")))
  }

  return server
}
<commit_msg>Re-enabled CSP protection<commit_after>import express from "express"
import shrinkRay from "shrink-ray"
import uuid from "uuid"
import hpp from "hpp"
import helmet from "helmet"
import { resolve } from "path"

import {
  ABSOLUTE_CLIENT_OUTPUT_PATH,
  ABSOLUTE_PUBLIC_PATH
} from "./config"

export function addFallbackHandler(server) {
  /* eslint-disable no-magic-numbers */

  // Handle 404 errors.
  // Note: the react application middleware hands 404 paths, but it is good to
  // have this backup for paths not handled by the universal middleware. For
  // example you may bind a /api path to express.
  server.use((req, res, next) => {
    // eslint-disable-line no-unused-vars,max-len
    res.status(404).send("Sorry, that resource was not found.")
  })

  // Handle all other errors (i.e. 500).
  // Note: You must provide specify all 4 parameters on this callback function
  // even if they aren't used, otherwise it won't be used.
  server.use((err, req, res, next) => {
    // eslint-disable-line no-unused-vars,max-len
    if (err) {
      console.log(err)
      console.log(err.stack)
    }

    res.status(500).send("Sorry, an unexpected error occurred.")
  })
}

export function generateServer()
{
  // Create our express based server.
  const server = express()

  // Attach a unique "nonce" to every response.  This allows use to declare
  // inline scripts as being safe for execution against our content security policy.
  // @see https://helmetjs.github.io/docs/csp/
  server.use((req, res, next) => {
    res.locals.nonce = uuid() // eslint-disable-line no-param-reassign
    next()
  })

  // Don't expose any software information to hackers.
  server.disable("x-powered-by")

  // Prevent HTTP Parameter pollution.
  server.use(hpp())

  // Content Security Policy (CSP)
  //
  // If you are unfamiliar with CSPs then I highly recommend that you do some
  // reading on the subject:
  //  - https://content-security-policy.com/
  //  - https://developers.google.com/web/fundamentals/security/csp/
  //  - https://developer.mozilla.org/en/docs/Web/Security/CSP
  //  - https://helmetjs.github.io/docs/csp/
  //
  // If you are relying on scripts/styles/assets from other servers (internal or
  // external to your company) then you will need to explicitly configure the
  // CSP below to allow for this.  For example you can see I have had to add
  // the polyfill.io CDN in order to allow us to use the polyfill script.
  // It can be a pain to manage these, but it's a really great habit to get in
  // to.
  //
  // You may find CSPs annoying at first, but it is a great habit to build.
  // The CSP configuration is an optional item for helmet, however you should
  // not remove it without making a serious consideration that you do not require
  // the added security.
  const cspConfig = {
    directives: {
      defaultSrc: [ "'self'" ],

      scriptSrc:
      [
        // Allow scripts hosted from our application.
        "'self'",

        // Note: We will execution of any inline scripts that have the following
        // nonce identifier attached to them.
        // This is useful for guarding your application whilst allowing an inline
        // script to do data store rehydration (redux/mobx/apollo) for example.
        // @see https://helmetjs.github.io/docs/csp/
        (req, res) => `'nonce-${res.locals.nonce}'`,

        // FIXME: Required for eval-source-maps (devtool in webpack)
        process.env.NODE_ENV === "development" ? "'unsafe-eval'" : ""
      ].filter((value) => value !== ""),

      styleSrc: [ "'self'", "'unsafe-inline'", "blob:" ],
      imgSrc: [ "'self'", "data:" ],
      fontSrc: [ "'self'" ],

      // Note: Setting this to stricter than * breaks the service worker. :(
      // I can't figure out how to get around this, so if you know of a safer
      // implementation that is kinder to service workers please let me know.
      // ["'self'", 'ws:'],
      connectSrc: [ "*" ],

      // objectSrc: [ "'none'" ],
      // mediaSrc: [ "'none'" ],

      childSrc: [ "'self'" ]
    }
  }

  if (process.env.NODE_ENV === "development") {
    // When in development mode we need to add our secondary express server that
    // is used to host our client bundle to our csp config.
    Object.keys(cspConfig.directives).forEach((directive) =>
      cspConfig.directives[directive].push(`localhost:${process.env.CLIENT_DEVSERVER_PORT}`)
    )
  }

  server.use(helmet.contentSecurityPolicy(cspConfig))

  // The xssFilter middleware sets the X-XSS-Protection header to prevent
  // reflected XSS attacks.
  // @see https://helmetjs.github.io/docs/xss-filter/
  server.use(helmet.xssFilter())

  // Frameguard mitigates clickjacking attacks by setting the X-Frame-Options header.
  // @see https://helmetjs.github.io/docs/frameguard/
  server.use(helmet.frameguard("deny"))

  // Sets the X-Download-Options to prevent Internet Explorer from executing
  // downloads in your site’s context.
  // @see https://helmetjs.github.io/docs/ienoopen/
  server.use(helmet.ieNoOpen())

  // Don’t Sniff Mimetype middleware, noSniff, helps prevent browsers from trying
  // to guess (“sniff”) the MIME type, which can have security implications. It
  // does this by setting the X-Content-Type-Options header to nosniff.
  // @see https://helmetjs.github.io/docs/dont-sniff-mimetype/
  server.use(helmet.noSniff())

  // Advanced response compression using a async zopfli/brotli combination
  // https://github.com/aickin/shrink-ray
  server.use(shrinkRay())

  // Configure static serving of our webpack bundled client files.
  server.use(
    process.env.CLIENT_BUNDLE_HTTP_PATH,
    express.static(ABSOLUTE_CLIENT_OUTPUT_PATH, {
      maxAge: process.env.CLIENT_BUNDLE_CACHE_MAXAGE
    })
  )

  // Configure static serving of our "public" root http path static files.
  server.use(express.static(ABSOLUTE_PUBLIC_PATH))

  // When in production mode, bind our service worker folder so that it can
  // be served.
  // Note: the service worker needs to be available at the http root of your
  // application for the offline support to work.
  if (process.env.NODE_ENV === "production") {
    server.use(express.static(resolve(ABSOLUTE_CLIENT_OUTPUT_PATH, "./serviceWorker")))
  }

  return server
}
<|endoftext|>


<commit_before>'use strict';

var conn = require('../dal/pgConnection.js');
var dal = require('../dal/watzdprice.js');
var elasticsearch = require('../dal/elasticsearch.js');

module.exports.getproductGET = function getproductGET (req, res, next) {
  if (req.swagger.params.apikey.value !== 'andre') {
    res.statusCode = 403;
    return res.end();
  } else {
    conn.execute(function (err, client, done) {
      if (err) {
        console.error('getproductGET - ' + JSON.stringify(req.swagger.params) + err.print());
        return next(err);
      }
      dal.getProduct(client, req.swagger.params.id.value, function (err, product) {
        if (err) {
          console.error('getproductGET - ' + JSON.stringify(req.swagger.params) + err.print());
          conn.rollback(client, done);
          return next(err);
        }

        conn.commit(client, done);
        res.statusCode = 200;
        return res.end(JSON.stringify({product: product}));
      });
    });
  }
};

module.exports.searchproductsGET = function searchproductsGET (req, res, next) {
  if (req.swagger.params.apikey.value !== 'andre') {
    res.statusCode = 403;
    return res.end();
  } else {
    var client = elasticsearch.startSession();
    elasticsearch.searchProducts(client, req.swagger.params.q.value, function (err, products) {
      if (err) {
        console.error('searchproductsGET - ' + JSON.stringify(req.swagger.params) + err.print());
        conn.rollback(client, done);
        return next(err);
      }

      res.statusCode = 200;
      return res.end(JSON.stringify({products: products}));
    });
  }
};
<commit_msg>rework<commit_after>'use strict';

var conn = require('../dal/pgConnection.js');
var dal = require('../dal/watzdprice.js');
var elasticsearch = require('../dal/elasticsearch.js');

module.exports.getproductGET = function getproductGET (req, res, next) {
  if (req.swagger.params.apikey.value !== 'andre') {
    res.statusCode = 403;
    return res.end();
  } else {
    conn.execute(function (err, client, done) {
      if (err) {
        console.error('getproductGET - ' + JSON.stringify(req.swagger.params) + err.print());
        return next(err);
      }
      dal.getProduct(client, req.swagger.params.id.value, function (err, product) {
        if (err) {
          console.error('getproductGET - ' + JSON.stringify(req.swagger.params) + err.print());
          conn.rollback(client, done);
          return next(err);
        }

        conn.commit(client, done);
        res.statusCode = 200;
        return res.end(JSON.stringify({product: product}));
      });
    });
  }
};

module.exports.searchproductsGET = function searchproductsGET (req, res, next) {
  if (req.swagger.params.apikey.value !== 'andre') {
    res.statusCode = 403;
    return res.end();
  } else {
    var client = elasticsearch.startSession();
    elasticsearch.searchProducts(client, req.swagger.params.q.value, function (err, products) {
      if (err) {
        console.error('searchproductsGET - ' + JSON.stringify(req.swagger.params) + err.print());
        return next(err);
      }

      res.statusCode = 200;
      return res.end(JSON.stringify({products: products}));
    });
  }
};
<|endoftext|>


<commit_before>var AudioletBuffer = new Class({
    initialize: function(numberOfChannels, length) {
        this.numberOfChannels = numberOfChannels;
        this.length = length;

        this.channels = [];
        for (var i=0; i<this.numberOfChannels; i++) {
            this.channels.push(new Float32Array(numberOfChannels * length));
        }

        this.unsliced_channels = [];
        for (var i=0; i<this.numberOfChannels; i++) {
            this.unsliced_channels.push(this.channels[i]);
        }

        this.isEmpty = false;
    },

    getChannelData: function(channel) {
        return (this.channels[channel]);
    },

    set: function(buffer) {
        var numberOfChannels = buffer.numberOfChannels;
        for (var i=0; i<numberOfChannels; i++) {
            this.channels[i].set(buffer.getChannelData(i));
        }
    },

    setSection: function(buffer, length, inputOffset, outputOffset) {
        inputOffset = inputOffset || 0;
        outputOffset = outputOffset || 0;
        var numberOfChannels = buffer.numberOfChannels;
        for (var i = 0; i < numberOfChannels; i++) {
            var channel1 = this.getChannelData(i).subarray(outputOffset,
                                                           outputOffset +
                                                           length);
            var channel2 = buffer.getChannelData(i).subarray(inputOffset,
                                                             inputOffset +
                                                             length);
            channel1.set(channel2);
        }
    },

    add: function(buffer) {
        var length = this.length;
        var numberOfChannels = buffer.numberOfChannels;
        for (var i = 0; i < numberOfChannels; i++) {
            var channel1 = this.getChannelData(i);
            var channel2 = buffer.getChannelData(i);
            for (var j = 0; j < length; j++) {
                channel1[j] += channel2[j];
            }
        }
    },

    addSection: function(buffer, length, inputOffset, outputOffset) {
        inputOffset = inputOffset || 0;
        outputOffset = outputOffset || 0;
        var numberOfChannels = buffer.numberOfChannels;
        for (var i = 0; i < numberOfChannels; i++) {
            var channel1 = this.getChannelData(i)
            var channel2 = buffer.getChannelData(i);
            for (var j = 0; j < length; j++) {
                channel1[j + outputOffset] += channel2[j + inputOffset];
            }
        }
    },

    resize: function(numberOfChannels, length, lazy, offset) {
        offset = offset || 0;
        for (var i=0; i<numberOfChannels; i++) {
            if (length > this.length) {
                var channel = this.channels[i];
                this.channels[i] = new Float32Array(length);
                if (!lazy && channel) {
                    this.channels[i].set(channel, offset);
                }
                this.unsliced_channels[i] = this.channels[i];
            }
            else {
                this.channels[i] = this.channels[i].subarray(offset, offset +
                                                                     length);
            }
        }
        this.numberOfChannels = numberOfChannels;
        this.length = length;
    },

    push: function(buffer) {
        var bufferLength = buffer.length;
        this.resize(this.numberOfChannels, this.length + bufferLength);
        this.setSection(buffer, bufferLength, 0, this.length - bufferLength);
    },

    pop: function(buffer) {
        var bufferLength = buffer.length;
        var offset = bufferLength - length;
        buffer.setSection(this, bufferLength, offset, 0);
        this.resize(this.numberOfChannels, offset);
    },

    unshift: function(buffer) {
        var bufferLength = buffer.length;
        this.resize(this.numberOfChannels, this.length + bufferLength, false,
                    bufferLength);
        this.setSection(buffer, bufferLength, 0, 0);
    },

    shift: function(buffer) {
        var bufferLength = buffer.length;
        buffer.setSection(this, bufferLength, 0, 0);
        this.resize(this.numberOfChannels, this.length - bufferLength,
                    false, bufferLength);
    },

    zero: function() {
       var numberOfChannels = this.numberOfChannels;
        for (var i = 0; i < numberOfChannels; i++) {
            var channel = this.getChannelData(i);
            var length = this.length;
            for (var j = 0; j < length; j++) {
                channel[j] = 0;
            }
        }
    },

    combined: function() {
        var channels = this.channels;
        var numberOfChannels = this.numberOfChannels;
        var length = this.length;
        var combined = new Float32Array(numberOfChannels * length);
        for (var i=0; i<numberOfChannels; i++) {
            combined.set(channels[i], i * length);
        }
        return combined;
    },

    interleaved: function() {
        var channels = this.channels;
        var numberOfChannels = this.numberOfChannels;
        var length = this.length;
        var interleaved = new Float32Array(numberOfChannels * length);
        for (var i = 0; i < length; i++) {
            for (var j=0; j<numberOfChannels; j++) {
                interleaved[numberOfChannels * i + j] = channels[j][i];
            }
        }
        return interleaved;
    },

    copy: function() {
        var buffer = new AudioletBuffer(this.numberOfChannels, this.length);
        buffer.set(this);
        return buffer;
    }
});
<commit_msg>Add missing semi-colon<commit_after>var AudioletBuffer = new Class({
    initialize: function(numberOfChannels, length) {
        this.numberOfChannels = numberOfChannels;
        this.length = length;

        this.channels = [];
        for (var i=0; i<this.numberOfChannels; i++) {
            this.channels.push(new Float32Array(numberOfChannels * length));
        }

        this.unsliced_channels = [];
        for (var i=0; i<this.numberOfChannels; i++) {
            this.unsliced_channels.push(this.channels[i]);
        }

        this.isEmpty = false;
    },

    getChannelData: function(channel) {
        return (this.channels[channel]);
    },

    set: function(buffer) {
        var numberOfChannels = buffer.numberOfChannels;
        for (var i=0; i<numberOfChannels; i++) {
            this.channels[i].set(buffer.getChannelData(i));
        }
    },

    setSection: function(buffer, length, inputOffset, outputOffset) {
        inputOffset = inputOffset || 0;
        outputOffset = outputOffset || 0;
        var numberOfChannels = buffer.numberOfChannels;
        for (var i = 0; i < numberOfChannels; i++) {
            var channel1 = this.getChannelData(i).subarray(outputOffset,
                                                           outputOffset +
                                                           length);
            var channel2 = buffer.getChannelData(i).subarray(inputOffset,
                                                             inputOffset +
                                                             length);
            channel1.set(channel2);
        }
    },

    add: function(buffer) {
        var length = this.length;
        var numberOfChannels = buffer.numberOfChannels;
        for (var i = 0; i < numberOfChannels; i++) {
            var channel1 = this.getChannelData(i);
            var channel2 = buffer.getChannelData(i);
            for (var j = 0; j < length; j++) {
                channel1[j] += channel2[j];
            }
        }
    },

    addSection: function(buffer, length, inputOffset, outputOffset) {
        inputOffset = inputOffset || 0;
        outputOffset = outputOffset || 0;
        var numberOfChannels = buffer.numberOfChannels;
        for (var i = 0; i < numberOfChannels; i++) {
            var channel1 = this.getChannelData(i);
            var channel2 = buffer.getChannelData(i);
            for (var j = 0; j < length; j++) {
                channel1[j + outputOffset] += channel2[j + inputOffset];
            }
        }
    },

    resize: function(numberOfChannels, length, lazy, offset) {
        offset = offset || 0;
        for (var i=0; i<numberOfChannels; i++) {
            if (length > this.length) {
                var channel = this.channels[i];
                this.channels[i] = new Float32Array(length);
                if (!lazy && channel) {
                    this.channels[i].set(channel, offset);
                }
                this.unsliced_channels[i] = this.channels[i];
            }
            else {
                this.channels[i] = this.channels[i].subarray(offset, offset +
                                                                     length);
            }
        }
        this.numberOfChannels = numberOfChannels;
        this.length = length;
    },

    push: function(buffer) {
        var bufferLength = buffer.length;
        this.resize(this.numberOfChannels, this.length + bufferLength);
        this.setSection(buffer, bufferLength, 0, this.length - bufferLength);
    },

    pop: function(buffer) {
        var bufferLength = buffer.length;
        var offset = bufferLength - length;
        buffer.setSection(this, bufferLength, offset, 0);
        this.resize(this.numberOfChannels, offset);
    },

    unshift: function(buffer) {
        var bufferLength = buffer.length;
        this.resize(this.numberOfChannels, this.length + bufferLength, false,
                    bufferLength);
        this.setSection(buffer, bufferLength, 0, 0);
    },

    shift: function(buffer) {
        var bufferLength = buffer.length;
        buffer.setSection(this, bufferLength, 0, 0);
        this.resize(this.numberOfChannels, this.length - bufferLength,
                    false, bufferLength);
    },

    zero: function() {
       var numberOfChannels = this.numberOfChannels;
        for (var i = 0; i < numberOfChannels; i++) {
            var channel = this.getChannelData(i);
            var length = this.length;
            for (var j = 0; j < length; j++) {
                channel[j] = 0;
            }
        }
    },

    combined: function() {
        var channels = this.channels;
        var numberOfChannels = this.numberOfChannels;
        var length = this.length;
        var combined = new Float32Array(numberOfChannels * length);
        for (var i=0; i<numberOfChannels; i++) {
            combined.set(channels[i], i * length);
        }
        return combined;
    },

    interleaved: function() {
        var channels = this.channels;
        var numberOfChannels = this.numberOfChannels;
        var length = this.length;
        var interleaved = new Float32Array(numberOfChannels * length);
        for (var i = 0; i < length; i++) {
            for (var j=0; j<numberOfChannels; j++) {
                interleaved[numberOfChannels * i + j] = channels[j][i];
            }
        }
        return interleaved;
    },

    copy: function() {
        var buffer = new AudioletBuffer(this.numberOfChannels, this.length);
        buffer.set(this);
        return buffer;
    }
});
<|endoftext|>


<commit_before>/**
 * @author mrdoob / http://mrdoob.com/
 */

THREE.DirectGeometry = function () {

	Object.defineProperty( this, 'id', { value: THREE.GeometryIdCount ++ } );

	this.uuid = THREE.Math.generateUUID();

	this.name = '';
	this.type = 'DirectGeometry';

	this.indices = [];
	this.vertices = [];
	this.colors = [];
	this.normals = [];
	this.colors = [];
	this.uvs = [];
	this.uvs2 = [];
	this.tangents = [];

	this.morphTargets = [];
	this.morphColors = [];
	this.morphNormals = [];

	this.skinWeights = [];
	this.skinIndices = [];

	// this.lineDistances = [];

	this.boundingBox = null;
	this.boundingSphere = null;

	// update flags

	this.verticesNeedUpdate = false;
	this.normalsNeedUpdate = false;
	this.colorsNeedUpdate = false;
	this.uvsNeedUpdate = false;
	this.tangentsNeedUpdate = false;

};

THREE.DirectGeometry.prototype = {

	constructor: THREE.DirectGeometry,

	computeBoundingBox: THREE.Geometry.prototype.computeBoundingBox,
	computeBoundingSphere: THREE.Geometry.prototype.computeBoundingSphere,

	computeFaceNormals: function () {

		console.warn( 'THREE.DirectGeometry: computeFaceNormals() is not a method of this type of geometry.' );
		return this;

	},

	computeVertexNormals: function () {

		console.warn( 'THREE.DirectGeometry: computeVertexNormals() is not a method of this type of geometry.' );
		return this;

	},

	computeTangents: function () {

		console.warn( 'THREE.DirectGeometry: computeTangents() is not a method of this type of geometry.' );
		return this;

	},

	fromGeometry: function ( geometry ) {

		var faces = geometry.faces;
		var vertices = geometry.vertices;
		var faceVertexUvs = geometry.faceVertexUvs;

		var hasFaceVertexUv = faceVertexUvs[ 0 ] && faceVertexUvs[ 0 ].length > 0;
		var hasFaceVertexUv2 = faceVertexUvs[ 1 ] && faceVertexUvs[ 1 ].length > 0;

		var hasTangents = geometry.hasTangents;

		// morphs

		var morphTargets = geometry.morphTargets;
		var morphTargetsLength = morphTargets.length;

		for ( var i = 0; i < morphTargetsLength; i ++ ) {

			this.morphTargets[ i ] = [];

		}

		var morphNormals = geometry.morphNormals;
		var morphNormalsLength = morphNormals.length;

		for ( var i = 0; i < morphNormalsLength; i ++ ) {

			this.morphNormals[ i ] = [];

		}

		var morphColors = geometry.morphColors;
		var morphColorsLength = morphColors.length;

		for ( var i = 0; i < morphColorsLength; i ++ ) {

			this.morphColors[ i ] = [];

		}

		// skins

		var skinIndices = geometry.skinIndices;
		var skinWeights = geometry.skinWeights;

		var hasSkinIndices = skinIndices.length === vertices.length;
		var hasSkinWeights = skinWeights.length === vertices.length;

		//

		for ( var i = 0; i < faces.length; i ++ ) {

			var face = faces[ i ];

			this.vertices.push( vertices[ face.a ], vertices[ face.b ], vertices[ face.c ] );

			var vertexNormals = face.vertexNormals;

			if ( vertexNormals.length === 3 ) {

				this.normals.push( vertexNormals[ 0 ], vertexNormals[ 1 ], vertexNormals[ 2 ] );

			} else {

				var normal = face.normal;

				this.normals.push( normal, normal, normal );

			}

			var vertexColors = face.vertexColors;

			if ( vertexColors.length === 3 ) {

				this.colors.push( vertexColors[ 0 ], vertexColors[ 1 ], vertexColors[ 2 ] );

			} else {

				var color = face.color;

				this.colors.push( color, color, color );

			}

			if ( hasFaceVertexUv === true ) {

				var vertexUvs = faceVertexUvs[ 0 ][ i ];

				if ( vertexUvs !== undefined ) {

					this.uvs.push( vertexUvs[ 0 ], vertexUvs[ 1 ], vertexUvs[ 2 ] );

				} else {

					console.warn( 'THREE.DirectGeometry.fromGeometry(): Undefined vertexUv ', i );

					this.uvs.push( new THREE.Vector2(), new THREE.Vector2(), new THREE.Vector2() );

				}

			}

			if ( hasFaceVertexUv2 === true ) {

				var vertexUvs = faceVertexUvs[ 1 ][ i ];

				if ( vertexUvs !== undefined ) {

					this.uvs2.push( vertexUvs[ 0 ], vertexUvs[ 1 ], vertexUvs[ 2 ] );

				} else {

					console.warn( 'THREE.DirectGeometry.fromGeometry(): Undefined vertexUv2 ', i );

					this.uvs2.push( new THREE.Vector2(), new THREE.Vector2(), new THREE.Vector2() );

				}

			}

			if ( hasTangents === true ) {

				var vertexTangents = face.vertexTangents;

				if ( vertexTangents.length === 3 ) {

					this.tangents.push( vertexTangents[ 0 ], vertexTangents[ 1 ], vertexTangents[ 2 ] );

				} else {

					console.warn( 'THREE.DirectGeometry.fromGeometry(): Undefined tangents ', i );

					this.tangents.push( new THREE.Vector4(), new THREE.Vector4(), new THREE.Vector4() );

				}

			}

			// morphs

			for ( var j = 0; j < morphTargetsLength; j ++ ) {

				var morphTarget = morphTargets[ j ].vertices;

				this.morphTargets[ j ].push( morphTarget[ face.a ], morphTarget[ face.b ], morphTarget[ face.c ] );

			}
			/*
			for ( var j = 0; j < morphNormalsLength; j ++ ) {

				var morphNormal = morphNormals[ j ].vertexNormals[ i ];

				this.morphNormals[ j ].push( morphNormal.a, morphNormal.b, morphNormal.c );

			}

			for ( var j = 0; j < morphColorsLength; j ++ ) {

				var morphColor = morphColors[ j ].colors;

				this.morphColors[ j ].push( morphColor[ face.a ], morphColor[ face.b ], morphColor[ face.c ] );

			}
			*/

			// skins

			if ( hasSkinIndices ) {

				this.skinIndices.push( skinIndices[ face.a ], skinIndices[ face.b ], skinIndices[ face.c ] );

			}

			if ( hasSkinWeights ) {

				this.skinWeights.push( skinWeights[ face.a ], skinWeights[ face.b ], skinWeights[ face.c ] );

			}

		}

		this.verticesNeedUpdate = geometry.verticesNeedUpdate;
		this.normalsNeedUpdate = geometry.normalsNeedUpdate;
		this.colorsNeedUpdate = geometry.colorsNeedUpdate;
		this.uvsNeedUpdate = geometry.uvsNeedUpdate;
		this.tangentsNeedUpdate = geometry.tangentsNeedUpdate;

		return this;

	},

	dispose: function () {

		this.dispatchEvent( { type: 'dispose' } );

	}

};

THREE.EventDispatcher.prototype.apply( THREE.DirectGeometry.prototype );
<commit_msg>two colors declaration<commit_after>/**
 * @author mrdoob / http://mrdoob.com/
 */

THREE.DirectGeometry = function () {

	Object.defineProperty( this, 'id', { value: THREE.GeometryIdCount ++ } );

	this.uuid = THREE.Math.generateUUID();

	this.name = '';
	this.type = 'DirectGeometry';

	this.indices = [];
	this.vertices = [];
	this.normals = [];
	this.colors = [];
	this.uvs = [];
	this.uvs2 = [];
	this.tangents = [];

	this.morphTargets = [];
	this.morphColors = [];
	this.morphNormals = [];

	this.skinWeights = [];
	this.skinIndices = [];

	// this.lineDistances = [];

	this.boundingBox = null;
	this.boundingSphere = null;

	// update flags

	this.verticesNeedUpdate = false;
	this.normalsNeedUpdate = false;
	this.colorsNeedUpdate = false;
	this.uvsNeedUpdate = false;
	this.tangentsNeedUpdate = false;

};

THREE.DirectGeometry.prototype = {

	constructor: THREE.DirectGeometry,

	computeBoundingBox: THREE.Geometry.prototype.computeBoundingBox,
	computeBoundingSphere: THREE.Geometry.prototype.computeBoundingSphere,

	computeFaceNormals: function () {

		console.warn( 'THREE.DirectGeometry: computeFaceNormals() is not a method of this type of geometry.' );
		return this;

	},

	computeVertexNormals: function () {

		console.warn( 'THREE.DirectGeometry: computeVertexNormals() is not a method of this type of geometry.' );
		return this;

	},

	computeTangents: function () {

		console.warn( 'THREE.DirectGeometry: computeTangents() is not a method of this type of geometry.' );
		return this;

	},

	fromGeometry: function ( geometry ) {

		var faces = geometry.faces;
		var vertices = geometry.vertices;
		var faceVertexUvs = geometry.faceVertexUvs;

		var hasFaceVertexUv = faceVertexUvs[ 0 ] && faceVertexUvs[ 0 ].length > 0;
		var hasFaceVertexUv2 = faceVertexUvs[ 1 ] && faceVertexUvs[ 1 ].length > 0;

		var hasTangents = geometry.hasTangents;

		// morphs

		var morphTargets = geometry.morphTargets;
		var morphTargetsLength = morphTargets.length;

		for ( var i = 0; i < morphTargetsLength; i ++ ) {

			this.morphTargets[ i ] = [];

		}

		var morphNormals = geometry.morphNormals;
		var morphNormalsLength = morphNormals.length;

		for ( var i = 0; i < morphNormalsLength; i ++ ) {

			this.morphNormals[ i ] = [];

		}

		var morphColors = geometry.morphColors;
		var morphColorsLength = morphColors.length;

		for ( var i = 0; i < morphColorsLength; i ++ ) {

			this.morphColors[ i ] = [];

		}

		// skins

		var skinIndices = geometry.skinIndices;
		var skinWeights = geometry.skinWeights;

		var hasSkinIndices = skinIndices.length === vertices.length;
		var hasSkinWeights = skinWeights.length === vertices.length;

		//

		for ( var i = 0; i < faces.length; i ++ ) {

			var face = faces[ i ];

			this.vertices.push( vertices[ face.a ], vertices[ face.b ], vertices[ face.c ] );

			var vertexNormals = face.vertexNormals;

			if ( vertexNormals.length === 3 ) {

				this.normals.push( vertexNormals[ 0 ], vertexNormals[ 1 ], vertexNormals[ 2 ] );

			} else {

				var normal = face.normal;

				this.normals.push( normal, normal, normal );

			}

			var vertexColors = face.vertexColors;

			if ( vertexColors.length === 3 ) {

				this.colors.push( vertexColors[ 0 ], vertexColors[ 1 ], vertexColors[ 2 ] );

			} else {

				var color = face.color;

				this.colors.push( color, color, color );

			}

			if ( hasFaceVertexUv === true ) {

				var vertexUvs = faceVertexUvs[ 0 ][ i ];

				if ( vertexUvs !== undefined ) {

					this.uvs.push( vertexUvs[ 0 ], vertexUvs[ 1 ], vertexUvs[ 2 ] );

				} else {

					console.warn( 'THREE.DirectGeometry.fromGeometry(): Undefined vertexUv ', i );

					this.uvs.push( new THREE.Vector2(), new THREE.Vector2(), new THREE.Vector2() );

				}

			}

			if ( hasFaceVertexUv2 === true ) {

				var vertexUvs = faceVertexUvs[ 1 ][ i ];

				if ( vertexUvs !== undefined ) {

					this.uvs2.push( vertexUvs[ 0 ], vertexUvs[ 1 ], vertexUvs[ 2 ] );

				} else {

					console.warn( 'THREE.DirectGeometry.fromGeometry(): Undefined vertexUv2 ', i );

					this.uvs2.push( new THREE.Vector2(), new THREE.Vector2(), new THREE.Vector2() );

				}

			}

			if ( hasTangents === true ) {

				var vertexTangents = face.vertexTangents;

				if ( vertexTangents.length === 3 ) {

					this.tangents.push( vertexTangents[ 0 ], vertexTangents[ 1 ], vertexTangents[ 2 ] );

				} else {

					console.warn( 'THREE.DirectGeometry.fromGeometry(): Undefined tangents ', i );

					this.tangents.push( new THREE.Vector4(), new THREE.Vector4(), new THREE.Vector4() );

				}

			}

			// morphs

			for ( var j = 0; j < morphTargetsLength; j ++ ) {

				var morphTarget = morphTargets[ j ].vertices;

				this.morphTargets[ j ].push( morphTarget[ face.a ], morphTarget[ face.b ], morphTarget[ face.c ] );

			}
			/*
			for ( var j = 0; j < morphNormalsLength; j ++ ) {

				var morphNormal = morphNormals[ j ].vertexNormals[ i ];

				this.morphNormals[ j ].push( morphNormal.a, morphNormal.b, morphNormal.c );

			}

			for ( var j = 0; j < morphColorsLength; j ++ ) {

				var morphColor = morphColors[ j ].colors;

				this.morphColors[ j ].push( morphColor[ face.a ], morphColor[ face.b ], morphColor[ face.c ] );

			}
			*/

			// skins

			if ( hasSkinIndices ) {

				this.skinIndices.push( skinIndices[ face.a ], skinIndices[ face.b ], skinIndices[ face.c ] );

			}

			if ( hasSkinWeights ) {

				this.skinWeights.push( skinWeights[ face.a ], skinWeights[ face.b ], skinWeights[ face.c ] );

			}

		}

		this.verticesNeedUpdate = geometry.verticesNeedUpdate;
		this.normalsNeedUpdate = geometry.normalsNeedUpdate;
		this.colorsNeedUpdate = geometry.colorsNeedUpdate;
		this.uvsNeedUpdate = geometry.uvsNeedUpdate;
		this.tangentsNeedUpdate = geometry.tangentsNeedUpdate;

		return this;

	},

	dispose: function () {

		this.dispatchEvent( { type: 'dispose' } );

	}

};

THREE.EventDispatcher.prototype.apply( THREE.DirectGeometry.prototype );
<|endoftext|>