<commit_before>// @format

import React from 'react'
import { shallow } from 'enzyme'
import moment from 'moment'

import {
  ChatHistory,
  sendReadIfExistNonRead,
  withLifecycleFactory,
  scrollToBottom,
} from './ChatHistory'
import Balloon from './Balloon'

describe('sendReadIfExistNonRead', () => {
  it('calls sendRead when the last message is not read by the current user', () => {
    const props = {
      sendRead: jest.fn(),
      entities: {
        messages: {
          byId: {
            5: {
              id: 5,
              readBy: [1, 3, 5],
              createdAt: moment('2018-01-10T21:57:29+09:00').valueOf(),
            },
            6: {
              id: 6,
              readBy: [1, 3, 5],
              createdAt: moment('2018-01-13T21:57:29+09:00').valueOf(),
            },
          },
          byRoomId: {
            3: [5, 6],
          },
        },
      },
      currentRoomId: 3,
      myId: 2,
    }
    sendReadIfExistNonRead(props)
    expect(props.sendRead).toHaveBeenCalledWith(
      props.currentRoomId,
      props.entities.messages.byId[6].createdAt
    )
  })

  it('doest NOT call sendRead when the last message has already been read by the current user', () => {
    const props = {
      sendRead: jest.fn(),
      entities: {
        messages: {
          byId: {
            5: {
              id: 5,
              // actually the situation should not exist where the last message is read but previous ones are not.
              // But this is just to test if the method only looks at the last message
              readBy: [1, 3, 5],
              createdAt: moment('2018-01-10T21:57:29+09:00').valueOf(),
            },
            6: {
              id: 6,
              readBy: [1, 2, 3, 5],
              createdAt: moment('2018-01-13T21:57:29+09:00').valueOf(),
            },
          },
          byRoomId: {
            3: [5, 6],
          },
        },
      },
      currentRoomId: 3,
      myId: 2,
    }
    sendReadIfExistNonRead(props)
    expect(props.sendRead).not.toHaveBeenCalled()
  })

  it('doest NOT call sendRead when the current room does not have any message', () => {
    const props = {
      sendRead: jest.fn(),
      entities: {
        messages: {
          byId: {},
          byRoomId: {},
        },
      },
      currentRoomId: 3,
      myId: 2,
    }
    sendReadIfExistNonRead(props)
    expect(props.sendRead).not.toHaveBeenCalled()
  })
})

describe('withLifecycle', () => {
  it('calls sendReadIfExistNonRead and scrollToBottom when component is mounted', () => {
    const sendReadIfExistNonRead = jest.fn()
    const scrollToBottom = jest.fn()
    const BaseComponent = () =>
dummy component

    const Component = withLifecycleFactory(
      sendReadIfExistNonRead,
      scrollToBottom
    )(BaseComponent)
    const props = {
      a: 2,
      b: 3,
      refs: {
        messageList: {
          a: 2,
        },
      },
    }
    shallow(<Component {...props} />)
    expect(sendReadIfExistNonRead).toHaveBeenCalledWith(props)
    expect(scrollToBottom).toHaveBeenCalledWith(props.refs.messageList)
  })

  it('calls sendReadIfExistNonRead and scrollToBottom when the room is changed', () => {
    const sendReadIfExistNonRead = jest.fn()
    const scrollToBottom = jest.fn()
    const BaseComponent = () =>
dummy component

    const Component = withLifecycleFactory(
      sendReadIfExistNonRead,
      scrollToBottom
    )(BaseComponent)
    const constantProps = {
      entities: {
        messages: {
          byId: {
            5: {
              id: 5,
              readBy: [1, 3, 5],
              createdAt: moment('2018-01-10T21:57:29+09:00').valueOf(),
            },
            6: {
              id: 6,
              readBy: [1, 3, 5],
              createdAt: moment('2018-01-13T21:57:29+09:00').valueOf(),
            },
          },
          byRoomId: {
            3: [5, 6],
          },
        },
      },
      refs: {
        messageList: {
          scrollTop: 10,
          scrollHeight: 10,
          clientHeight: 10,
        },
      },
    }
    const props = {
      currentRoomId: 2,
      ...constantProps,
    }
    const nextProps = {
      currentRoomId: 3,
      ...constantProps,
    }

    const wrapper = shallow(<Component {...props} />, {
      lifecycleExperimental: true,
    })
    wrapper.setProps(nextProps)
    expect(sendReadIfExistNonRead).toHaveBeenCalledTimes(2)
    expect(sendReadIfExistNonRead.mock.calls[1]).toEqual([nextProps])
    expect(scrollToBottom).toHaveBeenCalledTimes(2)
    expect(scrollToBottom.mock.calls[1]).toEqual([nextProps.refs.messageList])
  })

  it('calls sendReadIfExistNonRead when the message length is changed', () => {
    const sendReadIfExistNonRead = jest.fn()
    const BaseComponent = () =>
dummy component

    const Component = withLifecycleFactory(sendReadIfExistNonRead, () => {})(
      // scrollToBottom is set as empty func
      BaseComponent
    )
    const constantProps = {
      currentRoomId: 3,
      refs: {
        messageList: {
          scrollTop: 10,
          scrollHeight: 10,
          clientHeight: 10,
        },
      },
    }
    const props = {
      ...constantProps,
      entities: {
        messages: {
          byId: {
            5: {
              id: 5,
              readBy: [1, 3, 5],
              createdAt: moment('2018-01-10T21:57:29+09:00').valueOf(),
            },
          },
          byRoomId: {
            3: [5],
          },
        },
      },
    }
    const nextProps = {
      ...constantProps,
      entities: {
        messages: {
          byId: {
            5: {
              id: 5,
              readBy: [1, 3, 5],
              createdAt: moment('2018-01-10T21:57:29+09:00').valueOf(),
            },
            6: {
              id: 6,
              readBy: [1, 3, 5],
              createdAt: moment('2018-01-13T21:57:29+09:00').valueOf(),
            },
          },
          byRoomId: {
            3: [5, 6],
          },
        },
      },
    }

    const wrapper = shallow(<Component {...props} />, {
      lifecycleExperimental: true,
    })
    wrapper.setProps(nextProps)
    expect(sendReadIfExistNonRead).toHaveBeenCalledTimes(2)
    expect(sendReadIfExistNonRead.mock.calls[1]).toEqual([nextProps])
  })

  it('calls sendReadIfExistNonRead when it receives the first message in the room', () => {
    const sendReadIfExistNonRead = jest.fn()
    const BaseComponent = () =>
dummy component

    const Component = withLifecycleFactory(sendReadIfExistNonRead, () => {})(
      // scrollToBottom is set as empty func
      BaseComponent
    )
    const constantProps = {
      currentRoomId: 3,
      refs: {
        messageList: {
          scrollTop: 10,
          scrollHeight: 10,
          clientHeight: 10,
        },
      },
    }
    const props = {
      ...constantProps,
      entities: {
        messages: {
          byId: {},
          byRoomId: {},
        },
      },
    }
    const nextProps = {
      ...constantProps,
      entities: {
        messages: {
          byId: {
            5: {
              id: 5,
              readBy: [1, 3, 5],
              createdAt: moment('2018-01-10T21:57:29+09:00').valueOf(),
            },
          },
          byRoomId: {
            3: [5],
          },
        },
      },
    }

    const wrapper = shallow(<Component {...props} />, {
      lifecycleExperimental: true,
    })
    wrapper.setProps(nextProps)
    expect(sendReadIfExistNonRead).toHaveBeenCalledTimes(2)
    expect(sendReadIfExistNonRead.mock.calls[1]).toEqual([nextProps])
  })
})

describe('ChatHistory', () => {
  it('shows chat history given messages in entity', () => {
    const props = {
      currentRoomId: 5,
      entities: {
        users: {
          byId: {
            '2': {
              id: 2,
              username: 'user2',
            },
            '3': {
              id: 3,
              username: 'user3',
            },
          },
        },
        messages: {
          byRoomId: {
            '5': [1, 3, 5],
          },
          byId: {
            '1': {
              id: 1,
              userId: 2,
              text: 'first message',
              createdAt: 'date1', // createdAt actuall should be # of milliseconds
            },
            '3': {
              id: 3,
              userId: 3,
              text: 'second message',
              createdAt: 'date2', // createdAt actuall should be # of milliseconds
            },
            '5': {
              id: 5,
              userId: 2,
              text: 'third message',
              createdAt: 'date3', // createdAt actuall should be # of milliseconds
            },
          },
        },
      },
      session: {
        user: {
          id: 2,
        },
      },
      classes: {
        root: 'chatHistory',
      },
    }

    const wrapper = shallow(<ChatHistory {...props} />)
    const historyContainer = wrapper.find('.' + props.classes.root)
    expect(historyContainer).toHaveLength(1)
    const balloons = historyContainer.find(Balloon)
    expect(balloons).toHaveLength(3)

    expect(balloons.at(0).prop('message')).toBe(props.entities.messages.byId[1])
    expect(balloons.at(1).prop('message')).toBe(props.entities.messages.byId[3])
    expect(balloons.at(2).prop('message')).toBe(props.entities.messages.byId[5])
  })
})
<commit_msg>Test handleScroll<commit_after>// @format

import React from 'react'
import { shallow, mount } from 'enzyme'
import moment from 'moment'

import {
  withChatHistoryHandlers,
  ChatHistory,
  sendReadIfExistNonRead,
  withLifecycleFactory,
  scrollToBottom,
} from './ChatHistory'
import Balloon from './Balloon'

describe('handleScroll', () => {
  it('calls fetchHistory with current room id and oldest message timestamp when the scroll bar is at the top', () => {
    const BaseComponent = ({ handleScroll }) => {
      handleScroll({ target: { scrollTop: 0 } })
      return
hoge

    }
    const Component = withChatHistoryHandlers(BaseComponent)
    const props = {
      currentRoomId: 3,
      fetchHistory: jest.fn(),
      entities: { rooms: { byId: { 3: { oldestMessageTimestamp: 10000 } } } },
    }
    const wrapper = mount(<Component {...props} />)
    expect(props.fetchHistory).toHaveBeenCalledWith(
      props.currentRoomId,
      props.entities.rooms.byId[3].oldestMessageTimestamp
    )
  })

  it('does NOT call fetchHistory when the scroll bar is NOT at the top', () => {
    const BaseComponent = ({ handleScroll }) => {
      handleScroll({ target: { scrollTop: 100 } })
      return
hoge

    }
    const Component = withChatHistoryHandlers(BaseComponent)
    const props = {
      currentRoomId: 3,
      fetchHistory: jest.fn(),
      entities: { rooms: { byId: { 3: { oldestMessageTimestamp: 10000 } } } },
    }
    const wrapper = mount(<Component {...props} />)
    expect(props.fetchHistory).not.toHaveBeenCalled()
  })
})

describe('sendReadIfExistNonRead', () => {
  it('calls sendRead when the last message is not read by the current user', () => {
    const props = {
      sendRead: jest.fn(),
      entities: {
        messages: {
          byId: {
            5: {
              id: 5,
              readBy: [1, 3, 5],
              createdAt: moment('2018-01-10T21:57:29+09:00').valueOf(),
            },
            6: {
              id: 6,
              readBy: [1, 3, 5],
              createdAt: moment('2018-01-13T21:57:29+09:00').valueOf(),
            },
          },
          byRoomId: {
            3: [5, 6],
          },
        },
      },
      currentRoomId: 3,
      myId: 2,
    }
    sendReadIfExistNonRead(props)
    expect(props.sendRead).toHaveBeenCalledWith(
      props.currentRoomId,
      props.entities.messages.byId[6].createdAt
    )
  })

  it('doest NOT call sendRead when the last message has already been read by the current user', () => {
    const props = {
      sendRead: jest.fn(),
      entities: {
        messages: {
          byId: {
            5: {
              id: 5,
              // actually the situation should not exist where the last message is read but previous ones are not.
              // But this is just to test if the method only looks at the last message
              readBy: [1, 3, 5],
              createdAt: moment('2018-01-10T21:57:29+09:00').valueOf(),
            },
            6: {
              id: 6,
              readBy: [1, 2, 3, 5],
              createdAt: moment('2018-01-13T21:57:29+09:00').valueOf(),
            },
          },
          byRoomId: {
            3: [5, 6],
          },
        },
      },
      currentRoomId: 3,
      myId: 2,
    }
    sendReadIfExistNonRead(props)
    expect(props.sendRead).not.toHaveBeenCalled()
  })

  it('doest NOT call sendRead when the current room does not have any message', () => {
    const props = {
      sendRead: jest.fn(),
      entities: {
        messages: {
          byId: {},
          byRoomId: {},
        },
      },
      currentRoomId: 3,
      myId: 2,
    }
    sendReadIfExistNonRead(props)
    expect(props.sendRead).not.toHaveBeenCalled()
  })
})

describe('withLifecycle', () => {
  it('calls sendReadIfExistNonRead and scrollToBottom when component is mounted', () => {
    const sendReadIfExistNonRead = jest.fn()
    const scrollToBottom = jest.fn()
    const BaseComponent = () =>
dummy component

    const Component = withLifecycleFactory(
      sendReadIfExistNonRead,
      scrollToBottom
    )(BaseComponent)
    const props = {
      a: 2,
      b: 3,
      refs: {
        messageList: {
          a: 2,
        },
      },
    }
    shallow(<Component {...props} />)
    expect(sendReadIfExistNonRead).toHaveBeenCalledWith(props)
    expect(scrollToBottom).toHaveBeenCalledWith(props.refs.messageList)
  })

  it('calls sendReadIfExistNonRead and scrollToBottom when the room is changed', () => {
    const sendReadIfExistNonRead = jest.fn()
    const scrollToBottom = jest.fn()
    const BaseComponent = () =>
dummy component

    const Component = withLifecycleFactory(
      sendReadIfExistNonRead,
      scrollToBottom
    )(BaseComponent)
    const constantProps = {
      entities: {
        messages: {
          byId: {
            5: {
              id: 5,
              readBy: [1, 3, 5],
              createdAt: moment('2018-01-10T21:57:29+09:00').valueOf(),
            },
            6: {
              id: 6,
              readBy: [1, 3, 5],
              createdAt: moment('2018-01-13T21:57:29+09:00').valueOf(),
            },
          },
          byRoomId: {
            3: [5, 6],
          },
        },
      },
      refs: {
        messageList: {
          scrollTop: 10,
          scrollHeight: 10,
          clientHeight: 10,
        },
      },
    }
    const props = {
      currentRoomId: 2,
      ...constantProps,
    }
    const nextProps = {
      currentRoomId: 3,
      ...constantProps,
    }

    const wrapper = shallow(<Component {...props} />, {
      lifecycleExperimental: true,
    })
    wrapper.setProps(nextProps)
    expect(sendReadIfExistNonRead).toHaveBeenCalledTimes(2)
    expect(sendReadIfExistNonRead.mock.calls[1]).toEqual([nextProps])
    expect(scrollToBottom).toHaveBeenCalledTimes(2)
    expect(scrollToBottom.mock.calls[1]).toEqual([nextProps.refs.messageList])
  })

  it('calls sendReadIfExistNonRead when the message length is changed', () => {
    const sendReadIfExistNonRead = jest.fn()
    const BaseComponent = () =>
dummy component

    const Component = withLifecycleFactory(sendReadIfExistNonRead, () => {})(
      // scrollToBottom is set as empty func
      BaseComponent
    )
    const constantProps = {
      currentRoomId: 3,
      refs: {
        messageList: {
          scrollTop: 10,
          scrollHeight: 10,
          clientHeight: 10,
        },
      },
    }
    const props = {
      ...constantProps,
      entities: {
        messages: {
          byId: {
            5: {
              id: 5,
              readBy: [1, 3, 5],
              createdAt: moment('2018-01-10T21:57:29+09:00').valueOf(),
            },
          },
          byRoomId: {
            3: [5],
          },
        },
      },
    }
    const nextProps = {
      ...constantProps,
      entities: {
        messages: {
          byId: {
            5: {
              id: 5,
              readBy: [1, 3, 5],
              createdAt: moment('2018-01-10T21:57:29+09:00').valueOf(),
            },
            6: {
              id: 6,
              readBy: [1, 3, 5],
              createdAt: moment('2018-01-13T21:57:29+09:00').valueOf(),
            },
          },
          byRoomId: {
            3: [5, 6],
          },
        },
      },
    }

    const wrapper = shallow(<Component {...props} />, {
      lifecycleExperimental: true,
    })
    wrapper.setProps(nextProps)
    expect(sendReadIfExistNonRead).toHaveBeenCalledTimes(2)
    expect(sendReadIfExistNonRead.mock.calls[1]).toEqual([nextProps])
  })

  it('calls sendReadIfExistNonRead when it receives the first message in the room', () => {
    const sendReadIfExistNonRead = jest.fn()
    const BaseComponent = () =>
dummy component

    const Component = withLifecycleFactory(sendReadIfExistNonRead, () => {})(
      // scrollToBottom is set as empty func
      BaseComponent
    )
    const constantProps = {
      currentRoomId: 3,
      refs: {
        messageList: {
          scrollTop: 10,
          scrollHeight: 10,
          clientHeight: 10,
        },
      },
    }
    const props = {
      ...constantProps,
      entities: {
        messages: {
          byId: {},
          byRoomId: {},
        },
      },
    }
    const nextProps = {
      ...constantProps,
      entities: {
        messages: {
          byId: {
            5: {
              id: 5,
              readBy: [1, 3, 5],
              createdAt: moment('2018-01-10T21:57:29+09:00').valueOf(),
            },
          },
          byRoomId: {
            3: [5],
          },
        },
      },
    }

    const wrapper = shallow(<Component {...props} />, {
      lifecycleExperimental: true,
    })
    wrapper.setProps(nextProps)
    expect(sendReadIfExistNonRead).toHaveBeenCalledTimes(2)
    expect(sendReadIfExistNonRead.mock.calls[1]).toEqual([nextProps])
  })
})

describe('ChatHistory', () => {
  it('shows chat history given messages in entity', () => {
    const props = {
      currentRoomId: 5,
      entities: {
        users: {
          byId: {
            '2': {
              id: 2,
              username: 'user2',
            },
            '3': {
              id: 3,
              username: 'user3',
            },
          },
        },
        messages: {
          byRoomId: {
            '5': [1, 3, 5],
          },
          byId: {
            '1': {
              id: 1,
              userId: 2,
              text: 'first message',
              createdAt: 'date1', // createdAt actuall should be # of milliseconds
            },
            '3': {
              id: 3,
              userId: 3,
              text: 'second message',
              createdAt: 'date2', // createdAt actuall should be # of milliseconds
            },
            '5': {
              id: 5,
              userId: 2,
              text: 'third message',
              createdAt: 'date3', // createdAt actuall should be # of milliseconds
            },
          },
        },
      },
      session: {
        user: {
          id: 2,
        },
      },
      classes: {
        root: 'chatHistory',
      },
    }

    const wrapper = shallow(<ChatHistory {...props} />)
    const historyContainer = wrapper.find('.' + props.classes.root)
    expect(historyContainer).toHaveLength(1)
    const balloons = historyContainer.find(Balloon)
    expect(balloons).toHaveLength(3)

    expect(balloons.at(0).prop('message')).toBe(props.entities.messages.byId[1])
    expect(balloons.at(1).prop('message')).toBe(props.entities.messages.byId[3])
    expect(balloons.at(2).prop('message')).toBe(props.entities.messages.byId[5])
  })
})
<|endoftext|>


<commit_before>import styled from 'styled-components';
import Color from 'color';

const StyledHome = styled.div`
  header {
    position: relative;
    background: ${props => props.theme.colors.secondary};
    background: linear-gradient(0deg, white, ${props => Color(props.theme.colors.secondary).mix(Color('black')).hex()});
    width: 100%;
    padding-top: 20px;
    padding-bottom: 100px;
  }
  div.header-image {
    position: relative;
    max-width: 648px;
    width: 90vw;
    left: 50%;
    transform: translate(-50%, 0);
  }

  footer {
    float: left;
    position: relative;
    width: 100%;
    height: 100px;
    background: ${props => props.theme.colors.secondary};
    background: linear-gradient(180deg, white, ${props => Color(props.theme.colors.secondary).mix(Color('black')).hex()});
    p {
      position: absolute;
      right: 5%;
      bottom: 10px;
      font-size: 0.8em;
      line-height: 1em;
    }
  }

  .title {
    display: inline-block;
    font-size: 2em;
    line-height: 1em;
    margin: 0.5em 0 0.5em 0;
    font-family: ${props => props.theme.font.trebuchet};
  }
  div.title-icon {
    display: inline-block;
    position: static;
    margin: 0.5em 0 0.5em 1em;
    @media (min-width: ${props => props.theme.breakpoints.m + 1}px) {
      position: relative;
      vertical-align: top;
      transform: translate(0, 25%);
    }
  }
  div.title-icon:hover .icon-label {
    @media (min-width: ${props => props.theme.breakpoints.m + 1}px) {
      display: block;
    }
  }
  .title-icon .icon {
    display: inline-block;
    height: 2em;
    width: 2em;
  }
  .title-icon .icon-label {
    display: inline-block;
    margin: 0 0 0 10px;
    vertical-align: top;
    transform: translate(0, 50%);
    @media (min-width: ${props => props.theme.breakpoints.m + 1}px) {
      display: none;
      position: absolute;
      left: 50%;
      transform: translate(-50%, 0);
      margin: 0;
      padding: 10px;
      border: 1px solid rgba(0,0,0,0.1);
      border-radius: 10px;
      box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
      z-index: 1;
      background-color: ${props => props.theme.colors.background};
      font-size: .8em;
      white-space: nowrap;
    }
  }
  .title-icon a {
    color: inherit;
    text-decoration: inherit;
  }

  .content-container {
    position: relative;
    margin: auto;
    padding-left: 2em;
    padding-right: 2em;
    max-width: ${props => props.theme.breakpoints.l}px;
    background-color: ${props => props.theme.colors.background};
    color: ${props => {
      let background = Color(props.theme.colors.background);
      let base = Color(background.dark() ? 'white' : 'black');
      let alpha = background.light() ? props.theme.alphaDarkText.primary : props.theme.alphaLightText.primary;
      return base.mix(background, alpha).hex();
    }};
    font-family: ${props => props.theme.font.standard};
    line-height: 1.2;
    font-size: 18px;
    @media (min-width: ${props => props.theme.breakpoints.s + 1}px) and (max-width: ${props => props.theme.breakpoints.m}px) {
      font-size: 20px;
    }
    @media (min-width: ${props => props.theme.breakpoints.m + 1}px) {
      font-size: 24px;
    }
  }
  .content {
    float: left;
    position: relative;
    @media (min-width: ${props => props.theme.breakpoints.m + 1}px) {
      width: 90%;
      margin-left: 5%;
      margin-right: 5%;
    }
  }
  .column {
    @media (min-width: ${props => props.theme.breakpoints.m + 1}px) {
      width: 40%;
    }
  }

  ol.events {
    list-style-type: none;
    padding: 0;
    margin: 0;
    li {
      line-height: 1.2em;
      padding: 0.5em;
      background: ${props => Color(props.theme.colors.secondary).mix(Color('white'), 0.1).hex()};
    }
    li:nth-child(odd) {
      background: ${props => Color(props.theme.colors.secondary).mix(Color('white'), 0.2).hex()};
    }
    li:hover {
      background: ${props => Color(props.theme.colors.secondary).mix(Color('white'), 0.3).hex()};
    }
    .location {
      display: inline;
      padding-right: ${.5 / .7}em;
      font-size: 0.7em;
      @media (min-width: ${props => props.theme.breakpoints.s + 1}px) and (max-width: ${props => props.theme.breakpoints.m}px) {
        float: right;
      }
      @media (min-width: ${props => props.theme.breakpoints.l + 1}px) {
        float: right;
      }
    }
    .location:before {
      @media (max-width: ${props => props.theme.breakpoints.s}px) {
        content: '';
        display: block;
      }
      @media (min-width: ${props => props.theme.breakpoints.m + 1}px) and (max-width: ${props => props.theme.breakpoints.l}px) {
        content: '';
        display: block;
      }
    }
  }
`;

export default StyledHome;
<commit_msg>variable name tweak<commit_after>import styled from 'styled-components';
import Color from 'color';

const Home = styled.div`
  header {
    position: relative;
    background: ${props => props.theme.colors.secondary};
    background: linear-gradient(0deg, white, ${props => Color(props.theme.colors.secondary).mix(Color('black')).hex()});
    width: 100%;
    padding-top: 20px;
    padding-bottom: 100px;
  }
  div.header-image {
    position: relative;
    max-width: 648px;
    width: 90vw;
    left: 50%;
    transform: translate(-50%, 0);
  }

  footer {
    float: left;
    position: relative;
    width: 100%;
    height: 100px;
    background: ${props => props.theme.colors.secondary};
    background: linear-gradient(180deg, white, ${props => Color(props.theme.colors.secondary).mix(Color('black')).hex()});
    p {
      position: absolute;
      right: 5%;
      bottom: 10px;
      font-size: 0.8em;
      line-height: 1em;
    }
  }

  .title {
    display: inline-block;
    font-size: 2em;
    line-height: 1em;
    margin: 0.5em 0 0.5em 0;
    font-family: ${props => props.theme.font.trebuchet};
  }
  div.title-icon {
    display: inline-block;
    position: static;
    margin: 0.5em 0 0.5em 1em;
    @media (min-width: ${props => props.theme.breakpoints.m + 1}px) {
      position: relative;
      vertical-align: top;
      transform: translate(0, 25%);
    }
  }
  div.title-icon:hover .icon-label {
    @media (min-width: ${props => props.theme.breakpoints.m + 1}px) {
      display: block;
    }
  }
  .title-icon .icon {
    display: inline-block;
    height: 2em;
    width: 2em;
  }
  .title-icon .icon-label {
    display: inline-block;
    margin: 0 0 0 10px;
    vertical-align: top;
    transform: translate(0, 50%);
    @media (min-width: ${props => props.theme.breakpoints.m + 1}px) {
      display: none;
      position: absolute;
      left: 50%;
      transform: translate(-50%, 0);
      margin: 0;
      padding: 10px;
      border: 1px solid rgba(0,0,0,0.1);
      border-radius: 10px;
      box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
      z-index: 1;
      background-color: ${props => props.theme.colors.background};
      font-size: .8em;
      white-space: nowrap;
    }
  }
  .title-icon a {
    color: inherit;
    text-decoration: inherit;
  }

  .content-container {
    position: relative;
    margin: auto;
    padding-left: 2em;
    padding-right: 2em;
    max-width: ${props => props.theme.breakpoints.l}px;
    background-color: ${props => props.theme.colors.background};
    color: ${props => {
      let background = Color(props.theme.colors.background);
      let base = Color(background.dark() ? 'white' : 'black');
      let alpha = background.light() ? props.theme.alphaDarkText.primary : props.theme.alphaLightText.primary;
      return base.mix(background, alpha).hex();
    }};
    font-family: ${props => props.theme.font.standard};
    line-height: 1.2;
    font-size: 18px;
    @media (min-width: ${props => props.theme.breakpoints.s + 1}px) and (max-width: ${props => props.theme.breakpoints.m}px) {
      font-size: 20px;
    }
    @media (min-width: ${props => props.theme.breakpoints.m + 1}px) {
      font-size: 24px;
    }
  }
  .content {
    float: left;
    position: relative;
    @media (min-width: ${props => props.theme.breakpoints.m + 1}px) {
      width: 90%;
      margin-left: 5%;
      margin-right: 5%;
    }
  }
  .column {
    @media (min-width: ${props => props.theme.breakpoints.m + 1}px) {
      width: 40%;
    }
  }

  ol.events {
    list-style-type: none;
    padding: 0;
    margin: 0;
    li {
      line-height: 1.2em;
      padding: 0.5em;
      background: ${props => Color(props.theme.colors.secondary).mix(Color('white'), 0.1).hex()};
    }
    li:nth-child(odd) {
      background: ${props => Color(props.theme.colors.secondary).mix(Color('white'), 0.2).hex()};
    }
    li:hover {
      background: ${props => Color(props.theme.colors.secondary).mix(Color('white'), 0.3).hex()};
    }
    .location {
      display: inline;
      padding-right: ${.5 / .7}em;
      font-size: 0.7em;
      @media (min-width: ${props => props.theme.breakpoints.s + 1}px) and (max-width: ${props => props.theme.breakpoints.m}px) {
        float: right;
      }
      @media (min-width: ${props => props.theme.breakpoints.l + 1}px) {
        float: right;
      }
    }
    .location:before {
      @media (max-width: ${props => props.theme.breakpoints.s}px) {
        content: '';
        display: block;
      }
      @media (min-width: ${props => props.theme.breakpoints.m + 1}px) and (max-width: ${props => props.theme.breakpoints.l}px) {
        content: '';
        display: block;
      }
    }
  }
`;

export default Home;
<|endoftext|>


<commit_before>import React, { Component, PropTypes } from 'react';
import bowser from 'bowser';
import classNames from 'classnames';
import THREE from 'three';
import TWEEN from 'tween.js';
import Stats from 'stats.js';
import lodashThrottle from 'lodash/throttle';
import lodashIsEqual from 'lodash/isEqual';

import Stripes from '../Stripes';

import { randomWithRange } from '../../businessLogic/mathHelpers';
import { averageRGB,
         currentColorInTween,
         getColorFromPosition,
         mergeTopAndBottomColors,
         randomRGB } from '../../businessLogic/threeHelpers';

import './orthofloat.scss';

export default class Orthofloat extends Component {
    constructor(props) {
        super(props);

        this.state = {
            windowHeight: window.innerHeight,
            windowWidth: window.innerWidth
        };
    }

    componentWillMount() {
        this.setVendorPrefix();
        this.stripeWidth = 25;
    }

    componentDidMount() {
        this.initializeScene();

        this.windowResizeFunc = lodashThrottle(() => this.onWindowResize(), 16.667);
        window.addEventListener('resize', this.windowResizeFunc);
    }

    componentWillReceiveProps(nextProps) {
        if (!lodashIsEqual(nextProps.bottomColor, this.props.bottomColor)) {
            const { randomShapeMaterialBottom, randomShapeMaterialTop, randomShapeMaterialMid } = this;
            const oldColors = mergeTopAndBottomColors(this.props.topColor, this.props.bottomColor);
            const newColors = mergeTopAndBottomColors(nextProps.topColor, nextProps.bottomColor);

            const tween = new TWEEN.Tween(oldColors)
                .to(newColors, 1000)
                .easing(TWEEN.Easing.Quadratic.Out)
                .onUpdate(function() {
                    randomShapeMaterialBottom.color = currentColorInTween(this, 'bottom');
                    randomShapeMaterialTop.color = currentColorInTween(this, 'top');

                    randomShapeMaterialMid.color = averageRGB(
                        randomShapeMaterialBottom.color,
                        randomShapeMaterialTop.color
                    );
                })
                .start();
        }
    }

    componentWillUnmount() {
        window.removeEventListener('resize', this.windowResizeFunc);
    }

    initializeScene() {
        this.scene = new THREE.Scene();

        this.initializeCamera();
        this.initializeMeshes();
        this.initializeLights();
        this.initializeRenderer();

        if (this.props.initializeWithStats) {
            this.initializeStats();
        }

        this.renderAnimation();
    }

    initializeCamera() {
        const { windowHeight, windowWidth } = this.state;
        this.camFactor = 16;
        this.camera = new THREE.OrthographicCamera(
            windowWidth / - this.camFactor,
            windowWidth / this.camFactor,
            windowHeight / this.camFactor,
            windowHeight / - this.camFactor,
            -200,
            500
        );
        this.camera.position.x = 120;
        this.camera.lookAt(this.scene.position);
    }

    initializeMeshes() {
        this.meshSize = 2.5;
        this.initializeRandomShapes();
        this.initializeTetrahedrons();
    }

    initializeRandomShapes() {
        const { bottomColor, topColor } = this.props;
        const threeTopColor = new THREE.Color(topColor.r, topColor.g, topColor.b);
        const threeBottomColor = new THREE.Color(bottomColor.r, bottomColor.g, bottomColor.b);
        this.randomShapeMaterialTop = new THREE.MeshBasicMaterial({ color: threeTopColor });
        this.randomShapeMaterialBottom = new THREE.MeshBasicMaterial({ color: threeBottomColor });
        const averageColor = averageRGB(this.randomShapeMaterialTop.color, this.randomShapeMaterialBottom.color);
        this.randomShapeMaterialMid = new THREE.MeshBasicMaterial({
            color: new THREE.Color(averageColor.r, averageColor.g, averageColor.b)
        });
        this.randomShapes = [];
        for (let i = 0; i < 53; i++) {
            let shape;
            switch (i % 5) {
                case 0:
                    shape = new THREE.Mesh(this.generateRandomGeometry(), this.randomShapeMaterialTop);
                    break;
                case 1:
                    shape = new THREE.Mesh(this.generateRandomGeometry(), this.randomShapeMaterialBottom);
                    break;
                case 2:
                    shape = new THREE.Mesh(this.generateRandomGeometry(), this.randomShapeMaterialMid);
                    break;
                case 3:
                    shape = new THREE.Mesh(
                        this.generateRandomGeometry(),
                        new THREE.MeshBasicMaterial({ color: threeTopColor })
                    );
                    shape.colorTopToBottom = true;
                    break;
                case 4:
                default:
                    shape = new THREE.Mesh(
                        this.generateRandomGeometry(),
                        new THREE.MeshBasicMaterial({ color: threeBottomColor })
                    );
                    shape.colorBottomToTop = true;
            }
            this.setMeshInSpace(shape);
            this.randomShapes.push(shape);
            this.scene.add(shape);
        }
    }

    generateRandomGeometry() {
        const meshSize = randomWithRange(0.25, 2) * this.meshSize;
        const cylRadius = randomWithRange(meshSize / 2, meshSize);

        switch (Math.floor(Math.random() * 11)) {
            case 10:
                return new THREE.TorusKnotGeometry(
                    meshSize, meshSize / 3, 32, 12, randomWithRange(1, 7), randomWithRange(1, 10)
                );
            case 9:
                return new THREE.TorusGeometry(
                    meshSize, meshSize / 4, 16, 16, randomWithRange(Math.PI / 2, Math.PI * 3)
                );
            case 8:
                return new THREE.SphereGeometry(
                    meshSize,
                    16,
                    16,
                    0,
                    randomWithRange(Math.PI / 2, Math.PI * 3),
                    0,
                    randomWithRange(Math.PI / 2, Math.PI)
                );
            case 7:
                return new THREE.RingGeometry(
                    meshSize / 2, meshSize, 16, 1, 0, randomWithRange(Math.PI / 2, Math.PI * 3)
                );
            case 6:
                return new THREE.PlaneGeometry(meshSize, meshSize, 1, 1);
            case 5:
                return new THREE.OctahedronGeometry(meshSize, 0);
            case 4:
                return new THREE.IcosahedronGeometry(meshSize, 0);
            case 3:
                return new THREE.DodecahedronGeometry(meshSize, 0);
            case 2:
                return new THREE.CylinderGeometry(
                    cylRadius,
                    cylRadius,
                    randomWithRange(meshSize / 2, meshSize * 1.5),
                    16,
                    1,
                    Math.random() < 0.5,
                    0,
                    randomWithRange(Math.PI / 2, Math.PI * 3)
                );
            case 1:
                return new THREE.CircleGeometry(meshSize, 16);
            case 0:
            default:
                return new THREE.BoxGeometry(meshSize, meshSize, meshSize);
        }
    }

    initializeTetrahedrons() {
        const tetraGeometry = new THREE.TetrahedronGeometry(this.meshSize);
        const color = new THREE.Color(0xff6ba9);
        const tetraMaterial = new THREE.MeshBasicMaterial({color});
        this.tetras = [];
        for (let i = 0; i < 50; i++) {
            const tetra = new THREE.Mesh(tetraGeometry, tetraMaterial);
            this.setMeshInSpace(tetra);
            this.tetras.push(tetra);
            this.scene.add(tetra);
        }
    }

    setMeshInSpace(mesh) {
        const { windowHeight, windowWidth } = this.state;
        mesh.position.y = randomWithRange(windowHeight / 16, windowHeight / -16);
        mesh.position.z = randomWithRange(windowWidth / 16, windowWidth / -16);
        mesh.rotation.set(randomWithRange(0, Math.PI), randomWithRange(0, Math.PI), randomWithRange(0, Math.PI));
        mesh.rotationSpeed = {
            x: randomWithRange(-0.01, 0.01),
            y: randomWithRange(-0.01, 0.01),
            z: randomWithRange(-0.01, 0.01)
        };
        mesh.yVelocity = randomWithRange(0.03, 0.1);
        mesh.zVelocity = randomWithRange(-0.01, 0.01);
    }

    initializeLights() {
        const directionalLight = new THREE.DirectionalLight( 0xffffff, 0.7 );
        directionalLight.position.set(60, 60, 60);
        this.scene.add(directionalLight);

        const ambientLight = new THREE.AmbientLight(0x4B4B4B);
        this.scene.add(ambientLight);
    }

    initializeRenderer() {
        const { windowHeight, windowWidth } = this.state;
        this.renderer = new THREE.WebGLRenderer({ alpha: true });
        this.renderer.setSize(windowWidth, windowHeight);

        this.el.appendChild(this.renderer.domElement);
        this.renderer.render(this.scene, this.camera);
    }

    initializeStats() {
        this.stats = new Stats();
        this.el.appendChild(this.stats.dom);
    }

    setVendorPrefix() {
        if (bowser.webkit) {
            this.vendorPrefix = '-webkit-';
        } else if (bowser.firefox) {
            this.vendorPrefix = '-moz-';
        } else if (bowser.opera) {
            this.vendorPrefix = '-o-';
        } else {
            this.vendorPrefix = '';
        }
    }

    onWindowResize() {
        const windowHeight = window.innerHeight,
              windowWidth = window.innerWidth;

        this.setState({ windowHeight, windowWidth });

        this.renderer.setSize(windowWidth, windowHeight);

        this.camera.left = windowWidth / -this.camFactor;
        this.camera.right = windowWidth / this.camFactor;
        this.camera.top = windowHeight / this.camFactor;
        this.camera.bottom = windowHeight / -this.camFactor;
        this.camera.updateProjectionMatrix();
    }

    renderAnimation() {
        if (this.stats && this.props.showStats) {
            this.stats.begin();
        }

        const { windowHeight, windowWidth } = this.state;
        const { topColor, bottomColor } = this.props;

        for (let mesh of this.randomShapes) {
            // if shape is above top of window
            if (mesh.position.y > (windowHeight / 16 + this.meshSize * 4)) {
                mesh.geometry = this.generateRandomGeometry();
                this.repositionMeshToBottom(mesh);
            }

            this.moveMesh(mesh);

            if (mesh.colorTopToBottom) {
                mesh.material.color = getColorFromPosition(mesh.position.y, windowHeight, topColor, bottomColor);
            } else if (mesh.colorBottomToTop) {
                mesh.material.color = getColorFromPosition(mesh.position.y, windowHeight, bottomColor, topColor);
            }
        }

        for (let mesh of this.tetras) {
            // if shape is above top of window
            if (mesh.position.y > (windowHeight / 16 + this.meshSize * 4)) {
                this.repositionMeshToBottom(mesh);
            }

            this.moveMesh(mesh);
        }

        TWEEN.update();
        this.renderer.render(this.scene, this.camera);

        if (this.stats && this.props.showStats) {
            this.stats.end();
        }

        requestAnimationFrame(() => this.renderAnimation());
    }

    repositionMeshToBottom(mesh) {
        const { windowHeight, windowWidth } = this.state;
        // put it below the bottom of the window and give it a random z position
        mesh.position.y -= (windowHeight / 8 + this.meshSize * 8);
        mesh.position.z = randomWithRange(windowWidth / 16, windowWidth / -16);

        // give it new velocities
        mesh.yVelocity = randomWithRange(0.03, 0.1);
        mesh.zVelocity = randomWithRange(-0.01, 0.01);

        // give the mesh new rotation speeds
        for (let axis of ['x', 'y', 'z']) {
            mesh.rotationSpeed[axis] = randomWithRange(-0.01, 0.01);
        }
    }

    moveMesh(mesh) {
        const { windowHeight, windowWidth } = this.state;

        // translate the mesh
        mesh.position.y += mesh.yVelocity;
        mesh.position.z += mesh.zVelocity;

        // rotate the mesh around its axes
        for (let axis of ['x', 'y', 'z']) {
            mesh.rotation[axis] += mesh.rotationSpeed[axis];
        }
    }

    render() {
        const { showStats, topColor, bottomColor } = this.props;
        const { windowHeight, windowWidth } = this.state;
        const className = classNames('orthofloat-wrapper', { 'show-stats': showStats });
        const topColorStyle = (new THREE.Color(topColor.r, topColor.g, topColor.b)).getStyle();
        const bottomColorStyle = (new THREE.Color(bottomColor.r, bottomColor.g, bottomColor.b)).getStyle();
        const wrapperStyle = {
            backgroundImage: `${this.vendorPrefix}linear-gradient(${topColorStyle}, ${bottomColorStyle})`
        };

        return (
            <div className={className} style={wrapperStyle} ref={c => this.el = c}>
                <Stripes stripeWidth={this.stripeWidth}
                         vendorPrefix={this.vendorPrefix}
                         topColorStyle={topColorStyle}
                         bottomColorStyle={bottomColorStyle}
                         windowWidth={windowWidth} />
            </div>
        );
    }
}

Orthofloat.propTypes = {
    bottomColor: PropTypes.shape({
        r: PropTypes.number.isRequired,
        g: PropTypes.number.isRequired,
        b: PropTypes.number.isRequired
    }),
    topColor: PropTypes.shape({
        r: PropTypes.number.isRequired,
        g: PropTypes.number.isRequired,
        b: PropTypes.number.isRequired
    }),
    initializeWithStats: PropTypes.bool,
    showStats: PropTypes.bool
};

Orthofloat.defaultProps = {
    bottomColor: randomRGB(),
    topColor: randomRGB(),
    initializeWithStats: false,
    showStats: false
};<commit_msg>make componentWillReceiveProps smaller<commit_after>import React, { Component, PropTypes } from 'react';
import bowser from 'bowser';
import classNames from 'classnames';
import THREE from 'three';
import TWEEN from 'tween.js';
import Stats from 'stats.js';
import lodashThrottle from 'lodash/throttle';
import lodashIsEqual from 'lodash/isEqual';

import Stripes from '../Stripes';

import { randomWithRange } from '../../businessLogic/mathHelpers';
import { averageRGB,
         currentColorInTween,
         getColorFromPosition,
         mergeTopAndBottomColors,
         randomRGB } from '../../businessLogic/threeHelpers';

import './orthofloat.scss';

export default class Orthofloat extends Component {
    constructor(props) {
        super(props);

        this.state = {
            windowHeight: window.innerHeight,
            windowWidth: window.innerWidth
        };
    }

    componentWillMount() {
        this.setVendorPrefix();
        this.stripeWidth = 25;
    }

    componentDidMount() {
        this.initializeScene();

        this.windowResizeFunc = lodashThrottle(() => this.onWindowResize(), 16.667);
        window.addEventListener('resize', this.windowResizeFunc);
    }

    componentWillReceiveProps(nextProps) {
        if (!lodashIsEqual(nextProps.bottomColor, this.props.bottomColor)
            || !lodashIsEqual(nextProps.topColor, this.props.topColor)) {
            this.changeColors(nextProps.topColor, nextProps.bottomColor);
        }
    }

    componentWillUnmount() {
        window.removeEventListener('resize', this.windowResizeFunc);
    }

    changeColors(nextTopColor, nextBottomColor) {
        const { randomShapeMaterialBottom, randomShapeMaterialTop, randomShapeMaterialMid } = this;
        const oldColors = mergeTopAndBottomColors(this.props.topColor, this.props.bottomColor);
        const newColors = mergeTopAndBottomColors(nextTopColor, nextBottomColor);

        const tween = new TWEEN.Tween(oldColors)
            .to(newColors, 1000)
            .easing(TWEEN.Easing.Quadratic.Out)
            .onUpdate(function() {
                randomShapeMaterialBottom.color = currentColorInTween(this, 'bottom');
                randomShapeMaterialTop.color = currentColorInTween(this, 'top');

                randomShapeMaterialMid.color = averageRGB(
                    randomShapeMaterialBottom.color,
                    randomShapeMaterialTop.color
                );
            })
            .start();
    }

    initializeScene() {
        this.scene = new THREE.Scene();

        this.initializeCamera();
        this.initializeMeshes();
        this.initializeLights();
        this.initializeRenderer();

        if (this.props.initializeWithStats) {
            this.initializeStats();
        }

        this.renderAnimation();
    }

    initializeCamera() {
        const { windowHeight, windowWidth } = this.state;
        this.camFactor = 16;
        this.camera = new THREE.OrthographicCamera(
            windowWidth / - this.camFactor,
            windowWidth / this.camFactor,
            windowHeight / this.camFactor,
            windowHeight / - this.camFactor,
            -200,
            500
        );
        this.camera.position.x = 120;
        this.camera.lookAt(this.scene.position);
    }

    initializeMeshes() {
        this.meshSize = 2.5;
        this.initializeRandomShapes();
        this.initializeTetrahedrons();
    }

    initializeRandomShapes() {
        const { bottomColor, topColor } = this.props;
        const threeTopColor = new THREE.Color(topColor.r, topColor.g, topColor.b);
        const threeBottomColor = new THREE.Color(bottomColor.r, bottomColor.g, bottomColor.b);
        this.randomShapeMaterialTop = new THREE.MeshBasicMaterial({ color: threeTopColor });
        this.randomShapeMaterialBottom = new THREE.MeshBasicMaterial({ color: threeBottomColor });
        const averageColor = averageRGB(this.randomShapeMaterialTop.color, this.randomShapeMaterialBottom.color);
        this.randomShapeMaterialMid = new THREE.MeshBasicMaterial({
            color: new THREE.Color(averageColor.r, averageColor.g, averageColor.b)
        });
        this.randomShapes = [];
        for (let i = 0; i < 53; i++) {
            let shape;
            switch (i % 5) {
                case 0:
                    shape = new THREE.Mesh(this.generateRandomGeometry(), this.randomShapeMaterialTop);
                    break;
                case 1:
                    shape = new THREE.Mesh(this.generateRandomGeometry(), this.randomShapeMaterialBottom);
                    break;
                case 2:
                    shape = new THREE.Mesh(this.generateRandomGeometry(), this.randomShapeMaterialMid);
                    break;
                case 3:
                    shape = new THREE.Mesh(
                        this.generateRandomGeometry(),
                        new THREE.MeshBasicMaterial({ color: threeTopColor })
                    );
                    shape.colorTopToBottom = true;
                    break;
                case 4:
                default:
                    shape = new THREE.Mesh(
                        this.generateRandomGeometry(),
                        new THREE.MeshBasicMaterial({ color: threeBottomColor })
                    );
                    shape.colorBottomToTop = true;
            }
            this.setMeshInSpace(shape);
            this.randomShapes.push(shape);
            this.scene.add(shape);
        }
    }

    generateRandomGeometry() {
        const meshSize = randomWithRange(0.25, 2) * this.meshSize;
        const cylRadius = randomWithRange(meshSize / 2, meshSize);

        switch (Math.floor(Math.random() * 11)) {
            case 10:
                return new THREE.TorusKnotGeometry(
                    meshSize, meshSize / 3, 32, 12, randomWithRange(1, 7), randomWithRange(1, 10)
                );
            case 9:
                return new THREE.TorusGeometry(
                    meshSize, meshSize / 4, 16, 16, randomWithRange(Math.PI / 2, Math.PI * 3)
                );
            case 8:
                return new THREE.SphereGeometry(
                    meshSize,
                    16,
                    16,
                    0,
                    randomWithRange(Math.PI / 2, Math.PI * 3),
                    0,
                    randomWithRange(Math.PI / 2, Math.PI)
                );
            case 7:
                return new THREE.RingGeometry(
                    meshSize / 2, meshSize, 16, 1, 0, randomWithRange(Math.PI / 2, Math.PI * 3)
                );
            case 6:
                return new THREE.PlaneGeometry(meshSize, meshSize, 1, 1);
            case 5:
                return new THREE.OctahedronGeometry(meshSize, 0);
            case 4:
                return new THREE.IcosahedronGeometry(meshSize, 0);
            case 3:
                return new THREE.DodecahedronGeometry(meshSize, 0);
            case 2:
                return new THREE.CylinderGeometry(
                    cylRadius,
                    cylRadius,
                    randomWithRange(meshSize / 2, meshSize * 1.5),
                    16,
                    1,
                    Math.random() < 0.5,
                    0,
                    randomWithRange(Math.PI / 2, Math.PI * 3)
                );
            case 1:
                return new THREE.CircleGeometry(meshSize, 16);
            case 0:
            default:
                return new THREE.BoxGeometry(meshSize, meshSize, meshSize);
        }
    }

    initializeTetrahedrons() {
        const tetraGeometry = new THREE.TetrahedronGeometry(this.meshSize);
        const color = new THREE.Color(0xff6ba9);
        const tetraMaterial = new THREE.MeshBasicMaterial({color});
        this.tetras = [];
        for (let i = 0; i < 50; i++) {
            const tetra = new THREE.Mesh(tetraGeometry, tetraMaterial);
            this.setMeshInSpace(tetra);
            this.tetras.push(tetra);
            this.scene.add(tetra);
        }
    }

    setMeshInSpace(mesh) {
        const { windowHeight, windowWidth } = this.state;
        mesh.position.y = randomWithRange(windowHeight / 16, windowHeight / -16);
        mesh.position.z = randomWithRange(windowWidth / 16, windowWidth / -16);
        mesh.rotation.set(randomWithRange(0, Math.PI), randomWithRange(0, Math.PI), randomWithRange(0, Math.PI));
        mesh.rotationSpeed = {
            x: randomWithRange(-0.01, 0.01),
            y: randomWithRange(-0.01, 0.01),
            z: randomWithRange(-0.01, 0.01)
        };
        mesh.yVelocity = randomWithRange(0.03, 0.1);
        mesh.zVelocity = randomWithRange(-0.01, 0.01);
    }

    initializeLights() {
        const directionalLight = new THREE.DirectionalLight( 0xffffff, 0.7 );
        directionalLight.position.set(60, 60, 60);
        this.scene.add(directionalLight);

        const ambientLight = new THREE.AmbientLight(0x4B4B4B);
        this.scene.add(ambientLight);
    }

    initializeRenderer() {
        const { windowHeight, windowWidth } = this.state;
        this.renderer = new THREE.WebGLRenderer({ alpha: true });
        this.renderer.setSize(windowWidth, windowHeight);

        this.el.appendChild(this.renderer.domElement);
        this.renderer.render(this.scene, this.camera);
    }

    initializeStats() {
        this.stats = new Stats();
        this.el.appendChild(this.stats.dom);
    }

    setVendorPrefix() {
        if (bowser.webkit) {
            this.vendorPrefix = '-webkit-';
        } else if (bowser.firefox) {
            this.vendorPrefix = '-moz-';
        } else if (bowser.opera) {
            this.vendorPrefix = '-o-';
        } else {
            this.vendorPrefix = '';
        }
    }

    onWindowResize() {
        const windowHeight = window.innerHeight,
              windowWidth = window.innerWidth;

        this.setState({ windowHeight, windowWidth });

        this.renderer.setSize(windowWidth, windowHeight);

        this.camera.left = windowWidth / -this.camFactor;
        this.camera.right = windowWidth / this.camFactor;
        this.camera.top = windowHeight / this.camFactor;
        this.camera.bottom = windowHeight / -this.camFactor;
        this.camera.updateProjectionMatrix();
    }

    renderAnimation() {
        if (this.stats && this.props.showStats) {
            this.stats.begin();
        }

        const { windowHeight, windowWidth } = this.state;
        const { topColor, bottomColor } = this.props;

        for (let mesh of this.randomShapes) {
            // if shape is above top of window
            if (mesh.position.y > (windowHeight / 16 + this.meshSize * 4)) {
                mesh.geometry = this.generateRandomGeometry();
                this.repositionMeshToBottom(mesh);
            }

            this.moveMesh(mesh);

            if (mesh.colorTopToBottom) {
                mesh.material.color = getColorFromPosition(mesh.position.y, windowHeight, topColor, bottomColor);
            } else if (mesh.colorBottomToTop) {
                mesh.material.color = getColorFromPosition(mesh.position.y, windowHeight, bottomColor, topColor);
            }
        }

        for (let mesh of this.tetras) {
            // if shape is above top of window
            if (mesh.position.y > (windowHeight / 16 + this.meshSize * 4)) {
                this.repositionMeshToBottom(mesh);
            }

            this.moveMesh(mesh);
        }

        TWEEN.update();
        this.renderer.render(this.scene, this.camera);

        if (this.stats && this.props.showStats) {
            this.stats.end();
        }

        requestAnimationFrame(() => this.renderAnimation());
    }

    repositionMeshToBottom(mesh) {
        const { windowHeight, windowWidth } = this.state;
        // put it below the bottom of the window and give it a random z position
        mesh.position.y -= (windowHeight / 8 + this.meshSize * 8);
        mesh.position.z = randomWithRange(windowWidth / 16, windowWidth / -16);

        // give it new velocities
        mesh.yVelocity = randomWithRange(0.03, 0.1);
        mesh.zVelocity = randomWithRange(-0.01, 0.01);

        // give the mesh new rotation speeds
        for (let axis of ['x', 'y', 'z']) {
            mesh.rotationSpeed[axis] = randomWithRange(-0.01, 0.01);
        }
    }

    moveMesh(mesh) {
        const { windowHeight, windowWidth } = this.state;

        // translate the mesh
        mesh.position.y += mesh.yVelocity;
        mesh.position.z += mesh.zVelocity;

        // rotate the mesh around its axes
        for (let axis of ['x', 'y', 'z']) {
            mesh.rotation[axis] += mesh.rotationSpeed[axis];
        }
    }

    render() {
        const { showStats, topColor, bottomColor } = this.props;
        const { windowHeight, windowWidth } = this.state;
        const className = classNames('orthofloat-wrapper', { 'show-stats': showStats });
        const topColorStyle = (new THREE.Color(topColor.r, topColor.g, topColor.b)).getStyle();
        const bottomColorStyle = (new THREE.Color(bottomColor.r, bottomColor.g, bottomColor.b)).getStyle();
        const wrapperStyle = {
            backgroundImage: `${this.vendorPrefix}linear-gradient(${topColorStyle}, ${bottomColorStyle})`
        };

        return (
            <div className={className} style={wrapperStyle} ref={c => this.el = c}>
                <Stripes stripeWidth={this.stripeWidth}
                         vendorPrefix={this.vendorPrefix}
                         topColorStyle={topColorStyle}
                         bottomColorStyle={bottomColorStyle}
                         windowWidth={windowWidth} />
            </div>
        );
    }
}

Orthofloat.propTypes = {
    bottomColor: PropTypes.shape({
        r: PropTypes.number.isRequired,
        g: PropTypes.number.isRequired,
        b: PropTypes.number.isRequired
    }),
    topColor: PropTypes.shape({
        r: PropTypes.number.isRequired,
        g: PropTypes.number.isRequired,
        b: PropTypes.number.isRequired
    }),
    initializeWithStats: PropTypes.bool,
    showStats: PropTypes.bool
};

Orthofloat.defaultProps = {
    bottomColor: randomRGB(),
    topColor: randomRGB(),
    initializeWithStats: false,
    showStats: false
};<|endoftext|>


<commit_before>var fs = require("fs");
var path = require("path");
var vm = require("vm");

var execSync = require("exec-sync");
var EventEmitter = require("events").EventEmitter;

var Inflect = require("inflect-js");

/*
 * constructor function
 */
function ElmRunner(filename) {
  this.filename = filename;

  this.baseName = path.basename(filename, path.extname(filename));
  this.moduleName = Inflect.classify(this.baseName);

  this.outputPath = path.join(path.dirname(filename), this.baseName + ".js");

  this.compile();
  this.execute();
  this.wrap();
}

/**
 * run elm module through `elm-make` to generate compiled js
 */
ElmRunner.prototype.compile = function() {
  execSync("elm-make " + this.filename + " --output " + this.outputPath);
}

/**
 * execute script generated by elm-make in a vm context
 */
ElmRunner.prototype.execute = function() {
  var context = vm.createContext();
  var compiledOutput = fs.readFileSync(this.outputPath)

  vm.runInContext(compiledOutput, context, this.outputPath);

  this.compiledModule = context.Elm.worker(context.Elm[this.moduleName]);
}

/**
 * wrap compiled and executed object in EventEmitters
 */
ElmRunner.prototype.wrap = function(compiledModule) {
  var emitter = new EventEmitter();

  return emitter;
}

/**
 * expose a function that wraps new instances
 */
module.exports = function(filename) {
  return new ElmRunner(filename);
}
<commit_msg>add semicolons after function assignments<commit_after>var fs = require("fs");
var path = require("path");
var vm = require("vm");

var execSync = require("exec-sync");
var EventEmitter = require("events").EventEmitter;

var Inflect = require("inflect-js");

/*
 * constructor function
 */
function ElmRunner(filename) {
  this.filename = filename;

  this.baseName = path.basename(filename, path.extname(filename));
  this.moduleName = Inflect.classify(this.baseName);

  this.outputPath = path.join(path.dirname(filename), this.baseName + ".js");

  this.compile();
  this.execute();
  this.wrap();
}

/**
 * run elm module through `elm-make` to generate compiled js
 */
ElmRunner.prototype.compile = function() {
  execSync("elm-make " + this.filename + " --output " + this.outputPath);
};

/**
 * execute script generated by elm-make in a vm context
 */
ElmRunner.prototype.execute = function() {
  var context = vm.createContext();
  var compiledOutput = fs.readFileSync(this.outputPath)

  vm.runInContext(compiledOutput, context, this.outputPath);

  this.compiledModule = context.Elm.worker(context.Elm[this.moduleName]);
};

/**
 * wrap compiled and executed object in EventEmitters
 */
ElmRunner.prototype.wrap = function(compiledModule) {
  var emitter = new EventEmitter();

  return emitter;
};

/**
 * expose a function that wraps new instances
 */
module.exports = function(filename) {
  return new ElmRunner(filename);
};
<|endoftext|>


<commit_before>import React from 'react';
import Table from '@material-ui/core/Table';
import TableBody from '@material-ui/core/TableBody';
import TableCell from '@material-ui/core/TableCell';
import TableContainer from '@material-ui/core/TableContainer';
import TextField from '@material-ui/core/TextField';
import Toolbar from '@material-ui/core/Toolbar';
import TableHead from '@material-ui/core/TableHead';
import TableRow from '@material-ui/core/TableRow';
import InputAdornment from '@material-ui/core/InputAdornment';
import Paper from '@material-ui/core/Paper';
import TableFooter from '@material-ui/core/TableFooter';
import TablePagination from '@material-ui/core/TablePagination';
import styles from './admin.module.css';
import {Typography} from '@material-ui/core';
import DeleteIcon from '@material-ui/icons/Delete';
import EditIcon from '@material-ui/icons/Edit';
import SearchIcon from '@material-ui/icons/Search';

import TablePaginationActions from './TablePaginationActions';

function createData(id, name, email) {
  return {id, name, email};
}

const rows = [];

for (let i = 1; i < 30; i++) {
  rows.push(createData(i.toString(), 'John Dowe', 'johndowe@gmail.com'));
}

function EnhancedToolbar() {
  const handleOnSubmit = () => console.log('User has pressed search.');

  return (
    <Toolbar variant='dense' className={styles.titleUsersBar}>
      <Typography variant='h6' className={styles.titleUsersText}>
        Active members
      </Typography>
      <form onSubmit={handleOnSubmit}>
        <TextField
          id='outlined-margin-dense'
          placeholder='Search users...'
          margin='dense'
          variant='outlined'
          InputProps={{
            startAdornment: (
              <InputAdornment position='start'>
                <SearchIcon />
              </InputAdornment>
            ),
          }}
        />
      </form>
    </Toolbar>
  );
}

export default function UsersTable() {
  const [page, setPage] = React.useState(0);
  const [rowsPerPage, setRowsPerPage] = React.useState(5);

  const emptyRows = rowsPerPage -
      Math.min(rowsPerPage, rows.length - page * rowsPerPage);

  const handleChangePage = (event, newPage) => {
    setPage(newPage);
  };

  const handleChangeRowsPerPage = (event) => {
    setRowsPerPage(parseInt(event.target.value, 10));
    setPage(0);
  };

  return (
    <Paper>
      <EnhancedToolbar />
      <TableContainer>

          <TableHead>
            <TableRow>
              <TableCell>Name</TableCell>
              <TableCell>Email</TableCell>
              <TableCell></TableCell>
              <TableCell></TableCell>
            </TableRow>
          </TableHead>

          <TableBody>
            {(rowsPerPage > 0 ?
              rows.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage) :
              rows
            ).map((row) => (
              <TableRow key={row.id}>
                <TableCell>{row.name}</TableCell>
                <TableCell>{row.email}</TableCell>
                <TableCell><EditIcon /></TableCell>
                <TableCell><DeleteIcon/></TableCell>
              </TableRow>
            ))}

            {emptyRows > 0 && (
              <TableRow style={{height: 53 * emptyRows}}>
                <TableCell colSpan={4} />
              </TableRow>
            )}
          </TableBody>

          <TableFooter className={styles.footer}>
            <TableRow>
              <TablePagination
                rowsPerPageOptions={[5, 10, 25, {label: 'All', value: -1}]}
                count={rows.length}
                rowsPerPage={rowsPerPage}
                page={page}
                SelectProps={{
                  inputProps: {'aria-label': 'rows per page'},
                  native: true,
                }}
                onChangePage={handleChangePage}
                onChangeRowsPerPage={handleChangeRowsPerPage}
                ActionsComponent={TablePaginationActions}
              />
            </TableRow>
          </TableFooter>

      </TableContainer>
    </Paper>
  );
}
<commit_msg>re-write fake data for src/components/admin/UsersTable.js<commit_after>import React from 'react';
import Table from '@material-ui/core/Table';
import TableBody from '@material-ui/core/TableBody';
import TableCell from '@material-ui/core/TableCell';
import TableContainer from '@material-ui/core/TableContainer';
import TextField from '@material-ui/core/TextField';
import Toolbar from '@material-ui/core/Toolbar';
import TableHead from '@material-ui/core/TableHead';
import TableRow from '@material-ui/core/TableRow';
import InputAdornment from '@material-ui/core/InputAdornment';
import Paper from '@material-ui/core/Paper';
import TableFooter from '@material-ui/core/TableFooter';
import TablePagination from '@material-ui/core/TablePagination';
import styles from './admin.module.css';
import {Typography} from '@material-ui/core';
import DeleteIcon from '@material-ui/icons/Delete';
import EditIcon from '@material-ui/icons/Edit';
import SearchIcon from '@material-ui/icons/Search';

import TablePaginationActions from './TablePaginationActions';

const rows = Array.from(Array(30), (x, index) => ({
  id: index,
  name: 'John Dowe',
  email: 'johndowe@gmail.com'
});

function EnhancedToolbar() {
  const handleOnSubmit = () => console.log('User has pressed search.');

  return (
    <Toolbar variant='dense' className={styles.titleUsersBar}>
      <Typography variant='h6' className={styles.titleUsersText}>
        Active members
      </Typography>
      <form onSubmit={handleOnSubmit}>
        <TextField
          id='outlined-margin-dense'
          placeholder='Search users...'
          margin='dense'
          variant='outlined'
          InputProps={{
            startAdornment: (
              <InputAdornment position='start'>
                <SearchIcon />
              </InputAdornment>
            ),
          }}
        />
      </form>
    </Toolbar>
  );
}

export default function UsersTable() {
  const [page, setPage] = React.useState(0);
  const [rowsPerPage, setRowsPerPage] = React.useState(5);

  const emptyRows = rowsPerPage -
      Math.min(rowsPerPage, rows.length - page * rowsPerPage);

  const handleChangePage = (event, newPage) => {
    setPage(newPage);
  };

  const handleChangeRowsPerPage = (event) => {
    setRowsPerPage(parseInt(event.target.value, 10));
    setPage(0);
  };

  return (
    <Paper>
      <EnhancedToolbar />
      <TableContainer>

          <TableHead>
            <TableRow>
              <TableCell>Name</TableCell>
              <TableCell>Email</TableCell>
              <TableCell></TableCell>
              <TableCell></TableCell>
            </TableRow>
          </TableHead>

          <TableBody>
            {(rowsPerPage > 0 ?
              rows.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage) :
              rows
            ).map((row) => (
              <TableRow key={row.id}>
                <TableCell>{row.name}</TableCell>
                <TableCell>{row.email}</TableCell>
                <TableCell><EditIcon /></TableCell>
                <TableCell><DeleteIcon/></TableCell>
              </TableRow>
            ))}

            {emptyRows > 0 && (
              <TableRow style={{height: 53 * emptyRows}}>
                <TableCell colSpan={4} />
              </TableRow>
            )}
          </TableBody>

          <TableFooter className={styles.footer}>
            <TableRow>
              <TablePagination
                rowsPerPageOptions={[5, 10, 25, {label: 'All', value: -1}]}
                count={rows.length}
                rowsPerPage={rowsPerPage}
                page={page}
                SelectProps={{
                  inputProps: {'aria-label': 'rows per page'},
                  native: true,
                }}
                onChangePage={handleChangePage}
                onChangeRowsPerPage={handleChangeRowsPerPage}
                ActionsComponent={TablePaginationActions}
              />
            </TableRow>
          </TableFooter>

      </TableContainer>
    </Paper>
  );
}
<|endoftext|>


<commit_before>import React from "react";
import { connect } from "react-redux";
import { dataFont, medGrey } from "../../globalStyles";
import computeResponsive from "../../util/computeResponsive";
import Flex from "./flex";
import Radium from "radium";
import d3 from "d3";

@connect((state) => {
  return {
    tree: state.tree,
    metadata: state.metadata.metadata,
    browserDimensions: state.browserDimensions.browserDimensions
  };
})
@Radium
class Footer extends React.Component {
  constructor(props) {
    super(props);
  }
  static contextTypes = {
    router: React.PropTypes.object.isRequired
  }
  static propTypes = {
    style: React.PropTypes.object.isRequired
  }
  getStyles() {
    return {
      footer: {
        textAlign: "justify",
        marginLeft: "30px",
        // marginBottom: "30px",
        // padding: "0px",
        paddingBottom: "30px",
        fontFamily: dataFont,
        fontSize: 16,
        fontWeight: 300,
        color: medGrey
      },
      citationList: {
        marginTop: "10px"
      },
      citationItem: {
        paddingLeft: "0px",
        paddingRight: "10px",
        paddingTop: "1px",
        paddingBottom: "0px"
      },
      line: {
        marginTop: "20px",
        marginBottom: "20px",
        borderBottom: "1px solid #CCC"
      },
      fineprint: {
        fontSize: 14
      }
    };
  }
  getCitations(styles) {
    // HACK FOR h7n9
    if (this.context.router.location.pathname.includes("H7N9")) {
      return (
        <Flex wrap="wrap" justifyContent="flex-start" alignItems="center" style={styles.citationList}>

            Agriculture, Fisheries and Conservation Department

            Institute Pasteur of Shanghai, CAS

            Public Health Laboratory Services Branch, Centre for Health Protection

            Shanghai Zhijiang Biotechnology Co

            Beijing Institute of Microbiology and Epidemiology

            Centers for Disease Control and Prevention

            Central Veterinary Institute

            Fujian Center for Disease Control and Prevention

            Guandong Centers for Disease Control

            Guangzhou Institute of Respiratory Diseases (GIRD)

            Hangzhou Center for Disease Control and Prevention

            Harbin Veterinary Research Institute (CAAS)

            Jiangsu Provincial Center for Disease Control & Prevention

            Public Health Agency of Canada (PHAC)

            Shantou University Medical College

            South China Agricultural University

            Taiwan CDC

            WHO Chinese National Influenza Center

            Zhejiang Provincial Center for Disease Control and Prevention

        </Flex>
      );
    }
    // traverse tree.nodes
    // check if !hasChildren
    // in each node there is attr.authors and attr.url
    // construct array of unique author strings
    let authorsSet = d3.set();
    let authorsToURL = {};
    if (this.props.tree) {
      if (this.props.tree.nodes) {
        this.props.tree.nodes.forEach((node) => {
          if (node.children) { return; }
          if (node.attr.authors !== "" && node.attr.authors !== "?") {
            authorsSet.add(node.attr.authors);
            if (node.attr.url) {
              authorsToURL[node.attr.authors] = node.attr.url;
            }
          }
        });
      }
    }
    const authorsListItems = authorsSet.values().sort().map((authors, index) => {
        return (

            {authorsToURL[authors] ?
              {authors} :
              authors}

        );
      });
    return (
      <Flex wrap="wrap" justifyContent="flex-start" alignItems="center" style={styles.citationList}>
        {authorsListItems}
      </Flex>
    );
  }
  getUpdated(styles) {
    let updated = null;
    if (this.props.metadata) {
      if (this.props.metadata.updated) {
        updated = this.props.metadata.updated;
      }
    }
    return (
      updated ?
        <Flex style={styles.fineprint}>
          Data updated {updated}
        </Flex> :

    )
  }
  drawFooter(styles, width) {
    let text = "This work is made possible by the open sharing of genetic data by research groups from all over the world. We gratefully acknowledge their contributions. For data reuse (particularly for publication), please contact the original authors:"
    // HACK FOR h7n9
    if (this.context.router.location.pathname.includes("H7N9")) {
      text = (

          This work is made possible by the open sharing of genetic data by research groups from all over the world via GISAID. For data reuse please contact the submitting labs (listed below) or see this spreadsheet for a full list of authors and samples available.

      )
    }
    return (

        {text}
        {this.getCitations(styles)}

        {this.getUpdated(styles)}

    );
  }
  render() {
    const styles = this.getStyles();
    const responsive = computeResponsive({
      horizontal: 1,
      vertical: .3333333,
      browserDimensions: this.props.browserDimensions,
      sidebar: this.props.sidebar
    })
    const width = responsive.width - 30; // need to subtract margin when calculating div width
    return (

        {this.props.tree && this.props.browserDimensions ? this.drawFooter(styles, width) : "Waiting on citation data"}

    );
  }
}

export default Footer;
<commit_msg>Look for lowercase URL for H7N(<commit_after>import React from "react";
import { connect } from "react-redux";
import { dataFont, medGrey } from "../../globalStyles";
import computeResponsive from "../../util/computeResponsive";
import Flex from "./flex";
import Radium from "radium";
import d3 from "d3";

@connect((state) => {
  return {
    tree: state.tree,
    metadata: state.metadata.metadata,
    browserDimensions: state.browserDimensions.browserDimensions
  };
})
@Radium
class Footer extends React.Component {
  constructor(props) {
    super(props);
  }
  static contextTypes = {
    router: React.PropTypes.object.isRequired
  }
  static propTypes = {
    style: React.PropTypes.object.isRequired
  }
  getStyles() {
    return {
      footer: {
        textAlign: "justify",
        marginLeft: "30px",
        // marginBottom: "30px",
        // padding: "0px",
        paddingBottom: "30px",
        fontFamily: dataFont,
        fontSize: 16,
        fontWeight: 300,
        color: medGrey
      },
      citationList: {
        marginTop: "10px"
      },
      citationItem: {
        paddingLeft: "0px",
        paddingRight: "10px",
        paddingTop: "1px",
        paddingBottom: "0px"
      },
      line: {
        marginTop: "20px",
        marginBottom: "20px",
        borderBottom: "1px solid #CCC"
      },
      fineprint: {
        fontSize: 14
      }
    };
  }
  getCitations(styles) {
    // HACK FOR h7n9
    if (this.context.router.location.pathname.includes("h7n9")) {
      return (
        <Flex wrap="wrap" justifyContent="flex-start" alignItems="center" style={styles.citationList}>

            Agriculture, Fisheries and Conservation Department

            Institute Pasteur of Shanghai, CAS

            Public Health Laboratory Services Branch, Centre for Health Protection

            Shanghai Zhijiang Biotechnology Co

            Beijing Institute of Microbiology and Epidemiology

            Centers for Disease Control and Prevention

            Central Veterinary Institute

            Fujian Center for Disease Control and Prevention

            Guandong Centers for Disease Control

            Guangzhou Institute of Respiratory Diseases (GIRD)

            Hangzhou Center for Disease Control and Prevention

            Harbin Veterinary Research Institute (CAAS)

            Jiangsu Provincial Center for Disease Control & Prevention

            Public Health Agency of Canada (PHAC)

            Shantou University Medical College

            South China Agricultural University

            Taiwan CDC

            WHO Chinese National Influenza Center

            Zhejiang Provincial Center for Disease Control and Prevention

        </Flex>
      );
    }
    // traverse tree.nodes
    // check if !hasChildren
    // in each node there is attr.authors and attr.url
    // construct array of unique author strings
    let authorsSet = d3.set();
    let authorsToURL = {};
    if (this.props.tree) {
      if (this.props.tree.nodes) {
        this.props.tree.nodes.forEach((node) => {
          if (node.children) { return; }
          if (node.attr.authors !== "" && node.attr.authors !== "?") {
            authorsSet.add(node.attr.authors);
            if (node.attr.url) {
              authorsToURL[node.attr.authors] = node.attr.url;
            }
          }
        });
      }
    }
    const authorsListItems = authorsSet.values().sort().map((authors, index) => {
        return (

            {authorsToURL[authors] ?
              {authors} :
              authors}

        );
      });
    return (
      <Flex wrap="wrap" justifyContent="flex-start" alignItems="center" style={styles.citationList}>
        {authorsListItems}
      </Flex>
    );
  }
  getUpdated(styles) {
    let updated = null;
    if (this.props.metadata) {
      if (this.props.metadata.updated) {
        updated = this.props.metadata.updated;
      }
    }
    return (
      updated ?
        <Flex style={styles.fineprint}>
          Data updated {updated}
        </Flex> :

    )
  }
  drawFooter(styles, width) {
    let text = "This work is made possible by the open sharing of genetic data by research groups from all over the world. We gratefully acknowledge their contributions. For data reuse (particularly for publication), please contact the original authors:"
    // HACK FOR h7n9
    if (this.context.router.location.pathname.includes("h7n9")) {
      text = (

          This work is made possible by the open sharing of genetic data by research groups from all over the world via GISAID. For data reuse please contact the submitting labs (listed below) or see this spreadsheet for a full list of authors and samples available.

      )
    }
    return (

        {text}
        {this.getCitations(styles)}

        {this.getUpdated(styles)}

    );
  }
  render() {
    const styles = this.getStyles();
    const responsive = computeResponsive({
      horizontal: 1,
      vertical: .3333333,
      browserDimensions: this.props.browserDimensions,
      sidebar: this.props.sidebar
    })
    const width = responsive.width - 30; // need to subtract margin when calculating div width
    return (

        {this.props.tree && this.props.browserDimensions ? this.drawFooter(styles, width) : "Waiting on citation data"}

    );
  }
}

export default Footer;
<|endoftext|>


<commit_before>import React, { PropTypes } from 'react';
import ReactDOM from 'react-dom';
import Plotly from 'plotly.js';
import Theme from '../../helpers/theme.js';

class Position3D extends React.Component {

  // data should be an array of track and truth data
  // filtered by a track id
  static propTypes = {
    data: PropTypes.object,
    radarData: PropTypes.object,
    title: PropTypes.string,
    width: PropTypes.number,
    height: PropTypes.number
  }

  constructor () {
    super();
    this.state = {};
    this.constructRadar = this.constructRadar.bind(this);
  }

  componentDidMount () {
    const plotDiv = ReactDOM.findDOMNode(this);
    this.setState({
      plotDiv
    });
    Plotly.newPlot(plotDiv, this.createPlotData(this.props.data), this.createLayout());
  }

  componentWillReceiveProps (nextProps) {
    const { plotDiv } = this.state;
    if (plotDiv) {
      Plotly.newPlot(plotDiv, this.createPlotData(nextProps.data), this.createLayout());
    }
  }

  constructRadar (radarData) {
    this.maxAlt = radarData.get('maxAlt');
  }

  createPlotData (data) {
    let truthX = [];
    let truthY = [];
    let truthZ = [];
    let trackX = [];
    let trackY = [];
    let trackZ = [];
    let maxAlt = 0;
    // get track/truth points
    data.forEach((row) => {
      if (row.get('type') === 'truth') {
        truthX.push(row.get('lon'));
        truthY.push(row.get('lat'));
        truthZ.push(row.get('alt'));
      }
      if (row.get('type') === 'track') {
        trackX.push(row.get('lon'));
        trackY.push(row.get('lat'));
        trackZ.push(row.get('alt'));
      }
      if (row.get('alt') > maxAlt) {
        maxAlt = row.get('alt');
      }
    });
    this.maxAlt = maxAlt;
    const {radarData} = this.props;
    let { maxEl, minEl, lat, lon, maxRange, startAz, endAz } = radarData.toJS();
    console.log('truth length = ' + truthX.length);
    console.debug('track length = ' + trackX.length);
    if (minEl < 0) {
      minEl = 0;
    }
    minEl = this.toRadians(minEl);
    maxEl = this.toRadians(maxEl);

    console.debug('minAz = ' + startAz + 'lat = ' + lat + ' maxRange = ' + maxRange);
    const elMinAzMinRangeLat = this.latNew(startAz, lat, maxRange*Math.cos(minEl));
    const elMinAzMinRangeLon = this.lonNew(startAz, lat, lon, elMinAzMinRangeLat, maxRange*Math.cos(minEl));
    const elMaxAzMinRangeLat = this.latNew(startAz, lat, maxRange*Math.cos(maxEl));
    const elMaxAzMinRangeLon = this.lonNew(startAz, lat, lon, elMaxAzMinRangeLat, maxRange*Math.cos(maxEl));

    const elMinAzMaxRangeLat = this.latNew(endAz, lat, maxRange*Math.cos(minEl));
    const elMinAzMaxRangeLon = this.lonNew(endAz, lat, lon, elMinAzMaxRangeLat, maxRange*Math.cos(minEl));
    const elMaxAzMaxRangeLat = this.latNew(endAz, lat, maxRange*Math.cos(maxEl));
    const elMaxAzMaxRangeLon = this.lonNew(endAz, lat, lon, elMaxAzMaxRangeLat, maxRange*Math.cos(maxEl));

    const coverageX = [
      lon,
      elMinAzMinRangeLon,
      elMinAzMaxRangeLon,
      elMaxAzMaxRangeLon,
      elMaxAzMinRangeLon,
      lon,
      elMaxAzMaxRangeLon,
      elMaxAzMinRangeLon,
      elMinAzMaxRangeLon,
      elMinAzMinRangeLon,
      lon
    ];
    const coverageY = [
      lat,
      elMinAzMinRangeLat,
      elMinAzMaxRangeLat,
      elMaxAzMaxRangeLat,
      elMaxAzMinRangeLat,
      lat,
      elMaxAzMinRangeLat,
      elMaxAzMaxRangeLat,
      elMinAzMaxRangeLat,
      elMinAzMinRangeLat,
      lat
    ];
    const coverageZ = [
      0,
      maxRange*Math.sin(minEl),
      maxRange*Math.sin(minEl),
      maxRange*Math.sin(maxEl),
      maxRange*Math.sin(maxEl),
      0,
      maxRange*Math.sin(maxEl),
      maxRange*Math.sin(maxEl),
      maxRange*Math.sin(minEl),
      maxRange*Math.sin(minEl),
      0
    ];
    console.debug(minEl, maxEl, maxRange);
    console.debug(coverageX);
    console.debug(coverageY);
    console.debug(coverageZ);

    return [
      // {
      //   type: 'scatter3d',
      //   x: truthX,
      //   y: truthY,
      //   z: truthZ,
      //   mode: 'markers',
      //   marker: {
      //     color: Theme.palette.truth,
      //     size: 6,
      //     opacity: 0.5,
      //     symbol: 'dot',
      //     line: {
      //       opacity: 0.8,
      //       width: 1,
      //       weight: 1,
      //       color: Theme.palette.truth
      //     }
      //   },
      //   name: 'Truth'
      // },
      // {
      //   type: 'scatter3d',
      //   x: trackX,
      //   y: trackY,
      //   z: trackZ,
      //   mode: 'markers',
      //   marker: {
      //     color: Theme.palette.track,
      //     size: 4,
      //     opacity: 0.5,
      //     symbol: 'dot',
      //     line: {
      //       opacity: 0.8,
      //       width: 1,
      //       color: Theme.palette.track
      //     }
      //   },
      //   name: 'Track'
      // },
      {
        type: 'mesh3d',
        x: [0, 0, 1, 1, 0, 0, 1, 1],
        y: [0, 1, 1, 0, 0, 1, 1, 0],
        z: [0, 0, 0, 0, 1, 1, 1, 1],
        i: [7, 0, 0, 0, 4, 4, 6, 6, 4, 0, 3, 2],
        j: [3, 4, 1, 2, 5, 6, 5, 2, 0, 1, 6, 3],
        k: [0, 7, 2, 3, 6, 7, 1, 1, 5, 5, 7, 6],
        opacity: 0.4,
        name: 'Coverage'
      }
    ];
  }

  toRadians (deg) {
    return deg*Math.PI/180;
  }

  toDegrees (rad) {
    return rad*180/Math.PI;
  }

  latNew (bearing, originLat, radius) {
    console.debug('bearing =' + bearing + ' originLat = ' + originLat + ' radius  ' + radius);
    const R = 6378100;
    return Math.asin(Math.sin(originLat) * Math.cos(radius / R) +
          Math.cos(originLat) * Math.sin(radius / R) * Math.cos(bearing));
  }

  lonNew (bearing, originLat, originLon, destinationLat, radius) {
    console.debug(bearing + ', ' + originLat + ', ' + originLon + ' ' + destinationLat + ', ' + radius);
    const R = 6378100;
    return originLon + Math.atan2(Math.sin(bearing) * Math.sin(radius / R) * Math.cos(originLat),
            Math.cos(radius / R) - Math.sin(originLat) * Math.sin(destinationLat));
  }

  createLayout () {
    return {
      title: this.props.title,
      width: this.props.width,
      height: this.props.height,
      scene: {
        xaxis: {
          title: 'Lon'
        },
        yaxis: {
          title: 'Lat'
        },
        zaxis: {
          title: 'Alt'
          // range: [0, this.maxAlt*1.2]
        }
      }
    };
  }

  config = {
    // showLink: false,
    displayModeBar: true
  };

  makePlot () {

  }

  render () {
    return (

    );
  }
}

export default Position3D;
<commit_msg>undid some debugging<commit_after>import React, { PropTypes } from 'react';
import ReactDOM from 'react-dom';
import Plotly from 'plotly.js';
import Theme from '../../helpers/theme.js';

class Position3D extends React.Component {

  // data should be an array of track and truth data
  // filtered by a track id
  static propTypes = {
    data: PropTypes.object,
    radarData: PropTypes.object,
    title: PropTypes.string,
    width: PropTypes.number,
    height: PropTypes.number
  }

  constructor () {
    super();
    this.state = {};
    this.constructRadar = this.constructRadar.bind(this);
  }

  componentDidMount () {
    const plotDiv = ReactDOM.findDOMNode(this);
    this.setState({
      plotDiv
    });
    Plotly.newPlot(plotDiv, this.createPlotData(this.props.data), this.createLayout());
  }

  componentWillReceiveProps (nextProps) {
    const { plotDiv } = this.state;
    if (plotDiv) {
      Plotly.newPlot(plotDiv, this.createPlotData(nextProps.data), this.createLayout());
    }
  }

  constructRadar (radarData) {
    this.maxAlt = radarData.get('maxAlt');
  }

  createPlotData (data) {
    let truthX = [];
    let truthY = [];
    let truthZ = [];
    let trackX = [];
    let trackY = [];
    let trackZ = [];
    let maxAlt = 0;
    // get track/truth points
    data.forEach((row) => {
      if (row.get('type') === 'truth') {
        truthX.push(row.get('lon'));
        truthY.push(row.get('lat'));
        truthZ.push(row.get('alt'));
      }
      if (row.get('type') === 'track') {
        trackX.push(row.get('lon'));
        trackY.push(row.get('lat'));
        trackZ.push(row.get('alt'));
      }
      if (row.get('alt') > maxAlt) {
        maxAlt = row.get('alt');
      }
    });
    this.maxAlt = maxAlt;
    const {radarData} = this.props;
    let { maxEl, minEl, lat, lon, maxRange, startAz, endAz } = radarData.toJS();
    console.log('truth length = ' + truthX.length);
    console.debug('track length = ' + trackX.length);
    if (minEl < 0) {
      minEl = 0;
    }
    minEl = this.toRadians(minEl);
    maxEl = this.toRadians(maxEl);

    console.debug('minAz = ' + startAz + 'lat = ' + lat + ' maxRange = ' + maxRange);
    const elMinAzMinRangeLat = this.latNew(startAz, lat, maxRange*Math.cos(minEl));
    const elMinAzMinRangeLon = this.lonNew(startAz, lat, lon, elMinAzMinRangeLat, maxRange*Math.cos(minEl));
    const elMaxAzMinRangeLat = this.latNew(startAz, lat, maxRange*Math.cos(maxEl));
    const elMaxAzMinRangeLon = this.lonNew(startAz, lat, lon, elMaxAzMinRangeLat, maxRange*Math.cos(maxEl));

    const elMinAzMaxRangeLat = this.latNew(endAz, lat, maxRange*Math.cos(minEl));
    const elMinAzMaxRangeLon = this.lonNew(endAz, lat, lon, elMinAzMaxRangeLat, maxRange*Math.cos(minEl));
    const elMaxAzMaxRangeLat = this.latNew(endAz, lat, maxRange*Math.cos(maxEl));
    const elMaxAzMaxRangeLon = this.lonNew(endAz, lat, lon, elMaxAzMaxRangeLat, maxRange*Math.cos(maxEl));

    const coverageX = [
      lon,
      elMinAzMinRangeLon,
      elMinAzMaxRangeLon,
      elMaxAzMaxRangeLon,
      elMaxAzMinRangeLon,
      lon,
      elMaxAzMaxRangeLon,
      elMaxAzMinRangeLon,
      elMinAzMaxRangeLon,
      elMinAzMinRangeLon,
      lon
    ];
    const coverageY = [
      lat,
      elMinAzMinRangeLat,
      elMinAzMaxRangeLat,
      elMaxAzMaxRangeLat,
      elMaxAzMinRangeLat,
      lat,
      elMaxAzMinRangeLat,
      elMaxAzMaxRangeLat,
      elMinAzMaxRangeLat,
      elMinAzMinRangeLat,
      lat
    ];
    const coverageZ = [
      0,
      maxRange*Math.sin(minEl),
      maxRange*Math.sin(minEl),
      maxRange*Math.sin(maxEl),
      maxRange*Math.sin(maxEl),
      0,
      maxRange*Math.sin(maxEl),
      maxRange*Math.sin(maxEl),
      maxRange*Math.sin(minEl),
      maxRange*Math.sin(minEl),
      0
    ];
    console.debug(minEl, maxEl, maxRange);
    console.debug(coverageX);
    console.debug(coverageY);
    console.debug(coverageZ);

    return [
      {
        type: 'scatter3d',
        x: truthX,
        y: truthY,
        z: truthZ,
        mode: 'markers',
        marker: {
          color: Theme.palette.truth,
          size: 6,
          opacity: 0.5,
          symbol: 'dot',
          line: {
            opacity: 0.8,
            width: 1,
            weight: 1,
            color: Theme.palette.truth
          }
        },
        name: 'Truth'
      },
      {
        type: 'scatter3d',
        x: trackX,
        y: trackY,
        z: trackZ,
        mode: 'markers',
        marker: {
          color: Theme.palette.track,
          size: 4,
          opacity: 0.5,
          symbol: 'dot',
          line: {
            opacity: 0.8,
            width: 1,
            color: Theme.palette.track
          }
        },
        name: 'Track'
      }
      // ,
      // {
      //   type: 'mesh3d',
      //   x: [0, 0, 1, 1, 0, 0, 1, 1],
      //   y: [0, 1, 1, 0, 0, 1, 1, 0],
      //   z: [0, 0, 0, 0, 1, 1, 1, 1],
      //   i: [7, 0, 0, 0, 4, 4, 6, 6, 4, 0, 3, 2],
      //   j: [3, 4, 1, 2, 5, 6, 5, 2, 0, 1, 6, 3],
      //   k: [0, 7, 2, 3, 6, 7, 1, 1, 5, 5, 7, 6],
      //   opacity: 0.4,
      //   name: 'Coverage'
      // }
    ];
  }

  toRadians (deg) {
    return deg*Math.PI/180;
  }

  toDegrees (rad) {
    return rad*180/Math.PI;
  }

  latNew (bearing, originLat, radius) {
    console.debug('bearing =' + bearing + ' originLat = ' + originLat + ' radius  ' + radius);
    const R = 6378100;
    return Math.asin(Math.sin(originLat) * Math.cos(radius / R) +
          Math.cos(originLat) * Math.sin(radius / R) * Math.cos(bearing));
  }

  lonNew (bearing, originLat, originLon, destinationLat, radius) {
    console.debug(bearing + ', ' + originLat + ', ' + originLon + ' ' + destinationLat + ', ' + radius);
    const R = 6378100;
    return originLon + Math.atan2(Math.sin(bearing) * Math.sin(radius / R) * Math.cos(originLat),
            Math.cos(radius / R) - Math.sin(originLat) * Math.sin(destinationLat));
  }

  createLayout () {
    return {
      title: this.props.title,
      width: this.props.width,
      height: this.props.height,
      scene: {
        xaxis: {
          title: 'Lon'
        },
        yaxis: {
          title: 'Lat'
        },
        zaxis: {
          title: 'Alt'
          // range: [0, this.maxAlt*1.2]
        }
      }
    };
  }

  config = {
    // showLink: false,
    displayModeBar: true
  };

  makePlot () {

  }

  render () {
    return (

    );
  }
}

export default Position3D;
<|endoftext|>