<commit_before>import React, { Component } from 'react'
import {
  Box,
  Button,
  Flex,
  Heading,
  Icon,
  Input,
  Label,
  Text
} from '@hackclub/design-system'
import { toNumber } from 'lodash'

const Secure = Flex.extend`
  position: absolute;
  top: 0;
  right: 0;
  align-items: center;
  justify-content: flex-end;
  text-align: right;
`

const amounts = [5, 10, 25, 50, 100, 200, 250]

const AmountsGrid = Box.extend`
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-gap: ${props => props.theme.space[3]}px;
  input[type='radio'] {
    display: none;
    &:checked + label {
      background-color: ${props => props.theme.colors.blue[7]} !important;
    }
  }
  input[type='number'] {
    text-align: center;
    grid-column: 2 / span 2;
  }
`

const Amount = Button.withComponent('label').extend`
  border-radius: ${props => props.theme.radius};
`

const Option = ({ amount, ...props }) => [
  <input
    type="radio"
    id={`amount[${amount}]`}
    name="amount"
    value={amount}
    key={`input[${amount}]`}
    {...props}
  />,
  <Amount bg="info" htmlFor={`amount[${amount}]`} key={`label[${amount}]`}>
    <Text.span>${amount}</Text.span>
  </Amount>
]

class DonateForm extends Component {
  constructor(props) {
    super(props)

    this.state = {
      loading: true,
      stripeLoading: true,
      amount: 10,
      recurring: true
    }

    // Have to bind our handlers to this because of how JavaScript's scope works.
    this.onStripeUpdate = this.onStripeUpdate.bind(this)
    this.startStripe = this.startStripe.bind(this)
    this.loadStripe = this.loadStripe.bind(this)
    this.handleToken = this.handleToken.bind(this)
    this.handleAmountChange = this.handleAmountChange.bind(this)
    this.handleRecurringChange = this.handleRecurringChange.bind(this)
  }

  componentWillUnmount() {
    if (this.stripeHandler) {
      this.stripeHandler.close()
    }
  }

  render() {
    const { custom, recurring, amount } = this.state
    return (
      <Box align="center" style={{ position: 'relative' }}>
        <Secure p={2} mr={[-3, -4]}>
          <Text f={0} color="red.1" caps bold>
            Secure
          </Text>
          <Icon size={16} name="lock" color="red.1" ml={2} />
        </Secure>
        <Heading.h2
          mx={[-3, -4]}
          mt={[-3, -4]}
          mb={3}
          pt={4}
          pb={3}
          px={3}
          f={5}
          bg="primary"
          color="white"
        >
          Become a Patron
        </Heading.h2>
        <Flex align="center" justify="center" color="black">
          <Text f={3} bold>
            Select an amount
          </Text>
          <Text.span mx={2} children="—" />
          <input
            name="recurring"
            type="checkbox"
            checked={recurring}
            onChange={this.handleRecurringChange}
          />
          <Label htmlFor="recurring" f={2} ml={1}>
            monthly?
          </Label>
        </Flex>
        <AmountsGrid w={1} mt={3} mb={4}>
          {amounts.map(amount => (
            <Option
              amount={amount}
              onChange={e => this.handleAmountChange(amount)}
              key={amount}
            />
          ))}
          <Input
            type="number"
            placeholder="Other"
            onChange={e => this.handleAmountChange(e.target.value)}
            color="black"
            value={amount}
          />
        </AmountsGrid>
        <Button
          onClick={e => this.startStripe(e)}
          children={this.buttonText()}
          w={1}
        />
      </Box>
    )
  }

  loadStripe(onload) {
    if (!window.StripeCheckout) {
      const script = document.createElement('script')
      script.onload = () => {
        onload()
      }
      script.src = 'https://checkout.stripe.com/checkout.js'
      document.head.appendChild(script)
    } else {
      onload()
    }
  }

  startStripe(e) {
    this.loadStripe(() => {
      this.stripeHandler = window.StripeCheckout.configure({
        key: process.env.STRIPE_PUBLISHABLE_KEY || 'XXX',
        image: 'https://hackclub.com/social.png',
        locale: 'auto',
        amount: this.amountInCents(),
        token: this.handleToken
      })

      this.setState({
        stripeLoading: false,
        // loading needs to be explicitly set false so component will render in 'loaded' state.
        loading: false
      })

      this.onStripeUpdate(e)
    })
  }

  onStripeUpdate(e) {
    this.stripeHandler.open({
      name: 'Hack Club',
      description: 'Hack Club contribution.',
      panelLabel: 'Donate',
      allowRememberMe: false
    })

    e.preventDefault()
  }

  handleToken(token) {
    this.setState({ loading: true })

    var data = new FormData()

    data.append('stripe_email', token.email)
    data.append('stripe_token', token.id)
    data.append('recurring', this.state.recurring)
    data.append('amount', this.amountInCents())

    this.setState({ status: 'loading' })

    api
      .post('/v1/donations', { data })
      .then(data => {
        if (data.donation_successful) {
          this.setState({ status: 'done' })
        } else {
          this.setState({ status: 'error' })
        }
      })
      .catch(() => {
        this.setState({ status: 'error' })
      })
  }

  handleAmountChange(a) {
    const amount = toNumber(a || 1)
    this.setState({ amount })
  }

  handleRecurringChange(v) {
    this.setState({ recurring: v.target.checked })
  }

  buttonText() {
    switch (this.state.status) {
      case 'done':
        return 'Sent! Thank you for your contriution'
      case 'loading':
        return 'One moment…'
      case 'error':
        return 'Something went wrong!'
      default:
        const msg = `Donate $${this.state.amount}`
        return this.state.recurring ? `${msg} a month` : msg
    }
  }

  setAmount(amount) {
    return v => {
      this.setState({ amount, custom: false })
    }
  }

  amountInCents() {
    return this.state.amount * 100
  }
}

export default DonateForm
<commit_msg>Don't show amount in custom input field<commit_after>import React, { Component } from 'react'
import {
  Box,
  Button,
  Flex,
  Heading,
  Icon,
  Input,
  Label,
  Text
} from '@hackclub/design-system'
import { toNumber } from 'lodash'

const Secure = Flex.extend`
  position: absolute;
  top: 0;
  right: 0;
  align-items: center;
  justify-content: flex-end;
  text-align: right;
`

const amounts = [5, 10, 25, 50, 100, 200, 250]

const AmountsGrid = Box.extend`
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-gap: ${props => props.theme.space[3]}px;
  input[type='radio'] {
    display: none;
    &:checked + label {
      background-color: ${props => props.theme.colors.blue[7]} !important;
    }
  }
  input[type='number'] {
    text-align: center;
    grid-column: 2 / span 2;
  }
`

const Amount = Button.withComponent('label').extend`
  border-radius: ${props => props.theme.radius};
`

const Option = ({ amount, ...props }) => [
  <input
    type="radio"
    id={`amount[${amount}]`}
    name="amount"
    value={amount}
    key={`input[${amount}]`}
    {...props}
  />,
  <Amount bg="info" htmlFor={`amount[${amount}]`} key={`label[${amount}]`}>
    <Text.span>${amount}</Text.span>
  </Amount>
]

class DonateForm extends Component {
  constructor(props) {
    super(props)

    this.state = {
      loading: true,
      stripeLoading: true,
      amount: 10,
      recurring: true
    }

    // Have to bind our handlers to this because of how JavaScript's scope works.
    this.onStripeUpdate = this.onStripeUpdate.bind(this)
    this.startStripe = this.startStripe.bind(this)
    this.loadStripe = this.loadStripe.bind(this)
    this.handleToken = this.handleToken.bind(this)
    this.handleAmountChange = this.handleAmountChange.bind(this)
    this.handleRecurringChange = this.handleRecurringChange.bind(this)
  }

  componentWillUnmount() {
    if (this.stripeHandler) {
      this.stripeHandler.close()
    }
  }

  render() {
    const { custom, recurring, amount } = this.state
    return (
      <Box align="center" style={{ position: 'relative' }}>
        <Secure p={2} mr={[-3, -4]}>
          <Text f={0} color="red.1" caps bold>
            Secure
          </Text>
          <Icon size={16} name="lock" color="red.1" ml={2} />
        </Secure>
        <Heading.h2
          mx={[-3, -4]}
          mt={[-3, -4]}
          mb={3}
          pt={4}
          pb={3}
          px={3}
          f={5}
          bg="primary"
          color="white"
        >
          Become a Patron
        </Heading.h2>
        <Flex align="center" justify="center" color="black">
          <Text f={3} bold>
            Select an amount
          </Text>
          <Text.span mx={2} children="—" />
          <input
            name="recurring"
            type="checkbox"
            checked={recurring}
            onChange={this.handleRecurringChange}
          />
          <Label htmlFor="recurring" f={2} ml={1}>
            monthly?
          </Label>
        </Flex>
        <AmountsGrid w={1} mt={3} mb={4}>
          {amounts.map(amount => (
            <Option
              amount={amount}
              onChange={e => this.handleAmountChange(amount)}
              key={amount}
            />
          ))}
          <Input
            type="number"
            placeholder="Other"
            onChange={e => this.handleAmountChange(e.target.value)}
            color="black"
          />
        </AmountsGrid>
        <Button
          onClick={e => this.startStripe(e)}
          children={this.buttonText()}
          w={1}
        />
      </Box>
    )
  }

  loadStripe(onload) {
    if (!window.StripeCheckout) {
      const script = document.createElement('script')
      script.onload = () => {
        onload()
      }
      script.src = 'https://checkout.stripe.com/checkout.js'
      document.head.appendChild(script)
    } else {
      onload()
    }
  }

  startStripe(e) {
    this.loadStripe(() => {
      this.stripeHandler = window.StripeCheckout.configure({
        key: process.env.STRIPE_PUBLISHABLE_KEY || 'XXX',
        image: 'https://hackclub.com/social.png',
        locale: 'auto',
        amount: this.amountInCents(),
        token: this.handleToken
      })

      this.setState({
        stripeLoading: false,
        // loading needs to be explicitly set false so component will render in 'loaded' state.
        loading: false
      })

      this.onStripeUpdate(e)
    })
  }

  onStripeUpdate(e) {
    this.stripeHandler.open({
      name: 'Hack Club',
      description: 'Hack Club contribution.',
      panelLabel: 'Donate',
      allowRememberMe: false
    })

    e.preventDefault()
  }

  handleToken(token) {
    this.setState({ loading: true })

    var data = new FormData()

    data.append('stripe_email', token.email)
    data.append('stripe_token', token.id)
    data.append('recurring', this.state.recurring)
    data.append('amount', this.amountInCents())

    this.setState({ status: 'loading' })

    api
      .post('/v1/donations', { data })
      .then(data => {
        if (data.donation_successful) {
          this.setState({ status: 'done' })
        } else {
          this.setState({ status: 'error' })
        }
      })
      .catch(() => {
        this.setState({ status: 'error' })
      })
  }

  handleAmountChange(a) {
    const amount = toNumber(a || 1)
    this.setState({ amount })
  }

  handleRecurringChange(v) {
    this.setState({ recurring: v.target.checked })
  }

  buttonText() {
    switch (this.state.status) {
      case 'done':
        return 'Sent! Thank you for your contriution'
      case 'loading':
        return 'One moment…'
      case 'error':
        return 'Something went wrong!'
      default:
        const msg = `Donate $${this.state.amount}`
        return this.state.recurring ? `${msg} a month` : msg
    }
  }

  setAmount(amount) {
    return v => {
      this.setState({ amount, custom: false })
    }
  }

  amountInCents() {
    return this.state.amount * 100
  }
}

export default DonateForm
<|endoftext|>


<commit_before>import React, { Component, PropTypes } from 'react'
import classNames from 'classnames'
import { FORM_CONTROL_STATUS as STATUS } from '../../constants/gui_types'
import { RequestIcon, SuccessIcon, FailureIcon } from '../forms/FormIcons'

class FormControl extends Component {

  static propTypes = {
    classList: PropTypes.string,
    className: PropTypes.string,
    id: PropTypes.string.isRequired,
    kind: PropTypes.string,
    label: PropTypes.string,
    name: PropTypes.string,
    onBlur: PropTypes.func,
    onChange: PropTypes.func,
    onFocus: PropTypes.func,
    placeholder: PropTypes.string,
    renderStatus: PropTypes.func,
    renderFeedback: PropTypes.func,
    status: PropTypes.string,
    tabIndex: PropTypes.string.isRequired,
    text: PropTypes.string,
  }

  static defaultProps = {
    kind: 'input',
    renderFeedback: null,
    status: STATUS.INDETERMINATE,
    tabIndex: '0',
    text: '',
    type: 'text',
  }

  componentWillMount() {
    const { text } = this.props
    this.state = {
      hasFocus: false,
      hasValue: text && text.length,
      isInitialValue: true,
      text,
    }
    this.initialValue = text
    this.timer = setTimeout(this.checkValue, 250)
  }

  componentWillUnmount() {
    if (this.timer) {
      clearTimeout(this.timer)
    }
  }

  onFocusControl = (e) => {
    this.setState({ hasFocus: true })
    const { onFocus } = this.props
    if (typeof onFocus === 'function') {
      onFocus(e)
    }
  }

  onBlurControl = (e) => {
    this.setState({ hasFocus: false })
    const { onBlur } = this.props
    if (typeof onBlur === 'function') {
      onBlur(e)
    }
  }

  onChangeControl = (e) => {
    this.onChangeValue(e.target.value)
  }

  onChangeValue = (val) => {
    const { id, onChange } = this.props
    this.setState({
      text: val,
      hasValue: val.length,
      isInitialValue: val === this.initialValue,
    })
    if (id && typeof onChange === 'function') {
      onChange({ [id]: val })
    }
  }

  getStatusAsClassName() {
    const { status } = this.props
    switch (status) {
      case STATUS.REQUEST:
        return 'isValidating'
      case STATUS.FAILURE:
        return 'isFailing'
      case STATUS.SUCCESS:
        return 'isSucceeding'
      case STATUS.INDETERMINATE:
      default:
        return 'isIndeterminate'
    }
  }

  getGroupClassNames() {
    const { classList, className } = this.props
    const { hasFocus, hasValue, isInitialValue } = this.state
    const statusClassName = this.getStatusAsClassName()
    return classNames(
      'FormControl',
      classList,
      className,
      statusClassName,
      { hasFocus },
      { hasValue },
      { isInitialValue },
    )
  }

  getLabelClassNames() {
    const { classList } = this.props
    return classNames('FormControlLabel', classList)
  }

  getInputClassNames() {
    const { classList } = this.props
    return classNames('FormControlInput', classList)
  }

  getStatusClassNames() {
    const { classList } = this.props
    return classNames('FormControlStatus', classList)
  }

  getStatusIcon() {
    const { status } = this.props
    switch (status) {
      case STATUS.REQUEST:
        return <RequestIcon />
      case STATUS.FAILURE:
        return <FailureIcon />
      case STATUS.SUCCESS:
        return <SuccessIcon />
      case STATUS.INDETERMINATE:
      default:
        return null
    }
  }

  checkValue = () => {
    const inputControl = this.refs.input
    const { text } = this.state
    if (inputControl.value !== text) {}
    this.timer = setTimeout(this.checkValue, 250)
  }

  clear() {
    this.setState({ text: '' })
  }

  renderLabel() {
    const { id, label } = this.props
    const labelClassNames = this.getLabelClassNames()
    return (

        { label }

    )
  }

  renderTextArea(text, inputClassNames) {
    return (
      <textarea
        { ...this.props }
        className={ inputClassNames }
        onFocus={ this.onFocusControl }
        onBlur={ this.onBlurControl }
        onChange={ this.onChangeControl }
        ref="input"
        value={ text }
      />
    )
  }

  renderInput(text, inputClassNames) {
    return (
      <input
        { ...this.props }
        className={ inputClassNames }
        onFocus={ this.onFocusControl }
        onBlur={ this.onBlurControl }
        onChange={ this.onChangeControl }
        ref="input"
        value={ text }
      />
    )
  }

  render() {
    const { kind, label, renderFeedback, renderStatus } = this.props
    const { text } = this.state
    const groupClassNames = this.getGroupClassNames()
    const inputClassNames = this.getInputClassNames()
    const statusClassNames = this.getStatusClassNames()
    return (

        { label && this.renderLabel() }
        {
          kind === 'textarea' ?
            this.renderTextArea(text, inputClassNames) :
            this.renderInput(text, inputClassNames)
        }

          { this.getStatusIcon() }

        { renderStatus ? renderStatus() : null }
        { renderFeedback ? renderFeedback() : null }

    )
  }
}

export default FormControl
<commit_msg>fire the change event!  Easy as THAT!<commit_after>import React, { Component, PropTypes } from 'react'
import classNames from 'classnames'
import { FORM_CONTROL_STATUS as STATUS } from '../../constants/gui_types'
import { RequestIcon, SuccessIcon, FailureIcon } from '../forms/FormIcons'

class FormControl extends Component {

  static propTypes = {
    classList: PropTypes.string,
    className: PropTypes.string,
    id: PropTypes.string.isRequired,
    kind: PropTypes.string,
    label: PropTypes.string,
    name: PropTypes.string,
    onBlur: PropTypes.func,
    onChange: PropTypes.func,
    onFocus: PropTypes.func,
    placeholder: PropTypes.string,
    renderStatus: PropTypes.func,
    renderFeedback: PropTypes.func,
    status: PropTypes.string,
    tabIndex: PropTypes.string.isRequired,
    text: PropTypes.string,
  }

  static defaultProps = {
    kind: 'input',
    renderFeedback: null,
    status: STATUS.INDETERMINATE,
    tabIndex: '0',
    text: '',
    type: 'text',
  }

  componentWillMount() {
    const { text } = this.props
    this.state = {
      hasFocus: false,
      hasValue: text && text.length,
      isInitialValue: true,
      text,
    }
    this.initialValue = text
    this.timer = setTimeout(this.checkValue, 250)
  }

  componentWillUnmount() {
    if (this.timer) {
      clearTimeout(this.timer)
    }
  }

  onFocusControl = (e) => {
    this.setState({ hasFocus: true })
    const { onFocus } = this.props
    if (typeof onFocus === 'function') {
      onFocus(e)
    }
  }

  onBlurControl = (e) => {
    this.setState({ hasFocus: false })
    const { onBlur } = this.props
    if (typeof onBlur === 'function') {
      onBlur(e)
    }
  }

  onChangeControl = (e) => {
    this.onChangeValue(e.target.value)
  }

  onChangeValue = (val) => {
    const { id, onChange } = this.props
    this.setState({
      text: val,
      hasValue: val.length,
      isInitialValue: val === this.initialValue,
    })
    if (id && typeof onChange === 'function') {
      onChange({ [id]: val })
    }
  }

  getStatusAsClassName() {
    const { status } = this.props
    switch (status) {
      case STATUS.REQUEST:
        return 'isValidating'
      case STATUS.FAILURE:
        return 'isFailing'
      case STATUS.SUCCESS:
        return 'isSucceeding'
      case STATUS.INDETERMINATE:
      default:
        return 'isIndeterminate'
    }
  }

  getGroupClassNames() {
    const { classList, className } = this.props
    const { hasFocus, hasValue, isInitialValue } = this.state
    const statusClassName = this.getStatusAsClassName()
    return classNames(
      'FormControl',
      classList,
      className,
      statusClassName,
      { hasFocus },
      { hasValue },
      { isInitialValue },
    )
  }

  getLabelClassNames() {
    const { classList } = this.props
    return classNames('FormControlLabel', classList)
  }

  getInputClassNames() {
    const { classList } = this.props
    return classNames('FormControlInput', classList)
  }

  getStatusClassNames() {
    const { classList } = this.props
    return classNames('FormControlStatus', classList)
  }

  getStatusIcon() {
    const { status } = this.props
    switch (status) {
      case STATUS.REQUEST:
        return <RequestIcon />
      case STATUS.FAILURE:
        return <FailureIcon />
      case STATUS.SUCCESS:
        return <SuccessIcon />
      case STATUS.INDETERMINATE:
      default:
        return null
    }
  }

  checkValue = () => {
    const inputControl = this.refs.input
    const { text } = this.state
    if (inputControl.value !== text) {
      this.onChangeValue(inputControl.value)
    }
    this.timer = setTimeout(this.checkValue, 250)
  }

  clear() {
    this.setState({ text: '' })
  }

  renderLabel() {
    const { id, label } = this.props
    const labelClassNames = this.getLabelClassNames()
    return (

        { label }

    )
  }

  renderTextArea(text, inputClassNames) {
    return (
      <textarea
        { ...this.props }
        className={ inputClassNames }
        onFocus={ this.onFocusControl }
        onBlur={ this.onBlurControl }
        onChange={ this.onChangeControl }
        ref="input"
        value={ text }
      />
    )
  }

  renderInput(text, inputClassNames) {
    return (
      <input
        { ...this.props }
        className={ inputClassNames }
        onFocus={ this.onFocusControl }
        onBlur={ this.onBlurControl }
        onChange={ this.onChangeControl }
        ref="input"
        value={ text }
      />
    )
  }

  render() {
    const { kind, label, renderFeedback, renderStatus } = this.props
    const { text } = this.state
    const groupClassNames = this.getGroupClassNames()
    const inputClassNames = this.getInputClassNames()
    const statusClassNames = this.getStatusClassNames()
    return (

        { label && this.renderLabel() }
        {
          kind === 'textarea' ?
            this.renderTextArea(text, inputClassNames) :
            this.renderInput(text, inputClassNames)
        }

          { this.getStatusIcon() }

        { renderStatus ? renderStatus() : null }
        { renderFeedback ? renderFeedback() : null }

    )
  }
}

export default FormControl
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import PropTypes from '../PropTypes.js';
import { OwnEmail, GoogleMap } from '../controls.js';

const clubBankNr = 'BE55 0016 5927 6744';
const clubOrgNr = 'BE 0840.545.283';

export default class GeneralInfo extends Component {
  static contextTypes = PropTypes.contextTypes;
  render() {
    return (

{this.context.t('clubs.generalInfo.title')}

{this.context.t('clubs.generalInfo.contact')}

            {this.context.t('clubs.generalInfo.ourAddress')} {this.context.t('footer.location')}

{this.context.t('clubs.generalInfo.ourEmail')} <OwnEmail />

{this.context.t('clubs.generalInfo.openDays')}

            {this.context.t('footer.competitionDays')}

            {this.context.t('footer.trainingDays')}

{this.context.t('clubs.generalInfo.moneyMoney')}

            {this.context.t('clubs.generalInfo.bankNr')} {clubBankNr}

            {this.context.t('footer.adultMembership')}

            {this.context.t('footer.youthMembership')}

{this.context.t('clubs.generalInfo.other')}

            {this.context.t('clubs.generalInfo.orgNr')} {clubOrgNr}

{this.context.t('clubs.generalInfo.googleMap')}

            <GoogleMap />

    );
  }
}
<commit_msg>GeneralInfo: Added Sporta/Vttl club codes<commit_after>import React, { Component } from 'react';
import PropTypes from '../PropTypes.js';
import { OwnEmail, GoogleMap } from '../controls.js';

const clubBankNr = 'BE55 0016 5927 6744';
const clubOrgNr = 'BE 0840.545.283';

export default class GeneralInfo extends Component {
  static contextTypes = PropTypes.contextTypes;
  render() {
    return (

{this.context.t('clubs.generalInfo.title')}

{this.context.t('clubs.generalInfo.contact')}

            {this.context.t('clubs.generalInfo.ourAddress')} {this.context.t('footer.location')}

{this.context.t('clubs.generalInfo.ourEmail')} <OwnEmail />

{this.context.t('clubs.generalInfo.openDays')}

            {this.context.t('footer.competitionDays')}

            {this.context.t('footer.trainingDays')}

{this.context.t('clubs.generalInfo.moneyMoney')}

            {this.context.t('clubs.generalInfo.bankNr')} {clubBankNr}

            {this.context.t('footer.adultMembership')}

            {this.context.t('footer.youthMembership')}

{this.context.t('clubs.generalInfo.other')}

            VTTL OVL134

            Sporta 4055

            {this.context.t('clubs.generalInfo.orgNr')} {clubOrgNr}

{this.context.t('clubs.generalInfo.googleMap')}

            <GoogleMap />

    );
  }
}
<|endoftext|>


<commit_before>import React from 'react'
import TextField from 'material-ui/TextField'
import ShowQuestion from './ShowQuestion'

const isModifier = (key) => {
  const modifiers = [
    'Shift',
    'Control',
    'Meta',
    'Alt'
  ]
  return modifiers.indexOf(key) !== -1
}

const Question = ({currentQuestion, userAnswer, addKey, removeKey, answerQuestion, reset}) => {
  const handleKeypress = (e) => {
    e.preventDefault()
    addKey(e.key)
  }

  const handleKeyRelease = (e) => {
    e.preventDefault()
    removeKey(e.key)
    if (!isModifier(e.key)) {
      answerQuestion(currentQuestion.id, userAnswer)
    }
  }

  const questionString = "What's the keyboard shortcut for:"
  return (

{questionString}

      <TextField
        id='user-answer'
        autoFocus
        floatingLabelText={currentQuestion.q}
        value={userAnswer}
        onKeyDown={handleKeypress}
        onKeyUp={handleKeyRelease}
        onBlur={reset} />

  )
}

export default ShowQuestion(Question)
<commit_msg>adds error message when on mobile device<commit_after>import React from 'react'
import TextField from 'material-ui/TextField'
import ShowQuestion from './ShowQuestion'

const isModifier = (key) => {
  const modifiers = [
    'Shift',
    'Control',
    'Meta',
    'Alt'
  ]
  return modifiers.indexOf(key) !== -1
}

const isMobile = () => {
  if (navigator.userAgent.match(/Android/i) ||
    navigator.userAgent.match(/webOS/i) ||
    navigator.userAgent.match(/iPhone/i) ||
    navigator.userAgent.match(/iPad/i) ||
    navigator.userAgent.match(/iPod/i) ||
    navigator.userAgent.match(/BlackBerry/i) ||
    navigator.userAgent.match(/Windows Phone/i)
  ) {
    return true
  } else {
    return false
  }
}

const Question = ({currentQuestion, userAnswer, addKey, removeKey, answerQuestion, reset}) => {
  const handleKeypress = (e) => {
    e.preventDefault()
    addKey(e.key)
  }

  const handleKeyRelease = (e) => {
    e.preventDefault()
    removeKey(e.key)
    if (!isModifier(e.key)) {
      answerQuestion(currentQuestion.id, userAnswer)
    }
  }

  const questionString = "What's the keyboard shortcut for:"
  const errorText = "Doesn't work on mobile yet!"
  return (

{questionString}

      <TextField
        id='user-answer'
        autoFocus
        floatingLabelText={!isMobile() && currentQuestion.q}
        value={userAnswer}
        onKeyDown={handleKeypress}
        onKeyUp={handleKeyRelease}
        onBlur={reset}
        disabled={isMobile()}
        errorText={isMobile() && errorText} />

  )
}

export default ShowQuestion(Question)
<|endoftext|>


<commit_before>const grayBlue   = "rgb(100,  122,  133)";
const blue       = "rgb(0,    152,  137)";
const green      = "rgb(151,  191,  13 )";
const yellow     = "rgb(225,  221,  0  )";
const pink       = "rgb(229,  98,   146)";
const berry      = "rgb(170,  56,   108)";
const coal       = "rgb(26,   26,   26)";
const anthracite = "rgb(45,   45,   45)";
const darkGray   = "rgb(102,  102,  102)";
const gray       = "rgb(136,  136,  136)";
const lightGray  = "rgb(221,  221,  221)";

const rating_contexts = {
  "diversity": green,      // GREEN
  "renewable": yellow,      // YELLOW
  "fairness": pink,       // PINK
  "humanity": berry,       // RASPBERRY
  "transparency": grayBlue,   // BLUEGRAY
  "solidarity": blue,     // BLUE
  "default": lightGray
}

const bodyFont= `"Museo", sans-serif`;
const headerFont= `"Museo", sans-serif`;

module.exports = {
  grayBlue:   grayBlue,
  blue:       blue,
  green:      green,
  yellow:     yellow,
  pink:       pink,
  berry:      berry,
  coal:       coal,
  anthracite: anthracite,
  lightGray:  lightGray,
  darkGray:   darkGray,

  company:    blue,
  initiative: green,
  event:      pink,
  otherCategory: lightGray,

  rating_contexts: rating_contexts,

  bodyFont: bodyFont,
  headerFont: headerFont
};<commit_msg>styling(Flower): restore original KVM-Flower colors<commit_after>const grayBlue   = "rgb(99,   122,  132)";
const blue       = "rgb(0,    153,  173)";
const green      = "rgb(150,  191,  12 )";
const yellow     = "rgb(255,  221,  0  )";
const pink       = "rgb(229,  96,   145)";
const berry      = "rgb(170,  56,   107)";
const coal       = "rgb(26,   26,   26)";
const anthracite = "rgb(45,   45,   45)";
const darkGray   = "rgb(102,  102,  102)";
const gray       = "rgb(136,  136,  136)";
const lightGray  = "rgb(221,  221,  221)";

const rating_contexts = {
  "diversity": green,      // GREEN
  "renewable": yellow,      // YELLOW
  "fairness": pink,       // PINK
  "humanity": berry,       // RASPBERRY
  "transparency": grayBlue,   // BLUEGRAY
  "solidarity": blue,     // BLUE
  "default": lightGray
}

const bodyFont= `"Museo", sans-serif`;
const headerFont= `"Museo", sans-serif`;

module.exports = {
  grayBlue:   grayBlue,
  blue:       blue,
  green:      green,
  yellow:     yellow,
  pink:       pink,
  berry:      berry,
  coal:       coal,
  anthracite: anthracite,
  lightGray:  lightGray,
  darkGray:   darkGray,

  company:    blue,
  initiative: green,
  event:      pink,
  otherCategory: lightGray,

  rating_contexts: rating_contexts,

  bodyFont: bodyFont,
  headerFont: headerFont
};<|endoftext|>


<commit_before>import { browser } from 'webextension-polyfill-ts'

import { delayed, getTooltipState, getPositionState } from './utils'
import {
    createAndCopyDirectLink,
    createAnnotation,
} from '../direct-linking/content_script/interactions'
import { setupUIContainer, destroyUIContainer } from './components'
import { remoteFunction, makeRemotelyCallable } from '../util/webextensionRPC'
import { injectCSS } from '../search-injection/dom'

const openOptionsRPC = remoteFunction('openOptionsTab')

let mouseupListener = null

export function setupTooltipTrigger(callback) {
    mouseupListener = event => {
        conditionallyTriggerTooltip(callback, event)
    }

    document.body.addEventListener('mouseup', mouseupListener)
}

export function destroyTooltipTrigger() {
    document.body.removeEventListener('mouseup', mouseupListener)
    mouseupListener = null
}

const CLOSE_MESSAGESHOWN_KEY = 'tooltip.close-message-shown'

async function _setCloseMessageShown() {
    await browser.storage.local.set({
        [CLOSE_MESSAGESHOWN_KEY]: true,
    })
}

async function _getCloseMessageShown() {
    const {
        [CLOSE_MESSAGESHOWN_KEY]: closeMessageShown,
    } = await browser.storage.local.get({ [CLOSE_MESSAGESHOWN_KEY]: false })

    return closeMessageShown
}

// Target container for the Tooltip.
let target = null
let showTooltip = null

/**
 * Creates target container for Tooltip.
 * Injects content_script.css.
 * Mounts Tooltip React component.
 * Sets up Container <---> webpage Remote functions.
 */
export const insertTooltip = async ({ toolbarNotifications } = {}) => {
    // If target is set, Tooltip has already been injected.
    if (target) {
        return
    }

    target = document.createElement('div')
    target.setAttribute('id', 'memex-direct-linking-tooltip')
    document.body.appendChild(target)

    const cssFile = browser.extension.getURL('/content_script.css')
    injectCSS(cssFile)

    showTooltip = await setupUIContainer(target, {
        createAndCopyDirectLink,
        createAnnotation,
        openSettings: () => openOptionsRPC('settings'),
        destroyTooltip: async () => {
            removeTooltip()

            const closeMessageShown = await _getCloseMessageShown()
            if (!closeMessageShown) {
                toolbarNotifications.showToolbarNotification(
                    'tooltip-first-close',
                )
                _setCloseMessageShown()
            }
        },
    })

    setupTooltipTrigger(showTooltip)
    conditionallyTriggerTooltip(showTooltip)
}

const removeTooltip = () => {
    if (!target) {
        return
    }
    destroyTooltipTrigger()
    destroyUIContainer(target)
    target.remove()

    target = null
    showTooltip = null
}

/**
 * Sets up RPC functions to insert and remove Tooltip from Popup.
 */
export const setupRPC = ({ toolbarNotifications }) => {
    makeRemotelyCallable({
        showContentTooltip: async () => {
            if (!showTooltip) {
                await insertTooltip({ toolbarNotifications })
            }
            if (userSelectedText()) {
                const position = calculateTooltipPostion()
                showTooltip(position)
            }
        },
        insertTooltip: () => {
            insertTooltip({ toolbarNotifications })
        },
        removeTooltip: () => {
            removeTooltip()
        },
    })
}

/**
 * Checks for certain conditions before triggering the tooltip.
 * i) Whether the selection made by the user is just text.
 * ii) Whether the user has enabled the tooltip in his preferences.
 * iii) Whether the selected target is not inside the tooltip itself.
 *
 * Event is undefined in the scenario of user selecting the text before the
 * page has loaded. So we don't need to check for condition iii) since the
 * tooltip wouldn't have popped up yet.
 */
export const conditionallyTriggerTooltip = delayed(async (callback, event) => {
    const isTooltipEnabled = await getTooltipState()
    if (
        !userSelectedText() ||
        !isTooltipEnabled ||
        (event && isTargetInsideTooltip(event))
    ) {
        return
    }

    /*
    If all the conditions passed, then this returns the position to anchor the
    tooltip. The positioning is based on the user's preferred method. But in the
    case of tooltip popping up before page load, it resorts to text based method
    */
    const positioning = await getPositionState()
    let position
    if (positioning === 'text' || !event) {
        position = calculateTooltipPostion()
    } else if (positioning === 'mouse' && event) {
        position = { x: event.pageX, y: event.pageY }
    }
    callback(position)
}, 300)

export function calculateTooltipPostion() {
    const range = document.getSelection().getRangeAt(0)
    const boundingRect = range.getBoundingClientRect()
    // x = position of element from the left + half of it's width
    const x = boundingRect.left + boundingRect.width / 2
    // y = scroll height from top + pixels from top + height of element - offset
    const y = window.pageYOffset + boundingRect.top + boundingRect.height - 10
    return {
        x,
        y,
    }
}

function isAnchorOrContentEditable(selected) {
    // Returns true if the any of the parent is an anchor element
    // or is content editable.
    let parent = selected.parentElement
    while (parent) {
        if (parent.contentEditable === 'true' || parent.nodeName === 'A') {
            return true
        }
        parent = parent.parentElement
    }
    return false
}

export function userSelectedText() {
    const selection = document.getSelection()
    if (selection.type === 'None') {
        return false
    }

    const selectedString = selection.toString().trim()
    const container = selection.getRangeAt(0).commonAncestorContainer
    const extras = isAnchorOrContentEditable(container)

    const userSelectedText =
        !!selection && !selection.isCollapsed && !!selectedString && !extras
    return userSelectedText
}

function isTargetInsideTooltip(event) {
    const $tooltipContainer = document.querySelector(
        '#memex-direct-linking-tooltip',
    )
    if (!$tooltipContainer) {
        // edge case, where the destroy() is called
        return true
    }
    return $tooltipContainer.contains(event.target)
}
<commit_msg>Bugfix: Pressing 'show highlighter' shows it on that page until 'x' is clicked again<commit_after>import { browser } from 'webextension-polyfill-ts'

import { delayed, getPositionState } from './utils'
import {
    createAndCopyDirectLink,
    createAnnotation,
} from '../direct-linking/content_script/interactions'
import { setupUIContainer, destroyUIContainer } from './components'
import { remoteFunction, makeRemotelyCallable } from '../util/webextensionRPC'
import { injectCSS } from '../search-injection/dom'

const openOptionsRPC = remoteFunction('openOptionsTab')

let mouseupListener = null

export function setupTooltipTrigger(callback) {
    mouseupListener = event => {
        conditionallyTriggerTooltip(callback, event)
    }

    document.body.addEventListener('mouseup', mouseupListener)
}

export function destroyTooltipTrigger() {
    document.body.removeEventListener('mouseup', mouseupListener)
    mouseupListener = null
}

const CLOSE_MESSAGESHOWN_KEY = 'tooltip.close-message-shown'

async function _setCloseMessageShown() {
    await browser.storage.local.set({
        [CLOSE_MESSAGESHOWN_KEY]: true,
    })
}

async function _getCloseMessageShown() {
    const {
        [CLOSE_MESSAGESHOWN_KEY]: closeMessageShown,
    } = await browser.storage.local.get({ [CLOSE_MESSAGESHOWN_KEY]: false })

    return closeMessageShown
}

// Target container for the Tooltip.
let target = null
let showTooltip = null

/**
 * Creates target container for Tooltip.
 * Injects content_script.css.
 * Mounts Tooltip React component.
 * Sets up Container <---> webpage Remote functions.
 */
export const insertTooltip = async ({ toolbarNotifications } = {}) => {
    // If target is set, Tooltip has already been injected.
    if (target) {
        return
    }

    target = document.createElement('div')
    target.setAttribute('id', 'memex-direct-linking-tooltip')
    document.body.appendChild(target)

    const cssFile = browser.extension.getURL('/content_script.css')
    injectCSS(cssFile)

    showTooltip = await setupUIContainer(target, {
        createAndCopyDirectLink,
        createAnnotation,
        openSettings: () => openOptionsRPC('settings'),
        destroyTooltip: async () => {
            removeTooltip()

            const closeMessageShown = await _getCloseMessageShown()
            if (!closeMessageShown) {
                toolbarNotifications.showToolbarNotification(
                    'tooltip-first-close',
                )
                _setCloseMessageShown()
            }
        },
    })

    setupTooltipTrigger(showTooltip)
    conditionallyTriggerTooltip(showTooltip)
}

const removeTooltip = () => {
    if (!target) {
        return
    }
    destroyTooltipTrigger()
    destroyUIContainer(target)
    target.remove()

    target = null
    showTooltip = null
}

/**
 * Sets up RPC functions to insert and remove Tooltip from Popup.
 */
export const setupRPC = ({ toolbarNotifications }) => {
    makeRemotelyCallable({
        showContentTooltip: async () => {
            if (!showTooltip) {
                await insertTooltip({ toolbarNotifications })
            }
            if (userSelectedText()) {
                const position = calculateTooltipPostion()
                showTooltip(position)
            }
        },
        insertTooltip: () => {
            insertTooltip({ toolbarNotifications })
        },
        removeTooltip: () => {
            removeTooltip()
        },
    })
}

/**
 * Checks for certain conditions before triggering the tooltip.
 * i) Whether the selection made by the user is just text.
 * ii) Whether the selected target is not inside the tooltip itself.
 *
 * Event is undefined in the scenario of user selecting the text before the
 * page has loaded. So we don't need to check for condition ii) since the
 * tooltip wouldn't have popped up yet.
 */
export const conditionallyTriggerTooltip = delayed(async (callback, event) => {
    if (!userSelectedText() || (event && isTargetInsideTooltip(event))) {
        return
    }

    /*
    If all the conditions passed, then this returns the position to anchor the
    tooltip. The positioning is based on the user's preferred method. But in the
    case of tooltip popping up before page load, it resorts to text based method
    */
    const positioning = await getPositionState()
    let position
    if (positioning === 'text' || !event) {
        position = calculateTooltipPostion()
    } else if (positioning === 'mouse' && event) {
        position = { x: event.pageX, y: event.pageY }
    }
    callback(position)
}, 300)

export function calculateTooltipPostion() {
    const range = document.getSelection().getRangeAt(0)
    const boundingRect = range.getBoundingClientRect()
    // x = position of element from the left + half of it's width
    const x = boundingRect.left + boundingRect.width / 2
    // y = scroll height from top + pixels from top + height of element - offset
    const y = window.pageYOffset + boundingRect.top + boundingRect.height - 10
    return {
        x,
        y,
    }
}

function isAnchorOrContentEditable(selected) {
    // Returns true if the any of the parent is an anchor element
    // or is content editable.
    let parent = selected.parentElement
    while (parent) {
        if (parent.contentEditable === 'true' || parent.nodeName === 'A') {
            return true
        }
        parent = parent.parentElement
    }
    return false
}

export function userSelectedText() {
    const selection = document.getSelection()
    if (selection.type === 'None') {
        return false
    }

    const selectedString = selection.toString().trim()
    const container = selection.getRangeAt(0).commonAncestorContainer
    const extras = isAnchorOrContentEditable(container)

    const userSelectedText =
        !!selection && !selection.isCollapsed && !!selectedString && !extras
    return userSelectedText
}

function isTargetInsideTooltip(event) {
    const $tooltipContainer = document.querySelector(
        '#memex-direct-linking-tooltip',
    )
    if (!$tooltipContainer) {
        // edge case, where the destroy() is called
        return true
    }
    return $tooltipContainer.contains(event.target)
}
<|endoftext|>


<commit_before>const express = require('express');
const searchService = require('../services/searchservice');
const getPagination = require('../lib/pagination').getPagination;

const router = express.Router();

function get(req, res) {
  const filters = Object.assign({}, req.query);
  delete filters.term;
  delete filters.page;

  if (!req.query.term || req.query.term.length === 0) {
    res.render('search/facet-search', { result: null, pagination: null, params: req.query });
    return;
  }

  searchService.search({
    token: req.session.token,
    term: req.query.term,
    page: req.page,
    filters,
  })
    .then((result) => {
      const pagination = getPagination(req, result);
      res.render('search/facet-search', { result, pagination, params: req.query });
    })
    .catch(error => res.render('error', { error }));
}

router.get('/', get);

module.exports = {
  search: get, router,
};
<commit_msg>Fix pagination bug<commit_after>const express = require('express');
const searchService = require('../services/searchservice');
const getPagination = require('../lib/pagination').getPagination;

const router = express.Router();

function get(req, res) {
  const filters = Object.assign({}, req.query);
  delete filters.term;
  delete filters.page;

  if (!req.query.term || req.query.term.length === 0) {
    res.render('search/facet-search', { result: null, pagination: null, params: req.query });
    return;
  }

  searchService.search({
    token: req.session.token,
    term: req.query.term,
    page: req.query.page,
    filters,
  })
    .then((result) => {
      const pagination = getPagination(req, result);
      res.render('search/facet-search', { result, pagination, params: req.query });
    })
    .catch(error => res.render('error', { error }));
}

router.get('/', get);

module.exports = {
  search: get, router,
};
<|endoftext|>


<commit_before>'use strict';

const config = {

  browserPort: 3000,
  UIPort: 3001,

  scripts: {
    src: './app/js/**/*.js',
    dest: './build/js/'
  },

  images: {
    src: './app/images/**/*.{jpeg,jpg,png,gif}',
    dest: './build/images/'
  },

  styles: {
    src: './app/styles/**/*.scss',
    dest: './build/css/'
  },

  sourceDir: './app/',

  buildDir: './build/',

  testFiles: './__tests__/**/*.{js,jsx}',

  assetExtensions: [
    'js',
    'css',
    'png',
    'jpe?g',
    'gif',
    'svg',
    'eot',
    'otf',
    'ttc',
    'ttf',
    'woff2?'
  ]

};

export default config;<commit_msg>icons appearing, added svg to gulpfile for parsing<commit_after>'use strict';

const config = {

  browserPort: 3000,
  UIPort: 3001,

  scripts: {
    src: './app/js/**/*.js',
    dest: './build/js/'
  },

  images: {
    src: './app/images/**/*.{svg,jpeg,jpg,png,gif}',
    dest: './build/images/'
  },

  styles: {
    src: './app/styles/**/*.scss',
    dest: './build/css/'
  },

  sourceDir: './app/',

  buildDir: './build/',

  testFiles: './__tests__/**/*.{js,jsx}',

  assetExtensions: [
    'js',
    'css',
    'png',
    'jpe?g',
    'gif',
    'svg',
    'eot',
    'otf',
    'ttc',
    'ttf',
    'woff2?'
  ]

};

export default config;<|endoftext|>


<commit_before>var _ = require('lodash');
var cwd = process.cwd();
var src = './';
var build = src + 'build/';
var dist = src + 'dist/';
var templates = src + 'templates/';
var development = build + 'development/';
var production = build + 'production/';
var pkg = require('../package.json');

//
// https://github.com/kogakure/gulp-tutorial/blob/master/gulp/config.js
//

var bundleDependencies = {
    'vendor': [
        //'i18next',
        'lodash',
        'react',
        'rx'
    ]
};

module.exports = {
    clean: {
        styles: [
            'web/components/**/*.css',
            'web/styles/**/*.css'
        ],
        scripts: [
        ],
        templates: [
        ],
        vendor: [
            'web/vendor/**'
        ]
    },
    autoprefixer: {
        options: {
            browsers: [
                'last 2 versions',
                'safari 5',
                'ie 8',
                'ie 9',
                'opera 12.1',
                'ios 6',
                'android 4'
            ],
            cascade: true
        }
    },
    uglify: {
        options: {
        }
    },
    browserify: {
        'vendor': {
            dest: dist + 'assets/',
            options: {
                debug: true // Sourcemapping
            },
            require: bundleDependencies['vendor']
        },
        'app': {
            src: [
                './web/main.js'
            ],
            dest: dist + 'assets/',
            options: {
                paths: [],
                extensions: ['.jsx'], // default: .js and .json
                debug: true, // Sourcemapping

                // watchify requires these options
                cache: {},
                packageCache: {},
                fullPaths: true
            },
            external: _.union(
                bundleDependencies['vendor']
            ),
            transform: {
                'babelify': {
                    presets: ['es2015', 'stage-0', 'react']
                },
                'browserify-css': {
                    'autoInject': true,
                    'autoInjectOptions': {
                        'verbose': true
                    },
                    'rootDir': 'web/',
                    'minify': true
                },
                'browserify-shim': require('../config/shim')
            }
        }
    },
    csslint: {
        src: [
            'web/components/**/*.css',
            'web/styles/**/*.css'
            // exclusion
        ],
        options: require('../config/csslint')
    },
    handlebars: {
        common: {
            src: 'web/components/common/**/*.hbs',
            dest: 'web/components/common/'
        },
        portal: {
            src: 'web/components/portal/**/*.hbs',
            dest: 'web/components/portal/'
        }
    },
    jscs: {
        src: [
            'gulp/**/*.js',
            '*.js',
            '*.jsx',
            '{app,web,test}/**/*.js',

            // exclusion
            '!web/**/handlebars-templates.js', // exclude handlebars-templates.js:w!
            '!web/{vendor,test}/**',
            '!**/node_modules/**'
        ],
        options: require('../config/jscs')
    },
    eslint: {
        src: [
            'gulp/**/*.js',
            '*.js',
            '*.jsx',
            '{app,web}/**/*.js',
            '{app,web}/**/*.jsx',

            // exclusion
            '!web/**/handlebars-templates.js', // exclude handlebars-templates.js:w!
            '!web/{vendor,test}/**',
            '!**/node_modules/**'
        ],
        options: require('../config/eslint')
    },
    jshint: {
        src: [
            '*.json',
            '{app,web,test}/**/*.json',

            // exclusion
            '!web/{vendor,test}/**',
            '!**/node_modules/**'
        ],
        options: require('../config/jshint')
    },
    mainBowerFiles: {
        base: 'bower_components/',
        dest: 'web/vendor/',
        options: {
            checkExistence: true,
            debugging: true,
            paths: {
                bowerDirectory: 'bower_components',
                bowerrc: '.bowerrc',
                bowerJson: 'bower.json'
            }
        }
    },
    stylus: {
        src: ['web/**/*.styl'],
        dest: 'web/',
        options: {
            compress: true
        }
    },
    i18next: {
        src: [
            'web/**/*.html',
            'web/**/*.hbs',
            'web/**/*.js',
            'web/**/*.jsx',
            // Use ! to filter out files or directories
            '!web/{vendor,test,i18n}/**'
        ],
        dest: './',
        options: {
            debug: true,
            sort: true,
            lngs: ['en'],
            defaultValue: '__L10N__', // to indicate that a default value has not been defined for the key
            resGetPath: 'web/i18n/{{lng}}/{{ns}}.json',
            resSetPath: 'web/i18n/{{lng}}/{{ns}}.json', // or 'web/i18n/${lng}/${ns}.saveAll.json'
            nsseparator: ':', // namespace separator
            keyseparator: '.', // key separator
            interpolationPrefix: '{{',
            interpolationSuffix: '}}',
            ns: {
                namespaces: [
                    'locale', // locale: language, timezone, ...
                    'resource' // default
                ],
                defaultNs: 'resource'
            }
        }
    }
};
<commit_msg>Remove redundant settings<commit_after>var _ = require('lodash');
var cwd = process.cwd();
var src = './';
var build = src + 'build/';
var dist = src + 'dist/';
var templates = src + 'templates/';
var development = build + 'development/';
var production = build + 'production/';
var pkg = require('../package.json');

//
// https://github.com/kogakure/gulp-tutorial/blob/master/gulp/config.js
//

var bundleDependencies = {
    'vendor': [
        //'i18next',
        'lodash',
        'react',
        'rx'
    ]
};

module.exports = {
    clean: {
        styles: [
            'web/components/**/*.css',
            'web/styles/**/*.css'
        ],
        scripts: [
        ],
        templates: [
        ],
        vendor: [
            'web/vendor/**'
        ]
    },
    autoprefixer: {
        options: {
            browsers: [
                'last 2 versions',
                'safari 5',
                'ie 8',
                'ie 9',
                'opera 12.1',
                'ios 6',
                'android 4'
            ],
            cascade: true
        }
    },
    uglify: {
        options: {
        }
    },
    browserify: {
        'vendor': {
            dest: dist + 'assets/',
            options: {
                debug: true // Sourcemapping
            },
            require: bundleDependencies['vendor']
        },
        'app': {
            src: [
                './web/main.js'
            ],
            dest: dist + 'assets/',
            options: {
                paths: [],
                extensions: ['.jsx'], // default: .js and .json
                debug: true, // Sourcemapping

                // watchify requires these options
                cache: {},
                packageCache: {},
                fullPaths: true
            },
            external: _.union(
                bundleDependencies['vendor']
            ),
            transform: {
                'babelify': {
                    presets: ['es2015', 'stage-0', 'react']
                },
                'browserify-css': {
                    'autoInject': true,
                    'autoInjectOptions': {
                        'verbose': true
                    },
                    'rootDir': 'web/',
                    'minify': true
                }
            }
        }
    },
    csslint: {
        src: [
            'web/components/**/*.css',
            'web/styles/**/*.css'
            // exclusion
        ],
        options: require('../config/csslint')
    },
    handlebars: {
        common: {
            src: 'web/components/common/**/*.hbs',
            dest: 'web/components/common/'
        },
        portal: {
            src: 'web/components/portal/**/*.hbs',
            dest: 'web/components/portal/'
        }
    },
    jscs: {
        src: [
            'gulp/**/*.js',
            '*.js',
            '*.jsx',
            '{app,web,test}/**/*.js',

            // exclusion
            '!web/**/handlebars-templates.js', // exclude handlebars-templates.js:w!
            '!web/{vendor,test}/**',
            '!**/node_modules/**'
        ],
        options: require('../config/jscs')
    },
    eslint: {
        src: [
            'gulp/**/*.js',
            '*.js',
            '*.jsx',
            '{app,web}/**/*.js',
            '{app,web}/**/*.jsx',

            // exclusion
            '!web/**/handlebars-templates.js', // exclude handlebars-templates.js:w!
            '!web/{vendor,test}/**',
            '!**/node_modules/**'
        ],
        options: require('../config/eslint')
    },
    jshint: {
        src: [
            '*.json',
            '{app,web,test}/**/*.json',

            // exclusion
            '!web/{vendor,test}/**',
            '!**/node_modules/**'
        ],
        options: require('../config/jshint')
    },
    mainBowerFiles: {
        base: 'bower_components/',
        dest: 'web/vendor/',
        options: {
            checkExistence: true,
            debugging: true,
            paths: {
                bowerDirectory: 'bower_components',
                bowerrc: '.bowerrc',
                bowerJson: 'bower.json'
            }
        }
    },
    stylus: {
        src: ['web/**/*.styl'],
        dest: 'web/',
        options: {
            compress: true
        }
    },
    i18next: {
        src: [
            'web/**/*.html',
            'web/**/*.hbs',
            'web/**/*.js',
            'web/**/*.jsx',
            // Use ! to filter out files or directories
            '!web/{vendor,test,i18n}/**'
        ],
        dest: './',
        options: {
            debug: true,
            sort: true,
            lngs: ['en'],
            defaultValue: '__L10N__', // to indicate that a default value has not been defined for the key
            resGetPath: 'web/i18n/{{lng}}/{{ns}}.json',
            resSetPath: 'web/i18n/{{lng}}/{{ns}}.json', // or 'web/i18n/${lng}/${ns}.saveAll.json'
            nsseparator: ':', // namespace separator
            keyseparator: '.', // key separator
            interpolationPrefix: '{{',
            interpolationSuffix: '}}',
            ns: {
                namespaces: [
                    'locale', // locale: language, timezone, ...
                    'resource' // default
                ],
                defaultNs: 'resource'
            }
        }
    }
};
<|endoftext|>


<commit_before>import _ from 'lodash';
import path from 'path';
import fse from 'fs-extra';
import pkg from '../package.json';
import gutil from 'gulp-util';

const build = './build/';
const dist = './dist/';
const bundleDependencies = {
    'vendor': [
        'async',
        'classnames',
        'fbjs/lib/joinClasses',
        'fixed-data-table',
        'i18next',
        'jsuri',
        'lodash',
        'moment',
        'pubsub-js',
        'react',
        'react-dom',
        'react-addons-update',
        'react-bootstrap',
        'react-datagrid',
        'react-dom',
        'react-infinite',
        'react-router',
        'react-select',
        'redux',
        'sha1',
        'socketio-file-upload',
        'three',

        // Bower Components
        'Sortable',
        'stacktrace'
    ]
};

export default {
    clean: {
        styles: [
            'web/components/**/*.css',
            'web/styles/**/*.css'
        ],
        scripts: [
        ],
        templates: [
        ],
        vendor: [
            'web/vendor/**'
        ]
    },
    autoprefixer: {
        options: {
            browsers: [
                'last 2 versions',
                'safari 5',
                'ie 8',
                'ie 9',
                'opera 12.1',
                'ios 6',
                'android 4'
            ],
            cascade: true
        }
    },
    uglify: {
        options: {
            compress: true,
            mangle: false
        }
    },
    browserify: {
        'vendor': {
            dest: dist + 'assets/',
            options: {
                debug: true // Sourcemapping
            },
            require: bundleDependencies['vendor']
        },
        'app': {
            src: [
                './web/index.jsx'
            ],
            dest: dist + 'assets/',
            options: {
                paths: [],
                extensions: ['.jsx'], // default: .js and .json
                debug: true, // Sourcemapping

                // watchify requires these options
                cache: {},
                packageCache: {},
                fullPaths: true
            },
            external: _.union(
                bundleDependencies['vendor']
            ),
            transform: {
                'babelify': {
                    presets: ['es2015', 'stage-0', 'react']
                },
                'browserify-css': {
                    autoInject: true,
                    autoInjectOptions: {
                        'verbose': true
                    },
                    rootDir: 'web/',
                    minify: true,
                    // Example:
                    //   source={webroot}/../node_modules/bootstrap/**/*
                    //   target={webroot}/vendor/bootstrap/**/*
                    processRelativeUrl: (relativeUrl) => {
                        const stripQueryStringAndHashFromPath = (url) => {
                            return url.split('?')[0].split('#')[0];
                        };
                        let rootDir = path.resolve(process.cwd(), 'web');
                        let relativePath = stripQueryStringAndHashFromPath(relativeUrl);
                        let queryStringAndHash = relativeUrl.substring(relativePath.length);

                        let prefix = '../node_modules/';
                        if (_.startsWith(relativePath, prefix)) {
                            let vendorPath = 'vendor/' + relativePath.substring(prefix.length);
                            let source = path.join(rootDir, relativePath);
                            let target = path.join(rootDir, vendorPath);

                            gutil.log('Copying file from ' + JSON.stringify(source) + ' to ' + JSON.stringify(target));
                            fse.copySync(source, target);

                            // Returns a new path string with original query string and hash fragments
                            return vendorPath + queryStringAndHash;
                        }

                        return relativeUrl;

                    }
                }
            }
        }
    },
    csslint: {
        src: [
            'web/components/**/*.css',
            'web/styles/**/*.css'
            // exclusion
        ],
        options: require('../config/csslint')
    },
    handlebars: {
        common: {
            src: 'web/components/common/**/*.hbs',
            dest: 'web/components/common/'
        },
        portal: {
            src: 'web/components/portal/**/*.hbs',
            dest: 'web/components/portal/'
        }
    },
    jscs: {
        src: [
            'gulp/**/*.js',
            '*.js',
            '*.jsx',
            '{app,web,test}/**/*.js',

            // exclusion
            '!web/**/handlebars-templates.js', // exclude handlebars-templates.js:w!
            '!web/{vendor,test}/**',
            '!**/node_modules/**'
        ]
    },
    eslint: {
        src: [
            'gulp/**/*.js',
            '*.js',
            '*.jsx',
            '{app,web}/**/*.js',
            '{app,web}/**/*.jsx',

            // exclusion
            '!web/**/handlebars-templates.js', // exclude handlebars-templates.js:w!
            '!web/{vendor,test}/**',
            '!**/node_modules/**'
        ],
        options: require('../config/eslint')
    },
    jshint: {
        src: [
            '*.json',
            '{app,web,test}/**/*.json',

            // exclusion
            '!web/{vendor,test}/**',
            '!**/node_modules/**'
        ],
        options: require('../config/jshint')
    },
    mainBowerFiles: {
        base: 'bower_components/',
        dest: 'web/vendor/',
        options: {
            checkExistence: true,
            debugging: true,
            paths: {
                bowerDirectory: 'bower_components',
                bowerrc: '.bowerrc',
                bowerJson: 'bower.json'
            }
        }
    },
    stylus: {
        src: ['web/**/*.styl'],
        dest: 'web/',
        options: {
            compress: true
        }
    },
    i18next: {
        src: [
            'web/**/*.html',
            'web/**/*.hbs',
            'web/**/*.js',
            'web/**/*.jsx',
            // Use ! to filter out files or directories
            '!web/{vendor,test,i18n}/**'
        ],
        dest: './',
        options: {
            debug: true,
            sort: true,
            lngs: ['en'],
            defaultValue: '__L10N__', // to indicate that a default value has not been defined for the key
            resGetPath: 'web/i18n/{{lng}}/{{ns}}.json',
            resSetPath: 'web/i18n/{{lng}}/{{ns}}.json', // or 'web/i18n/${lng}/${ns}.saveAll.json'
            nsseparator: ':', // namespace separator
            keyseparator: '.', // key separator
            interpolationPrefix: '{{',
            interpolationSuffix: '}}',
            ns: {
                namespaces: [
                    'locale', // locale: language, timezone, ...
                    'resource' // default
                ],
                defaultNs: 'resource'
            }
        }
    }
};
<commit_msg>Remove unused dependencies<commit_after>import _ from 'lodash';
import path from 'path';
import fse from 'fs-extra';
import pkg from '../package.json';
import gutil from 'gulp-util';

const build = './build/';
const dist = './dist/';
const bundleDependencies = {
    'vendor': [
        'async',
        'classnames',
        'fbjs/lib/joinClasses',
        'fixed-data-table',
        'i18next',
        'jsuri',
        'lodash',
        'moment',
        'pubsub-js',
        'react',
        'react-dom',
        'react-addons-update',
        'react-bootstrap',
        'react-datagrid',
        'react-dom',
        'react-infinite',
        'react-router',
        'react-select',
        'sha1',
        'socketio-file-upload',
        'three',

        // Bower Components
        'Sortable',
        'stacktrace'
    ]
};

export default {
    clean: {
        styles: [
            'web/components/**/*.css',
            'web/styles/**/*.css'
        ],
        scripts: [
        ],
        templates: [
        ],
        vendor: [
            'web/vendor/**'
        ]
    },
    autoprefixer: {
        options: {
            browsers: [
                'last 2 versions',
                'safari 5',
                'ie 8',
                'ie 9',
                'opera 12.1',
                'ios 6',
                'android 4'
            ],
            cascade: true
        }
    },
    uglify: {
        options: {
            compress: true,
            mangle: false
        }
    },
    browserify: {
        'vendor': {
            dest: dist + 'assets/',
            options: {
                debug: true // Sourcemapping
            },
            require: bundleDependencies['vendor']
        },
        'app': {
            src: [
                './web/index.jsx'
            ],
            dest: dist + 'assets/',
            options: {
                paths: [],
                extensions: ['.jsx'], // default: .js and .json
                debug: true, // Sourcemapping

                // watchify requires these options
                cache: {},
                packageCache: {},
                fullPaths: true
            },
            external: _.union(
                bundleDependencies['vendor']
            ),
            transform: {
                'babelify': {
                    presets: ['es2015', 'stage-0', 'react']
                },
                'browserify-css': {
                    autoInject: true,
                    autoInjectOptions: {
                        'verbose': true
                    },
                    rootDir: 'web/',
                    minify: true,
                    // Example:
                    //   source={webroot}/../node_modules/bootstrap/**/*
                    //   target={webroot}/vendor/bootstrap/**/*
                    processRelativeUrl: (relativeUrl) => {
                        const stripQueryStringAndHashFromPath = (url) => {
                            return url.split('?')[0].split('#')[0];
                        };
                        let rootDir = path.resolve(process.cwd(), 'web');
                        let relativePath = stripQueryStringAndHashFromPath(relativeUrl);
                        let queryStringAndHash = relativeUrl.substring(relativePath.length);

                        let prefix = '../node_modules/';
                        if (_.startsWith(relativePath, prefix)) {
                            let vendorPath = 'vendor/' + relativePath.substring(prefix.length);
                            let source = path.join(rootDir, relativePath);
                            let target = path.join(rootDir, vendorPath);

                            gutil.log('Copying file from ' + JSON.stringify(source) + ' to ' + JSON.stringify(target));
                            fse.copySync(source, target);

                            // Returns a new path string with original query string and hash fragments
                            return vendorPath + queryStringAndHash;
                        }

                        return relativeUrl;

                    }
                }
            }
        }
    },
    csslint: {
        src: [
            'web/components/**/*.css',
            'web/styles/**/*.css'
            // exclusion
        ],
        options: require('../config/csslint')
    },
    handlebars: {
        common: {
            src: 'web/components/common/**/*.hbs',
            dest: 'web/components/common/'
        },
        portal: {
            src: 'web/components/portal/**/*.hbs',
            dest: 'web/components/portal/'
        }
    },
    jscs: {
        src: [
            'gulp/**/*.js',
            '*.js',
            '*.jsx',
            '{app,web,test}/**/*.js',

            // exclusion
            '!web/**/handlebars-templates.js', // exclude handlebars-templates.js:w!
            '!web/{vendor,test}/**',
            '!**/node_modules/**'
        ]
    },
    eslint: {
        src: [
            'gulp/**/*.js',
            '*.js',
            '*.jsx',
            '{app,web}/**/*.js',
            '{app,web}/**/*.jsx',

            // exclusion
            '!web/**/handlebars-templates.js', // exclude handlebars-templates.js:w!
            '!web/{vendor,test}/**',
            '!**/node_modules/**'
        ],
        options: require('../config/eslint')
    },
    jshint: {
        src: [
            '*.json',
            '{app,web,test}/**/*.json',

            // exclusion
            '!web/{vendor,test}/**',
            '!**/node_modules/**'
        ],
        options: require('../config/jshint')
    },
    mainBowerFiles: {
        base: 'bower_components/',
        dest: 'web/vendor/',
        options: {
            checkExistence: true,
            debugging: true,
            paths: {
                bowerDirectory: 'bower_components',
                bowerrc: '.bowerrc',
                bowerJson: 'bower.json'
            }
        }
    },
    stylus: {
        src: ['web/**/*.styl'],
        dest: 'web/',
        options: {
            compress: true
        }
    },
    i18next: {
        src: [
            'web/**/*.html',
            'web/**/*.hbs',
            'web/**/*.js',
            'web/**/*.jsx',
            // Use ! to filter out files or directories
            '!web/{vendor,test,i18n}/**'
        ],
        dest: './',
        options: {
            debug: true,
            sort: true,
            lngs: ['en'],
            defaultValue: '__L10N__', // to indicate that a default value has not been defined for the key
            resGetPath: 'web/i18n/{{lng}}/{{ns}}.json',
            resSetPath: 'web/i18n/{{lng}}/{{ns}}.json', // or 'web/i18n/${lng}/${ns}.saveAll.json'
            nsseparator: ':', // namespace separator
            keyseparator: '.', // key separator
            interpolationPrefix: '{{',
            interpolationSuffix: '}}',
            ns: {
                namespaces: [
                    'locale', // locale: language, timezone, ...
                    'resource' // default
                ],
                defaultNs: 'resource'
            }
        }
    }
};
<|endoftext|>


<commit_before>module.exports = {
  cleancss: {
    opts: {
      compatibility: 'ie11,-properties.merging',
      level: {
        1: {
          specialComments: false,
          removeQuotes: true,
          removeWhitespace: true,
          tidyAtRules: true,
          cleanupCharsets: true,
          selectorsSortingMethod: 'natural'
        },
        2: {
          mergeSemantically: false,
          overrideProperties: true,
          removeEmpty: true,
          removeDuplicateRules: true,
          reduceNonAdjacentRules: true,
          removeDuplicateFontRules: true,
          removeDuplicateMediaBlocks: true,
          removeUnusedAtRules: true,
          restructureRules: true
        }
      }
    }
  },
  rename: {
    min: {
      suffix: '.min'
    }
  },
  browsersync: {
    // Important! If src is wrong, styles will not inject to the browser
    src: ['css/**/*'],
    opts: {
      logLevel: 'debug',
      injectChanges: true,
      proxy: 'https://airdev.test',
      browser: 'Google Chrome',
      open: false,
      notify: true,
      // Generate with: mkdir -p /var/www/certs && cd /var/www/certs && mkcert localhost 192.168.x.xxx ::1
      https: {
        key: "/var/www/certs/localhost+2-key.pem",
        cert: "/var/www/certs/localhost+2.pem",
      }
    },
    watch: [
      '**/*.php',
      'js/src/**/*.js'
    ]
  },
  styles: {
    gutenberg: 'sass/base/gutenberg.scss',
    src: 'sass/**/*.{sass,scss}',
    watch: 'sass/**/*.{sass,scss}',
    development: 'css/dev',
    production: 'css/prod',
    stylelint: {
      opts: {
        fix: false,
        ignoreFiles: ['!*.scss'],
        reporters: [{
          formatter: 'string',
          console: true,
          failAfterError: false,
          debug: false
        }]
      }
    },
    opts: {
      development: {
        bundleExec: true,
        outputStyle: 'expanded',
        debugInfo: true,
        errLogToConsole: true,
        includePaths: ['node_modules/']
      },
      production: {
        bundleExec: true,
        outputStyle: 'compressed',
        debugInfo: true,
        errLogToConsole: true,
        includePaths: ['node_modules/']
      }
    }
  },
  js: {
    src: 'js/src/*.js',
    watch: 'js/src/**/*',
    production: 'js/prod/',
    development: 'js/dev/',
  },
  php: {
    src: '**/*.php'
  },
  phpcs: {
    src: ['**/*.php', '!' + 'node_modules/**/*'],
    opts: {
      bin: '/usr/local/bin/phpcs',
      standard: 'phpcs.xml',
      warningSeverity: 0
    }
  }
};
<commit_msg>Add themeDir to all asset file paths/names to match devpackages<commit_after>const themeDir = './';

module.exports = {
  cleancss: {
    opts: {
      compatibility: 'ie11,-properties.merging',
      level: {
        1: {
          specialComments: false,
          removeQuotes: true,
          removeWhitespace: true,
          tidyAtRules: true,
          cleanupCharsets: true,
          selectorsSortingMethod: 'natural'
        },
        2: {
          mergeSemantically: false,
          overrideProperties: true,
          removeEmpty: true,
          removeDuplicateRules: true,
          reduceNonAdjacentRules: true,
          removeDuplicateFontRules: true,
          removeDuplicateMediaBlocks: true,
          removeUnusedAtRules: true,
          restructureRules: true
        }
      }
    }
  },
  rename: {
    min: {
      suffix: '.min'
    }
  },
  browsersync: {
    // Important! If src is wrong, styles will not inject to the browser
    src: [themeDir + 'css/**/*'],
    opts: {
      logLevel: 'debug',
      injectChanges: true,
      proxy: 'https://airdev.test',
      browser: 'Google Chrome',
      open: false,
      notify: true,
      // Generate with: mkdir -p /var/www/certs && cd /var/www/certs && mkcert localhost 192.168.x.xxx ::1
      https: {
        key: "/var/www/certs/localhost-key.pem",
        cert: "/var/www/certs/localhost.pem",
      }
    },
    watch: [
      themeDir + '**/*.php',
      themeDir + 'js/src/**/*.js'
    ]
  },
  styles: {
    gutenberg: themeDir + 'sass/base/gutenberg.scss',
    src: themeDir + 'sass/*.scss',
    watch: themeDir + 'sass/**/*.{sass,scss}',
    development: themeDir + 'css/dev/',
    production: themeDir + 'css/prod/',
    stylelint: {
      opts: {
        fix: false,
        ignoreFiles: ['!*.scss'],
        reporters: [{
          formatter: 'string',
          console: true,
          failAfterError: false,
          debug: false
        }]
      }
    },
    opts: {
      development: {
        bundleExec: true,
        outputStyle: 'expanded',
        debugInfo: true,
        errLogToConsole: true,
        includePaths: [themeDir + 'node_modules/']
      },
      production: {
        bundleExec: true,
        outputStyle: 'compressed',
        debugInfo: true,
        errLogToConsole: true,
        includePaths: [themeDir + 'node_modules/']
      }
    }
  },
  js: {
    src: themeDir + 'js/src/*.js',
    watch: themeDir + 'js/src/**/*',
    production: themeDir + 'js/prod/',
    development: themeDir + 'js/dev/',
  },
  php: {
    src: '**/*.php'
  },
  phpcs: {
    src: [themeDir + '**/*.php', '!' + themeDir + 'node_modules/**/*'],
    opts: {
      bin: '/usr/local/bin/phpcs',
      standard: themeDir + 'phpcs.xml',
      warningSeverity: 0
    }
  }
};<|endoftext|>


<commit_before>// =============================================================================
// GULP SCSS
// =============================================================================

var gulp = require('gulp'),
    browserSync = require('browser-sync'),
    cache = require('gulp-cached'),
    scss = require('gulp-sass'),
    lint = require('gulp-scss-lint'),
    optimise  = require('gulp-csso'),
    rename = require('gulp-rename'),
    filesize = require('gulp-filesize');

// Compile the SCSS
// -----------------------------------------------------------------------------
// 1. Place the compiled CSS into the ./build directory.
// 2. Reload the page whenever SCSS recompiles.
// -----------------------------------------------------------------------------

gulp.task('scss', ['scss:lint'], function () {
    return gulp.src(['./src/**/*.scss', './docs/assets/css/**/*.scss'])
        .pipe(scss({
            includePaths: ['scss'],
            outputStyle: 'expanded',
        }).on('error', scss.logError))
        .pipe(gulp.dest('build/static/css')) // 1
        .pipe(browserSync.reload({stream:true})) // 2
});

// Lint the SCSS
// -----------------------------------------------------------------------------
// 1. Cache the SCSS so the task only lints modified files.
// 2. Fail the task only when an error happens, don't fail on warnings.
// -----------------------------------------------------------------------------

gulp.task('scss:lint', function () {
    return gulp.src(['./src/**/*.scss', './docs/assets/css/**/*.scss'])
        .pipe(cache('linting')) // 1
        .pipe(lint())
        .pipe(lint.failReporter('E')) // 2
});

// Production ready CSS
// -----------------------------------------------------------------------------
// 1. Run CSSO and improve the CSS for production.
// 2. Rename the file to include '.min' as CSSO will minify the file.
// 3. Print the minified CSS filesize in terminal.
// -----------------------------------------------------------------------------

gulp.task('dist:css', function () {
    return gulp.src('./build/css/*.css')
        .pipe(optimise()) // 1
        .pipe(rename(function (path) {
            path.basename += ".min"; // 2
        }))
        .pipe(gulp.dest('./dist/css'))
        .pipe(filesize()) // 3
});
<commit_msg>Modifying the dist task<commit_after>// =============================================================================
// GULP SCSS
// =============================================================================

var gulp = require('gulp'),
    browserSync = require('browser-sync'),
    cache = require('gulp-cached'),
    scss = require('gulp-sass'),
    lint = require('gulp-scss-lint'),
    optimise  = require('gulp-csso'),
    rename = require('gulp-rename'),
    filesize = require('gulp-filesize');

// Compile the SCSS
// -----------------------------------------------------------------------------
// 1. Place the compiled CSS into the ./build directory.
// 2. Reload the page whenever SCSS recompiles.
// -----------------------------------------------------------------------------

gulp.task('scss', ['scss:lint'], function () {
    return gulp.src(['./src/**/*.scss', './docs/assets/css/**/*.scss'])
        .pipe(scss({
            includePaths: ['scss'],
            outputStyle: 'expanded',
        }).on('error', scss.logError))
        .pipe(gulp.dest('build/static/css')) // 1
        .pipe(browserSync.reload({stream:true})) // 2
});

// Lint the SCSS
// -----------------------------------------------------------------------------
// 1. Cache the SCSS so the task only lints modified files.
// 2. Fail the task only when an error happens, don't fail on warnings.
// -----------------------------------------------------------------------------

gulp.task('scss:lint', function () {
    return gulp.src(['./src/**/*.scss', './docs/assets/css/**/*.scss'])
        .pipe(cache('linting')) // 1
        .pipe(lint())
        .pipe(lint.failReporter('E')) // 2
});

// Production ready CSS
// -----------------------------------------------------------------------------
// 1. Run CSSO and improve the CSS for production.
// 2. Rename the file to include '.min' as CSSO will minify the file.
// 3. Print the minified CSS filesize in terminal.
// -----------------------------------------------------------------------------

gulp.task('dist:css', function () {
    return gulp.src('./build/static/css/water.css')
        .pipe(optimise()) // 1
        .pipe(rename(function (path) {
            path.basename += ".min"; // 2
        }))
        .pipe(gulp.dest('./dist/'))
        .pipe(filesize()) // 3
});
<|endoftext|>


<commit_before>import { createContext, useContext } from 'react';
import auth0 from 'auth0-js';
import history from './history';
import isFunction from 'lodash/isFunction';

const CLIENT_ID = 'sUts4RKy04JcyZ2IVFgMAC0rhPARCQYg';

const permissionsKey = 'https://toiletmap.org.uk/permissions';

const options = {
  domain: 'gbptm.eu.auth0.com',
  responseType: 'token id_token',
  audience: 'https://www.toiletmap.org.uk/graphql',
  scope: 'openid profile report:loo',
};

const makeAuth = () => {
  return new auth0.WebAuth({
    ...options,
    clientID: CLIENT_ID, // web client id from auth0
    redirectUri: `${window.location.origin}/callback`,
  });
};

class Auth {
  auth0 = makeAuth();

  handleAuthentication = () =>
    new Promise((resolve, reject) => {
      this.auth0.parseHash((err, authResult) => {
        if (err) {
          reject(err);
          return;
        }

        this.setSession(authResult);
        resolve();
      });
    });

  setSession = (authResult) => {
    if (!authResult) {
      return;
    }
    // Set the time that the Access Token will expire at
    let expiresAt = JSON.stringify(
      authResult.expiresIn * 1000 + new Date().getTime()
    );
    localStorage.setItem('access_token', authResult.accessToken);
    localStorage.setItem('id_token', authResult.idToken);
    localStorage.setItem('expires_at', expiresAt);
    localStorage.setItem('nickname', authResult.idTokenPayload.nickname);
    localStorage.setItem(
      'permissions',
      JSON.stringify(authResult.idTokenPayload[permissionsKey])
    );
  };

  getAccessToken = () => {
    const accessToken = localStorage.getItem('access_token');
    if (!accessToken) {
      throw new Error('No access token found');
    }
    return accessToken;
  };

  fetchProfile = () =>
    new Promise((resolve, reject) => {
      const accessToken = this.getAccessToken();
      this.auth0.client.userInfo(accessToken, (err, profile) => {
        if (profile) {
          localStorage.setItem('name', profile.name);
          resolve(profile);
        } else if (err) {
          reject(err);
        }
      });
    });

  getProfile = () => {
    return {
      name: localStorage.getItem('name'),
    };
  };

  redirectOnNextLogin = (location) => {
    localStorage.setItem('redirectOnLogin', JSON.stringify(location));
  };

  redirectOnLogin = () => {
    return JSON.parse(localStorage.getItem('redirectOnLogin'));
  };

  login = () => {
    this.auth0.authorize();
  };

  logout = () => {
    // Clear Access Token and ID Token from local storage also the cached email
    localStorage.removeItem('name');
    localStorage.removeItem('nickname');
    localStorage.removeItem('access_token');
    localStorage.removeItem('id_token');
    localStorage.removeItem('expires_at');
    localStorage.removeItem('permissions');

    // navigate to the home route
    history.replace('/');
  };

  isAuthenticated = () => {
    // Check whether the current time is past the
    // Access Token's expiry time
    let expiresAt = JSON.parse(localStorage.getItem('expires_at'));
    return new Date().getTime() < expiresAt;
  };

  getPermissions() {
    return JSON.parse(localStorage.getItem('permissions') || '[]');
  }

  checkPermission(perm) {
    return this.getPermissions().includes(perm);
  }

  reactContextLogin = () => {
    this.login();
    return;
  };

  reactContextLogout = (logoutMutation, history) => {
    this.logout();
    logoutMutation();
    history.push('/');
  };
}

export const AuthContext = createContext(new Auth());

const AuthProvider = ({ children }) => {
  const auth = useContext(AuthContext);

  return isFunction(children) ? children(auth) : children;
};

export const useAuth = () => {
  const auth = useContext(AuthContext);

  return auth;
};

export default AuthProvider;
<commit_msg>refactor: simplify authprovider<commit_after>import React from 'react';
import auth0 from 'auth0-js';
import history from './history';

const CLIENT_ID = 'sUts4RKy04JcyZ2IVFgMAC0rhPARCQYg';
const permissionsKey = 'https://toiletmap.org.uk/permissions';

export const AuthContext = React.createContext();
export const useAuth = () => React.useContext(AuthContext);

const AuthProvider = ({ children }) => {
  const auth0Ref = React.useRef(
    new auth0.WebAuth({
      domain: 'gbptm.eu.auth0.com',
      responseType: 'token id_token',
      audience: 'https://www.toiletmap.org.uk/graphql',
      scope: 'openid profile report:loo',
      clientID: CLIENT_ID,
      redirectUri: `${window.location.origin}/callback`,
    })
  );

  const handleAuthentication = () =>
    new Promise((resolve, reject) => {
      auth0Ref.current.parseHash((err, authResult) => {
        if (err) {
          reject(err);
          return;
        }

        if (!authResult) {
          reject('No auth result');
          return;
        }

        // Set the time that the Access Token will expire at
        const expiresAt = JSON.stringify(
          authResult.expiresIn * 1000 + new Date().getTime()
        );

        localStorage.setItem('access_token', authResult.accessToken);
        localStorage.setItem('id_token', authResult.idToken);
        localStorage.setItem('expires_at', expiresAt);
        localStorage.setItem('nickname', authResult.idTokenPayload.nickname);
        localStorage.setItem(
          'permissions',
          JSON.stringify(authResult.idTokenPayload[permissionsKey])
        );

        resolve();
      });
    });

  const getAccessToken = () => {
    const accessToken = localStorage.getItem('access_token');
    if (!accessToken) {
      throw new Error('No access token found');
    }
    return accessToken;
  };

  const fetchProfile = () =>
    new Promise((resolve, reject) => {
      const accessToken = getAccessToken();
      auth0Ref.current.client.userInfo(accessToken, (err, profile) => {
        if (profile) {
          localStorage.setItem('name', profile.name);
          resolve(profile);
        } else if (err) {
          reject(err);
        }
      });
    });

  const getProfile = () => {
    return {
      name: localStorage.getItem('name'),
    };
  };

  const redirectOnNextLogin = (location) => {
    localStorage.setItem('redirectOnLogin', JSON.stringify(location));
  };

  const redirectOnLogin = () => {
    return JSON.parse(localStorage.getItem('redirectOnLogin'));
  };

  const login = () => {
    auth0Ref.current.authorize();
  };

  const logout = () => {
    // Clear Access Token and ID Token from local storage also the cached email
    localStorage.removeItem('name');
    localStorage.removeItem('nickname');
    localStorage.removeItem('access_token');
    localStorage.removeItem('id_token');
    localStorage.removeItem('expires_at');
    localStorage.removeItem('permissions');

    // navigate to the home route
    history.push('/');
  };

  const isAuthenticated = () => {
    // Check whether the current time is past the
    // Access Token's expiry time
    let expiresAt = JSON.parse(localStorage.getItem('expires_at'));
    return new Date().getTime() < expiresAt;
  };

  const checkPermission = (perm) => {
    const permissions = JSON.parse(localStorage.getItem('permissions') || '[]');
    return permissions.includes(perm);
  };

  return (
    <AuthContext.Provider
      value={{
        isAuthenticated,
        getAccessToken,
        redirectOnNextLogin,
        redirectOnLogin,
        login,
        logout,
        checkPermission,
        handleAuthentication,
        fetchProfile,
        getProfile,
      }}
    >
      {children}
    </AuthContext.Provider>
  );
};

export default AuthProvider;
<|endoftext|>