<commit_before>/*

### withList

Paginated items container

Options:

  - queryName: an arbitrary name for the query
  - collection: the collection to fetch the documents from
  - fragment: the fragment that defines which properties to fetch
  - fragmentName: the name of the fragment, passed to getFragment
  - limit: the number of documents to show initially
  - pollInterval: how often the data should be updated, in ms (set to 0 to disable polling)

Props Received:

  - terms: an object that defines which documents to fetch

Terms object can have the following properties:

  - view: String
  - userId: String
  - cat: String
  - date: String
  - after: String
  - before: String
  - enableCache: Boolean
  - listId: String
  - query: String # search query
  - postId: String
  - limit: String

*/

import React, { PropTypes, Component } from 'react';
import { graphql } from 'react-apollo';
import gql from 'graphql-tag';
import update from 'immutability-helper';
import { getFragment, getFragmentName } from 'meteor/vulcan:core';
import Mingo from 'mingo';
import { compose, withState } from 'recompose';
import { withApollo } from 'react-apollo';

const withList = (options) => {

  const { collection, limit = 10, pollInterval = 20000 } = options,
        queryName = options.queryName || `${collection.options.collectionName}ListQuery`,
        fragment = options.fragment || getFragment(options.fragmentName),
        fragmentName = getFragmentName(fragment),
        listResolverName = collection.options.resolvers.list && collection.options.resolvers.list.name,
        totalResolverName = collection.options.resolvers.total && collection.options.resolvers.total.name;

  return compose(

    // wrap component with Apollo HoC to give it access to the store
    withApollo,

    // wrap component with HoC that manages the terms object via its state
    withState('paginationTerms', 'setPaginationTerms', props => {

      // either get initial limit from options, or default to settings
      const paginationTerms = {
        limit,
        itemsPerPage: limit,
      };

      return paginationTerms;
    }),

    // wrap component with graphql HoC
    graphql(

      // build graphql query from options
      gql`
        query ${queryName}($terms: JSON) {
          ${totalResolverName}(terms: $terms)
          ${listResolverName}(terms: $terms) {
            __typename
            ...${fragmentName}
          }
        }
        ${fragment}
      `,

      {
        alias: 'withList',

        // graphql query options
        options({terms, paginationTerms, client: apolloClient}) {
          const mergedTerms = {...terms, ...paginationTerms};
          return {
            variables: {
              terms: mergedTerms,
            },
            // note: pollInterval can be set to 0 to disable polling (20s by default)
            pollInterval,
            reducer: (previousResults, action) => {

              // see queryReducer function defined below
              return queryReducer(previousResults, action, collection, mergedTerms, listResolverName, totalResolverName, queryName, apolloClient);

            },
          };
        },

        // define props returned by graphql HoC
        props(props) {

          const refetch = props.data.refetch,
                // results = Utils.convertDates(collection, props.data[listResolverName]),
                results = props.data[listResolverName],
                totalCount = props.data[totalResolverName],
                networkStatus = props.data.networkStatus,
                error = props.data.error;

          if (error) {
            console.log(error);
          }

          return {
            // see https://github.com/apollostack/apollo-client/blob/master/src/queries/store.ts#L28-L36
            // note: loading will propably change soon https://github.com/apollostack/apollo-client/issues/831
            loading: networkStatus === 1, // networkStatus = 1 <=> the graphql container is loading
            results,
            totalCount,
            refetch,
            networkStatus,
            error,
            count: results && results.length,

            // regular load more (reload everything)
            loadMore(providedTerms) {
              // if new terms are provided by presentational component use them, else default to incrementing current limit once
              const newTerms = typeof providedTerms === 'undefined' ? { /*...props.ownProps.terms,*/ ...props.ownProps.paginationTerms, limit: results.length + props.ownProps.paginationTerms.itemsPerPage } : providedTerms;

              props.ownProps.setPaginationTerms(newTerms);
            },

            // incremental loading version (only load new content)
            // note: not compatible with polling
            loadMoreInc(providedTerms) {

              // get terms passed as argument or else just default to incrementing the offset
              const newTerms = typeof providedTerms === 'undefined' ? { ...props.ownProps.terms, ...props.ownProps.paginationTerms, offset: results.length } : providedTerms;

              return props.data.fetchMore({
                variables: { terms: newTerms }, // ??? not sure about 'terms: newTerms'
                updateQuery(previousResults, { fetchMoreResult }) {
                  // no more post to fetch
                  if (!fetchMoreResult.data) {
                    return previousResults;
                  }
                  const newResults = {};
                  newResults[listResolverName] = [...previousResults[listResolverName], ...fetchMoreResult.data[listResolverName]];
                  // return the previous results "augmented" with more
                  return {...previousResults, ...newResults};
                },
              });
            },

            fragmentName,
            fragment,
            ...props.ownProps // pass on the props down to the wrapped component
          };
        },
      }
    )
  );
}

// define query reducer separately
const queryReducer = (previousResults, action, collection, mergedTerms, listResolverName, totalResolverName, queryName, apolloClient) => {

  // if collection has no mutations defined, just return previous results
  if (!collection.options.mutations) {
    return previousResults;
  }

  const newMutationName = collection.options.mutations.new && collection.options.mutations.new.name;
  const editMutationName = collection.options.mutations.edit && collection.options.mutations.edit.name;
  const removeMutationName = collection.options.mutations.remove && collection.options.mutations.remove.name;

  let newResults = previousResults;

  // get mongo selector and options objects based on current terms
  const { selector, options } = collection.getParameters(mergedTerms, apolloClient);
  const mingoQuery = Mingo.Query(selector);

  // function to remove a document from a results object, used by edit and remove cases below
  const removeFromResults = (results, document) => {
    const listWithoutDocument = results[listResolverName].filter(doc => doc._id !== document._id);
    const newResults = update(results, {
      [listResolverName]: { $set: listWithoutDocument }, // ex: postsList
      [totalResolverName]: { $set: results[totalResolverName] - 1 } // ex: postsListTotal
    });
    return newResults;
  }

  // add document to a results object
  const addToResults = (results, document) => {

    return update(results, {
      [listResolverName]: { $unshift: [document] },
      [totalResolverName]: { $set: results[totalResolverName] + 1 }
    });
  }

  // reorder results according to a sort
  const reorderResults = (results, sort) => {
    const list = results[listResolverName];
    // const convertedList = Utils.convertDates(collection, list); // convert date strings to date objects
    const convertedList = list;
    const cursor = mingoQuery.find(convertedList);
    const sortedList = cursor.sort(sort).all();
    results[listResolverName] = sortedList;
    return results;
  }

  // console.log('// withList reducer');
  // console.log('queryName: ', queryName);
  // console.log('terms: ', mergedTerms);
  // console.log('selector: ', selector);
  // console.log('options: ', options);
  // console.log('previousResults: ', previousResults);
  // console.log('previous titles: ', _.pluck(previousResults[listResolverName], 'title'))
  // console.log('action: ', action);

  switch (action.operationName) {

    case newMutationName:
      // if new document belongs to current list (based on view selector), add it
      const newDocument = action.result.data[newMutationName];
      if (mingoQuery.test(newDocument)) {
        newResults = addToResults(previousResults, newDocument);
        newResults = reorderResults(newResults, options.sort);
      }
      // console.log('** new **')
      // console.log('newDocument: ', newDocument)
      // console.log('belongs to list: ', mingoQuery.test(newDocument))
      break;

    case editMutationName:
      const editedDocument = action.result.data[editMutationName];
      if (mingoQuery.test(editedDocument)) {
        // edited document belongs to the list
        if (!_.findWhere(previousResults[listResolverName], {_id: editedDocument._id})) {
          // if document wasn't already in list, add it
          newResults = addToResults(previousResults, editedDocument);
        }
        newResults = reorderResults(newResults, options.sort);
      } else {
        // if edited doesn't belong to current list anymore (based on view selector), remove it
        newResults = removeFromResults(previousResults, editedDocument);
      }
      // console.log('** edit **')
      // console.log('editedDocument: ', editedDocument)
      // console.log('belongs to list: ', mingoQuery.test(editedDocument))
      // console.log('exists in list: ', !!_.findWhere(previousResults[listResolverName], {_id: editedDocument._id}))
      break;

    case removeMutationName:
      const removedDocument = action.result.data[removeMutationName];
      newResults = removeFromResults(previousResults, removedDocument);
      // console.log('** remove **')
      // console.log('removedDocument: ', removedDocument)
      break;

    case 'vote':
      // console.log('** vote **')
      // reorder results in case vote changed the order
      newResults = reorderResults(newResults, options.sort);
      break;

    default:
      // console.log('** no action **')
      return previousResults;
  }

  // console.log('newResults: ', newResults)
  // console.log('new titles: ', _.pluck(newResults[listResolverName], 'title'))
  // console.log('\n\n')

  // copy over arrays explicitely to ensure new sort is taken into account
  return {
    [listResolverName]: [...newResults[listResolverName]],
    [totalResolverName]: newResults[totalResolverName],
  }

}

export default withList;
<commit_msg>get limit from terms<commit_after>/*

### withList

Paginated items container

Options:

  - queryName: an arbitrary name for the query
  - collection: the collection to fetch the documents from
  - fragment: the fragment that defines which properties to fetch
  - fragmentName: the name of the fragment, passed to getFragment
  - limit: the number of documents to show initially
  - pollInterval: how often the data should be updated, in ms (set to 0 to disable polling)

Props Received:

  - terms: an object that defines which documents to fetch

Terms object can have the following properties:

  - view: String
  - userId: String
  - cat: String
  - date: String
  - after: String
  - before: String
  - enableCache: Boolean
  - listId: String
  - query: String # search query
  - postId: String
  - limit: String

*/

import React, { PropTypes, Component } from 'react';
import { graphql } from 'react-apollo';
import gql from 'graphql-tag';
import update from 'immutability-helper';
import { getFragment, getFragmentName } from 'meteor/vulcan:core';
import Mingo from 'mingo';
import { compose, withState } from 'recompose';
import { withApollo } from 'react-apollo';

const withList = (options) => {

  const { collection, limit = 10, pollInterval = 20000 } = options,
        queryName = options.queryName || `${collection.options.collectionName}ListQuery`,
        fragment = options.fragment || getFragment(options.fragmentName),
        fragmentName = getFragmentName(fragment),
        listResolverName = collection.options.resolvers.list && collection.options.resolvers.list.name,
        totalResolverName = collection.options.resolvers.total && collection.options.resolvers.total.name;

  return compose(

    // wrap component with Apollo HoC to give it access to the store
    withApollo,

    // wrap component with HoC that manages the terms object via its state
    withState('paginationTerms', 'setPaginationTerms', props => {

      // get initial limit from props, or else options
      const paginationLimit = props.terms && props.terms.limit || limit;
      const paginationTerms = {
        limit: paginationLimit,
        itemsPerPage: paginationLimit,
      };

      return paginationTerms;
    }),

    // wrap component with graphql HoC
    graphql(

      // build graphql query from options
      gql`
        query ${queryName}($terms: JSON) {
          ${totalResolverName}(terms: $terms)
          ${listResolverName}(terms: $terms) {
            __typename
            ...${fragmentName}
          }
        }
        ${fragment}
      `,

      {
        alias: 'withList',

        // graphql query options
        options({terms, paginationTerms, client: apolloClient}) {
          const mergedTerms = {...terms, ...paginationTerms};
          return {
            variables: {
              terms: mergedTerms,
            },
            // note: pollInterval can be set to 0 to disable polling (20s by default)
            pollInterval,
            reducer: (previousResults, action) => {

              // see queryReducer function defined below
              return queryReducer(previousResults, action, collection, mergedTerms, listResolverName, totalResolverName, queryName, apolloClient);

            },
          };
        },

        // define props returned by graphql HoC
        props(props) {

          const refetch = props.data.refetch,
                // results = Utils.convertDates(collection, props.data[listResolverName]),
                results = props.data[listResolverName],
                totalCount = props.data[totalResolverName],
                networkStatus = props.data.networkStatus,
                error = props.data.error;

          if (error) {
            console.log(error);
          }

          return {
            // see https://github.com/apollostack/apollo-client/blob/master/src/queries/store.ts#L28-L36
            // note: loading will propably change soon https://github.com/apollostack/apollo-client/issues/831
            loading: networkStatus === 1, // networkStatus = 1 <=> the graphql container is loading
            results,
            totalCount,
            refetch,
            networkStatus,
            error,
            count: results && results.length,

            // regular load more (reload everything)
            loadMore(providedTerms) {
              // if new terms are provided by presentational component use them, else default to incrementing current limit once
              const newTerms = typeof providedTerms === 'undefined' ? { /*...props.ownProps.terms,*/ ...props.ownProps.paginationTerms, limit: results.length + props.ownProps.paginationTerms.itemsPerPage } : providedTerms;

              props.ownProps.setPaginationTerms(newTerms);
            },

            // incremental loading version (only load new content)
            // note: not compatible with polling
            loadMoreInc(providedTerms) {

              // get terms passed as argument or else just default to incrementing the offset
              const newTerms = typeof providedTerms === 'undefined' ? { ...props.ownProps.terms, ...props.ownProps.paginationTerms, offset: results.length } : providedTerms;

              return props.data.fetchMore({
                variables: { terms: newTerms }, // ??? not sure about 'terms: newTerms'
                updateQuery(previousResults, { fetchMoreResult }) {
                  // no more post to fetch
                  if (!fetchMoreResult.data) {
                    return previousResults;
                  }
                  const newResults = {};
                  newResults[listResolverName] = [...previousResults[listResolverName], ...fetchMoreResult.data[listResolverName]];
                  // return the previous results "augmented" with more
                  return {...previousResults, ...newResults};
                },
              });
            },

            fragmentName,
            fragment,
            ...props.ownProps // pass on the props down to the wrapped component
          };
        },
      }
    )
  );
}

// define query reducer separately
const queryReducer = (previousResults, action, collection, mergedTerms, listResolverName, totalResolverName, queryName, apolloClient) => {

  // if collection has no mutations defined, just return previous results
  if (!collection.options.mutations) {
    return previousResults;
  }

  const newMutationName = collection.options.mutations.new && collection.options.mutations.new.name;
  const editMutationName = collection.options.mutations.edit && collection.options.mutations.edit.name;
  const removeMutationName = collection.options.mutations.remove && collection.options.mutations.remove.name;

  let newResults = previousResults;

  // get mongo selector and options objects based on current terms
  const { selector, options } = collection.getParameters(mergedTerms, apolloClient);
  const mingoQuery = Mingo.Query(selector);

  // function to remove a document from a results object, used by edit and remove cases below
  const removeFromResults = (results, document) => {
    const listWithoutDocument = results[listResolverName].filter(doc => doc._id !== document._id);
    const newResults = update(results, {
      [listResolverName]: { $set: listWithoutDocument }, // ex: postsList
      [totalResolverName]: { $set: results[totalResolverName] - 1 } // ex: postsListTotal
    });
    return newResults;
  }

  // add document to a results object
  const addToResults = (results, document) => {

    return update(results, {
      [listResolverName]: { $unshift: [document] },
      [totalResolverName]: { $set: results[totalResolverName] + 1 }
    });
  }

  // reorder results according to a sort
  const reorderResults = (results, sort) => {
    const list = results[listResolverName];
    // const convertedList = Utils.convertDates(collection, list); // convert date strings to date objects
    const convertedList = list;
    const cursor = mingoQuery.find(convertedList);
    const sortedList = cursor.sort(sort).all();
    results[listResolverName] = sortedList;
    return results;
  }

  // console.log('// withList reducer');
  // console.log('queryName: ', queryName);
  // console.log('terms: ', mergedTerms);
  // console.log('selector: ', selector);
  // console.log('options: ', options);
  // console.log('previousResults: ', previousResults);
  // console.log('previous titles: ', _.pluck(previousResults[listResolverName], 'title'))
  // console.log('action: ', action);

  switch (action.operationName) {

    case newMutationName:
      // if new document belongs to current list (based on view selector), add it
      const newDocument = action.result.data[newMutationName];
      if (mingoQuery.test(newDocument)) {
        newResults = addToResults(previousResults, newDocument);
        newResults = reorderResults(newResults, options.sort);
      }
      // console.log('** new **')
      // console.log('newDocument: ', newDocument)
      // console.log('belongs to list: ', mingoQuery.test(newDocument))
      break;

    case editMutationName:
      const editedDocument = action.result.data[editMutationName];
      if (mingoQuery.test(editedDocument)) {
        // edited document belongs to the list
        if (!_.findWhere(previousResults[listResolverName], {_id: editedDocument._id})) {
          // if document wasn't already in list, add it
          newResults = addToResults(previousResults, editedDocument);
        }
        newResults = reorderResults(newResults, options.sort);
      } else {
        // if edited doesn't belong to current list anymore (based on view selector), remove it
        newResults = removeFromResults(previousResults, editedDocument);
      }
      // console.log('** edit **')
      // console.log('editedDocument: ', editedDocument)
      // console.log('belongs to list: ', mingoQuery.test(editedDocument))
      // console.log('exists in list: ', !!_.findWhere(previousResults[listResolverName], {_id: editedDocument._id}))
      break;

    case removeMutationName:
      const removedDocument = action.result.data[removeMutationName];
      newResults = removeFromResults(previousResults, removedDocument);
      // console.log('** remove **')
      // console.log('removedDocument: ', removedDocument)
      break;

    case 'vote':
      // console.log('** vote **')
      // reorder results in case vote changed the order
      newResults = reorderResults(newResults, options.sort);
      break;

    default:
      // console.log('** no action **')
      return previousResults;
  }

  // console.log('newResults: ', newResults)
  // console.log('new titles: ', _.pluck(newResults[listResolverName], 'title'))
  // console.log('\n\n')

  // copy over arrays explicitely to ensure new sort is taken into account
  return {
    [listResolverName]: [...newResults[listResolverName]],
    [totalResolverName]: newResults[totalResolverName],
  }

}

export default withList;
<|endoftext|>


<commit_before>import React, { Component, PropTypes } from 'react';
import styles from '../Chat.css';
import Avatar from './Avatar';
import ToggleDisplay from '../utils/ToggleDisplay';
import getTimeStamp from '../utils/getTimeStamp.js';
import emojify from '../utils/emojify';
import convertMedia from '../utils/convertMedia';
import VideoContainer from './VideoContainer';
import MdAccessTime from 'react-icons/lib/md/access-time';
import MdReply from 'react-icons/lib/md/reply';
import MdClose from 'react-icons/lib/md/close';
import MdPlayArrow from 'react-icons/lib/md/play-arrow';
import MdStop from 'react-icons/lib/md/stop';
import MdTranslate from 'react-icons/lib/md/translate';

let className_;
export default class ChatArea extends Component {
  static propTypes = {
    messages: PropTypes.array,
    replay: PropTypes.func,
    isMine: PropTypes.func,
    onTranslate: PropTypes.func,
    onDelete: PropTypes.func,
    lng: PropTypes.string,
  };

  static defaultProps = {
    lng: 'en'
  };

  componentWillMount() {
    if ((!this.props.onDelete) && (!this.props.onTranslate)) {
      className_ = styles.secondCellMsg1;
    } else if ((!this.props.onDelete) || (!this.props.onTranslate)) {
      className_ = styles.secondCellMsg2;
    }
    else className_ = styles.secondCellMsg3;
  }

  componentDidMount() {
    setTimeout(this.updateScrollTop, 500);
  }

  componentDidUpdate = () => {
    if (this.message.childNodes.length === 3) {
      if ((this.message.childNodes[0].textContent === '') &&
        (this.message.childNodes[2].textContent === '')) {
        this.message.childNodes[1].style.fontSize = '34px';
      }
    }
    this.updateScrollTop();
  };

  updateScrollTop = () => {
    let node = document.getElementById('container');
    if (!node) {
      return;
    }
    node.scrollTop = node.scrollHeight;
  };

  isMedia = (msg) => {
    const media = convertMedia(msg, 150, true);
    if ((media.indexOf(' -1) || (media.indexOf(' -1)) return media;
    return false;
  };

  showTranslate = () => {
    return (this.props.onTranslate) ? true : false;
  };
  showDelete = () => {
    return (this.props.onDelete) ? true : false;
  };

  deleteMsg = (message, e) => {
    this.props.onDelete(message, function success() {
      //on delete success
    });
  };
  prepareForTranslation = (message) => {
    return message.replace(/(<([^>]+)>)/ig, '').replace(/\+/g, '');
  };
  play = (message, e) => {
    const node = e.currentTarget.parentNode;
    this.toggleIcons(node.children[1], node.children[0]);

    const msg = new SpeechSynthesisUtterance(this.prepareForTranslation(message));
    msg.lang = this.props.lng;
    const that = this;
    msg.onend = function (event) {
      that.toggleIcons(node.children[0], node.children[1]);
    };
    window.speechSynthesis.speak(msg);
  };
  stop = (e) => {
    window.speechSynthesis.cancel();

    const node = e.currentTarget.parentNode;
    this.toggleIcons(node.children[0], node.children[1]);
  };
  toggleIcons = (icon1, icon2) => {
    const node1 = icon1;
    const node2 = icon2;
    node1.style.display = 'none';
    node2.style.display = 'block';
  };

  render() {
    const { messages, replay, isMine } = this.props;
    return (

        {
          messages.map(message => {
            return (

                {
                  !isMine(message.sender) &&
                  ((typeof message.showAvatars === 'undefined') ? true : message.showAvatars) &&
                  <Avatar style={styles.avatar}
                    src={message.avatar}
                    name={message.name}
                  />
                }

 this.message = ref}
                    className={message.msg.length < 8 ?
                    styles.big_content_msg :
                    styles.small_content_msg}
                  >
                    {this.isMedia(message.msg) ?
                      <VideoContainer src={this.isMedia(message.msg)}/> :
                      emojify(message.msg)}

                    {
                      !isMine(message.sender) &&

                          {message.name}
                          <MdAccessTime className={styles.timeIcon} />
                          <MdReply className={styles.replyIcon} />

                    }

{getTimeStamp(message.time)}

 this.secondCellMsg = ref} className={className_}>
                    <ToggleDisplay show={this.showTranslate()}>
                        <MdTranslate/>
                    </ToggleDisplay>

                      <MdStop onClick={this.stop.bind(this)} style={{ display: 'none' }}/>
                      <MdPlayArrow onClick={this.play.bind(this, message.msg)}/>

                    <ToggleDisplay
                      show={this.showDelete()}
                      onClick={this.deleteMsg.bind(this, message.id)}
                    >
                      <MdClose/>
                    </ToggleDisplay>

            );
          })
        }

    );
  }
}
<commit_msg>Enhances speech synthesis function<commit_after>import React, { Component, PropTypes } from 'react';
import styles from '../Chat.css';
import Avatar from './Avatar';
import ToggleDisplay from '../utils/ToggleDisplay';
import getTimeStamp from '../utils/getTimeStamp.js';
import emojify from '../utils/emojify';
import convertMedia from '../utils/convertMedia';
import VideoContainer from './VideoContainer';
import MdAccessTime from 'react-icons/lib/md/access-time';
import MdReply from 'react-icons/lib/md/reply';
import MdClose from 'react-icons/lib/md/close';
import MdPlayArrow from 'react-icons/lib/md/play-arrow';
import MdStop from 'react-icons/lib/md/stop';
import MdTranslate from 'react-icons/lib/md/translate';

let className_;
export default class ChatArea extends Component {
  static propTypes = {
    messages: PropTypes.array,
    replay: PropTypes.func,
    isMine: PropTypes.func,
    onTranslate: PropTypes.func,
    onDelete: PropTypes.func,
    lng: PropTypes.string,
  };

  static defaultProps = {
    lng: 'en'
  };

  componentWillMount() {
    if ((!this.props.onDelete) && (!this.props.onTranslate)) {
      className_ = styles.secondCellMsg1;
    } else if ((!this.props.onDelete) || (!this.props.onTranslate)) {
      className_ = styles.secondCellMsg2;
    }
    else className_ = styles.secondCellMsg3;
  }

  componentDidMount() {
    setTimeout(this.updateScrollTop, 500);
  }

  componentDidUpdate = () => {
    if (this.message.childNodes.length === 3) {
      if ((this.message.childNodes[0].textContent === '') &&
        (this.message.childNodes[2].textContent === '')) {
        this.message.childNodes[1].style.fontSize = '34px';
      }
    }
    this.updateScrollTop();
  };

  updateScrollTop = () => {
    let node = document.getElementById('container');
    if (!node) {
      return;
    }
    node.scrollTop = node.scrollHeight;
  };

  isMedia = (msg) => {
    const media = convertMedia(msg, 150, true);
    if ((media.indexOf(' -1) || (media.indexOf(' -1)) return media;
    return false;
  };

  showTranslate = () => {
    return (this.props.onTranslate) ? true : false;
  };
  showDelete = () => {
    return (this.props.onDelete) ? true : false;
  };

  deleteMsg = (message, e) => {
    this.props.onDelete(message, function success() {
      //on delete success
    });
  };
  prepareForTranslation = (message) => {
    return message.replace(/(<([^>]+)>)/ig, '').replace(/\+/g, '');
  };
  play = (message, e) => {
    const node = e.currentTarget;
    if (node.children[0].style.display === 'none') {
      this.toggleIcons(node.children[1], node.children[0]);

      const msg = new SpeechSynthesisUtterance(this.prepareForTranslation(message));
      msg.lang = this.props.lng;
      const that = this;
      msg.onend = function (event) {
        that.toggleIcons(node.children[0], node.children[1]);
      };
      window.speechSynthesis.speak(msg);
    } else {
      window.speechSynthesis.cancel();
      this.toggleIcons(node.children[0], node.children[1]);
    }
  };
  toggleIcons = (icon1, icon2) => {
    const node1 = icon1;
    const node2 = icon2;
    node1.style.display = 'none';
    node2.style.display = 'block';
  };

  render() {
    const { messages, replay, isMine } = this.props;
    return (

        {
          messages.map(message => {
            return (

                {
                  !isMine(message.sender) &&
                  ((typeof message.showAvatars === 'undefined') ? true : message.showAvatars) &&
                  <Avatar style={styles.avatar}
                    src={message.avatar}
                    name={message.name}
                  />
                }

 this.message = ref}
                    className={message.msg.length < 8 ?
                    styles.big_content_msg :
                    styles.small_content_msg}
                  >
                    {this.isMedia(message.msg) ?
                      <VideoContainer src={this.isMedia(message.msg)}/> :
                      emojify(message.msg)}

                    {
                      !isMine(message.sender) &&

                          {message.name}
                          <MdAccessTime className={styles.timeIcon} />
                          <MdReply className={styles.replyIcon} />

                    }

{getTimeStamp(message.time)}

 this.secondCellMsg = ref} className={className_}>
                    <ToggleDisplay show={this.showTranslate()}>
                        <MdTranslate/>
                    </ToggleDisplay>

                      <MdStop style={{ display: 'none' }}/>
                      <MdPlayArrow/>

                    <ToggleDisplay
                      show={this.showDelete()}
                      onClick={this.deleteMsg.bind(this, message.id)}
                    >
                      <MdClose/>
                    </ToggleDisplay>

            );
          })
        }

    );
  }
}
<|endoftext|>


<commit_before>function Sun(distFromOrigin, omega) {
	// The light the scene will get at day time
	this.daylight = new Material(
		vec4(0.3, 0.3, 0.3, 1.0),
		vec4(0.7, 0.7, 0.7, 1.0)
	);

	// The light the scene will get if the sun has set
	this.darkness = new Material(
		vec4(0.2, 0.2, 0.2, 1.0),
		vec4(0.0, 0.0, 0.0, 1.0)
	);

	// The light the scene will get at night time from the moon
	this.moonlight = new Material(
		vec4(0.2, 0.2, 0.2, 1.0),
		vec4(0.25, 0.33, 0.36, 1.0)
	);

	// The material the glHelper will use for calculating light products
	this.lightMaterial = this.daylight;

	this.angle = 0;      // Stores the time of day
	this.omega = omega;  // Determines how fast the day is
	this.distFromOrigin = distFromOrigin;

	this.daySky     = vec4(0.54, 0.81, 0.94, 1.0);
	this.settingSky = vec4(0.82, 0.53, 0.42, 1.0);
	this.nightSky   = vec4(0.19, 0.17, 0.21, 1.0);

	this.skyColor = this.daySky;

	this.sun = new Sphere(new Material(vec4(1.0, 1.0, 0.0, 1.0), vec4(1.0, 1.0, 0.0, 1.0)), null, true);
	this.sun.radius = 4;

	var moonTex = new Texture.fromImageSrc(
		'./images/moon.jpg',
		gl.REPEAT, gl.REPEAT,
		gl.LINEAR, gl.LINEAR_MIPMAP_LINEAR
	);

	this.moon = new Sphere(new Material(vec4(0.3, 0.3, 0.3, 1.0), vec4(0.65, 0.65, 0.65, 1.0)), moonTex, false);
	this.moon.radius = 7;
}

Sun.prototype.draw = function(dt) {
	this.angle = (this.angle + dt * this.omega) % 360;
	this.sun.position = this.moon.position = vec3(this.distFromOrigin, 0.0, 0.0);

	var rad = radians(this.angle);
	var sin = Math.sin(rad);
	var cos = Math.cos(rad);
	var posCos = this.distFromOrigin * cos;
	var posSin = this.distFromOrigin * sin;

	var lightAlpha = Math.abs(sin); // Amount of light (from day to night) that the scene gets
	var epsilon = 5;
	var envLight = this.daylight;
	var oldLight = this.lightMaterial;

	glHelper.setLightPosition(vec3(posCos, posSin, 0));

	// The sun and the moon get lit using full day light no matter what
	this.lightMaterial = this.daylight;

	// If the sun is rising/setting, still draw the moon to avoid it
	// jumping in/out when it is around the horizon.
	if(this.angle > 180 - epsilon || this.angle < epsilon ) {
		// Sun's light position already set
		this.moon.position = vec3(-posCos, -posSin, 0);
		this.moon.draw(dt, mat4());
	}

	// Draw the sun around the horizon to avoid it jumping in/out of view
	if(this.angle > 360 - epsilon || this.angle < 180 + epsilon) {
		this.sun.draw(dt, rotate(this.angle, vec3(0, 0, 1)));
	}

	// Restore the light material for the rest of the scene
	this.lightMaterial = oldLight;

	// Leaving these values hardcoded becuase the result looks decent enough
	// and this should never have to change at run time.
	if(this.angle < 160 && this.angle > 140) {
		// Start setting, go from daytime to setting sky
		this.skyColor = mix(this.settingSky, this.daySky, (this.angle - 140)/20);

	} else if(this.angle >= 160 && this.angle < 200) {
		// Start darkening, from setting sky to night time
		this.skyColor = mix(this.nightSky, this.settingSky, (this.angle - 160)/40);

	} else if(this.angle >= 200 && this.angle < 340) {
		// Stay at night time
		this.skyColor = this.nightSky;

	} else if(this.angle >= 340) {
		// Sunrise, start going from dark to the sunset/sunrise color
		this.skyColor = mix(this.settingSky, this.nightSky, (this.angle - 340)/40);
	} else if(this.angle < 20) {
		// Finish transitioning from night to sunrise sky
		this.skyColor = mix(this.settingSky, this.nightSky, (this.angle+20)/40);

	} else if(this.angle >= 20 && this.angle < 40) {
		// Transition from sunrise to day time sky
		this.skyColor = mix(this.daySky, this.settingSky, (this.angle - 20)/20);

	} else {
		// Default day sky
		this.skyColor = this.daySky;
	}

	// If the sun is "gone", use the moon's position as a light source
	// and moonlight for the rest of the scene
	if(this.angle > 180) {
		glHelper.setLightPosition(this.moon.position);
		envLight = this.moonlight;
	}

	this.lightMaterial = new Material(
		mix(envLight.ambient, this.darkness.ambient, lightAlpha),
		mix(envLight.diffuse, this.darkness.diffuse, lightAlpha)
	);
}
<commit_msg>Make sunrise/sunsets a bit shorter<commit_after>function Sun(distFromOrigin, omega) {
	// The light the scene will get at day time
	this.daylight = new Material(
		vec4(0.3, 0.3, 0.3, 1.0),
		vec4(0.7, 0.7, 0.7, 1.0)
	);

	// The light the scene will get if the sun has set
	this.darkness = new Material(
		vec4(0.2, 0.2, 0.2, 1.0),
		vec4(0.0, 0.0, 0.0, 1.0)
	);

	// The light the scene will get at night time from the moon
	this.moonlight = new Material(
		vec4(0.2, 0.2, 0.2, 1.0),
		vec4(0.25, 0.33, 0.36, 1.0)
	);

	// The material the glHelper will use for calculating light products
	this.lightMaterial = this.daylight;

	this.angle = 0;      // Stores the time of day
	this.omega = omega;  // Determines how fast the day is
	this.distFromOrigin = distFromOrigin;

	this.daySky     = vec4(0.54, 0.81, 0.94, 1.0);
	this.settingSky = vec4(0.82, 0.53, 0.42, 1.0);
	this.nightSky   = vec4(0.19, 0.17, 0.21, 1.0);

	this.skyColor = this.daySky;

	this.sun = new Sphere(new Material(vec4(1.0, 1.0, 0.0, 1.0), vec4(1.0, 1.0, 0.0, 1.0)), null, true);
	this.sun.radius = 4;

	var moonTex = new Texture.fromImageSrc(
		'./images/moon.jpg',
		gl.REPEAT, gl.REPEAT,
		gl.LINEAR, gl.LINEAR_MIPMAP_LINEAR
	);

	this.moon = new Sphere(new Material(vec4(0.3, 0.3, 0.3, 1.0), vec4(0.65, 0.65, 0.65, 1.0)), moonTex, false);
	this.moon.radius = 7;
}

Sun.prototype.draw = function(dt) {
	this.angle = (this.angle + dt * this.omega) % 360;
	this.sun.position = this.moon.position = vec3(this.distFromOrigin, 0.0, 0.0);

	var rad = radians(this.angle);
	var sin = Math.sin(rad);
	var cos = Math.cos(rad);
	var posCos = this.distFromOrigin * cos;
	var posSin = this.distFromOrigin * sin;

	var lightAlpha = Math.abs(sin); // Amount of light (from day to night) that the scene gets
	var epsilon = 5;
	var envLight = this.daylight;
	var oldLight = this.lightMaterial;

	glHelper.setLightPosition(vec3(posCos, posSin, 0));

	// The sun and the moon get lit using full day light no matter what
	this.lightMaterial = this.daylight;

	// If the sun is rising/setting, still draw the moon to avoid it
	// jumping in/out when it is around the horizon.
	if(this.angle > 180 - epsilon || this.angle < epsilon ) {
		// Sun's light position already set
		this.moon.position = vec3(-posCos, -posSin, 0);
		this.moon.draw(dt, mat4());
	}

	// Draw the sun around the horizon to avoid it jumping in/out of view
	if(this.angle > 360 - epsilon || this.angle < 180 + epsilon) {
		this.sun.draw(dt, rotate(this.angle, vec3(0, 0, 1)));
	}

	// Restore the light material for the rest of the scene
	this.lightMaterial = oldLight;

	// Leaving these values hardcoded becuase the result looks decent enough
	// and this should never have to change at run time.
	if(this.angle < 170 && this.angle > 160) {
		// Start setting, go from daytime to setting sky
		this.skyColor = mix(this.settingSky, this.daySky, (this.angle - 160)/10);

	} else if(this.angle >= 170 && this.angle < 190) {
		// Start darkening, from setting sky to night time
		this.skyColor = mix(this.nightSky, this.settingSky, (this.angle - 170)/20);

	} else if(this.angle >= 190 && this.angle < 350) {
		// Stay at night time
		this.skyColor = this.nightSky;

	} else if(this.angle >= 350) {
		// Sunrise, start going from dark to the sunset/sunrise color
		this.skyColor = mix(this.settingSky, this.nightSky, (this.angle - 350)/20);
	} else if(this.angle < 10) {
		// Finish transitioning from night to sunrise sky
		this.skyColor = mix(this.settingSky, this.nightSky, (this.angle + 10)/20);

	} else if(this.angle >= 10 && this.angle < 20) {
		// Transition from sunrise to day time sky
		this.skyColor = mix(this.daySky, this.settingSky, (this.angle - 10)/10);

	} else {
		// Default day sky
		this.skyColor = this.daySky;
	}

	// If the sun is "gone", use the moon's position as a light source
	// and moonlight for the rest of the scene
	if(this.angle > 180) {
		glHelper.setLightPosition(this.moon.position);
		envLight = this.moonlight;
	}

	this.lightMaterial = new Material(
		mix(envLight.ambient, this.darkness.ambient, lightAlpha),
		mix(envLight.diffuse, this.darkness.diffuse, lightAlpha)
	);
}
<|endoftext|>


<commit_before>/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

var CMIS = (function () {

	var VERSION = '0.1.0';

	// global object
	var root = this;
	var $ = root.jQuery || root.Zepto || root.ender || root.$;

	var config = {
		url : '',
		basicauth : '',
		repositoryId : '',
		repositoryUrl : '',
		rootFolderUrl : ''
	};

	var defaultOptions = {
		data : {
			'succinct' : 'true'
		}
	};

	return {

		// helpers

		transformProperties : function (properties) {
			var params = {} , i = 0;
			$.each(properties, function(key,value) {
				if (key.indexOf(':') > 0) {
					params['propertyId['+i+']'] = key;
					params['propertyValue['+i+']'] = value;
					++i;
				} else {
					params[key] = value;
				}
			});
			return params;
		},

		asFormData : function (properties) {
			var formData = new FormData();

			$.each(properties, function (key,value) {
				formData.append(key,value);
			});

			return formData;
		},

		setDefaultOptions : function (options) {
			defaultOptions = options;
		},

		// init

		init : function (parameters) {

			config.url = parameters['url'];
			config.basicauth = btoa(parameters['username'] + ':' + parameters['password']);
			config.repositoryId = parameters['repositoryId'];

			// TODO: these parameters should be obtained directly from the repository (getRepositories)
			config.repositoryUrl = config.url + "/" + config.repositoryId;
			config.rootFolderUrl = config.repositoryUrl + '/root'; // TODO: here it is assumed that root is the name of the root folder, which is not always true

			$.ajaxSetup({
				beforeSend : function (xhr) {
					xhr.setRequestHeader('Authorization', "Basic " + config.basicauth);
				},
			});

		},

		// ajax Handling

		ajaxCall : function (params) {
			params = $.extend(true,{},defaultOptions,params);

			if (params.data.path) {
				params.url += path.data.path;
				delete params.data.path;
			}

			params.data = this.transformProperties(params.data);

			// exceptions :(
			if (params.data['cmisaction'] === 'createDocument') {
				params.data = this.asFormData(params.data);

				params.processData = false; // prevent jQuery from processing the data to a string
				params.contentType = false; // contentType will be set from browser
				params.cache = false;
			}

			/*
			// just for debugging purposes
			$.each(params.data, function(key,value) {
				console.info(key + " - " + value);
			});
			*/

			return $.ajax(params);
		},

		repositoryService : function (inputs, options) {
			options || (options = {});
			options.data = inputs;
			options.url = config.repositoryUrl;

			return this.ajaxCall(options);
		},

		objectService : function (inputs, options) {
			options || (options = {});
			options.data = inputs;
			options.url = config.rootFolderUrl;

			return this.ajaxCall(options);
		},

		// object Services

		createFolder : function (inputs, options) {
			options || (options = {});
			options.type = 'POST';

			var data = {
				'cmisaction' : 'createFolder',
				'cmis:objectTypeId' : 'cmis:folder'
			};

			return this.objectService($.extend(data,inputs),options);
		},

		createDocument : function(inputs,options) {
			options || (options = {});
			options.type = 'POST';

			var data = {
				'cmisaction' : 'createDocument',
				'cmis:objectTypeId' : 'cmis:document'
			};

			return this.objectService($.extend(data,inputs),options);
		},

		deleteObject : function(inputs, options) {
			options || (options = {});
			options.type = 'POST';

			var data = {
				'cmisaction' : 'delete',
				'allVersions' : 'true'
			};

			return this.objectService($.extend(data,inputs),options);
		},

		deleteTree : function(inputs, options) {
			options || (options = {});
			options.type = 'POST';

			var data = {
				'cmisaction' : 'deleteTree',
				'allVersions' : 'true'
			};

			return this.objectService($.extend(data,inputs),options);
		},

		children : function(inputs, options) {
			var data = {
				'cmisselector' : 'children'
			};

			return this.objectService($.extend(data,inputs),options);
		},

		update : function(inputs,options) {
			options || (options = {});
			options.type = 'POST';

			var data = {
				'cmisaction' : 'update',
				'allVersions' : 'true'
			};

			return this.objectService($.extend(data,inputs),options);
		},

		object : function(inputs,options) {
			var data = {
				'cmisselector' : 'object'
			};

			return this.objectService($.extend(data,inputs),options);
		},

		move : function(inputs,options) {
			options || (options = {});
			options.type = 'POST';

			var data = {
				'cmisaction' : 'move'
			};

			return this.objectService($.extend(data,inputs),options);
		},

		// repository Services

		query : function(inputs,options) {
			var data = {
				'cmisselector' : 'query'
			};

			return this.repositoryService($.extend(data,inputs),options);

		},

	};

})();<commit_msg>bugfix<commit_after>/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

var CMIS = (function () {

	var VERSION = '0.1.0';

	// global object
	var root = this;
	var $ = root.jQuery || root.Zepto || root.ender || root.$;

	var config = {
		url : '',
		basicauth : '',
		repositoryId : '',
		repositoryUrl : '',
		rootFolderUrl : ''
	};

	var defaultOptions = {
		data : {
			'succinct' : 'true'
		}
	};

	return {

		// helpers

		transformProperties : function (properties) {
			var params = {} , i = 0;
			$.each(properties, function(key,value) {
				if (key.indexOf(':') > 0) {
					params['propertyId['+i+']'] = key;
					params['propertyValue['+i+']'] = value;
					++i;
				} else {
					params[key] = value;
				}
			});
			return params;
		},

		asFormData : function (properties) {
			var formData = new FormData();

			$.each(properties, function (key,value) {
				formData.append(key,value);
			});

			return formData;
		},

		setDefaultOptions : function (options) {
			defaultOptions = options;
		},

		// init

		init : function (parameters) {

			config.url = parameters['url'];
			config.basicauth = btoa(parameters['username'] + ':' + parameters['password']);
			config.repositoryId = parameters['repositoryId'];

			// TODO: these parameters should be obtained directly from the repository (getRepositories)
			config.repositoryUrl = config.url + "/" + config.repositoryId;
			config.rootFolderUrl = config.repositoryUrl + '/root'; // TODO: here it is assumed that root is the name of the root folder, which is not always true

			$.ajaxSetup({
				beforeSend : function (xhr) {
					xhr.setRequestHeader('Authorization', "Basic " + config.basicauth);
				},
			});

		},

		// ajax Handling

		ajaxCall : function (params) {
			params = $.extend(true,{},defaultOptions,params);

			if (params.data && params.data.path) {
				params.url += params.data.path;
				delete params.data.path;
			}

			params.data = this.transformProperties(params.data);

			// exceptions :(
			if (params.data['cmisaction'] === 'createDocument') {
				params.data = this.asFormData(params.data);

				params.processData = false; // prevent jQuery from processing the data to a string
				params.contentType = false; // contentType will be set from browser
				params.cache = false;
			}

			/*
			// just for debugging purposes
			$.each(params.data, function(key,value) {
				console.info(key + " - " + value);
			});
			*/

			return $.ajax(params);
		},

		repositoryService : function (inputs, options) {
			options || (options = {});
			options.data = inputs;
			options.url = config.repositoryUrl;

			return this.ajaxCall(options);
		},

		objectService : function (inputs, options) {
			options || (options = {});
			options.data = inputs;
			options.url = config.rootFolderUrl;

			return this.ajaxCall(options);
		},

		// object Services

		createFolder : function (inputs, options) {
			options || (options = {});
			options.type = 'POST';

			var data = {
				'cmisaction' : 'createFolder',
				'cmis:objectTypeId' : 'cmis:folder'
			};

			return this.objectService($.extend(data,inputs),options);
		},

		createDocument : function(inputs,options) {
			options || (options = {});
			options.type = 'POST';

			var data = {
				'cmisaction' : 'createDocument',
				'cmis:objectTypeId' : 'cmis:document'
			};

			return this.objectService($.extend(data,inputs),options);
		},

		deleteObject : function(inputs, options) {
			options || (options = {});
			options.type = 'POST';

			var data = {
				'cmisaction' : 'delete',
				'allVersions' : 'true'
			};

			return this.objectService($.extend(data,inputs),options);
		},

		deleteTree : function(inputs, options) {
			options || (options = {});
			options.type = 'POST';

			var data = {
				'cmisaction' : 'deleteTree',
				'allVersions' : 'true'
			};

			return this.objectService($.extend(data,inputs),options);
		},

		children : function(inputs, options) {
			var data = {
				'cmisselector' : 'children'
			};

			return this.objectService($.extend(data,inputs),options);
		},

		update : function(inputs,options) {
			options || (options = {});
			options.type = 'POST';

			var data = {
				'cmisaction' : 'update',
				'allVersions' : 'true'
			};

			return this.objectService($.extend(data,inputs),options);
		},

		object : function(inputs,options) {
			var data = {
				'cmisselector' : 'object'
			};

			return this.objectService($.extend(data,inputs),options);
		},

		move : function(inputs,options) {
			options || (options = {});
			options.type = 'POST';

			var data = {
				'cmisaction' : 'move'
			};

			return this.objectService($.extend(data,inputs),options);
		},

		// repository Services

		query : function(inputs,options) {
			var data = {
				'cmisselector' : 'query'
			};

			return this.repositoryService($.extend(data,inputs),options);

		},

	};

})();<|endoftext|>


<commit_before>/**
 * Simple HTTP requests monitor and analyzer
 */
"use strict";

const assert = require("assert"),
  debug = require("debug")("phantomas:modules:requestsMonitor");

/**
 * Given key-value set of HTTP headers returns the set with lowercased header names
 *
 * @param {object} headers
 * @returns {object}
 */
function lowerCaseHeaders(headers) {
  var res = {};

  Object.keys(headers).forEach((headerName) => {
    res[headerName.toLowerCase()] = headers[headerName];
  });

  return res;
}

// parse given URL to get protocol and domain
function parseEntryUrl(entry) {
  var parsed;

  // asset type
  entry.type = "other";

  if (entry.url.indexOf("data:") === 0) {
    // base64 encoded data
    entry.domain = false;
    entry.protocol = false;
    entry.isBase64 = true;

  } else if (entry.url.indexOf("blob:") === 0) {
    // blob image or video
    entry.domain = false;
    entry.protocol = false;
    entry.isBlob = true;

  } else {
    parsed = new URL(entry.url) || {};

    entry.protocol = parsed.protocol.replace(":", ""); // e.g. "http:"
    entry.domain = parsed.hostname;
    entry.query = parsed.search.substring(1);

    if (entry.protocol === "https") {
      entry.isSSL = true;
    }
  }

  return entry;
}

/**
 * Detect response content type using "Content-Type header value"
 *
 * @param {string} headerValue
 * @param {object} entry
 */
function addContentType(headerValue, entry) {
  var value = headerValue.split(";").shift().toLowerCase();
  entry.contentType = value;

  switch (value) {
    case "text/html":
      entry.type = "html";
      entry.isHTML = true;
      break;

    case "text/xml":
      entry.type = "xml";
      entry.isXML = true;
      break;

    case "text/css":
      entry.type = "css";
      entry.isCSS = true;
      break;

    case "application/x-javascript":
    case "application/javascript":
    case "text/javascript":
      entry.type = "js";
      entry.isJS = true;
      break;

    case "application/json":
      entry.type = "json";
      entry.isJSON = true;
      break;

    case "image/png":
    case "image/jpeg":
    case "image/gif":
    case "image/svg+xml":
    case "image/webp":
      entry.type = "image";
      entry.isImage = true;

      if (value === "image/svg+xml") {
        entry.isSVG = true;
      }
      break;

    case "video/webm":
      entry.type = "video";
      entry.isVideo = true;
      break;

    // @see http://stackoverflow.com/questions/2871655/proper-mime-type-for-fonts
    case "application/font-wof":
    case "application/font-woff":
    case "application/font-woff2":
    case "application/vnd.ms-fontobject":
    case "application/x-font-opentype":
    case "application/x-font-truetype":
    case "application/x-font-ttf":
    case "application/x-font-woff":
    case "font/opentype":
    case "font/ttf":
    case "font/woff":
    case "font/woff2":
      entry.type = "webfont";
      entry.isWebFont = true;

      if (/ttf|truetype$/.test(value)) {
        entry.isTTF = true;
      }
      break;

    case "application/octet-stream":
      var ext = (entry.url || "").split(".").pop();

      switch (ext) {
        // @see http://stackoverflow.com/questions/2871655/proper-mime-type-for-fonts#comment-8077637
        case "otf":
          entry.type = "webfont";
          entry.isWebFont = true;
          break;
      }
      break;

    case "image/x-icon":
    case "image/vnd.microsoft.icon":
      entry.type = "favicon";
      entry.isFavicon = true;
      break;

    default:
      debug(
        "Unknown content type found: " + value + " for <" + entry.url + ">"
      );
  }

  return entry;
}

module.exports = function (phantomas) {
  // imports
  var HTTP_STATUS_CODES = require("http").STATUS_CODES;

  // register metric
  phantomas.setMetric("requests"); // @desc total number of HTTP requests made
  phantomas.setMetric("gzipRequests"); // @desc number of gzipped HTTP responses @unreliable
  phantomas.setMetric("postRequests"); // @desc number of POST requests
  phantomas.setMetric("httpsRequests"); // @desc number of HTTPS requests
  phantomas.setMetric("notFound"); // @desc number of HTTP 404 responses
  phantomas.setMetric("bodySize"); // @desc size of the uncompressed content of all responses
  phantomas.setMetric("contentLength"); // @desc size of the compressed content of all responses, i.e. what was transfered in packets
  phantomas.setMetric("httpTrafficCompleted"); // @desc time it took to receive the last byte of the last HTTP response

  var requests = {};

  phantomas.on(
    "request",
    /** @param {Request} request **/ (request) => {
      const resId = request._requestId;
      requests[resId] = request;

      // request data
      // https://github.com/GoogleChrome/puppeteer/blob/v1.11.0/docs/api.md#class-request
      phantomas.emit("send", request); // @desc request has been sent
    }
  );

  phantomas.on(
    "response",
    /** @param {Response} resp **/ (resp) => {
      const resId = resp._requestId,
        request = requests[resId];

      if (resp.fromDiskCache === true) {
        phantomas.log("response from disk cache ignored: %j", resp);
        return;
      }

      var entry = {
        id: resId,
        url: resp.url,
        method: request.method,
        headers: lowerCaseHeaders(resp.headers), // All header names are lower-case
        bodySize: resp.dataLength,
        transferedSize: resp.encodedDataLength,
      };

      // this is set to zero when requests interception is enabled
      // use Content-Length response header instead
      if (entry.transferedSize == 0) {
        entry.transferedSize = parseInt(
          entry.headers["content-length"] || "0",
          10
        );
      }

      // that's the response size as reported by Chrome's dev tools (headers + compressed body)
      // note: base64-encoded resources do not have "resp.headersText" set
      entry.responseSize = entry.transferedSize;

      phantomas.log("headers: %j", resp);

      entry = parseEntryUrl(entry);

      /**
       * Time to First Byte is the amount of time it takes for the browser
       * to receive the first byte of data from the server
       * after the browser makes the request.
       *
       * https://developers.google.com/web/tools/chrome-devtools/network-performance/reference#timing-explanation
       * https://www.w3.org/TR/navigation-timing/#performancetiming
       * https://chromedevtools.github.io/devtools-protocol/tot/Network#type-ResourceTiming
       *
       * "Throughout this work, time is measured in milliseconds"
       */
      if (!entry.isBase64 && !entry.isBlob) {
        // resp.timing is empty when handling data:image/gif;base64,R0lGODlhAQABAIABAAAAAP///yH5BAEAAAEALAAAAAABAAEAQAICTAEAOw%3D%3D
        assert(
          typeof resp.timing !== "undefined",
          "resp.timing is empty when handling " + resp.url
        );

        // how long a given request stalled waiting for DNS, proxy, connection, SSL negotation, etc.
        entry.stalled = resp.timing.sendStart;

        // how it took to receive a first byte of the response after making a request
        entry.timeToFirstByte =
          resp.timing.receiveHeadersEnd - resp.timing.sendEnd;

        // difference between when the request was sent and when it was received
        entry.timeToLastByte = resp._timestamp - request._timestamp;
      }

      // POST requests
      if (entry.method === "POST") {
        phantomas.incrMetric("postRequests");
        phantomas.addOffender("postRequests", entry.url);
      }

      entry.headersSize = 0;

      // response content type
      // https://chromedevtools.github.io/devtools-protocol/tot/Network#type-ResourceType
      Object.keys(entry.headers).forEach((headerName) => {
        const headerValue = entry.headers[headerName];

        entry.headersSize +=
          headerName.length +
          headerValue.length +
          2 /* ": " */ +
          2; /* line break - CR+LF */

        switch (headerName) {
          // detect content type
          case "content-type":
            entry = addContentType(headerValue, entry);
            break;

          // content compression
          // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Encoding#Directives
          case "content-encoding":
            if (headerValue === "gzip" || headerValue === "br") {
              entry.gzip = true;

              phantomas.log(
                "Response compressed with %s, %f kB -> %f kB (x%f)",
                headerValue,
                entry.bodySize / 1024,
                entry.transferedSize / 1024,
                entry.bodySize / entry.transferedSize
              );
            }

            // A format using the Brotli algorithm.
            if (headerValue === "br") {
              entry.brotli = true;
            }
            break;

          // detect cookies (issue #92)
          case "set-cookie":
            entry.hasCookies = true;
            break;
        }
      });

      // HTTP code
      entry.status = resp.status || 200; // for base64 data
      entry.statusText = HTTP_STATUS_CODES[entry.status];

      switch (entry.status) {
        case 301: // Moved Permanently
        case 302: // Found
        case 303: // See Other
          entry.isRedirect = true;
          break;

        case 404: // Not Found
          phantomas.incrMetric("notFound");
          phantomas.addOffender("notFound", entry.url);
          break;
      }

      // HTTP and TLS protocols version
      entry.httpVersion = resp.protocol;
      if (resp.securityDetails) {
        entry.tlsVersion = resp.securityDetails.protocol;
      }

      // requests stats
      if (!entry.isBase64 && !entry.isBlob) {
        phantomas.incrMetric("requests");
        phantomas.addOffender("requests", {
          url: entry.url,
          type: entry.type,
          size: entry.responseSize,
        });

        phantomas.incrMetric("bodySize", entry.bodySize);
        phantomas.incrMetric("contentLength", entry.transferedSize);
      }

      if (entry.gzip) {
        phantomas.incrMetric("gzipRequests");
        phantomas.addOffender("gzipRequests", {
          url: entry.url,
          transferedSize: entry.transferedSize,
          bodySize: entry.bodySize,
        });
      }

      if (entry.isSSL) {
        phantomas.incrMetric("httpsRequests");
        phantomas.addOffender("httpsRequests", entry.url);
      }

      if (entry.isBase64) {
        phantomas.emit("base64recv", entry, resp); // @desc base64-encoded "response" has been received
      } else if (entry.isBlob) {
        // Do nothing
      } else {
        phantomas.log(
          "recv: HTTP %d <%s> [%s]",
          entry.status,
          entry.url,
          entry.contentType
        );
        phantomas.emit("recv", entry, resp); // @desc response has been received
      }

      phantomas.log("Response metadata: %j", entry);
    }
  );

  // completion of the last HTTP request
  var loadStartedTime;
  phantomas.on("loadStarted", () => (loadStartedTime = Date.now())); // when the monitoring started?

  phantomas.on("recv", (entry) =>
    phantomas.setMetric(
      "httpTrafficCompleted",
      entry.recvEndTime - loadStartedTime
    )
  );
};
<commit_msg>Run prettier<commit_after>/**
 * Simple HTTP requests monitor and analyzer
 */
"use strict";

const assert = require("assert"),
  debug = require("debug")("phantomas:modules:requestsMonitor");

/**
 * Given key-value set of HTTP headers returns the set with lowercased header names
 *
 * @param {object} headers
 * @returns {object}
 */
function lowerCaseHeaders(headers) {
  var res = {};

  Object.keys(headers).forEach((headerName) => {
    res[headerName.toLowerCase()] = headers[headerName];
  });

  return res;
}

// parse given URL to get protocol and domain
function parseEntryUrl(entry) {
  var parsed;

  // asset type
  entry.type = "other";

  if (entry.url.indexOf("data:") === 0) {
    // base64 encoded data
    entry.domain = false;
    entry.protocol = false;
    entry.isBase64 = true;
  } else if (entry.url.indexOf("blob:") === 0) {
    // blob image or video
    entry.domain = false;
    entry.protocol = false;
    entry.isBlob = true;
  } else {
    parsed = new URL(entry.url) || {};

    entry.protocol = parsed.protocol.replace(":", ""); // e.g. "http:"
    entry.domain = parsed.hostname;
    entry.query = parsed.search.substring(1);

    if (entry.protocol === "https") {
      entry.isSSL = true;
    }
  }

  return entry;
}

/**
 * Detect response content type using "Content-Type header value"
 *
 * @param {string} headerValue
 * @param {object} entry
 */
function addContentType(headerValue, entry) {
  var value = headerValue.split(";").shift().toLowerCase();
  entry.contentType = value;

  switch (value) {
    case "text/html":
      entry.type = "html";
      entry.isHTML = true;
      break;

    case "text/xml":
      entry.type = "xml";
      entry.isXML = true;
      break;

    case "text/css":
      entry.type = "css";
      entry.isCSS = true;
      break;

    case "application/x-javascript":
    case "application/javascript":
    case "text/javascript":
      entry.type = "js";
      entry.isJS = true;
      break;

    case "application/json":
      entry.type = "json";
      entry.isJSON = true;
      break;

    case "image/png":
    case "image/jpeg":
    case "image/gif":
    case "image/svg+xml":
    case "image/webp":
      entry.type = "image";
      entry.isImage = true;

      if (value === "image/svg+xml") {
        entry.isSVG = true;
      }
      break;

    case "video/webm":
      entry.type = "video";
      entry.isVideo = true;
      break;

    // @see http://stackoverflow.com/questions/2871655/proper-mime-type-for-fonts
    case "application/font-wof":
    case "application/font-woff":
    case "application/font-woff2":
    case "application/vnd.ms-fontobject":
    case "application/x-font-opentype":
    case "application/x-font-truetype":
    case "application/x-font-ttf":
    case "application/x-font-woff":
    case "font/opentype":
    case "font/ttf":
    case "font/woff":
    case "font/woff2":
      entry.type = "webfont";
      entry.isWebFont = true;

      if (/ttf|truetype$/.test(value)) {
        entry.isTTF = true;
      }
      break;

    case "application/octet-stream":
      var ext = (entry.url || "").split(".").pop();

      switch (ext) {
        // @see http://stackoverflow.com/questions/2871655/proper-mime-type-for-fonts#comment-8077637
        case "otf":
          entry.type = "webfont";
          entry.isWebFont = true;
          break;
      }
      break;

    case "image/x-icon":
    case "image/vnd.microsoft.icon":
      entry.type = "favicon";
      entry.isFavicon = true;
      break;

    default:
      debug(
        "Unknown content type found: " + value + " for <" + entry.url + ">"
      );
  }

  return entry;
}

module.exports = function (phantomas) {
  // imports
  var HTTP_STATUS_CODES = require("http").STATUS_CODES;

  // register metric
  phantomas.setMetric("requests"); // @desc total number of HTTP requests made
  phantomas.setMetric("gzipRequests"); // @desc number of gzipped HTTP responses @unreliable
  phantomas.setMetric("postRequests"); // @desc number of POST requests
  phantomas.setMetric("httpsRequests"); // @desc number of HTTPS requests
  phantomas.setMetric("notFound"); // @desc number of HTTP 404 responses
  phantomas.setMetric("bodySize"); // @desc size of the uncompressed content of all responses
  phantomas.setMetric("contentLength"); // @desc size of the compressed content of all responses, i.e. what was transfered in packets
  phantomas.setMetric("httpTrafficCompleted"); // @desc time it took to receive the last byte of the last HTTP response

  var requests = {};

  phantomas.on(
    "request",
    /** @param {Request} request **/ (request) => {
      const resId = request._requestId;
      requests[resId] = request;

      // request data
      // https://github.com/GoogleChrome/puppeteer/blob/v1.11.0/docs/api.md#class-request
      phantomas.emit("send", request); // @desc request has been sent
    }
  );

  phantomas.on(
    "response",
    /** @param {Response} resp **/ (resp) => {
      const resId = resp._requestId,
        request = requests[resId];

      if (resp.fromDiskCache === true) {
        phantomas.log("response from disk cache ignored: %j", resp);
        return;
      }

      var entry = {
        id: resId,
        url: resp.url,
        method: request.method,
        headers: lowerCaseHeaders(resp.headers), // All header names are lower-case
        bodySize: resp.dataLength,
        transferedSize: resp.encodedDataLength,
      };

      // this is set to zero when requests interception is enabled
      // use Content-Length response header instead
      if (entry.transferedSize == 0) {
        entry.transferedSize = parseInt(
          entry.headers["content-length"] || "0",
          10
        );
      }

      // that's the response size as reported by Chrome's dev tools (headers + compressed body)
      // note: base64-encoded resources do not have "resp.headersText" set
      entry.responseSize = entry.transferedSize;

      phantomas.log("headers: %j", resp);

      entry = parseEntryUrl(entry);

      /**
       * Time to First Byte is the amount of time it takes for the browser
       * to receive the first byte of data from the server
       * after the browser makes the request.
       *
       * https://developers.google.com/web/tools/chrome-devtools/network-performance/reference#timing-explanation
       * https://www.w3.org/TR/navigation-timing/#performancetiming
       * https://chromedevtools.github.io/devtools-protocol/tot/Network#type-ResourceTiming
       *
       * "Throughout this work, time is measured in milliseconds"
       */
      if (!entry.isBase64 && !entry.isBlob) {
        // resp.timing is empty when handling data:image/gif;base64,R0lGODlhAQABAIABAAAAAP///yH5BAEAAAEALAAAAAABAAEAQAICTAEAOw%3D%3D
        assert(
          typeof resp.timing !== "undefined",
          "resp.timing is empty when handling " + resp.url
        );

        // how long a given request stalled waiting for DNS, proxy, connection, SSL negotation, etc.
        entry.stalled = resp.timing.sendStart;

        // how it took to receive a first byte of the response after making a request
        entry.timeToFirstByte =
          resp.timing.receiveHeadersEnd - resp.timing.sendEnd;

        // difference between when the request was sent and when it was received
        entry.timeToLastByte = resp._timestamp - request._timestamp;
      }

      // POST requests
      if (entry.method === "POST") {
        phantomas.incrMetric("postRequests");
        phantomas.addOffender("postRequests", entry.url);
      }

      entry.headersSize = 0;

      // response content type
      // https://chromedevtools.github.io/devtools-protocol/tot/Network#type-ResourceType
      Object.keys(entry.headers).forEach((headerName) => {
        const headerValue = entry.headers[headerName];

        entry.headersSize +=
          headerName.length +
          headerValue.length +
          2 /* ": " */ +
          2; /* line break - CR+LF */

        switch (headerName) {
          // detect content type
          case "content-type":
            entry = addContentType(headerValue, entry);
            break;

          // content compression
          // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Encoding#Directives
          case "content-encoding":
            if (headerValue === "gzip" || headerValue === "br") {
              entry.gzip = true;

              phantomas.log(
                "Response compressed with %s, %f kB -> %f kB (x%f)",
                headerValue,
                entry.bodySize / 1024,
                entry.transferedSize / 1024,
                entry.bodySize / entry.transferedSize
              );
            }

            // A format using the Brotli algorithm.
            if (headerValue === "br") {
              entry.brotli = true;
            }
            break;

          // detect cookies (issue #92)
          case "set-cookie":
            entry.hasCookies = true;
            break;
        }
      });

      // HTTP code
      entry.status = resp.status || 200; // for base64 data
      entry.statusText = HTTP_STATUS_CODES[entry.status];

      switch (entry.status) {
        case 301: // Moved Permanently
        case 302: // Found
        case 303: // See Other
          entry.isRedirect = true;
          break;

        case 404: // Not Found
          phantomas.incrMetric("notFound");
          phantomas.addOffender("notFound", entry.url);
          break;
      }

      // HTTP and TLS protocols version
      entry.httpVersion = resp.protocol;
      if (resp.securityDetails) {
        entry.tlsVersion = resp.securityDetails.protocol;
      }

      // requests stats
      if (!entry.isBase64 && !entry.isBlob) {
        phantomas.incrMetric("requests");
        phantomas.addOffender("requests", {
          url: entry.url,
          type: entry.type,
          size: entry.responseSize,
        });

        phantomas.incrMetric("bodySize", entry.bodySize);
        phantomas.incrMetric("contentLength", entry.transferedSize);
      }

      if (entry.gzip) {
        phantomas.incrMetric("gzipRequests");
        phantomas.addOffender("gzipRequests", {
          url: entry.url,
          transferedSize: entry.transferedSize,
          bodySize: entry.bodySize,
        });
      }

      if (entry.isSSL) {
        phantomas.incrMetric("httpsRequests");
        phantomas.addOffender("httpsRequests", entry.url);
      }

      if (entry.isBase64) {
        phantomas.emit("base64recv", entry, resp); // @desc base64-encoded "response" has been received
      } else if (entry.isBlob) {
        // Do nothing
      } else {
        phantomas.log(
          "recv: HTTP %d <%s> [%s]",
          entry.status,
          entry.url,
          entry.contentType
        );
        phantomas.emit("recv", entry, resp); // @desc response has been received
      }

      phantomas.log("Response metadata: %j", entry);
    }
  );

  // completion of the last HTTP request
  var loadStartedTime;
  phantomas.on("loadStarted", () => (loadStartedTime = Date.now())); // when the monitoring started?

  phantomas.on("recv", (entry) =>
    phantomas.setMetric(
      "httpTrafficCompleted",
      entry.recvEndTime - loadStartedTime
    )
  );
};
<|endoftext|>


<commit_before>/*
 * jQuery doTimeout: Like setTimeout, but better! - v1.0 - 3/3/2010
 * http://benalman.com/projects/jquery-dotimeout-plugin/
 *
 * Copyright (c) 2010 "Cowboy" Ben Alman
 * Dual licensed under the MIT and GPL licenses.
 * http://benalman.com/about/license/
 */
(function($){var a={},c="doTimeout",d=Array.prototype.slice;$[c]=function(){return b.apply(window,[0].concat(d.call(arguments)))};$.fn[c]=function(){var f=d.call(arguments),e=b.apply(this,[c+f[0]].concat(f));return typeof f[0]==="number"||typeof f[1]==="number"?this:e};function b(l){var m=this,h,k={},g=l?$.fn:$,n=arguments,i=4,f=n[1],j=n[2],p=n[3];if(typeof f!=="string"){i--;f=l=0;j=n[1];p=n[2]}if(l){h=m.eq(0);h.data(l,k=h.data(l)||{})}else{if(f){k=a[f]||(a[f]={})}}k.id&&clearTimeout(k.id);delete k.id;function e(){if(l){h.removeData(l)}else{if(f){delete a[f]}}}function o(){k.id=setTimeout(function(){k.fn()},j)}if(p){k.fn=function(q){if(typeof p==="string"){p=g[p]}p.apply(m,d.call(n,i))===true&&!q?o():e()};o()}else{if(k.fn){j===undefined?e():k.fn(j===false);return true}else{e()}}}})(jQuery);

// Making sure that console.log does not throw errors on Firefox + IE etc.
if (typeof console == "undefined") var console = { log: function() {} };

var DiscoJuice = {};

/*
 * Country codes available here http://en.wikipedia.org/wiki/ISO_3166-1_alpha-2
 */
DiscoJuice.Constants = {
	"Countries": {
		'CZ': 'Czech',
		'DK': 'Denmark',
		'FI': 'Finland',
		'FR': 'France',
		'DE': 'Germany',
		'GR': 'Greece',
		'HR': 'Croatia',
		'IE': 'Ireland',
		'IT': 'Italy',
		'HU': 'Hungary',
		'LU': 'Luxembourg',
		'NL': 'Netherlands',
		'NO': 'Norway',
		'PL': 'Poland',
		'PT': 'Portugal',
		'SI': 'Slovenia',
		'ES': 'Spain',
		'SE': 'Sweden',
		'CH': 'Switzerland',
		'TR': 'Turkey',
		'US': 'USA',
		'GB': 'UK',
		'XX': 'Experimental'
	},
	"Flags": {
		'CZ': 'cz.png',
		'DK': 'dk.png',
		'FI': 'fi.png',
		'FR': 'fr.png',
		'DE': 'de.png',
		'GR': 'gr.png',
		'HR': 'hr.png',
		'IE': 'ie.png',
		'IT': 'it.png',
		'HU': 'hu.png',
		'LU': 'lu.png',
		'NL': 'nl.png',
		'NO': 'no.png',
		'PL': 'pl.png',
		'PT': 'pt.png',
		'SI': 'si.png',
		'ES': 'es.png',
		'SE': 'se.png',
		'CH': 'ch.png',
		'TR': 'tr.png',
		'GB': 'gb.png',
		'US': 'us.png',
	}
};

DiscoJuice.Utils = {
	"log": function(string) {
		console.log(string);
		// opera.postError(string);
	},
	"options": function() {
		var options;
		return {
			"get": function (key, def) {
	//			DiscoJuice.log(options);
	//			DiscoJuice.log('Getting [' + key + '] default [' + def + '] val [' + options[key] + ']');
				if (!options) return def;
				if (!options[key]) return def;
				return options[key];
			},
			"set": function(opts) {
				options = opts;
			},
			"update": function(key, value) {
				options[key] = value;
			}
		}
	}(),

	/* Functions for setting, reading and erasing cookies */
	"createCookie": function(value, type) {
		var type = type || 'EntityID';
		var name = '_DiscoJuice_' + type;
		var days = 1825;
		if (days) {
			var date = new Date();
			date.setTime(date.getTime()+(days*24*60*60*1000));
			var expires = "; expires="+date.toGMTString();
		}
		else var expires = "";
		document.cookie = name+"="+escape(value)+expires+"; path=/";
	},
	"readCookie": function(type) {
		var type = type || 'EntityID';
		var name = '_DiscoJuice_' + type;
		var days = 1825;
		var nameEQ = name + "=";
		var ca = document.cookie.split(';');
		for(var i=0;i < ca.length;i++) {
			var c = ca[i];
			while (c.charAt(0)==' ') c = c.substring(1,c.length);
			if (c.indexOf(nameEQ) == 0) return unescape(c.substring(nameEQ.length,c.length));
		}
		return null;
	},
	"eraseCookie": function (type) {
		var type = type || 'EntityID';
		var name = '_DiscoJuice_' + type;
		DiscoJuice.createCookie(name,"",-1);
	},
	/* ------ ------ ------ ------ ------ */

	/*
	 * Performs a search 'term' against an entity.
	 * If no match, return false.
	 * If match return the keyword that matches.
	 */
	"searchMatch": function(item, term) {
		if (item.title.toLowerCase().search(term.toLowerCase()) !== -1) return true;
		var key, i, keyword;

		if (item.keywords) {
			for(key in item.keywords) {
				keyword = item.keywords[key];
				for(i = 0; i < keyword.length; i++) {
					if (keyword[i].toLowerCase().search(term.toLowerCase()) !== -1) return keyword[i];
				}
			}
		}
		return false;
	},

	// calculate distance between two locations
	"calculateDistance": function (lat1, lon1, lat2, lon2) {
		var R = 6371; // km
		var dLat = this.toRad(lat2-lat1);
		var dLon = this.toRad(lon2-lon1);
		var a = Math.sin(dLat/2) * Math.sin(dLat/2) +
				Math.cos(this.toRad(lat1)) * Math.cos(this.toRad(lat2)) *
				Math.sin(dLon/2) * Math.sin(dLon/2);
		var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
		var d = R * c;
		return d;
	},

	"toRad": function (deg) {
		return deg * Math.PI/180;
	}

};

/*
	Plugin for JQuery.
	*/
(function($) {
	$.fn.DiscoJuice = function(options) {
		return this.each(function() {
			DiscoJuice.Utils.options.set(options);

			DiscoJuice.Control.ui = DiscoJuice.UI;
			DiscoJuice.UI.control = DiscoJuice.Control;

			DiscoJuice.UI.enable(this);

		});
	};
})(jQuery);

<commit_msg>Fix syntax error in DiscoJuice<commit_after>/*
 * jQuery doTimeout: Like setTimeout, but better! - v1.0 - 3/3/2010
 * http://benalman.com/projects/jquery-dotimeout-plugin/
 *
 * Copyright (c) 2010 "Cowboy" Ben Alman
 * Dual licensed under the MIT and GPL licenses.
 * http://benalman.com/about/license/
 */
(function($){var a={},c="doTimeout",d=Array.prototype.slice;$[c]=function(){return b.apply(window,[0].concat(d.call(arguments)))};$.fn[c]=function(){var f=d.call(arguments),e=b.apply(this,[c+f[0]].concat(f));return typeof f[0]==="number"||typeof f[1]==="number"?this:e};function b(l){var m=this,h,k={},g=l?$.fn:$,n=arguments,i=4,f=n[1],j=n[2],p=n[3];if(typeof f!=="string"){i--;f=l=0;j=n[1];p=n[2]}if(l){h=m.eq(0);h.data(l,k=h.data(l)||{})}else{if(f){k=a[f]||(a[f]={})}}k.id&&clearTimeout(k.id);delete k.id;function e(){if(l){h.removeData(l)}else{if(f){delete a[f]}}}function o(){k.id=setTimeout(function(){k.fn()},j)}if(p){k.fn=function(q){if(typeof p==="string"){p=g[p]}p.apply(m,d.call(n,i))===true&&!q?o():e()};o()}else{if(k.fn){j===undefined?e():k.fn(j===false);return true}else{e()}}}})(jQuery);

// Making sure that console.log does not throw errors on Firefox + IE etc.
if (typeof console == "undefined") var console = { log: function() {} };

var DiscoJuice = {};

/*
 * Country codes available here http://en.wikipedia.org/wiki/ISO_3166-1_alpha-2
 */
DiscoJuice.Constants = {
	"Countries": {
		'CZ': 'Czech',
		'DK': 'Denmark',
		'FI': 'Finland',
		'FR': 'France',
		'DE': 'Germany',
		'GR': 'Greece',
		'HR': 'Croatia',
		'IE': 'Ireland',
		'IT': 'Italy',
		'HU': 'Hungary',
		'LU': 'Luxembourg',
		'NL': 'Netherlands',
		'NO': 'Norway',
		'PL': 'Poland',
		'PT': 'Portugal',
		'SI': 'Slovenia',
		'ES': 'Spain',
		'SE': 'Sweden',
		'CH': 'Switzerland',
		'TR': 'Turkey',
		'US': 'USA',
		'GB': 'UK',
		'XX': 'Experimental'
	},
	"Flags": {
		'CZ': 'cz.png',
		'DK': 'dk.png',
		'FI': 'fi.png',
		'FR': 'fr.png',
		'DE': 'de.png',
		'GR': 'gr.png',
		'HR': 'hr.png',
		'IE': 'ie.png',
		'IT': 'it.png',
		'HU': 'hu.png',
		'LU': 'lu.png',
		'NL': 'nl.png',
		'NO': 'no.png',
		'PL': 'pl.png',
		'PT': 'pt.png',
		'SI': 'si.png',
		'ES': 'es.png',
		'SE': 'se.png',
		'CH': 'ch.png',
		'TR': 'tr.png',
		'GB': 'gb.png',
		'US': 'us.png'
	}
};

DiscoJuice.Utils = {
	"log": function(string) {
		console.log(string);
		// opera.postError(string);
	},
	"options": function() {
		var options;
		return {
			"get": function (key, def) {
	//			DiscoJuice.log(options);
	//			DiscoJuice.log('Getting [' + key + '] default [' + def + '] val [' + options[key] + ']');
				if (!options) return def;
				if (!options[key]) return def;
				return options[key];
			},
			"set": function(opts) {
				options = opts;
			},
			"update": function(key, value) {
				options[key] = value;
			}
		}
	}(),

	/* Functions for setting, reading and erasing cookies */
	"createCookie": function(value, type) {
		var type = type || 'EntityID';
		var name = '_DiscoJuice_' + type;
		var days = 1825;
		if (days) {
			var date = new Date();
			date.setTime(date.getTime()+(days*24*60*60*1000));
			var expires = "; expires="+date.toGMTString();
		}
		else var expires = "";
		document.cookie = name+"="+escape(value)+expires+"; path=/";
	},
	"readCookie": function(type) {
		var type = type || 'EntityID';
		var name = '_DiscoJuice_' + type;
		var days = 1825;
		var nameEQ = name + "=";
		var ca = document.cookie.split(';');
		for(var i=0;i < ca.length;i++) {
			var c = ca[i];
			while (c.charAt(0)==' ') c = c.substring(1,c.length);
			if (c.indexOf(nameEQ) == 0) return unescape(c.substring(nameEQ.length,c.length));
		}
		return null;
	},
	"eraseCookie": function (type) {
		var type = type || 'EntityID';
		var name = '_DiscoJuice_' + type;
		DiscoJuice.createCookie(name,"",-1);
	},
	/* ------ ------ ------ ------ ------ */

	/*
	 * Performs a search 'term' against an entity.
	 * If no match, return false.
	 * If match return the keyword that matches.
	 */
	"searchMatch": function(item, term) {
		if (item.title.toLowerCase().search(term.toLowerCase()) !== -1) return true;
		var key, i, keyword;

		if (item.keywords) {
			for(key in item.keywords) {
				keyword = item.keywords[key];
				for(i = 0; i < keyword.length; i++) {
					if (keyword[i].toLowerCase().search(term.toLowerCase()) !== -1) return keyword[i];
				}
			}
		}
		return false;
	},

	// calculate distance between two locations
	"calculateDistance": function (lat1, lon1, lat2, lon2) {
		var R = 6371; // km
		var dLat = this.toRad(lat2-lat1);
		var dLon = this.toRad(lon2-lon1);
		var a = Math.sin(dLat/2) * Math.sin(dLat/2) +
				Math.cos(this.toRad(lat1)) * Math.cos(this.toRad(lat2)) *
				Math.sin(dLon/2) * Math.sin(dLon/2);
		var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
		var d = R * c;
		return d;
	},

	"toRad": function (deg) {
		return deg * Math.PI/180;
	}

};

/*
	Plugin for JQuery.
	*/
(function($) {
	$.fn.DiscoJuice = function(options) {
		return this.each(function() {
			DiscoJuice.Utils.options.set(options);

			DiscoJuice.Control.ui = DiscoJuice.UI;
			DiscoJuice.UI.control = DiscoJuice.Control;

			DiscoJuice.UI.enable(this);

		});
	};
})(jQuery);

<|endoftext|>


<commit_before>import * as commentsTypes from './commentsActionTypes';
import * as userProfileTypes from './../user/userProfileActionTypes';

const initialState = {
  lists: {},
  comments: {},
  commentingDraft: {},
  isCommenting: false,
  currentDraftId: null,
};

const initialCommentingDraftItem = {
  parentAuthor: null,
  parentPermlink: null,
  category: null,
  body: null,
};

const commentsList = (state = {}, action) => {
  switch (action.type) {
    case commentsTypes.GET_COMMENTS_START:
      return {
        ...state,
        isFetching: true,
        list: [],
        show: 1,
      };
    case commentsTypes.GET_COMMENTS_SUCCESS:
      const list = action.payload.map(comment => comment.id);
      return {
        ...state,
        list,
        isFetching: false,
      };
    case commentsTypes.GET_MORE_COMMENTS_START:
      return {
        ...state,
        isFetching: true,
      };
    case commentsTypes.GET_MORE_COMMENTS_SUCCESS:
      const moreList = action.payload.map(comment => comment.id);
      const show = state.show === 1? 10 : state.show + 10;
      return {
        ...state,
        isFetching: true,
        list: [
          ...state.list,
          moreList,
        ],
        show,
      };
    default:
      return state;
  }
};

const commentsLists = (state = {}, action) => {
  switch (action.type) {
    case commentsTypes.GET_COMMENTS_START:
    case commentsTypes.GET_COMMENTS_SUCCESS:
    case commentsTypes.GET_MORE_COMMENTS_START:
    case commentsTypes.GET_MORE_COMMENTS_SUCCESS:
      return {
        ...state,
        [action.meta.id]: commentsList(state[action.meta.id], action),
      };
    default:
      return state;
  }
};

const commentsData = (state = {}, action) => {
  switch (action.type) {
    case commentsTypes.GET_COMMENTS_SUCCESS:
    case commentsTypes.GET_MORE_COMMENTS_SUCCESS:
      const data = {};
      action.payload.forEach((comment) => {
        data[comment.id] = comment;
      });
      return {
        ...state,
        ...data,
      };
    case userProfileTypes.GET_USER_COMMENTS_SUCCESS:
      const commentsList = {};
      // map data based on id
      Object.keys(action.payload.content).forEach((key) => {
        const { id } = action.payload.content[key];
        commentsList[id] = action.payload.content[key];
      });
      return {
        ...state,
        ...commentsList,
      };
    case userProfileTypes.GET_MORE_USER_COMMENTS_SUCCESS:
      const commentsMoreList = {};
      action.payload.result.forEach(comment => {
        commentsMoreList[comment.id] = comment;
      });
      return {
        ...state,
        ...commentsMoreList,
      };
    default:
      return state;
  }
};

const commentingDraftItem = (state = initialCommentingDraftItem, action) => {
  switch (action.type) {
    case commentsTypes.OPEN_COMMENTING_DRAFT:
      const { parentAuthor, parentPermlink, category } = action.payload;
      return {
        ...state,
        parentAuthor,
        parentPermlink,
        category,
      };
    case commentsTypes.UPDATE_COMMENTING_DRAFT:
      return {
        ...state,
        body: action.payload.body,
      };
    default:
      return state;
  }
};

const commentingDraft = (state = {}, action) => {
  switch (action.type) {
    case commentsTypes.OPEN_COMMENTING_DRAFT:
    case commentsTypes.UPDATE_COMMENTING_DRAFT:
      const { id } = action.payload;
      return {
        ...state,
        [id]: commentingDraftItem(state[id], action),
      };
    default:
      return state;
  }
};

const comments = (state = initialState, action) => {
  switch (action.type) {
    case commentsTypes.GET_COMMENTS_START:
    case commentsTypes.GET_COMMENTS_SUCCESS:
    case userProfileTypes.GET_USER_COMMENTS_SUCCESS:
    case userProfileTypes.GET_MORE_USER_COMMENTS_SUCCESS:
    case commentsTypes.GET_MORE_COMMENTS_START:
    case commentsTypes.GET_MORE_COMMENTS_SUCCESS:
      return {
        ...state,
        comments: commentsData(state.comments, action),
        lists: commentsLists(state.lists, action),
      };
    case commentsTypes.OPEN_COMMENTING_DRAFT:
    case commentsTypes.UPDATE_COMMENTING_DRAFT:
      return {
        ...state,
        commentingDraft: commentingDraft(state.commentingDraft, action),
        isCommenting: true,
        currentDraftId: action.payload.id,
      };
    case commentsTypes.CLOSE_COMMENTING_DRAFT:
      return {
        ...state,
        commentingDraft: commentingDraft(state.commentingDraft, action),
        isCommenting: false,
        currentDraftId: null,
      };
    default:
      return state;
  }
};

export default comments;
<commit_msg>fix the reducer for comments<commit_after>import * as commentsTypes from './commentsActionTypes';
import * as userProfileTypes from './../user/userProfileActionTypes';

const initialState = {
  lists: {},
  comments: {},
  commentingDraft: {},
  isCommenting: false,
  currentDraftId: null,
};

const initialCommentingDraftItem = {
  parentAuthor: null,
  parentPermlink: null,
  category: null,
  body: null,
};

const commentsList = (state = {}, action) => {
  switch (action.type) {
    case commentsTypes.GET_COMMENTS_START:
      return {
        ...state,
        isFetching: true,
        list: [],
        show: 1,
        hasMore: true,
      };
    case commentsTypes.GET_COMMENTS_SUCCESS:
      return {
        ...state,
        list: action.payload.list,
        isFetching: false,
      };
    case commentsTypes.GET_MORE_COMMENTS_START:
      return {
        ...state,
        isFetching: true,
      };
    case commentsTypes.GET_MORE_COMMENTS_SUCCESS:
      let hasMore = true;
      if(action.payload[action.payload.length - 1].id === state.list[state.list.length - 1]) {
        // there are no more comments
        hasMore = false;
      }
      const moreList = action.payload.map(comment => comment.id);
      return {
        ...state,
        isFetching: false,
        hasMore,
        list: [
          ...state.list,
          ...moreList,
        ],
      };
    case commentsTypes.SHOW_MORE_COMMENTS:
      return {
        ...state,
        show: state.show === 1 ? 10 : state.show + 10,
      };
    default:
      return state;
  }
};

const commentsLists = (state = {}, action) => {
  switch (action.type) {
    case commentsTypes.GET_COMMENTS_START:
    case commentsTypes.GET_COMMENTS_SUCCESS:
    case commentsTypes.GET_MORE_COMMENTS_START:
    case commentsTypes.GET_MORE_COMMENTS_SUCCESS:
    case commentsTypes.SHOW_MORE_COMMENTS:
      return {
        ...state,
        [action.meta.id]: commentsList(state[action.meta.id], action),
      };
    default:
      return state;
  }
};

const mapCommentsBasedOnId = (data) => {
  const commentsList = {};
  Object.keys(data).forEach((key) => {
    commentsList[data[key].id] = data[key];
  });
  return commentsList;
};

const commentsData = (state = {}, action) => {
  switch (action.type) {
    case commentsTypes.GET_COMMENTS_SUCCESS:
    case userProfileTypes.GET_USER_COMMENTS_SUCCESS:
      return {
        ...state,
        ...mapCommentsBasedOnId(action.payload.content),
      };
    case userProfileTypes.GET_MORE_USER_COMMENTS_SUCCESS:
      const commentsMoreList = {};
      action.payload.result.forEach(comment => {
        commentsMoreList[comment.id] = comment;
      });
      return {
        ...state,
        ...commentsMoreList,
      };
    default:
      return state;
  }
};

const commentingDraftItem = (state = initialCommentingDraftItem, action) => {
  switch (action.type) {
    case commentsTypes.OPEN_COMMENTING_DRAFT:
      const { parentAuthor, parentPermlink, category } = action.payload;
      return {
        ...state,
        parentAuthor,
        parentPermlink,
        category,
      };
    case commentsTypes.UPDATE_COMMENTING_DRAFT:
      return {
        ...state,
        body: action.payload.body,
      };
    default:
      return state;
  }
};

const commentingDraft = (state = {}, action) => {
  switch (action.type) {
    case commentsTypes.OPEN_COMMENTING_DRAFT:
    case commentsTypes.UPDATE_COMMENTING_DRAFT:
      const { id } = action.payload;
      return {
        ...state,
        [id]: commentingDraftItem(state[id], action),
      };
    default:
      return state;
  }
};

const comments = (state = initialState, action) => {
  switch (action.type) {
    case commentsTypes.GET_COMMENTS_START:
    case commentsTypes.GET_COMMENTS_SUCCESS:
    case userProfileTypes.GET_USER_COMMENTS_SUCCESS:
    case userProfileTypes.GET_MORE_USER_COMMENTS_SUCCESS:
    case commentsTypes.GET_MORE_COMMENTS_START:
    case commentsTypes.GET_MORE_COMMENTS_SUCCESS:
    case commentsTypes.SHOW_MORE_COMMENTS:
      return {
        ...state,
        comments: commentsData(state.comments, action),
        lists: commentsLists(state.lists, action),
      };
    case commentsTypes.OPEN_COMMENTING_DRAFT:
    case commentsTypes.UPDATE_COMMENTING_DRAFT:
      return {
        ...state,
        commentingDraft: commentingDraft(state.commentingDraft, action),
        isCommenting: true,
        currentDraftId: action.payload.id,
      };
    case commentsTypes.CLOSE_COMMENTING_DRAFT:
      return {
        ...state,
        commentingDraft: commentingDraft(state.commentingDraft, action),
        isCommenting: false,
        currentDraftId: null,
      };
    default:
      return state;
  }
};

export default comments;
<|endoftext|>