<commit_before>import React, { useEffect, useRef, useState } from 'react';
import { useContentObj } from 'helpers/hooks';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import {
  changeByUserStatusForThumbs,
  editVideoThumbs,
  likeVideo,
  setDifficulty
} from 'redux/actions/VideoActions';
import Carousel from 'components/Carousel';
import Button from 'components/Button';
import Loading from 'components/Loading';
import VideoPlayer from 'components/VideoPlayer';
import NotFound from 'components/NotFound';
import CheckListGroup from 'components/CheckListGroup';
import PageTab from './PageTab';
import Comments from 'components/Comments';
import Details from './Details';
import NavMenu from './NavMenu';
import ResultModal from './Modals/ResultModal';
import QuestionsBuilder from './QuestionsBuilder';
import ConfirmModal from 'components/Modals/ConfirmModal';
import {
  fetchedVideoCodeFromURL,
  stringIsEmpty
} from 'helpers/stringHelpers';
import queryString from 'query-string';
import ErrorBoundary from 'components/Wrappers/ErrorBoundary';
import Subjects from 'components/Subjects';
import RewardStatus from 'components/RewardStatus';
import request from 'axios';
import { Color, mobileMaxWidth } from 'constants/css';
import { css } from 'emotion';
import {
  auth,
  fetchPlaylistsContaining,
  handleError,
  loadComments,
  loadSubjects
} from 'helpers/requestHelpers';
import URL from 'constants/URL';

VideoPage.propTypes = {
  authLevel: PropTypes.number,
  canEdit: PropTypes.bool,
  changeByUserStatusForThumbs: PropTypes.func.isRequired,
  dispatch: PropTypes.func.isRequired,
  editVideoThumbs: PropTypes.func.isRequired,
  history: PropTypes.object.isRequired,
  likeVideo: PropTypes.func.isRequired,
  location: PropTypes.object.isRequired,
  match: PropTypes.object.isRequired,
  setDifficulty: PropTypes.func.isRequired,
  userId: PropTypes.number
};

function VideoPage({
  authLevel,
  canEdit,
  changeByUserStatusForThumbs,
  dispatch,
  editVideoThumbs,
  history,
  likeVideo,
  location: { search },
  match: {
    params: { videoId: initialVideoId }
  },
  setDifficulty,
  userId
}) {
  const [changingPage, setChangingPage] = useState(false);
  const [watchTabActive, setWatchTabActive] = useState(true);
  const [currentSlide, setCurrentSlide] = useState(0);
  const [userAnswers, setUserAnswers] = useState([]);
  const [resultModalShown, setResultModalShown] = useState(false);
  const [confirmModalShown, setConfirmModalShown] = useState(false);
  const [onEdit, setOnEdit] = useState(false);
  const [questionsBuilderShown, setQuestionsBuilderShown] = useState(false);
  const [videoId, setVideoId] = useState(initialVideoId);
  const [videoLoading, setVideoLoading] = useState(true);
  const [videoUnavailable, setVideoUnavailable] = useState(false);
  const mounted = useRef(true);
  const CommentInputAreaRef = useRef(null);
  const videoIdRef = useRef(initialVideoId);
  const {
    contentObj,
    contentObj: {
      byUser,
      childComments: comments,
      commentsLoadMoreButton,
      content,
      description,
      difficulty,
      hasHqThumb,
      likes,
      questions,
      stars,
      subjects,
      subjectsLoadMoreButton,
      tags,
      timeStamp,
      title,
      uploader,
      views
    },
    setContentObj,
    onAddTags,
    onAttachStar,
    onDeleteComment,
    onDeleteSubject,
    onEditComment,
    onEditRewardComment,
    onEditSubject,
    onInitContent,
    onLikeComment,
    onLikeContent,
    onLoadSubjectComments,
    onLoadMoreComments,
    onLoadMoreReplies,
    onLoadMoreSubjects,
    onLoadMoreSubjectComments,
    onLoadMoreSubjectReplies,
    onSetDifficulty,
    onSetSubjectDifficulty,
    onUploadComment,
    onUploadReply,
    onUploadSubject
  } = useContentObj({
    type: 'video',
    contentId: Number(videoId)
  });

  useEffect(() => {
    mounted.current = true;
    setVideoId(initialVideoId);
    videoIdRef.current = initialVideoId;
    setChangingPage(true);
    loadVideoPage();
    setChangingPage(false);
    async function loadVideoPage() {
      try {
        const { data } = await request.get(
          `${URL}/video/page?videoId=${videoIdRef.current}`
        );
        const subjectsObj = await loadSubjects({
          type: 'video',
          contentId: videoIdRef.current
        });
        const commentsObj = await loadComments({
          id: videoIdRef.current,
          type: 'video'
        });
        const tags = await fetchPlaylistsContaining({
          videoId: videoIdRef.current
        });
        if (mounted.current) {
          setVideoLoading(false);
          onInitContent({
            content: {
              ...data,
              tags,
              contentId: Number(videoIdRef.current),
              childComments: commentsObj?.comments || [],
              commentsLoadMoreButton: commentsObj?.loadMoreButton || false,
              subjects: subjectsObj?.results || [],
              subjectsLoadMoreButton: subjectsObj?.loadMoreButton || false
            }
          });
        }
      } catch (error) {
        if (error.response) {
          const { data = {} } = error.response;
          if (data.notFound) {
            setVideoUnavailable(true);
          }
        }
        console.error(error.response || error);
      }
    }
    return function cleanUp() {
      mounted.current = false;
    };
  }, [initialVideoId]);

  const { playlist: playlistId } = queryString.parse(search);
  const userIsUploader = uploader?.id === userId;
  const userCanEditThis = canEdit && authLevel >= uploader?.authLevel;
  return (
    <ErrorBoundary
      className={css`
        display: flex;
        justify-content: space-between;
        width: 100%;
        height: 100%;
        margin-top: 1rem;
        @media (max-width: ${mobileMaxWidth}) {
          margin-top: 0;
          flex-direction: column;
        }
      `}
    >

        {(videoLoading || videoUnavailable) && (

            {videoLoading && <Loading text="Loading Video..." />}
            {videoUnavailable && <NotFound text="Video does not exist" />}

        )}
        {!videoLoading && !videoUnavailable && content && (

              <PageTab
                questions={questions}
                watchTabActive={watchTabActive}
                onWatchTabClick={() => setWatchTabActive(true)}
                onQuestionTabClick={() => setWatchTabActive(false)}
              />

                {!questionsBuilderShown && (
                  <VideoPlayer
                    difficulty={difficulty}
                    byUser={!!byUser}
                    key={videoId}
                    hasHqThumb={hasHqThumb}
                    onEdit={onEdit}
                    videoId={videoId}
                    videoCode={content}
                    title={title}
                    uploader={uploader}
                    minimized={!watchTabActive}
                  />
                )}
                {!watchTabActive && questions.length > 0 && (
                  <Carousel
                    allowDrag={false}
                    style={{ marginTop: !!difficulty && '1rem' }}
                    progressBar
                    showQuestionsBuilder={() => setQuestionsBuilderShown(true)}
                    userIsUploader={userIsUploader}
                    userCanEditThis={userCanEditThis}
                    slidesToShow={1}
                    slidesToScroll={1}
                    slideIndex={currentSlide}
                    afterSlide={setCurrentSlide}
                    onFinish={() => setResultModalShown(true)}
                  >
                    {handleRenderSlides()}
                  </Carousel>
                )}
                {!watchTabActive && questions.length === 0 && (

There are no questions yet.

                    {(userIsUploader || userCanEditThis) && (
                      <Button
                        style={{ marginTop: '2rem', fontSize: '2rem' }}
                        success
                        filled
                        onClick={() => setQuestionsBuilderShown(true)}
                      >
                        Add Questions
                      </Button>
                    )}

                )}

               setOnEdit(true)}
                onEditCancel={() => setOnEdit(false)}
                onEditFinish={handleEditVideoPage}
                onDelete={() => setConfirmModalShown(true)}
                setDifficulty={handleSetDifficulty}
                stars={stars}
                tags={tags}
                title={title}
                timeStamp={timeStamp}
                uploader={uploader}
                userId={userId}
                videoId={videoId}
                videoViews={views}
              />
              <RewardStatus
                contentType="video"
                difficulty={byUser ? 5 : 0}
                onCommentEdit={onEditRewardComment}
                style={{
                  fontSize: '1.4rem'
                }}
                stars={stars}
                uploaderName={uploader.username}
              />

            <Subjects
              loadMoreButton={subjectsLoadMoreButton}
              subjects={subjects}
              onLoadMoreSubjects={onLoadMoreSubjects}
              onLoadSubjectComments={onLoadSubjectComments}
              onSubjectEditDone={onEditSubject}
              onSubjectDelete={onDeleteSubject}
              setSubjectDifficulty={onSetSubjectDifficulty}
              uploadSubject={onUploadSubject}
              contentId={videoId}
              type="video"
              rootDifficulty={difficulty}
              commentActions={{
                attachStar: onAttachStar,
                editRewardComment: onEditRewardComment,
                onDelete: onDeleteComment,
                onEditDone: onEditComment,
                onLikeClick: onLikeComment,
                onLoadMoreComments: onLoadMoreSubjectComments,
                onLoadMoreReplies: onLoadMoreSubjectReplies,
                onUploadComment,
                onUploadReply
              }}
            />

                Comment on this video

              <Comments
                autoExpand
                comments={comments}
                inputAreaInnerRef={CommentInputAreaRef}
                inputTypeLabel={'comment'}
                loadMoreButton={commentsLoadMoreButton}
                onAttachStar={onAttachStar}
                onCommentSubmit={onUploadComment}
                onDelete={onDeleteComment}
                onEditDone={onEditComment}
                onLikeClick={onLikeComment}
                onLoadMoreComments={onLoadMoreComments}
                onLoadMoreReplies={onLoadMoreReplies}
                onReplySubmit={onUploadReply}
                onRewardCommentEdit={onEditRewardComment}
                parent={{
                  type: 'video',
                  difficulty,
                  id: Number(videoId),
                  uploader
                }}
                style={{ paddingTop: '1rem' }}
                userId={userId}
              />

            {resultModalShown && (
              <ResultModal
                onHide={() => setResultModalShown(false)}
                numberCorrect={numberCorrect}
                totalQuestions={questions.length}
              />
            )}
            {confirmModalShown && (
              <ConfirmModal
                title="Remove Video"
                onHide={() => setConfirmModalShown(false)}
                onConfirm={handleDeleteVideo}
              />
            )}
            {questionsBuilderShown && (
              <QuestionsBuilder
                questions={questions}
                title={title}
                videoCode={content}
                onSubmit={handleUploadQuestions}
                onHide={() => setQuestionsBuilderShown(false)}
              />
            )}

        )}

      <NavMenu videoId={videoId} playlistId={playlistId} />
    </ErrorBoundary>
  );

  async function handleDeleteVideo() {
    await request.delete(`${URL}/video?videoId=${videoId}`, auth());
    history.push('/videos');
  }

  async function handleEditVideoPage(params) {
    setOnEdit(false);
    try {
      await request.post(`${URL}/video/edit/page`, params, auth());
      const url = fetchedVideoCodeFromURL(params.url);
      setContentObj({
        ...contentObj,
        title: params.title,
        description: params.description,
        content: url
      });
      editVideoThumbs({
        videoId: Number(params.videoId),
        title: params.title,
        url
      });
    } catch (error) {
      handleError(error, dispatch);
    }
  }

  function handleChangeByUserStatus({ contentId, byUser }) {
    setContentObj({
      ...contentObj,
      byUser: byUser
    });
    changeByUserStatusForThumbs({ videoId: Number(contentId), byUser });
  }

  function handleLikeVideo(likes, videoId) {
    onLikeContent({ likes, type: 'video', contentId: videoId });
    likeVideo({ likes, videoId });
  }

  function numberCorrect() {
    const correctAnswers = questions.map(question => {
      return question.correctChoice;
    });
    let numberCorrect = 0;
    for (let i = 0; i < userAnswers.length; i++) {
      if (userAnswers[i] + 1 === correctAnswers[i]) numberCorrect++;
    }
    return numberCorrect;
  }

  function handleSelectChoice(newAnswer) {
    if (typeof userAnswers[currentSlide] === 'number') {
      return setUserAnswers(
        userAnswers.map((answer, index) => {
          return index === currentSlide ? newAnswer : answer;
        })
      );
    }
    const newAnswers = [...userAnswers];
    newAnswers[currentSlide] = newAnswer;
    setUserAnswers(newAnswers);
  }

  function handleRenderSlides() {
    return questions.map((question, index) => {
      const filteredChoices = question.choices.filter(choice => !!choice);
      let isCurrentSlide = index === currentSlide;
      const listItems = filteredChoices.map((choice, index) => {
        let isSelectedChoice = index === userAnswers[currentSlide];
        return {
          label: choice,
          checked: isCurrentSlide && isSelectedChoice
        };
      });
      return (

          <CheckListGroup
            inputType="radio"
            listItems={listItems}
            onSelect={handleSelectChoice}
            style={{ marginTop: '1.5rem', paddingRight: '1rem' }}
          />

      );
    });
  }

  function handleSetDifficulty({ contentId, difficulty }) {
    onSetDifficulty({ contentId, difficulty });
    setDifficulty({ videoId: Number(contentId), difficulty });
  }

  async function handleUploadQuestions(questions) {
    const data = {
      videoId,
      questions: questions.map(question => {
        const choices = question.choices.filter(choice => {
          return choice.label && !stringIsEmpty(choice.label);
        });
        return {
          videoId,
          title: question.title,
          correctChoice: (() => {
            let correctChoice = 0;
            for (let i = 0; i < choices.length; i++) {
              if (choices[i].checked) correctChoice = i + 1;
            }
            return correctChoice;
          })(),
          choice1: choices[0].label,
          choice2: choices[1].label,
          choice3: choices[2]?.label,
          choice4: choices[3]?.label,
          choice5: choices[4]?.label,
          creator: userId
        };
      })
    };
    try {
      await request.post(`${URL}/video/questions`, data, auth());
      const questions = data.questions.map(question => {
        return {
          title: question.title,
          choices: [
            question.choice1,
            question.choice2,
            question.choice3,
            question.choice4,
            question.choice5
          ],
          correctChoice: question.correctChoice
        };
      });
      setContentObj({
        ...contentObj,
        questions
      });
      setQuestionsBuilderShown(false);
      setCurrentSlide(0);
      setUserAnswers([]);
    } catch (error) {
      handleError(error, dispatch);
    }
  }
}

export default connect(
  state => ({
    authLevel: state.UserReducer.authLevel,
    canEdit: state.UserReducer.canEdit,
    userType: state.UserReducer.userType,
    userId: state.UserReducer.userId
  }),
  dispatch => ({
    dispatch,
    changeByUserStatusForThumbs: params =>
      dispatch(changeByUserStatusForThumbs(params)),
    editVideoThumbs: params => dispatch(editVideoThumbs(params)),
    likeVideo: params => dispatch(likeVideo(params)),
    setDifficulty: params => dispatch(setDifficulty(params))
  })
)(VideoPage);
<commit_msg>minor fix<commit_after>import React, { useEffect, useRef, useState } from 'react';
import { useContentObj } from 'helpers/hooks';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import {
  changeByUserStatusForThumbs,
  editVideoThumbs,
  likeVideo,
  setDifficulty
} from 'redux/actions/VideoActions';
import Carousel from 'components/Carousel';
import Button from 'components/Button';
import Loading from 'components/Loading';
import VideoPlayer from 'components/VideoPlayer';
import NotFound from 'components/NotFound';
import CheckListGroup from 'components/CheckListGroup';
import PageTab from './PageTab';
import Comments from 'components/Comments';
import Details from './Details';
import NavMenu from './NavMenu';
import ResultModal from './Modals/ResultModal';
import QuestionsBuilder from './QuestionsBuilder';
import ConfirmModal from 'components/Modals/ConfirmModal';
import { fetchedVideoCodeFromURL, stringIsEmpty } from 'helpers/stringHelpers';
import queryString from 'query-string';
import ErrorBoundary from 'components/Wrappers/ErrorBoundary';
import Subjects from 'components/Subjects';
import RewardStatus from 'components/RewardStatus';
import request from 'axios';
import { Color, mobileMaxWidth } from 'constants/css';
import { css } from 'emotion';
import {
  auth,
  fetchPlaylistsContaining,
  handleError,
  loadComments,
  loadSubjects
} from 'helpers/requestHelpers';
import URL from 'constants/URL';

VideoPage.propTypes = {
  authLevel: PropTypes.number,
  canEdit: PropTypes.bool,
  changeByUserStatusForThumbs: PropTypes.func.isRequired,
  dispatch: PropTypes.func.isRequired,
  editVideoThumbs: PropTypes.func.isRequired,
  history: PropTypes.object.isRequired,
  likeVideo: PropTypes.func.isRequired,
  location: PropTypes.object.isRequired,
  match: PropTypes.object.isRequired,
  setDifficulty: PropTypes.func.isRequired,
  userId: PropTypes.number
};

function VideoPage({
  authLevel,
  canEdit,
  changeByUserStatusForThumbs,
  dispatch,
  editVideoThumbs,
  history,
  likeVideo,
  location: { search },
  match: {
    params: { videoId: initialVideoId }
  },
  setDifficulty,
  userId
}) {
  const [changingPage, setChangingPage] = useState(false);
  const [watchTabActive, setWatchTabActive] = useState(true);
  const [currentSlide, setCurrentSlide] = useState(0);
  const [userAnswers, setUserAnswers] = useState([]);
  const [resultModalShown, setResultModalShown] = useState(false);
  const [confirmModalShown, setConfirmModalShown] = useState(false);
  const [onEdit, setOnEdit] = useState(false);
  const [questionsBuilderShown, setQuestionsBuilderShown] = useState(false);
  const [videoId, setVideoId] = useState(initialVideoId);
  const [videoLoading, setVideoLoading] = useState(true);
  const [videoUnavailable, setVideoUnavailable] = useState(false);
  const mounted = useRef(true);
  const CommentInputAreaRef = useRef(null);
  const videoIdRef = useRef(initialVideoId);
  const {
    contentObj,
    contentObj: {
      byUser,
      childComments: comments,
      commentsLoadMoreButton,
      content,
      description,
      difficulty,
      hasHqThumb,
      likes,
      questions,
      stars,
      subjects,
      subjectsLoadMoreButton,
      tags,
      timeStamp,
      title,
      uploader,
      views
    },
    setContentObj,
    onAddTags,
    onAttachStar,
    onDeleteComment,
    onDeleteSubject,
    onEditComment,
    onEditRewardComment,
    onEditSubject,
    onInitContent,
    onLikeComment,
    onLikeContent,
    onLoadSubjectComments,
    onLoadMoreComments,
    onLoadMoreReplies,
    onLoadMoreSubjects,
    onLoadMoreSubjectComments,
    onLoadMoreSubjectReplies,
    onSetDifficulty,
    onSetSubjectDifficulty,
    onUploadComment,
    onUploadReply,
    onUploadSubject
  } = useContentObj({
    type: 'video',
    contentId: Number(videoId)
  });

  useEffect(() => {
    mounted.current = true;
    videoIdRef.current = initialVideoId;
    setChangingPage(true);
    loadVideoPage();
    setVideoId(initialVideoId);
    setChangingPage(false);
    async function loadVideoPage() {
      try {
        const { data } = await request.get(
          `${URL}/video/page?videoId=${videoIdRef.current}`
        );
        const subjectsObj = await loadSubjects({
          type: 'video',
          contentId: videoIdRef.current
        });
        const commentsObj = await loadComments({
          id: videoIdRef.current,
          type: 'video'
        });
        const tags = await fetchPlaylistsContaining({
          videoId: videoIdRef.current
        });
        if (mounted.current) {
          setVideoLoading(false);
          onInitContent({
            content: {
              ...data,
              tags,
              contentId: Number(videoIdRef.current),
              childComments: commentsObj?.comments || [],
              commentsLoadMoreButton: commentsObj?.loadMoreButton || false,
              subjects: subjectsObj?.results || [],
              subjectsLoadMoreButton: subjectsObj?.loadMoreButton || false
            }
          });
        }
      } catch (error) {
        if (error.response) {
          const { data = {} } = error.response;
          if (data.notFound) {
            setVideoUnavailable(true);
          }
        }
        console.error(error.response || error);
      }
    }
    return function cleanUp() {
      mounted.current = false;
    };
  }, [initialVideoId]);

  const { playlist: playlistId } = queryString.parse(search);
  const userIsUploader = uploader?.id === userId;
  const userCanEditThis = canEdit && authLevel >= uploader?.authLevel;
  return (
    <ErrorBoundary
      className={css`
        display: flex;
        justify-content: space-between;
        width: 100%;
        height: 100%;
        margin-top: 1rem;
        @media (max-width: ${mobileMaxWidth}) {
          margin-top: 0;
          flex-direction: column;
        }
      `}
    >

        {(videoLoading || videoUnavailable) && (

            {videoLoading && <Loading text="Loading Video..." />}
            {videoUnavailable && <NotFound text="Video does not exist" />}

        )}
        {!videoLoading && !videoUnavailable && content && (

              <PageTab
                questions={questions}
                watchTabActive={watchTabActive}
                onWatchTabClick={() => setWatchTabActive(true)}
                onQuestionTabClick={() => setWatchTabActive(false)}
              />

                {!questionsBuilderShown && (
                  <VideoPlayer
                    difficulty={difficulty}
                    byUser={!!byUser}
                    key={videoId}
                    hasHqThumb={hasHqThumb}
                    onEdit={onEdit}
                    videoId={videoId}
                    videoCode={content}
                    title={title}
                    uploader={uploader}
                    minimized={!watchTabActive}
                  />
                )}
                {!watchTabActive && questions.length > 0 && (
                  <Carousel
                    allowDrag={false}
                    style={{ marginTop: !!difficulty && '1rem' }}
                    progressBar
                    showQuestionsBuilder={() => setQuestionsBuilderShown(true)}
                    userIsUploader={userIsUploader}
                    userCanEditThis={userCanEditThis}
                    slidesToShow={1}
                    slidesToScroll={1}
                    slideIndex={currentSlide}
                    afterSlide={setCurrentSlide}
                    onFinish={() => setResultModalShown(true)}
                  >
                    {handleRenderSlides()}
                  </Carousel>
                )}
                {!watchTabActive && questions.length === 0 && (

There are no questions yet.

                    {(userIsUploader || userCanEditThis) && (
                      <Button
                        style={{ marginTop: '2rem', fontSize: '2rem' }}
                        success
                        filled
                        onClick={() => setQuestionsBuilderShown(true)}
                      >
                        Add Questions
                      </Button>
                    )}

                )}

               setOnEdit(true)}
                onEditCancel={() => setOnEdit(false)}
                onEditFinish={handleEditVideoPage}
                onDelete={() => setConfirmModalShown(true)}
                setDifficulty={handleSetDifficulty}
                stars={stars}
                tags={tags}
                title={title}
                timeStamp={timeStamp}
                uploader={uploader}
                userId={userId}
                videoId={videoId}
                videoViews={views}
              />
              <RewardStatus
                contentType="video"
                difficulty={byUser ? 5 : 0}
                onCommentEdit={onEditRewardComment}
                style={{
                  fontSize: '1.4rem'
                }}
                stars={stars}
                uploaderName={uploader.username}
              />

            <Subjects
              loadMoreButton={subjectsLoadMoreButton}
              subjects={subjects}
              onLoadMoreSubjects={onLoadMoreSubjects}
              onLoadSubjectComments={onLoadSubjectComments}
              onSubjectEditDone={onEditSubject}
              onSubjectDelete={onDeleteSubject}
              setSubjectDifficulty={onSetSubjectDifficulty}
              uploadSubject={onUploadSubject}
              contentId={videoId}
              type="video"
              rootDifficulty={difficulty}
              commentActions={{
                attachStar: onAttachStar,
                editRewardComment: onEditRewardComment,
                onDelete: onDeleteComment,
                onEditDone: onEditComment,
                onLikeClick: onLikeComment,
                onLoadMoreComments: onLoadMoreSubjectComments,
                onLoadMoreReplies: onLoadMoreSubjectReplies,
                onUploadComment,
                onUploadReply
              }}
            />

                Comment on this video

              <Comments
                autoExpand
                comments={comments}
                inputAreaInnerRef={CommentInputAreaRef}
                inputTypeLabel={'comment'}
                loadMoreButton={commentsLoadMoreButton}
                onAttachStar={onAttachStar}
                onCommentSubmit={onUploadComment}
                onDelete={onDeleteComment}
                onEditDone={onEditComment}
                onLikeClick={onLikeComment}
                onLoadMoreComments={onLoadMoreComments}
                onLoadMoreReplies={onLoadMoreReplies}
                onReplySubmit={onUploadReply}
                onRewardCommentEdit={onEditRewardComment}
                parent={{
                  type: 'video',
                  difficulty,
                  id: Number(videoId),
                  uploader
                }}
                style={{ paddingTop: '1rem' }}
                userId={userId}
              />

            {resultModalShown && (
              <ResultModal
                onHide={() => setResultModalShown(false)}
                numberCorrect={numberCorrect}
                totalQuestions={questions.length}
              />
            )}
            {confirmModalShown && (
              <ConfirmModal
                title="Remove Video"
                onHide={() => setConfirmModalShown(false)}
                onConfirm={handleDeleteVideo}
              />
            )}
            {questionsBuilderShown && (
              <QuestionsBuilder
                questions={questions}
                title={title}
                videoCode={content}
                onSubmit={handleUploadQuestions}
                onHide={() => setQuestionsBuilderShown(false)}
              />
            )}

        )}

      <NavMenu videoId={videoId} playlistId={playlistId} />
    </ErrorBoundary>
  );

  async function handleDeleteVideo() {
    await request.delete(`${URL}/video?videoId=${videoId}`, auth());
    history.push('/videos');
  }

  async function handleEditVideoPage(params) {
    setOnEdit(false);
    try {
      await request.post(`${URL}/video/edit/page`, params, auth());
      const url = fetchedVideoCodeFromURL(params.url);
      setContentObj({
        ...contentObj,
        title: params.title,
        description: params.description,
        content: url
      });
      editVideoThumbs({
        videoId: Number(params.videoId),
        title: params.title,
        url
      });
    } catch (error) {
      handleError(error, dispatch);
    }
  }

  function handleChangeByUserStatus({ contentId, byUser }) {
    setContentObj({
      ...contentObj,
      byUser: byUser
    });
    changeByUserStatusForThumbs({ videoId: Number(contentId), byUser });
  }

  function handleLikeVideo(likes, videoId) {
    onLikeContent({ likes, type: 'video', contentId: videoId });
    likeVideo({ likes, videoId });
  }

  function numberCorrect() {
    const correctAnswers = questions.map(question => {
      return question.correctChoice;
    });
    let numberCorrect = 0;
    for (let i = 0; i < userAnswers.length; i++) {
      if (userAnswers[i] + 1 === correctAnswers[i]) numberCorrect++;
    }
    return numberCorrect;
  }

  function handleSelectChoice(newAnswer) {
    if (typeof userAnswers[currentSlide] === 'number') {
      return setUserAnswers(
        userAnswers.map((answer, index) => {
          return index === currentSlide ? newAnswer : answer;
        })
      );
    }
    const newAnswers = [...userAnswers];
    newAnswers[currentSlide] = newAnswer;
    setUserAnswers(newAnswers);
  }

  function handleRenderSlides() {
    return questions.map((question, index) => {
      const filteredChoices = question.choices.filter(choice => !!choice);
      let isCurrentSlide = index === currentSlide;
      const listItems = filteredChoices.map((choice, index) => {
        let isSelectedChoice = index === userAnswers[currentSlide];
        return {
          label: choice,
          checked: isCurrentSlide && isSelectedChoice
        };
      });
      return (

          <CheckListGroup
            inputType="radio"
            listItems={listItems}
            onSelect={handleSelectChoice}
            style={{ marginTop: '1.5rem', paddingRight: '1rem' }}
          />

      );
    });
  }

  function handleSetDifficulty({ contentId, difficulty }) {
    onSetDifficulty({ contentId, difficulty });
    setDifficulty({ videoId: Number(contentId), difficulty });
  }

  async function handleUploadQuestions(questions) {
    const data = {
      videoId,
      questions: questions.map(question => {
        const choices = question.choices.filter(choice => {
          return choice.label && !stringIsEmpty(choice.label);
        });
        return {
          videoId,
          title: question.title,
          correctChoice: (() => {
            let correctChoice = 0;
            for (let i = 0; i < choices.length; i++) {
              if (choices[i].checked) correctChoice = i + 1;
            }
            return correctChoice;
          })(),
          choice1: choices[0].label,
          choice2: choices[1].label,
          choice3: choices[2]?.label,
          choice4: choices[3]?.label,
          choice5: choices[4]?.label,
          creator: userId
        };
      })
    };
    try {
      await request.post(`${URL}/video/questions`, data, auth());
      const questions = data.questions.map(question => {
        return {
          title: question.title,
          choices: [
            question.choice1,
            question.choice2,
            question.choice3,
            question.choice4,
            question.choice5
          ],
          correctChoice: question.correctChoice
        };
      });
      setContentObj({
        ...contentObj,
        questions
      });
      setQuestionsBuilderShown(false);
      setCurrentSlide(0);
      setUserAnswers([]);
    } catch (error) {
      handleError(error, dispatch);
    }
  }
}

export default connect(
  state => ({
    authLevel: state.UserReducer.authLevel,
    canEdit: state.UserReducer.canEdit,
    userType: state.UserReducer.userType,
    userId: state.UserReducer.userId
  }),
  dispatch => ({
    dispatch,
    changeByUserStatusForThumbs: params =>
      dispatch(changeByUserStatusForThumbs(params)),
    editVideoThumbs: params => dispatch(editVideoThumbs(params)),
    likeVideo: params => dispatch(likeVideo(params)),
    setDifficulty: params => dispatch(setDifficulty(params))
  })
)(VideoPage);
<|endoftext|>


<commit_before>import mobx from 'mobx';
import Endpoint from '../models/http/Endpoint';
import Response from '../models/http/Response';
import Header from '../models/http/Header';
import ContentType from '../models/http/ContentType';
import SocketEndpoint from '../models/socket/SocketEndpoint';
import GraphqlEndpoint from '../models/graphql/GraphqlEndpoint';
import JsonServerEndpoint from '../models/jsonServer/JsonServerEndpoint';
import ProxyEndpoint from '../models/proxy/ProxyEndpoint';

/**
 * Parses the spec to endpoints
 */
export function parseSpec(spec) {
  var endpoints = [];
  if (!spec.endpoints && !spec.socketEndpoints && !spec.graphqlEndpoints && !spec.jsonServerEndpoint) {
    // if the spec doesn't have endpoint, initialize with a default http endpoint
    endpoints.push(new Endpoint('/', 'GET', [new Header('Access-Control-Allow-Origin', '*')], new Response(contentTypes[0], '{}')));
  } else {
    for (let endpoint of spec.endpoints) {
      let response = new Response(new ContentType(endpoint.response.contentType.type, endpoint.response.contentType.contentType), endpoint.response.content, endpoint.response.responseCode);
      endpoints.push(new Endpoint(endpoint.url, endpoint.method, getHeadersFromJson(endpoint), response));
    }

    for (let endpoint of spec.socketEndpoints) {
      endpoints.push(new SocketEndpoint(endpoint.eventName, endpoint.eventToEmit, endpoint.payload, endpoint.emitType));
    }

    for (let endpoint of spec.graphqlEndpoints) {
      endpoints.push(new GraphqlEndpoint(endpoint.url, endpoint.schema));
    }

    for (let endpoint of spec.proxyEndpoints) {
      endpoints.push(new ProxyEndpoint(endpoint.url, endpoint.urlToProxy));
    }

    if (spec.jsonServerEndpoint) {
      endpoints.push(new JsonServerEndpoint(spec.jsonServerEndpoint.url, spec.jsonServerEndpoint.model));
    }
  }
  return endpoints;
}

/**
 * Returns a plain object tranforming the endpoints
 */
export function getPayload(endpoints) {
  let httpEndpoints = [];
  let socketEndpoints = [];
  let graphqlEndpoints = [];
  let proxyEndpoints = [];
  let jsonServerEndpoint = null;

  for (let endpoint of endpoints) {
    if (endpoint.type === 'http') {
      httpEndpoints.push(endpoint);
    } else if (endpoint.type === 'socket') {
      socketEndpoints.push(endpoint);
    } else if (endpoint.type === 'gql') {
      graphqlEndpoints.push(endpoint);
    } else if (endpoint.type === 'jsonServer') {
      jsonServerEndpoint = endpoint;
    } else if (endpoint.type === 'proxy') {
      proxyEndpoints.push(endpoint);
    }
  }

  let payload = {
    endpoints: mobx.toJSON(httpEndpoints),
    socketEndpoints: mobx.toJSON(socketEndpoints),
    graphqlEndpoints: mobx.toJSON(graphqlEndpoints),
    jsonServerEndpoint: jsonServerEndpoint,
    proxyEndpoints: mobx.toJSON(proxyEndpoints)
  };

  return payload;
}

/**
 * Retrurns true if a json-server endpoint already exists; Else false.
 */
export function isAnyJsonServerEndpointAvailable(endpoints) {
  for (let endpoint of endpoints) {
    if (endpoint.type === 'jsonServer') {
      return true;
    }
  }
  return false;
}

/**
 * Returns an array of headers for a http request
 */
function getHeadersFromJson(endpoint) {
  let headers = [];
  for (let header of endpoint.headers) {
    headers.push(new Header(header.key, header.value));
  }
  return headers;
}<commit_msg>Fix content types error<commit_after>import mobx from 'mobx';
import Endpoint from '../models/http/Endpoint';
import Response from '../models/http/Response';
import Header from '../models/http/Header';
import ContentType from '../models/http/ContentType';
import contentTypes from '../models/http/ContentTypes';
import SocketEndpoint from '../models/socket/SocketEndpoint';
import GraphqlEndpoint from '../models/graphql/GraphqlEndpoint';
import JsonServerEndpoint from '../models/jsonServer/JsonServerEndpoint';
import ProxyEndpoint from '../models/proxy/ProxyEndpoint';

/**
 * Parses the spec to endpoints
 */
export function parseSpec(spec) {
  var endpoints = [];
  if (!spec.endpoints && !spec.socketEndpoints && !spec.graphqlEndpoints && !spec.jsonServerEndpoint) {
    // if the spec doesn't have endpoint, initialize with a default http endpoint
    endpoints.push(new Endpoint('/', 'GET', [new Header('Access-Control-Allow-Origin', '*')], new Response(contentTypes[0], '{}')));
  } else {
    for (let endpoint of spec.endpoints) {
      let response = new Response(new ContentType(endpoint.response.contentType.type, endpoint.response.contentType.contentType), endpoint.response.content, endpoint.response.responseCode);
      endpoints.push(new Endpoint(endpoint.url, endpoint.method, getHeadersFromJson(endpoint), response));
    }

    for (let endpoint of spec.socketEndpoints) {
      endpoints.push(new SocketEndpoint(endpoint.eventName, endpoint.eventToEmit, endpoint.payload, endpoint.emitType));
    }

    for (let endpoint of spec.graphqlEndpoints) {
      endpoints.push(new GraphqlEndpoint(endpoint.url, endpoint.schema));
    }

    for (let endpoint of spec.proxyEndpoints) {
      endpoints.push(new ProxyEndpoint(endpoint.url, endpoint.urlToProxy));
    }

    if (spec.jsonServerEndpoint) {
      endpoints.push(new JsonServerEndpoint(spec.jsonServerEndpoint.url, spec.jsonServerEndpoint.model));
    }
  }
  return endpoints;
}

/**
 * Returns a plain object tranforming the endpoints
 */
export function getPayload(endpoints) {
  let httpEndpoints = [];
  let socketEndpoints = [];
  let graphqlEndpoints = [];
  let proxyEndpoints = [];
  let jsonServerEndpoint = null;

  for (let endpoint of endpoints) {
    if (endpoint.type === 'http') {
      httpEndpoints.push(endpoint);
    } else if (endpoint.type === 'socket') {
      socketEndpoints.push(endpoint);
    } else if (endpoint.type === 'gql') {
      graphqlEndpoints.push(endpoint);
    } else if (endpoint.type === 'jsonServer') {
      jsonServerEndpoint = endpoint;
    } else if (endpoint.type === 'proxy') {
      proxyEndpoints.push(endpoint);
    }
  }

  let payload = {
    endpoints: mobx.toJSON(httpEndpoints),
    socketEndpoints: mobx.toJSON(socketEndpoints),
    graphqlEndpoints: mobx.toJSON(graphqlEndpoints),
    jsonServerEndpoint: jsonServerEndpoint,
    proxyEndpoints: mobx.toJSON(proxyEndpoints)
  };

  return payload;
}

/**
 * Retrurns true if a json-server endpoint already exists; Else false.
 */
export function isAnyJsonServerEndpointAvailable(endpoints) {
  for (let endpoint of endpoints) {
    if (endpoint.type === 'jsonServer') {
      return true;
    }
  }
  return false;
}

/**
 * Returns an array of headers for a http request
 */
function getHeadersFromJson(endpoint) {
  let headers = [];
  for (let header of endpoint.headers) {
    headers.push(new Header(header.key, header.value));
  }
  return headers;
}<|endoftext|>


<commit_before>import React, { useEffect, useRef, useState } from 'react';
import { useContentObj } from 'helpers/hooks';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import {
  changeByUserStatusForThumbs,
  editVideoThumbs,
  likeVideo,
  setDifficulty
} from 'redux/actions/VideoActions';
import Carousel from 'components/Carousel';
import Button from 'components/Button';
import Loading from 'components/Loading';
import VideoPlayer from 'components/VideoPlayer';
import NotFound from 'components/NotFound';
import CheckListGroup from 'components/CheckListGroup';
import PageTab from './PageTab';
import Comments from 'components/Comments';
import Details from './Details';
import NavMenu from './NavMenu';
import ResultModal from './Modals/ResultModal';
import QuestionsBuilder from './QuestionsBuilder';
import ConfirmModal from 'components/Modals/ConfirmModal';
import { fetchedVideoCodeFromURL, stringIsEmpty } from 'helpers/stringHelpers';
import queryString from 'query-string';
import ErrorBoundary from 'components/Wrappers/ErrorBoundary';
import Subjects from 'components/Subjects';
import RewardStatus from 'components/RewardStatus';
import request from 'axios';
import { Color, mobileMaxWidth } from 'constants/css';
import { css } from 'emotion';
import {
  auth,
  handleError,
  loadComments,
  loadSubjects
} from 'helpers/requestHelpers';
import URL from 'constants/URL';

VideoPage.propTypes = {
  authLevel: PropTypes.number,
  canEdit: PropTypes.bool,
  changeByUserStatusForThumbs: PropTypes.func.isRequired,
  dispatch: PropTypes.func.isRequired,
  editVideoThumbs: PropTypes.func.isRequired,
  history: PropTypes.object.isRequired,
  likeVideo: PropTypes.func.isRequired,
  location: PropTypes.object.isRequired,
  match: PropTypes.object.isRequired,
  setDifficulty: PropTypes.func.isRequired,
  userId: PropTypes.number
};

function VideoPage({
  authLevel,
  canEdit,
  changeByUserStatusForThumbs,
  dispatch,
  editVideoThumbs,
  history,
  likeVideo,
  location: { search },
  match: {
    params: { videoId }
  },
  setDifficulty,
  userId
}) {
  const [changingPage, setChangingPage] = useState(false);
  const [watchTabActive, setWatchTabActive] = useState(true);
  const [currentSlide, setCurrentSlide] = useState(0);
  const [userAnswers, setUserAnswers] = useState([]);
  const [resultModalShown, setResultModalShown] = useState(false);
  const [confirmModalShown, setConfirmModalShown] = useState(false);
  const [onEdit, setOnEdit] = useState(false);
  const [questionsBuilderShown, setQuestionsBuilderShown] = useState(false);
  const [videoLoading, setVideoLoading] = useState(true);
  const [videoUnavailable, setVideoUnavailable] = useState(false);
  const mounted = useRef(true);
  const CommentInputAreaRef = useRef(null);
  const {
    contentObj,
    contentObj: {
      byUser,
      childComments: comments,
      commentsLoadMoreButton,
      content,
      description,
      difficulty,
      hasHqThumb,
      likes,
      questions,
      stars,
      subjects,
      subjectsLoadMoreButton,
      tags,
      timeStamp,
      title,
      uploader,
      views
    },
    setContentObj,
    onAddTags,
    onAttachStar,
    onDeleteComment,
    onDeleteSubject,
    onEditComment,
    onEditRewardComment,
    onEditSubject,
    onInitContent,
    onLikeComment,
    onLikeContent,
    onLoadSubjectComments,
    onLoadMoreComments,
    onLoadMoreReplies,
    onLoadMoreSubjects,
    onLoadMoreSubjectComments,
    onLoadMoreSubjectReplies,
    onLoadTags,
    onSetDifficulty,
    onSetSubjectDifficulty,
    onUploadComment,
    onUploadReply,
    onUploadSubject
  } = useContentObj({
    type: 'video',
    contentId: Number(videoId)
  });

  useEffect(() => {
    mounted.current = true;
    setChangingPage(true);
    loadVideoPage();
    setChangingPage(false);
    async function loadVideoPage() {
      try {
        const { data } = await request.get(
          `${URL}/video/page?videoId=${videoId}`
        );
        const subjectsObj = await loadSubjects({
          type: 'video',
          contentId: videoId
        });
        const commentsObj = await loadComments({
          id: videoId,
          type: 'video'
        });
        if (mounted.current) {
          setVideoLoading(false);
          onInitContent({
            content: {
              ...data,
              childComments: commentsObj?.comments || [],
              commentsLoadMoreButton: commentsObj?.loadMoreButton || false,
              subjects: subjectsObj?.results || [],
              subjectsLoadMoreButton: subjectsObj?.loadMoreButton || false
            }
          });
        }
      } catch (error) {
        if (error.response) {
          const { data = {} } = error.response;
          if (data.notFound) {
            setVideoUnavailable(true);
          }
        }
        console.error(error.response || error);
      }
    }

    return function cleanUp() {
      mounted.current = false;
    };
  }, [videoId]);

  const { playlist: playlistId } = queryString.parse(search);
  const userIsUploader = uploader?.id === userId;
  const userCanEditThis = canEdit && authLevel >= uploader?.authLevel;
  return (
    <ErrorBoundary
      className={css`
        display: flex;
        justify-content: space-between;
        width: 100%;
        height: 100%;
        margin-top: 1rem;
        @media (max-width: ${mobileMaxWidth}) {
          margin-top: 0;
          flex-direction: column;
        }
      `}
    >

        {(videoLoading || videoUnavailable) && (

            {videoLoading && <Loading text="Loading Video..." />}
            {videoUnavailable && <NotFound text="Video does not exist" />}

        )}
        {!videoLoading && !videoUnavailable && content && (

              <PageTab
                questions={questions}
                watchTabActive={watchTabActive}
                onWatchTabClick={() => setWatchTabActive(true)}
                onQuestionTabClick={() => setWatchTabActive(false)}
              />

                {!questionsBuilderShown && (
                  <VideoPlayer
                    difficulty={difficulty}
                    byUser={!!byUser}
                    key={videoId}
                    hasHqThumb={hasHqThumb}
                    onEdit={onEdit}
                    videoId={videoId}
                    videoCode={content}
                    title={title}
                    uploader={uploader}
                    minimized={!watchTabActive}
                  />
                )}
                {!watchTabActive && questions.length > 0 && (
                  <Carousel
                    allowDrag={false}
                    style={{ marginTop: !!difficulty && '1rem' }}
                    progressBar
                    showQuestionsBuilder={() => setQuestionsBuilderShown(true)}
                    userIsUploader={userIsUploader}
                    userCanEditThis={userCanEditThis}
                    slidesToShow={1}
                    slidesToScroll={1}
                    slideIndex={currentSlide}
                    afterSlide={setCurrentSlide}
                    onFinish={() => setResultModalShown(true)}
                  >
                    {handleRenderSlides()}
                  </Carousel>
                )}
                {!watchTabActive && questions.length === 0 && (

There are no questions yet.

                    {(userIsUploader || userCanEditThis) && (
                      <Button
                        style={{ marginTop: '2rem', fontSize: '2rem' }}
                        success
                        filled
                        onClick={() => setQuestionsBuilderShown(true)}
                      >
                        Add Questions
                      </Button>
                    )}

                )}

               setOnEdit(true)}
                onEditCancel={() => setOnEdit(false)}
                onEditFinish={handleEditVideoPage}
                onDelete={() => setConfirmModalShown(true)}
                setDifficulty={handleSetDifficulty}
                stars={stars}
                tags={tags}
                title={title}
                timeStamp={timeStamp}
                uploader={uploader}
                userId={userId}
                videoId={videoId}
                videoViews={views}
              />
              <RewardStatus
                contentType="video"
                difficulty={byUser ? 5 : 0}
                onCommentEdit={onEditRewardComment}
                style={{
                  fontSize: '1.4rem'
                }}
                stars={stars}
                uploaderName={uploader.username}
              />

            <Subjects
              loadMoreButton={subjectsLoadMoreButton}
              subjects={subjects}
              onLoadMoreSubjects={onLoadMoreSubjects}
              onLoadSubjectComments={onLoadSubjectComments}
              onSubjectEditDone={onEditSubject}
              onSubjectDelete={onDeleteSubject}
              setSubjectDifficulty={onSetSubjectDifficulty}
              uploadSubject={onUploadSubject}
              contentId={videoId}
              type="video"
              rootDifficulty={difficulty}
              commentActions={{
                attachStar: onAttachStar,
                editRewardComment: onEditRewardComment,
                onDelete: onDeleteComment,
                onEditDone: onEditComment,
                onLikeClick: onLikeComment,
                onLoadMoreComments: onLoadMoreSubjectComments,
                onLoadMoreReplies: onLoadMoreSubjectReplies,
                onUploadComment,
                onUploadReply
              }}
            />

                Comment on this video

              <Comments
                autoExpand
                comments={comments}
                inputAreaInnerRef={CommentInputAreaRef}
                inputTypeLabel={'comment'}
                loadMoreButton={commentsLoadMoreButton}
                onAttachStar={onAttachStar}
                onCommentSubmit={onUploadComment}
                onDelete={onDeleteComment}
                onEditDone={onEditComment}
                onLikeClick={onLikeComment}
                onLoadMoreComments={onLoadMoreComments}
                onLoadMoreReplies={onLoadMoreReplies}
                onReplySubmit={onUploadReply}
                onRewardCommentEdit={onEditRewardComment}
                parent={{
                  type: 'video',
                  difficulty,
                  id: Number(videoId),
                  uploader
                }}
                style={{ paddingTop: '1rem' }}
                userId={userId}
              />

            {resultModalShown && (
              <ResultModal
                onHide={() => setResultModalShown(false)}
                numberCorrect={numberCorrect}
                totalQuestions={questions.length}
              />
            )}
            {confirmModalShown && (
              <ConfirmModal
                title="Remove Video"
                onHide={() => setConfirmModalShown(false)}
                onConfirm={handleDeleteVideo}
              />
            )}
            {questionsBuilderShown && (
              <QuestionsBuilder
                questions={questions}
                title={title}
                videoCode={content}
                onSubmit={handleUploadQuestions}
                onHide={() => setQuestionsBuilderShown(false)}
              />
            )}

        )}

      <NavMenu videoId={videoId} playlistId={playlistId} />
    </ErrorBoundary>
  );

  async function handleDeleteVideo() {
    await request.delete(`${URL}/video?videoId=${videoId}`, auth());
    history.push('/videos');
  }

  async function handleEditVideoPage(params) {
    setOnEdit(false);
    try {
      await request.post(`${URL}/video/edit/page`, params, auth());
      const url = fetchedVideoCodeFromURL(params.url);
      setContentObj({
        ...contentObj,
        title: params.title,
        description: params.description,
        content: url
      });
      editVideoThumbs({
        videoId: Number(params.videoId),
        title: params.title,
        url
      });
    } catch (error) {
      handleError(error, dispatch);
    }
  }

  function handleChangeByUserStatus({ contentId, byUser }) {
    setContentObj({
      ...contentObj,
      byUser: byUser
    });
    changeByUserStatusForThumbs({ videoId: Number(contentId), byUser });
  }

  function handleLikeVideo(likes, videoId) {
    onLikeContent({ likes, type: 'video', contentId: videoId });
    likeVideo({ likes, videoId });
  }

  function numberCorrect() {
    const correctAnswers = questions.map(question => {
      return question.correctChoice;
    });
    let numberCorrect = 0;
    for (let i = 0; i < userAnswers.length; i++) {
      if (userAnswers[i] + 1 === correctAnswers[i]) numberCorrect++;
    }
    return numberCorrect;
  }

  function handleSelectChoice(newAnswer) {
    if (typeof userAnswers[currentSlide] === 'number') {
      return setUserAnswers(
        userAnswers.map((answer, index) => {
          return index === currentSlide ? newAnswer : answer;
        })
      );
    }
    const newAnswers = [...userAnswers];
    newAnswers[currentSlide] = newAnswer;
    setUserAnswers(newAnswers);
  }

  function handleRenderSlides() {
    return questions.map((question, index) => {
      const filteredChoices = question.choices.filter(choice => !!choice);
      let isCurrentSlide = index === currentSlide;
      const listItems = filteredChoices.map((choice, index) => {
        let isSelectedChoice = index === userAnswers[currentSlide];
        return {
          label: choice,
          checked: isCurrentSlide && isSelectedChoice
        };
      });
      return (

          <CheckListGroup
            inputType="radio"
            listItems={listItems}
            onSelect={handleSelectChoice}
            style={{ marginTop: '1.5rem', paddingRight: '1rem' }}
          />

      );
    });
  }

  function handleSetDifficulty({ contentId, difficulty }) {
    onSetDifficulty({ contentId, difficulty });
    setDifficulty({ videoId: Number(contentId), difficulty });
  }

  async function handleUploadQuestions(questions) {
    const data = {
      videoId,
      questions: questions.map(question => {
        const choices = question.choices.filter(choice => {
          return choice.label && !stringIsEmpty(choice.label);
        });
        return {
          videoId,
          title: question.title,
          correctChoice: (() => {
            let correctChoice = 0;
            for (let i = 0; i < choices.length; i++) {
              if (choices[i].checked) correctChoice = i + 1;
            }
            return correctChoice;
          })(),
          choice1: choices[0].label,
          choice2: choices[1].label,
          choice3: choices[2]?.label,
          choice4: choices[3]?.label,
          choice5: choices[4]?.label,
          creator: userId
        };
      })
    };
    try {
      await request.post(`${URL}/video/questions`, data, auth());
      const questions = data.questions.map(question => {
        return {
          title: question.title,
          choices: [
            question.choice1,
            question.choice2,
            question.choice3,
            question.choice4,
            question.choice5
          ],
          correctChoice: question.correctChoice
        };
      });
      setContentObj({
        ...contentObj,
        questions
      });
      setQuestionsBuilderShown(false);
      setCurrentSlide(0);
      setUserAnswers([]);
    } catch (error) {
      handleError(error, dispatch);
    }
  }
}

export default connect(
  state => ({
    authLevel: state.UserReducer.authLevel,
    canEdit: state.UserReducer.canEdit,
    userType: state.UserReducer.userType,
    userId: state.UserReducer.userId
  }),
  dispatch => ({
    dispatch,
    changeByUserStatusForThumbs: params =>
      dispatch(changeByUserStatusForThumbs(params)),
    editVideoThumbs: params => dispatch(editVideoThumbs(params)),
    likeVideo: params => dispatch(likeVideo(params)),
    setDifficulty: params => dispatch(setDifficulty(params))
  })
)(VideoPage);
<commit_msg>minor update<commit_after>import React, { useEffect, useRef, useState } from 'react';
import { useContentObj } from 'helpers/hooks';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import {
  changeByUserStatusForThumbs,
  editVideoThumbs,
  likeVideo,
  setDifficulty
} from 'redux/actions/VideoActions';
import Carousel from 'components/Carousel';
import Button from 'components/Button';
import Loading from 'components/Loading';
import VideoPlayer from 'components/VideoPlayer';
import NotFound from 'components/NotFound';
import CheckListGroup from 'components/CheckListGroup';
import PageTab from './PageTab';
import Comments from 'components/Comments';
import Details from './Details';
import NavMenu from './NavMenu';
import ResultModal from './Modals/ResultModal';
import QuestionsBuilder from './QuestionsBuilder';
import ConfirmModal from 'components/Modals/ConfirmModal';
import { fetchedVideoCodeFromURL, stringIsEmpty } from 'helpers/stringHelpers';
import queryString from 'query-string';
import ErrorBoundary from 'components/Wrappers/ErrorBoundary';
import Subjects from 'components/Subjects';
import RewardStatus from 'components/RewardStatus';
import request from 'axios';
import { Color, mobileMaxWidth } from 'constants/css';
import { css } from 'emotion';
import {
  auth,
  handleError,
  loadComments,
  loadSubjects
} from 'helpers/requestHelpers';
import URL from 'constants/URL';

VideoPage.propTypes = {
  authLevel: PropTypes.number,
  canEdit: PropTypes.bool,
  changeByUserStatusForThumbs: PropTypes.func.isRequired,
  dispatch: PropTypes.func.isRequired,
  editVideoThumbs: PropTypes.func.isRequired,
  history: PropTypes.object.isRequired,
  likeVideo: PropTypes.func.isRequired,
  location: PropTypes.object.isRequired,
  match: PropTypes.object.isRequired,
  setDifficulty: PropTypes.func.isRequired,
  userId: PropTypes.number
};

function VideoPage({
  authLevel,
  canEdit,
  changeByUserStatusForThumbs,
  dispatch,
  editVideoThumbs,
  history,
  likeVideo,
  location: { search },
  match: {
    params: { videoId: initialVideoId }
  },
  setDifficulty,
  userId
}) {
  const [changingPage, setChangingPage] = useState(false);
  const [watchTabActive, setWatchTabActive] = useState(true);
  const [currentSlide, setCurrentSlide] = useState(0);
  const [userAnswers, setUserAnswers] = useState([]);
  const [resultModalShown, setResultModalShown] = useState(false);
  const [confirmModalShown, setConfirmModalShown] = useState(false);
  const [onEdit, setOnEdit] = useState(false);
  const [questionsBuilderShown, setQuestionsBuilderShown] = useState(false);
  const [videoId, setVideoId] = useState(initialVideoId);
  const [videoLoading, setVideoLoading] = useState(true);
  const [videoUnavailable, setVideoUnavailable] = useState(false);
  const mounted = useRef(true);
  const CommentInputAreaRef = useRef(null);
  const videoIdRef = useRef(initialVideoId);
  const {
    contentObj,
    contentObj: {
      byUser,
      childComments: comments,
      commentsLoadMoreButton,
      content,
      description,
      difficulty,
      hasHqThumb,
      likes,
      questions,
      stars,
      subjects,
      subjectsLoadMoreButton,
      tags,
      timeStamp,
      title,
      uploader,
      views
    },
    setContentObj,
    onAddTags,
    onAttachStar,
    onDeleteComment,
    onDeleteSubject,
    onEditComment,
    onEditRewardComment,
    onEditSubject,
    onInitContent,
    onLikeComment,
    onLikeContent,
    onLoadSubjectComments,
    onLoadMoreComments,
    onLoadMoreReplies,
    onLoadMoreSubjects,
    onLoadMoreSubjectComments,
    onLoadMoreSubjectReplies,
    onLoadTags,
    onSetDifficulty,
    onSetSubjectDifficulty,
    onUploadComment,
    onUploadReply,
    onUploadSubject
  } = useContentObj({
    type: 'video',
    contentId: Number(videoId)
  });

  useEffect(() => {
    mounted.current = true;
    setVideoId(initialVideoId);
    videoIdRef.current = initialVideoId;
    setChangingPage(true);
    loadVideoPage();
    setChangingPage(false);
    async function loadVideoPage() {
      try {
        const { data } = await request.get(
          `${URL}/video/page?videoId=${videoIdRef.current}`
        );
        const subjectsObj = await loadSubjects({
          type: 'video',
          contentId: videoIdRef.current
        });
        const commentsObj = await loadComments({
          id: videoIdRef.current,
          type: 'video'
        });
        if (mounted.current) {
          setVideoLoading(false);
          onInitContent({
            content: {
              ...data,
              contentId: Number(videoIdRef.current),
              childComments: commentsObj?.comments || [],
              commentsLoadMoreButton: commentsObj?.loadMoreButton || false,
              subjects: subjectsObj?.results || [],
              subjectsLoadMoreButton: subjectsObj?.loadMoreButton || false
            }
          });
        }
      } catch (error) {
        if (error.response) {
          const { data = {} } = error.response;
          if (data.notFound) {
            setVideoUnavailable(true);
          }
        }
        console.error(error.response || error);
      }
    }
    return function cleanUp() {
      mounted.current = false;
    };
  }, [initialVideoId]);

  const { playlist: playlistId } = queryString.parse(search);
  const userIsUploader = uploader?.id === userId;
  const userCanEditThis = canEdit && authLevel >= uploader?.authLevel;
  return (
    <ErrorBoundary
      className={css`
        display: flex;
        justify-content: space-between;
        width: 100%;
        height: 100%;
        margin-top: 1rem;
        @media (max-width: ${mobileMaxWidth}) {
          margin-top: 0;
          flex-direction: column;
        }
      `}
    >

        {(videoLoading || videoUnavailable) && (

            {videoLoading && <Loading text="Loading Video..." />}
            {videoUnavailable && <NotFound text="Video does not exist" />}

        )}
        {!videoLoading && !videoUnavailable && content && (

              <PageTab
                questions={questions}
                watchTabActive={watchTabActive}
                onWatchTabClick={() => setWatchTabActive(true)}
                onQuestionTabClick={() => setWatchTabActive(false)}
              />

                {!questionsBuilderShown && (
                  <VideoPlayer
                    difficulty={difficulty}
                    byUser={!!byUser}
                    key={videoId}
                    hasHqThumb={hasHqThumb}
                    onEdit={onEdit}
                    videoId={videoId}
                    videoCode={content}
                    title={title}
                    uploader={uploader}
                    minimized={!watchTabActive}
                  />
                )}
                {!watchTabActive && questions.length > 0 && (
                  <Carousel
                    allowDrag={false}
                    style={{ marginTop: !!difficulty && '1rem' }}
                    progressBar
                    showQuestionsBuilder={() => setQuestionsBuilderShown(true)}
                    userIsUploader={userIsUploader}
                    userCanEditThis={userCanEditThis}
                    slidesToShow={1}
                    slidesToScroll={1}
                    slideIndex={currentSlide}
                    afterSlide={setCurrentSlide}
                    onFinish={() => setResultModalShown(true)}
                  >
                    {handleRenderSlides()}
                  </Carousel>
                )}
                {!watchTabActive && questions.length === 0 && (

There are no questions yet.

                    {(userIsUploader || userCanEditThis) && (
                      <Button
                        style={{ marginTop: '2rem', fontSize: '2rem' }}
                        success
                        filled
                        onClick={() => setQuestionsBuilderShown(true)}
                      >
                        Add Questions
                      </Button>
                    )}

                )}

               setOnEdit(true)}
                onEditCancel={() => setOnEdit(false)}
                onEditFinish={handleEditVideoPage}
                onDelete={() => setConfirmModalShown(true)}
                setDifficulty={handleSetDifficulty}
                stars={stars}
                tags={tags}
                title={title}
                timeStamp={timeStamp}
                uploader={uploader}
                userId={userId}
                videoId={videoId}
                videoViews={views}
              />
              <RewardStatus
                contentType="video"
                difficulty={byUser ? 5 : 0}
                onCommentEdit={onEditRewardComment}
                style={{
                  fontSize: '1.4rem'
                }}
                stars={stars}
                uploaderName={uploader.username}
              />

            <Subjects
              loadMoreButton={subjectsLoadMoreButton}
              subjects={subjects}
              onLoadMoreSubjects={onLoadMoreSubjects}
              onLoadSubjectComments={onLoadSubjectComments}
              onSubjectEditDone={onEditSubject}
              onSubjectDelete={onDeleteSubject}
              setSubjectDifficulty={onSetSubjectDifficulty}
              uploadSubject={onUploadSubject}
              contentId={videoId}
              type="video"
              rootDifficulty={difficulty}
              commentActions={{
                attachStar: onAttachStar,
                editRewardComment: onEditRewardComment,
                onDelete: onDeleteComment,
                onEditDone: onEditComment,
                onLikeClick: onLikeComment,
                onLoadMoreComments: onLoadMoreSubjectComments,
                onLoadMoreReplies: onLoadMoreSubjectReplies,
                onUploadComment,
                onUploadReply
              }}
            />

                Comment on this video

              <Comments
                autoExpand
                comments={comments}
                inputAreaInnerRef={CommentInputAreaRef}
                inputTypeLabel={'comment'}
                loadMoreButton={commentsLoadMoreButton}
                onAttachStar={onAttachStar}
                onCommentSubmit={onUploadComment}
                onDelete={onDeleteComment}
                onEditDone={onEditComment}
                onLikeClick={onLikeComment}
                onLoadMoreComments={onLoadMoreComments}
                onLoadMoreReplies={onLoadMoreReplies}
                onReplySubmit={onUploadReply}
                onRewardCommentEdit={onEditRewardComment}
                parent={{
                  type: 'video',
                  difficulty,
                  id: Number(videoId),
                  uploader
                }}
                style={{ paddingTop: '1rem' }}
                userId={userId}
              />

            {resultModalShown && (
              <ResultModal
                onHide={() => setResultModalShown(false)}
                numberCorrect={numberCorrect}
                totalQuestions={questions.length}
              />
            )}
            {confirmModalShown && (
              <ConfirmModal
                title="Remove Video"
                onHide={() => setConfirmModalShown(false)}
                onConfirm={handleDeleteVideo}
              />
            )}
            {questionsBuilderShown && (
              <QuestionsBuilder
                questions={questions}
                title={title}
                videoCode={content}
                onSubmit={handleUploadQuestions}
                onHide={() => setQuestionsBuilderShown(false)}
              />
            )}

        )}

      <NavMenu videoId={videoId} playlistId={playlistId} />
    </ErrorBoundary>
  );

  async function handleDeleteVideo() {
    await request.delete(`${URL}/video?videoId=${videoId}`, auth());
    history.push('/videos');
  }

  async function handleEditVideoPage(params) {
    setOnEdit(false);
    try {
      await request.post(`${URL}/video/edit/page`, params, auth());
      const url = fetchedVideoCodeFromURL(params.url);
      setContentObj({
        ...contentObj,
        title: params.title,
        description: params.description,
        content: url
      });
      editVideoThumbs({
        videoId: Number(params.videoId),
        title: params.title,
        url
      });
    } catch (error) {
      handleError(error, dispatch);
    }
  }

  function handleChangeByUserStatus({ contentId, byUser }) {
    setContentObj({
      ...contentObj,
      byUser: byUser
    });
    changeByUserStatusForThumbs({ videoId: Number(contentId), byUser });
  }

  function handleLikeVideo(likes, videoId) {
    onLikeContent({ likes, type: 'video', contentId: videoId });
    likeVideo({ likes, videoId });
  }

  function numberCorrect() {
    const correctAnswers = questions.map(question => {
      return question.correctChoice;
    });
    let numberCorrect = 0;
    for (let i = 0; i < userAnswers.length; i++) {
      if (userAnswers[i] + 1 === correctAnswers[i]) numberCorrect++;
    }
    return numberCorrect;
  }

  function handleSelectChoice(newAnswer) {
    if (typeof userAnswers[currentSlide] === 'number') {
      return setUserAnswers(
        userAnswers.map((answer, index) => {
          return index === currentSlide ? newAnswer : answer;
        })
      );
    }
    const newAnswers = [...userAnswers];
    newAnswers[currentSlide] = newAnswer;
    setUserAnswers(newAnswers);
  }

  function handleRenderSlides() {
    return questions.map((question, index) => {
      const filteredChoices = question.choices.filter(choice => !!choice);
      let isCurrentSlide = index === currentSlide;
      const listItems = filteredChoices.map((choice, index) => {
        let isSelectedChoice = index === userAnswers[currentSlide];
        return {
          label: choice,
          checked: isCurrentSlide && isSelectedChoice
        };
      });
      return (

          <CheckListGroup
            inputType="radio"
            listItems={listItems}
            onSelect={handleSelectChoice}
            style={{ marginTop: '1.5rem', paddingRight: '1rem' }}
          />

      );
    });
  }

  function handleSetDifficulty({ contentId, difficulty }) {
    onSetDifficulty({ contentId, difficulty });
    setDifficulty({ videoId: Number(contentId), difficulty });
  }

  async function handleUploadQuestions(questions) {
    const data = {
      videoId,
      questions: questions.map(question => {
        const choices = question.choices.filter(choice => {
          return choice.label && !stringIsEmpty(choice.label);
        });
        return {
          videoId,
          title: question.title,
          correctChoice: (() => {
            let correctChoice = 0;
            for (let i = 0; i < choices.length; i++) {
              if (choices[i].checked) correctChoice = i + 1;
            }
            return correctChoice;
          })(),
          choice1: choices[0].label,
          choice2: choices[1].label,
          choice3: choices[2]?.label,
          choice4: choices[3]?.label,
          choice5: choices[4]?.label,
          creator: userId
        };
      })
    };
    try {
      await request.post(`${URL}/video/questions`, data, auth());
      const questions = data.questions.map(question => {
        return {
          title: question.title,
          choices: [
            question.choice1,
            question.choice2,
            question.choice3,
            question.choice4,
            question.choice5
          ],
          correctChoice: question.correctChoice
        };
      });
      setContentObj({
        ...contentObj,
        questions
      });
      setQuestionsBuilderShown(false);
      setCurrentSlide(0);
      setUserAnswers([]);
    } catch (error) {
      handleError(error, dispatch);
    }
  }
}

export default connect(
  state => ({
    authLevel: state.UserReducer.authLevel,
    canEdit: state.UserReducer.canEdit,
    userType: state.UserReducer.userType,
    userId: state.UserReducer.userId
  }),
  dispatch => ({
    dispatch,
    changeByUserStatusForThumbs: params =>
      dispatch(changeByUserStatusForThumbs(params)),
    editVideoThumbs: params => dispatch(editVideoThumbs(params)),
    likeVideo: params => dispatch(likeVideo(params)),
    setDifficulty: params => dispatch(setDifficulty(params))
  })
)(VideoPage);
<|endoftext|>


<commit_before>var AMOUNTX	= 50;
var AMOUNTY	= 50;

var container, stats;
var camera, scene, renderer, particle;
var mouseX = 0, mouseY = 0;

//var windowHalfX = window.innerWidth / 2;
//var windowHalfY = window.innerHeight / 2;

init();
animate();

/**
 * Build ui with Data.GUI
*/
function buildGui(parameters, callback)
{
	var gui = new DAT.GUI({
		height	: 5 * 32 - 1
	});

	gui.add(parameters, 'iterations').name('Iterations').min(10000).max(500000).step(1)
		.onFinishChange(function(){callback(parameters)}).onChange(function(){callback(parameters)});
	gui.add(parameters, 'interval').name('Interval').min(0.001).max(0.1)
		.onFinishChange(function(){callback(parameters)}).onChange(function(){callback(parameters)});
	gui.add(parameters, 'a').name('Sigma').min(1).max(30)
		.onFinishChange(function(){callback(parameters)}).onChange(function(){callback(parameters)});
	gui.add(parameters, 'b').name('Rho').min(1).max(30)
		.onFinishChange(function(){callback(parameters)}).onChange(function(){callback(parameters)});
	gui.add(parameters, 'c').name('Beta').min(0.01).max(3)
		.onFinishChange(function(){callback(parameters)}).onChange(function(){callback(parameters)});
}

function cpuDotLorentz(particules, opts)
{
	var x	= 0.1;
	var y	= 0.1;
	var z	= 0.1;

	//var a	= 5;
	//var b	= 15;
	//var c	= 1;
	//var interval     = 0.020;

	//
	console.assert( 'a' in opts )
	console.assert( 'b' in opts )
	console.assert( 'c' in opts )
	console.assert( 'interval' in opts )

	a	= opts.a;
	b	= opts.b;
	c	= opts.c;
	interval= opts.interval;

	for(var i = 0; i < particules.length; i++){
		// compute lorentz delata
		var dx	= (y - x) * a;
		var dy	= (b - z) * x - y;
		var dz	= x*y - c*z;
		// goto next coord
		x	+= dx * interval;
		y	+= dy * interval;
		z	+= dz * interval;

		var particle	= particules[i];
		var scale	= 8;
		particle.position.x = x*scale;
		particle.position.y = y*scale;
		particle.position.z = (z-b)*scale;
	}
}

function init()
{
	// maybe replace that by window... or something
	var parameters = {
		iterations	: 10000,
		interval	: 0.02,
		a		: 5,
		b		: 15,
		c		: 1
	};

	buildGui(parameters, function(){
		console.log("parameters", JSON.stringify(parameters, null, '\t'))
		cpuDotLorentz(particules, parameters);
	});

	// create the container
	container = document.createElement( 'div' );
	document.body.appendChild( container );
	// create the Camera
	camera = new THREE.Camera(30, window.innerWidth / window.innerHeight, 1, 10000 );
	camera.position.z = 100;
	// build the scene
	scene = new THREE.Scene();

	// not sure what is it
	// maybe the way each particule are drawn
	var particleDraw	= function(context, color){
		context.beginPath();
		context.arc( 0, 0, 0.5, 0, Math.PI * 2, true );
		context.closePath();
		context.fill();
	}
	//var material = new THREE.ParticleCanvasMaterial( {
	//	color	: 0xffffff,
	//	program	: particleDraw
	//} );

	// create all the particules objects and add them to the scene
	var nbParticules= AMOUNTX * AMOUNTY;
	var particules	= new Array(nbParticules);
	for(var i = 0; i < nbParticules; i++){
		var particle = new THREE.Particle( new THREE.ParticleCanvasMaterial( {
			color	: Math.random() * 0x808080 + 0x808080,
			program	: particleDraw
		} ) );
		//particle.materials[0].color.setRGB(iy/AMOUNTX, 0, 0)
		scene.addObject( particle );
		particules[i]	= particle;
	}
	// compute the position of the particules
	//cpuDotFloor(particules)
	cpuDotLorentz(particules, parameters);

	// init the renderer
	renderer	= new THREE.CanvasRenderer();
	renderer.setSize( window.innerWidth, window.innerHeight );
	container.appendChild( renderer.domElement );

	// init the Stats
	stats	= new Stats();
	stats.domElement.style.position	= 'absolute';
	stats.domElement.style.top	= '0px';
	container.appendChild( stats.domElement );

	// listen to mousemove to animate the scene
	document.addEventListener( 'mousemove', onDocumentMouseMove, false );
}

//

function onDocumentMouseMove(event)
{
	mouseX = event.clientX - window.innerWidth / 2;
	mouseY = event.clientY - window.innerHeight / 2;
}

//
function animate() {
	requestAnimationFrame( animate );
	render();
	stats.update();
}

function render()
{
	// move the camera
	camera.position.x += ( mouseX - camera.position.x ) * .05;
	camera.position.y += ( - mouseY - camera.position.y ) * .05;
	// actually render the scene
	renderer.render( scene, camera );
}
<commit_msg>source/data/funky_dots/funky_dots.js more comment<commit_after>var AMOUNTX	= 50;
var AMOUNTY	= 50;

var container, stats;
var camera, scene, renderer, particle;
var mouseX = 0, mouseY = 0;

//var windowHalfX = window.innerWidth / 2;
//var windowHalfY = window.innerHeight / 2;

init();
animate();

/**
 * Build ui with Data.GUI
*/
function buildGui(parameters, callback)
{
	var gui = new DAT.GUI({
		height	: 5 * 32 - 1
	});

	gui.add(parameters, 'iterations').name('Iterations').min(10000).max(500000).step(1)
		.onFinishChange(function(){callback(parameters)}).onChange(function(){callback(parameters)});
	gui.add(parameters, 'interval').name('Interval').min(0.001).max(0.1)
		.onFinishChange(function(){callback(parameters)}).onChange(function(){callback(parameters)});
	gui.add(parameters, 'a').name('Sigma').min(1).max(30)
		.onFinishChange(function(){callback(parameters)}).onChange(function(){callback(parameters)});
	gui.add(parameters, 'b').name('Rho').min(1).max(30)
		.onFinishChange(function(){callback(parameters)}).onChange(function(){callback(parameters)});
	gui.add(parameters, 'c').name('Beta').min(0.01).max(3)
		.onFinishChange(function(){callback(parameters)}).onChange(function(){callback(parameters)});
}

function cpuDotLorentz(particules, opts)
{

	// sanity check
	console.assert( 'a' in opts )
	console.assert( 'b' in opts )
	console.assert( 'c' in opts )
	console.assert( 'interval' in opts )

	a	= opts.a;
	b	= opts.b;
	c	= opts.c;
	interval= opts.interval;

	// initial value
	var x	= 0.1;
	var y	= 0.1;
	var z	= 0.1;
	var scale	= 8;
	// go thru each particule
	for(var i = 0; i < particules.length; i++){
		// compute lorentz delata
		var dx	= (y - x) * a;
		var dy	= (b - z) * x - y;
		var dz	= x*y - c*z;
		// goto next coord
		x	+= dx * interval;
		y	+= dy * interval;
		z	+= dz * interval;
		// get the coord for this particule
		var particle	= particules[i];
		particle.position.x = x*scale;
		particle.position.y = y*scale;
		particle.position.z = (z-b)*scale;
	}
}

function init()
{
	// maybe replace that by window... or something
	var parameters = {
		iterations	: 10000,
		interval	: 0.02,
		a		: 5,
		b		: 15,
		c		: 1
	};

	buildGui(parameters, function(){
		console.log("parameters", JSON.stringify(parameters, null, '\t'))
		cpuDotLorentz(particules, parameters);
	});

	// create the container
	container = document.createElement( 'div' );
	document.body.appendChild( container );
	// create the Camera
	camera = new THREE.Camera(30, window.innerWidth / window.innerHeight, 1, 10000 );
	camera.position.z = 100;
	// build the scene
	scene = new THREE.Scene();

	// not sure what is it
	// maybe the way each particule are drawn
	var particleDraw	= function(context, color){
		context.beginPath();
		context.arc( 0, 0, 0.5, 0, Math.PI * 2, true );
		context.closePath();
		context.fill();
	}

	// create all the particules objects and add them to the scene
	var nbParticules= AMOUNTX * AMOUNTY;
	var particules	= new Array(nbParticules);
	for(var i = 0; i < nbParticules; i++){
		var particle = new THREE.Particle( new THREE.ParticleCanvasMaterial( {
			color	: Math.random() * 0x808080 + 0x808080,
			program	: particleDraw
		} ) );
		//particle.materials[0].color.setRGB(iy/AMOUNTX, 0, 0)
		scene.addObject( particle );
		particules[i]	= particle;
	}
	// compute the position of the particules
	cpuDotLorentz(particules, parameters);

	// init the renderer
	renderer	= new THREE.CanvasRenderer();
	renderer.setSize( window.innerWidth, window.innerHeight );
	container.appendChild( renderer.domElement );

	// init the Stats
	stats	= new Stats();
	stats.domElement.style.position	= 'absolute';
	stats.domElement.style.top	= '0px';
	container.appendChild( stats.domElement );

	// listen to mousemove to animate the scene
	document.addEventListener( 'mousemove', onDocumentMouseMove, false );
}

//

function onDocumentMouseMove(event)
{
	mouseX = event.clientX - window.innerWidth / 2;
	mouseY = event.clientY - window.innerHeight / 2;
}

//
function animate() {
	requestAnimationFrame( animate );
	render();
	stats.update();
}

function render()
{
	// move the camera
	camera.position.x += ( mouseX - camera.position.x ) * .05;
	camera.position.y += ( - mouseY - camera.position.y ) * .05;
	// actually render the scene
	renderer.render( scene, camera );
}
<|endoftext|>


<commit_before>'use strict';

import React, { Component } from 'react';
import {
  Text,
  View,
  Button,
  Container,
  TouchableHighlight,
  ScrollView,
} from 'react-native';
import { connect } from 'react-redux';

import { editWord, goBack } from '../actions';
import { styles } from '../styles';
import { words } from '../words';

const WordPicker = ({ wordType, wordIndex, editWordClick, goBackClick }) => {
  var categories = [];
  var wordList = words[wordType]['categories'];
  for (var category in wordList) {
    var wordStyle = ([
      styles.categoryWord,
      { backgroundColor: words[wordType]['color'] }
    ]);
    var wordOptions = wordList[category].map(item =>
      <TouchableHighlight
        style={wordStyle}
        underlayColor='transparent'
        key={item}
        onPress={editWordClick.bind(this,item,wordIndex)}>
        <Text key={item} style={styles.wordText}>{item}</Text>
      </TouchableHighlight>
    );
    var categoryTitle = category !== 'all' ?
      <Text key={category} style={styles.categoryHeader}>{category}</Text>
      : null;
    categories.push(
      <View key={category + "_view" } style={styles.page}>
        { categoryTitle }
        <View style={styles.categoryContainer}>
          { wordOptions }
        </View>
      </View>
    );
  }

  return (
  <ScrollView style={styles.wordPickerScrollContainer}>
    <View style={styles.wordPickerContainer}>
      <Text style={styles.wordsHeader}>{ wordType }s</Text>
      <View style={styles.wordPicker}>
        { categories }
      </View>
      <Button title = "Go Back"
              onPress = {() => goBackClick()} />
    </View>
  </ScrollView>
  );
}

/* Container Component */

const mapDispatchToProps = (dispatch) => {
  return {
    editWordClick: (word, wordIndex) => {
      dispatch(editWord(word,wordIndex))
    },
    goBackClick: () => {
      dispatch(goBack())
    }
  }
}

const mapStateToProps = (state) => {
  return {
    wordType: state.sentences.wordPicker,
    wordIndex: state.sentences.editIndex,
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(WordPicker)<commit_msg>punctuations => punctuation<commit_after>'use strict';

import React, { Component } from 'react';
import {
  Text,
  View,
  Button,
  Container,
  TouchableHighlight,
  ScrollView,
} from 'react-native';
import { connect } from 'react-redux';

import { editWord, goBack } from '../actions';
import { styles } from '../styles';
import { words } from '../words';

const WordPicker = ({ wordType, wordIndex, editWordClick, goBackClick }) => {
  var categories = [];

  var wordTypePlural = wordType == "punctuation"
                            || wordType == "wh-"
                            || wordType == "social"
                        ? wordType
                        : wordType + "s";

  var wordList = words[wordType]['categories'];
  for (var category in wordList) {
    var wordStyle = ([
      styles.categoryWord,
      { backgroundColor: words[wordType]['color'] }
    ]);
    var wordOptions = wordList[category].map(item =>
      <TouchableHighlight
        style={wordStyle}
        underlayColor='transparent'
        key={item}
        onPress={editWordClick.bind(this,item,wordIndex)}>
        <Text key={item} style={styles.wordText}>{item}</Text>
      </TouchableHighlight>
    );
    var categoryTitle = category !== 'all' ?
      <Text key={category} style={styles.categoryHeader}>{category}</Text>
      : null;
    categories.push(
      <View key={category + "_view" } style={styles.page}>
        { categoryTitle }
        <View style={styles.categoryContainer}>
          { wordOptions }
        </View>
      </View>
    );
  }

  return (
  <ScrollView style={styles.wordPickerScrollContainer}>
    <View style={styles.wordPickerContainer}>
      <Text style={styles.wordsHeader}>{ wordTypePlural }</Text>
      <View style={styles.wordPicker}>
        { categories }
      </View>
      <Button title = "Go Back"
              onPress = {() => goBackClick()} />
    </View>
  </ScrollView>
  );
}

/* Container Component */

const mapDispatchToProps = (dispatch) => {
  return {
    editWordClick: (word, wordIndex) => {
      dispatch(editWord(word,wordIndex))
    },
    goBackClick: () => {
      dispatch(goBack())
    }
  }
}

const mapStateToProps = (state) => {
  return {
    wordType: state.sentences.wordPicker,
    wordIndex: state.sentences.editIndex,
  }
}

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


<commit_before>/******************************************************
* #### jQuery-Youtube-Channels-Playlist ####
* Coded by Ican Bachors 2014.
* http://ibacor.com/labs/jquery-youtube-channels-playlist/
* Updates will be posted to this site.
******************************************************/

eval(function(p,a,c,k,e,r){e=function(c){return(c35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--)r[e(c)]=k[c]||e(c);k=[function(e){return r[e]}];e=function(){return'\\w+'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('8 26=4(m,n,o,r,q,p){$.L({A:\'I://B.N.C/D/V/27?W=X&\'+o+\'=\'+m+\'&O=\'+p,1t:1u,P:\'Y\'}).Q(4(a){8 b=a.7[0].X.28.29,1v=a.7[0].Z,1w=\'\',17=0;w(q){17=1}10(b,p,r,1w,n,1v,m,17)});4 10(f,g,n,h,j,k,l,m){$.L({A:\'I://B.N.C/D/V/2a?W=z&1x=\'+n+\'&2b=\'+f+\'&O=\'+g+\'&2c=\'+h,P:\'Y\'}).Q(4(c){8 d=\'\';d+=\'<9 5="6-v-2d"><t 5="6-2e-R">\'+j+\'</t><18><t 5="6-v-1y">1y \'+l+\'</t><1d>\';d+=\' \';d+=\'</9><9 5="6-v-2j">\';$.1C(c.7,4(i,a){8 b=c.7[i].z.2k.2l;1D(b,i,g);d+=\'<9 5="6-1e" 1f-1g="\'+b+\'"><9 5="2m"><13 1h="\'+c.7[i].z.2n.2o.A+\'" 1E="2p"><t 5="6-v-1F\'+i+\'"></t></9>\';d+=c.7[i].z.R+\'</9>\'});d+=\'</9>\';$(\'.2q\').E(d);w(c.1i==1G){8 e=$(".6-1e").1H("1f-1g");1j(e,k,l,g,m)}w(c.1i!=1G){$(\'.1A\').1k(4(){8 a=c.1i;10(f,g,n,a,j,k,l,m);u 1l})}$(\'.1B\').1k(4(){8 a=c.2r;10(f,g,n,a,j,k,l,m);u 1l});$(".6-1e").1C(4(){$(1m).1k(4(){8 a=$(1m).1H("1f-1g");$(\'9\').2s(\'6-v-1I\');$(1m).2t(\'6-v-1I\');1j(a,k,l,g,m);u 1l})})})}4 1j(c,d,e,g,f){$.L({A:\'I://B.N.C/D/V/12?Z=\'+c+\'&O=\'+g+\'&W=z,14\',P:\'Y\'}).Q(4(a){8 b=\'\',1J=a.7[0].14.2u,1K=a.7[0].14.2v,1L=a.7[0].14.2w,2x=\'\',1M=a.7[0].z.1N.R,1O=a.7[0].z.1N.2y;b+=\'<1n 1h="1a://B.D.C/2z/\'+c+\'?1P=0&2A;2B=\'+f+\'" 2C="" 2D="0" 5="6-v-1n"></1n>\';b+=\'<9 5="6-v-1o"><1Q>\'+1M+\'</1Q><1d>\';b+=\'<9 5="6-v-1o-11"><t Z="6-11-13"></t> <t 5="6-2F">\'+a.7[0].z.2G+\'</t><1R 5="1p-1q"> \'+15(1J)+\'</1R></9></9>\';b+=\'<9 5="6-v-1o"> <t>\'+1S(1T 1U(a.7[0].z.2I).2J())+\'</t> \'+15(1L)+\' \'+15(1K)+\'<1d>\'+1W(1O).J(/\\n/g,\'<18>\')+\'<18></9>\';1X(e,g);$(\'.2M\').E(b)})}4 1D(c,i,g){$.L({A:\'I://B.N.C/D/V/12?Z=\'+c+\'&O=\'+g+\'&W=X\',P:\'Y\'}).Q(4(a){8 b=a.7[0].X.2N,F=\'\',16=\'\',T=\'\';w(b.2O(/M/g)){F=b.1r(\'M\');16=F[0].J(\'1Y\',\'\');w(F[1]!=\'\'){T=F[1].J(\'S\',\'\')}1Z{T=\'20\'}}1Z{F=b.1r(\'1Y\');16=\'20\';T=F[1].J(\'S\',\'\')}$(\'.6-v-1F\'+i).E(16+\':\'+T)})}4 1W(b){8 c=/(I?:\\/\\/[^\\s]+)/g;u b.J(c,4(a){u\'\'+a+\'\'})}4 1X(c,g){8 d=\'I://B.N.C/2Q/2R/2S/\',21=\'/2T/2U\',1s=\'7(22(23(A)))\';$.L({A:d+\'+\'+c+21+\'?O=\'+g+\'&1s=\'+1s+\'&1x=1\',1t:1u,P:\'2V\'}).Q(4(a){8 b=a.7,i=0,E=\'\';2W(i=0;i<b.24;i+=1){E+=\'<13 1h="\'+b[i].22.23.A+\'" 1E="" />\'}$(\'#6-11-13\').E(E)})}4 1S(a){8 s=G.H((1T 1U()-a)/2X),i=G.H(s/2Y);w(i>1){u i+" 2Z K"}i=G.H(s/30);w(i>1){u i+" 31 K"}i=G.H(s/32);w(i>1){u i+" 33 K"}i=G.H(s/34);w(i>1){u i+" 35 K"}i=G.H(s/36);w(i>1){u i+" 37 K"}u G.H(s)+" 38 K"}4 15(a){a+=\'\';x=a.1r(\'.\');U=x[0];25=x.24>1?\'.\'+x[1]:\'\';8 b=/(\\d+)(\\d{3})/;39(b.3a(U)){U=U.J(b,\'$1\'+\',\'+\'$2\')}u U+25}}',62,197,'||||function|class|ibacordotcom|items|var|div||||||||||||||||||||span|return|vid|if||fa|snippet|url|www|com|youtube|html|dataw|Math|floor|https|replace|ago|ajax||googleapis|key|dataType|done|title||detik|x1|v3|part|contentDetails|json|id|youtube_video_list|user|videos|img|statistics|addCommas|menit|autop|br|href|http|target|_BLANK|hr|play|data|vvv|src|prevPageToken|youtube_det|click|false|this|iframe|box|pull|right|split|fields|crossDomain|true|chid|nekpag|maxResults|by|fast|ibacordotcom_vid_prev|ibacordotcom_vid_next|each|getwaktu|alt|tm|null|attr|active|viewc|likc|likd|judul|localized|desc|rel|h1|h4|_timeSince|new|Date|thumbs|urlify|gplus|PT|else|00|apiend|actor|image|length|x2|ibacor_ycp|channels|relatedPlaylists|uploads|playlistItems|playlistId|pageToken|top|nav|backward|Previous|forward|Next|bottom|resourceId|videoId|ibacordotcom_youtube_thumb|thumbnails|default|ibacor|ibacordotcom_youtube_channels|nextPageToken|removeClass|addClass|viewCount|likeCount|dislikeCount|category|description|embed|amp|autoplay|allowfullscreen|frameborder|channel|uploader|channelTitle|clock|publishedAt|getTime|down|up|ibacordotcom_vid_play|duration|match|nofollow|plus|v1|people|activities|public|jsonp|for|1000|31536000|years|2592000|months|86400|days|3600|hours|60|minutes|seconds|while|test'.split('|'),0,{}))
<commit_msg>v02 add window.history.pushState dinamic url<commit_after>/******************************************************
* #### jQuery-Youtube-Channels-Playlist ####
* Coded by Ican Bachors 2014.
* http://ibacor.com/labs/jquery-youtube-channels-playlist/
* Updates will be posted to this site.
******************************************************/

eval(function(p,a,c,k,e,r){e=function(c){return(c35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--)r[e(c)]=k[c]||e(c);k=[function(e){return r[e]}];e=function(){return'\\w+'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('5 2b=4(m,n,o,r,q,p){5 t=\'1w\';t=t.D(/[\\[]/,"\\\\[").D(/[\\]]/,"\\\\]");5 u=1b 2c("[\\\\?&]"+t+"=([^&#]*)"),1c=u.2d(2e.2f);w(1c===W){1d(\'\',m,n,o,r,q,p)}14{5 v=2g(1c[1].D(/\\+/g," "));$.O({E:\'H://F.P.G/I/X/Y?Q=\'+v+\'&R=\'+p+\'&Z=z\',T:\'10\'}).U(4(a){1d(v,a.6[0].z.2h,n,\'Q\',r,q,p)})}4 1d(c,m,n,o,r,q,p){$.O({E:\'H://F.P.G/I/X/2i?Z=15&\'+o+\'=\'+m+\'&R=\'+p,1x:1y,T:\'10\'}).U(4(a){5 b=a.6[0].15.2j.2k,1z=a.6[0].Q,1A=\'\',1e=0;w(q){1e=1}16(c,b,p,r,1A,n,1z,m,1e)})}4 16(o,f,g,n,h,j,k,l,m){$.O({E:\'H://F.P.G/I/X/2l?Z=z&1B=\'+n+\'&2m=\'+f+\'&R=\'+g+\'&2n=\'+h,T:\'10\'}).U(4(c){5 d=\'\';d+=\'<9 7="8-A-2o">\'+j+\'<1f>\';d+=\' \';d+=\'</9><9 7="8-A-2u">\';$.1F(c.6,4(i,a){5 b=c.6[i].z.2v.2w;1G(b,i,g);d+=\'<9 7="8-1g" 1h-1i="\'+b+\'"><9 7="2x"><17 1j="\'+c.6[i].z.2y.2z.E+\'" 1H="2A"></9>\';d+=c.6[i].z.11+\'</9>\'});d+=\'</9>\';$(\'.2B\').J(d);w(c.1k==W){5 e=$(".8-1g").1J("1h-1i");1l(o,e,k,l,g,m)}w(c.1k!=W){$(\'.1D\').1m(4(){5 a=c.1k;16(\'\',f,g,n,a,j,k,l,m);y 1n})}$(\'.1E\').1m(4(){5 a=c.2C;16(\'\',f,g,n,a,j,k,l,m);y 1n});$(".8-1g").1F(4(){$(1o).1m(4(){5 a=$(1o).1J("1h-1i");2D.2E.2F(W,W,"?1w="+a);$(\'9\').2G(\'8-A-1K\');$(1o).2H(\'8-A-1K\');1l(\'\',a,k,l,g,m);y 1n})})})}4 1l(h,c,d,e,g,f){5 i=\'\';w(h==\'\'){i=c}14{i=h}$.O({E:\'H://F.P.G/I/X/Y?Q=\'+i+\'&R=\'+g+\'&Z=z,18\',T:\'10\'}).U(4(a){5 b=\'\',1L=a.6[0].18.2I,1M=a.6[0].18.2J,1N=a.6[0].18.2K,2L=\'\',1O=a.6[0].z.1P.11,1Q=a.6[0].z.1P.2M;b+=\'<1p 1j="1R://F.I.G/2N/\'+i+\'?1S=0&2O;2P=\'+f+\'" 2Q="" 2R="0" 7="8-A-1p"></1p>\';b+=\'<9 7="8-A-1q"><1T>\'+1O+\'</1T><1f>\';b+=\'<9 7="8-A-1q-1r"> \'+a.6[0].z.2U+\'<1X 7="1s-1t"> \'+19(1L)+\'</1X></9></9>\';b+=\'<9 7="8-A-1q"> \'+1Y(1b 1Z(a.6[0].z.2W).2X())+\' \'+19(1N)+\' \'+19(1M)+\'<1f>\'+21(1Q).D(/\\n/g,\'<22>\')+\'<22></9>\';23(e,g);$(\'.30\').J(b)})}4 1G(c,i,g){$.O({E:\'H://F.P.G/I/X/Y?Q=\'+c+\'&R=\'+g+\'&Z=15\',T:\'10\'}).U(4(a){5 b=a.6[0].15.31,K=\'\',1a=\'\',12=\'\';w(b.32(/M/g)){K=b.1u(\'M\');1a=K[0].D(\'24\',\'\');w(K[1]!=\'\'){12=K[1].D(\'S\',\'\')}14{12=\'25\'}}14{K=b.1u(\'24\');1a=\'25\';12=K[1].D(\'S\',\'\')}$(\'.8-A-1I\'+i).J(1a+\':\'+12)})}4 21(b){5 c=/(H?:\\/\\/[^\\s]+)/g;y b.D(c,4(a){y\'\'+a+\'\'})}4 23(c,g){5 d=\'H://F.P.G/34/35/36/\',26=\'/37/38\',1v=\'6(27(28(E)))\';$.O({E:d+\'+\'+c+26+\'?R=\'+g+\'&1v=\'+1v+\'&1B=1\',1x:1y,T:\'39\'}).U(4(a){5 b=a.6,i=0,J=\'\';3a(i=0;i<b.29;i+=1){J+=\'<17 1j="\'+b[i].27.28.E+\'" 1H="" />\'}$(\'#8-1r-17\').J(J)})}4 1Y(a){5 s=L.N((1b 1Z()-a)/3b),i=L.N(s/3c);w(i>1){y i+" 3d V"}i=L.N(s/3e);w(i>1){y i+" 3f V"}i=L.N(s/3g);w(i>1){y i+" 3h V"}i=L.N(s/3i);w(i>1){y i+" 3j V"}i=L.N(s/3k);w(i>1){y i+" 3l V"}y L.N(s)+" 3m V"}4 19(a){a+=\'\';x=a.1u(\'.\');13=x[0];2a=x.29>1?\'.\'+x[1]:\'\';5 b=/(\\d+)(\\d{3})/;3n(b.3o(13)){13=13.D(b,\'$1\'+\',\'+\'$2\')}y 13+2a}}',62,211,'||||function|var|items|class|ibacordotcom|div|||||||||||||||||||||||if||return|snippet|vid|span|fa|replace|url|www|com|https|youtube|html|dataw|Math||floor|ajax|googleapis|id|key||dataType|done|ago|null|v3|videos|part|json|title|detik|x1|else|contentDetails|youtube_video_list|img|statistics|addCommas|menit|new|results|ayomain|autop|hr|play|data|vvv|src|prevPageToken|youtube_det|click|false|this|iframe|box|user|pull|right|split|fields|video|crossDomain|true|chid|nekpag|maxResults|fast|ibacordotcom_vid_prev|ibacordotcom_vid_next|each|getwaktu|alt|tm|attr|active|viewc|likc|likd|judul|localized|desc|http|rel|h1|href|target|_BLANK|h4|_timeSince|Date|thumbs|urlify|br|gplus|PT|00|apiend|actor|image|length|x2|ibacor_ycp|RegExp|exec|location|search|decodeURIComponent|channelId|channels|relatedPlaylists|uploads|playlistItems|playlistId|pageToken|top|nav|backward|Previous|forward|Next|bottom|resourceId|videoId|ibacordotcom_youtube_thumb|thumbnails|default|ibacor|ibacordotcom_youtube_channels|nextPageToken|window|history|pushState|removeClass|addClass|viewCount|likeCount|dislikeCount|category|description|embed|amp|autoplay|allowfullscreen|frameborder|channel|uploader|channelTitle|clock|publishedAt|getTime|down|up|ibacordotcom_vid_play|duration|match|nofollow|plus|v1|people|activities|public|jsonp|for|1000|31536000|years|2592000|months|86400|days|3600|hours|60|minutes|seconds|while|test'.split('|'),0,{}))
<|endoftext|>


<commit_before>const recursive = require('recursive-readdir');
const path = require('path');
const jsonfile = require('jsonfile');

(function () {
  'use strict';

  module.exports = {
    topic: 'source',
    command: 'create',
    description: 'Create a manifest file for your open source project',
    help: 'help text for force:source:manifest:create',
    flags: [{
      name: 'path',
      char: 'p',
      description: 'Path for your source',
      required: true,
      hasValue: true
    }],
    run(context) {

      const dir = context.flags.path;

      let json = {
        sfdxSource: true,
        version : '1.0.0',
        files: [
        ]
      };

      recursive(dir, (err, files) => {

        // console.log(file);
        json.files.push(files);
        // console.log(json);

        const filePathAndName = path.join('.', 'sfdx-oss-manifest.json');

        jsonfile.writeFileSync(filePathAndName, json, {spaces: 2});

        console.log(`Created file ${filePathAndName}`);

      });

    }
  };
}());<commit_msg>Refactoring for linter<commit_after>const recursive = require('recursive-readdir');
const path = require('path');
const jsonfile = require('jsonfile');

(function () {
  'use strict';

  module.exports = {
    topic: 'source',
    command: 'create',
    description: 'Create a manifest file for your open source project',
    help: 'help text for force:source:manifest:create',
    flags: [{
      name: 'path',
      char: 'p',
      description: 'Path for your source',
      required: true,
      hasValue: true
    }],
    run(context) {

      const dir = context.flags.path;
      const filePathAndName = path.join('.', 'sfdx-oss-manifest.json');
      const json = {
        sfdxSource: true,
        version : '1.0.0',
        files: [
        ]
      };

      recursive(dir, (err, files) => {

        json.files.push(files);
        jsonfile.writeFileSync(filePathAndName, json, {spaces: 2});
        console.log(`Created file ${filePathAndName}`); // eslint-disable-line no-console

      });
    }
  };
}());<|endoftext|>