<commit_before>import React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux';
import { push } from 'react-router-redux';
import includes from 'lodash/includes';
import get from 'lodash/get';
import flatten from 'lodash/flatten';
import sampleSize from 'lodash/sampleSize';
import Row from 'react-bootstrap/lib/Row';
import Col from 'react-bootstrap/lib/Col';
import Button from 'react-bootstrap/lib/Button';
import Modal from 'react-bootstrap/lib/Modal';
import ReactCSSTransitionGroup from 'react-addons-css-transition-group';
import xor from 'lodash/xor';
import c from 'classnames';
import * as tagsActions from 'redux/modules/tags';
import * as instructionActions from 'redux/modules/isInstructionRead';
import { formValueSelector, destroy, touch as touchForm } from 'redux-form';
import { destroy as _destroyPreview } from 'redux/modules/preview';
import {
  ReactTags,
  Pagination,
  Instruction,
  LogoSVG,
  ResourceCard,
  AddResource,
} from 'components';

const addResourceFormSelector = formValueSelector('addResource');

@connect(
  (state) => ({
    user: state.auth.user,
    allTags: state.tags.data,
    inputTags: state.tags.inputTags,
    inputTagsInfo: state.tags.inputTagsInfo,
    resources: state.tags.resources,
    isInstructionRead: state.isInstructionRead,
    addResourceInputTag: addResourceFormSelector(state, 'addResourceInputTag'),
    resourceUrl: addResourceFormSelector(state, 'resourceUrl'),
    formInvalid: !get(state, 'form.addResource.syncErrors')
  }),
  {
    ...tagsActions,
    ...instructionActions,
    destroyForm: destroy,
    destroyPreview: _destroyPreview,
    push,
    touchForm,
  }
)
export default class Home extends Component {
  static propTypes = {
    user: PropTypes.object,
    allTags: PropTypes.array,
    inputTags: PropTypes.array,
    addTag: PropTypes.func,
    removeTag: PropTypes.func,
    inputTagsInfo: PropTypes.object,
    changeTags: PropTypes.func,
    loadTag: PropTypes.func,
    resources: PropTypes.array,
    readInstruction: PropTypes.func,
    isInstructionRead: PropTypes.bool,
    addResourceInputTag: PropTypes.array,
    resourceUrl: PropTypes.string,
    destroyForm: PropTypes.func.isRequired,
    destroyPreview: PropTypes.func.isRequired,
    push: PropTypes.func.isRequired,
    formInvalid: PropTypes.bool.isRequired,
    touchForm: PropTypes.func.isRequired,
  };

  constructor(props) {
    super(props);
    this.state = {
      showAddResourceModal: false
    };
  }

  componentDidMount() {
    const cb = () => {
      const {
        allTags,
        inputTags,
        resources
      } = this.props;
      const possibleTags = (inputTags && inputTags.length) ?
        xor(flatten(resources.map((resource) => resource.tags)), inputTags) :
        allTags;
      this.setState({
        pickedSuggestedTags: sampleSize(possibleTags || [], 3)
      });
    };
    this.interval = setInterval(cb, 3000);
    cb();
  }

  componentWillUnmount() {
    if (this.interval) {
      clearInterval(this.interval);
    }
  }

  openAddResourceModal = () => {
    /* Check if user is logged. If not, redirect to Login page */
    const { user, push } = this.props;
    if (user) {
      this.setState({
        showAddResourceModal: true
      });
    } else {
      push('/login');
    }
  };

  closeAddResourceModal = () => {
    this.setState({
      showAddResourceModal: false
    });
  };

  handleAddition = (tag) => {
    // const { isTagLoaded } = tagsActions;
    const {
      addTag,
      allTags,
      // inputTagsInfo,
      loadTag
    } = this.props;
    if (includes(allTags, tag)) {
      addTag(tag);
      /* Load even when it's not available */
      loadTag(tag);

      /* Only load when it's not available */
      // if (!isTagLoaded(inputTagsInfo, tag)) {
      //   loadTag(tag);
      // }
    }
  };

  handleChange = (tags) => {
    console.log('Handle change: ', tags);
    // const { isTagLoaded } = tagsActions;
    const {
      // inputTagsInfo,
      loadTag
    } = this.props;

    this.props.changeTags(tags);
    tags.forEach((tag) => {
      /* Load even when it's not available */
      loadTag(tag);

      /* Only load when it's not available */
      // if (!isTagLoaded(inputTagsInfo, tag)) {
      //   loadTag(tag);
      // }
    });
  };

  submit = () => {
    const { addResourceInputTag, resourceUrl, destroyForm, destroyPreview, touchForm } = this.props;
    touchForm('addResource');
    console.log(addResourceInputTag, resourceUrl);
    this.setState({
      ...this.state,
      showSuccessMessage: true,
      showAddResourceModal: false
    });
    destroyForm('addResource');
    destroyPreview();

    this.successMessageTimeout = setTimeout(() => {
      this.setState({
        ...this.state,
        showSuccessMessage: false
      });
    }, 3000);
  };

  render() {
    const styles = require('./Home.scss');
    const {
      allTags,
      inputTags,
      resources,
      readInstruction,
      isInstructionRead,
      // addResourceInputTag,
      // resourceUrl,
      formInvalid,
      destroyForm,
      destroyPreview,
    } = this.props;

    const possibleTags = inputTags.length ? xor(flatten(resources.map((resource) => resource.tags)), inputTags) : allTags;

    const renderItem = (props, i) => <ResourceCard key={i} {...props} />;

    return (
      <div className={`container ${styles.home}`}>
        <div className={`${styles.masthead}`}>
          {/* <div className={`${styles.logo} hidden-xs`}>
 */}
          <LogoSVG className={c('hidden-xs', styles.logo)} />

          <ReactCSSTransitionGroup
            transitionEnterTimeout={1000}
            transitionLeaveTimeout={1000}
            transitionName={{
              enter: 'animatedEnter',
              enterActive: 'bounceInLeft',
              leave: 'animated',
              leaveActive: 'bounceOutRight'
            }} >
            { !isInstructionRead && <Instruction
              className={c('hidden-xs', styles.instruction)}
              read={readInstruction} />
            }
          </ReactCSSTransitionGroup>

          <ReactTags
            classNames={{
              tagInputField: `${styles.tagInputField}`
            }}
            tags={inputTags}
            suggestions={possibleTags}
            handleChange={this.handleChange}
            autocomplete
            renderInputComponent={(props) => {
              // console.log(props);
              const onInputFocus = (e) => {
                if (window.innerWidth < 769) {
                  window.scrollTo(0, 0);
                  document.body.scrollTop = 0;
                }
                if (props.onFocus) {
                  props.onFocus(e);
                }
              };
              return (

                <input
                  {...props}
                  // ref={this.props.ref}
                  type="text"
                  className="form-control"
                  onFocus={onInputFocus} />

                  <button className="btn">

                  </button>

);
            }}
          />
          {/* </AlwaysVisible> */}

          <Row>
            <Col xs={12}>
              <p className={`gray ${styles.tagsSuggestionLabel}`}>
                Try:
                <ReactCSSTransitionGroup
                  transitionEnterTimeout={1000}
                  transitionLeaveTimeout={1000}
                  transitionName={{
                    enter: 'animated',
                    enterActive: 'fadeInUp',
                    leave: 'animated',
                    leaveActive: 'fadeOutUp'
                  }} >

                    { get(this.state, 'pickedSuggestedTags', []).map((tag, i) => (

                        {i !== 0 && , }
                         this.handleAddition(tag)}>
                          {tag}

                    )) }

                </ReactCSSTransitionGroup>

            </Col>
          </Row>

          <Pagination
            items={resources || []}
            renderItem={renderItem}
            itemsPerPage={10} />

          add_to_photos

        <Modal
          show={this.state.showAddResourceModal}
          className={c(styles.addResourceModal)}
          onHide={this.closeAddResourceModal} >
          <Modal.Header>

Add Resource

          </Modal.Header>
          <Modal.Body>
            <AddResource />

              <Button
                bsStyle={'danger'}
                onClick={() => {
                  destroyForm('addResource');
                  destroyPreview();
                }} >
                delete
                Discard
              </Button>
              <Button
                disabled={!formInvalid}
                onClick={this.submit}
                bsStyle={'success'}
                className={c('m-l-23')}>
                send
                Post
              </Button>

          </Modal.Body>
        </Modal>

        <ReactCSSTransitionGroup
          transitionEnterTimeout={1000}
          transitionLeaveTimeout={1000}
          transitionName={{
            enter: 'animatedEnter',
            enterActive: 'fadeInUp',
            leave: 'animated',
            leaveActive: 'fadeOutDown'
          }} >
          { get(this.state, 'showSuccessMessage') &&

                Thank you for sharing!
                 {
                    if (this.successMessageTimeout) {
                      clearTimeout(this.successMessageTimeout);
                      this.setState({
                        ...this.state,
                        showSuccessMessage: false
                      });
                    }
                  }}
                  className="material-icons pull-right" >
                  close

          }
        </ReactCSSTransitionGroup>

        {/* <SuggestResource /> */}

    );
  }
}
<commit_msg>saveResource in Home page<commit_after>import React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux';
import { push } from 'react-router-redux';
import includes from 'lodash/includes';
import get from 'lodash/get';
import flatten from 'lodash/flatten';
import sampleSize from 'lodash/sampleSize';
import Row from 'react-bootstrap/lib/Row';
import Col from 'react-bootstrap/lib/Col';
import Button from 'react-bootstrap/lib/Button';
import Modal from 'react-bootstrap/lib/Modal';
import ReactCSSTransitionGroup from 'react-addons-css-transition-group';
import xor from 'lodash/xor';
import c from 'classnames';
import * as tagsActions from 'redux/modules/tags';
import * as instructionActions from 'redux/modules/isInstructionRead';
import { formValueSelector, destroy, touch as touchForm } from 'redux-form';
import { destroy as _destroyPreview } from 'redux/modules/preview';
import { save as saveResource } from 'redux/modules/userResources';
import {
  ReactTags,
  Pagination,
  Instruction,
  LogoSVG,
  ResourceCard,
  AddResource,
} from 'components';

const addResourceFormSelector = formValueSelector('addResource');

@connect(
  (state) => ({
    user: state.auth.user,
    allTags: state.tags.data,
    inputTags: state.tags.inputTags,
    inputTagsInfo: state.tags.inputTagsInfo,
    resources: state.tags.resources,
    isInstructionRead: state.isInstructionRead,
    addResourceInputTag: addResourceFormSelector(state, 'addResourceInputTag'),
    resourceUrl: addResourceFormSelector(state, 'resourceUrl'),
    formValid: !get(state, 'form.addResource.syncErrors')
  }),
  {
    ...tagsActions,
    ...instructionActions,
    destroyForm: destroy,
    destroyPreview: _destroyPreview,
    push,
    touchForm,
    saveResource,
  }
)
export default class Home extends Component {
  static propTypes = {
    user: PropTypes.object,
    allTags: PropTypes.array,
    inputTags: PropTypes.array,
    addTag: PropTypes.func,
    removeTag: PropTypes.func,
    inputTagsInfo: PropTypes.object,
    changeTags: PropTypes.func,
    loadTag: PropTypes.func,
    resources: PropTypes.array,
    readInstruction: PropTypes.func,
    isInstructionRead: PropTypes.bool,
    addResourceInputTag: PropTypes.array,
    resourceUrl: PropTypes.string,
    destroyForm: PropTypes.func.isRequired,
    destroyPreview: PropTypes.func.isRequired,
    push: PropTypes.func.isRequired,
    formValid: PropTypes.bool.isRequired,
    touchForm: PropTypes.func.isRequired,
    saveResource: PropTypes.func.isRequired,
  };

  constructor(props) {
    super(props);
    this.state = {
      showAddResourceModal: false
    };
  }

  componentDidMount() {
    const cb = () => {
      const {
        allTags,
        inputTags,
        resources
      } = this.props;
      const possibleTags = (inputTags && inputTags.length) ?
        xor(flatten(resources.map((resource) => resource.tags)), inputTags) :
        allTags;
      this.setState({
        pickedSuggestedTags: sampleSize(possibleTags || [], 3)
      });
    };
    this.interval = setInterval(cb, 3000);
    cb();
  }

  componentWillUnmount() {
    if (this.interval) {
      clearInterval(this.interval);
    }
  }

  openAddResourceModal = () => {
    /* Check if user is logged. If not, redirect to Login page */
    const { user, push } = this.props;
    if (user) {
      this.setState({
        showAddResourceModal: true
      });
    } else {
      push('/login');
    }
  };

  closeAddResourceModal = () => {
    this.setState({
      showAddResourceModal: false
    });
  };

  handleAddition = (tag) => {
    // const { isTagLoaded } = tagsActions;
    const {
      addTag,
      allTags,
      // inputTagsInfo,
      loadTag
    } = this.props;
    if (includes(allTags, tag)) {
      addTag(tag);
      /* Load even when it's not available */
      loadTag(tag);

      /* Only load when it's not available */
      // if (!isTagLoaded(inputTagsInfo, tag)) {
      //   loadTag(tag);
      // }
    }
  };

  handleChange = (tags) => {
    console.log('Handle change: ', tags);
    // const { isTagLoaded } = tagsActions;
    const {
      // inputTagsInfo,
      loadTag
    } = this.props;

    this.props.changeTags(tags);
    tags.forEach((tag) => {
      /* Load even when it's not available */
      loadTag(tag);

      /* Only load when it's not available */
      // if (!isTagLoaded(inputTagsInfo, tag)) {
      //   loadTag(tag);
      // }
    });
  };

  submit = () => {
    const {
      addResourceInputTag,
      resourceUrl,
      destroyForm,
      destroyPreview,
      touchForm,
      formValid,
      saveResource,
    } = this.props;
    touchForm('addResource');

    if (formValid) {
      /* Form valid */
      saveResource({
        resourceUrl,
        tags: addResourceInputTag
      }).then(() => {
        this.setState({
          ...this.state,
          showSuccessMessage: true,
          showAddResourceModal: false
        });
        destroyForm('addResource');
        destroyPreview();

        this.successMessageTimeout = setTimeout(() => {
          this.setState({
            ...this.state,
            showSuccessMessage: false
          });
        }, 3000);
      });
    }
  };

  render() {
    const styles = require('./Home.scss');
    const {
      allTags,
      inputTags,
      resources,
      readInstruction,
      isInstructionRead,
      // addResourceInputTag,
      // resourceUrl,
      formValid,
      destroyForm,
      destroyPreview,
    } = this.props;

    const possibleTags = inputTags.length ? xor(flatten(resources.map((resource) => resource.tags)), inputTags) : allTags;

    const renderItem = (props, i) => <ResourceCard key={i} {...props} />;

    return (
      <div className={`container ${styles.home}`}>
        <div className={`${styles.masthead}`}>
          {/* <div className={`${styles.logo} hidden-xs`}>
 */}
          <LogoSVG className={c('hidden-xs', styles.logo)} />

          <ReactCSSTransitionGroup
            transitionEnterTimeout={1000}
            transitionLeaveTimeout={1000}
            transitionName={{
              enter: 'animatedEnter',
              enterActive: 'bounceInLeft',
              leave: 'animated',
              leaveActive: 'bounceOutRight'
            }} >
            { !isInstructionRead && <Instruction
              className={c('hidden-xs', styles.instruction)}
              read={readInstruction} />
            }
          </ReactCSSTransitionGroup>

          <ReactTags
            classNames={{
              tagInputField: `${styles.tagInputField}`
            }}
            tags={inputTags}
            suggestions={possibleTags}
            handleChange={this.handleChange}
            autocomplete
            renderInputComponent={(props) => {
              // console.log(props);
              const onInputFocus = (e) => {
                if (window.innerWidth < 769) {
                  window.scrollTo(0, 0);
                  document.body.scrollTop = 0;
                }
                if (props.onFocus) {
                  props.onFocus(e);
                }
              };
              return (

                <input
                  {...props}
                  // ref={this.props.ref}
                  type="text"
                  className="form-control"
                  onFocus={onInputFocus} />

                  <button className="btn">

                  </button>

);
            }}
          />
          {/* </AlwaysVisible> */}

          <Row>
            <Col xs={12}>
              <p className={`gray ${styles.tagsSuggestionLabel}`}>
                Try:
                <ReactCSSTransitionGroup
                  transitionEnterTimeout={1000}
                  transitionLeaveTimeout={1000}
                  transitionName={{
                    enter: 'animated',
                    enterActive: 'fadeInUp',
                    leave: 'animated',
                    leaveActive: 'fadeOutUp'
                  }} >

                    { get(this.state, 'pickedSuggestedTags', []).map((tag, i) => (

                        {i !== 0 && , }
                         this.handleAddition(tag)}>
                          {tag}

                    )) }

                </ReactCSSTransitionGroup>

            </Col>
          </Row>

          <Pagination
            items={resources || []}
            renderItem={renderItem}
            itemsPerPage={10} />

          add_to_photos

        <Modal
          show={this.state.showAddResourceModal}
          className={c(styles.addResourceModal)}
          onHide={this.closeAddResourceModal} >
          <Modal.Header>

Add Resource

          </Modal.Header>
          <Modal.Body>
            <AddResource />

              <Button
                bsStyle={'danger'}
                onClick={() => {
                  destroyForm('addResource');
                  destroyPreview();
                }} >
                delete
                Discard
              </Button>
              <Button
                disabled={!formValid}
                onClick={this.submit}
                bsStyle={'success'}
                className={c('m-l-23')}>
                send
                Post
              </Button>

          </Modal.Body>
        </Modal>

        <ReactCSSTransitionGroup
          transitionEnterTimeout={1000}
          transitionLeaveTimeout={1000}
          transitionName={{
            enter: 'animatedEnter',
            enterActive: 'fadeInUp',
            leave: 'animated',
            leaveActive: 'fadeOutDown'
          }} >
          { get(this.state, 'showSuccessMessage') &&

                Thank you for sharing!
                 {
                    if (this.successMessageTimeout) {
                      clearTimeout(this.successMessageTimeout);
                      this.setState({
                        ...this.state,
                        showSuccessMessage: false
                      });
                    }
                  }}
                  className="material-icons pull-right" >
                  close

          }
        </ReactCSSTransitionGroup>

        {/* <SuggestResource /> */}

    );
  }
}
<|endoftext|>


<commit_before>import React, { Component, PropTypes } from 'react';
import { Grid, Row, Col, Well, Label } from 'react-bootstrap/lib';
import {connect} from 'react-redux';
import config from '../../config';
import Helmet from 'react-helmet';
import superagent from 'superagent';
import { push } from 'react-router-redux';

import { getBlogEntries } from '../../redux/actions/getBlogEntriesActions';

@connect((store) => {
  return {
    getBlogEntriesState: store.getBlogEntries.articleList,
  };
})

export default class Home extends Component {

  componentDidMount() {
    superagent
    .post('/community')
    .send({ loadStatus: 2 })
    .set('Accept', 'application/json')
    .end((error, res) => {
      if (res.body.status === 1) {
        this.props.dispatch(getBlogEntries(res.body.blogArticles));
      }
    });
  }

  static propTypes = {
    userLoggedIn: PropTypes.string
  }

  loadArticle = (id) => {
    superagent
    .post('/getSpecificArticle')
    .send({ artId: id })
    .set('Accept', 'application/json')
    .end((error, res) => {
      if(res.body.status === 1) {
        this.props.dispatch(push('community/'+res.body.specificArticleData.urlFriendlyTitel));
      }
    });
  }

  render() {
    const styles = require('./Home.scss');
    const stylesMyProfile = require('../MyProfile/MyProfile.scss');
    const stylesCommunity = require('../Community/Community.scss');
    const { getBlogEntriesState } = this.props;
    // require the logo image both from client and server
    const logoImage = require('./logo.png');

    let blogContentDef = []; let whichCategory = '';
    getBlogEntriesState.articles.forEach(function(entry){
      if(entry.category === 'Projekt'){
        whichCategory = 'label-primary';
      }else{
        whichCategory = 'label-info';
      }
      blogContentDef.push(

 this.loadArticle(entry.articleId)} className={styles.topLine + ' animated fadeInUp col-xs-12 ' + styles.hover}>

{entry.userNickname}

{entry.titel}

{entry.category}

{ Object.keys(entry.technologies[0]).map(key => ) }

{entry.timeFormatted}

      );
    }.bind(this));

    return (

        <Helmet title="Home"/>

{config.app.title}

{config.app.description}

{config.app.subdescription}

React Activity Feed

            {blogContentDef}

    );
  }
}
<commit_msg>hdhdhd36875555444ss<commit_after>import React, { Component, PropTypes } from 'react';
import { Grid, Row, Col, Well, Label } from 'react-bootstrap/lib';
import {connect} from 'react-redux';
import config from '../../config';
import Helmet from 'react-helmet';
import superagent from 'superagent';
import { push } from 'react-router-redux';

import { getBlogEntries } from '../../redux/actions/getBlogEntriesActions';

@connect((store) => {
  return {
    getBlogEntriesState: store.getBlogEntries.articleList,
  };
})

export default class Home extends Component {

  componentDidMount() {
    superagent
    .post('/community')
    .send({ loadStatus: 2 })
    .set('Accept', 'application/json')
    .end((error, res) => {
      if (res.body.status === 1) {
        this.props.dispatch(getBlogEntries(res.body.blogArticles));
      }
    });
  }

  static propTypes = {
    userLoggedIn: PropTypes.string
  }

  loadArticle = (id) => {
    superagent
    .post('/getSpecificArticle')
    .send({ artId: id })
    .set('Accept', 'application/json')
    .end((error, res) => {
      if(res.body.status === 1) {
        this.props.dispatch(push('community/'+res.body.specificArticleData.urlFriendlyTitel));
      }
    });
  }

  render() {
    const styles = require('./Home.scss');
    const stylesMyProfile = require('../MyProfile/MyProfile.scss');
    const stylesCommunity = require('../Community/Community.scss');
    const { getBlogEntriesState } = this.props;
    // require the logo image both from client and server
    const logoImage = require('./logo.png');

    let blogContentDef = []; let whichCategory = '';
    getBlogEntriesState.articles.forEach(function(entry){
      if(entry.category === 'Projekt'){
        whichCategory = 'label-primary';
      }else{
        whichCategory = 'label-info';
      }
      blogContentDef.push(

 this.loadArticle(entry.articleId)} className={styles.topLine + ' animated fadeInUp col-xs-12 ' + styles.hover}>

{entry.userNickname}

{entry.titel}

{entry.category}

{ Object.keys(entry.technologies[0]).map(entry.technologies[0][key].length > 1 ? (key => ) : '' ) }

{entry.timeFormatted}

      );
    }.bind(this));

    return (

        <Helmet title="Home"/>

{config.app.title}

{config.app.description}

{config.app.subdescription}

React Activity Feed

            {blogContentDef}

    );
  }
}
<|endoftext|>


<commit_before>import React from 'react';
import querystring from 'querystring';
// import wiki from 'wikijs';

import QuoteCard from '../components/QuoteCard';

const makeTweetLink = (hashtags, related, text) => {
  const tweetUrl = 'https://twitter.com/intent/tweet';
  const urlParams = {};

  if (hashtags.length) {
    urlParams.hashtags = hashtags.join(',');
  }

  if (related.length) {
    urlParams.related = related.join(',');
  }

  urlParams.text = text;

  return `${tweetUrl}?${querystring.stringify(urlParams)}`;
};

const containerStyles = {
  margin: 15
};

class QuotesPage extends React.Component {
  componentWillMount() {
    console.log('componentWillMount');

    this.setState({
      quoteAuthor: 'N/A',
      quoteText: 'Loading...'
    });

    this.getRandomQuote();
  }

  onTweetClick = () => {
    const hashtags = ['quotes'];
    const related = ['freecodecamp'];
    const maxTweetLength = 140;
    const ourTweetLength = this.state.quoteText.length + (hashtags.length * 2) +
      hashtags.join('').length;

    if (ourTweetLength > maxTweetLength) {
      window.alert('Quote too long, can\'t be tweeted!');
    } else {
      window.open(
        makeTweetLink(hashtags, related, this.state.quoteText)
      );
    }
  };

  onGetNewQuote = () => {
    console.log('I want new quote');

    this.getRandomQuote();
  };

  getRandomQuote = () => {
    // wiki({
    //   apiUrl: 'https://en.wikiquote.org/w/api.php'
    // }).page('Batman')
    //   .then(console.log)
    //   .catch(err => {
    //     console.error('fetch failed', err);
    //   });

    // The only working API I could find is about Chuck Norris of course
    fetch('https://api.icndb.com/jokes/random')
      .then(response => response.json())
      .then(response => {
        if (response.type === 'success') {
          this.setState({
            quoteAuthor: 'Anonymous',
            quoteText: response.value.joke
          });
        }
      });
  };

  render() {
    return (

        <QuoteCard
          author={this.state.quoteAuthor}
          text={this.state.quoteText}
          onTweetClick={this.onTweetClick}
          onGetNewQuote={this.onGetNewQuote}
        />

    );
  }
}

export default QuotesPage;
<commit_msg>API param to get decoded HTML entities<commit_after>import React from 'react';
import querystring from 'querystring';
// import wiki from 'wikijs';

import QuoteCard from '../components/QuoteCard';

const makeTweetLink = (hashtags, related, text) => {
  const tweetUrl = 'https://twitter.com/intent/tweet';
  const urlParams = {};

  if (hashtags.length) {
    urlParams.hashtags = hashtags.join(',');
  }

  if (related.length) {
    urlParams.related = related.join(',');
  }

  urlParams.text = text;

  return `${tweetUrl}?${querystring.stringify(urlParams)}`;
};

const containerStyles = {
  margin: 15
};

class QuotesPage extends React.Component {
  componentWillMount() {
    console.log('componentWillMount');

    this.setState({
      quoteAuthor: 'N/A',
      quoteText: 'Loading...'
    });

    this.getRandomQuote();
  }

  onTweetClick = () => {
    const hashtags = ['quotes'];
    const related = ['freecodecamp'];
    const maxTweetLength = 140;
    const ourTweetLength = this.state.quoteText.length + (hashtags.length * 2) +
      hashtags.join('').length;

    if (ourTweetLength > maxTweetLength) {
      window.alert('Quote too long, can\'t be tweeted!');
    } else {
      window.open(
        makeTweetLink(hashtags, related, this.state.quoteText)
      );
    }
  };

  onGetNewQuote = () => {
    console.log('I want new quote');

    this.getRandomQuote();
  };

  getRandomQuote = () => {
    // wiki({
    //   apiUrl: 'https://en.wikiquote.org/w/api.php'
    // }).page('Batman')
    //   .then(console.log)
    //   .catch(err => {
    //     console.error('fetch failed', err);
    //   });

    // The only working API I could find is about Chuck Norris of course
    fetch('https://api.icndb.com/jokes/random?escape=javascript')
      .then(response => response.json())
      .then(response => {
        if (response.type === 'success') {
          this.setState({
            quoteAuthor: 'Anonymous',
            quoteText: response.value.joke
          });
        }
      });
  };

  render() {
    return (

        <QuoteCard
          author={this.state.quoteAuthor}
          text={this.state.quoteText}
          onTweetClick={this.onTweetClick}
          onGetNewQuote={this.onGetNewQuote}
        />

    );
  }
}

export default QuotesPage;
<|endoftext|>


<commit_before>'use strict';

const proxyquire = require('proxyquire').noCallThru(),
    expect = require('chai').expect;

describe('auth.service', () => {
    let sut,
        jsonwebtoken,
        expressJWT,
        envValues,
        userModel,
        userInstance;

    const expressJWTResult = function expressJWTResult() {
        return () => {
        };
    };

    const token = 'token';
    beforeEach(() => {
        jsonwebtoken = {
            sign: env.spy(() => token)
        };
        expressJWT = env.spy(expressJWTResult);
        envValues = {
            SESSION_SECRET: 'testSecret'
        };
        userInstance = {
            save: env.stub()
        };
        userModel = env.spy(() => (userInstance));
        userModel.findOne = env.stub();
        userModel.findById = env.stub();
        sut = proxyquire('./auth.service', {
            jsonwebtoken,
            'express-jwt': expressJWT,
            '../../../../env': envValues,
            '../api/models/user': userModel
        });
    });

    it('should export validateJwt middleware', () => {
        expect(sut.validateJwt).to.be.an.instanceof(Function);
    });

    it('should export responseAuthToken middleware', () => {
        expect(sut.responseAuthToken).to.be.an.instanceof(Function);
    });

    it('should init Jwt middleware', () => {
        expressJWT.should.been.calledWith({
            secret: 'testSecret',
            credentialsRequired: false,
            requestProperty: 'auth'
        });
    });

    describe('responseAuthToken middleware', () => {

        let next,
            req,
            res;

        beforeEach(() => {
            next = env.spy();
            req = {};
            res = {
                json: env.spy()
            };
        });

        it('should return error if where no user in request', () => {
            sut.responseAuthToken(req, res, next);
            next.should.been.calledWith(new Error('Something went wrong, please try again.'));
        });

        it('should response with token if user exists', () => {
            req.user = { _id: 'testId' };
            sut.responseAuthToken(req, res, next);
            jsonwebtoken.sign.should.been.calledWith({ id: 'testId' },
                'testSecret', { expiresIn: 18000 });
            res.json.should.been.calledWith({
                success: true,
                token
            });
        });

    });

    describe('generateOAuth2VerifyCallback', () => {

        const providerProperty = 'testProvided';

        it('should generate verify callback', () => {
            const result = sut.generateOAuth2VerifyCallback(userModel, providerProperty);
            expect(result).to.be.an.instanceof(Function);
        });

        it('should find user by provider property', () => {
            const callback = sut.generateOAuth2VerifyCallback(userModel, providerProperty);
            userModel.findOne.returns(Promise.resolve(null));
            callback('token', '', {
                id: 'profileId'
            }, () => {
            });
            userModel.findOne.should.calledWith({
                'testProvided.id': 'profileId'
            });
        });

        it('should resolve if user exists', (done) => {
            const existingUser = {
                'testProvided.id': 'profileId'
            };
            userModel.findOne.returns(Promise.resolve(existingUser));
            const callback = sut.generateOAuth2VerifyCallback(userModel, providerProperty);
            callback('token', '', {
                id: 'profileId'
            }, (err, user) => {
                user.should.equal(existingUser);
                done();
            });
        });

        it('should create new user if not exists', (done) => {
            const newUser = {
                'testProvided.id': 'profileId'
            };
            userInstance.save.returns(Promise.resolve(newUser));
            userModel.findOne.returns(Promise.resolve(null));
            const callback = sut.generateOAuth2VerifyCallback(userModel, providerProperty);
            callback('token', '', {
                id: 'profileId',
                photos: [{ value: 'testPhoto' }],
                emails: [{ value: 'testEmail' }],
                displayName: 'test user'
            }, (err, user) => {
                userInstance.fullName.should.equal('test user');
                user.should.equal(newUser);
                done();
            });
        });

        it('should return error on reject', (done) => {
            const error = {
                'testProvided.id': 'profileId'
            };
            userModel.findOne.returns(Promise.reject(error));
            const callback = sut.generateOAuth2VerifyCallback(userModel, providerProperty);
            callback('token', '', {
                id: 'profileId',
                displayName: 'test user'
            }, (err) => {
                err.should.equal(error);
                done();
            });
        });

    });

    describe('isAuthenticated', () => {

        it('should return true if authenticated', () => {
            const req = {
                auth: { id: 'testId' }
            };
            sut.initRequest(req);
            let result = req.isAuthenticated();
            result.should.equal(true);
            req.auth = {};
            result = req.isAuthenticated();
            result.should.equal(false);
        });

    });

    describe('getCurrentUser', () => {

        it('should return promise', () => {
            const req = {};
            sut.initRequest(req);
            req.isAuthenticated = () => (false);
            const result = req.getCurrentUser();
            expect(result).to.be.an.instanceof(Promise);
        });

        it('should resolve with current user', (done) => {
            const CurrentUser = {
                _id: 'testId',
                fullName: 'curUser'
            };
            userModel.findById.resolves(CurrentUser);

            const req = {
                auth: {
                    id: 'testId'
                }
            };
            sut.initRequest(req);
            req.isAuthenticated = () => (true);
            req.getCurrentUser()
                .then((user) => {
                    user.should.equal(CurrentUser);
                })
                .finally(() => {
                    userModel.findById.should.calledWith('testId');
                    done();
                });
        });

        it('should reject if not authenticated', () => {
            const req = {};
            sut.initRequest(req);
            req.isAuthenticated = () => (false);
            req.getCurrentUser()
                .catch((error) => {
                    expect(error).to.be.an.instanceof(Error);
                });
        });

    });

    describe('restrictUnauthenticated', () => {

        it('should response if unauthenticated', () => {
            const res = {
                status: env.stub().returns({
                    json: env.stub()
                })
            };
            sut.restrictUnauthenticated({
                isAuthenticated: () => (false)
            }, res);
            res.status.should.calledWith(401);
        });

        it('should move next if authenticated', () => {
            const next = env.stub();
            sut.restrictUnauthenticated({
                isAuthenticated: () => (true)
            }, {}, next);
            next.should.calledWith();
        });

    });

});

<commit_msg>fixing heroku<commit_after>'use strict';

const proxyquire = require('proxyquire').noCallThru(),
    expect = require('chai').expect;

describe('auth.service', () => {
    let sut,
        jsonwebtoken,
        expressJWT,
        envValues,
        userModel,
        userInstance;

    const expressJWTResult = function expressJWTResult() {
        return () => {
        };
    };

    const token = 'token';
    beforeEach(() => {
        jsonwebtoken = {
            sign: env.spy(() => token)
        };
        expressJWT = env.spy(expressJWTResult);
        envValues = {
            SESSION_SECRET: 'testSecret'
        };
        userInstance = {
            save: env.stub()
        };
        userModel = env.spy(() => (userInstance));
        userModel.findOne = env.stub();
        userModel.findById = env.stub();
        sut = proxyquire('./auth.service', {
            jsonwebtoken,
            'express-jwt': expressJWT,
            '../../../../env': envValues,
            '../api/models/user': userModel
        });
    });

    it('should export validateJwt middleware', () => {
        expect(sut.validateJwt).to.be.an.instanceof(Function);
    });

    it('should export responseAuthToken middleware', () => {
        expect(sut.responseAuthToken).to.be.an.instanceof(Function);
    });

    it('should init Jwt middleware', () => {
        expressJWT.should.been.calledWith({
            secret: 'testSecret',
            credentialsRequired: false,
            requestProperty: 'auth'
        });
    });

    describe('responseAuthToken middleware', () => {

        let next,
            req,
            res;

        beforeEach(() => {
            next = env.spy();
            req = {};
            res = {
                json: env.spy()
            };
        });

        it('should return error if where no user in request', () => {
            sut.responseAuthToken(req, res, next);
            next.should.been.calledWith(new Error('Something went wrong, please try again.'));
        });

        it('should response with token if user exists', () => {
            req.user = { _id: 'testId' };
            sut.responseAuthToken(req, res, next);
            jsonwebtoken.sign.should.been.calledWith({ id: 'testId' },
                'testSecret', { expiresIn: 18000 });
            res.json.should.been.calledWith({
                success: true,
                token
            });
        });

    });

    describe('generateOAuth2VerifyCallback', () => {

        const providerProperty = 'testProvided';

        it('should generate verify callback', () => {
            const result = sut.generateOAuth2VerifyCallback(userModel, providerProperty);
            expect(result).to.be.an.instanceof(Function);
        });

        it('should find user by provider property', () => {
            const callback = sut.generateOAuth2VerifyCallback(userModel, providerProperty);
            userModel.findOne.returns(Promise.resolve(null));
            callback('token', '', {
                id: 'profileId'
            }, () => {
            });
            userModel.findOne.should.calledWith({
                'testProvided.id': 'profileId'
            });
        });

        it('should resolve if user exists', (done) => {
            const existingUser = {
                'testProvided.id': 'profileId'
            };
            userModel.findOne.returns(Promise.resolve(existingUser));
            const callback = sut.generateOAuth2VerifyCallback(userModel, providerProperty);
            callback('token', '', {
                id: 'profileId'
            }, (err, user) => {
                user.should.equal(existingUser);
                done();
            });
        });

        it('should create new user if not exists', (done) => {
            const newUser = {
                'testProvided.id': 'profileId'
            };
            userInstance.save.returns(Promise.resolve(newUser));
            userModel.findOne.returns(Promise.resolve(null));
            const callback = sut.generateOAuth2VerifyCallback(userModel, providerProperty);
            callback('token', '', {
                id: 'profileId',
                photos: [{ value: 'testPhoto' }],
                emails: [{ value: 'testEmail' }],
                displayName: 'test user'
            }, (err, user) => {
                userInstance.fullName.should.equal('test user');
                user.should.equal(newUser);
                done();
            });
        });

        it('should return error on reject', (done) => {
            const error = {
                'testProvided.id': 'profileId'
            };
            userModel.findOne.returns(Promise.reject(error));
            const callback = sut.generateOAuth2VerifyCallback(userModel, providerProperty);
            callback('token', '', {
                id: 'profileId',
                displayName: 'test user'
            }, (err) => {
                err.should.equal(error);
                done();
            });
        });

    });

    describe('isAuthenticated', () => {

        it('should return true if authenticated', () => {
            const req = {
                auth: { id: 'testId' }
            };
            sut.initRequest(req, {}, () => {});
            let result = req.isAuthenticated();
            result.should.equal(true);
            req.auth = {};
            result = req.isAuthenticated();
            result.should.equal(false);
        });

    });

    describe('getCurrentUser', () => {

        it('should return promise', () => {
            const req = {};
            sut.initRequest(req, {}, () => {});
            req.isAuthenticated = () => (false);
            const result = req.getCurrentUser();
            expect(result).to.be.an.instanceof(Promise);
        });

        it('should resolve with current user', (done) => {
            const CurrentUser = {
                _id: 'testId',
                fullName: 'curUser'
            };
            userModel.findById.resolves(CurrentUser);

            const req = {
                auth: {
                    id: 'testId'
                }
            };
            sut.initRequest(req, {}, () => {});
            req.isAuthenticated = () => (true);
            req.getCurrentUser()
                .then((user) => {
                    user.should.equal(CurrentUser);
                })
                .finally(() => {
                    userModel.findById.should.calledWith('testId');
                    done();
                });
        });

        it('should reject if not authenticated', () => {
            const req = {};
            sut.initRequest(req, {}, () => {});
            req.isAuthenticated = () => (false);
            req.getCurrentUser()
                .catch((error) => {
                    expect(error).to.be.an.instanceof(Error);
                });
        });

    });

    describe('restrictUnauthenticated', () => {

        it('should response if unauthenticated', () => {
            const res = {
                status: env.stub().returns({
                    json: env.stub()
                })
            };
            sut.restrictUnauthenticated({
                isAuthenticated: () => (false)
            }, res);
            res.status.should.calledWith(401);
        });

        it('should move next if authenticated', () => {
            const next = env.stub();
            sut.restrictUnauthenticated({
                isAuthenticated: () => (true)
            }, {}, next);
            next.should.calledWith();
        });

    });

});

<|endoftext|>


<commit_before>'use strict';

var inherits = require('inherits');
var ContentListView = require('streamhub-sdk/content/views/content-list-view');

var ContentRepliesView = function (opts) {
    opts = opts || {};

    if (! opts.content) {
        throw 'Expected opts.content when constructing ContentRepliesView';
    }

    opts.autoRender = false;
    opts.maxVisibleItems = 2;
    ContentListView.call(this, opts);

    this.content = opts.content;
    this.comparator = opts.comparator || ContentRepliesView.comparators.CREATEDAT_ASCENDING;
    this._maxNestLevel = Math.max(0, opts.maxNestLevel);

    if (this.comparator === ContentRepliesView.comparators.CREATEDAT_DESCENDING) {
        this._showMoreHeader = true;
    } else {
        this._showMoreHeader = false;
    }

    this.content.on('reply', function (reply) {
        this.add(reply);
    }.bind(this));
};
inherits(ContentRepliesView, ContentListView);

ContentRepliesView.comparators = {
    CREATEDAT_DESCENDING: function (a, b) {
        var aDate = a.content.createdAt || a.createdAt,
            bDate = b.content.createdAt || b.createdAt;
        return aDate - bDate;
    },
    CREATEDAT_ASCENDING: function (a, b) {
        var aDate = a.content.createdAt || a.createdAt,
            bDate = b.content.createdAt || b.createdAt;
        return bDate - aDate;
    }
}

ContentRepliesView.prototype._addReplies = function (replies) {
    replies = replies || [];
    for (var i=0; i < replies.length; i++) {
        var reply = replies[i];
        this.add(reply);
    }
};

ContentRepliesView.prototype.createContentView = function (content) {
    var ContentThreadView = require('thread');
    return new ContentThreadView({
        content: content,
        maxNestLevel: this._maxNestLevel-1,
        showMoreHeader: this._showMoreHeader
    });
};

ContentRepliesView.prototype.render = function () {
    ContentListView.prototype.render.call(this);

    if (this._showMoreHeader) {
        this.$el.find(this.showMoreElSelector).insertBefore(this.$listEl);
    }

    this._addReplies(this.content.replies);
};

module.exports = ContentRepliesView;
<commit_msg>Stop propagation of showMore.hub event to prevent parent level threads showing more<commit_after>'use strict';

var inherits = require('inherits');
var ContentListView = require('streamhub-sdk/content/views/content-list-view');

var ContentRepliesView = function (opts) {
    opts = opts || {};

    if (! opts.content) {
        throw 'Expected opts.content when constructing ContentRepliesView';
    }

    opts.autoRender = false;
    opts.maxVisibleItems = 2;
    ContentListView.call(this, opts);

    this.content = opts.content;
    this.comparator = opts.comparator || ContentRepliesView.comparators.CREATEDAT_ASCENDING;
    this._maxNestLevel = Math.max(0, opts.maxNestLevel);

    if (this.comparator === ContentRepliesView.comparators.CREATEDAT_DESCENDING) {
        this._showMoreHeader = true;
    } else {
        this._showMoreHeader = false;
    }

    this.content.on('reply', function (reply) {
        this.add(reply);
    }.bind(this));
};
inherits(ContentRepliesView, ContentListView);

ContentRepliesView.prototype.events = ContentListView.prototype.events.extended({
    'showMore.hub': function (e) {
        e.stopPropagation();
        this.showMore();
    }
});

ContentRepliesView.comparators = {
    CREATEDAT_DESCENDING: function (a, b) {
        var aDate = a.content.createdAt || a.createdAt,
            bDate = b.content.createdAt || b.createdAt;
        return aDate - bDate;
    },
    CREATEDAT_ASCENDING: function (a, b) {
        var aDate = a.content.createdAt || a.createdAt,
            bDate = b.content.createdAt || b.createdAt;
        return bDate - aDate;
    }
}

ContentRepliesView.prototype._addReplies = function (replies) {
    replies = replies || [];
    for (var i=0; i < replies.length; i++) {
        var reply = replies[i];
        this.add(reply);
    }
};

ContentRepliesView.prototype.createContentView = function (content) {
    var ContentThreadView = require('thread');
    return new ContentThreadView({
        content: content,
        maxNestLevel: this._maxNestLevel-1,
        showMoreHeader: this._showMoreHeader
    });
};

ContentRepliesView.prototype.render = function () {
    ContentListView.prototype.render.call(this);

    if (this._showMoreHeader) {
        this.$el.find(this.showMoreElSelector).insertBefore(this.$listEl);
    }

    this._addReplies(this.content.replies);
};

module.exports = ContentRepliesView;
<|endoftext|>


<commit_before>/**
 * @name- follow.js
 *
 * @chill-
 *
 *
 * @description- Follow and unfollow fns for user
 *
 * @author- heartit pirates
 */
import { AsyncStorage } from 'react-native';

import * as actionCreators from '../../action_creators';

("use strict");

export async function unFollowUser(store, next, action) {
  try {
    await store.dispatch(actionCreators.startLoading());
    let jollyroger = await AsyncStorage.getItem("jollyroger");
    var unFollowReq = await fetch(
      `http://mri2189.badlee.com/follow.php?userid=${action.id}`,
      {
        method: "DELETE",
        headers: {
          Authorization: jollyroger
        }
      }
    );
    if (unFollowReq.status === 200 && unFollowReq.ok) {
      action.userID = action.id;
      next(action);
    }
  } catch (err) {
    console.log(err);
  } finally {
    await store.dispatch(actionCreators.finishLoading());
  }
}

export async function followUser(store, next, action) {
  try {
    await store.dispatch(actionCreators.startLoading());
    let jollyroger = await AsyncStorage.getItem("jollyroger");
    var followReq = await fetch(
      `http://mri2189.badlee.com/follow.php?userid=${action.id}`,
      {
        method: "POST",
        headers: {
          Authorization: jollyroger
        }
      }
    );
    if (followReq.status === 200 && followReq.ok) {
      action.userID = action.id;
      next(action);
    }
  } catch (err) {
    console.log(err);
  } finally {
    await store.dispatch(actionCreators.finishLoading());
  }
}
<commit_msg>added live update to state in follow<commit_after>/**
 * @name- follow.js
 *
 * @chill-
 *
 *
 * @description- Follow and unfollow fns for user
 *
 * @author- heartit pirates
 */
import { AsyncStorage } from 'react-native';

import * as actionCreators from '../../action_creators';

("use strict");

export async function unFollowUser(store, next, action) {
  try {
    action.userID = action.id;
    next(action);
    let jollyroger = await AsyncStorage.getItem("jollyroger");
    var unFollowReq = await fetch(
      `http://mri2189.badlee.com/follow.php?userid=${action.id}`,
      {
        method: "DELETE",
        headers: {
          Authorization: jollyroger
        }
      }
    );
    if (!unFollowReq.status === 200 || !unFollowReq.ok) {
      store.dispatch(actionCreators.followUser(action.id));
    }
  } catch (err) {
    console.log(err);
  } finally {
  }
}

export async function followUser(store, next, action) {
  try {
    action.userID = action.id;
    next(action);
    let jollyroger = await AsyncStorage.getItem("jollyroger");
    var followReq = await fetch(
      `http://mri2189.badlee.com/follow.php?userid=${action.id}`,
      {
        method: "POST",
        headers: {
          Authorization: jollyroger
        }
      }
    );
    if (!followReq.status === 200 || !followReq.ok) {
      store.dispatch(actionCreators.unFollowUser(action.id));
    }
  } catch (err) {
    console.log(err);
  } finally {
  }
}
<|endoftext|>


<commit_before>/*
 * L.Control.Zoom is used for the default zoom buttons on the map.
 */

L.Control.Zoom = L.Control.extend({
	options: {
		position: 'topleft',
		zoomInText: '+',
		zoomInTitle: 'Zoom in',
		zoomOutText: '-',
		zoomOutTitle: 'Zoom out'
	},

	onAdd: function (map) {
		var zoomName = 'leaflet-control-zoom',
		    container = L.DomUtil.create('div', zoomName + ' leaflet-bar');

		this._map = map;

		this._zoomInButton  = this._createButton(
		        this.options.zoomInText, this.options.zoomInTitle,
		        zoomName + '-in',  container, this._zoomIn,  this);
		this._zoomOutButton = this._createButton(
		        this.options.zoomOutText, this.options.zoomOutTitle,
		        zoomName + '-out', container, this._zoomOut, this);

		map.on('zoomend zoomlevelschange', this._updateDisabled, this);

		return container;
	},

	onRemove: function (map) {
		map.off('zoomend zoomlevelschange', this._updateDisabled, this);
	},

	_zoomIn: function (e) {
		this._map.zoomIn(e.shiftKey ? 3 : 1);
	},

	_zoomOut: function (e) {
		this._map.zoomOut(e.shiftKey ? 3 : 1);
	},

	_createButton: function (html, title, className, container, fn, context) {
		var link = L.DomUtil.create('a', className, container);
		link.innerHTML = html;
		link.href = '#';
		link.title = title;

		var stop = L.DomEvent.stopPropagation;

		L.DomEvent
		    .on(link, 'click', stop)
		    .on(link, 'mousedown', stop)
		    .on(link, 'dblclick', stop)
		    .on(link, 'click', L.DomEvent.preventDefault)
		    .on(link, 'click', fn, context);

		return link;
	},

	_updateDisabled: function () {
		var map = this._map,
			className = 'leaflet-disabled';

		L.DomUtil.removeClass(this._zoomInButton, className);
		L.DomUtil.removeClass(this._zoomOutButton, className);

		if (map._zoom === map.getMinZoom()) {
			L.DomUtil.addClass(this._zoomOutButton, className);
		}
		if (map._zoom === map.getMaxZoom()) {
			L.DomUtil.addClass(this._zoomInButton, className);
		}
	}
});

L.Map.mergeOptions({
	zoomControl: true
});

L.Map.addInitHook(function () {
	if (this.options.zoomControl) {
		this.zoomControl = new L.Control.Zoom();
		this.addControl(this.zoomControl);
	}
});

L.control.zoom = function (options) {
	return new L.Control.Zoom(options);
};

<commit_msg>fix zoom control not always reflecting disabled state initially, close #2083<commit_after>/*
 * L.Control.Zoom is used for the default zoom buttons on the map.
 */

L.Control.Zoom = L.Control.extend({
	options: {
		position: 'topleft',
		zoomInText: '+',
		zoomInTitle: 'Zoom in',
		zoomOutText: '-',
		zoomOutTitle: 'Zoom out'
	},

	onAdd: function (map) {
		var zoomName = 'leaflet-control-zoom',
		    container = L.DomUtil.create('div', zoomName + ' leaflet-bar');

		this._map = map;

		this._zoomInButton  = this._createButton(
		        this.options.zoomInText, this.options.zoomInTitle,
		        zoomName + '-in',  container, this._zoomIn,  this);
		this._zoomOutButton = this._createButton(
		        this.options.zoomOutText, this.options.zoomOutTitle,
		        zoomName + '-out', container, this._zoomOut, this);

		this._updateDisabled();
		map.on('zoomend zoomlevelschange', this._updateDisabled, this);

		return container;
	},

	onRemove: function (map) {
		map.off('zoomend zoomlevelschange', this._updateDisabled, this);
	},

	_zoomIn: function (e) {
		this._map.zoomIn(e.shiftKey ? 3 : 1);
	},

	_zoomOut: function (e) {
		this._map.zoomOut(e.shiftKey ? 3 : 1);
	},

	_createButton: function (html, title, className, container, fn, context) {
		var link = L.DomUtil.create('a', className, container);
		link.innerHTML = html;
		link.href = '#';
		link.title = title;

		var stop = L.DomEvent.stopPropagation;

		L.DomEvent
		    .on(link, 'click', stop)
		    .on(link, 'mousedown', stop)
		    .on(link, 'dblclick', stop)
		    .on(link, 'click', L.DomEvent.preventDefault)
		    .on(link, 'click', fn, context);

		return link;
	},

	_updateDisabled: function () {
		var map = this._map,
			className = 'leaflet-disabled';

		L.DomUtil.removeClass(this._zoomInButton, className);
		L.DomUtil.removeClass(this._zoomOutButton, className);

		if (map._zoom === map.getMinZoom()) {
			L.DomUtil.addClass(this._zoomOutButton, className);
		}
		if (map._zoom === map.getMaxZoom()) {
			L.DomUtil.addClass(this._zoomInButton, className);
		}
	}
});

L.Map.mergeOptions({
	zoomControl: true
});

L.Map.addInitHook(function () {
	if (this.options.zoomControl) {
		this.zoomControl = new L.Control.Zoom();
		this.addControl(this.zoomControl);
	}
});

L.control.zoom = function (options) {
	return new L.Control.Zoom(options);
};

<|endoftext|>


<commit_before>import Promise from 'bluebird';
import stripeFactory from 'stripe';
const PRICE_PER_HOOK = 0.02;
const stripe = stripeFactory(process.env.STRIPE_TOKEN);

import isLinkPaid from 'helpers/isLinkPaid';

// Given a number of links, return the "payment quantity" that is set within stripe.
export function getPaymentQuantityForLinks(linkCount) {
  return linkCount;
}

// Called when a paid link is added or changed
// Directly called by updatePaidLink below
export function changePaidLinkQuantity(User, user, amount) {
  if (user.customerId) {
    function createSubscription() {
      // create a subscription
      return stripe.subscriptions.create({
        customer: user.customerId,
        plan: 'premium_link',
        quantity: getPaymentQuantityForLinks(amount),
        metadata: {
          repos: amount,
        },
      }).then(subscription => {
        return User.update({_id: user._id}, {subscriptionId: subscription.id}).exec();
      });
    }

    // create or update subscription?
    return stripe.subscriptions.retrieve(user.subscriptionId).then(subsc => {
      if (subsc) {
        // update the subscription
        return stripe.subscriptions.update(user.subscriptionId, {
          quantity: getPaymentQuantityForLinks(amount),
          metadata: {
            repos: amount,
          },
        });
      } else {
        return createSubscription();
      }
    }).catch(createSubscription);
  } else {
    return Promise.reject(new Error('Payment info not specified.'));
  }
}

// A paid link is defined as:
// - A link with a private repo within.
// - A link that is also enabled.
export function updatePaidLinks(Link, User, user) {
  return Link.find({owner: user, enabled: true}).exec().then(links => {
    let paidLinks = links.map(link => isLinkPaid(user, link));

    return Promise.all(paidLinks).then(linksPaid => {
      return linksPaid.filter(f => f).length;
    }).then(linkCount => {
      if (user.customerId) {
        return changePaidLinkQuantity(User, user, linkCount);
      } else if (linkCount > 0) {
        throw new Error(`Cannot add a private repo for a user that doesn't have payment info.`);
      } else {
        return true; // No payment required.
      }
    });
  });
}

// A route to get subscription information for the logged-in user.
export function getSubscriptionInformation(req, res) {
  if (req.isAuthenticated()) {
    if (req.user.customerId && req.user.subscriptionId) {
      return stripe.subscriptions.retrieve(req.user.subscriptionId).then(subsc => {
        res.status(200).send({
          status: 'ok',
          paymentBlockQuantity: subsc.quantity,
          paymentAmount: subsc.quantity * (subsc.plan.amount / 100),
        });
      }).catch(err => {
        res.status(500).send({error: "Server error"});
        process.env.NODE_ENV !== 'test' && (() => {throw err})();
      });
    } else if (req.user.customerId) {
      // A user isn't paying anything and doesn't have a subscription associated with their user
      res.status(200).send({
        status: 'ok',
        paymentBlockQuantity: 0,
        paymentAmount: 0,
        note: 'No subscription on the authenticated user.'
      });
    } else {
      res.status(400).send({error: "No customer associated with this user."});
    }
  } else {
    res.status(403).send({error: 'Not authenticated.'});
  }
}

// Add a card to a user
export function addPaymentToUser(User, req, res) {
  if (req.isAuthenticated()) {
    if (req.body.source) {
      function createCustomer() {
        // create a customer
        return stripe.customers.create({
          source: req.body.source,
          email: req.body.email,
          metadata: {
            provider: req.user.provider,
            user: req.user.user,
          },
        }).then(customer => {
          return User.update({_id: req.user._id}, {customerId: customer.id}).exec();
        });
      }

      // create or update subscription?
      return stripe.customers.retrieve(req.user.customerId).then(customer => {
        if (customer) {
          // update the customer
          return stripe.customers.update(req.user.customerId, {
            source: req.body.source,
            email: req.body.email,
            metadata: {
              provider: req.user.provider,
              user: req.user.user,
            },
          });
        } else {
          return createCustomer();
        }
      }).catch(createCustomer).then(() => {
        res.status(200).send({status: 'ok'});
      }).catch(error => {
        if (error.message.indexOf('You cannot use a Stripe token more than once') === 0) {
          res.status(400).send({error: `You can't reuse payment source tokens!`});
        } else {
          return Promise.reject(error);
        }
      });
    } else {
      return Promise.reject(new Error('Payment source not provided in body.'));
    }
  } else {
    res.status(403).send({error: 'Not authenticated.'});
  }
}
<commit_msg>code: don't reject errors, throw them.<commit_after>import Promise from 'bluebird';
import stripeFactory from 'stripe';
const PRICE_PER_HOOK = 0.02;
const stripe = stripeFactory(process.env.STRIPE_TOKEN);

import isLinkPaid from 'helpers/isLinkPaid';

// Given a number of links, return the "payment quantity" that is set within stripe.
export function getPaymentQuantityForLinks(linkCount) {
  return linkCount;
}

// Called when a paid link is added or changed
// Directly called by updatePaidLink below
export function changePaidLinkQuantity(User, user, amount) {
  if (user.customerId) {
    function createSubscription() {
      // create a subscription
      return stripe.subscriptions.create({
        customer: user.customerId,
        plan: 'premium_link',
        quantity: getPaymentQuantityForLinks(amount),
        metadata: {
          repos: amount,
        },
      }).then(subscription => {
        return User.update({_id: user._id}, {subscriptionId: subscription.id}).exec();
      });
    }

    // create or update subscription?
    return stripe.subscriptions.retrieve(user.subscriptionId).then(subsc => {
      if (subsc) {
        // update the subscription
        return stripe.subscriptions.update(user.subscriptionId, {
          quantity: getPaymentQuantityForLinks(amount),
          metadata: {
            repos: amount,
          },
        });
      } else {
        return createSubscription();
      }
    }).catch(createSubscription);
  } else {
    return Promise.reject(new Error('Payment info not specified.'));
  }
}

// A paid link is defined as:
// - A link with a private repo within.
// - A link that is also enabled.
export function updatePaidLinks(Link, User, user) {
  return Link.find({owner: user, enabled: true}).exec().then(links => {
    let paidLinks = links.map(link => isLinkPaid(user, link));

    return Promise.all(paidLinks).then(linksPaid => {
      return linksPaid.filter(f => f).length;
    }).then(linkCount => {
      if (user.customerId) {
        return changePaidLinkQuantity(User, user, linkCount);
      } else if (linkCount > 0) {
        throw new Error(`Cannot add a private repo for a user that doesn't have payment info.`);
      } else {
        return true; // No payment required.
      }
    });
  });
}

// A route to get subscription information for the logged-in user.
export function getSubscriptionInformation(req, res) {
  if (req.isAuthenticated()) {
    if (req.user.customerId && req.user.subscriptionId) {
      return stripe.subscriptions.retrieve(req.user.subscriptionId).then(subsc => {
        res.status(200).send({
          status: 'ok',
          paymentBlockQuantity: subsc.quantity,
          paymentAmount: subsc.quantity * (subsc.plan.amount / 100),
        });
      }).catch(err => {
        res.status(500).send({error: "Server error"});
        process.env.NODE_ENV !== 'test' && (() => {throw err})();
      });
    } else if (req.user.customerId) {
      // A user isn't paying anything and doesn't have a subscription associated with their user
      res.status(200).send({
        status: 'ok',
        paymentBlockQuantity: 0,
        paymentAmount: 0,
        note: 'No subscription on the authenticated user.'
      });
    } else {
      res.status(400).send({error: "No customer associated with this user."});
    }
  } else {
    res.status(403).send({error: 'Not authenticated.'});
  }
}

// Add a card to a user
export function addPaymentToUser(User, req, res) {
  if (req.isAuthenticated()) {
    if (req.body.source) {
      function createCustomer() {
        // create a customer
        return stripe.customers.create({
          source: req.body.source,
          email: req.body.email,
          metadata: {
            provider: req.user.provider,
            user: req.user.user,
          },
        }).then(customer => {
          return User.update({_id: req.user._id}, {customerId: customer.id}).exec();
        });
      }

      // create or update subscription?
      return stripe.customers.retrieve(req.user.customerId).then(customer => {
        if (customer) {
          // update the customer
          return stripe.customers.update(req.user.customerId, {
            source: req.body.source,
            email: req.body.email,
            metadata: {
              provider: req.user.provider,
              user: req.user.user,
            },
          });
        } else {
          return createCustomer();
        }
      }).catch(createCustomer).then(() => {
        res.status(200).send({status: 'ok'});
      }).catch(error => {
        if (error.message.indexOf('You cannot use a Stripe token more than once') === 0) {
          res.status(400).send({error: `You can't reuse payment source tokens!`});
        } else {
          res.status(500).send({error: "Server error"});
          process.env.NODE_ENV !== 'test' && (() => {throw err})();
        }
      });
    } else {
      return Promise.reject(new Error('Payment source not provided in body.'));
    }
  } else {
    res.status(403).send({error: 'Not authenticated.'});
  }
}
<|endoftext|>


<commit_before>'use strict'

const vueLoaderConfig = require('./vue-loader.config')
const { defaults } = require('lodash')
const { join } = require('path')
const { urlJoin } = require('../../utils')

/*
|--------------------------------------------------------------------------
| Webpack Shared Config
|
| This is the config which is extended by the server and client
| webpack config files
|--------------------------------------------------------------------------
*/
module.exports = function () {
  const nodeModulesDir = join(__dirname, '..', '..', '..', 'node_modules')
  let config = {
    devtool: 'source-map',
    entry: {
      vendor: ['vue', 'vue-router', 'vue-meta', 'es6-promise', 'es6-object-assign']
    },
    output: {
      publicPath: urlJoin(this.options.router.base, '/_nuxt/')
    },
    resolve: {
      // Disable for now
      alias: {
        'static': join(this.dir, 'static'), // use in template with
        '~static': join(this.dir, 'static'),
        'assets': join(this.dir, 'assets'), // use in template with
        '~assets': join(this.dir, 'assets'),
        '~plugins': join(this.dir, 'plugins'),
        '~store': join(this.dir, 'store'),
        '~router': join(this.dir, '.nuxt/router'),
        '~pages': join(this.dir, 'pages'),
        '~components': join(this.dir, 'components')
      },
      modules: [
        nodeModulesDir,
        join(this.dir, 'node_modules')
      ]
    },
    resolveLoader: {
      modules: [
        nodeModulesDir,
        join(this.dir, 'node_modules')
      ]
    },
    module: {
      rules: [
        {
          test: /\.vue$/,
          loader: 'vue-loader',
          query: vueLoaderConfig.call(this)
        },
        {
          test: /\.js$/,
          loader: 'babel-loader',
          exclude: /node_modules/,
          query: defaults(this.options.build.babel, {
            presets: [
              ['es2015', { modules: false }],
              'stage-2'
            ],
            cacheDirectory: !!this.dev
          })
        }
      ]
    },
    plugins: this.options.build.plugins
  }
  // Add nuxt build loaders (can be configured in nuxt.config.js)
  config.module.rules = config.module.rules.concat(this.options.build.loaders)
  // Return config
  return config
}
<commit_msg>Add ~ alias<commit_after>'use strict'

const vueLoaderConfig = require('./vue-loader.config')
const { defaults } = require('lodash')
const { join } = require('path')
const { urlJoin } = require('../../utils')

/*
|--------------------------------------------------------------------------
| Webpack Shared Config
|
| This is the config which is extended by the server and client
| webpack config files
|--------------------------------------------------------------------------
*/
module.exports = function () {
  const nodeModulesDir = join(__dirname, '..', '..', '..', 'node_modules')
  let config = {
    devtool: 'source-map',
    entry: {
      vendor: ['vue', 'vue-router', 'vue-meta', 'es6-promise', 'es6-object-assign']
    },
    output: {
      publicPath: urlJoin(this.options.router.base, '/_nuxt/')
    },
    resolve: {
      // Disable for now
      alias: {
        '~': join(this.dir),
        'static': join(this.dir, 'static'), // use in template with
        '~static': join(this.dir, 'static'),
        'assets': join(this.dir, 'assets'), // use in template with
        '~assets': join(this.dir, 'assets'),
        '~plugins': join(this.dir, 'plugins'),
        '~store': join(this.dir, 'store'),
        '~router': join(this.dir, '.nuxt/router'),
        '~pages': join(this.dir, 'pages'),
        '~components': join(this.dir, 'components')
      },
      modules: [
        nodeModulesDir,
        join(this.dir, 'node_modules')
      ]
    },
    resolveLoader: {
      modules: [
        nodeModulesDir,
        join(this.dir, 'node_modules')
      ]
    },
    module: {
      rules: [
        {
          test: /\.vue$/,
          loader: 'vue-loader',
          query: vueLoaderConfig.call(this)
        },
        {
          test: /\.js$/,
          loader: 'babel-loader',
          exclude: /node_modules/,
          query: defaults(this.options.build.babel, {
            presets: [
              ['es2015', { modules: false }],
              'stage-2'
            ],
            cacheDirectory: !!this.dev
          })
        }
      ]
    },
    plugins: this.options.build.plugins
  }
  // Add nuxt build loaders (can be configured in nuxt.config.js)
  config.module.rules = config.module.rules.concat(this.options.build.loaders)
  // Return config
  return config
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

// @flow

import React, { Component } from "react";
import { connect } from "react-redux";

import Reps from "devtools-reps";
const {
  REPS: { Rep },
  MODE,
  ObjectInspector,
  ObjectInspectorUtils
} = Reps;

const {
  createNode,
  getChildren,
  getValue,
  nodeIsPrimitive,
  NODE_TYPES
} = ObjectInspectorUtils.node;
const { loadItemProperties } = ObjectInspectorUtils.loadProperties;

import actions from "../../../actions";
import { getAllPopupObjectProperties } from "../../../selectors";
import Popover from "../../shared/Popover";
import PreviewFunction from "../../shared/PreviewFunction";
import { markText } from "../../../utils/editor";
import Svg from "../../shared/Svg";
import { createObjectClient } from "../../../client/firefox";

import "./Popup.css";

import type { EditorRange } from "../../../utils/editor/types";

type PopupValue = Object | null;
type Props = {
  setPopupObjectProperties: (Object, Object) => void,
  addExpression: (string, ?Object) => void,
  popupObjectProperties: Object,
  popoverPos: Object,
  value: PopupValue,
  expression: string,
  onClose: () => void,
  range: EditorRange,
  editor: any,
  editorRef: ?HTMLDivElement,
  selectSourceURL: (string, Object) => void,
  openLink: string => void,
  extra: Object
};

export class Popup extends Component<Props> {
  marker: any;
  pos: any;

  async componentWillMount() {
    const {
      value,
      setPopupObjectProperties,
      popupObjectProperties
    } = this.props;

    const root = this.getRoot();

    if (
      !nodeIsPrimitive(root) &&
      value &&
      value.actor &&
      !popupObjectProperties[value.actor]
    ) {
      const onLoadItemProperties = loadItemProperties(root, createObjectClient);
      if (onLoadItemProperties !== null) {
        const properties = await onLoadItemProperties;
        setPopupObjectProperties(root.contents.value, properties);
      }
    }
  }

  componentDidMount() {
    const { value, editor, range } = this.props;

    if (!value || !value.type == "object") {
      return;
    }

    this.marker = markText(editor, "preview-selection", range);
  }

  componentWillUnmount() {
    if (this.marker) {
      this.marker.clear();
    }
  }

  onMouseLeave = (e: SyntheticMouseEvent<HTMLDivElement>) => {
    const relatedTarget: Element = (e.relatedTarget: any);

    if (
      relatedTarget &&
      (relatedTarget.classList.contains("popover") ||
        relatedTarget.classList.contains("debug-expression") ||
        relatedTarget.classList.contains("editor-mount"))
    ) {
      return;
    }

    this.props.onClose();
  };

  getRoot() {
    const { expression, value, extra } = this.props;

    let rootValue = value;
    if (extra.immutable) {
      rootValue = extra.immutable.entries;
    }

    return createNode({
      name: expression,
      path: expression,
      contents: { value: rootValue }
    });
  }

  getChildren() {
    const { popupObjectProperties } = this.props;

    const root = this.getRoot();
    const value = getValue(root);
    const actor = value ? value.actor : null;
    const loadedRootProperties = popupObjectProperties[actor];
    if (!loadedRootProperties) {
      return null;
    }

    const children = getChildren({
      item: root,
      loadedProperties: new Map([[root.path, loadedRootProperties]])
    });

    if (children.length > 0) {
      return children;
    }

    return null;
  }

  renderFunctionPreview() {
    const { selectSourceURL, value } = this.props;

    if (!value) {
      return null;
    }

    const { location } = value;
    return (

 selectSourceURL(location.url, { line: location.line })}
      >
        <PreviewFunction func={value} />

    );
  }

  renderReact(react: Object) {
    const reactHeader = react.displayName || "React Component";

    return (

        <Svg name="react" className="logo" />

{reactHeader}

    );
  }

  renderImmutable(immutable: Object) {
    const immutableHeader = immutable.type || "Immutable";

    return (

        <Svg name="immutable" className="logo" />

{immutableHeader}

    );
  }

  renderObjectPreview() {
    const { extra } = this.props;
    const root = this.getRoot();

    if (nodeIsPrimitive(root)) {
      return null;
    }

    let roots = this.getChildren();
    if (!Array.isArray(roots) || roots.length === 0) {
      return null;
    }

    let header = null;
    if (extra.immutable) {
      header = this.renderImmutable(extra.immutable);
      roots = roots.filter(r => r.type != NODE_TYPES.PROTOTYPE);
    }

    if (extra.react) {
      header = this.renderReact(extra.react);
      roots = roots.filter(r => ["state", "props"].includes(r.name));
    }

    return (

        {header}
        {this.renderObjectInspector(roots)}

    );
  }

  renderSimplePreview(value: any) {
    const { openLink } = this.props;
    return (

        {Rep({
          object: value,
          mode: MODE.LONG,
          openLink
        })}

    );
  }

  renderObjectInspector(roots: Array<Object>) {
    const { openLink } = this.props;

    return (
      <ObjectInspector
        roots={roots}
        autoExpandDepth={0}
        disableWrap={true}
        focusable={false}
        openLink={openLink}
        createObjectClient={grip => createObjectClient(grip)}
      />
    );
  }

  renderPreview() {
    // We don't have to check and
    // return on `false`, `""`, `0`, `undefined` etc,
    // these falsy simple typed value because we want to
    // do `renderSimplePreview` on these values below.
    const { value } = this.props;

    if (value && value.class === "Function") {
      return this.renderFunctionPreview();
    }

    if (value && value.type === "object") {
      return
{this.renderObjectPreview()}
;
    }

    return this.renderSimplePreview(value);
  }

  getPreviewType(value: any) {
    if (
      typeof value == "number" ||
      typeof value == "boolean" ||
      (typeof value == "string" && value.length < 10) ||
      (typeof value == "number" && value.toString().length < 10) ||
      value.type == "null" ||
      value.type == "undefined" ||
      value.class === "Function"
    ) {
      return "tooltip";
    }

    return "popover";
  }

  render() {
    const { popoverPos, value, editorRef } = this.props;
    const type = this.getPreviewType(value);

    if (value && value.type === "object" && !this.getChildren()) {
      return null;
    }

    return (
      <Popover
        targetPosition={popoverPos}
        onMouseLeave={this.onMouseLeave}
        type={type}
        editorRef={editorRef}
      >
        {this.renderPreview()}
      </Popover>
    );
  }
}

const mapStateToProps = state => ({
  popupObjectProperties: getAllPopupObjectProperties(state)
});

const {
  addExpression,
  selectSourceURL,
  selectLocation,
  setPopupObjectProperties,
  openLink
} = actions;

const mapDispatchToProps = {
  addExpression,
  selectSourceURL,
  selectLocation,
  setPopupObjectProperties,
  openLink
};

export default connect(mapStateToProps, mapDispatchToProps)(Popup);
<commit_msg>Fix #6201 - Prevent relatedTarget.classList error (#6309)<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

// @flow

import React, { Component } from "react";
import { connect } from "react-redux";

import Reps from "devtools-reps";
const {
  REPS: { Rep },
  MODE,
  ObjectInspector,
  ObjectInspectorUtils
} = Reps;

const {
  createNode,
  getChildren,
  getValue,
  nodeIsPrimitive,
  NODE_TYPES
} = ObjectInspectorUtils.node;
const { loadItemProperties } = ObjectInspectorUtils.loadProperties;

import actions from "../../../actions";
import { getAllPopupObjectProperties } from "../../../selectors";
import Popover from "../../shared/Popover";
import PreviewFunction from "../../shared/PreviewFunction";
import { markText } from "../../../utils/editor";
import Svg from "../../shared/Svg";
import { createObjectClient } from "../../../client/firefox";

import "./Popup.css";

import type { EditorRange } from "../../../utils/editor/types";

type PopupValue = Object | null;
type Props = {
  setPopupObjectProperties: (Object, Object) => void,
  addExpression: (string, ?Object) => void,
  popupObjectProperties: Object,
  popoverPos: Object,
  value: PopupValue,
  expression: string,
  onClose: () => void,
  range: EditorRange,
  editor: any,
  editorRef: ?HTMLDivElement,
  selectSourceURL: (string, Object) => void,
  openLink: string => void,
  extra: Object
};

export class Popup extends Component<Props> {
  marker: any;
  pos: any;

  async componentWillMount() {
    const {
      value,
      setPopupObjectProperties,
      popupObjectProperties
    } = this.props;

    const root = this.getRoot();

    if (
      !nodeIsPrimitive(root) &&
      value &&
      value.actor &&
      !popupObjectProperties[value.actor]
    ) {
      const onLoadItemProperties = loadItemProperties(root, createObjectClient);
      if (onLoadItemProperties !== null) {
        const properties = await onLoadItemProperties;
        setPopupObjectProperties(root.contents.value, properties);
      }
    }
  }

  componentDidMount() {
    const { value, editor, range } = this.props;

    if (!value || !value.type == "object") {
      return;
    }

    this.marker = markText(editor, "preview-selection", range);
  }

  componentWillUnmount() {
    if (this.marker) {
      this.marker.clear();
    }
  }

  onMouseLeave = (e: SyntheticMouseEvent<HTMLDivElement>) => {
    const relatedTarget: Element = (e.relatedTarget: any);

    if (
      relatedTarget &&
      relatedTarget.classList &&
      (relatedTarget.classList.contains("popover") ||
        relatedTarget.classList.contains("debug-expression") ||
        relatedTarget.classList.contains("editor-mount"))
    ) {
      return;
    }

    this.props.onClose();
  };

  getRoot() {
    const { expression, value, extra } = this.props;

    let rootValue = value;
    if (extra.immutable) {
      rootValue = extra.immutable.entries;
    }

    return createNode({
      name: expression,
      path: expression,
      contents: { value: rootValue }
    });
  }

  getChildren() {
    const { popupObjectProperties } = this.props;

    const root = this.getRoot();
    const value = getValue(root);
    const actor = value ? value.actor : null;
    const loadedRootProperties = popupObjectProperties[actor];
    if (!loadedRootProperties) {
      return null;
    }

    const children = getChildren({
      item: root,
      loadedProperties: new Map([[root.path, loadedRootProperties]])
    });

    if (children.length > 0) {
      return children;
    }

    return null;
  }

  renderFunctionPreview() {
    const { selectSourceURL, value } = this.props;

    if (!value) {
      return null;
    }

    const { location } = value;
    return (

 selectSourceURL(location.url, { line: location.line })}
      >
        <PreviewFunction func={value} />

    );
  }

  renderReact(react: Object) {
    const reactHeader = react.displayName || "React Component";

    return (

        <Svg name="react" className="logo" />

{reactHeader}

    );
  }

  renderImmutable(immutable: Object) {
    const immutableHeader = immutable.type || "Immutable";

    return (

        <Svg name="immutable" className="logo" />

{immutableHeader}

    );
  }

  renderObjectPreview() {
    const { extra } = this.props;
    const root = this.getRoot();

    if (nodeIsPrimitive(root)) {
      return null;
    }

    let roots = this.getChildren();
    if (!Array.isArray(roots) || roots.length === 0) {
      return null;
    }

    let header = null;
    if (extra.immutable) {
      header = this.renderImmutable(extra.immutable);
      roots = roots.filter(r => r.type != NODE_TYPES.PROTOTYPE);
    }

    if (extra.react) {
      header = this.renderReact(extra.react);
      roots = roots.filter(r => ["state", "props"].includes(r.name));
    }

    return (

        {header}
        {this.renderObjectInspector(roots)}

    );
  }

  renderSimplePreview(value: any) {
    const { openLink } = this.props;
    return (

        {Rep({
          object: value,
          mode: MODE.LONG,
          openLink
        })}

    );
  }

  renderObjectInspector(roots: Array<Object>) {
    const { openLink } = this.props;

    return (
      <ObjectInspector
        roots={roots}
        autoExpandDepth={0}
        disableWrap={true}
        focusable={false}
        openLink={openLink}
        createObjectClient={grip => createObjectClient(grip)}
      />
    );
  }

  renderPreview() {
    // We don't have to check and
    // return on `false`, `""`, `0`, `undefined` etc,
    // these falsy simple typed value because we want to
    // do `renderSimplePreview` on these values below.
    const { value } = this.props;

    if (value && value.class === "Function") {
      return this.renderFunctionPreview();
    }

    if (value && value.type === "object") {
      return
{this.renderObjectPreview()}
;
    }

    return this.renderSimplePreview(value);
  }

  getPreviewType(value: any) {
    if (
      typeof value == "number" ||
      typeof value == "boolean" ||
      (typeof value == "string" && value.length < 10) ||
      (typeof value == "number" && value.toString().length < 10) ||
      value.type == "null" ||
      value.type == "undefined" ||
      value.class === "Function"
    ) {
      return "tooltip";
    }

    return "popover";
  }

  render() {
    const { popoverPos, value, editorRef } = this.props;
    const type = this.getPreviewType(value);

    if (value && value.type === "object" && !this.getChildren()) {
      return null;
    }

    return (
      <Popover
        targetPosition={popoverPos}
        onMouseLeave={this.onMouseLeave}
        type={type}
        editorRef={editorRef}
      >
        {this.renderPreview()}
      </Popover>
    );
  }
}

const mapStateToProps = state => ({
  popupObjectProperties: getAllPopupObjectProperties(state)
});

const {
  addExpression,
  selectSourceURL,
  selectLocation,
  setPopupObjectProperties,
  openLink
} = actions;

const mapDispatchToProps = {
  addExpression,
  selectSourceURL,
  selectLocation,
  setPopupObjectProperties,
  openLink
};

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


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

// @flow

import React, { Component } from "react";
import { connect } from "react-redux";

import Reps from "devtools-reps";
const { REPS: { Rep }, MODE, ObjectInspector, ObjectInspectorUtils } = Reps;

const {
  createNode,
  getChildren,
  getValue,
  nodeIsPrimitive
} = ObjectInspectorUtils.node;
const { loadItemProperties } = ObjectInspectorUtils.loadProperties;

import actions from "../../../actions";
import { getAllPopupObjectProperties } from "../../../selectors";
import Popover from "../../shared/Popover";
import PreviewFunction from "../../shared/PreviewFunction";
import { markText } from "../../../utils/editor";
import { isReactComponent, isImmutable } from "../../../utils/preview";
import Svg from "../../shared/Svg";
import { createObjectClient } from "../../../client/firefox";

import "./Popup.css";

import type { EditorRange } from "../../../utils/editor/types";
import type { Node } from "../../../utils/sources-tree/types";

type PopupValue = Object | null;
type Props = {
  setPopupObjectProperties: (Object, Object) => void,
  addExpression: (string, ?Object) => void,
  popupObjectProperties: Object,
  popoverPos: Object,
  value: PopupValue,
  expression: string,
  onClose: () => void,
  range: EditorRange,
  editor: any,
  editorRef: ?HTMLDivElement,
  selectSourceURL: (string, Object) => void,
  openLink: string => void,
  extra: Object
};

export class Popup extends Component<Props> {
  marker: any;
  pos: any;

  async componentWillMount() {
    const {
      value,
      expression,
      setPopupObjectProperties,
      popupObjectProperties
    } = this.props;
    const root = createNode(null, expression, expression, { value });

    if (
      !nodeIsPrimitive(root) &&
      value &&
      value.actor &&
      !popupObjectProperties[value.actor]
    ) {
      const onLoadItemProperties = loadItemProperties(root, createObjectClient);
      if (onLoadItemProperties !== null) {
        const properties = await onLoadItemProperties;
        setPopupObjectProperties(value, properties);
      }
    }
  }

  componentDidMount() {
    const { value, editor, range } = this.props;

    if (!value || !value.type == "object") {
      return;
    }

    this.marker = markText(editor, "preview-selection", range);
  }

  componentWillUnmount() {
    if (this.marker) {
      this.marker.clear();
    }
  }

  getRoot() {
    const { expression, value } = this.props;

    return {
      name: expression,
      path: expression,
      contents: { value }
    };
  }

  getChildren() {
    const { popupObjectProperties } = this.props;

    const root = this.getRoot();
    const value = getValue(root);
    const actor = value ? value.actor : null;
    const loadedRootProperties = popupObjectProperties[actor];
    if (!loadedRootProperties) {
      return null;
    }

    const children = getChildren({
      item: root,
      loadedProperties: new Map([[root.path, loadedRootProperties]])
    });

    if (children.length > 0) {
      return children;
    }

    return null;
  }

  renderFunctionPreview() {
    const { selectSourceURL, value } = this.props;

    if (!value) {
      return null;
    }

    const { location } = value;
    return (

 selectSourceURL(location.url, { line: location.line })}
      >
        <PreviewFunction func={value} />

    );
  }

  renderReact(react: Object, roots: Array<Node>) {
    const reactHeader = react.displayName || "React Component";

    const header = (

{reactHeader}

    );

    roots = roots.filter(r => ["state", "props"].includes(r.name));
    return (

        {header}
        {this.renderObjectInspector(roots)}

    );
  }

  renderImmutable(immutable: Object) {
    const immutableHeader = immutable.type || "Immutable";

    const header = (

        <Svg name="immutable" className="immutable-logo" />

{immutableHeader}

    );

    const roots = [
      createNode(null, "entries", "entries", { value: immutable.entries })
    ];

    return (

        {header}
        {this.renderObjectInspector(roots)}

    );
  }

  renderObjectPreview() {
    const root = this.getRoot();

    if (nodeIsPrimitive(root)) {
      return null;
    }

    const roots = this.getChildren();
    if (!Array.isArray(roots) || roots.length === 0) {
      return null;
    }

    const { extra: { react, immutable } } = this.props;
    const grip = getValue(root);

    if (isReactComponent(grip)) {
      return this.renderReact(react, roots);
    }

    if (isImmutable(grip)) {
      return this.renderImmutable(immutable);
    }

    return (

{this.renderObjectInspector(roots)}

    );
  }

  renderSimplePreview(value: Object) {
    const { openLink } = this.props;
    return (

        {Rep({
          object: value,
          mode: MODE.LONG,
          openLink
        })}

    );
  }

  renderObjectInspector(roots: Array<Object>) {
    const { openLink } = this.props;

    return (
      <ObjectInspector
        roots={roots}
        autoExpandDepth={0}
        disableWrap={true}
        disabledFocus={true}
        openLink={openLink}
        createObjectClient={grip => createObjectClient(grip)}
      />
    );
  }

  renderPreview() {
    const { value } = this.props;
    if (!value) {
      return null;
    }

    if (value.class === "Function") {
      return this.renderFunctionPreview();
    }

    if (value.type === "object") {
      return
{this.renderObjectPreview()}
;
    }

    return this.renderSimplePreview(value);
  }

  getPreviewType(value: any) {
    if (
      typeof value == "number" ||
      typeof value == "boolean" ||
      (typeof value == "string" && value.length < 10) ||
      (typeof value == "number" && value.toString().length < 10) ||
      value.type == "null" ||
      value.type == "undefined" ||
      value.class === "Function"
    ) {
      return "tooltip";
    }

    return "popover";
  }

  render() {
    const { popoverPos, onClose, value, editorRef } = this.props;
    const type = this.getPreviewType(value);

    if (value && value.type === "object" && !this.getChildren()) {
      return null;
    }

    return (
      <Popover
        targetPosition={popoverPos}
        onMouseLeave={onClose}
        type={type}
        editorRef={editorRef}
      >
        {this.renderPreview()}
      </Popover>
    );
  }
}

const {
  addExpression,
  selectSourceURL,
  selectLocation,
  setPopupObjectProperties,
  openLink
} = actions;

export default connect(
  state => ({
    popupObjectProperties: getAllPopupObjectProperties(state)
  }),
  {
    addExpression,
    selectSourceURL,
    selectLocation,
    setPopupObjectProperties,
    openLink
  }
)(Popup);
<commit_msg>[Preview] Show empty values (#5279)<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */

// @flow

import React, { Component } from "react";
import { connect } from "react-redux";

import Reps from "devtools-reps";
const { REPS: { Rep }, MODE, ObjectInspector, ObjectInspectorUtils } = Reps;

const {
  createNode,
  getChildren,
  getValue,
  nodeIsPrimitive
} = ObjectInspectorUtils.node;
const { loadItemProperties } = ObjectInspectorUtils.loadProperties;

import actions from "../../../actions";
import { getAllPopupObjectProperties } from "../../../selectors";
import Popover from "../../shared/Popover";
import PreviewFunction from "../../shared/PreviewFunction";
import { markText } from "../../../utils/editor";
import { isReactComponent, isImmutable } from "../../../utils/preview";
import Svg from "../../shared/Svg";
import { createObjectClient } from "../../../client/firefox";

import "./Popup.css";

import type { EditorRange } from "../../../utils/editor/types";
import type { Node } from "../../../utils/sources-tree/types";

type PopupValue = Object | null;
type Props = {
  setPopupObjectProperties: (Object, Object) => void,
  addExpression: (string, ?Object) => void,
  popupObjectProperties: Object,
  popoverPos: Object,
  value: PopupValue,
  expression: string,
  onClose: () => void,
  range: EditorRange,
  editor: any,
  editorRef: ?HTMLDivElement,
  selectSourceURL: (string, Object) => void,
  openLink: string => void,
  extra: Object
};

export class Popup extends Component<Props> {
  marker: any;
  pos: any;

  async componentWillMount() {
    const {
      value,
      expression,
      setPopupObjectProperties,
      popupObjectProperties
    } = this.props;
    const root = createNode(null, expression, expression, { value });

    if (
      !nodeIsPrimitive(root) &&
      value &&
      value.actor &&
      !popupObjectProperties[value.actor]
    ) {
      const onLoadItemProperties = loadItemProperties(root, createObjectClient);
      if (onLoadItemProperties !== null) {
        const properties = await onLoadItemProperties;
        setPopupObjectProperties(value, properties);
      }
    }
  }

  componentDidMount() {
    const { value, editor, range } = this.props;

    if (!value || !value.type == "object") {
      return;
    }

    this.marker = markText(editor, "preview-selection", range);
  }

  componentWillUnmount() {
    if (this.marker) {
      this.marker.clear();
    }
  }

  getRoot() {
    const { expression, value } = this.props;

    return {
      name: expression,
      path: expression,
      contents: { value }
    };
  }

  getChildren() {
    const { popupObjectProperties } = this.props;

    const root = this.getRoot();
    const value = getValue(root);
    const actor = value ? value.actor : null;
    const loadedRootProperties = popupObjectProperties[actor];
    if (!loadedRootProperties) {
      return null;
    }

    const children = getChildren({
      item: root,
      loadedProperties: new Map([[root.path, loadedRootProperties]])
    });

    if (children.length > 0) {
      return children;
    }

    return null;
  }

  renderFunctionPreview() {
    const { selectSourceURL, value } = this.props;

    if (!value) {
      return null;
    }

    const { location } = value;
    return (

 selectSourceURL(location.url, { line: location.line })}
      >
        <PreviewFunction func={value} />

    );
  }

  renderReact(react: Object, roots: Array<Node>) {
    const reactHeader = react.displayName || "React Component";

    const header = (

{reactHeader}

    );

    roots = roots.filter(r => ["state", "props"].includes(r.name));
    return (

        {header}
        {this.renderObjectInspector(roots)}

    );
  }

  renderImmutable(immutable: Object) {
    const immutableHeader = immutable.type || "Immutable";

    const header = (

        <Svg name="immutable" className="immutable-logo" />

{immutableHeader}

    );

    const roots = [
      createNode(null, "entries", "entries", { value: immutable.entries })
    ];

    return (

        {header}
        {this.renderObjectInspector(roots)}

    );
  }

  renderObjectPreview() {
    const root = this.getRoot();

    if (nodeIsPrimitive(root)) {
      return null;
    }

    const roots = this.getChildren();
    if (!Array.isArray(roots) || roots.length === 0) {
      return null;
    }

    const { extra: { react, immutable } } = this.props;
    const grip = getValue(root);

    if (isReactComponent(grip)) {
      return this.renderReact(react, roots);
    }

    if (isImmutable(grip)) {
      return this.renderImmutable(immutable);
    }

    return (

{this.renderObjectInspector(roots)}

    );
  }

  renderSimplePreview(value: any) {
    const { openLink } = this.props;
    return (

        {Rep({
          object: value,
          mode: MODE.LONG,
          openLink
        })}

    );
  }

  renderObjectInspector(roots: Array<Object>) {
    const { openLink } = this.props;

    return (
      <ObjectInspector
        roots={roots}
        autoExpandDepth={0}
        disableWrap={true}
        disabledFocus={true}
        openLink={openLink}
        createObjectClient={grip => createObjectClient(grip)}
      />
    );
  }

  renderPreview() {
    // We don't have to check and
    // return on `false`, `""`, `0`, `undefined` etc,
    // these falsy simple typed value because we want to
    // do `renderSimplePreview` on these values below.
    const { value } = this.props;

    if (value && value.class === "Function") {
      return this.renderFunctionPreview();
    }

    if (value && value.type === "object") {
      return
{this.renderObjectPreview()}
;
    }

    return this.renderSimplePreview(value);
  }

  getPreviewType(value: any) {
    if (
      typeof value == "number" ||
      typeof value == "boolean" ||
      (typeof value == "string" && value.length < 10) ||
      (typeof value == "number" && value.toString().length < 10) ||
      value.type == "null" ||
      value.type == "undefined" ||
      value.class === "Function"
    ) {
      return "tooltip";
    }

    return "popover";
  }

  render() {
    const { popoverPos, onClose, value, editorRef } = this.props;
    const type = this.getPreviewType(value);

    if (value && value.type === "object" && !this.getChildren()) {
      return null;
    }

    return (
      <Popover
        targetPosition={popoverPos}
        onMouseLeave={onClose}
        type={type}
        editorRef={editorRef}
      >
        {this.renderPreview()}
      </Popover>
    );
  }
}

const {
  addExpression,
  selectSourceURL,
  selectLocation,
  setPopupObjectProperties,
  openLink
} = actions;

export default connect(
  state => ({
    popupObjectProperties: getAllPopupObjectProperties(state)
  }),
  {
    addExpression,
    selectSourceURL,
    selectLocation,
    setPopupObjectProperties,
    openLink
  }
)(Popup);
<|endoftext|>