<commit_before>import React, { PureComponent } from 'react'
import PropTypes from 'prop-types'
import classNames from 'classnames'
import { scrollToPosition } from './../../lib/jello'
import { css, media, select } from './../../styles/jss'
import * as s from './../../styles/jso'
import {
  ChevronIcon,
  XIcon,
} from '../assets/Icons'

const categoryPostSelectorStyle = css(
  s.inlineBlock,
  s.relative,
  s.mr10,
  s.ml10,
  {
    width: 300,
  },
  media(s.maxBreak2,
    s.m0,
    s.fullWidth,
    { marginBottom: 10 },
  ),
)

const categoriesSelectionsStyle = css(
  { cursor: 'pointer' },
  select('& input', s.resetInput),
  select('& .selector, & .selected',
    s.fullWidth,
    s.block,
    s.pr20,
    s.pl20,
    s.zIndex2,
    {
      cursor: 'pointer',
      paddingTop: 9,
      paddingBottom: 9,
      lineHeight: 20,
      border: '1px solid #aaa',
      borderRadius: 5,
    },
  ),
  select('&.open .selector, &.open .selected',
    {
      borderBottomRightRadius: 0,
      borderBottomLeftRadius: 0,
    },
  ),
  // selector
  select('& .selector',
    s.relative,
  ),
  select('&.open .selector',
    {
      cursor: 'text',
    },
  ),
  select('& .selector-label',
    s.absolute,
    s.colorA,
    s.zIndex1,
    {
      left: 20,
    },
  ),
  select('& svg.ChevronIcon',
    s.absolute,
    s.block,
    s.colorA,
    s.rotate90,
    s.zIndex1,
    {
      cursor: 'pointer',
      top: 10,
      right: 10,
    },
    // select('& polyline', { fill: '#aaa' }),
  ),
  select('&.open svg.ChevronIcon',
    s.zIndex3,
  ),
  // selected
  select('& .selected',
    {
      borderColor: '#979797',
    },
    select('& b',
      s.sansRegular,
      {
        lineHeight: 20,
      },
      select('& i',
        s.sansRegular,
        s.colorA,
      ),
    ),
    select('& button',
      s.absolute,
      {
        right: 15,
      },
      media(s.maxBreak2,
        { right: 18 },
      ),
    ),
    select('& button span.text', s.displayNone),
  ),
)

const categoriesListStyle = css(
  s.absolute,
  s.fullWidth,
  s.p10,
  s.overflowHidden,
  s.overflowScrollWebY,
  s.bgcWhite,
  s.zTools,
  {
    top: 39,
    left: 0,
    maxHeight: 260,
    border: '1px solid #aaa',
    borderRadius: 0,
    borderBottomRightRadius: 5,
    borderBottomLeftRadius: 5,
  },
  select('& .divider',
    s.p0,
    s.m10,
    s.bgcA,
    {
      height: 1,
      border: 0,
    },
  ),
  // list items
  select('& b',
    s.block,
    s.m0,
    s.p5,
    s.pr10,
    s.pl10,
    s.colorA,
    s.sansRegular,
    {
      lineHeight: 20,
    },
  ),
  select('& ul',
    s.resetList,
    select('& li',
      s.m0,
      s.p0,
      select('& button',
        s.block,
        s.fullWidth,
        {
          marginTop: 1,
          marginBottom: 1,
          padding: 4,
          height: 'auto',
          textAlign: 'left',
          lineHeight: 20,
        },
        // have to be below `padding: 4`
        s.pr10,
        s.pl10,
      ),
      select('& button:active',
        s.colorBlack,
        s.bgcWhite,
      ),

      select('&.isSelected button, & button:hover',
        s.colorWhite,
        s.bgcBlack,
        { borderRadius: 3 },
      ),
    ),
  ),
  // mobile tweaks
  media(s.maxBreak2,
    s.relative,
    s.block,
    {
      WebkitOverflowScrolling: 'none',
      overflowX: 'hidden',
      overflowY: 'visible',
      top: 'auto',
      left: 'auto',
      maxHeight: '100%',
      borderTopWidth: 0,
    },
  ),
)

function filterSearch(categories, searchText) {
  if (searchText === '') { return categories }
  return categories.filter(c => c.get('name').toLowerCase().includes(searchText.toLowerCase()))
}

function CategoryItem({ category, index, selectedIndexCurrent, onSelect }) {
  const isSelected = selectedIndexCurrent === index
  return (
    <li
      id={`categorySelect_${category.get('id')}`}
      className={classNames({ isSelected })}
    >
      <button
        role="option"
        aria-selected={isSelected}
        onClick={() => onSelect(category)}
      >
        {category.get('name')}
      </button>

  )
}

CategoryItem.propTypes = {
  category: PropTypes.object.isRequired,
  index: PropTypes.number.isRequired,
  selectedIndexCurrent: PropTypes.number,
  onSelect: PropTypes.func.isRequired,
}

CategoryItem.defaultProps = {
  selectedIndexCurrent: null,
}

export default class CategoryPostSelector extends PureComponent {
  static propTypes = {
    onSelect: PropTypes.func.isRequired,
    onClear: PropTypes.func.isRequired,
    subscribedCategories: PropTypes.array.isRequired,
    featuredInCategories: PropTypes.array.isRequired,
    unsubscribedCategories: PropTypes.array.isRequired,
    selectedCategories: PropTypes.array.isRequired,
    resetSelection: PropTypes.bool.isRequired,
  }

  constructor(props) {
    super(props)
    const { subscribedCategories, unsubscribedCategories } = props
    this.state = {
      subscribedCategories,
      unsubscribedCategories,
      searchText: '',
      selectedIndex: null,
      focused: false,
      open: false,
    }

    this.setWrapperRef = this.setWrapperRef.bind(this)
    this.handleClickOutside = this.handleClickOutside.bind(this)
  }

  componentDidMount() {
    const { onSelect, featuredInCategories, selectedCategories } = this.props
    if (featuredInCategories.length > 0) {
      onSelect(featuredInCategories[0])
    }

    if (selectedCategories) {
      this.setIndexOfSelection()
    }
  }

  componentWillReceiveProps(nextProps) {
    const { subscribedCategories, unsubscribedCategories } = nextProps
    const { searchText } = this.state
    this.setState({
      subscribedCategories: filterSearch(subscribedCategories, searchText),
      unsubscribedCategories: filterSearch(unsubscribedCategories, searchText),
    })
  }

  componentDidUpdate(prevProps, prevState) {
    if ((!prevState.open && this.state.open) ||
      (!this.state.open && !prevState.focused && this.state.focused)) {
      document.addEventListener('mousedown', this.handleClickOutside)
    }

    // close & reset selection
    if (!prevProps.resetSelection && this.props.resetSelection) {
      this.close()
    }

    if (this.state.open &&
      (prevState.selectedIndex !== this.state.selectedIndex)) {
      this.scrollToSelectedCategory()
    }

    if (this.props.selectedCategories &&
      prevProps.selectedCategories !== this.props.selectedCategories) {
      this.setIndexOfSelection()
    }

    if (this.state.open && !this.props.selectedCategories.length > 0) {
      this.categorySelectorRef.focus()
    }
  }

  componentWillUnmount() {
    document.removeEventListener('mousedown', this.handleClickOutside)
  }

  setIndexOfSelection() {
    const { selectedCategories } = this.props
    const {
      subscribedCategories,
      unsubscribedCategories,
    } = this.state

    if (selectedCategories.length > 0) {
      // assume we only have one selection at a time for now
      const selectedCategory = selectedCategories[0]
      const categories = subscribedCategories.concat(unsubscribedCategories)

      let selectedIndex = null
      categories.map((category, index) => {
        if (selectedCategory.get('id') === category.get('id')) {
          selectedIndex = index
        }
        return selectedIndex
      })

      return this.setState({ selectedIndex })
    }
    return this.setState({ selectedIndex: null })
  }

  setWrapperRef(node) {
    this.wrapperRef = node
  }

  handleSearch = (event) => {
    const searchText = event.target.value
    const { subscribedCategories, unsubscribedCategories } = this.props
    const { selectedIndex } = this.state

    let newSelectedIndex = selectedIndex
    if (!selectedIndex) {
      newSelectedIndex = searchText === '' ? null : 0
      // newSelectedIndex = 0
    }
    this.setState({
      selectedIndex: newSelectedIndex,
      searchText,
      subscribedCategories: filterSearch(subscribedCategories, searchText),
      unsubscribedCategories: filterSearch(unsubscribedCategories, searchText),
    })
  }

  handleKeyDown = (event) => {
    const { onSelect } = this.props
    const {
      subscribedCategories,
      unsubscribedCategories,
      selectedIndex,
    } = this.state
    const categories = subscribedCategories.concat(unsubscribedCategories)
    const max = categories.length
    const selectedIsNull = (selectedIndex === null)

    if (event.key === 'ArrowDown' && selectedIsNull) {
      this.setState({ selectedIndex: 0 })
    } else if (event.key === 'ArrowDown' && selectedIndex === (max - 1)) {
      this.setState({ selectedIndex: 0 })
    } else if (event.key === 'ArrowDown' && selectedIndex < max) {
      this.setState({ selectedIndex: selectedIndex + 1 })
    } else if (event.key === 'ArrowUp' && !selectedIsNull && selectedIndex > 0) {
      event.preventDefault()
      this.setState({ selectedIndex: selectedIndex - 1 })
    } else if (event.key === 'ArrowUp' && !selectedIsNull && selectedIndex === 0) {
      event.preventDefault()
      this.setState({ selectedIndex: max - 1 })
    } else if (event.key === 'Enter' && !selectedIsNull) {
      onSelect(categories[selectedIndex])
    } else if (event.key === 'Escape') {
      this.categorySelectorRef.blur()
      this.close()
    }
  }

  handleBlur(isFocused) {
    this.setState({
      focused: isFocused,
    })

    if (isFocused) {
      this.open()
    }
  }

  handleSelectorClick(e) {
    if (e.target.nodeName === 'polyline' ||
      e.target.nodeName === 'g' ||
      e.target.nodeName === 'svg') {
      return this.close()
    }
    if (e.target.nodeName !== 'BUTTON') {
      return this.open()
    }
    return null
  }

  handleClickOutside(event) {
    if (this.state.open) {
      if (this.wrapperRef && !this.wrapperRef.contains(event.target)) {
        this.close()
      }
    }
  }

  scrollToSelectedCategory() {
    const {
      subscribedCategories,
      unsubscribedCategories,
      selectedIndex,
    } = this.state
    const categories = subscribedCategories.concat(unsubscribedCategories)
    const selectedCategory = categories[selectedIndex]
    let selectedCategoryDomId = null
    if (selectedCategory) {
      selectedCategoryDomId = `categorySelect_${selectedCategory.get('id')}`
    }

    // grab elements from the dom
    const categoryListInDom = document.getElementById('categoryList')
    const selectedCategoryItemInDom = document.getElementById(selectedCategoryDomId)

    // determine scroll offset of category item in dom
    let categoryItemInDomTopOffset = null
    let categoryListInDomTopOffset = null
    let categoryItemInDomHeight = null
    let categoryListInDomHeight = null
    if (selectedCategoryDomId) {
      categoryItemInDomTopOffset = selectedCategoryItemInDom.getBoundingClientRect().top
      categoryListInDomTopOffset = categoryListInDom.getBoundingClientRect().top
      categoryItemInDomHeight = selectedCategoryItemInDom.clientHeight
      categoryListInDomHeight = categoryListInDom.clientHeight
    }

    if (categoryItemInDomTopOffset) {
      // adjust scroll offset for window height / nav bar
      const scrollElement = categoryListInDom
      const scrollTo = (categoryItemInDomTopOffset - categoryListInDomTopOffset)
      const scrollCheckOffset = categoryItemInDomHeight

      if (
        ((scrollTo + scrollCheckOffset) > categoryListInDomHeight) ||
        (scrollTo < 0)
      ) {
        // scroll to new position
        scrollToPosition(0, scrollTo, { el: scrollElement, duration: 150 })
      }
    }
  }

  open() {
    if (!this.state.open) {
      this.setState({
        open: true,
      })
    }
  }

  close() {
    if (this.state.open) {
      this.resetSelection()
    }
  }

  clearLocal() {
    const { onClear } = this.props
    const { open } = this.state
    if (open) {
      // this.categorySelectorRef.focus()
    }
    onClear()
  }

  resetSelection() {
    const { subscribedCategories, unsubscribedCategories } = this.props
    this.setState({
      subscribedCategories,
      unsubscribedCategories,
      searchText: '',
      focused: false,
      open: false,
    })
  }

  render() {
    const { selectedCategories, onSelect } = this.props
    const {
      subscribedCategories,
      unsubscribedCategories,
      searchText,
      selectedIndex,
      open,
    } = this.state
    // Everything except this component could support multiple categories, but for now
    // we can assume there is only one selected category per post.
    const selectedCategory = selectedCategories[0]
    const offset = subscribedCategories.length
    return (
      /* eslint-disable jsx-a11y/interactive-supports-focus */
       this.handleSelectorClick(e)}
      >
        <span className={classNames({ open }, `${categoriesSelectionsStyle}`)}>
          {!selectedCategory &&

              {!searchText &&

                  {open ? 'Type community name' : 'Choose Community'}

              }
              <ChevronIcon />
              <input
                ref={(node) => { this.categorySelectorRef = node }}
                className="selector"
                name="categorySelector"
                type="search"
                value={searchText}
                onChange={this.handleSearch}
                onKeyDown={this.handleKeyDown}
                onBlur={() => this.handleBlur(false)}
                onFocus={() => this.handleBlur(true)}
              />

          }
          {selectedCategory &&

              Post into: {selectedCategory.get('name')}
              <button onClick={() => this.clearLocal()}>
                Remove

                  <XIcon />

              </button>

          }

        {open &&

            {subscribedCategories.length > 0 &&

                Your Categories

                  {subscribedCategories.map((category, index) =>
                    (<CategoryItem
                      key={`categorySelect:${category.get('id')}`}
                      category={category}
                      index={index}
                      selectedIndexCurrent={selectedIndex}
                      onSelect={onSelect}
                    />),
                  )}

            }

              All Categories

                {unsubscribedCategories.map((category, index) =>
                  (<CategoryItem
                    key={`categorySelect:${category.get('id')}`}
                    category={category}
                    index={index + offset}
                    selectedIndexCurrent={selectedIndex}
                    onSelect={onSelect}
                  />),
                )}

        }

    )
  }
}
<commit_msg>editor - category selector - add truncation to selection<commit_after>import React, { PureComponent } from 'react'
import PropTypes from 'prop-types'
import classNames from 'classnames'
import { scrollToPosition } from './../../lib/jello'
import { css, media, select } from './../../styles/jss'
import * as s from './../../styles/jso'
import {
  ChevronIcon,
  XIcon,
} from '../assets/Icons'

const categoryPostSelectorStyle = css(
  s.inlineBlock,
  s.relative,
  s.mr10,
  s.ml10,
  {
    width: 300,
  },
  media(s.maxBreak2,
    s.m0,
    s.fullWidth,
    { marginBottom: 10 },
  ),
)

const categoriesSelectionsStyle = css(
  { cursor: 'pointer' },
  select('& input', s.resetInput),
  select('& .selector, & .selected',
    s.fullWidth,
    s.block,
    s.pr20,
    s.pl20,
    s.zIndex2,
    {
      cursor: 'pointer',
      paddingTop: 9,
      paddingBottom: 9,
      lineHeight: 20,
      border: '1px solid #aaa',
      borderRadius: 5,
    },
  ),
  select('&.open .selector, &.open .selected',
    {
      borderBottomRightRadius: 0,
      borderBottomLeftRadius: 0,
    },
  ),
  // selector
  select('& .selector',
    s.relative,
  ),
  select('&.open .selector',
    {
      cursor: 'text',
    },
  ),
  select('& .selector-label',
    s.absolute,
    s.colorA,
    s.zIndex1,
    {
      left: 20,
    },
  ),
  select('& svg.ChevronIcon',
    s.absolute,
    s.block,
    s.colorA,
    s.rotate90,
    s.zIndex1,
    {
      cursor: 'pointer',
      top: 10,
      right: 10,
    },
    // select('& polyline', { fill: '#aaa' }),
  ),
  select('&.open svg.ChevronIcon',
    s.zIndex3,
  ),
  // selected
  select('& .selected',
    {
      borderColor: '#979797',
    },
    select('& b',
      s.sansRegular,
      s.inlineBlock,
      s.fullWidth,
      s.truncate,
      {
        width: 'calc(100% - 24px)',
        lineHeight: 20,
        verticalAlign: 'middle',
      },
      select('& i',
        s.sansRegular,
        s.colorA,
      ),
    ),
    select('& button',
      s.absolute,
      {
        right: 15,
      },
      media(s.maxBreak2,
        { right: 18 },
      ),
    ),
    select('& button span.text', s.displayNone),
  ),
)

const categoriesListStyle = css(
  s.absolute,
  s.fullWidth,
  s.p10,
  s.overflowHidden,
  s.overflowScrollWebY,
  s.bgcWhite,
  s.zTools,
  {
    top: 39,
    left: 0,
    maxHeight: 260,
    border: '1px solid #aaa',
    borderRadius: 0,
    borderBottomRightRadius: 5,
    borderBottomLeftRadius: 5,
  },
  select('& .divider',
    s.p0,
    s.m10,
    s.bgcA,
    {
      height: 1,
      border: 0,
    },
  ),
  // list items
  select('& b',
    s.block,
    s.m0,
    s.p5,
    s.pr10,
    s.pl10,
    s.colorA,
    s.sansRegular,
    {
      lineHeight: 20,
    },
  ),
  select('& ul',
    s.resetList,
    select('& li',
      s.m0,
      s.p0,
      select('& button',
        s.block,
        s.fullWidth,
        {
          marginTop: 1,
          marginBottom: 1,
          padding: 4,
          height: 'auto',
          textAlign: 'left',
          lineHeight: 20,
        },
        // have to be below `padding: 4`
        s.pr10,
        s.pl10,
      ),
      select('& button:active',
        s.colorBlack,
        s.bgcWhite,
      ),

      select('&.isSelected button, & button:hover',
        s.colorWhite,
        s.bgcBlack,
        { borderRadius: 3 },
      ),
    ),
  ),
  // mobile tweaks
  media(s.maxBreak2,
    s.relative,
    s.block,
    {
      WebkitOverflowScrolling: 'none',
      overflowX: 'hidden',
      overflowY: 'visible',
      top: 'auto',
      left: 'auto',
      maxHeight: '100%',
      borderTopWidth: 0,
    },
  ),
)

function filterSearch(categories, searchText) {
  if (searchText === '') { return categories }
  return categories.filter(c => c.get('name').toLowerCase().includes(searchText.toLowerCase()))
}

function CategoryItem({ category, index, selectedIndexCurrent, onSelect }) {
  const isSelected = selectedIndexCurrent === index
  return (
    <li
      id={`categorySelect_${category.get('id')}`}
      className={classNames({ isSelected })}
    >
      <button
        role="option"
        aria-selected={isSelected}
        onClick={() => onSelect(category)}
      >
        {category.get('name')}
      </button>

  )
}

CategoryItem.propTypes = {
  category: PropTypes.object.isRequired,
  index: PropTypes.number.isRequired,
  selectedIndexCurrent: PropTypes.number,
  onSelect: PropTypes.func.isRequired,
}

CategoryItem.defaultProps = {
  selectedIndexCurrent: null,
}

export default class CategoryPostSelector extends PureComponent {
  static propTypes = {
    onSelect: PropTypes.func.isRequired,
    onClear: PropTypes.func.isRequired,
    subscribedCategories: PropTypes.array.isRequired,
    featuredInCategories: PropTypes.array.isRequired,
    unsubscribedCategories: PropTypes.array.isRequired,
    selectedCategories: PropTypes.array.isRequired,
    resetSelection: PropTypes.bool.isRequired,
  }

  constructor(props) {
    super(props)
    const { subscribedCategories, unsubscribedCategories } = props
    this.state = {
      subscribedCategories,
      unsubscribedCategories,
      searchText: '',
      selectedIndex: null,
      focused: false,
      open: false,
    }

    this.setWrapperRef = this.setWrapperRef.bind(this)
    this.handleClickOutside = this.handleClickOutside.bind(this)
  }

  componentDidMount() {
    const { onSelect, featuredInCategories, selectedCategories } = this.props
    if (featuredInCategories.length > 0) {
      onSelect(featuredInCategories[0])
    }

    if (selectedCategories) {
      this.setIndexOfSelection()
    }
  }

  componentWillReceiveProps(nextProps) {
    const { subscribedCategories, unsubscribedCategories } = nextProps
    const { searchText } = this.state
    this.setState({
      subscribedCategories: filterSearch(subscribedCategories, searchText),
      unsubscribedCategories: filterSearch(unsubscribedCategories, searchText),
    })
  }

  componentDidUpdate(prevProps, prevState) {
    if ((!prevState.open && this.state.open) ||
      (!this.state.open && !prevState.focused && this.state.focused)) {
      document.addEventListener('mousedown', this.handleClickOutside)
    }

    // close & reset selection
    if (!prevProps.resetSelection && this.props.resetSelection) {
      this.close()
    }

    if (this.state.open &&
      (prevState.selectedIndex !== this.state.selectedIndex)) {
      this.scrollToSelectedCategory()
    }

    if (this.props.selectedCategories &&
      prevProps.selectedCategories !== this.props.selectedCategories) {
      this.setIndexOfSelection()
    }

    if (this.state.open && !this.props.selectedCategories.length > 0) {
      this.categorySelectorRef.focus()
    }
  }

  componentWillUnmount() {
    document.removeEventListener('mousedown', this.handleClickOutside)
  }

  setIndexOfSelection() {
    const { selectedCategories } = this.props
    const {
      subscribedCategories,
      unsubscribedCategories,
    } = this.state

    if (selectedCategories.length > 0) {
      // assume we only have one selection at a time for now
      const selectedCategory = selectedCategories[0]
      const categories = subscribedCategories.concat(unsubscribedCategories)

      let selectedIndex = null
      categories.map((category, index) => {
        if (selectedCategory.get('id') === category.get('id')) {
          selectedIndex = index
        }
        return selectedIndex
      })

      return this.setState({ selectedIndex })
    }
    return this.setState({ selectedIndex: null })
  }

  setWrapperRef(node) {
    this.wrapperRef = node
  }

  handleSearch = (event) => {
    const searchText = event.target.value
    const { subscribedCategories, unsubscribedCategories } = this.props
    const { selectedIndex } = this.state

    let newSelectedIndex = selectedIndex
    if (!selectedIndex) {
      newSelectedIndex = searchText === '' ? null : 0
      // newSelectedIndex = 0
    }
    this.setState({
      selectedIndex: newSelectedIndex,
      searchText,
      subscribedCategories: filterSearch(subscribedCategories, searchText),
      unsubscribedCategories: filterSearch(unsubscribedCategories, searchText),
    })
  }

  handleKeyDown = (event) => {
    const { onSelect } = this.props
    const {
      subscribedCategories,
      unsubscribedCategories,
      selectedIndex,
    } = this.state
    const categories = subscribedCategories.concat(unsubscribedCategories)
    const max = categories.length
    const selectedIsNull = (selectedIndex === null)

    if (event.key === 'ArrowDown' && selectedIsNull) {
      this.setState({ selectedIndex: 0 })
    } else if (event.key === 'ArrowDown' && selectedIndex === (max - 1)) {
      this.setState({ selectedIndex: 0 })
    } else if (event.key === 'ArrowDown' && selectedIndex < max) {
      this.setState({ selectedIndex: selectedIndex + 1 })
    } else if (event.key === 'ArrowUp' && !selectedIsNull && selectedIndex > 0) {
      event.preventDefault()
      this.setState({ selectedIndex: selectedIndex - 1 })
    } else if (event.key === 'ArrowUp' && !selectedIsNull && selectedIndex === 0) {
      event.preventDefault()
      this.setState({ selectedIndex: max - 1 })
    } else if (event.key === 'Enter' && !selectedIsNull) {
      onSelect(categories[selectedIndex])
    } else if (event.key === 'Escape') {
      this.categorySelectorRef.blur()
      this.close()
    }
  }

  handleBlur(isFocused) {
    this.setState({
      focused: isFocused,
    })

    if (isFocused) {
      this.open()
    }
  }

  handleSelectorClick(e) {
    if (e.target.nodeName === 'polyline' ||
      e.target.nodeName === 'g' ||
      e.target.nodeName === 'svg') {
      return this.close()
    }
    if (e.target.nodeName !== 'BUTTON') {
      return this.open()
    }
    return null
  }

  handleClickOutside(event) {
    if (this.state.open) {
      if (this.wrapperRef && !this.wrapperRef.contains(event.target)) {
        this.close()
      }
    }
  }

  scrollToSelectedCategory() {
    const {
      subscribedCategories,
      unsubscribedCategories,
      selectedIndex,
    } = this.state
    const categories = subscribedCategories.concat(unsubscribedCategories)
    const selectedCategory = categories[selectedIndex]
    let selectedCategoryDomId = null
    if (selectedCategory) {
      selectedCategoryDomId = `categorySelect_${selectedCategory.get('id')}`
    }

    // grab elements from the dom
    const categoryListInDom = document.getElementById('categoryList')
    const selectedCategoryItemInDom = document.getElementById(selectedCategoryDomId)

    // determine scroll offset of category item in dom
    let categoryItemInDomTopOffset = null
    let categoryListInDomTopOffset = null
    let categoryItemInDomHeight = null
    let categoryListInDomHeight = null
    if (selectedCategoryDomId) {
      categoryItemInDomTopOffset = selectedCategoryItemInDom.getBoundingClientRect().top
      categoryListInDomTopOffset = categoryListInDom.getBoundingClientRect().top
      categoryItemInDomHeight = selectedCategoryItemInDom.clientHeight
      categoryListInDomHeight = categoryListInDom.clientHeight
    }

    if (categoryItemInDomTopOffset) {
      // adjust scroll offset for window height / nav bar
      const scrollElement = categoryListInDom
      const scrollTo = (categoryItemInDomTopOffset - categoryListInDomTopOffset)
      const scrollCheckOffset = categoryItemInDomHeight

      if (
        ((scrollTo + scrollCheckOffset) > categoryListInDomHeight) ||
        (scrollTo < 0)
      ) {
        // scroll to new position
        scrollToPosition(0, scrollTo, { el: scrollElement, duration: 150 })
      }
    }
  }

  open() {
    if (!this.state.open) {
      this.setState({
        open: true,
      })
    }
  }

  close() {
    if (this.state.open) {
      this.resetSelection()
    }
  }

  clearLocal() {
    const { onClear } = this.props
    const { open } = this.state
    if (open) {
      // this.categorySelectorRef.focus()
    }
    onClear()
  }

  resetSelection() {
    const { subscribedCategories, unsubscribedCategories } = this.props
    this.setState({
      subscribedCategories,
      unsubscribedCategories,
      searchText: '',
      focused: false,
      open: false,
    })
  }

  render() {
    const { selectedCategories, onSelect } = this.props
    const {
      subscribedCategories,
      unsubscribedCategories,
      searchText,
      selectedIndex,
      open,
    } = this.state
    // Everything except this component could support multiple categories, but for now
    // we can assume there is only one selected category per post.
    const selectedCategory = selectedCategories[0]
    const offset = subscribedCategories.length
    return (
      /* eslint-disable jsx-a11y/interactive-supports-focus */
       this.handleSelectorClick(e)}
      >
        <span className={classNames({ open }, `${categoriesSelectionsStyle}`)}>
          {!selectedCategory &&

              {!searchText &&

                  {open ? 'Type community name' : 'Choose Community'}

              }
              <ChevronIcon />
              <input
                ref={(node) => { this.categorySelectorRef = node }}
                className="selector"
                name="categorySelector"
                type="search"
                value={searchText}
                onChange={this.handleSearch}
                onKeyDown={this.handleKeyDown}
                onBlur={() => this.handleBlur(false)}
                onFocus={() => this.handleBlur(true)}
              />

          }
          {selectedCategory &&

              Post into: {selectedCategory.get('name')}
              <button onClick={() => this.clearLocal()}>
                Remove

                  <XIcon />

              </button>

          }

        {open &&

            {subscribedCategories.length > 0 &&

                Your Categories

                  {subscribedCategories.map((category, index) =>
                    (<CategoryItem
                      key={`categorySelect:${category.get('id')}`}
                      category={category}
                      index={index}
                      selectedIndexCurrent={selectedIndex}
                      onSelect={onSelect}
                    />),
                  )}

            }

              All Categories

                {unsubscribedCategories.map((category, index) =>
                  (<CategoryItem
                    key={`categorySelect:${category.get('id')}`}
                    category={category}
                    index={index + offset}
                    selectedIndexCurrent={selectedIndex}
                    onSelect={onSelect}
                  />),
                )}

        }

    )
  }
}
<|endoftext|>


<commit_before>#ifndef __browser_Dimension__
#define __browser_Dimension__

#ifdef HIDDEN
/**
 * Some code in this file is based on Mootools.net and adapted to the
 * architecture of Bootstrap, with added changes in design and architecture
 * where deemeded necessary.
 * See http://www.bootstrap-js.net/wiki/MootoolsDifferences
 */
#endif

#include "Element.js"

////////////////////////////////////////////////////////////////////////////////
// Dimension

Element.inject(function() {
#ifdef BROWSER_LEGACY
	function cumulate(name, parent, iter, fix) {
		fix = fix && Browser.MACIE;
#else // !BROWSER_LEGACY
	function cumulate(name, parent, iter) {
#endif // !BROWSER_LEGACY
		var left = name + 'Left', top = name + 'Top';
		return function() {
			var el, next = this, x = 0, y = 0;
			do {
				el = next;
				x += el[left] || 0;
				y += el[top] || 0;
			} while((next = el[parent]) && (!iter || iter(el)))
#ifdef BROWSER_LEGACY
			// fix body on mac ie
			if (fix) ['margin', 'padding'].each(function(val) {
				x += this.getStyle(val + '-left').toInt() || 0;
				y += this.getStyle(val + '-top').toInt() || 0;
			}, $(el));
#endif // BROWSER_LEGACY
			return { x: x, y: y };
		}
	}

	var fields = {
		getSize: function() {
			return { width: this.offsetWidth, height: this.offsetHeight };
		},

		getOffset: cumulate('offset', 'offsetParent', Browser.KHTML ? function(el) {
			// Safari returns margins on body which is incorrect if the
			// child is absolutely positioned.
			return el.offsetParent != document.body ||
				$(el).getStyle('position') != 'absolute';
		} : null, true),

		getRelativeOffset: cumulate('offset', 'offsetParent', function(el) {
	        return Element.prototype.getTag.call(el) != 'body' &&
				!/^(relative|absolute)$/.test(Element.prototype.getStyle.call(el, 'position'))
		}),

		// TODO: Needed?
		getScrollOffset: cumulate('scroll', 'parentNode'),

		// TODO: Consider rename:
		getScrollPos: function() {
			return { x: this.scrollLeft, y: this.scrollTop };
		},

		getScrollSize: function() {
			return { width: this.scrollWidth, height: this.scrollHeight };
		},

		getBounds: function() {
			var off = this.getOffset();
			return {
				width: this.offsetWidth,
				height: this.offsetHeight,
				left: off.x,
				top: off.y,
				right: off.x + this.offsetWidth,
				bottom: off.y + this.offsetHeight
			};
		},

		setBounds: function(bounds) {
			this.setStyles(bounds.each(function(val, i) {
				if (val || val == 0) this[i] = val + 'px';
			}, { position: 'absolute' }));
		},

		moveTo: function(x, y) {
			this.setLeft(x);
			this.setTop(y);
		},

		moveBy: function(x, y) {
			if (x) this.setLeft(this.getLeft() + x);
			if (y) this.setTop(this.getTop() + y);
		},

		scrollTo: function(x, y) {
			this.scrollLeft = x;
			this.scrollTop = y;
		},

		scrollBy: function(x, y) {
			this.scrollLeft += x;
			this.scrollTop += y;
		}
	};

	// Dimension properties:
	$A('left top width height').each(function(name) {
		var part = name.capitalize();
		fields['get' + part] = function() {
			return this['offset' + part];
		};
		fields['set' + part] = function(value) {
			this.style[name] = value + 'px';
		};
	});

	return fields;
});

#endif // __browser_Dimension__<commit_msg>Added position-aware getAt() and contains().<commit_after>#ifndef __browser_Dimension__
#define __browser_Dimension__

#ifdef HIDDEN
/**
 * Some code in this file is based on Mootools.net and adapted to the
 * architecture of Bootstrap, with added changes in design and architecture
 * where deemeded necessary.
 * See http://www.bootstrap-js.net/wiki/MootoolsDifferences
 */
#endif

#include "Element.js"

////////////////////////////////////////////////////////////////////////////////
// Dimension

Element.inject(function() {
#ifdef BROWSER_LEGACY
	function cumulate(name, parent, iter, fix) {
		fix = fix && Browser.MACIE;
#else // !BROWSER_LEGACY
	function cumulate(name, parent, iter) {
#endif // !BROWSER_LEGACY
		var left = name + 'Left', top = name + 'Top';
		return function() {
			var el, next = this, x = 0, y = 0;
			do {
				el = next;
				x += el[left] || 0;
				y += el[top] || 0;
			} while((next = el[parent]) && (!iter || iter(el)))
#ifdef BROWSER_LEGACY
			// fix body on mac ie
			if (fix) ['margin', 'padding'].each(function(val) {
				x += this.getStyle(val + '-left').toInt() || 0;
				y += this.getStyle(val + '-top').toInt() || 0;
			}, $(el));
#endif // BROWSER_LEGACY
			return { x: x, y: y };
		}
	}

	var fields = {
		getSize: function() {
			return { width: this.offsetWidth, height: this.offsetHeight };
		},

		getOffset: cumulate('offset', 'offsetParent', Browser.KHTML ? function(el) {
			// Safari returns margins on body which is incorrect if the
			// child is absolutely positioned.
			return el.offsetParent != document.body ||
				$(el).getStyle('position') != 'absolute';
		} : null, true),

		getRelativeOffset: cumulate('offset', 'offsetParent', function(el) {
	        return Element.prototype.getTag.call(el) != 'body' &&
				!/^(relative|absolute)$/.test(Element.prototype.getStyle.call(el, 'position'))
		}),

		// TODO: Needed?
		getScrollOffset: cumulate('scroll', 'parentNode'),

		// TODO: Consider rename:
		getScrollPos: function() {
			return { x: this.scrollLeft, y: this.scrollTop };
		},

		getScrollSize: function() {
			return { width: this.scrollWidth, height: this.scrollHeight };
		},

		getBounds: function() {
			var off = this.getOffset();
			return {
				width: this.offsetWidth,
				height: this.offsetHeight,
				left: off.x,
				top: off.y,
				right: off.x + this.offsetWidth,
				bottom: off.y + this.offsetHeight
			};
		},

		setBounds: function(bounds) {
			this.setStyles(bounds.each(function(val, i) {
				if (val || val == 0) this[i] = val + 'px';
			}, { position: 'absolute' }));
		},

		// TODO: maybe confusing name? something with 'within'?
		contains: function(pos) {
			var bounds = this.getBounds();
			return pos.x >= bounds.left && pos.x < bounds.right &&
				pos.y >= bounds.top && pos.y < bounds.bottom;
		},

		scrollTo: function(x, y) {
			this.scrollLeft = x;
			this.scrollTop = y;
		},

		$static: {
			getAt: function(pos, exclude) {
				var el = $E('body');
				while (true) {
					var max = -1;
					var ch = el.getFirst();
					while (ch) {
						if (ch.contains(pos) && ch != exclude) {
							var z = ch.style.zIndex.toInt() || 0;
							if (z >= max) {
								el = ch;
								max = z;
							}
						}
						ch = ch.getNext();
					}
					if (max < 0) break;
				}
				return el;
			}
		}
	};

	// Dimension getters and setters:
	$A('left top width height').each(function(name) {
		var part = name.capitalize();
		fields['get' + part] = function() {
			return this['offset' + part];
		};
		fields['set' + part] = function(value) {
			this.style[name] = value + 'px';
		};
	});

	return fields;
});

#endif // __browser_Dimension__<|endoftext|>


<commit_before>"use strict";

var request = require("request");

function Genius(accessToken) {

  function _makeRequest(options, callback) {
    var baseRequest = request.defaults({
      baseUrl: "https://api.genius.com",
      headers: { "Authorization": "Bearer " + accessToken }
    });

    baseRequest(options, function(error, response) {
      if (response.statusCode > 399) {
        callback(response.body);
        return;
      }
      callback(null, response.body);
    });
  }

  /**
   * Search genius.com.
   * @param  {String}   query    The term to search for.
   * @param  {Function} callback The function to call once the search results are available.
   * @see https://docs.genius.com/#search-h2
   * @example
   */
  this.search = function(query, callback) {
    var request = {
      url: "https://api.genius.com/search",
      qs: { "q": query }
    };
    _makeRequest(request, callback);
  };

  /**
   * Look up an artist.
   * @param  {String}   artistId The artist's id.
   * @param  {Object}   options  The possible options e.g. text_format.
   * @param  {Function} callback The function to call once the artist data is available.
   * @see https://docs.genius.com/#artists-h2
   */
  this.getArtist = function(artistId, options, callback) {
    if (typeof options === "function") {
      callback = options;
    }

    var request = {
      url: "artists/" + artistId,
      qs: options
    };
    _makeRequest(request, callback);
  };

  this.getArtistSongs = function(artistId, options, callback) {
    if (typeof options === "function") {
      callback = options;
    }

    var request = {
      url: "artists/" + artistId + "/songs",
      qs: options
    };
    _makeRequest(request, callback);
  };

  this.getSong = function(songId, options, callback) {
    if (typeof options === "function") {
      callback = options;
    }

    var request = {
      url: "songs/" + songId,
      qs: options
    };
    _makeRequest(request, callback);
  };

  this.getAnnotation = function(annotationId, options, callback) {
    if (typeof options === "function") {
      callback = options;
    }

    var request = {
      url: "annotations/" + annotationId,
      qs: options
    };
    _makeRequest(request, callback);
  };

  this.createAnnotation = function(annotation, callback) {
    var request = {
      url: "annotations",
      json: true,
      method: "POST",
      body: annotation
    };
    _makeRequest(request, callback);
  };

  this.deleteAnnotation = function (annotationId, callback) {
    var request = {
      url: "annotations/" + annotationId,
      method: "DELETE",
    };
    _makeRequest(request, callback);
  };

  this.upvoteAnnotation = function (annotationId, callback) {
    var request = {
      url: "annotations/" + annotationId + "/upvote",
      method: "PUT",
    };
    _makeRequest(request, callback);
  };

  this.unvoteAnnotation = function (annotationId, callback) {
    var request = {
      url: "annotations/" + annotationId + "/unvote",
      method: "PUT",
    };
    _makeRequest(request, callback);
  };

  this.downvoteAnnotation = function (annotationId, callback) {
    var request = {
      url: "annotations/" + annotationId + "/downvote",
      method: "PUT",
    };
    _makeRequest(request, callback);
  };

  this.downvoteAnnotation = function (annotationId, callback) {
    var request = {
      url: "annotations/" + annotationId + "/downvote",
      method: "PUT",
    };
    _makeRequest(request, callback);
  };

  this.getMe = function (options, callback) {
    if (typeof options === "function") {
      callback = options;
    }

    var request = {
      url: "account",
      qs: options
    };
    _makeRequest(request, callback);
  };
}

module.exports = Genius;<commit_msg>added @example comments<commit_after>"use strict";

var request = require("request");

function Genius(accessToken) {

  function _makeRequest(options, callback) {
    var baseRequest = request.defaults({
      baseUrl: "https://api.genius.com",
      headers: { "Authorization": "Bearer " + accessToken }
    });

    baseRequest(options, function(error, response) {
      if (response.statusCode > 399) {
        callback(response.body);
        return;
      }
      callback(null, response.body);
    });
  }

  /**
   * Search genius.com.
   * @param  {String}   query    The term to search for.
   * @param  {Function} callback The function to call once the search results are available.
   * @see https://docs.genius.com/#search-h2
   * @example
   * search("Kendrick Lamar", function (error, searchResults) { });
   */
  this.search = function(query, callback) {
    var request = {
      url: "https://api.genius.com/search",
      qs: { "q": query }
    };
    _makeRequest(request, callback);
  };

  /**
   * Look up an artist.
   * @param  {String}   artistId The artist's id.
   * @param  {Object}   options  The possible options e.g. text_format.
   * @param  {Function} callback The function to call once the artist data is available.
   * @see https://docs.genius.com/#artists-h2
   * @example
   * getArtist("16775", function (error, artist) { });
   */
  this.getArtist = function(artistId, options, callback) {
    if (typeof options === "function") {
      callback = options;
    }

    var request = {
      url: "artists/" + artistId,
      qs: options
    };
    _makeRequest(request, callback);
  };

  this.getArtistSongs = function(artistId, options, callback) {
    if (typeof options === "function") {
      callback = options;
    }

    var request = {
      url: "artists/" + artistId + "/songs",
      qs: options
    };
    _makeRequest(request, callback);
  };

  this.getSong = function(songId, options, callback) {
    if (typeof options === "function") {
      callback = options;
    }

    var request = {
      url: "songs/" + songId,
      qs: options
    };
    _makeRequest(request, callback);
  };

  this.getAnnotation = function(annotationId, options, callback) {
    if (typeof options === "function") {
      callback = options;
    }

    var request = {
      url: "annotations/" + annotationId,
      qs: options
    };
    _makeRequest(request, callback);
  };

  this.createAnnotation = function(annotation, callback) {
    var request = {
      url: "annotations",
      json: true,
      method: "POST",
      body: annotation
    };
    _makeRequest(request, callback);
  };

  this.deleteAnnotation = function (annotationId, callback) {
    var request = {
      url: "annotations/" + annotationId,
      method: "DELETE",
    };
    _makeRequest(request, callback);
  };

  this.upvoteAnnotation = function (annotationId, callback) {
    var request = {
      url: "annotations/" + annotationId + "/upvote",
      method: "PUT",
    };
    _makeRequest(request, callback);
  };

  this.unvoteAnnotation = function (annotationId, callback) {
    var request = {
      url: "annotations/" + annotationId + "/unvote",
      method: "PUT",
    };
    _makeRequest(request, callback);
  };

  this.downvoteAnnotation = function (annotationId, callback) {
    var request = {
      url: "annotations/" + annotationId + "/downvote",
      method: "PUT",
    };
    _makeRequest(request, callback);
  };

  this.downvoteAnnotation = function (annotationId, callback) {
    var request = {
      url: "annotations/" + annotationId + "/downvote",
      method: "PUT",
    };
    _makeRequest(request, callback);
  };

  this.getMe = function (options, callback) {
    if (typeof options === "function") {
      callback = options;
    }

    var request = {
      url: "account",
      qs: options
    };
    _makeRequest(request, callback);
  };
}

module.exports = Genius;<|endoftext|>


<commit_before>var React = require('react');

var points = (function () {
  var color = '#df4b26';
  var size = 5;
  var points = [];
  var forEach = function (fn, a) { a.forEach(fn); };

  var Point = function (x, y, dragging, color, size) {
    this.getX = function () { return x; };
    this.getY = function () { return y; };
    this.getColor = function () { return color; };
    this.getSize = function () { return size; };
    this.getDragging = function () { return dragging; };
  };

  Point.prototype.data = function () {
    return {
      x: this.getX(),
      y: this.getY(),
      color: this.getColor(),
      size: this.getSize(),
      dragging: this.getDragging()
    }
  };

  var add = function (x, y, dragging) {
    var p;
    if (x instanceof Object) {
      p = new Point(x.x, x.y, !!x.dragging, x.color, x.size);
    } else {
      p = new Point(x, y, !!dragging, color, size);
    }
    points.push(p);
    return p;
  };

  return {
		/**
		 * Add a new Point to the drawing.
		 *
		 * @method add
		 * @param x {int} The x cordination.
		 * @param y {int} The y cordination.
		 * @param dragging {boolean} True if its not the first point in current line.
		 * @return Returns the newly created Point.
		 */

		add: add,

		addArray: forEach.bind(null, add),

		nrOfPoints: function () { return points.length; },

		/**
		 * Set the color. All points after the color is changed till its
		 * changed again will have the same color.
		 *
		 * @method setColor
		 * @param c {string} The new color.
		 */

		setColor: function (c) { color = c; },

		/**
		 * Set the size. All points after the size is changed til its changed
		 * again will have the same size.
		 *
		 * @method setSize
		 * @param s {int} The new size.
		 */

		setSize: function (s) { size = s; },

		/**
		 * Remove all points.
		 *
		 * @method clear
		 */

		clear: function () { points = []; },

		/**
		 * Iterate through each point.
		 *
		 * @method each
		 * @param fn {function} Callback function that takes two parameters
		 *                      (previous and current). Previous is undefined
		 *                      if its the first point.
		 */

		each: function (fn) {
			var i, previous, current;
			for (i = 0; i < points.length; i += 1) {
				previous = points[i - 1] && points[i - 1].data();
				current = points[i].data();
				fn(previous, current);
			}
		},

		/**
		 * Gets the previous and the current point.
		 *
		 * @method last
		 * @param fn {function} Callbakc function that takes two parameters
		 *                      (previous and current). Previous is undefined
		 *                      if its the first point.
		 */

		last: function (fn) {
			var i = points.length - 1,
				previous = points[i - 1] && points[i - 1].data(),
				current = points[i].data();
			fn(previous, current);
		}
  };
}());

module.exports = React.createClass({
  propTypes: {
    offsetX: React.PropTypes.number,
    offsetY: React.PropTypes.number
  },

  getDefaultProps: function () {
    return { offsetX: 0, offsetY: 0 };
  },

  getInitialState: function () {
    return { painting: false };
  },

  componentDidMount: function () {
    this.context = this.refs.canvas.getDOMNode().getContext('2d');

    // Prevent Chrome from selecting the canvas
    this.context.canvas.addEventListener('selectstart', function () { return false; });
    this.context.canvas.addEventListener('mousedown', function () { return false; });

    this._clear();

    this._start(); // TODO Remove!
  },

  componentDidUpdate: function () {
    // Need to upadate the context when the component is updated or
    // this.context will be undefined.
    this.context = this.refs.canvas.getDOMNode().getContext('2d');
  },

  _start: function () {
    this.context.canvas.addEventListener('mousedown', this._startDrawing);
    this.context.canvas.addEventListener('mouseup', this._stopDrawing);
    this.context.canvas.addEventListener('mousemove', this._drawing);
    this.context.canvas.addEventListener('mouseleave', this._stopDrawing); // Firefox etc.
    this.context.canvas.addEventListener('mouseout', this._stopDrawing); // Chrome
  },

  _stop: function () {
    this.context.canvas.removeEventListener('mousedown', this._startDrawing);
    this.context.canvas.removeEventListener('mouseup', this._stopDrawing);
    this.context.canvas.removeEventListener('mousemove', this._drawing);
    this.context.canvas.removeEventListener('mouseleave', this._stopDrawing); // Firefox etc.
    this.context.canvas.removeEventListener('mouseout', this._stopDrawing); // Chrome
  },

  _clear: function () {
    this.setState({ painting: false });
    // Remove all points
    points.clear();
    // Clear the canvas
    this.context.clearRect(0, 0, this.context.canvas.width, this.context.canvas.height);
    // Pencil style
    this.context.lineJoin = 'round';
  },

  _addPoint: function (x, y, dragging) {
    var xx = x - this.context.canvas.offsetLeft - this.props.offsetX;
    var yy = y - this.context.canvas.offsetTop - this.props.offsetY;
    var p = points.add(xx, yy, dragging);

    // Send points to server
    //room.sendPoints(p.data());
    // Start drawing
    points.last(this._drawLine);
  },

  _startDrawing: function (e) {
    this.setState({ painting: true });
		this._addPoint(e.pageX, e.pageY);
	},

  _stopDrawing: function () {
    this.setState({ painting: false });
	},

  _drawing: function (e) {
		if (this.state.painting) {
			this._addPoint(e.pageX, e.pageY, true);
		}
	},

  // former draw() TODO Remove this comment!!!!
  _drawLine: function (prev, curr) {
		if (typeof prev === 'undefined' || prev.color !== curr.color || prev.size !== curr.size) {
			// Pencil style
			this.context.strokeStyle = curr.color;
			this.context.lineWidth = curr.size;
		}

		// Drawing
		this.context.beginPath();

		if (curr.dragging) {
			this.context.moveTo(prev.x, prev.y);
		} else {
			this.context.moveTo(curr.x - 0.1, curr.y - 0.1); // Make a dot
		}
		this.context.lineTo(curr.x, curr.y);

		this.context.closePath();
		this.context.stroke();
	},

  render: function () {
    return (

          <canvas id="artboard" width="800" height="600" ref="canvas"></canvas>

        );
  }
});
<commit_msg>Fix indentation<commit_after>var React = require('react');

var points = (function () {
  var color = '#df4b26';
  var size = 5;
  var points = [];
  var forEach = function (fn, a) { a.forEach(fn); };

  var Point = function (x, y, dragging, color, size) {
    this.getX = function () { return x; };
    this.getY = function () { return y; };
    this.getColor = function () { return color; };
    this.getSize = function () { return size; };
    this.getDragging = function () { return dragging; };
  };

  Point.prototype.data = function () {
    return {
      x: this.getX(),
      y: this.getY(),
      color: this.getColor(),
      size: this.getSize(),
      dragging: this.getDragging()
    }
  };

  var add = function (x, y, dragging) {
    var p;
    if (x instanceof Object) {
      p = new Point(x.x, x.y, !!x.dragging, x.color, x.size);
    } else {
      p = new Point(x, y, !!dragging, color, size);
    }
    points.push(p);
    return p;
  };

  return {
    /**
     * Add a new Point to the drawing.
     *
     * @method add
     * @param x {int} The x cordination.
     * @param y {int} The y cordination.
     * @param dragging {boolean} True if its not the first point in current line.
     * @return Returns the newly created Point.
     */

    add: add,

    addArray: forEach.bind(null, add),

    nrOfPoints: function () { return points.length; },

    /**
     * Set the color. All points after the color is changed till its
     * changed again will have the same color.
     *
     * @method setColor
     * @param c {string} The new color.
     */

    setColor: function (c) { color = c; },

    /**
     * Set the size. All points after the size is changed til its changed
     * again will have the same size.
     *
     * @method setSize
     * @param s {int} The new size.
     */

    setSize: function (s) { size = s; },

    /**
     * Remove all points.
     *
     * @method clear
     */

    clear: function () { points = []; },

    /**
     * Iterate through each point.
     *
     * @method each
     * @param fn {function} Callback function that takes two parameters
     *                      (previous and current). Previous is undefined
     *                      if its the first point.
     */

    each: function (fn) {
      var i, previous, current;
      for (i = 0; i < points.length; i += 1) {
        previous = points[i - 1] && points[i - 1].data();
        current = points[i].data();
        fn(previous, current);
      }
    },

    /**
     * Gets the previous and the current point.
     *
     * @method last
     * @param fn {function} Callbakc function that takes two parameters
     *                      (previous and current). Previous is undefined
     *                      if its the first point.
     */

    last: function (fn) {
      var i = points.length - 1,
      previous = points[i - 1] && points[i - 1].data(),
      current = points[i].data();
      fn(previous, current);
    }
  };
}());

module.exports = React.createClass({
  propTypes: {
    offsetX: React.PropTypes.number,
    offsetY: React.PropTypes.number
  },

  getDefaultProps: function () {
    return { offsetX: 0, offsetY: 0 };
  },

  getInitialState: function () {
    return { painting: false };
  },

  componentDidMount: function () {
    this.context = this.refs.canvas.getDOMNode().getContext('2d');

    // Prevent Chrome from selecting the canvas
    this.context.canvas.addEventListener('selectstart', function () { return false; });
    this.context.canvas.addEventListener('mousedown', function () { return false; });

    this._clear();

    this._start(); // TODO Remove!
  },

  componentDidUpdate: function () {
    // Need to upadate the context when the component is updated or
    // this.context will be undefined.
    this.context = this.refs.canvas.getDOMNode().getContext('2d');
  },

  _start: function () {
    this.context.canvas.addEventListener('mousedown', this._startDrawing);
    this.context.canvas.addEventListener('mouseup', this._stopDrawing);
    this.context.canvas.addEventListener('mousemove', this._drawing);
    this.context.canvas.addEventListener('mouseleave', this._stopDrawing); // Firefox etc.
    this.context.canvas.addEventListener('mouseout', this._stopDrawing); // Chrome
  },

  _stop: function () {
    this.context.canvas.removeEventListener('mousedown', this._startDrawing);
    this.context.canvas.removeEventListener('mouseup', this._stopDrawing);
    this.context.canvas.removeEventListener('mousemove', this._drawing);
    this.context.canvas.removeEventListener('mouseleave', this._stopDrawing); // Firefox etc.
    this.context.canvas.removeEventListener('mouseout', this._stopDrawing); // Chrome
  },

  _clear: function () {
    this.setState({ painting: false });
    // Remove all points
    points.clear();
    // Clear the canvas
    this.context.clearRect(0, 0, this.context.canvas.width, this.context.canvas.height);
    // Pencil style
    this.context.lineJoin = 'round';
  },

  _addPoint: function (x, y, dragging) {
    var xx = x - this.context.canvas.offsetLeft - this.props.offsetX;
    var yy = y - this.context.canvas.offsetTop - this.props.offsetY;
    var p = points.add(xx, yy, dragging);

    // Send points to server
    //room.sendPoints(p.data());
    // Start drawing
    points.last(this._drawLine);
  },

  _startDrawing: function (e) {
    this.setState({ painting: true });
		this._addPoint(e.pageX, e.pageY);
	},

  _stopDrawing: function () {
    this.setState({ painting: false });
	},

  _drawing: function (e) {
		if (this.state.painting) {
			this._addPoint(e.pageX, e.pageY, true);
		}
	},

  // former draw() TODO Remove this comment!!!!
  _drawLine: function (prev, curr) {
		if (typeof prev === 'undefined' || prev.color !== curr.color || prev.size !== curr.size) {
			// Pencil style
			this.context.strokeStyle = curr.color;
			this.context.lineWidth = curr.size;
		}

		// Drawing
		this.context.beginPath();

		if (curr.dragging) {
			this.context.moveTo(prev.x, prev.y);
		} else {
			this.context.moveTo(curr.x - 0.1, curr.y - 0.1); // Make a dot
		}
		this.context.lineTo(curr.x, curr.y);

		this.context.closePath();
		this.context.stroke();
	},

  render: function () {
    return (

          <canvas id="artboard" width="800" height="600" ref="canvas"></canvas>

        );
  }
});
<|endoftext|>


<commit_before>var Sequelize = require('sequelize'),
    _ = require('lodash');

module.exports = function(target) {
  if (target instanceof Sequelize.Model) {
    // Model
    createHook(target);
  } else {
    // Sequelize instance
    target.afterDefine(createHook);
  }
}

function createHook(model) {
   model.hook('beforeFindAfterOptions', function(options) {
    if (options.role) {
      var role = options.role,
          attributes = options.attributes,
          includes = options.include;

      options.attributes = attrAccessible(model, role, attributes);
      cleanIncludesRecursive(includes, role);
    }
  });
}

function attrAccessible(model, role, attributes) {
  var attrsToReject = attrProtected(model, role);
  return rejectAttributes(attributes, attrsToReject);
}

function attrProtected(model, role) {
  return _.keys(_.pickBy(model.attributes, function(attr, name) {
    return !_.isUndefined(attr.access) && ( attr.access === false || attr.access[role] === false );
  }));
}

function rejectAttributes(attributes, attrsToRemove) {
  return _.reject(attributes, function(attr) {
    if (_.isArray(attr)) {
      attr = attr[1];
    }

    return _.includes(attrsToRemove, attr);
  });
}

function cleanIncludesRecursive(includes, role) {
  _.each(includes, function(include) {
    var model = include.model,
        attributes = include.attributes;

    include.attributes = attrAccessible(model, role, attributes);

    if (include.include) {
      cleanIncludesRecursive(include.include, role);
    }
  });
}
<commit_msg>Replace usage of deprecated hook method<commit_after>var Sequelize = require('sequelize'),
    _ = require('lodash');

module.exports = function(target) {
  if (target instanceof Sequelize.Model) {
    // Model
    createHook(target);
  } else {
    // Sequelize instance
    target.afterDefine(createHook);
  }
}

function createHook(model) {
   model.addHook('beforeFindAfterOptions', function(options) {
    if (options.role) {
      var role = options.role,
          attributes = options.attributes,
          includes = options.include;

      options.attributes = attrAccessible(model, role, attributes);
      cleanIncludesRecursive(includes, role);
    }
  });
}

function attrAccessible(model, role, attributes) {
  var attrsToReject = attrProtected(model, role);
  return rejectAttributes(attributes, attrsToReject);
}

function attrProtected(model, role) {
  return _.keys(_.pickBy(model.attributes, function(attr, name) {
    return !_.isUndefined(attr.access) && ( attr.access === false || attr.access[role] === false );
  }));
}

function rejectAttributes(attributes, attrsToRemove) {
  return _.reject(attributes, function(attr) {
    if (_.isArray(attr)) {
      attr = attr[1];
    }

    return _.includes(attrsToRemove, attr);
  });
}

function cleanIncludesRecursive(includes, role) {
  _.each(includes, function(include) {
    var model = include.model,
        attributes = include.attributes;

    include.attributes = attrAccessible(model, role, attributes);

    if (include.include) {
      cleanIncludesRecursive(include.include, role);
    }
  });
}
<|endoftext|>


<commit_before>'use strict';

var childProcess = require('child_process');
var dargs = require('dargs');
var defaults = require('lodash.defaults');
var path = require('path');
var PluginError = require('gulp-util').PluginError;
var fs = require('vinyl-fs');
var pkgName = require('../package.json').name;
var through = require('through2');
var temp = require('temp').track();
var async = require('async');

function pluginError(msg) {
  return new PluginError(pkgName, msg);
}

/**
 * Given `{Object}` `options`, apply default settings.
 * @param {Object} [options] Options object
 * @parma {string} [options.source] Source directory
 * @param {string} [options.layouts='_layouts'] Layouts directory
 * @param {string} [options.cwd=process.cwd()] Other specified directories are
 *   relative to this
 * @param {(Array|string)} [options.plugins='_plugins'] Plugins directory or
 *   directories
 * @param {boolean} [options.bundleExec=false] Run with `bundle exec`
 * @param {boolean} [options.quiet=true] Quiet mode.  If `true`, implies
 *   `--trace`
 * @param {boolean} [options.safe=false] Run Jekyll in "safe" mode
 * @returns {Object} `options` with defaults applied
 */
function defaultOptions(options) {
  var cwd;
  options = options || {};
  cwd = options.cwd || process.cwd();

  return defaults(options, {
    bundleExec: false,
    quiet: true,
    cwd: cwd,
    plugins: path.join(cwd, '_plugins/'),
    layouts: path.join(cwd, '_layouts/'),
    clean: true
  });
}

function buildArgs(file, options, done) {
  var includes;
  var args = arguments;

  process.nextTick(function build() {
    if (!args.length) {
      done(new Error('buildArgs: Invalid parameters'));
    }

    options = options || {};
    options.source = options.source || file.path || process.cwd();

    includes = [
      'source',
      'destination',
      'plugins',
      'layouts'
    ];

    if (options.trace) {
      includes.push('trace');
    }

    if (options.safe) {
      includes.push('safe');
    }

    done(null, (options.bundleExec ? ['bundle', 'exec'] : [])
      .concat(['jekyll', 'build'])
      .concat(dargs(options, {
        includes: includes
      })), options);
  });
}

function spawn(args, options, done) {
  var proc;
  var errors = '';

  temp.mkdir(pkgName, function(err, dirpath) {
    if (err) {
      return done(new Error(err));
    }
    args.push(dargs({
      destination: dirpath
    }));
    proc = childProcess.spawn(args.shift(), args)
      .on('error', function onError(e) {
        done(new Error(e));
      })
      .on('close', function onClose(code) {
        if (code === 127) {
          return done(new Error('You need to have Ruby and Jekyll installed ' +
            'and in your PATH for this task to work.\n' +
            'See http://jekyllrb.com/docs/installation/'));
        }

        if (errors) {
          return done(new Error(errors));
        }

        if (code) {
          return done(new Error('Jekyll exited with error code ' + code));
        }

        done(null, fs.src(path.join(dirpath, '**', '*')));
      });

    proc.stderr.setEncoding('utf8');
    proc.stderr.on('data', function onStderr(data) {
      errors += data;
    });

    if (!options.quiet) {
      proc.stdout.pipe(process.stdout);
    }
  });
}

function jekyll(options) {
  var opts = defaultOptions(options);

  return through.obj(function spawnJekyll(file, enc, done) {
    var push = this.push.bind(this);
    var emit = this.emit.bind(this);

    function streamOutput(src, cb) {
      src.pipe(through.obj(function transform(vfile, encoding, next) {
        push(vfile);
        next();
      }, function flush(next) {
        next();
        cb();
      }));

      if (options.destination) {
        src.pipe(fs.dest(options.destination));
      }
    }

    if (!file.isNull()) {
      emit('error', pluginError('Source must be a directory!'));
      return done();
    }

    async.seq(buildArgs, spawn, streamOutput)(file, opts, function(err) {
      if (err) {
        emit('error', pluginError(err));
      }
      done();
    });
  });
}

module.exports = jekyll;
<commit_msg>Added `--config` option and Windows fix<commit_after>'use strict';

var childProcess = require('child_process');
var dargs = require('dargs');
var defaults = require('lodash.defaults');
var path = require('path');
var PluginError = require('gulp-util').PluginError;
var fs = require('vinyl-fs');
var pkgName = require('../package.json').name;
var through = require('through2');
var temp = require('temp').track();
var async = require('async');

function pluginError(msg) {
  return new PluginError(pkgName, msg);
}

/**
 * Given `{Object}` `options`, apply default settings.
 * @param {Object} [options] Options object
 * @parma {string} [options.source] Source directory
 * @param {string} [options.layouts='_layouts'] Layouts directory
 * @param {string} [options.cwd=process.cwd()] Other specified directories are
 *   relative to this
 * @param {(Array|string)} [options.plugins='_plugins'] Plugins directory or
 *   directories
 * @param {boolean} [options.bundleExec=false] Run with `bundle exec`
 * @param {boolean} [options.quiet=true] Quiet mode.  If `true`, implies
 *   `--trace`
 * @param {boolean} [options.safe=false] Run Jekyll in "safe" mode
 * @returns {Object} `options` with defaults applied
 */
function defaultOptions(options) {
  var cwd;
  options = options || {};
  cwd = options.cwd || process.cwd();

  return defaults(options, {
    bundleExec: false,
    quiet: true,
    cwd: cwd,
    plugins: path.join(cwd, '_plugins/'),
    layouts: path.join(cwd, '_layouts/'),
    clean: true
  });
}

function buildArgs(file, options, done) {
  var includes, jekyllCmd;
  var args = arguments;

  process.nextTick(function build() {
    if (!args.length) {
      done(new Error('buildArgs: Invalid parameters'));
    }

    options = options || {};
    options.source = options.source || file.path || process.cwd();

    includes = [
      'source',
      'destination',
      'plugins',
      'layouts',
      'config'
    ];

    if (options.trace) {
      includes.push('trace');
    }

    if (options.safe) {
      includes.push('safe');
    }

    jekyllCmd = /win(32|64)/i.test(process.platform) ? 'jekyll.bat' : 'jekyll';

    done(null, (options.bundleExec ? ['bundle', 'exec'] : [])
      .concat([jekyllCmd, 'build'])
      .concat(dargs(options, {
        includes: includes
      })), options);
  });
}

function spawn(args, options, done) {
  var proc;
  var errors = '';

  temp.mkdir(pkgName, function(err, dirpath) {
    if (err) {
      return done(new Error(err));
    }
    args.push(dargs({
      destination: dirpath
    }));
    proc = childProcess.spawn(args.shift(), args)
      .on('error', function onError(e) {
        done(new Error(e));
      })
      .on('close', function onClose(code) {
        if (code === 127) {
          return done(new Error('You need to have Ruby and Jekyll installed ' +
            'and in your PATH for this task to work.\n' +
            'See http://jekyllrb.com/docs/installation/'));
        }

        if (errors) {
          return done(new Error(errors));
        }

        if (code) {
          return done(new Error('Jekyll exited with error code ' + code));
        }

        done(null, fs.src(path.join(dirpath, '**', '*')));
      });

    proc.stderr.setEncoding('utf8');
    proc.stderr.on('data', function onStderr(data) {
      errors += data;
    });

    if (!options.quiet) {
      proc.stdout.pipe(process.stdout);
    }
  });
}

function jekyll(options) {
  var opts = defaultOptions(options);

  return through.obj(function spawnJekyll(file, enc, done) {
    var push = this.push.bind(this);
    var emit = this.emit.bind(this);

    function streamOutput(src, cb) {
      src.pipe(through.obj(function transform(vfile, encoding, next) {
        push(vfile);
        next();
      }, function flush(next) {
        next();
        cb();
      }));

      if (options.destination) {
        src.pipe(fs.dest(options.destination));
      }
    }

    if (!file.isNull()) {
      emit('error', pluginError('Source must be a directory!'));
      return done();
    }

    async.seq(buildArgs, spawn, streamOutput)(file, opts, function(err) {
      if (err) {
        emit('error', pluginError(err));
      }
      done();
    });
  });
}

module.exports = jekyll;
<|endoftext|>


<commit_before>/* jshint esversion:6, node: true */

'use strict';

const express = require('express');
const bodyParser = require('body-parser');
const request = require('request');
const app = express();

app.set('port', (process.env.PORT || 5000));

// parse application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({extended: false}));

// parse application/json
app.use(bodyParser.json());

// index
app.get('/', function (req, res) {
	res.send('hello world i am a secret bot');
});

// for facebook verification
app.get('/webhook/', function (req, res) {
	if (req.query['hub.verify_token'] === 'my_voice_is_my_password_verify_me') {
		res.send(req.query['hub.challenge']);
	} else {
		res.send('Error, wrong token');
	}
});

// to post data
app.post('/webhook/', function (req, res) {
	var results;

	let messaging_events = req.body.entry[0].messaging;
	for (let i = 0; i < messaging_events.length; i++) {
		let event = req.body.entry[0].messaging[i];
		let sender = event.sender.id;

		if (event.message && event.message.text) {
			let text = event.message.text;
			if (text === 'Generic') {
				sendGenericMessage(sender);
				continue;
			}

			else if (text == 'cookies'){
				var headers = {
				    'Authorization': 'bearer fd887ce5-7b15-4c1e-93a0-dc91ce883ec6'
				};

				var options = {
				    url: 'https://www.loblaws.ca/ecommerce/v2/loblaw/products/search?q=apple&page=0&pageSize=10&sort=recommended&filters=category:LSL001006000000',
				    headers: headers
				};
				function callback(error, response, body) {
				    if (!error && response.statusCode == 200) {
				        //console.log(body);
				        var resp = JSON.parse(body);
				        results = resp["searchResults"];
				        console.log(results[0]);
				        console.log(results.products[0].productName);
				    }
				}

				request(options, callback);

			}
			sendProductMessage(sender, results);
			sendTextMessage(sender, "echo: " + text.substring(0, 200));
		}
		if (event.postback) {
			let text = JSON.stringify(event.postback);
			sendTextMessage(sender, "Postback received: "+text.substring(0, 200), token);
			continue;
		}
	}
	res.sendStatus(200);
});

// recommended to inject access tokens as environmental variables, e.g.
const token = process.env.PAGE_ACCESS_TOKEN;
//const token = "<PAGE_ACCESS_TOKEN>"

function sendTextMessage(sender, text) {
	let messageData = { text:text };

	request({
		url: 'https://graph.facebook.com/v2.6/me/messages',
		qs: {access_token:token},
		method: 'POST',
		json: {
			recipient: {id:sender},
			message: messageData,
		}
	}, function(error, response, body) {
		if (error) {
			console.log('Error sending messages: ', error);
		} else if (response.body.error) {
			console.log('Error: ', response.body.error);
		}
	});
}

function sendGenericMessage(sender) {
	let messageData = {
		"attachment": {
			"type": "template",
			"payload": {
				"template_type": "generic",
				"elements": [{
					"title": "First card",
					"subtitle": "Element #1 of an hscroll",
					"image_url": "http://messengerdemo.parseapp.com/img/rift.png",
					"buttons": [{
						"type": "web_url",
						"url": "https://www.messenger.com",
						"title": "web url"
					}, {
						"type": "postback",
						"title": "Postback",
						"payload": "Payload for first element in a generic bubble",
					}],
				}, {
					"title": "Second card",
					"subtitle": "Element #2 of an hscroll",
					"image_url": "http://messengerdemo.parseapp.com/img/gearvr.png",
					"buttons": [{
						"type": "postback",
						"title": "Postback",
						"payload": "Payload for second element in a generic bubble",
					}],
				}]
			}
		}
	};

	function sendProductMessage(sender, results) {
	let messageData = {
		"attachment": {
			"type": "template",
			"payload": {
				"template_type": "generic",
				"elements": [{
					"title": results.products[0].productName,
					"subtitle": results.products[0].description,
					"image_url": results.products[0].primaryImage,
					"buttons": [{
						"type": "web_url",
						"url": "https://www.messenger.com",
						"title": "web url"
					}, {
						"type": "postback",
						"title": "Postback",
						"payload": "Payload for first element in a generic bubble",
					}],
				}, {
					"title": "Second card",
					"subtitle": "Element #2 of an hscroll",
					"image_url": "http://messengerdemo.parseapp.com/img/gearvr.png",
					"buttons": [{
						"type": "postback",
						"title": "Postback",
						"payload": "Payload for second element in a generic bubble",
					}],
				}]
			}
		}
	};

	request({
		url: 'https://graph.facebook.com/v2.6/me/messages',
		qs: {access_token:token},
		method: 'POST',
		json: {
			recipient: {id:sender},
			message: messageData,
		}
	}, function(error, response, body) {
		if (error) {
			console.log('Error sending messages: ', error);
		} else if (response.body.error) {
			console.log('Error: ', response.body.error);
		}
	});
}

// spin spin sugar
app.listen(app.get('port'), function() {
	console.log('running on port', app.get('port'));
});
<commit_msg>heroku<commit_after>/* jshint esversion:6, node: true */

'use strict';

const express = require('express');
const bodyParser = require('body-parser');
const request = require('request');
const app = express();

app.set('port', (process.env.PORT || 5000));

// parse application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({extended: false}));

// parse application/json
app.use(bodyParser.json());

// index
app.get('/', function (req, res) {
	res.send('hello world i am a secret bot');
});

// for facebook verification
app.get('/webhook/', function (req, res) {
	if (req.query['hub.verify_token'] === 'my_voice_is_my_password_verify_me') {
		res.send(req.query['hub.challenge']);
	} else {
		res.send('Error, wrong token');
	}
});

// to post data
app.post('/webhook/', function (req, res) {
	var results;

	let messaging_events = req.body.entry[0].messaging;
	for (let i = 0; i < messaging_events.length; i++) {
		let event = req.body.entry[0].messaging[i];
		let sender = event.sender.id;

		if (event.message && event.message.text) {
			let text = event.message.text;
			if (text === 'Generic') {
				sendGenericMessage(sender);
				continue;
			}

			else if (text == 'cookies'){
				var headers = {
				    'Authorization': 'bearer fd887ce5-7b15-4c1e-93a0-dc91ce883ec6'
				};

				var options = {
				    url: 'https://www.loblaws.ca/ecommerce/v2/loblaw/products/search?q=apple&page=0&pageSize=10&sort=recommended&filters=category:LSL001006000000',
				    headers: headers
				};
				function callback(error, response, body) {
				    if (!error && response.statusCode == 200) {
				        //console.log(body);
				        var resp = JSON.parse(body);
				        results = resp["searchResults"];
				        console.log(results[0]);
				        console.log(results.products[0].productName);
				    }
				}

				request(options, callback);

			}
			sendTextMessage(sender, "echo: " + text.substring(0, 200));
		}
		if (event.postback) {
			let text = JSON.stringify(event.postback);
			sendTextMessage(sender, "Postback received: "+text.substring(0, 200), token);
			continue;
		}
	}
	res.sendStatus(200);
});

// recommended to inject access tokens as environmental variables, e.g.
const token = process.env.PAGE_ACCESS_TOKEN;
//const token = "<PAGE_ACCESS_TOKEN>"

function sendTextMessage(sender, text) {
	let messageData = { text:text };

	request({
		url: 'https://graph.facebook.com/v2.6/me/messages',
		qs: {access_token:token},
		method: 'POST',
		json: {
			recipient: {id:sender},
			message: messageData,
		}
	}, function(error, response, body) {
		if (error) {
			console.log('Error sending messages: ', error);
		} else if (response.body.error) {
			console.log('Error: ', response.body.error);
		}
	});
}

function sendGenericMessage(sender) {
	let messageData = {
		"attachment": {
			"type": "template",
			"payload": {
				"template_type": "generic",
				"elements": [{
					"title": "First card",
					"subtitle": "Element #1 of an hscroll",
					"image_url": "http://messengerdemo.parseapp.com/img/rift.png",
					"buttons": [{
						"type": "web_url",
						"url": "https://www.messenger.com",
						"title": "web url"
					}, {
						"type": "postback",
						"title": "Postback",
						"payload": "Payload for first element in a generic bubble",
					}],
				}, {
					"title": "Second card",
					"subtitle": "Element #2 of an hscroll",
					"image_url": "http://messengerdemo.parseapp.com/img/gearvr.png",
					"buttons": [{
						"type": "postback",
						"title": "Postback",
						"payload": "Payload for second element in a generic bubble",
					}],
				}]
			}
		}
	};

	request({
		url: 'https://graph.facebook.com/v2.6/me/messages',
		qs: {access_token:token},
		method: 'POST',
		json: {
			recipient: {id:sender},
			message: messageData,
		}
	}, function(error, response, body) {
		if (error) {
			console.log('Error sending messages: ', error);
		} else if (response.body.error) {
			console.log('Error: ', response.body.error);
		}
	});
}

function sendProductMessage(sender, results) {
	let messageData = {
		"attachment": {
			"type": "template",
			"payload": {
				"template_type": "generic",
				"elements": [{
					"title": results.products[0].productName,
					"subtitle": results.products[0].description,
					"image_url": results.products[0].primaryIm,
					"buttons": [{
						"type": "web_url",
						"url": "https://www.messenger.com",
						"title": "web url"
					}, {
						"type": "postback",
						"title": "Postback",
						"payload": "Payload for first element in a generic bubble",
					}],
				}, {
					"title": "Second card",
					"subtitle": "Element #2 of an hscroll",
					"image_url": "http://messengerdemo.parseapp.com/img/gearvr.png",
					"buttons": [{
						"type": "postback",
						"title": "Postback",
						"payload": "Payload for second element in a generic bubble",
					}],
				}]
			}
		}
	};

	request({
		url: 'https://graph.facebook.com/v2.6/me/messages',
		qs: {access_token:token},
		method: 'POST',
		json: {
			recipient: {id:sender},
			message: messageData,
		}
	}, function(error, response, body) {
		if (error) {
			console.log('Error sending messages: ', error);
		} else if (response.body.error) {
			console.log('Error: ', response.body.error);
		}
	});
}

// spin spin sugar
app.listen(app.get('port'), function() {
	console.log('running on port', app.get('port'));
});
<|endoftext|>


<commit_before>import React from 'react';
import PropTypes from 'prop-types';
import { intlShape } from 'react-intl';
import { omit, map, size } from 'lodash';

import ProfileModal from 'components/profile-modal';
import Navigation from 'components/navigation';
import Header, { HeaderType } from 'primitives/text/header';
import FlexContainer from 'primitives/container/flex-container';
import LinkRow from 'primitives/other/link-row';

import Entities from 'constants/entities';

import './style.css';

export default function OverviewList({
  toHome,
  children,
  currentSector,
  entities,
  isInitialized,
  params,
  intl,
}) {
  if (!entities[Entities.sector.key][currentSector] && isInitialized) {
    toHome();
  }

  return (
    <FlexContainer>
      <Navigation />
      <FlexContainer flex="1">
        <FlexContainer
          direction="column"
          align="center"
          className="OverviewList"
        >
          Entities

            {map(
              omit(entities, Entities.sector.key),
              (entityList, entityType) => (
                <LinkRow
                  key={entityType}
                  to={`/overview/${currentSector}/${entityType}`}
                  title={intl.formatMessage({ id: Entities[entityType].name })}
                  additional={isInitialized ? `${size(entityList)}` : undefined}
                  arrowClassName="OverviewList-Arrow"
                  className={
                    params.entityType === entityType
                      ? 'OverviewList-Item--selected'
                      : ''
                  }
                />
              ),
            )}

        </FlexContainer>
        {children}
      </FlexContainer>
      <ProfileModal />
    </FlexContainer>
  );
}

OverviewList.propTypes = {
  toHome: PropTypes.func.isRequired,
  children: PropTypes.node.isRequired,
  currentSector: PropTypes.string.isRequired,
  entities: PropTypes.shape().isRequired,
  isInitialized: PropTypes.bool.isRequired,
  params: PropTypes.shape({
    entityType: PropTypes.string,
  }).isRequired,
  intl: intlShape.isRequired,
};
<commit_msg>Translate "Entities" on the entity list overview<commit_after>import React from 'react';
import PropTypes from 'prop-types';
import { FormattedMessage, intlShape } from 'react-intl';
import { omit, map, size } from 'lodash';

import ProfileModal from 'components/profile-modal';
import Navigation from 'components/navigation';
import Header, { HeaderType } from 'primitives/text/header';
import FlexContainer from 'primitives/container/flex-container';
import LinkRow from 'primitives/other/link-row';

import Entities from 'constants/entities';

import './style.css';

export default function OverviewList({
  toHome,
  children,
  currentSector,
  entities,
  isInitialized,
  params,
  intl,
}) {
  if (!entities[Entities.sector.key][currentSector] && isInitialized) {
    toHome();
  }

  return (
    <FlexContainer>
      <Navigation />
      <FlexContainer flex="1">
        <FlexContainer
          direction="column"
          align="center"
          className="OverviewList"
        >

            <FormattedMessage id="misc.entities" />

            {map(
              omit(entities, Entities.sector.key),
              (entityList, entityType) => (
                <LinkRow
                  key={entityType}
                  to={`/overview/${currentSector}/${entityType}`}
                  title={intl.formatMessage({ id: Entities[entityType].name })}
                  additional={isInitialized ? `${size(entityList)}` : undefined}
                  arrowClassName="OverviewList-Arrow"
                  className={
                    params.entityType === entityType
                      ? 'OverviewList-Item--selected'
                      : ''
                  }
                />
              ),
            )}

        </FlexContainer>
        {children}
      </FlexContainer>
      <ProfileModal />
    </FlexContainer>
  );
}

OverviewList.propTypes = {
  toHome: PropTypes.func.isRequired,
  children: PropTypes.node.isRequired,
  currentSector: PropTypes.string.isRequired,
  entities: PropTypes.shape().isRequired,
  isInitialized: PropTypes.bool.isRequired,
  params: PropTypes.shape({
    entityType: PropTypes.string,
  }).isRequired,
  intl: intlShape.isRequired,
};
<|endoftext|>