<commit_before>import PropTypes from 'prop-types';
import React, { Component } from 'react';
import ReactPlayer from 'react-player';
import { Color } from 'constants/css';
import { connect } from 'react-redux';
import { auth } from 'helpers/requestHelpers';
import {
  addVideoView,
  fillCurrentVideoSlot,
  emptyCurrentVideoSlot
} from 'redux/actions/VideoActions';
import { changeUserXP } from 'redux/actions/UserActions';
import request from 'axios';
import StarMark from 'components/StarMark';
import { URL } from 'constants/URL';
import ErrorBoundary from 'components/Wrappers/ErrorBoundary';
import ProgressBar from 'components/ProgressBar';
import Icon from 'components/Icon';
import Spinner from 'components/Spinner';
import { css } from 'emotion';

const CONTENT_URL = `${URL}/content`;
const VIDEO_URL = `${URL}/video`;
const intervalLength = 2000;
const denominator = 4;
const requiredDurationCap = 150;

class VideoPlayer extends Component {
  static propTypes = {
    addVideoView: PropTypes.func.isRequired,
    chatMode: PropTypes.bool,
    emptyCurrentVideoSlot: PropTypes.func,
    fillCurrentVideoSlot: PropTypes.func,
    hasHqThumb: PropTypes.number,
    changeUserXP: PropTypes.func,
    isStarred: PropTypes.bool,
    minimized: PropTypes.bool,
    stretch: PropTypes.bool,
    onEdit: PropTypes.bool,
    pageVisible: PropTypes.bool,
    currentVideoSlot: PropTypes.number,
    style: PropTypes.object,
    userId: PropTypes.number,
    videoCode: PropTypes.string.isRequired,
    videoId: PropTypes.oneOfType([PropTypes.string, PropTypes.number])
      .isRequired
  };

  interval = null;
  Player = null;
  rewardingXP = false;
  playing = false;

  state = {
    started: false,
    timeWatched: 0,
    totalDuration: 0,
    xpEarned: false,
    justEarned: false,
    imageUrl: ''
  };

  async componentDidMount() {
    const { hasHqThumb, isStarred, userId, videoCode, videoId } = this.props;
    this.mounted = true;

    if (videoCode && typeof hasHqThumb !== 'number') {
      try {
        const {
          data: { payload }
        } = await request.put(`${CONTENT_URL}/videoThumb`, {
          videoCode,
          videoId
        });
        if (this.mounted) {
          this.setState({
            imageUrl:
              payload || `https://img.youtube.com/vi/${videoCode}/mqdefault.jpg`
          });
        }
      } catch (error) {
        console.error(error.response || error);
      }
    } else {
      const imageName = hasHqThumb ? 'maxresdefault' : 'mqdefault';
      this.setState({
        imageUrl: `https://img.youtube.com/vi/${videoCode}/${imageName}.jpg`
      });
    }

    if (isStarred && userId) {
      try {
        const {
          data: { xpEarned }
        } = await request.get(
          `${VIDEO_URL}/xpEarned?videoId=${videoId}`,
          auth()
        );
        if (this.mounted) this.setState(() => ({ xpEarned: !!xpEarned }));
      } catch (error) {
        console.error(error.response || error);
      }
    }
  }

  async componentDidUpdate(prevProps) {
    const {
      onEdit,
      chatMode,
      currentVideoSlot,
      hasHqThumb,
      isStarred,
      pageVisible,
      userId,
      videoCode,
      videoId
    } = this.props;
    const { started, xpEarned, justEarned } = this.state;
    if (prevProps.onEdit !== onEdit) {
      if (onEdit === true) this.onVideoStop();
      this.setState({ started: false });
    }
    const userWatchingMultipleVideo =
      currentVideoSlot &&
      currentVideoSlot !== prevProps.currentVideoSlot &&
      currentVideoSlot !== Number(videoId);
    const alreadyEarned = xpEarned || justEarned;

    if (prevProps.userId && !userId) {
      this.setState(state => ({
        ...state,
        timeWatched: 0,
        xpEarned: false,
        justEarned: false
      }));
    }

    if (prevProps.videoCode !== videoCode) {
      const newImageName = hasHqThumb ? 'maxresdefault' : 'mqdefault';
      this.setState({
        imageUrl: `https://img.youtube.com/vi/${videoCode}/${newImageName}.jpg`
      });
    }

    if (isStarred && userId && userId !== prevProps.userId) {
      try {
        const {
          data: { xpEarned }
        } = await request.get(
          `${VIDEO_URL}/xpEarned?videoId=${videoId}`,
          auth()
        );
        if (xpEarned && this.mounted) {
          this.setState(() => ({ xpEarned: !!xpEarned }));
        }
      } catch (error) {
        console.error(error.response || error);
      }
    }

    if (started && userWatchingMultipleVideo) {
      this.onVideoStop();
      if (this.Player) {
        if (this.Player.getInternalPlayer()) {
          this.Player.getInternalPlayer().pauseVideo();
        }
      }
    }

    if (
      started &&
      isStarred &&
      userId &&
      pageVisible !== prevProps.pageVisible &&
      !alreadyEarned
    ) {
      this.onVideoStop();
      if (this.Player) {
        if (this.Player.getInternalPlayer()) {
          this.Player.getInternalPlayer().pauseVideo();
        }
      }
    }

    if (
      started &&
      isStarred &&
      chatMode !== prevProps.chatMode &&
      !alreadyEarned
    ) {
      this.onVideoStop();
      if (this.Player) {
        if (this.Player.getInternalPlayer()) {
          this.Player.getInternalPlayer().pauseVideo();
        }
      }
    }
  }

  componentWillUnmount() {
    this.onVideoStop();
    this.Player = undefined;
    this.mounted = false;
  }

  render() {
    const {
      isStarred,
      minimized,
      stretch,
      onEdit,
      videoCode,
      style = {},
      userId
    } = this.props;
    const {
      imageUrl,
      started,
      timeWatched,
      totalDuration,
      xpEarned,
      justEarned
    } = this.state;
    const requiredViewDuration = Math.min(
      totalDuration / denominator,
      requiredDurationCap
    );
    const progress = xpEarned
      ? 100
      : requiredViewDuration > 0
        ? Math.floor(
            (Math.min(timeWatched, requiredViewDuration) * 100) /
              requiredViewDuration
          )
        : 0;
    return (
      <ErrorBoundary style={style}>
        <div
          className={`${css`
            user-select: none;
            position: relative;
            padding-top: 56.25%;
          `}${minimized ? ' desktop' : ''}`}
          style={{
            display: minimized && !started && 'none',
            width: started && minimized && '39rem',
            paddingTop: started && minimized && '22rem',
            position: started && minimized && 'absolute',
            bottom: started && minimized && '1rem',
            right: started && minimized && '1rem',
            cursor: !onEdit && !started && 'pointer'
          }}
          onClick={() => {
            if (!onEdit && !started) {
              this.setState({ started: true });
            }
          }}
        >
          {!minimized &&
            !started && (
              <>
                <img
                  alt=""
                  src={imageUrl}
                  className={css`
                    position: absolute;
                    width: 100%;
                    height: 100%;
                    top: 0;
                    right: 0;
                    left: 0;
                    bottom: 0;
                  `}
                />
                {isStarred && (
                  <StarMark
                    style={{
                      top: '1rem',
                      left: '1rem'
                    }}
                  />
                )}
              </>
            )}
          {!onEdit && (
            <ReactPlayer
              ref={ref => {
                this.Player = ref;
              }}
              className={css`
                position: absolute;
                top: 0;
                left: 0;
                z-index: 1;
              `}
              width="100%"
              height="100%"
              url={`https://www.youtube.com/watch?v=${videoCode}`}
              playing={started}
              controls
              config={{
                youtube: { preload: true }
              }}
              onReady={this.onVideoReady}
              onPlay={this.onVideoPlay}
              onPause={this.onVideoStop}
              onEnded={this.onVideoStop}
            />
          )}
          {!onEdit && !minimized && started ? (
            <div
              className={css`
                position: absolute;
                top: 0;
                bottom: 0;
                left: 0;
                right: 0;
                font-size: 3rem;
                display: flex;
                align-items: center;
                justify-content: center;
              `}
              style={style}
            >
              <Spinner />
            </div>
          ) : !onEdit ? (
            <a
              className={css`
                position: absolute;
                display: block;
                background: url('/img/play-button-image.png');
                background-size: contain;
                height: 5rem;
                width: 5rem;
                top: 50%;
                left: 50%;
                margin: -2.5rem 0 0 -2.5rem;
              `}
            />
          ) : null}
        </div>
        {isStarred &&
          (!started || xpEarned) && (
            <div
              style={{
                background: xpEarned ? Color.green() : Color.logoBlue(),
                padding: '0.5rem',
                color: '#fff',
                fontSize: '1.5rem',
                fontWeight: 'bold',
                textAlign: 'center'
              }}
            >
              {!xpEarned && <Icon icon="star" />}
              {xpEarned
                ? 'You have already earned XP from this video'
                : ' Watch this video and get 100XP'}
            </div>
          )}
        {!xpEarned &&
          isStarred &&
          userId &&
          started && (
            <ProgressBar
              progress={progress}
              noBorderRadius={stretch}
              color={justEarned ? Color.green() : Color.blue()}
              text={justEarned ? 'Earned 100 XP!' : ''}
            />
          )}
      </ErrorBoundary>
    );
  }

  onVideoReady = () => {
    if (this.Player.getInternalPlayer()) {
      this.setState(() => ({
        totalDuration: this.Player.getInternalPlayer().getDuration()
      }));
    }
  };

  onVideoPlay = () => {
    const {
      currentVideoSlot,
      isStarred,
      videoId,
      userId,
      addVideoView,
      fillCurrentVideoSlot
    } = this.props;
    const { justEarned, xpEarned } = this.state;
    this.setState({ started: true });
    if (!this.playing) {
      this.playing = true;
      const time = this.Player.getCurrentTime();
      if (Math.floor(time) === 0) {
        addVideoView({ videoId, userId });
      }
      if (!currentVideoSlot) {
        fillCurrentVideoSlot(Number(videoId));
        if (userId) {
          this.interval = setInterval(this.increaseProgress, intervalLength);
        }
      }
      const authorization = auth();
      const authExists = !!authorization.headers.authorization;
      if (authExists && isStarred && !(justEarned || xpEarned)) {
        try {
          request.put(
            `${VIDEO_URL}/currentlyWatching`,
            { videoId },
            authorization
          );
        } catch (error) {
          console.error(error.response || error);
        }
      }
    }
  };

  onVideoStop = () => {
    const { emptyCurrentVideoSlot } = this.props;
    this.playing = false;
    clearInterval(this.interval);
    emptyCurrentVideoSlot();
    const authorization = auth();
    const authExists = !!authorization.headers.authorization;
    if (authExists) {
      try {
        request.put(
          `${VIDEO_URL}/currentlyWatching`,
          { videoId: null },
          authorization
        );
      } catch (error) {
        console.error(error.response || error);
      }
    }
  };

  increaseProgress = async() => {
    const { justEarned, xpEarned, timeWatched, totalDuration } = this.state;
    const { changeUserXP, isStarred, videoId } = this.props;
    if (isStarred && !xpEarned && !justEarned && this.Player) {
      if (this.Player.getInternalPlayer()) {
        if (this.Player.getInternalPlayer().isMuted()) {
          this.Player.getInternalPlayer().unMute();
        }
        if (this.Player.getInternalPlayer().getVolume() < 30) {
          this.Player.getInternalPlayer().setVolume(30);
        }
      }
    }
    if (
      isStarred &&
      timeWatched >=
        Math.min(totalDuration / denominator, requiredDurationCap) &&
      !this.rewardingXP
    ) {
      this.rewardingXP = true;
      try {
        await request.put(`${VIDEO_URL}/xpEarned`, { videoId }, auth());
        await changeUserXP({
          type: 'increase',
          action: 'watch',
          target: 'video',
          targetId: videoId,
          amount: 100
        });
        if (this.mounted) {
          this.setState(() => ({
            justEarned: true
          }));
        }
        this.rewardingXP = false;
      } catch (error) {
        console.error(error.response || error);
      }
    }
    if (!xpEarned && this.mounted) {
      this.setState(state => ({
        timeWatched: state.timeWatched + intervalLength / 1000
      }));
    }
    const authorization = auth();
    const authExists = !!authorization.headers.authorization;
    if (authExists) {
      try {
        const {
          data: { currentlyWatchingAnotherVideo, success }
        } = await request.put(
          `${VIDEO_URL}/duration`,
          { videoId, isStarred, xpEarned },
          authorization
        );
        if (success) return;
        if (currentlyWatchingAnotherVideo) {
          if (this.Player) {
            if (this.Player.getInternalPlayer()) {
              this.Player.getInternalPlayer().pauseVideo();
            }
          }
        }
      } catch (error) {
        console.error(error.response || error);
      }
    }
  };
}

export default connect(
  state => ({
    chatMode: state.ChatReducer.chatMode,
    userId: state.UserReducer.userId,
    pageVisible: state.ViewReducer.pageVisible,
    currentVideoSlot: state.VideoReducer.currentVideoSlot
  }),
  {
    addVideoView,
    changeUserXP,
    fillCurrentVideoSlot,
    emptyCurrentVideoSlot
  }
)(VideoPlayer);
<commit_msg>minor change<commit_after>import PropTypes from 'prop-types';
import React, { Component } from 'react';
import ReactPlayer from 'react-player';
import { Color } from 'constants/css';
import { connect } from 'react-redux';
import { auth } from 'helpers/requestHelpers';
import {
  addVideoView,
  fillCurrentVideoSlot,
  emptyCurrentVideoSlot
} from 'redux/actions/VideoActions';
import { changeUserXP } from 'redux/actions/UserActions';
import request from 'axios';
import StarMark from 'components/StarMark';
import { URL } from 'constants/URL';
import ErrorBoundary from 'components/Wrappers/ErrorBoundary';
import ProgressBar from 'components/ProgressBar';
import Icon from 'components/Icon';
import Spinner from 'components/Spinner';
import { css } from 'emotion';

const CONTENT_URL = `${URL}/content`;
const VIDEO_URL = `${URL}/video`;
const intervalLength = 2000;
const denominator = 4;
const requiredDurationCap = 150;

class VideoPlayer extends Component {
  static propTypes = {
    addVideoView: PropTypes.func.isRequired,
    chatMode: PropTypes.bool,
    emptyCurrentVideoSlot: PropTypes.func,
    fillCurrentVideoSlot: PropTypes.func,
    hasHqThumb: PropTypes.number,
    changeUserXP: PropTypes.func,
    isStarred: PropTypes.bool,
    minimized: PropTypes.bool,
    stretch: PropTypes.bool,
    onEdit: PropTypes.bool,
    pageVisible: PropTypes.bool,
    currentVideoSlot: PropTypes.number,
    style: PropTypes.object,
    userId: PropTypes.number,
    videoCode: PropTypes.string.isRequired,
    videoId: PropTypes.oneOfType([PropTypes.string, PropTypes.number])
      .isRequired
  };

  interval = null;
  Player = null;
  rewardingXP = false;
  playing = false;

  state = {
    started: false,
    timeWatched: 0,
    totalDuration: 0,
    xpLoaded: false,
    xpEarned: false,
    justEarned: false,
    imageUrl: ''
  };

  async componentDidMount() {
    const { hasHqThumb, isStarred, userId, videoCode, videoId } = this.props;
    this.mounted = true;

    if (videoCode && typeof hasHqThumb !== 'number') {
      try {
        const {
          data: { payload }
        } = await request.put(`${CONTENT_URL}/videoThumb`, {
          videoCode,
          videoId
        });
        if (this.mounted) {
          this.setState({
            imageUrl:
              payload || `https://img.youtube.com/vi/${videoCode}/mqdefault.jpg`
          });
        }
      } catch (error) {
        console.error(error.response || error);
      }
    } else {
      const imageName = hasHqThumb ? 'maxresdefault' : 'mqdefault';
      this.setState({
        imageUrl: `https://img.youtube.com/vi/${videoCode}/${imageName}.jpg`
      });
    }

    if (isStarred && userId) {
      try {
        const {
          data: { xpEarned }
        } = await request.get(
          `${VIDEO_URL}/xpEarned?videoId=${videoId}`,
          auth()
        );
        if (this.mounted) {
          this.setState(state => ({ xpEarned: !!xpEarned, xpLoaded: true }));
        }
      } catch (error) {
        console.error(error.response || error);
      }
    }
  }

  async componentDidUpdate(prevProps) {
    const {
      onEdit,
      chatMode,
      currentVideoSlot,
      hasHqThumb,
      isStarred,
      pageVisible,
      userId,
      videoCode,
      videoId
    } = this.props;
    const { started, xpEarned, justEarned } = this.state;
    if (prevProps.onEdit !== onEdit) {
      if (onEdit === true) this.onVideoStop();
      this.setState({ started: false });
    }
    const userWatchingMultipleVideo =
      currentVideoSlot &&
      currentVideoSlot !== prevProps.currentVideoSlot &&
      currentVideoSlot !== Number(videoId);
    const alreadyEarned = xpEarned || justEarned;

    if (prevProps.userId && !userId) {
      this.setState(state => ({
        ...state,
        timeWatched: 0,
        xpEarned: false,
        justEarned: false,
        xpLoaded: false
      }));
    }

    if (prevProps.videoCode !== videoCode) {
      const newImageName = hasHqThumb ? 'maxresdefault' : 'mqdefault';
      this.setState({
        imageUrl: `https://img.youtube.com/vi/${videoCode}/${newImageName}.jpg`
      });
    }

    if (isStarred && userId && userId !== prevProps.userId) {
      try {
        const {
          data: { xpEarned }
        } = await request.get(
          `${VIDEO_URL}/xpEarned?videoId=${videoId}`,
          auth()
        );
        if (this.mounted) {
          this.setState(state => ({
            xpEarned: !!xpEarned,
            xpLoaded: true
          }));
        }
      } catch (error) {
        console.error(error.response || error);
      }
    }

    if (started && userWatchingMultipleVideo) {
      this.onVideoStop();
      if (this.Player) {
        if (this.Player.getInternalPlayer()) {
          this.Player.getInternalPlayer().pauseVideo();
        }
      }
    }

    if (
      started &&
      isStarred &&
      userId &&
      pageVisible !== prevProps.pageVisible &&
      !alreadyEarned
    ) {
      this.onVideoStop();
      if (this.Player) {
        if (this.Player.getInternalPlayer()) {
          this.Player.getInternalPlayer().pauseVideo();
        }
      }
    }

    if (
      started &&
      isStarred &&
      chatMode !== prevProps.chatMode &&
      !alreadyEarned
    ) {
      this.onVideoStop();
      if (this.Player) {
        if (this.Player.getInternalPlayer()) {
          this.Player.getInternalPlayer().pauseVideo();
        }
      }
    }
  }

  componentWillUnmount() {
    this.onVideoStop();
    this.Player = undefined;
    this.mounted = false;
  }

  render() {
    const {
      isStarred,
      minimized,
      stretch,
      onEdit,
      videoCode,
      style = {},
      userId
    } = this.props;
    const {
      imageUrl,
      started,
      timeWatched,
      totalDuration,
      xpEarned,
      xpLoaded,
      justEarned
    } = this.state;
    const requiredViewDuration = Math.min(
      totalDuration / denominator,
      requiredDurationCap
    );
    const progress = xpEarned
      ? 100
      : requiredViewDuration > 0
        ? Math.floor(
            (Math.min(timeWatched, requiredViewDuration) * 100) /
              requiredViewDuration
          )
        : 0;
    return (
      <ErrorBoundary style={style}>
        <div
          className={`${css`
            user-select: none;
            position: relative;
            padding-top: 56.25%;
          `}${minimized ? ' desktop' : ''}`}
          style={{
            display: minimized && !started && 'none',
            width: started && minimized && '39rem',
            paddingTop: started && minimized && '22rem',
            position: started && minimized && 'absolute',
            bottom: started && minimized && '1rem',
            right: started && minimized && '1rem',
            cursor: !onEdit && !started && 'pointer'
          }}
          onClick={() => {
            if (!onEdit && !started) {
              this.setState({ started: true });
            }
          }}
        >
          {!minimized &&
            !started && (
              <>
                <img
                  alt=""
                  src={imageUrl}
                  className={css`
                    position: absolute;
                    width: 100%;
                    height: 100%;
                    top: 0;
                    right: 0;
                    left: 0;
                    bottom: 0;
                  `}
                />
                {isStarred && (
                  <StarMark
                    style={{
                      top: '1rem',
                      left: '1rem'
                    }}
                  />
                )}
              </>
            )}
          {!onEdit && (
            <ReactPlayer
              ref={ref => {
                this.Player = ref;
              }}
              className={css`
                position: absolute;
                top: 0;
                left: 0;
                z-index: 1;
              `}
              width="100%"
              height="100%"
              url={`https://www.youtube.com/watch?v=${videoCode}`}
              playing={started}
              controls
              config={{
                youtube: { preload: true }
              }}
              onReady={this.onVideoReady}
              onPlay={this.onVideoPlay}
              onPause={this.onVideoStop}
              onEnded={this.onVideoStop}
            />
          )}
          {!onEdit && !minimized && started ? (
            <div
              className={css`
                position: absolute;
                top: 0;
                bottom: 0;
                left: 0;
                right: 0;
                font-size: 3rem;
                display: flex;
                align-items: center;
                justify-content: center;
              `}
              style={style}
            >
              <Spinner />
            </div>
          ) : !onEdit ? (
            <a
              className={css`
                position: absolute;
                display: block;
                background: url('/img/play-button-image.png');
                background-size: contain;
                height: 5rem;
                width: 5rem;
                top: 50%;
                left: 50%;
                margin: -2.5rem 0 0 -2.5rem;
              `}
            />
          ) : null}
        </div>
        {xpLoaded &&
          isStarred &&
          (!started || xpEarned) && (
            <div
              style={{
                background: xpEarned ? Color.green() : Color.logoBlue(),
                padding: '0.5rem',
                color: '#fff',
                fontSize: '1.5rem',
                fontWeight: 'bold',
                textAlign: 'center'
              }}
            >
              {!xpEarned && <Icon icon="star" />}
              {xpEarned
                ? 'You have already earned XP from this video'
                : ' Watch this video and get 100XP'}
            </div>
          )}
        {!xpEarned &&
          isStarred &&
          userId &&
          started && (
            <ProgressBar
              progress={progress}
              noBorderRadius={stretch}
              color={justEarned ? Color.green() : Color.blue()}
              text={justEarned ? 'Earned 100 XP!' : ''}
            />
          )}
      </ErrorBoundary>
    );
  }

  onVideoReady = () => {
    if (this.Player.getInternalPlayer()) {
      this.setState(() => ({
        totalDuration: this.Player.getInternalPlayer().getDuration()
      }));
    }
  };

  onVideoPlay = () => {
    const {
      currentVideoSlot,
      isStarred,
      videoId,
      userId,
      addVideoView,
      fillCurrentVideoSlot
    } = this.props;
    const { justEarned, xpEarned } = this.state;
    this.setState({ started: true });
    if (!this.playing) {
      this.playing = true;
      const time = this.Player.getCurrentTime();
      if (Math.floor(time) === 0) {
        addVideoView({ videoId, userId });
      }
      if (!currentVideoSlot) {
        fillCurrentVideoSlot(Number(videoId));
        if (userId) {
          this.interval = setInterval(this.increaseProgress, intervalLength);
        }
      }
      const authorization = auth();
      const authExists = !!authorization.headers.authorization;
      if (authExists && isStarred && !(justEarned || xpEarned)) {
        try {
          request.put(
            `${VIDEO_URL}/currentlyWatching`,
            { videoId },
            authorization
          );
        } catch (error) {
          console.error(error.response || error);
        }
      }
    }
  };

  onVideoStop = () => {
    const { emptyCurrentVideoSlot } = this.props;
    this.playing = false;
    clearInterval(this.interval);
    emptyCurrentVideoSlot();
    const authorization = auth();
    const authExists = !!authorization.headers.authorization;
    if (authExists) {
      try {
        request.put(
          `${VIDEO_URL}/currentlyWatching`,
          { videoId: null },
          authorization
        );
      } catch (error) {
        console.error(error.response || error);
      }
    }
  };

  increaseProgress = async() => {
    const { justEarned, xpEarned, timeWatched, totalDuration } = this.state;
    const { changeUserXP, isStarred, videoId } = this.props;
    if (isStarred && !xpEarned && !justEarned && this.Player) {
      if (this.Player.getInternalPlayer()) {
        if (this.Player.getInternalPlayer().isMuted()) {
          this.Player.getInternalPlayer().unMute();
        }
        if (this.Player.getInternalPlayer().getVolume() < 30) {
          this.Player.getInternalPlayer().setVolume(30);
        }
      }
    }
    if (
      isStarred &&
      timeWatched >=
        Math.min(totalDuration / denominator, requiredDurationCap) &&
      !this.rewardingXP
    ) {
      this.rewardingXP = true;
      try {
        await request.put(`${VIDEO_URL}/xpEarned`, { videoId }, auth());
        await changeUserXP({
          type: 'increase',
          action: 'watch',
          target: 'video',
          targetId: videoId,
          amount: 100
        });
        if (this.mounted) {
          this.setState(() => ({
            justEarned: true
          }));
        }
        this.rewardingXP = false;
      } catch (error) {
        console.error(error.response || error);
      }
    }
    if (!xpEarned && this.mounted) {
      this.setState(state => ({
        timeWatched: state.timeWatched + intervalLength / 1000
      }));
    }
    const authorization = auth();
    const authExists = !!authorization.headers.authorization;
    if (authExists) {
      try {
        const {
          data: { currentlyWatchingAnotherVideo, success }
        } = await request.put(
          `${VIDEO_URL}/duration`,
          { videoId, isStarred, xpEarned },
          authorization
        );
        if (success) return;
        if (currentlyWatchingAnotherVideo) {
          if (this.Player) {
            if (this.Player.getInternalPlayer()) {
              this.Player.getInternalPlayer().pauseVideo();
            }
          }
        }
      } catch (error) {
        console.error(error.response || error);
      }
    }
  };
}

export default connect(
  state => ({
    chatMode: state.ChatReducer.chatMode,
    userId: state.UserReducer.userId,
    pageVisible: state.ViewReducer.pageVisible,
    currentVideoSlot: state.VideoReducer.currentVideoSlot
  }),
  {
    addVideoView,
    changeUserXP,
    fillCurrentVideoSlot,
    emptyCurrentVideoSlot
  }
)(VideoPlayer);
<|endoftext|>


<commit_before>const library = (lib) => {

    const peerEndpoint = (peer) => {
//        const { config } = lib.store.getState();
//        const protocol = config[local].api.protocol;
//        const port = config[remote].api.port;

        const [local, remote] = process.type === 'renderer'
            ? ['renderer', 'electron']
            : ['electron', 'renderer'];

        const protocol = 'http';
        const host = peer.isLocal ? 'localhost' : peer.ip;
        const port = '54321';

        return `${protocol}://${host}:${port}`;
    };

    const peerIsMe = (peer) => {
        const { network : { me } } = lib.store.getState();
        return peer.hostname === me.hostname;
    }

    const trackEndpoint = ({ _id, peer }) => `${peerEndpoint(peer)}/api/track/download?_id=${_id}`;

    const coverEndpoint = ({ _id, peer }) => `${peerEndpoint(peer)}/api/track/cover?_id=${_id}`;

    const dispatchEndpoint = ({ peer }) => `${peerEndpoint(peer)}/api/store/dispatch`;

    return {
        peerEndpoint,
        peerIsMe,
        trackEndpoint,
        coverEndpoint
    }
}

export default library;
<commit_msg>export<commit_after>const library = (lib) => {

    const peerEndpoint = (peer) => {
//        const { config } = lib.store.getState();
//        const protocol = config[local].api.protocol;
//        const port = config[remote].api.port;

        const [local, remote] = process.type === 'renderer'
            ? ['renderer', 'electron']
            : ['electron', 'renderer'];

        const protocol = 'http';
        const host = peer.isLocal ? 'localhost' : peer.ip;
        const port = '54321';

        return `${protocol}://${host}:${port}`;
    };

    const peerIsMe = (peer) => {
        const { network : { me } } = lib.store.getState();
        return peer.hostname === me.hostname;
    }

    const trackEndpoint = ({ _id, peer }) => `${peerEndpoint(peer)}/api/track/download?_id=${_id}`;

    const coverEndpoint = ({ _id, peer }) => `${peerEndpoint(peer)}/api/track/cover?_id=${_id}`;

    const dispatchEndpoint = ({ peer }) => `${peerEndpoint(peer)}/api/store/dispatch`;

    return {
        peerEndpoint,
        peerIsMe,
        trackEndpoint,
        coverEndpoint,
        dispatchEndpoint
    }
}

export default library;
<|endoftext|>


<commit_before>
Ui.ToolBar.extend('Ui.MenuBar', {});

Ui.Pressable.extend('Ui.Menu',
/**@lends Ui.Menu#*/
{
	headerLabel: undefined,
	header: undefined,
	dialog: undefined,
	scope: undefined,

    /**
     * @constructs
	 * @class
     * @extends Ui.Pressable
     */
	constructor: function(config) {
		this.addEvents('item');

		this.headerLabel = new Ui.Label({ margin: 5, horizontalAlign: 'left' });
		this.append(this.headerLabel);

		this.dialog = new Ui.MenuDialog({ element: this.headerLabel });
		this.connect(this.dialog, 'item', function(dialog, item) {
			this.fireEvent('item', this, item);
		});
		this.connect(this, 'press', this.onTitlePress);
	},

	setHeader: function(header) {
		if(this.header != header) {
			this.header = header;
			this.headerLabel.setText(header);
			this.dialog.setHeader(header);
		}
	},

	appendItem: function(item, callback, scope) {
		if(typeof(item) == 'string')
			item = new Ui.Label({ horizontalAlign: 'left', text: item });
		var menu = this;
		var pressable = this.dialog.appendItem(item);
		if(callback != undefined) {
			if(scope === undefined) {
				if(this.scope === undefined)
					scope = this;
				else
					scope = this.scope;
			}
			scope.connect(pressable, 'press', function(pressable) {
				callback.call(this, menu, pressable.getChildren()[0]);
			});
		}
	},

	setScope: function(scope) {
		this.scope = scope;
	},

	setItems: function(items) {
		this.dialog.clearItems();
		for(var i = 0; i < items.length; i++) {
			this.appendItem(items[i].item, items[i].callback, items[i].scope);
		}
	},

	appendSeparator: function() {
		this.dialog.appendSeparator();
	},

	getIsOpen: function() {
		return this.dialog.getIsVisible();
	},

	open: function() {
		this.dialog.show();
	},

	close: function() {
		this.dialog.hide();
	},

	onTitlePress: function() {
		this.open();
	}
});

Ui.Container.extend('Ui.MenuDialog',
/**@lends Ui.MenuDialog#*/
{
	element: undefined,
	background: undefined,
	header: undefined,
	scroll: undefined,
	content: undefined,
	shadow: undefined,

	/**
	 * @constructs
	 * @class
	 * @extends Ui.Container
	 */
	constructor: function(config) {
		this.addEvents('item');

		if('element' in config) {
			this.element = config.element;
			delete(config.element);
		}
		else
			throw('element property is NEEDED for Ui.MenuDialog');

		this.shadow = new Ui.Rectangle({ opacity: 0, fill: new Ui.Color({ a: 0 }) });
		this.appendChild(this.shadow);

		this.background = new Ui.MenuBackground({ fill: new Ui.Color({ r: 0.96, g: 0.96, b: 0.96 }), radius: 4 });
		this.appendChild(this.background);

		this.header = new Ui.Label({ margin: 5, horizontalAlign: 'left' });
		this.appendChild(this.header);

		this.scroll = new Ui.ScrollingArea({ margin: 5, scrollHorizontal: true });
		this.content = new Ui.VBox();
		this.scroll.setContent(this.content);
		this.appendChild(this.scroll);

		this.connect(this.getDrawing(), 'mousedown', this.onMouseDown);
		this.connect(this.scroll.getDrawing(), 'mousedown', function(event) { event.stopPropagation(); });
		this.connect(this.getDrawing(), 'fingerdown', this.onFingerDown);
		this.connect(this.scroll.getDrawing(), 'fingerdown', function(event) { event.stopPropagation(); });

		// handle keyboard
		this.connect(this.getDrawing(), 'keydown', this.onKeyDown);
	},

	setHeader: function(header) {
		this.header.setText(header);
	},

	clearItems: function() {
		while(this.content.getFirstChild() != undefined) {
			this.content.remove(this.content.getFirstChild());
		}
	},

	appendItem: function(item) {
		var menuItem = new Ui.MenuItem();
		menuItem.setContent(item);
		this.connect(menuItem, 'press', this.onItemPress);
		this.content.append(menuItem);
		return menuItem;
	},

	appendSeparator: function() {
		this.content.append(new Ui.Separator());
	},

	onItemPress: function(menuitem) {
		var item = menuitem.getContent();
		this.fireEvent('item', this, item);
		this.hide();
	},

	onMouseDown: function(event) {
		this.hide();
		event.preventDefault();
		event.stopPropagation();
	},

	onFingerDown: function(event) {
		this.hide();
		event.preventDefault();
		event.stopPropagation();
	},

	onKeyDown: function(event) {
		var key = event.which;
		// escape
		if(key == 27) {
			event.preventDefault();
			event.stopPropagation();
			this.hide();
		}
		// arrow down
		else if(key == 40) {
			for(var i = 0; i < this.content.getChildren().length; i++) {
				if(!Ui.MenuItem.hasInstance(this.content.getChildren()[i]))
					continue;
				if(this.content.getChildren()[i].getIsCurrent()) {
					for(i = i+1;i < this.content.getChildren().length; i++) {
						if(Ui.MenuItem.hasInstance(this.content.getChildren()[i])) {
							event.preventDefault();
							event.stopPropagation();
							this.content.getChildren()[i].setCurrent();
							break;
						}
					}
				}
			}
		}
		// arrow up
		else if(key == 38) {
			var prev = undefined;
			for(var i = 0; i < this.content.getChildren().length; i++) {
				if(!Ui.MenuItem.hasInstance(this.content.getChildren()[i]))
					continue;
				if(this.content.getChildren()[i].getIsCurrent()) {
					if(prev != undefined) {
						event.preventDefault();
						event.stopPropagation();
						prev.setCurrent();
					}
					break;
				}
				prev = this.content.getChildren()[i];
			}
		}
	}
},
/**@lends Ui.MenuDialog#*/
{
	visible: false,

	show: function() {
		var oldVisible = this.getIsVisible();
		Ui.MenuDialog.base.show.call(this);

		if(!oldVisible) {
			this.header.setFontWeight(this.element.getFontWeight());
			this.header.setFontSize(this.element.getFontSize());
			Ui.App.current.appendDialog(this);
		}
	},

	onHidden: function() {
		Ui.App.current.removeDialog(this);
	},

	measureCore: function(width, height) {
		this.shadow.measure(width, height);

		var point1 = this.element.pointToWindow({ x: 0, y: 0 });
		var point2 = this.element.pointToWindow({ x: this.element.getLayoutWidth(), y: this.element.getLayoutHeight() });

		var headerSize = this.header.measure(point2.x, point2.y);
		var scrollSize = this.scroll.measure(width, height - point2.y);

		return { width: Math.max(headerSize.width, scrollSize.width), height: (headerSize.height + scrollSize.height) };
	},

	arrangeCore: function(width, height) {
		this.shadow.arrange(0, 0, width, height);

		var point1 = this.element.pointToWindow({ x: 0, y: 0 });
		var point2 = this.element.pointToWindow({ x: this.element.getLayoutWidth(), y: this.element.getLayoutHeight() });

		this.header.arrange(point1.x - 5, point1.y - 5, this.header.getMeasureWidth(), this.header.getMeasureHeight());

		var offset = 0;
		if(this.scroll.getMeasureWidth() > width - point1.x - 5)
			offset = point1.x - (width - this.scroll.getMeasureWidth()) - 5;
		this.scroll.arrange(point1.x - offset - 5, point2.y, Math.max(this.header.getMeasureWidth(), this.scroll.getMeasureWidth()), this.scroll.getMeasureHeight());

		this.background.setTab(offset, this.header.getMeasureWidth(), this.header.getMeasureHeight() - 5);

		this.background.arrange(point1.x - offset - 5, point1.y - 5, Math.max(this.header.getMeasureWidth(), this.scroll.getMeasureWidth()), this.header.getMeasureHeight() + this.scroll.getMeasureHeight() - 5);
	}
});

Ui.Container.extend('Ui.MenuBackground',
/**@lends Ui.MenuBackground#*/
{
	darkShadow: undefined,
	lightShadow: undefined,
	background: undefined,

	radius: 8,
	fill: 'black',
	tabOffset: 30,
	tabWidth: 10,
	tabHeight: 10,

	/**
	 * @constructs
	 * @class
	 * @extends Ui.Container
	 */
	constructor: function(config) {

		this.darkShadow = new Ui.Shape({ fill: '#010002' });
		this.appendChild(this.darkShadow);

		this.lightShadow = new Ui.Shape({ fill: '#5f625b' });
		this.appendChild(this.lightShadow);

		var yuv = (new Ui.Color({ r: 0.50, g: 0.50, b: 0.50 })).getYuv();
		var gradient = new Ui.LinearGradient({ stops: [
			{ offset: 0, color: new Ui.Color({ y: 0.25, u: yuv.u, v: yuv.v }) },
			{ offset: 1, color: new Ui.Color({ y: 0.16, u: yuv.u, v: yuv.v }) }
		] });

		this.background = new Ui.Shape({ fill: gradient });
		this.appendChild(this.background);
	},

	setTab: function(offset, width, height) {
		this.tabOffset = offset;
		this.tabWidth = width;
		this.tabHeight = height;
		this.invalidateArrange();
	},

	setRadius: function(radius) {
		if(this.radius != radius) {
			this.radius = radius;
			this.invalidateArrange();
		}
	},

	setFill: function(fill) {
		if(this.fill != fill) {
			this.fill = Ui.Color.create(fill);
			var yuv = this.fill.getYuva();
			if(this.getLayoutHeight() > 0) {
				var gradient = new Ui.LinearGradient({ stops: [
					{ offset: 0, color: new Ui.Color({ y: yuv.y - 0.1, u: yuv.u, v: yuv.v }) },
					{ offset: this.tabHeight/(this.getLayoutHeight()*2), color: new Ui.Color({ y: yuv.y, u: yuv.u, v: yuv.v }) },
					{ offset: 1, color: new Ui.Color({ y: yuv.y, u: yuv.u, v: yuv.v }) }
				] });
				this.background.setFill(gradient);
			}
			this.darkShadow.setFill(new Ui.Color({ y: yuv.y - 0.4, u: yuv.u, v: yuv.v }));
			this.lightShadow.setFill(new Ui.Color({ y: yuv.y + 0.1, u: yuv.u, v: yuv.v }));
		}
	},

	/**#@+
	* @private
	*/

	genPath: function(width, height, radius, tabOffset, tabWidth, tabHeight) {
		if(tabWidth == width)
			return 'M'+radius+',0 L'+(width-radius)+',0 Q'+width+',0 '+width+','+radius+' L'+width+','+(height-radius)+' Q'+width+','+height+' '+(width-radius)+','+height+' L'+radius+','+height+' Q'+0+','+height+' 0,'+(height-radius)+' L0,'+radius+' Q0,0 '+radius+',0 z';

		var str = 'M'+tabOffset+','+tabHeight+' L'+tabOffset+','+radius+' Q'+tabOffset+',0 '+(tabOffset+radius)+',0 L'+(tabOffset + tabWidth - radius)+',0 Q'+(tabOffset + tabWidth)+',0 '+(tabOffset + tabWidth)+','+radius+' L'+(tabOffset + tabWidth)+','+tabHeight+' L'+(width - radius)+','+tabHeight+' Q'+width+','+tabHeight+' '+width+','+(tabHeight+radius)+' L'+width+','+(height-radius)+' Q'+width+','+height+' '+(width-radius)+','+height+' L'+radius+','+height+' Q0,'+height+' 0,'+(height-radius);
		if(tabOffset == 0)
			str += ' z';
		else
			str += ' L0,'+(tabHeight+radius)+' Q0,'+tabHeight+' '+radius+','+tabHeight+' z';
		return str;
	}
	/**#@-*/
},
/**@lends Ui.MenuBackground#*/
{
	arrangeCore: function(width, height) {
		this.darkShadow.setPath(this.genPath(width, height, this.radius, this.tabOffset, this.tabWidth, this.tabHeight));
		this.darkShadow.arrange(0, 0, width, height);

		this.lightShadow.setPath(this.genPath(width-2, height-2, this.radius-1, this.tabOffset, this.tabWidth-2, this.tabHeight));
		this.lightShadow.arrange(1, 1, width - 2, height - 2);

		this.background.setPath(this.genPath(width-4, height-4, this.radius - 1.4, this.tabOffset, this.tabWidth-4, this.tabHeight));
		this.background.arrange(2, 2, width - 4, height - 4);

		var yuv = this.fill.getYuva();
		var gradient = new Ui.LinearGradient({ stops: [
			{ offset: 0, color: new Ui.Color({ y: yuv.y - 0.1, u: yuv.u, v: yuv.v }) },
			{ offset: this.tabHeight/(height*2), color: new Ui.Color({ y: yuv.y, u: yuv.u, v: yuv.v }) },
			{ offset: 1, color: new Ui.Color({ y: yuv.y, u: yuv.u, v: yuv.v }) }
		] });
		this.background.setFill(gradient);
	}
});

Ui.MouseOverable.extend('Ui.MenuItem',
/**@lends Ui.MenuItem#*/
{
	background: undefined,
	pressable: undefined,

	/**
	 * @constructs
	 * @class
	 * @extends Ui.MouseOverable
	 */
	constructor: function() {
		this.addEvents('press');

		this.background = new Ui.Rectangle({ fill: '#1c8ef2', opacity: 0, radius: 4 });
		this.append(this.background);

		this.pressable = new Ui.Pressable({ padding: 5, paddingLeft: 10, paddingRight: 10 });
		this.append(this.pressable);
		this.connect(this.pressable, 'press', function() {
			this.fireEvent('press', this);
		});

		this.connect(this, 'enter', this.onEnter);
		this.connect(this, 'leave', this.onLeave);
		this.connect(this.pressable, 'down', this.onDown);
		this.connect(this.pressable, 'up', this.onUp);

		this.connect(this.pressable, 'focus', this.onItemFocus);
		this.connect(this.pressable, 'blur', this.onItemBlur);

	},

	getIsCurrent: function() {
		return this.pressable.getHasFocus();
	},

	setCurrent: function() {
		this.pressable.focus();
	},

	getContent: function() {
		return this.pressable.getChildren()[0];
	},

	onEnter: function() {
		this.pressable.focus();
	},

	onLeave: function() {
	},

	onDown: function() {
		this.background.setOpacity(1);
	},

	onUp: function() {
		this.background.setOpacity(0);
	},

	onItemFocus: function() {
		this.background.setOpacity(0.6);
	},

	onItemBlur: function() {
		this.background.setOpacity(0);
	}
}, {
	setContent: function(content) {
		this.pressable.append(Ui.Element.create(content));
	}
});

<commit_msg>flatten the design<commit_after>
Ui.ToolBar.extend('Ui.MenuBar', {});

Ui.Pressable.extend('Ui.Menu',
/**@lends Ui.Menu#*/
{
	headerLabel: undefined,
	header: undefined,
	dialog: undefined,
	scope: undefined,

    /**
     * @constructs
	 * @class
     * @extends Ui.Pressable
     */
	constructor: function(config) {
		this.addEvents('item');

		var vbox = new Ui.VBox();
		this.append(vbox);

		this.headerLabel = new Ui.Label({ margin: 5, marginBottom: 0, horizontalAlign: 'left' });
		vbox.append(this.headerLabel);

		vbox.append(new Ui.Rectangle({ height: 1, width: 1, fill: '#aaaaaa', marginLeft: 5, marginRight: 5 }));

		this.dialog = new Ui.MenuDialog({ element: this.headerLabel });
		this.connect(this.dialog, 'item', function(dialog, item) {
			this.fireEvent('item', this, item);
		});
		this.connect(this, 'press', this.onTitlePress);
	},

	setHeader: function(header) {
		if(this.header != header) {
			this.header = header;
			this.headerLabel.setText(header);
		}
	},

	appendItem: function(item, callback, scope) {
		if(typeof(item) == 'string')
			item = new Ui.Label({ horizontalAlign: 'left', text: item });
		var menu = this;
		var pressable = this.dialog.appendItem(item);
		if(callback != undefined) {
			if(scope === undefined) {
				if(this.scope === undefined)
					scope = this;
				else
					scope = this.scope;
			}
			scope.connect(pressable, 'press', function(pressable) {
				callback.call(this, menu, pressable.getChildren()[0]);
			});
		}
	},

	setScope: function(scope) {
		this.scope = scope;
	},

	setItems: function(items) {
		this.dialog.clearItems();
		for(var i = 0; i < items.length; i++) {
			this.appendItem(items[i].item, items[i].callback, items[i].scope);
		}
	},

	appendSeparator: function() {
		this.dialog.appendSeparator();
	},

	getIsOpen: function() {
		return this.dialog.getIsVisible();
	},

	open: function() {
		this.dialog.show();
	},

	close: function() {
		this.dialog.hide();
	},

	onTitlePress: function() {
		this.open();
	}
});

Ui.Popup.extend('Ui.MenuDialog',
/**@lends Ui.MenuDialog#*/
{
	element: undefined,
	scroll: undefined,
	content: undefined,

	/**
	 * @constructs
	 * @class
	 * @extends Ui.Popup
	 */
	constructor: function(config) {
		this.addEvents('item');

		if('element' in config) {
			this.element = config.element;
			delete(config.element);
		}
		else
			throw('element property is NEEDED for Ui.MenuDialog');

		this.setExpandable(true);
		this.setAutoHide(true);

		this.scroll = new Ui.ScrollingArea({ scrollHorizontal: true });
		this.content = new Ui.VBox();
		this.scroll.setContent(this.content);
		this.setContent(this.scroll);

		// handle keyboard
		this.connect(this.getDrawing(), 'keydown', this.onKeyDown);
	},

	clearItems: function() {
		while(this.content.getFirstChild() != undefined) {
			this.content.remove(this.content.getFirstChild());
		}
	},

	appendItem: function(item) {
		var menuItem = new Ui.MenuItem();
		menuItem.setContent(item);
		this.connect(menuItem, 'press', this.onItemPress);
		this.content.append(menuItem);
		return menuItem;
	},

	appendSeparator: function() {
		this.content.append(new Ui.Separator());
	},

	onItemPress: function(menuitem) {
		var item = menuitem.getContent();
		this.fireEvent('item', this, item);
		this.hide();
	},

	onKeyDown: function(event) {
		var key = event.which;
		// escape
		if(key == 27) {
			event.preventDefault();
			event.stopPropagation();
			this.hide();
		}
		// arrow down
		else if(key == 40) {
			for(var i = 0; i < this.content.getChildren().length; i++) {
				if(!Ui.MenuItem.hasInstance(this.content.getChildren()[i]))
					continue;
				if(this.content.getChildren()[i].getIsCurrent()) {
					for(i = i+1;i < this.content.getChildren().length; i++) {
						if(Ui.MenuItem.hasInstance(this.content.getChildren()[i])) {
							event.preventDefault();
							event.stopPropagation();
							this.content.getChildren()[i].setCurrent();
							break;
						}
					}
				}
			}
		}
		// arrow up
		else if(key == 38) {
			var prev = undefined;
			for(var i = 0; i < this.content.getChildren().length; i++) {
				if(!Ui.MenuItem.hasInstance(this.content.getChildren()[i]))
					continue;
				if(this.content.getChildren()[i].getIsCurrent()) {
					if(prev != undefined) {
						event.preventDefault();
						event.stopPropagation();
						prev.setCurrent();
					}
					break;
				}
				prev = this.content.getChildren()[i];
			}
		}
	}
},
/**@lends Ui.MenuDialog#*/
{
	show: function() {
		var oldVisible = this.getIsVisible();
		Ui.MenuDialog.base.show.call(this, this.element, 'bottom');
	}
});

Ui.MouseOverable.extend('Ui.MenuItem',
/**@lends Ui.MenuItem#*/
{
	background: undefined,
	pressable: undefined,

	/**
	 * @constructs
	 * @class
	 * @extends Ui.MouseOverable
	 */
	constructor: function() {
		this.addEvents('press');

		this.background = new Ui.Rectangle({ fill: '#cecece', opacity: 0 });
		this.append(this.background);

		this.pressable = new Ui.Pressable({ padding: 10 });
		this.append(this.pressable);
		this.connect(this.pressable, 'press', function() {
			this.fireEvent('press', this);
		});

		this.connect(this, 'enter', this.onEnter);
		this.connect(this, 'leave', this.onLeave);
		this.connect(this.pressable, 'down', this.onDown);
		this.connect(this.pressable, 'up', this.onUp);

		this.connect(this.pressable, 'focus', this.onItemFocus);
		this.connect(this.pressable, 'blur', this.onItemBlur);

	},

	getIsCurrent: function() {
		return this.pressable.getHasFocus();
	},

	setCurrent: function() {
		this.pressable.focus();
	},

	getContent: function() {
		return this.pressable.getChildren()[0];
	},

	onEnter: function() {
		this.pressable.focus();
	},

	onLeave: function() {
	},

	onDown: function() {
		this.background.setOpacity(1);
	},

	onUp: function() {
		this.background.setOpacity(0);
	},

	onItemFocus: function() {
		this.background.setOpacity(0.6);
	},

	onItemBlur: function() {
		this.background.setOpacity(0);
	}
}, {
	setContent: function(content) {
		this.pressable.append(Ui.Element.create(content));
	}
});

<|endoftext|>


<commit_before>'use strict';

import request from 'request';
import {spawn} from 'child_process';
import temp from 'temp';
import fs from 'fs';
temp.track();

function talk(position, language = 'de', cb) {
    if ('function' === typeof language) {
        cb = language;
        language = 'de';
    }
    let options = {
        url: 'https://translate.google.com/translate_tts',
        qs: {
            ie: 'UTF-8',
            q: position.label,
            tl: language,
            total: 1,
            idx: 0,
            textlen: position.label.length,
            tk: '162937|286006',
            client: 't'
        },
        headers: {
            'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36',
            'Referer': 'https://translate.google.com/'
        }
    };

    temp.open({suffix: '.mp3'}, (err, info) => {
        if (err) {
            throw err;
        }
        request(options).pipe(fs.createWriteStream(info.path))
            .on('close', () => {
                let proc = spawn('mpg123', [info.path]);
                proc.on('close', function () {
                    temp.cleanup();
                    cb();
                });
            });
    });
}

export default talk;
<commit_msg>use predefined files for audio output<commit_after>'use strict';

import request from 'request';
import {spawn} from 'child_process';
import fs from 'fs';
import settings from './settings';
import path from 'path';

let dir = settings.get('talk').audioDir;

function talk(position, language = 'de', cb) {
    if ('function' === typeof language) {
        cb = language;
        language = 'de';
    }

    let audioFile = path.join(dir, position.label + '.mp3');

    let stat = fs.statSync(audioFile);
    if (stat.isFile() && stat.size) {
        let proc = spawn('mpg123', [audioFile]);
        proc.on('close', function () {
            cb();
        });
    } else {
        cb();
    }
}

export default talk;
<|endoftext|>


<commit_before>angular.module('app', ['ngTweets'])
  .controller('twitterController', function($scope, tweets) {
    $scope.tweets = tweets.get({
      widgetId: '598065534836600832'
    }).success(function(data){
      $scope.tweets = data;
    });
  });
<commit_msg>Demo uses promises<commit_after>angular.module('app', ['ngTweets'])
  .controller('twitterController', function($scope, tweets) {
    $scope.tweets = tweets.get({
      widgetId: '598065534836600832'
    }).then(function(response){
      $scope.tweets = response.data;
    });
  });
<|endoftext|>


<commit_before>var crypto = require('crypto');
/**
 * 生成uid
 * @param  int length
 * @return string
 */
var uid = function(length){
    var ratio = Math.log(64) / Math.log(256);
    var numbytes = Math.ceil(length * ratio);
    var str = crypto.randomBytes(numbytes).toString('base64').slice(0, length);
    return str.replace(/\+/g, '_').replace(/\//g, '-');
};
/**
 * 生成cookie签名
 * @param  string val
 * @param  string secret
 * @return string
 */
var cookieSign = function(val, secret){
    secret = crypto.createHmac('sha256', secret).update(val).digest('base64');
    secret = secret.replace(/\=+$/, '');
    return val + '.' + secret;
};
/**
 * 解析cookie签名
 * @param  {[type]} val
 * @param  {[type]} secret
 * @return {[type]}
 */
var cookieUnsign = function(val, secret){
    var str = val.slice(0, val.lastIndexOf('.'));
    return cookieSign(str, secret) === val ? str : false;
}

/**
 * 定时器
 * @type {Number}
 */
var gcTimer = 0;
/**
 * 清除已经过期的session数据
 * @return {[type]} [description]
 */
var gc = function(instance){
    if (APP_DEBUG || APP_MODE || gcTimer) {
        return;
    };
    //超时时间
    var timeout = C('cookie_expires') || 24 * 3600 * 1000;
    //1天执行一次
    gcTimer = setInterval(function(){
        var hour = (new Date).getHours();
        //早上四点清理
        if (hour !== 4) {
            return;
        };
        var now = Date.now();
        instance.gc && instance.gc(now, timeout);
    }, 3600 * 1000);
}

/**
 * 存储session的数据变量
 * @type {Object}
 */
var session_data = {};
var Session = module.exports = Class(function(){
    return {
        cookie: null,
        init: function(http){
            this.http = http;
            //session_cookie是未签名的cookie
            this.cookie = http.session_cookie;
            gc(this);
            this.afterInit && this.afterInit();
        },
        /**
         * 差异化的init放在这里实现
         * @return {[type]} [description]
         */
        afterInit: function(){
            if (!(this.cookie in session_data)) {
                session_data[this.cookie] = {};
            };
        },
        /**
         * 获取session，返回一个promise
         * @param  {[type]} name
         * @return {[type]}
         */
        get: function(name){
            session_data[this.cookie].__ACTIVE_TIME__ = Date.now();
            var value = session_data[this.cookie][name] || "";
            return getPromise(value);
        },
        /**
         * 设置session
         * @param {[type]} name  [description]
         * @param {[type]} value [description]
         */
        set: function(name, value){
            if (isObject(name)) {
                for(var key in name){
                    session_data[this.cookie][key] = name[key];
                }
            }else {
                session_data[this.cookie][name] = value;
            }
        },
        /**
         * 移除session
         * @return {[type]} [description]
         */
        rm: function(name){
            if (name) {
                delete session_data[this.cookie][name];
            }else{
                session_data[this.cookie] = {};
            }
        },
        /**
         * gc
         * @param  {[type]} now     [description]
         * @param  {[type]} timeout [description]
         * @return {[type]}         [description]
         */
        gc: function(now, timeout){
            for(var cookie in session_data){
                var time = session_data[cookie].__ACTIVE_TIME__;
                if (now - time > timeout) {
                    delete session_data[cookie];
                };
            }
        }
    }
});

//解析cookie
Session.start = function(http){
    if (http.session_cookie) {
        return http.session_cookie;
    };
    var name = C('session_id');
    //是否使用签名
    var secret = C('session_cookieSign');
    var cookie = http.cookie[name];
    if (cookie && secret) {
        cookie = cookieUnsign(cookie, secret);
    };
    var session_cookie = cookie;
    if (!cookie) {
        cookie = uid(32);
        session_cookie = cookie;
        if (secret) {
            cookie = cookieSign(cookie, secret);
        };
        http.setCookie(name, cookie, C('session_options'));
    }
    //将cookie值放到http对象上，方便后续获取
    http.session_cookie = session_cookie;

    var name = C('session_type') + "Session";
    //session类
    http.session = thinkRequire(name)(http);

    return cookie;
};<commit_msg>update<commit_after>var crypto = require('crypto');
/**
 * 生成uid
 * @param  int length
 * @return string
 */
var uid = function(length){
    var ratio = Math.log(64) / Math.log(256);
    var numbytes = Math.ceil(length * ratio);
    var str = crypto.randomBytes(numbytes).toString('base64').slice(0, length);
    return str.replace(/\+/g, '_').replace(/\//g, '-');
};
/**
 * 生成cookie签名
 * @param  string val
 * @param  string secret
 * @return string
 */
var cookieSign = function(val, secret){
    secret = crypto.createHmac('sha256', secret).update(val).digest('base64');
    secret = secret.replace(/\=+$/, '');
    return val + '.' + secret;
};
/**
 * 解析cookie签名
 * @param  {[type]} val
 * @param  {[type]} secret
 * @return {[type]}
 */
var cookieUnsign = function(val, secret){
    var str = val.slice(0, val.lastIndexOf('.'));
    return cookieSign(str, secret) === val ? str : false;
}

/**
 * 定时器
 * @type {Number}
 */
var gcTimer = 0;
/**
 * 清除已经过期的session数据
 * @return {[type]} [description]
 */
var gc = function(instance){
    if (APP_DEBUG || APP_MODE || gcTimer) {
        return;
    };
    //超时时间
    var timeout = Math.max(24 * 3600 * 1000, C('cookie_expires'));
    gcTimer = setInterval(function(){
        var hour = (new Date).getHours();
        //早上四点清理
        if (hour !== 4) {
            return;
        };
        var now = Date.now();
        instance.gc && instance.gc(now, timeout);
    }, 3600 * 1000);
}

/**
 * 存储session的数据变量
 * @type {Object}
 */
var session_data = {};
var Session = module.exports = Class(function(){
    return {
        cookie: null,
        init: function(http){
            this.http = http;
            //session_cookie是未签名的cookie
            this.cookie = http.session_cookie;
            gc(this);
            this.afterInit && this.afterInit();
        },
        /**
         * 差异化的init放在这里实现
         * @return {[type]} [description]
         */
        afterInit: function(){
            if (!(this.cookie in session_data)) {
                session_data[this.cookie] = {};
            };
        },
        /**
         * 获取session，返回一个promise
         * @param  {[type]} name
         * @return {[type]}
         */
        get: function(name){
            session_data[this.cookie].__ACTIVE_TIME__ = Date.now();
            var value = session_data[this.cookie][name] || "";
            return getPromise(value);
        },
        /**
         * 设置session
         * @param {[type]} name  [description]
         * @param {[type]} value [description]
         */
        set: function(name, value){
            if (isObject(name)) {
                for(var key in name){
                    session_data[this.cookie][key] = name[key];
                }
            }else {
                session_data[this.cookie][name] = value;
            }
        },
        /**
         * 移除session
         * @return {[type]} [description]
         */
        rm: function(name){
            if (name) {
                delete session_data[this.cookie][name];
            }else{
                session_data[this.cookie] = {};
            }
        },
        /**
         * gc
         * @param  {[type]} now     [description]
         * @param  {[type]} timeout [description]
         * @return {[type]}         [description]
         */
        gc: function(now, timeout){
            for(var cookie in session_data){
                var time = session_data[cookie].__ACTIVE_TIME__ || 0;
                if (now - time > timeout) {
                    delete session_data[cookie];
                };
            }
        }
    }
});

//解析cookie
Session.start = function(http){
    if (http.session_cookie) {
        return http.session_cookie;
    };
    var name = C('session_id');
    //是否使用签名
    var secret = C('session_cookieSign');
    var cookie = http.cookie[name];
    if (cookie && secret) {
        cookie = cookieUnsign(cookie, secret);
    };
    var session_cookie = cookie;
    if (!cookie) {
        cookie = uid(32);
        session_cookie = cookie;
        if (secret) {
            cookie = cookieSign(cookie, secret);
        };
        http.setCookie(name, cookie, C('session_options'));
    }
    //将cookie值放到http对象上，方便后续获取
    http.session_cookie = session_cookie;

    var name = C('session_type') + "Session";
    //session类
    http.session = thinkRequire(name)(http);

    return cookie;
};<|endoftext|>


<commit_before>var mpns = require('mpns');
var util = require('util');
var BaseNotification = require('./BaseNotification');

util.inherits(MPNSNotification, BaseNotification);

function MPNSNotification() {

}

module.exports = MPNSNotification;
<commit_msg>Remove MPNS<commit_after><|endoftext|>


<commit_before>
"use strict";

var protobufjs = require('protobufjs');
var fs = require('fs');
var path = require('path');

var map = {
    0: 'Version',
    1: 'UDPTunnel',
    2: 'Authenticate',
    3: 'Ping',
    4: 'Reject',
    5: 'ServerSync',
    6: 'ChannelRemove',
    7: 'ChannelState',
    8: 'UserRemove',
    9: 'UserState',
    10: 'BanList',
    11: 'TextMessage',
    12: 'PermissionDenied',
    13: 'ACL',
    14: 'QueryUsers',
    15: 'CryptSetup',
    16: 'ContextActionModify',
    17: 'ContextAction',
    18: 'UserList',
    19: 'VoiceTarget',
    20: 'PermissionQuery',
    21: 'CodecVersion',
    22: 'UserStats',
    23: 'RequestBlob',
    24: 'ServerConfig',
    25: 'SuggestConfig'
};

var builder = protobufjs.loadProtoFile(path.join(__dirname, mumble.proto));
var mumble = builder.build('MumbleProto');
var messages = {};
var context = {schema: mumble};

module.exports.buildPacket = function (type, payload) {
    return new context.schema[type](payload || {})
}

module.exports.decodePacket = function (type_id, payload) {
    var type = map[type_id]
    return new context.schema[type].decode(payload || {})
}

module.exports.idByName = {};
module.exports.nameById = {};

for (var k in map) {
    module.exports.idByName[map[k]] = k * 1;
    module.exports.nameById[k] = map[k];
}<commit_msg>use Mumbe.proto filename<commit_after>
"use strict";

var protobufjs = require('protobufjs');
var fs = require('fs');
var path = require('path');

var map = {
    0: 'Version',
    1: 'UDPTunnel',
    2: 'Authenticate',
    3: 'Ping',
    4: 'Reject',
    5: 'ServerSync',
    6: 'ChannelRemove',
    7: 'ChannelState',
    8: 'UserRemove',
    9: 'UserState',
    10: 'BanList',
    11: 'TextMessage',
    12: 'PermissionDenied',
    13: 'ACL',
    14: 'QueryUsers',
    15: 'CryptSetup',
    16: 'ContextActionModify',
    17: 'ContextAction',
    18: 'UserList',
    19: 'VoiceTarget',
    20: 'PermissionQuery',
    21: 'CodecVersion',
    22: 'UserStats',
    23: 'RequestBlob',
    24: 'ServerConfig',
    25: 'SuggestConfig'
};

var builder = protobufjs.loadProtoFile(path.join(__dirname, 'Mumble.proto'));
var mumble = builder.build('MumbleProto');
var messages = {};
var context = {schema: mumble};

module.exports.buildPacket = function (type, payload) {
    return new context.schema[type](payload || {})
}

module.exports.decodePacket = function (type_id, payload) {
    var type = map[type_id]
    return new context.schema[type].decode(payload || {})
}

module.exports.idByName = {};
module.exports.nameById = {};

for (var k in map) {
    module.exports.idByName[map[k]] = k * 1;
    module.exports.nameById[k] = map[k];
}<|endoftext|>


<commit_before>/* eslint camelcase: 0 */
import common from './common/en_US';
import confirmation from './confirmation/en_US';
import dapps from './dapps/en_US';
import dappsMaker from './dapps-maker/en_US';
import header from './header/en_US';
import home from './home/en_US';
import footer from './footer/en_US';
import gettingStarted from './getting-started/en_US';
import privacyPolicy from './privacy-policy/en_US';
import termsAndConditions from './terms-of-conditions/en_US';
import createWallet from './create-wallet/en_US';
import accessWallet from './access-wallet/en_US';
import interfaceTranslations from './interface/en_US';
import team from './team/en_US';
import popover from './popover/en_US';
import withoutWallet from './without-wallet/en_US';
import errorsGlobal from './errors-global/en_US';
import trezorError from './errors-trezor/en_US';
import ledgerError from './errors-ledger/en_US';
import secalotError from './errors-secalot/en_US';
import keepkeyError from './errors-keepkey/en_US';
import bitboxError from './errors-bitbox/en_US';
import buyHardwareWallet from './buy-hardware-wallet/en_US';

const en_US = {
  common: common,
  confirmation: confirmation,
  dapps: dapps,
  header: header,
  home: home,
  footer: footer,
  gettingStarted: gettingStarted,
  privacyPol: privacyPolicy,
  termsOfConditions: termsAndConditions,
  team: team,
  accessWallet: accessWallet,
  createWallet: createWallet,
  interface: interfaceTranslations,
  popover: popover,
  withoutWallet: withoutWallet,
  errorsGlobal: errorsGlobal,
  trezorError: trezorError,
  ledgerError: ledgerError,
  secalotError: secalotError,
  keepkeyError: keepkeyError,
  bitboxError: bitboxError,
  buyHardwareWallet: buyHardwareWallet
  dappsMaker: dappsMaker
};
export default en_US;
<commit_msg>fix missing comma breaking build<commit_after>/* eslint camelcase: 0 */
import common from './common/en_US';
import confirmation from './confirmation/en_US';
import dapps from './dapps/en_US';
import dappsMaker from './dapps-maker/en_US';
import header from './header/en_US';
import home from './home/en_US';
import footer from './footer/en_US';
import gettingStarted from './getting-started/en_US';
import privacyPolicy from './privacy-policy/en_US';
import termsAndConditions from './terms-of-conditions/en_US';
import createWallet from './create-wallet/en_US';
import accessWallet from './access-wallet/en_US';
import interfaceTranslations from './interface/en_US';
import team from './team/en_US';
import popover from './popover/en_US';
import withoutWallet from './without-wallet/en_US';
import errorsGlobal from './errors-global/en_US';
import trezorError from './errors-trezor/en_US';
import ledgerError from './errors-ledger/en_US';
import secalotError from './errors-secalot/en_US';
import keepkeyError from './errors-keepkey/en_US';
import bitboxError from './errors-bitbox/en_US';
import buyHardwareWallet from './buy-hardware-wallet/en_US';

const en_US = {
  common: common,
  confirmation: confirmation,
  dapps: dapps,
  header: header,
  home: home,
  footer: footer,
  gettingStarted: gettingStarted,
  privacyPol: privacyPolicy,
  termsOfConditions: termsAndConditions,
  team: team,
  accessWallet: accessWallet,
  createWallet: createWallet,
  interface: interfaceTranslations,
  popover: popover,
  withoutWallet: withoutWallet,
  errorsGlobal: errorsGlobal,
  trezorError: trezorError,
  ledgerError: ledgerError,
  secalotError: secalotError,
  keepkeyError: keepkeyError,
  bitboxError: bitboxError,
  buyHardwareWallet: buyHardwareWallet,
  dappsMaker: dappsMaker
};
export default en_US;
<|endoftext|>


<commit_before>// ==UserScript==
// @name        lpPerson
// @namespace   lphelper
// @include     https://bugs.launchpad.net*
// @require     http://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js
// @require     http://ajax.googleapis.com/ajax/libs/jqueryui/1.11.2/jquery-ui.min.js
// @resource    jQueryUICSS   http://ajax.googleapis.com/ajax/libs/jqueryui/1.11.2/themes/smoothness/jquery-ui.css
// @version     1
// @grant       none
// ==/UserScript==

var APIDomain = 'api.launchpad.net';
var APIUrl = 'https://' + APIDomain + '/1.0/';
var currentBugNumber;

var bugLastUpdatedImportanceClasses = [
    {olderThan: moment.duration(1, 'month'), importance: 'critical'},
    {olderThan: moment.duration(2, 'weeks'), importance: 'high'},
    {olderThan: moment.duration(1, 'week'), importance: 'medium'}
];

var calculateImportanceFromLastUpdated = function(last_updated) {
    var now = moment(),
        diff = now.diff(moment(last_updated)) / 1000;

    for(var i = 0; i < bugLastUpdatedImportanceClasses.length; i++) {
        var obj = bugLastUpdatedImportanceClasses[i];
        if (diff >= obj.olderThan.asSeconds()) {
            return obj.importance;
        }
    }
};

var importanceClass = function(importance) {
    if (importance) {
        return 'importance' + importance.toUpperCase();
    }

    return '';
};
var importanceSpan = function(contents, importance) {
    return '' + contents + '';
};
var clearLastUpdatedSpan = function($el) {
    $el.find('.js-lphelper-last-updated').remove();
};
var lastUpdatedSpan = function(response) {
    var importance = importanceClass(calculateImportanceFromLastUpdated(response.date_last_updated)),
        $innerSpan = $('[Last updated: ' + moment(response.date_last_updated).fromNow() + ']'),
        $el = $('');
    $el.append($innerSpan);
    $el.append('   ');

    return $el;
};

var bugNumberFromLink = function(url) {
    var match = (url || '').match(/^.*\+bug\/(\d+).*/);

    return match && match[1];
};
var makeUserLink = function(username) {
    return '' + username + '';
};
var usernameFromLink = function(url) {
    return (url || '').replace('https://launchpad.net/~', '')
        .replace('https://api.launchpad.net/1.0/~', '');
};

var urlRe = /(https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*))/g;
var urlify = function(str) {
    return str.replace(urlRe, function(s) {
        // Remove endings that usually break urls
        var badEndings = ['.', ',', ';'],
            ending = '';

        if (badEndings.indexOf(s.slice(-1)) > -1) {
            ending = s.slice(-1);
            s = s.slice(0, -1);
        }

        return '' + s + '' + ending;
    });
};

var cluetipOptions = function() {
    return {
        splitTitle: '|',
        showTitle: true,
        sticky: true,
        hoverIntent: false,
        mouseOutClose: true
    };
};

var bugCluetip = function() {
    var $el = $(this),
        $ela = $el.attr('href') ? $el : $el.find('a'),
        bugnumber = bugNumberFromLink($ela.attr('href')),
        url = APIUrl + 'bugs/' + bugnumber;

    if ((bugnumber === null) || (bugnumber == currentBugNumber)) {
        return;
    }

    $.ajax({
        method: 'GET',
        url: url,
        headers: {'Content-Type': 'application/json'},
        cache: true
    }).done(function(response) {
        clearLastUpdatedSpan($ela);
        var $lastUpdatedSpan = lastUpdatedSpan(response);
        $ela.prepend($lastUpdatedSpan);

        // Save on requests, fetch tasks lazily
        $ela.one('mouseenter', function() {
            $.ajax({
                method: 'GET',
                url: response.bug_tasks_collection_link,
                headers: {'Content-Type': 'application/json'},
                cache: true
            }).done(function(responseBugTasks) {
                var opts = cluetipOptions();
                opts.width = 600;

                var assignees = $.map(responseBugTasks.entries, function(entry) {
                    var assignee = usernameFromLink(entry.assignee_link);
                    if (assignee) {
                        assignee = makeUserLink(assignee);
                    } else {
                        assignee = 'None';
                    }

                    return assignee + ' ' + importanceSpan('[' + entry.bug_target_name + ']', entry.importance);
                });
                var owner = usernameFromLink(response.owner_link);
                var title = 'Bug ' + bugnumber + '
 Owner: ' + makeUserLink(owner) + '
 Assignees: ' + assignees.join(', ');
                var description = response.description.replace(/\n/g, '
');
                description = urlify(description);

                $ela.attr('title', title + '|' + description);
                $ela.cluetip(opts);
                $ela.trigger('showCluetip');
            });
        });
    });
};

var personCluetip = function() {
    var $el = $(this),
        href = $el.attr('href'),
        username = usernameFromLink(href),
        url = APIUrl + '~' + username + '/super_teams';

    $.ajax({
        method: "GET",
        url: url,
        headers: {'Content-Type': 'application/json'},
        cache: true
    }).done(function(response) {
        var teams = [];

        response.entries.sort(function(e1, e2) {
            return e1.display_name.localeCompare(e2.name);
        });
        teams = $.map(response.entries, function(entry) {
            return '' + entry.display_name + '';
        });
        $el.attr('title', username + '|' + teams.join('
'));
        $el.cluetip(cluetipOptions());
        $el.trigger('showCluetip');
    }).fail(function(r) {
        console.log('error', r.status, r.statusText);
    });
};

currentBugNumber = bugNumberFromLink(window.location.href);

$(document).ready(function() {
    var subscribersPollNum = 0;

    // Person info
    $(document).find('a.sprite.person').one('mouseenter', personCluetip);

    // Persons are also fetched later (for example: subscribers on the bug page)
    // So we poll for them a couple of times
    var findSubscribers = function() {
        var $elems = $(document).find('a:has(.sprite.person)');

        if($elems.length > 0) {
            $elems.one('mouseenter', personCluetip);
        } else {
            subscribersPollNum++;
            if (subscribersPollNum < 10) {
                setTimeout(findSubscribers, 1000);
            }
        }
    };
    findSubscribers();

    // Bug info
    $(document).find('.buglisting-row .buginfo').each(bugCluetip);
    $(document).find('a[href*="bugs.launchpad.net"]').each(bugCluetip);
});
<commit_msg>Refactoring: grouping common functions together<commit_after>// ==UserScript==
// @name        lpPerson
// @namespace   lphelper
// @include     https://bugs.launchpad.net*
// @require     http://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js
// @require     http://ajax.googleapis.com/ajax/libs/jqueryui/1.11.2/jquery-ui.min.js
// @resource    jQueryUICSS   http://ajax.googleapis.com/ajax/libs/jqueryui/1.11.2/themes/smoothness/jquery-ui.css
// @version     1
// @grant       none
// ==/UserScript==

var APIDomain = 'api.launchpad.net';
var APIUrl = 'https://' + APIDomain + '/1.0/';
var currentBugNumber;

var bugLastUpdatedImportanceClasses = [
    {olderThan: moment.duration(1, 'month'), importance: 'critical'},
    {olderThan: moment.duration(2, 'weeks'), importance: 'high'},
    {olderThan: moment.duration(1, 'week'), importance: 'medium'}
];

var calculateImportanceFromLastUpdated = function(last_updated) {
    var now = moment(),
        diff = now.diff(moment(last_updated)) / 1000;

    for(var i = 0; i < bugLastUpdatedImportanceClasses.length; i++) {
        var obj = bugLastUpdatedImportanceClasses[i];
        if (diff >= obj.olderThan.asSeconds()) {
            return obj.importance;
        }
    }
};

var HTMLHelpers = {
    importanceClass: function(importance) {
        if (importance) {
            return 'importance' + importance.toUpperCase();
        }

        return '';
    },
    importanceSpan: function(contents, importance) {
        return '' + contents + '';
    },
    clearLastUpdatedSpan: function($el) {
        $el.find('.js-lphelper-last-updated').remove();
    },
    lastUpdatedSpan: function(response) {
        var importance = HTMLHelpers.importanceClass(calculateImportanceFromLastUpdated(response.date_last_updated)),
            $innerSpan = $('[Last updated: ' + moment(response.date_last_updated).fromNow() + ']'),
            $el = $('');
        $el.append($innerSpan);
        $el.append('   ');

        return $el;
    },

    urlRe: /(https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*))/g,
    urlify: function(str) {
        return str.replace(HTMLHelpers.urlRe, function(s) {
            // Remove endings that usually break urls
            var badEndings = ['.', ',', ';'],
                ending = '';

            if (badEndings.indexOf(s.slice(-1)) > -1) {
                ending = s.slice(-1);
                s = s.slice(0, -1);
            }

            return '' + s + '' + ending;
        });
    }
};

var URLHelpers = {
    bugNumberFromURL: function(url) {
        var match = (url || '').match(/^.*\+bug\/(\d+).*/);

        return match && match[1];
    },
    makeUserURL: function(username) {
        return '' + username + '';
    },
    usernameFromURL: function(url) {
        return (url || '').replace('https://launchpad.net/~', '')
            .replace('https://api.launchpad.net/1.0/~', '');
    }
};

var cluetipOptions = function() {
    return {
        splitTitle: '|',
        showTitle: true,
        sticky: true,
        hoverIntent: false,
        mouseOutClose: true
    };
};

var CluetipFunctions = {
    bug: function() {
        var $el = $(this),
            $ela = $el.attr('href') ? $el : $el.find('a'),
            bugnumber = URLHelpers.bugNumberFromURL($ela.attr('href')),
            url = APIUrl + 'bugs/' + bugnumber;

        if ((bugnumber === null) || (bugnumber == currentBugNumber)) {
            return;
        }

        $.ajax({
            method: 'GET',
            url: url,
            headers: {'Content-Type': 'application/json'},
            cache: true
        }).done(function(response) {
            HTMLHelpers.clearLastUpdatedSpan($ela);
            var $lastUpdatedSpan = HTMLHelpers.lastUpdatedSpan(response);
            $ela.prepend($lastUpdatedSpan);

            // Save on requests, fetch tasks lazily
            $ela.one('mouseenter', function() {
                $.ajax({
                    method: 'GET',
                    url: response.bug_tasks_collection_link,
                    headers: {'Content-Type': 'application/json'},
                    cache: true
                }).done(function(responseBugTasks) {
                    var opts = cluetipOptions();
                    opts.width = 600;

                    var assignees = $.map(responseBugTasks.entries, function(entry) {
                        var assignee = URLHelpers.usernameFromURL(entry.assignee_link);
                        if (assignee) {
                            assignee = URLHelpers.makeUserURL(assignee);
                        } else {
                            assignee = 'None';
                        }

                        return assignee + ' ' + HTMLHelpers.importanceSpan('[' + entry.bug_target_name + ']', entry.importance);
                    });
                    var owner = URLHelpers.usernameFromURL(response.owner_link);
                    var title = 'Bug ' + bugnumber + '
 Owner: ' + URLHelpers.makeUserURL(owner) + '
 Assignees: ' + assignees.join(', ');
                    var description = response.description.replace(/\n/g, '
');
                    description = HTMLHelpers.urlify(description);

                    $ela.attr('title', title + '|' + description);
                    $ela.cluetip(opts);
                    $ela.trigger('showCluetip');
                });
            });
        });
    },

    person: function() {
        var $el = $(this),
            href = $el.attr('href'),
            username = URLHelpers.usernameFromURL(href),
            url = APIUrl + '~' + username + '/super_teams';

        $.ajax({
            method: "GET",
            url: url,
            headers: {'Content-Type': 'application/json'},
            cache: true
        }).done(function(response) {
            var teams = [];

            response.entries.sort(function(e1, e2) {
                return e1.display_name.localeCompare(e2.name);
            });
            teams = $.map(response.entries, function(entry) {
                return '' + entry.display_name + '';
            });
            $el.attr('title', username + '|' + teams.join('
'));
            $el.cluetip(cluetipOptions());
            $el.trigger('showCluetip');
        }).fail(function(r) {
            console.log('error', r.status, r.statusText);
        });
    }
};

currentBugNumber = URLHelpers.bugNumberFromURL(window.location.href);

$(document).ready(function() {
    var subscribersPollNum = 0;

    // Person info
    $(document).find('a.sprite.person').one('mouseenter', CluetipFunctions.person);

    // Persons are also fetched later (for example: subscribers on the bug page)
    // So we poll for them a couple of times
    var findSubscribers = function() {
        var $elems = $(document).find('a:has(.sprite.person)');

        if($elems.length > 0) {
            $elems.one('mouseenter', CluetipFunctions.person);
        } else {
            subscribersPollNum++;
            if (subscribersPollNum < 10) {
                setTimeout(findSubscribers, 1000);
            }
        }
    };
    findSubscribers();

    // Bug info
    $(document).find('.buglisting-row .buginfo').each(CluetipFunctions.bug);
    $(document).find('a[href*="bugs.launchpad.net"]').each(CluetipFunctions.bug);
});
<|endoftext|>


<commit_before>// This is just for successful submission to generate the xref links
var oabSuccess = (function($) {

  function parseCrossRef(entry, doi) {
    var metadata = {'doi': doi},
        item = entry["pam:message"]["pam:article"];

    if (item["dc:title"]) {
      metadata["title"] = item["dc:title"];
    }

    if (item["dc:creator"]) {
      if (!$.isArray(item["dc:creator"])) {
        item["dc:creator"] = [item["dc:creator"]];
      }

      metadata["authors"] = item["dc:creator"].join(", ");
    }

    if (item["prism:publicationName"]) {
      metadata["publication"] = item["prism:publicationName"];
    }

    if (item["prism:publicationDate"]) {
      // TODO: zero-pad or reformat the date, using Moment.js?
      metadata["date"] = item["prism:publicationDate"];
    }

    return metadata;
  }

  function lookupCrossRef() {
    var doi = $('body').data('doi');

    if (doi) {
      $.ajax({
          url: 'http://data.crossref.org/' + encodeURIComponent(doi),
          dataType: "json",
          success: function(response) {
            if (response.feed.entry) {
              var metadata = parseCrossRef(response.feed.entry, doi);

              addPubMedCentralLink(metadata);
              addScholarDOILink(metadata);
              addScholarTitleLink(metadata);
              discoverCORELinks(metadata);
            }
          }
      });
    }
  }

  function addPubMedCentralLink(metadata) {
    var doi = metadata["doi"];

    if (doi) {
      $.ajax({
          url: 'http://eutils.ncbi.nlm.nih.gov/entrez/eutils/esearch.fcgi',
          data: {
            db: "pmc",
            retmax: 1,
            term: doi + "[DOI]",
            tool: "oabutton"
          },
          dataType: "xml",
          success: function(response) {
            if (response.getElementsByTagName('Count')[0].textContent === '0') {
              return;
            }

            var pmcid = response.getElementsByTagName('Id')[0].textContent;
            var url = "http://www.ncbi.nlm.nih.gov/pmc/articles/PMC" + pmcid + "/";
            var pmc_link = document.createElement("a");
            pmc_link.setAttribute("class", "likeabutton btn btn-primary btn-block")
            pmc_link.setAttribute("href", url);
            pmc_link.setAttribute("target", "_blank");
            pmc_link.innerHTML = "View on PubMedCentral";

            $("#pmc_links").append(pmc_link);
          }
      });
    }
  }

  function addScholarDOILink(metadata) {
    var doi = metadata["doi"];

    if (doi) {
      var url = 'http://scholar.google.com/scholar?cluster=' + encodeURIComponent('http://dx.doi.org/' + doi);

      var sch_link = document.createElement("a");
      sch_link.setAttribute("class", "likeabutton btn btn-primary btn-block")
      sch_link.setAttribute("href", url);
      sch_link.setAttribute("target", "_blank");
      sch_link.innerHTML = "Search Google Scholar by DOI";

      $("#google_links").append(sch_link);
    }
  }

  function addScholarTitleLink(metadata) {
    var title = metadata["title"];

    if (title) {
      var url = 'http://scholar.google.com/scholar?as_occt=title&as_q=' + encodeURIComponent(title);

      var sch_link = document.createElement("a");
      sch_link.setAttribute("class", "likeabutton btn btn-primary btn-block")
      sch_link.setAttribute("href", url);
      sch_link.setAttribute("target", "_blank");
      sch_link.innerHTML = "Search Google Scholar by title";

      $("#google_links").append(sch_link);
    }
  }

  function discoverCORELinks(metadata) {
    var title = metadata["title"];

    if (title) {
        $.ajax({
            url: "/metadata/coresearch.json/title:(" + encodeURIComponent(title) + ")",
            dataType: 'json',
            success: function(response) {
                var records = response.ListRecords;
                var $list = $('

');
                for (var i = 1; i < records.length; i++) {
                    metadata = records[i]['record']['metadata']['oai_dc:dc'];
                    $list.append('
'
                        + metadata['dc:creator']
                        + ' (' + metadata['dc:date'] + '); '
                        + metadata['dc:title']
                        + '
');
                }

                $core_div  = $('
Matches from the CORE repository:
');
                $core_div.append($list);
                $("#core_links").append($core_div);
            }
        });
    }
  }

  return {
    parseCrossRef: parseCrossRef,
    lookupCrossRef: lookupCrossRef,
    addScholarDOILink: addScholarDOILink,
    addScholarTitleLink: addScholarTitleLink,
    discoverCORELinks: discoverCORELinks,
  };

})(jQuery);

$(document).ready(function() {
  oabSuccess.lookupCrossRef();
});
<commit_msg>Tweak styling of Google Scholar search buttons<commit_after>// This is just for successful submission to generate the xref links
var oabSuccess = (function($) {

  function parseCrossRef(entry, doi) {
    var metadata = {'doi': doi},
        item = entry["pam:message"]["pam:article"];

    if (item["dc:title"]) {
      metadata["title"] = item["dc:title"];
    }

    if (item["dc:creator"]) {
      if (!$.isArray(item["dc:creator"])) {
        item["dc:creator"] = [item["dc:creator"]];
      }

      metadata["authors"] = item["dc:creator"].join(", ");
    }

    if (item["prism:publicationName"]) {
      metadata["publication"] = item["prism:publicationName"];
    }

    if (item["prism:publicationDate"]) {
      // TODO: zero-pad or reformat the date, using Moment.js?
      metadata["date"] = item["prism:publicationDate"];
    }

    return metadata;
  }

  function lookupCrossRef() {
    var doi = $('body').data('doi');

    if (doi) {
      $.ajax({
          url: 'http://data.crossref.org/' + encodeURIComponent(doi),
          dataType: "json",
          success: function(response) {
            if (response.feed.entry) {
              var metadata = parseCrossRef(response.feed.entry, doi);

              addPubMedCentralLink(metadata);
              addScholarDOILink(metadata);
              addScholarTitleLink(metadata);
              discoverCORELinks(metadata);
            }
          }
      });
    }
  }

  function addPubMedCentralLink(metadata) {
    var doi = metadata["doi"];

    if (doi) {
      $.ajax({
          url: 'http://eutils.ncbi.nlm.nih.gov/entrez/eutils/esearch.fcgi',
          data: {
            db: "pmc",
            retmax: 1,
            term: doi + "[DOI]",
            tool: "oabutton"
          },
          dataType: "xml",
          success: function(response) {
            if (response.getElementsByTagName('Count')[0].textContent === '0') {
              return;
            }

            var pmcid = response.getElementsByTagName('Id')[0].textContent;
            var url = "http://www.ncbi.nlm.nih.gov/pmc/articles/PMC" + pmcid + "/";
            var pmc_link = document.createElement("a");
            pmc_link.setAttribute("class", "likeabutton btn btn-primary btn-block")
            pmc_link.setAttribute("href", url);
            pmc_link.setAttribute("target", "_blank");
            pmc_link.innerHTML = "View on PubMedCentral";

            $("#pmc_links").append(pmc_link);
          }
      });
    }
  }

  function addScholarDOILink(metadata) {
    var doi = metadata["doi"];

    if (doi) {
      var url = 'http://scholar.google.com/scholar?cluster=' + encodeURIComponent('http://dx.doi.org/' + doi);

      var sch_link = document.createElement("a");
      sch_link.setAttribute("class", "btn btn-primary btn-block")
      sch_link.setAttribute("href", url);
      sch_link.setAttribute("target", "_blank");
      sch_link.innerHTML = "Search Google Scholar by DOI";

      $("#google_links").append(sch_link);
    }
  }

  function addScholarTitleLink(metadata) {
    var title = metadata["title"];

    if (title) {
      var url = 'http://scholar.google.com/scholar?as_occt=title&as_q=' + encodeURIComponent(title);

      var sch_link = document.createElement("a");
      sch_link.setAttribute("class", "btn btn-primary btn-block")
      sch_link.setAttribute("href", url);
      sch_link.setAttribute("target", "_blank");
      sch_link.innerHTML = "Search Google Scholar by title";

      $("#google_links").append(sch_link);
    }
  }

  function discoverCORELinks(metadata) {
    var title = metadata["title"];

    if (title) {
        $.ajax({
            url: "/metadata/coresearch.json/title:(" + encodeURIComponent(title) + ")",
            dataType: 'json',
            success: function(response) {
                var records = response.ListRecords;
                var $list = $('

');
                for (var i = 1; i < records.length; i++) {
                    metadata = records[i]['record']['metadata']['oai_dc:dc'];
                    $list.append('
'
                        + metadata['dc:creator']
                        + ' (' + metadata['dc:date'] + '); '
                        + metadata['dc:title']
                        + '
');
                }

                $core_div  = $('
Matches from the CORE repository:
');
                $core_div.append($list);
                $("#core_links").append($core_div);
            }
        });
    }
  }

  return {
    parseCrossRef: parseCrossRef,
    lookupCrossRef: lookupCrossRef,
    addScholarDOILink: addScholarDOILink,
    addScholarTitleLink: addScholarTitleLink,
    discoverCORELinks: discoverCORELinks,
  };

})(jQuery);

$(document).ready(function() {
  oabSuccess.lookupCrossRef();
});
<|endoftext|>


<commit_before>/*
This is the spritecontroller class
the engine creates one of these at runtime, it is called "SL".

Sprites are defined in obj/Sprites.js and you can access them like this:
   sprite = SL.Sprite(<SPRITENAME>);

to draw:
   sprite.draw(x,y,ctx);
or if manually:
   image = sprite.get();
or
   image = SL.get(<SPRITENAME>);
*/

class SpriteController {
  constructor () {
    this.Sprites = undefined;
    this.initialized = false;

    //Called in Main.js (Initialises Sprite Controller)
    this.init = function(x) {
      this.initialized = true;
      this.Sprites = x;
      this.len = Object.keys(this.Sprites).length;
      window.setTimeout(function() {SL.fixN()},6);
      window.setTimeout(function() {SL.fixN()},300);
    }

    this.fixN = function() {
      //set up natural
      for (var s in this.Sprites) {
          let spr = this.Sprites[s];
          let wi = spr.width;
          let he = spr.height;
          let fw = spr.frameSize[0];
          let fh = spr.frameSize[1];
          let src = spr.src;
          let img = new Image();
          img.src = src;
          let nw = img.naturalWidth;
          let nh = img.naturalHeight;

          if (wi == "n") {spr.width = nw;}
          if (he == "n") {spr.height = nh;}
          if (fw == "n") {spr.frameSize[0] = nw;}
          if (fh == "n") {spr.frameSize[1] = nh;}
      }
   }

    //Returns Sprite used like SL.Sprite(<SpriteName>)
    this.Sprite = function(x) {
      let obj = this.Sprites[x];

      //Add Sprite Functions
      obj.get = function() {
         let img = new Image();
         img.src = this.src;
         return img;
      }

      obj.getDrawArgs = function(x,y) {
         let args = {
            'sx': this.frameOffset[0],
            'sy': this.frameOffset[1],
            'sWidth': this.frameSize[0],
            'sHeight': this.frameSize[1],
            'scale': this.scale,
            'dWidth': this.width*this.scale[0],
            'dHeight': this.height*this.scale[1],
            'height': this.height,
            'width': this.width,
            'dx': x || 0,
            'dy': y || 0
         }
         return args;
      }

      obj.draw = function(x,y,ctx) {
         let img = new Image();
         img.src = this.src;

         let args = this.getDrawArgs(x,y);
         ctx.drawImage(img, args.sx, args.sy, args.sWidth, args.sHeight, args.dx, args.dy, args.dWidth, args.dHeight);
      }

      return obj;
    }

    //This is a shortcut, used like SL.get(<SpriteName>)
    this.get = function(x) {
      let spr = this.Sprites[x];
      let a = new Image();
      a.src = spr.src;
      return a;
   }

    //Used to add sprite after initialization
    this.addSprite = function(name,src,width,height) {
      this.Sprites[name] = {};
      this.Sprites[name].src = src;
      this.Sprites[name].width = width;
      this.Sprites[name].height = height;
      return this.Sprites[name];
    }
  }
}
<commit_msg>Warn of 'n' tags<commit_after>/*
This is the spritecontroller class
the engine creates one of these at runtime, it is called "SL".

Sprites are defined in obj/Sprites.js and you can access them like this:
   sprite = SL.Sprite(<SPRITENAME>);

to draw:
   sprite.draw(x,y,ctx);
or if manually:
   image = sprite.get();
or
   image = SL.get(<SPRITENAME>);

Note: You can use 'n' in place of natural size,
but only locally as it doesnt work with loading delays

*/

class SpriteController {
  constructor () {
    this.Sprites = undefined;
    this.initialized = false;

    //Called in Main.js (Initialises Sprite Controller)
    this.init = function(x) {
      this.initialized = true;
      this.Sprites = x;
      this.len = Object.keys(this.Sprites).length;
      window.setTimeout(function() {SL.fixN()},6);
      window.setTimeout(function() {SL.fixN()},300);
    }

    this.fixN = function() {
      //set up natural
      for (var s in this.Sprites) {
          let spr = this.Sprites[s];
          let wi = spr.width;
          let he = spr.height;
          let fw = spr.frameSize[0];
          let fh = spr.frameSize[1];
          let src = spr.src;
          let img = new Image();
          img.src = src;
          let nw = img.naturalWidth;
          let nh = img.naturalHeight;

          if (wi == "n") {spr.width = nw;}
          if (he == "n") {spr.height = nh;}
          if (fw == "n") {spr.frameSize[0] = nw;}
          if (fh == "n") {spr.frameSize[1] = nh;}
      }
   }

    //Returns Sprite used like SL.Sprite(<SpriteName>)
    this.Sprite = function(x) {
      let obj = this.Sprites[x];

      //Add Sprite Functions
      obj.get = function() {
         let img = new Image();
         img.src = this.src;
         return img;
      }

      obj.getDrawArgs = function(x,y) {
         let args = {
            'sx': this.frameOffset[0],
            'sy': this.frameOffset[1],
            'sWidth': this.frameSize[0],
            'sHeight': this.frameSize[1],
            'scale': this.scale,
            'dWidth': this.width*this.scale[0],
            'dHeight': this.height*this.scale[1],
            'height': this.height,
            'width': this.width,
            'dx': x || 0,
            'dy': y || 0
         }
         return args;
      }

      obj.draw = function(x,y,ctx) {
         let img = new Image();
         img.src = this.src;

         let args = this.getDrawArgs(x,y);
         ctx.drawImage(img, args.sx, args.sy, args.sWidth, args.sHeight, args.dx, args.dy, args.dWidth, args.dHeight);
      }

      return obj;
    }

    //This is a shortcut, used like SL.get(<SpriteName>)
    this.get = function(x) {
      let spr = this.Sprites[x];
      let a = new Image();
      a.src = spr.src;
      return a;
   }

    //Used to add sprite after initialization
    this.addSprite = function(name,src,width,height) {
      this.Sprites[name] = {};
      this.Sprites[name].src = src;
      this.Sprites[name].width = width;
      this.Sprites[name].height = height;
      return this.Sprites[name];
    }
  }
}
<|endoftext|>