<commit_before>/* eslint-disable react/no-danger */
import React from "react";
import { connect } from "react-redux";
import queryString from "query-string";
import ReactPageScroller from "./ReactPageScroller";
import { changePage } from "../../actions/navigation";
import { CHANGE_URL_QUERY_BUT_NOT_REDUX_STATE, TOGGLE_NARRATIVE } from "../../actions/types";
import { sidebarColor } from "../../globalStyles";
import { narrativeNavBarHeight } from "../../util/globals";

/* regarding refs: https://reactjs.org/docs/refs-and-the-dom.html#exposing-dom-refs-to-parent-components */
const progressHeight = 25;
const linkStyles = { // would be better to get CSS specificity working
  color: "#5097BA",
  textDecoration: "none",
  cursor: "pointer",
  fontFamily: "Lato",
  fontWeight: "400",
  fontSize: "1.8em"
};

@connect((state) => ({
  loaded: state.narrative.loaded,
  blocks: state.narrative.blocks,
  currentInFocusBlockIdx: state.narrative.blockIdx
}))
class Narrative extends React.Component {
  constructor(props) {
    super(props);
    this.state = {showingEndOfNarrativePage: false};
    this.exitNarrativeMode = () => {
      this.props.dispatch({type: TOGGLE_NARRATIVE, display: false});
    };
    this.changeAppStateViaBlock = (reactPageScrollerIdx) => {
      const idx = reactPageScrollerIdx-1;
      if (idx === this.props.blocks.length) {
        this.setState({showingEndOfNarrativePage: true});
      } else {
        if (this.state.showingEndOfNarrativePage) {
          this.setState({showingEndOfNarrativePage: false});
        }
        this.props.dispatch(changePage({
          // path: this.props.blocks[blockIdx].dataset, // not yet implemented properly
          dontChangeDataset: true,
          query: queryString.parse(this.props.blocks[idx].query),
          queryToDisplay: {n: idx},
          push: true
        }));
      }
    };
  }
  componentDidMount() {
    if (window.twttr && window.twttr.ready) {
      window.twttr.widgets.load();
    }
    /* if the query has defined a block to be shown (that's not the first)
    then we must scroll to that block */
    if (this.props.currentInFocusBlockIdx !== 0) {
      this.reactPageScroller.goToPage(this.props.currentInFocusBlockIdx);
    }
  }
  renderOpacityFade(top) {
    const style = {
      zIndex: 200,
      position: "absolute",
      backgroundImage: `linear-gradient(to ${top?"top":"bottom"}, rgba(255, 255, 255, 0), ${sidebarColor})`,
      width: "100%",
      height: "30px"
    };
    if (top) style.top = narrativeNavBarHeight + progressHeight;
    else style.bottom = 0;
    return (
      <div id={`fade${top?"Top":"Bottom"}`} style={style}/>
    );
  }
  renderChevron(pointUp) {
    const dims = {w: 30, h: 30};
    const style = {
      zIndex: 200,
      position: "absolute",
      cursor: "pointer",
      left: `${this.props.width/2 - dims.w/2}px`
    };
    if (pointUp) style.top = narrativeNavBarHeight + progressHeight;
    else style.bottom = 0;
    let gotoIdx = pointUp ? this.props.currentInFocusBlockIdx-1 : this.props.currentInFocusBlockIdx+1;
    if (this.state.showingEndOfNarrativePage) {
      gotoIdx = this.props.blocks.length-1;
    }
    const svgPathD = pointUp ?
      "M240.971 130.524l194.343 194.343c9.373 9.373 9.373 24.569 0 33.941l-22.667 22.667c-9.357 9.357-24.522 9.375-33.901.04L224 227.495 69.255 381.516c-9.379 9.335-24.544 9.317-33.901-.04l-22.667-22.667c-9.373-9.373-9.373-24.569 0-33.941L207.03 130.525c9.372-9.373 24.568-9.373 33.941-.001z" :
      "M207.029 381.476L12.686 187.132c-9.373-9.373-9.373-24.569 0-33.941l22.667-22.667c9.357-9.357 24.522-9.375 33.901-.04L224 284.505l154.745-154.021c9.379-9.335 24.544-9.317 33.901.04l22.667 22.667c9.373 9.373 9.373 24.569 0 33.941L240.971 381.476c-9.373 9.372-24.569 9.372-33.942 0z";
    return (
      <div id={`hand${pointUp?"Up":"Down"}`} style={style} onClick={() => this.reactPageScroller.goToPage(gotoIdx)}>
        <svg width={`${dims.w}px`} height={`${dims.h}px`} viewBox="0 0 448 512">
          <path d={svgPathD} fill="black"/>
        </svg>

    );
  }
  renderProgress() {
    return (
      <div
        style={{
          height: `${progressHeight}px`,
          width: "100%",
          backgroundColor: "inherit",
          boxShadow: '0px -3px 3px -3px rgba(0, 0, 0, 0.2) inset',
          display: "flex",
          flexDirection: "row",
          justifyContent: "space-evenly",
          alignItems: "center"
        }}
      >
        {this.props.blocks.map((b, i) => {
          const d = (!this.state.showingEndOfNarrativePage) && this.props.currentInFocusBlockIdx === i ?
            "14px" : "6px";
          return (
 this.reactPageScroller.goToPage(i)}
          />);
        })}

    );
  }
  renderBlocks() {
    const ret = this.props.blocks.map((b, i) => (
      <div
        id={`NarrativeBlock_${i}`}
        key={b.__html.slice(0, 50)}
        style={{
          padding: "10px 20px",
          height: "inherit",
          overflow: "hidden"
        }}
        dangerouslySetInnerHTML={b}
      />
    ));
    ret.push((

          END OF NARRATIVE

 this.scrollToBlock(0)}>
          Click here to Scroll back to top

          Click here to Exit narrative mode

    ));
    return ret;
  }
  render() {
    if (!this.props.loaded) {return null;}
    return (

        {this.renderProgress()}
        {this.renderOpacityFade(true)}
        {this.renderOpacityFade(false)}
        {this.props.currentInFocusBlockIdx !== 0 ? this.renderChevron(true) : null}
        {!this.state.showingEndOfNarrativePage ? this.renderChevron(false) : null}
        <ReactPageScroller
          ref={(c) => {this.reactPageScroller = c;}}
          containerHeight={this.props.height-progressHeight}
          pageOnChange={this.changeAppStateViaBlock}
        >
          {this.renderBlocks()}
        </ReactPageScroller>

    );
  }
  componentWillUnmount() {
    this.props.dispatch({
      type: CHANGE_URL_QUERY_BUT_NOT_REDUX_STATE,
      pathname: this.props.blocks[this.props.currentInFocusBlockIdx].dataset,
      query: queryString.parse(this.props.blocks[this.props.currentInFocusBlockIdx].url)
    });
  }
}
export default Narrative;
<commit_msg>fix "scroll back to top" functionality<commit_after>/* eslint-disable react/no-danger */
import React from "react";
import { connect } from "react-redux";
import queryString from "query-string";
import ReactPageScroller from "./ReactPageScroller";
import { changePage } from "../../actions/navigation";
import { CHANGE_URL_QUERY_BUT_NOT_REDUX_STATE, TOGGLE_NARRATIVE } from "../../actions/types";
import { sidebarColor } from "../../globalStyles";
import { narrativeNavBarHeight } from "../../util/globals";

/* regarding refs: https://reactjs.org/docs/refs-and-the-dom.html#exposing-dom-refs-to-parent-components */
const progressHeight = 25;
const linkStyles = { // would be better to get CSS specificity working
  color: "#5097BA",
  textDecoration: "none",
  cursor: "pointer",
  fontFamily: "Lato",
  fontWeight: "400",
  fontSize: "1.8em"
};

@connect((state) => ({
  loaded: state.narrative.loaded,
  blocks: state.narrative.blocks,
  currentInFocusBlockIdx: state.narrative.blockIdx
}))
class Narrative extends React.Component {
  constructor(props) {
    super(props);
    this.state = {showingEndOfNarrativePage: false};
    this.exitNarrativeMode = () => {
      this.props.dispatch({type: TOGGLE_NARRATIVE, display: false});
    };
    this.changeAppStateViaBlock = (reactPageScrollerIdx) => {
      const idx = reactPageScrollerIdx-1;
      if (idx === this.props.blocks.length) {
        this.setState({showingEndOfNarrativePage: true});
      } else {
        if (this.state.showingEndOfNarrativePage) {
          this.setState({showingEndOfNarrativePage: false});
        }
        this.props.dispatch(changePage({
          // path: this.props.blocks[blockIdx].dataset, // not yet implemented properly
          dontChangeDataset: true,
          query: queryString.parse(this.props.blocks[idx].query),
          queryToDisplay: {n: idx},
          push: true
        }));
      }
    };
  }
  componentDidMount() {
    if (window.twttr && window.twttr.ready) {
      window.twttr.widgets.load();
    }
    /* if the query has defined a block to be shown (that's not the first)
    then we must scroll to that block */
    if (this.props.currentInFocusBlockIdx !== 0) {
      this.reactPageScroller.goToPage(this.props.currentInFocusBlockIdx);
    }
  }
  renderOpacityFade(top) {
    const style = {
      zIndex: 200,
      position: "absolute",
      backgroundImage: `linear-gradient(to ${top?"top":"bottom"}, rgba(255, 255, 255, 0), ${sidebarColor})`,
      width: "100%",
      height: "30px"
    };
    if (top) style.top = narrativeNavBarHeight + progressHeight;
    else style.bottom = 0;
    return (
      <div id={`fade${top?"Top":"Bottom"}`} style={style}/>
    );
  }
  renderChevron(pointUp) {
    const dims = {w: 30, h: 30};
    const style = {
      zIndex: 200,
      position: "absolute",
      cursor: "pointer",
      left: `${this.props.width/2 - dims.w/2}px`
    };
    if (pointUp) style.top = narrativeNavBarHeight + progressHeight;
    else style.bottom = 0;
    let gotoIdx = pointUp ? this.props.currentInFocusBlockIdx-1 : this.props.currentInFocusBlockIdx+1;
    if (this.state.showingEndOfNarrativePage) {
      gotoIdx = this.props.blocks.length-1;
    }
    const svgPathD = pointUp ?
      "M240.971 130.524l194.343 194.343c9.373 9.373 9.373 24.569 0 33.941l-22.667 22.667c-9.357 9.357-24.522 9.375-33.901.04L224 227.495 69.255 381.516c-9.379 9.335-24.544 9.317-33.901-.04l-22.667-22.667c-9.373-9.373-9.373-24.569 0-33.941L207.03 130.525c9.372-9.373 24.568-9.373 33.941-.001z" :
      "M207.029 381.476L12.686 187.132c-9.373-9.373-9.373-24.569 0-33.941l22.667-22.667c9.357-9.357 24.522-9.375 33.901-.04L224 284.505l154.745-154.021c9.379-9.335 24.544-9.317 33.901.04l22.667 22.667c9.373 9.373 9.373 24.569 0 33.941L240.971 381.476c-9.373 9.372-24.569 9.372-33.942 0z";
    return (
      <div id={`hand${pointUp?"Up":"Down"}`} style={style} onClick={() => this.reactPageScroller.goToPage(gotoIdx)}>
        <svg width={`${dims.w}px`} height={`${dims.h}px`} viewBox="0 0 448 512">
          <path d={svgPathD} fill="black"/>
        </svg>

    );
  }
  renderProgress() {
    return (
      <div
        style={{
          height: `${progressHeight}px`,
          width: "100%",
          backgroundColor: "inherit",
          boxShadow: '0px -3px 3px -3px rgba(0, 0, 0, 0.2) inset',
          display: "flex",
          flexDirection: "row",
          justifyContent: "space-evenly",
          alignItems: "center"
        }}
      >
        {this.props.blocks.map((b, i) => {
          const d = (!this.state.showingEndOfNarrativePage) && this.props.currentInFocusBlockIdx === i ?
            "14px" : "6px";
          return (
 this.reactPageScroller.goToPage(i)}
          />);
        })}

    );
  }
  renderBlocks() {
    const ret = this.props.blocks.map((b, i) => (
      <div
        id={`NarrativeBlock_${i}`}
        key={b.__html.slice(0, 50)}
        style={{
          padding: "10px 20px",
          height: "inherit",
          overflow: "hidden"
        }}
        dangerouslySetInnerHTML={b}
      />
    ));
    ret.push((

          END OF NARRATIVE

 this.reactPageScroller.goToPage(0)}>
          Click here to Scroll back to top

          Click here to Exit narrative mode

    ));
    return ret;
  }
  render() {
    if (!this.props.loaded) {return null;}
    return (

        {this.renderProgress()}
        {this.renderOpacityFade(true)}
        {this.renderOpacityFade(false)}
        {this.props.currentInFocusBlockIdx !== 0 ? this.renderChevron(true) : null}
        {!this.state.showingEndOfNarrativePage ? this.renderChevron(false) : null}
        <ReactPageScroller
          ref={(c) => {this.reactPageScroller = c;}}
          containerHeight={this.props.height-progressHeight}
          pageOnChange={this.changeAppStateViaBlock}
        >
          {this.renderBlocks()}
        </ReactPageScroller>

    );
  }
  componentWillUnmount() {
    this.props.dispatch({
      type: CHANGE_URL_QUERY_BUT_NOT_REDUX_STATE,
      pathname: this.props.blocks[this.props.currentInFocusBlockIdx].dataset,
      query: queryString.parse(this.props.blocks[this.props.currentInFocusBlockIdx].url)
    });
  }
}
export default Narrative;
<|endoftext|>


<commit_before>import React, { Component, PropTypes } from 'react'
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux'
import { Link } from 'react-router'
import { push, replace } from 'react-router-redux'
import classNames from 'classnames'
import { LOAD_STREAM_REQUEST } from '../../constants/action_types'
import { COMMENTS, POSTS } from '../../constants/mapping_types'
import { openModal, closeModal } from '../../actions/modals'
import * as postActions from '../../actions/posts'
import { trackEvent } from '../../actions/tracking'
import ConfirmDialog from '../dialogs/ConfirmDialog'
import FlagDialog from '../dialogs/FlagDialog'
import RegistrationRequestDialog from '../dialogs/RegistrationRequestDialog'
import ShareDialog from '../dialogs/ShareDialog'
import Hint from '../hints/Hint'
import {
  BubbleIcon,
  ChevronIcon,
  EyeIcon,
  FlagIcon,
  HeartIcon,
  PencilIcon,
  RepostIcon,
  ShareIcon,
  XBoxIcon,
} from '../posts/PostIcons'
import { numberToHuman } from '../../vendor/number_to_human'

class PostTools extends Component {

  static propTypes = {
    author: PropTypes.object.isRequired,
    dispatch: PropTypes.func.isRequired,
    isCommentsRequesting: PropTypes.bool,
    isGridLayout: PropTypes.bool,
    isLoggedIn: PropTypes.bool.isRequired,
    isOwnPost: PropTypes.bool.isRequired,
    isRepostAnimating: PropTypes.bool,
    pathname: PropTypes.string.isRequired,
    postCommentsCount: PropTypes.number.isRequired,
    postLoved: PropTypes.bool.isRequired,
    postLovesCount: PropTypes.number.isRequired,
    postReposted: PropTypes.bool.isRequired,
    postRepostsCount: PropTypes.number.isRequired,
    postShowComments: PropTypes.bool,
    postShowLovers: PropTypes.bool,
    postShowReposters: PropTypes.bool,
    postViewsCountRounded: PropTypes.string.isRequired,
    post: PropTypes.object.isRequired,
    previousPath: PropTypes.string,
  }

  componentWillMount() {
    this.state = {
      isMoreToolActive: false,
      isCommentsActive: false,
      isRepostAnimating: false,
      postShowComments: false,
      postShowLovers: false,
      postShowReposters: false,
    }
  }

  onClickMoreToggle = () => {
    this.setState({ isMoreToolActive: !this.state.isMoreToolActive })
  }

  onClickToggleComments = () => {
    const { dispatch, post, postShowComments } = this.props
    const nextShowComments = !postShowComments
    this.setState({ isCommentsActive: nextShowComments })
    dispatch(postActions.toggleComments(post, nextShowComments))
  }

  onClickLovePost = () => {
    const { dispatch, isLoggedIn, post, postLoved } = this.props
    if (!isLoggedIn) {
      this.signUp()
      return
    }
    if (postLoved) {
      dispatch(postActions.unlovePost(post))
    } else {
      dispatch(postActions.lovePost(post))
    }
  }

  onClickToggleLovers = () => {
    const { dispatch, isGridLayout, isLoggedIn, pathname, post, postShowLovers } = this.props
    if (!isLoggedIn) {
      this.signUp()
      return
    }
    const detailLink = this.getPostDetailLink()
    if (isGridLayout && pathname !== detailLink) {
      dispatch(push(detailLink))
    } else {
      const showLovers = !postShowLovers
      dispatch(postActions.toggleLovers(post, showLovers))
    }
  }

  onClickToggleReposters = () => {
    const { dispatch, isGridLayout, isLoggedIn, pathname, post, postShowReposters } = this.props
    if (!isLoggedIn) {
      this.signUp()
      return
    }
    const detailLink = this.getPostDetailLink()
    if (isGridLayout && pathname !== detailLink) {
      dispatch(push(detailLink))
    } else {
      const showReposters = !postShowReposters
      dispatch(postActions.toggleReposters(post, showReposters))
    }
  }

  onClickSharePost = () => {
    const { author, dispatch, post } = this.props
    const action = bindActionCreators(trackEvent, dispatch)
    dispatch(openModal(<ShareDialog author={ author } post={ post } trackEvent={ action } />))
    dispatch(trackEvent('open-share-dialog'))
  }

  onClickFlagPost = () => {
    const { dispatch } = this.props
    dispatch(openModal(
      <FlagDialog
        onResponse={ this.onPostWasFlagged }
        onConfirm={ this.closeModal }
      />))
  }

  onPostWasFlagged = ({ flag }) => {
    const { dispatch, post } = this.props
    dispatch(postActions.flagPost(post, flag))
  }

  onClickEditPost = () => {
    const { dispatch, post } = this.props
    dispatch(postActions.toggleEditing(post, true))
    dispatch(postActions.loadEditablePost(post.id))
  }

  onClickRepostPost = () => {
    const { dispatch, isLoggedIn, post, postReposted } = this.props
    if (!isLoggedIn) {
      this.signUp()
      return
    }
    if (!postReposted) {
      dispatch(postActions.toggleReposting(post, true))
      dispatch(postActions.loadEditablePost(post.id))
    }
  }

  onClickDeletePost = () => {
    const { dispatch } = this.props
    dispatch(openModal(
      <ConfirmDialog
        title="Delete Post?"
        onConfirm={ this.onCofirmDeletePost }
        onDismiss={ this.closeModal }
      />))
  }

  onCofirmDeletePost = () => {
    const { dispatch, pathname, post, previousPath } = this.props
    this.closeModal()
    dispatch(postActions.deletePost(post))
    if (pathname.match(post.token)) {
      dispatch(replace(previousPath || '/'))
    }
  }

  getPostDetailLink() {
    const { author, post } = this.props
    return `/${author.username}/post/${post.token}`
  }

  getToolCells() {
    const { author, isLoggedIn, isOwnPost, isRepostAnimating, post,
      postViewsCountRounded, postCommentsCount, postLovesCount, postLoved, postRepostsCount,
    } = this.props
    const cells = []
    cells.push(
      <span
        className={ classNames('PostTool', 'ViewsTool', { asPill: isLoggedIn }) }
        key={ `ViewsTool_${post.id}` }
      >
        <Link to={ this.getPostDetailLink() }>
          <EyeIcon />
          { postViewsCountRounded }
          <Hint>Views</Hint>
        </Link>

    )
    cells.push(
      <span className="PostTool TimeAgoTool" key={ `TimeAgoTool_${post.id}` }>
        <Link to={ this.getPostDetailLink() }>
          { new Date(post.createdAt).timeAgoInWords() }
          <Hint>Visit</Hint>
        </Link>

    )
    if (author.hasCommentingEnabled) {
      cells.push(
        <span
          className="PostTool CommentTool"
          data-count={ postCommentsCount }
          key={ `CommentTool_${post.id}` }
        >
          { isLoggedIn ?
            <button onClick={ this.onClickToggleComments } >
              <BubbleIcon />

                { numberToHuman(postCommentsCount, false) }

              <Hint>Comment</Hint>
            </button> :
            <Link to={ this.getPostDetailLink() }>
              <BubbleIcon />

                { numberToHuman(postCommentsCount, false) }

              <Hint>Comment</Hint>
            </Link>
          }

      )
    }
    if (author.hasLovesEnabled) {
      cells.push(
        <span
          className="PostTool LoveTool"
          data-count={ postLovesCount }
          key={ `LoveTool_${post.id}` }
        >
          <button
            className={ classNames({ active: postLoved, hasPostToolDrawer: postLovesCount > 0 }) }
            onClick={ this.onClickLovePost }
          >
            <HeartIcon />
            <Hint>Love</Hint>
          </button>
          <button
            className={ classNames({ active: postLoved }, 'PostToolDrawerButton') }
            onClick={ this.onClickToggleLovers }
          >

              { numberToHuman(postLovesCount, false) }

            <Hint>Loved by</Hint>
          </button>

      )
    }
    if (author.hasRepostingEnabled && !(isOwnPost && parseInt(postRepostsCount, 10) === 0)) {
      const repostIcon = <RepostIcon className={ classNames({ isRepostAnimating })} />
      cells.push(
        <span
          className="PostTool RepostTool"
          data-count={ postRepostsCount }
          key={ `RepostTool_${post.id}` }
        >
          <button
            className={ classNames({ hasPostToolDrawer: postRepostsCount > 0 }) }
            onClick={ !isOwnPost ? this.onClickRepostPost : null }
            style={{ pointerEvents: isOwnPost ? 'none' : null }}
          >
            { repostIcon }
            <Hint>Repost</Hint>
          </button>
          <button className="PostToolDrawerButton" onClick={ this.onClickToggleReposters }>

              { numberToHuman(postRepostsCount, false) }

            <Hint>Reposted by</Hint>
          </button>

      )
    }
    if (author.hasSharingEnabled) {
      cells.push(
        <span
          className={ classNames('PostTool', 'ShareTool', { asPill: !isLoggedIn }) }
          key={ `ShareTool_${post.id}` }
        >
          <button onClick={ this.onClickSharePost }>
            <ShareIcon />
            <Hint>Share</Hint>
          </button>

      )
    }
    if (isLoggedIn) {
      if (isOwnPost) {
        cells.push(
          <span className="PostTool EditTool ShyTool" key={ `EditTool_${post.id}` }>
            <button onClick={ this.onClickEditPost }>
              <PencilIcon />
              <Hint>Edit</Hint>
            </button>

        )
        cells.push(
          <span className="PostTool DeleteTool ShyTool" key={`DeleteTool_${post.id}`}>
            <button onClick={ this.onClickDeletePost }>
              <XBoxIcon />
              <Hint>Delete</Hint>
            </button>

        )
      } else {
        cells.push(
          <span className="PostTool FlagTool ShyTool" key={ `FlagTool_${post.id}` }>
            <button onClick={ this.onClickFlagPost }>
              <FlagIcon />
              <Hint>Flag</Hint>
            </button>

        )
      }
    }
    cells.push(
      <span className={ "PostTool MoreTool" } key={ `MoreTool_${post.id}` }>
        <button onClick={ this.onClickMoreToggle }>
          <ChevronIcon />
        </button>

    )
    return cells
  }

  closeModal = () => {
    const { dispatch } = this.props
    dispatch(closeModal())
  }

  signUp = () => {
    const { dispatch } = this.props
    dispatch(openModal(<RegistrationRequestDialog />, 'asDecapitated'))
    dispatch(trackEvent('open-registration-request-post-tools'))
  }

  render() {
    const { post, isCommentsRequesting } = this.props
    if (!post) { return null }
    const classes = classNames(
      'PostTools',
      { isCommentsRequesting },
      { isMoreToolActive: this.state.isMoreToolActive },
      { isCommentsActive: this.state.isCommentsActive },
    )
    return (

        { this.getToolCells() }

    )
  }
}

const mapStateToProps = ({ authentication, json, profile, routing, stream }, ownProps) => {
  const post = json[POSTS][ownProps.post.id]
  const isCommentsRequesting = stream.type === LOAD_STREAM_REQUEST &&
                               stream.meta.mappingType === COMMENTS &&
                               (stream.payload.postIdOrToken === ownProps.post.id ||
                                stream.payload.postIdOrToken === ownProps.post.token)
  return {
    isCommentsRequesting,
    isLoggedIn: authentication.isLoggedIn,
    isOwnPost: profile && ownProps.post.authorId === profile.id,
    pathname: routing.location.pathname,
    previousPath: routing.previousPath,
    postCommentsCount: post.commentsCount,
    postLoved: post.loved,
    postLovesCount: post.lovesCount,
    postReposted: post.reposted,
    postRepostsCount: post.repostsCount,
    postShowComments: post.showComments,
    postShowLovers: post.showLovers,
    postShowReposters: post.showReposters,
    postViewsCountRounded: post.viewsCountRounded,
  }
}

export default connect(mapStateToProps)(PostTools)

<commit_msg>disable link if already reposted<commit_after>import React, { Component, PropTypes } from 'react'
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux'
import { Link } from 'react-router'
import { push, replace } from 'react-router-redux'
import classNames from 'classnames'
import { LOAD_STREAM_REQUEST } from '../../constants/action_types'
import { COMMENTS, POSTS } from '../../constants/mapping_types'
import { openModal, closeModal } from '../../actions/modals'
import * as postActions from '../../actions/posts'
import { trackEvent } from '../../actions/tracking'
import ConfirmDialog from '../dialogs/ConfirmDialog'
import FlagDialog from '../dialogs/FlagDialog'
import RegistrationRequestDialog from '../dialogs/RegistrationRequestDialog'
import ShareDialog from '../dialogs/ShareDialog'
import Hint from '../hints/Hint'
import {
  BubbleIcon,
  ChevronIcon,
  EyeIcon,
  FlagIcon,
  HeartIcon,
  PencilIcon,
  RepostIcon,
  ShareIcon,
  XBoxIcon,
} from '../posts/PostIcons'
import { numberToHuman } from '../../vendor/number_to_human'

class PostTools extends Component {

  static propTypes = {
    author: PropTypes.object.isRequired,
    dispatch: PropTypes.func.isRequired,
    isCommentsRequesting: PropTypes.bool,
    isGridLayout: PropTypes.bool,
    isLoggedIn: PropTypes.bool.isRequired,
    isOwnPost: PropTypes.bool.isRequired,
    isRepostAnimating: PropTypes.bool,
    pathname: PropTypes.string.isRequired,
    postCommentsCount: PropTypes.number.isRequired,
    postLoved: PropTypes.bool.isRequired,
    postLovesCount: PropTypes.number.isRequired,
    postReposted: PropTypes.bool.isRequired,
    postRepostsCount: PropTypes.number.isRequired,
    postShowComments: PropTypes.bool,
    postShowLovers: PropTypes.bool,
    postShowReposters: PropTypes.bool,
    postViewsCountRounded: PropTypes.string.isRequired,
    post: PropTypes.object.isRequired,
    previousPath: PropTypes.string,
  }

  componentWillMount() {
    this.state = {
      isMoreToolActive: false,
      isCommentsActive: false,
      isRepostAnimating: false,
      postShowComments: false,
      postShowLovers: false,
      postShowReposters: false,
    }
  }

  onClickMoreToggle = () => {
    this.setState({ isMoreToolActive: !this.state.isMoreToolActive })
  }

  onClickToggleComments = () => {
    const { dispatch, post, postShowComments } = this.props
    const nextShowComments = !postShowComments
    this.setState({ isCommentsActive: nextShowComments })
    dispatch(postActions.toggleComments(post, nextShowComments))
  }

  onClickLovePost = () => {
    const { dispatch, isLoggedIn, post, postLoved } = this.props
    if (!isLoggedIn) {
      this.signUp()
      return
    }
    if (postLoved) {
      dispatch(postActions.unlovePost(post))
    } else {
      dispatch(postActions.lovePost(post))
    }
  }

  onClickToggleLovers = () => {
    const { dispatch, isGridLayout, isLoggedIn, pathname, post, postShowLovers } = this.props
    if (!isLoggedIn) {
      this.signUp()
      return
    }
    const detailLink = this.getPostDetailLink()
    if (isGridLayout && pathname !== detailLink) {
      dispatch(push(detailLink))
    } else {
      const showLovers = !postShowLovers
      dispatch(postActions.toggleLovers(post, showLovers))
    }
  }

  onClickToggleReposters = () => {
    const { dispatch, isGridLayout, isLoggedIn, pathname, post, postShowReposters } = this.props
    if (!isLoggedIn) {
      this.signUp()
      return
    }
    const detailLink = this.getPostDetailLink()
    if (isGridLayout && pathname !== detailLink) {
      dispatch(push(detailLink))
    } else {
      const showReposters = !postShowReposters
      dispatch(postActions.toggleReposters(post, showReposters))
    }
  }

  onClickSharePost = () => {
    const { author, dispatch, post } = this.props
    const action = bindActionCreators(trackEvent, dispatch)
    dispatch(openModal(<ShareDialog author={ author } post={ post } trackEvent={ action } />))
    dispatch(trackEvent('open-share-dialog'))
  }

  onClickFlagPost = () => {
    const { dispatch } = this.props
    dispatch(openModal(
      <FlagDialog
        onResponse={ this.onPostWasFlagged }
        onConfirm={ this.closeModal }
      />))
  }

  onPostWasFlagged = ({ flag }) => {
    const { dispatch, post } = this.props
    dispatch(postActions.flagPost(post, flag))
  }

  onClickEditPost = () => {
    const { dispatch, post } = this.props
    dispatch(postActions.toggleEditing(post, true))
    dispatch(postActions.loadEditablePost(post.id))
  }

  onClickRepostPost = () => {
    const { dispatch, isLoggedIn, post, postReposted } = this.props
    if (!isLoggedIn) {
      this.signUp()
      return
    }
    if (!postReposted) {
      dispatch(postActions.toggleReposting(post, true))
      dispatch(postActions.loadEditablePost(post.id))
    }
  }

  onClickDeletePost = () => {
    const { dispatch } = this.props
    dispatch(openModal(
      <ConfirmDialog
        title="Delete Post?"
        onConfirm={ this.onCofirmDeletePost }
        onDismiss={ this.closeModal }
      />))
  }

  onCofirmDeletePost = () => {
    const { dispatch, pathname, post, previousPath } = this.props
    this.closeModal()
    dispatch(postActions.deletePost(post))
    if (pathname.match(post.token)) {
      dispatch(replace(previousPath || '/'))
    }
  }

  getPostDetailLink() {
    const { author, post } = this.props
    return `/${author.username}/post/${post.token}`
  }

  getToolCells() {
    const { author, isLoggedIn, isOwnPost, isRepostAnimating, post,
      postReposted, postRepostsCount,
      postLoved, postLovesCount,
      postViewsCountRounded, postCommentsCount,
    } = this.props
    const cells = []
    cells.push(
      <span
        className={ classNames('PostTool', 'ViewsTool', { asPill: isLoggedIn }) }
        key={ `ViewsTool_${post.id}` }
      >
        <Link to={ this.getPostDetailLink() }>
          <EyeIcon />
          { postViewsCountRounded }
          <Hint>Views</Hint>
        </Link>

    )
    cells.push(
      <span className="PostTool TimeAgoTool" key={ `TimeAgoTool_${post.id}` }>
        <Link to={ this.getPostDetailLink() }>
          { new Date(post.createdAt).timeAgoInWords() }
          <Hint>Visit</Hint>
        </Link>

    )
    if (author.hasCommentingEnabled) {
      cells.push(
        <span
          className="PostTool CommentTool"
          data-count={ postCommentsCount }
          key={ `CommentTool_${post.id}` }
        >
          { isLoggedIn ?
            <button onClick={ this.onClickToggleComments } >
              <BubbleIcon />

                { numberToHuman(postCommentsCount, false) }

              <Hint>Comment</Hint>
            </button> :
            <Link to={ this.getPostDetailLink() }>
              <BubbleIcon />

                { numberToHuman(postCommentsCount, false) }

              <Hint>Comment</Hint>
            </Link>
          }

      )
    }
    if (author.hasLovesEnabled) {
      cells.push(
        <span
          className="PostTool LoveTool"
          data-count={ postLovesCount }
          key={ `LoveTool_${post.id}` }
        >
          <button
            className={ classNames({ active: postLoved, hasPostToolDrawer: postLovesCount > 0 }) }
            onClick={ this.onClickLovePost }
          >
            <HeartIcon />
            <Hint>Love</Hint>
          </button>
          <button
            className={ classNames({ active: postLoved }, 'PostToolDrawerButton') }
            onClick={ this.onClickToggleLovers }
          >

              { numberToHuman(postLovesCount, false) }

            <Hint>Loved by</Hint>
          </button>

      )
    }
    if (author.hasRepostingEnabled && !(isOwnPost && parseInt(postRepostsCount, 10) === 0)) {
      const repostIcon = <RepostIcon className={ classNames({ isRepostAnimating })} />
      cells.push(
        <span
          className="PostTool RepostTool"
          data-count={ postRepostsCount }
          key={ `RepostTool_${post.id}` }
        >
          <button
            className={ classNames({ hasPostToolDrawer: postRepostsCount > 0 }) }
            onClick={ !isOwnPost ? this.onClickRepostPost : null }
            style={{ pointerEvents: isOwnPost || postReposted ? 'none' : null }}
          >
            { repostIcon }
            <Hint>Repost</Hint>
          </button>
          <button className="PostToolDrawerButton" onClick={ this.onClickToggleReposters }>

              { numberToHuman(postRepostsCount, false) }

            <Hint>Reposted by</Hint>
          </button>

      )
    }
    if (author.hasSharingEnabled) {
      cells.push(
        <span
          className={ classNames('PostTool', 'ShareTool', { asPill: !isLoggedIn }) }
          key={ `ShareTool_${post.id}` }
        >
          <button onClick={ this.onClickSharePost }>
            <ShareIcon />
            <Hint>Share</Hint>
          </button>

      )
    }
    if (isLoggedIn) {
      if (isOwnPost) {
        cells.push(
          <span className="PostTool EditTool ShyTool" key={ `EditTool_${post.id}` }>
            <button onClick={ this.onClickEditPost }>
              <PencilIcon />
              <Hint>Edit</Hint>
            </button>

        )
        cells.push(
          <span className="PostTool DeleteTool ShyTool" key={`DeleteTool_${post.id}`}>
            <button onClick={ this.onClickDeletePost }>
              <XBoxIcon />
              <Hint>Delete</Hint>
            </button>

        )
      } else {
        cells.push(
          <span className="PostTool FlagTool ShyTool" key={ `FlagTool_${post.id}` }>
            <button onClick={ this.onClickFlagPost }>
              <FlagIcon />
              <Hint>Flag</Hint>
            </button>

        )
      }
    }
    cells.push(
      <span className={ "PostTool MoreTool" } key={ `MoreTool_${post.id}` }>
        <button onClick={ this.onClickMoreToggle }>
          <ChevronIcon />
        </button>

    )
    return cells
  }

  closeModal = () => {
    const { dispatch } = this.props
    dispatch(closeModal())
  }

  signUp = () => {
    const { dispatch } = this.props
    dispatch(openModal(<RegistrationRequestDialog />, 'asDecapitated'))
    dispatch(trackEvent('open-registration-request-post-tools'))
  }

  render() {
    const { post, isCommentsRequesting } = this.props
    if (!post) { return null }
    const classes = classNames(
      'PostTools',
      { isCommentsRequesting },
      { isMoreToolActive: this.state.isMoreToolActive },
      { isCommentsActive: this.state.isCommentsActive },
    )
    return (

        { this.getToolCells() }

    )
  }
}

const mapStateToProps = ({ authentication, json, profile, routing, stream }, ownProps) => {
  const post = json[POSTS][ownProps.post.id]
  const isCommentsRequesting = stream.type === LOAD_STREAM_REQUEST &&
                               stream.meta.mappingType === COMMENTS &&
                               (stream.payload.postIdOrToken === ownProps.post.id ||
                                stream.payload.postIdOrToken === ownProps.post.token)
  return {
    isCommentsRequesting,
    isLoggedIn: authentication.isLoggedIn,
    isOwnPost: profile && ownProps.post.authorId === profile.id,
    pathname: routing.location.pathname,
    previousPath: routing.previousPath,
    postCommentsCount: post.commentsCount,
    postLoved: post.loved,
    postLovesCount: post.lovesCount,
    postReposted: post.reposted,
    postRepostsCount: post.repostsCount,
    postShowComments: post.showComments,
    postShowLovers: post.showLovers,
    postShowReposters: post.showReposters,
    postViewsCountRounded: post.viewsCountRounded,
  }
}

export default connect(mapStateToProps)(PostTools)

<|endoftext|>


<commit_before>import Paper from 'material-ui/Paper';
import { List, ListItem } from 'material-ui/List';

export class Recipes extends React.Component {
  componentWillMount () {
    fetch('/api/recipes')
      .then(response => response.json())
      .then(json => this.props.onRecipesLoad(json.recipes));
  }

  render () {
    return (
      <Paper zDepth={3}>
        <List>
          {
            this.props.recipes.map(recipe => <ListItem key={recipe.id} primaryText={recipe.name} />)
          }
        </List>
      </Paper>
    );
  }
}
<commit_msg>change recipe list to grid view<commit_after>import { GridList, GridTile } from 'material-ui/GridList';

export class Recipes extends React.Component {
  componentWillMount () {
    fetch('/api/recipes')
      .then(response => response.json())
      .then(json => this.props.onRecipesLoad(json.recipes));
  }

  render () {
    return (

        <GridList cellHeight={180} >
          {this.props.recipes.map((recipe) => (
            <GridTile key={recipe.id} title={recipe.name} subtitle={by {recipe.author}} >

            </GridTile>
          ))}
        </GridList>

    );
  }
}
<|endoftext|>


<commit_before>import React, {Component, PropTypes} from 'react'
import Immutable from 'immutable'
import cx from 'classnames'
import {oxford} from 'humanize-plus'
import plur from 'plur'
import sample from 'lodash/collection/sample'

import AvatarLetter from './avatar-letter'
import ContentEditable from './content-editable'

import studentActions from '../flux/student-actions'
import Student from '../models/student'

import countCredits from '../lib/count-credits'

import './student-summary.scss'

const goodGraduationMessage = "It looks like you'll make it! Just follow the plan, and go over my output with your advisor a few times."
const badGraduationMessage = "You haven't planned everything out yet. Ask your advisor if you need help fitting everything in."

const welcomeMessages = [
	'Hi, ',
	'Hi there, ',
	'Hello, ',
	'Konnichiwa, ',
	'こんにちは、',
]

export default class StudentSummary extends Component {
	static propTypes = {
		courses: PropTypes.instanceOf(Immutable.List),
		coursesLoaded: PropTypes.bool.isRequired,
		graduatability: PropTypes.bool.isRequired,
		student: PropTypes.instanceOf(Student).isRequired,
	}

	constructor() {
		super()
		this.state = {
			welcome: sample(welcomeMessages),
		}
	}

	render() {
		const canGraduate = this.props.graduatability
		const student = this.props.student
		const studies = student.studies

		const NameEl = (
			<ContentEditable
				className='autosize-input'
				onChange={ev => studentActions.changeName(student.id, ev.target.value)}
				value={String(student.name)}
			/>
		)

		const degrees = studies.filter(s => s.type === 'degree')
		const majors = studies.filter(s => s.type === 'major')
		const concentrations = studies.filter(s => s.type === 'concentration')
		const emphases = studies.filter(s => s.type === 'emphasis')

		const degreeWord = plur('degree', degrees.size)
		const majorWord = plur('major', majors.size)
		const concentrationWord = plur('concentration', concentrations.size)
		const emphasisWord = plur('emphasis', 'emphases', emphases.size)

		const degreeEmphasizer = (degrees.size === 1) ? 'a ' : ''
		const majorEmphasizer = (majors.size === 1) ? 'a ' : ''
		const concentrationEmphasizer = (concentrations.size === 1) ? 'a ' : ''
		const emphasisEmphasizer = (emphases.size === 1) ? 'an ' : ''

		const degreeList = oxford(degrees.map(s => s.name).toArray())
		const majorList = oxford(majors.map(s => s.name).toArray())
		const concentrationList = oxford(concentrations.map(s => s.name).toArray())
		const emphasisList = oxford(emphases.map(s => s.name).toArray())

		const graduationEl = (
			<ContentEditable
				className='autosize-input'
				onChange={ev => studentActions.changeGraduation(student.id, parseInt(ev.target.value || 0))}
				value={String(student.graduation)}
			/>
		)

		const matriculationEl = (
			<ContentEditable
				className='autosize-input'
				onChange={ev => studentActions.changeMatriculation(student.id, parseInt(ev.target.value || 0))}
				value={String(student.matriculation)}
			/>
		)

		return (

					<AvatarLetter
						className={cx(
							'student-letter',
							this.props.graduatability
								? 'can-graduate'
								: 'cannot-graduate'
						)}
						value={student.name}
					/>

{this.state.welcome}{NameEl}!

						After matriculating in {matriculationEl}, you are planning to graduate in {graduationEl}, with {' '}
						{(degrees.size > 0) ? `${degreeEmphasizer}${degreeList} ${degreeWord}` : `no ${degreeWord}`}
						{(majors.size) && (concentrations.size || emphases.size) ? ', ' : ' and '}
						{(majors.size > 0) && `${majorEmphasizer}${majorWord} in ${majorList}`}
						{(majors.size && concentrations.size) ? ', and ' : ''}
						{(concentrations.size > 0) && `${concentrationEmphasizer}${concentrationWord} in ${concentrationList}`}
						{((majors.size || concentrations.size) && emphases.size) ? ', ' : ''}
						{(emphases.size > 0) && `not to mention ${emphasisEmphasizer}${emphasisWord} in ${emphasisList}`}
						{'. '}
						{this.props.coursesLoaded && `You have currently planned for ${countCredits(this.props.courses)} of your ${student.creditsNeeded} required credits.`}

						{canGraduate ? goodGraduationMessage : badGraduationMessage}

		)
	}
}
<commit_msg>add a :thumbsup: emoji to the successful credit check<commit_after>import React, {Component, PropTypes} from 'react'
import Immutable from 'immutable'
import cx from 'classnames'
import {oxford} from 'humanize-plus'
import plur from 'plur'
import sample from 'lodash/collection/sample'

import AvatarLetter from './avatar-letter'
import ContentEditable from './content-editable'

import studentActions from '../flux/student-actions'
import Student from '../models/student'

import countCredits from '../lib/count-credits'

import './student-summary.scss'

const goodGraduationMessage = "It looks like you'll make it! Just follow the plan, and go over my output with your advisor a few times."
const badGraduationMessage = "You haven't planned everything out yet. Ask your advisor if you need help fitting everything in."

const welcomeMessages = [
	'Hi, ',
	'Hi there, ',
	'Hello, ',
	'Konnichiwa, ',
	'こんにちは、',
]

export default class StudentSummary extends Component {
	static propTypes = {
		courses: PropTypes.instanceOf(Immutable.List),
		coursesLoaded: PropTypes.bool.isRequired,
		graduatability: PropTypes.bool.isRequired,
		student: PropTypes.instanceOf(Student).isRequired,
	}

	constructor() {
		super()
		this.state = {
			welcome: sample(welcomeMessages),
		}
	}

	render() {
		const canGraduate = this.props.graduatability
		const student = this.props.student
		const studies = student.studies

		const NameEl = (
			<ContentEditable
				className='autosize-input'
				onChange={ev => studentActions.changeName(student.id, ev.target.value)}
				value={String(student.name)}
			/>
		)

		const degrees = studies.filter(s => s.type === 'degree')
		const majors = studies.filter(s => s.type === 'major')
		const concentrations = studies.filter(s => s.type === 'concentration')
		const emphases = studies.filter(s => s.type === 'emphasis')

		const degreeWord = plur('degree', degrees.size)
		const majorWord = plur('major', majors.size)
		const concentrationWord = plur('concentration', concentrations.size)
		const emphasisWord = plur('emphasis', 'emphases', emphases.size)

		const degreeEmphasizer = (degrees.size === 1) ? 'a ' : ''
		const majorEmphasizer = (majors.size === 1) ? 'a ' : ''
		const concentrationEmphasizer = (concentrations.size === 1) ? 'a ' : ''
		const emphasisEmphasizer = (emphases.size === 1) ? 'an ' : ''

		const degreeList = oxford(degrees.map(s => s.name).toArray())
		const majorList = oxford(majors.map(s => s.name).toArray())
		const concentrationList = oxford(concentrations.map(s => s.name).toArray())
		const emphasisList = oxford(emphases.map(s => s.name).toArray())

		const currentCredits = countCredits(this.props.courses)
		const neededCredits = student.creditsNeeded
		const enoughCredits = currentCredits >= neededCredits

		const graduationEl = (
			<ContentEditable
				className='autosize-input'
				onChange={ev => studentActions.changeGraduation(student.id, parseInt(ev.target.value || 0))}
				value={String(student.graduation)}
			/>
		)

		const matriculationEl = (
			<ContentEditable
				className='autosize-input'
				onChange={ev => studentActions.changeMatriculation(student.id, parseInt(ev.target.value || 0))}
				value={String(student.matriculation)}
			/>
		)

		return (

					<AvatarLetter
						className={cx(
							'student-letter',
							this.props.graduatability
								? 'can-graduate'
								: 'cannot-graduate'
						)}
						value={student.name}
					/>

{this.state.welcome}{NameEl}!

						After matriculating in {matriculationEl}, you are planning to graduate in {graduationEl}, with {' '}
						{(degrees.size > 0) ? `${degreeEmphasizer}${degreeList} ${degreeWord}` : `no ${degreeWord}`}
						{(majors.size) && (concentrations.size || emphases.size) ? ', ' : ' and '}
						{(majors.size > 0) && `${majorEmphasizer}${majorWord} in ${majorList}`}
						{(majors.size && concentrations.size) ? ', and ' : ''}
						{(concentrations.size > 0) && `${concentrationEmphasizer}${concentrationWord} in ${concentrationList}`}
						{((majors.size || concentrations.size) && emphases.size) ? ', ' : ''}
						{(emphases.size > 0) && `not to mention ${emphasisEmphasizer}${emphasisWord} in ${emphasisList}`}
						{'. '}
						{this.props.coursesLoaded && `You have currently planned for ${currentCredits} of your ${neededCredits} required credits. ${enoughCredits ? '👍' : ''}`}

						{canGraduate ? goodGraduationMessage : badGraduationMessage}

		)
	}
}
<|endoftext|>


<commit_before>var AlexaAppServer = require('alexa-app-server');
var alexa = require('alexa-app');
var SpotifyWebApi = require('spotify-web-api-node');
var request = require('request');
var randomstring = require('randomstring');
var express = require('express');
var cookieParser = require('cookie-parser');
var querystring = require('querystring');

var express_app = express();

var app = new alexa.app('connect');
app.express({ expressApp: express_app });

var spotifyApi = new SpotifyWebApi({
    clientId: '8f8e09d45e574e07b0a040bd70c95fb6',
    clientSecret: process.env.client_secret,
    redirectUri: 'http://localhost:8888/callback'
});

app.intent('PlayIntent', {
    "utterances": [
        "play",
        "resume",
        "continue"
    ]
},
    function (req, res) {
        request.put("https://api.spotify.com/v1/me/player/play").auth(null, null, true, req.sessionDetails.accessToken);
        res.say('Playing');
    }
);

app.intent('PauseIntent', {
    "utterances": [
        "pause"
    ]
},
    function (req, res) {
        request.put("https://api.spotify.com/v1/me/player/pause").auth(null, null, true, req.sessionDetails.accessToken);
        res.say('Paused');
    }
);

app.intent('GetDevicesIntent', {
    "utterances": [
        "get devices"
    ]
},
    function (req, res) {
        request.get({
            url: "https://api.spotify.com/v1/me/player/devices",
            auth: {
                "bearer": req.sessionDetails.accessToken
            },
            json: true
        },
            function (error, response, body) {
                if (error != null) console.log('error:', error);
                var devices = body.devices;
                res.say("I found these devices");
                for (var i = 0; i < devices.length; i++) {
                    res.say(devices[i].name);
                }
            });
    }
);

express_app.use(express.static(__dirname));

express_app.get('/login', function (req, res) {
    var scopes = ['user-read-playback-state', 'user-modify-playback-state'];
    var state = randomstring.generate(16);
    res.cookie('spotify_auth_state', state);
    var authorizeURL = spotifyApi.createAuthorizeURL(scopes, state);
    res.redirect(authorizeURL);
});

express_app.get('/callback', function (req, res) {

    var code = req.query.code;
    var state = req.query.state;

    spotifyApi.authorizationCodeGrant(code)
        .then(function (data) {
            console.log('The token expires in ' + data.body['expires_in']);
            console.log('The access token is ' + data.body['access_token']);
            console.log('The refresh token is ' + data.body['refresh_token']);

            // Set the access token on the API object to use it in later calls
            spotifyApi.setAccessToken(data.body['access_token']);
            spotifyApi.setRefreshToken(data.body['refresh_token']);
            res.redirect('/#' + querystring.stringify({ access_token: data.body['access_token'], refresh_token: data.body['refresh_token'] }));
        }, function (err) {
            console.log('Something went wrong!', err);
        });
});

express_app.get('/refresh_token', function (req, res) {
    spotifyApi.refreshAccessToken()
        .then(function (data) {
            console.log('The access token has been refreshed!');
            console.log('The access token is ' + data.body['access_token']);

            // Save the access token so that it's used in future calls
            spotifyApi.setAccessToken(data.body['access_token']);
            res.redirect('/#' + querystring.stringify({ access_token: data.body['access_token'] }));
        }, function (err) {
            console.log('Could not refresh access token', err);
        });
});

express_app.post('/playpause', function (req, res) {
    console.log("PLAYPAUSE");
    //var playing = request.get("https://api.spotify.com/v1/me/player", { "auth":{"bearer":spotifyApi.getAccessToken()}, json: true });
    if (playing) {
        request.put("https://api.spotify.com/v1/me/player/pause").auth(null, null, true, spotifyApi.getAccessToken())
    }
    else {
        request.put("https://api.spotify.com/v1/me/player/play").auth(null, null, true, spotifyApi.getAccessToken())
    }
});

express_app.post('/getdevices', function (req, res) {
    // spotifyApi.getMyDevices()
    //     .then(function (data) {
    //         var devices = data.body.devices;
    //         for (var i = 0; i < devices.length; i++) {
    //             console.log(devices[i].id + ":" + devices[i].name + ":" + devices[i].volume_percent);
    //         }
    //     }, function (err) {
    //         console.error(err);
    //     });
});

var port = process.env.PORT || 8888;
console.log("Listening on port " + port);
express_app.listen(port);

module.exports = app;
<commit_msg>Add utterances for device list<commit_after>var alexa = require('alexa-app');
var SpotifyWebApi = require('spotify-web-api-node');
var request = require('request');
var randomstring = require('randomstring');
var express = require('express');
var cookieParser = require('cookie-parser');
var querystring = require('querystring');

var express_app = express();

var app = new alexa.app('connect');
app.express({ expressApp: express_app });

var spotifyApi = new SpotifyWebApi({
    clientId: '8f8e09d45e574e07b0a040bd70c95fb6',
    clientSecret: process.env.client_secret,
    redirectUri: 'http://localhost:8888/callback'
});

app.intent('PlayIntent', {
    "utterances": [
        "play",
        "resume",
        "continue"
    ]
},
    function (req, res) {
        request.put("https://api.spotify.com/v1/me/player/play").auth(null, null, true, req.sessionDetails.accessToken);
        res.say('Playing');
    }
);

app.intent('PauseIntent', {
    "utterances": [
        "pause"
    ]
},
    function (req, res) {
        request.put("https://api.spotify.com/v1/me/player/pause").auth(null, null, true, req.sessionDetails.accessToken);
        res.say('Paused');
    }
);

app.intent('GetDevicesIntent', {
    "utterances": [
        "devices",
        "list"
    ]
},
    function (req, res) {
        request.get({
            url: "https://api.spotify.com/v1/me/player/devices",
            auth: {
                "bearer": req.sessionDetails.accessToken
            },
            json: true
        },
            function (error, response, body) {
                if (error != null) console.log('error:', error);
                var devices = body.devices;
                res.say("I found these devices");
                for (var i = 0; i < devices.length; i++) {
                    res.say(devices[i].name);
                }
            });
    }
);

express_app.use(express.static(__dirname));

express_app.get('/login', function (req, res) {
    var scopes = ['user-read-playback-state', 'user-modify-playback-state'];
    var state = randomstring.generate(16);
    res.cookie('spotify_auth_state', state);
    var authorizeURL = spotifyApi.createAuthorizeURL(scopes, state);
    res.redirect(authorizeURL);
});

express_app.get('/callback', function (req, res) {

    var code = req.query.code;
    var state = req.query.state;

    spotifyApi.authorizationCodeGrant(code)
        .then(function (data) {
            console.log('The token expires in ' + data.body['expires_in']);
            console.log('The access token is ' + data.body['access_token']);
            console.log('The refresh token is ' + data.body['refresh_token']);

            // Set the access token on the API object to use it in later calls
            spotifyApi.setAccessToken(data.body['access_token']);
            spotifyApi.setRefreshToken(data.body['refresh_token']);
            res.redirect('/#' + querystring.stringify({ access_token: data.body['access_token'], refresh_token: data.body['refresh_token'] }));
        }, function (err) {
            console.log('Something went wrong!', err);
        });
});

express_app.get('/refresh_token', function (req, res) {
    spotifyApi.refreshAccessToken()
        .then(function (data) {
            console.log('The access token has been refreshed!');
            console.log('The access token is ' + data.body['access_token']);

            // Save the access token so that it's used in future calls
            spotifyApi.setAccessToken(data.body['access_token']);
            res.redirect('/#' + querystring.stringify({ access_token: data.body['access_token'] }));
        }, function (err) {
            console.log('Could not refresh access token', err);
        });
});

express_app.post('/playpause', function (req, res) {
    console.log("PLAYPAUSE");
    //var playing = request.get("https://api.spotify.com/v1/me/player", { "auth":{"bearer":spotifyApi.getAccessToken()}, json: true });
    if (playing) {
        request.put("https://api.spotify.com/v1/me/player/pause").auth(null, null, true, spotifyApi.getAccessToken())
    }
    else {
        request.put("https://api.spotify.com/v1/me/player/play").auth(null, null, true, spotifyApi.getAccessToken())
    }
});

express_app.post('/getdevices', function (req, res) {
    // spotifyApi.getMyDevices()
    //     .then(function (data) {
    //         var devices = data.body.devices;
    //         for (var i = 0; i < devices.length; i++) {
    //             console.log(devices[i].id + ":" + devices[i].name + ":" + devices[i].volume_percent);
    //         }
    //     }, function (err) {
    //         console.error(err);
    //     });
});

var port = process.env.PORT || 8888;
console.log("Listening on port " + port);
express_app.listen(port);

module.exports = app;
<|endoftext|>


<commit_before>/**
 * Created by lrollus on 7/14/14.
 */
angular.module("sampleApp")
    .factory("cytomineService",function($http,projectUrl) {

        var publicKey;
        var privateKey;

        return {

            getPublicKey : function() {
                return publicKey; //"29f51819-3dc6-468c-8aa7-9c81b9bc236b";
            },
            getPrivateKey : function() {
                return privateKey; //"db214699-0384-498c-823f-801654238a67";
            },

            setKeys : function(publicK,privateK) {
                console.log("set key="+publicK + " " + privateK);
                publicKey = publicK;
                privateKey = privateK;
            },

            addKeys : function(url) {
                url = url + ((url.indexOf("?")==-1)? "?" : "&");
                url = url + "publicKey={publicKey}&privateKey={privateKey}";
                url = url.replace("{publicKey}",this.getPublicKey());
                url = url.replace("{privateKey}",this.getPrivateKey());
                return url;
            }
        };
    });<commit_msg>clean<commit_after>/**
 * Created by lrollus on 7/14/14.
 */
angular.module("sampleApp")
    .factory("cytomineService",function($http,projectUrl) {

        var publicKey;
        var privateKey;

        return {

            getPublicKey : function() {
                return publicKey;
            },
            getPrivateKey : function() {
                return privateKey;
            },

            setKeys : function(publicK,privateK) {
                console.log("set key="+publicK + " " + privateK);
                publicKey = publicK;
                privateKey = privateK;
            },

            addKeys : function(url) {
                url = url + ((url.indexOf("?")==-1)? "?" : "&");
                url = url + "publicKey={publicKey}&privateKey={privateKey}";
                url = url.replace("{publicKey}",this.getPublicKey());
                url = url.replace("{privateKey}",this.getPrivateKey());
                return url;
            }
        };
    });
<|endoftext|>


<commit_before>var _currentPageURL;
var contentPageLinksArray;

//document.addEventListener("DOMContentLoaded", ready);
//function ready (){

//option 2 to try
window.onload = function() {
    console.log( "Document ready!" );
    _currentPageURL = window.location.href;
    contentPageLinksArray = [];
    addAllLinksToContentPageLinksArray();
    console.log("sending message to background: Should I test?");
    chrome.runtime.sendMessage({greeting: "Should I test?", currentPageLinks: contentPageLinksArray, currentPageURL: _currentPageURL}, function(response) {
      console.log("shouldITest response is: " + response.shouldITest);
      console.log("test for: " + response.testFor);
      console.log("next page is: " + response.nextPage);
      if (response.shouldITest === "No"){
        setUpListener();
        return null;
      }
      else{
        testPage(response.testFor);
        if (response.nextPage !== "Stop"){
        loadNextPage(response.nextPage);
      } else {
            chrome.runtime.sendMessage({greeting: "Should I test?", currentPageLinks: []});
      }
      }
    });
};

 function setUpListener(){
        console.log("setUpListener called");
        chrome.runtime.onMessage.addListener(
        function(request, sender, sendResponse) {
        sendResponse("test started");
        console.log("extension called from background");
        console.log("greeting is " + request.greeting);
        console.log("testFor is " + request.testFor);
        console.log("nextPage is " + request.nextPage);
        testPage(request.testFor);
        if (request.nextPage !== null){
          console.log("next page is not stop");
          loadNextPage(request.nextPage);
        }
      });
}

function testPage(error){
  let isError = $( "*:contains('" + error + "')" );
    for (let j = 0; j < isError.length; i++) {
        if ($(isError[j]).is(':visible')){
            reportErrorToBackground();
            return;
        }
    }
}

$.expr[":"].contains = $.expr.createPseudo(function(arg) {
  return function( elem ) {
   return $(elem).text().toUpperCase().indexOf(arg.toUpperCase()) >= 0;
  };
});

function loadNextPage(_nextPage){
  window.location.assign( _nextPage );
  console.log("passed location.assignn");
  setTimeout(function(){console.log("waited 1000")}, 1000);
  }

function reportErrorToBackground(){
  let currentPageURL = window.location.href;
  chrome.runtime.sendMessage({errorPage: currentPageURL, greeting: "Sending error"});
}

function addAllLinksToContentPageLinksArray(){
  console.log("addAllLinksToContentPageLinksArray called");
  $("a:visible").each(function() {
    let _thisHref = this.href;
    if (_thisHref.includes("#!") === false){
      if (_thisHref.includes("#") ){
          _thisHref = _thisHref.split("#")[0];
        }
      }
    contentPageLinksArray.push(_thisHref);
  });
}
<commit_msg>reverted back to on document ready in the content script as the visibility checks should take care of the false positive results<commit_after>var _currentPageURL;
var contentPageLinksArray;

//document.addEventListener("DOMContentLoaded", ready);
//function ready (){

//option 2 to try
//window.onload = function() {
$( document ).ready(function() {
    console.log( "Document ready!" );
    _currentPageURL = window.location.href;
    contentPageLinksArray = [];
    addAllLinksToContentPageLinksArray();
    console.log("sending message to background: Should I test?");
    chrome.runtime.sendMessage({greeting: "Should I test?", currentPageLinks: contentPageLinksArray, currentPageURL: _currentPageURL}, function(response) {
      console.log("shouldITest response is: " + response.shouldITest);
      console.log("test for: " + response.testFor);
      console.log("next page is: " + response.nextPage);
      if (response.shouldITest === "No"){
        setUpListener();
        return null;
      }
      else{
        testPage(response.testFor);
        if (response.nextPage !== "Stop"){
        loadNextPage(response.nextPage);
      } else {
            chrome.runtime.sendMessage({greeting: "Should I test?", currentPageLinks: []});
      }
      }
    });
});

 function setUpListener(){
        console.log("setUpListener called");
        chrome.runtime.onMessage.addListener(
        function(request, sender, sendResponse) {
        sendResponse("test started");
        console.log("extension called from background");
        console.log("greeting is " + request.greeting);
        console.log("testFor is " + request.testFor);
        console.log("nextPage is " + request.nextPage);
        testPage(request.testFor);
        if (request.nextPage !== null){
          console.log("next page is not stop");
          loadNextPage(request.nextPage);
        }
      });
}

function testPage(error){
  let isError = $( "*:contains('" + error + "')" );
    for (let j = 0; j < isError.length; i++) {
        if ($(isError[j]).is(':visible')){
            reportErrorToBackground();
            return;
        }
    }
}

$.expr[":"].contains = $.expr.createPseudo(function(arg) {
  return function( elem ) {
   return $(elem).text().toUpperCase().indexOf(arg.toUpperCase()) >= 0;
  };
});

function loadNextPage(_nextPage){
  window.location.assign( _nextPage );
  console.log("passed location.assignn");
  setTimeout(function(){console.log("waited 1000")}, 1000);
  }

function reportErrorToBackground(){
  let currentPageURL = window.location.href;
  chrome.runtime.sendMessage({errorPage: currentPageURL, greeting: "Sending error"});
}

function addAllLinksToContentPageLinksArray(){
  console.log("addAllLinksToContentPageLinksArray called");
  $("a:visible").each(function() {
    let _thisHref = this.href;
    if (_thisHref.includes("#!") === false){
      if (_thisHref.includes("#") ){
          _thisHref = _thisHref.split("#")[0];
        }
      }
    contentPageLinksArray.push(_thisHref);
  });
}
<|endoftext|>


<commit_before>const fs = require ('fs');

// use at your own risk

const tradeKeys = [ 'info',
    'timestamp',
    'datetime',
    'symbol',
    'id',
    'order',
    'type',
    'takerOrMaker',
    'side',
    'price',
    'amount',
    'cost',
    'fee'
]

const marketKeys = [ 'id',
    'symbol',
    'base',
    'quote',
    'baseId',
    'quoteId',
    'active',
    'precision',
    'limits',
    'info'
]

const currencyKeys = [ 'id',
    'code',
    'name',
    'active',
    'fee',
    'precision',
    'limits',
    'info'
]

const tickerKeys = [
    'symbol',
    'info',
    'timestamp',
    'datetime',
    'high',
    'low',
    'bid',
    'bidVolume',
    'ask',
    'askVolume',
    'vwap',
    'open',
    'close',
    'last',
    'previousClose',
    'change',
    'percentage',
    'average',
    'baseVolume',
    'quoteVolume'
]

const orderKeys = [
    'info',
    'id',
    'clientOrderId',
    'timestamp',
    'datetime',
    'lastTradeTimestamp',
    'symbol',
    'type',
    'side',
    'price',
    'amount',
    'cost',
    'average',
    'filled',
    'remaining',
    'status',
    'trades'
]

const things = [ tradeKeys, marketKeys, currencyKeys, tickerKeys, orderKeys ]

// we generate the unique keys from the above objects
const uniqueKeys = {}
for (let i = 0; i < things.length; i++) {
    const thing = things[i];
    let everythingElse = new Set ();
    for (let j = 0; j < things.length; j++) {
        if (j === i) {
            continue
        }
        everythingElse = new Set ([...everythingElse, ...things[j]]) // set union
    }
    const difference = new Set (thing.filter (x => !everythingElse.has (x)))
    for (const item of difference) {
        uniqueKeys[item] = thing
    }
}

// these keys can be found in some places that are not where we want to inject
delete uniqueKeys['base']
delete uniqueKeys['quote']
delete uniqueKeys['name']
delete uniqueKeys['percentage']
delete uniqueKeys['open']
delete uniqueKeys['code']
delete uniqueKeys['status']
delete uniqueKeys['order']
delete uniqueKeys['filled']
delete uniqueKeys['clientOrderId']
delete uniqueKeys['remaining']
delete uniqueKeys['trades']
delete uniqueKeys['bid']
delete uniqueKeys['ask']
const unique = Object.keys (uniqueKeys)
console.log (unique)

function runAllExchanges () {
    const classNames = fs.readdirSync ('./js')
        .filter (file => file.includes ('.js')).map (className => './js/' + className)
    for (const className of classNames) {
        searchFor (className)
    }
}

function searchFor (filename) {
    const lines = fs.readFileSync (filename).toString ().split ('\n')
    for (const key of unique) {
        const regex = new RegExp ('^\\s+\'' + key + '\':')
        const toInject = []
        for (let i = 0; i < lines.length; i++) {
            const line = lines[i]
            const found = line.match (regex)
            let injected = undefined
            if (found) {
                const { top, bottom } = extractObject (lines, i)
                const sectionLines = lines.slice (top, bottom)
                const begin = sectionLines[0]
                sectionLines[0] = begin.match (/{.*/)[0]
                const end = sectionLines[sectionLines.length - 1]
                sectionLines[sectionLines.length - 1] = '}'
                const section = sectionLines.join ('\n').replace (/\/\/.*$/mg, '').replace (/':\s.*,/g, '\': undefined,')
                eval (`injected = ${section}`)
                const toAdd = []
                for (const allFields of uniqueKeys[key]) {
                    if (!(allFields in injected)) {
                        toAdd.push (allFields)
                    }
                }
                sectionLines[0] = begin
                sectionLines.length = sectionLines.length - 1
                const indent = sectionLines[sectionLines.length - 1].match (/^\s+/)[0].length
                for (const missingKey of toAdd) {
                    sectionLines.push (' '.repeat (indent) + '\'' + missingKey + '\': ' + (missingKey === 'precision' || missingKey === 'limits' ? '{}' : 'undefined') + ',')
                }
                sectionLines.push (end)
                toInject.push ({top, bottom, sectionLines})
            }
        }
        // iterate through toInject backwards injeceting the stuff using splice
        for (let i = toInject.length - 1; i >= 0; i--) {
            const injector = toInject[i]
            const { top, bottom, sectionLines } = injector
            lines.splice (top, bottom - top, ...sectionLines)
        }
    }
    const output = lines.join ('\n')
    fs.writeFileSync (filename, output)
}

function extractObject (lines, i) {
    let depth = 1
    let top = i
    while (depth > 0) {
        top -= 1
        const line = lines[top]
        depth += (line.match (/}/g) || []).length
        depth -= (line.match (/{/g) || []).length
    }
    depth = 1
    let bottom = i
    while (depth > 0) {
        bottom += 1
        const line = lines[bottom]
        depth += (line.match (/{/g) || []).length
        depth -= (line.match (/}/g) || []).length

    }
    bottom += 1

    return { top, bottom }
}

runAllExchanges ()
<commit_msg>inject-fields fixup for precision and limits<commit_after>const fs = require ('fs');

// use at your own risk

const tradeKeys = [ 'info',
    'timestamp',
    'datetime',
    'symbol',
    'id',
    'order',
    'type',
    'takerOrMaker',
    'side',
    'price',
    'amount',
    'cost',
    'fee'
]

const marketKeys = [ 'id',
    'symbol',
    'base',
    'quote',
    'baseId',
    'quoteId',
    'active',
    'precision',
    'limits',
    'info'
]

const currencyKeys = [ 'id',
    'code',
    'name',
    'active',
    'fee',
    'precision',
    'limits',
    'info'
]

const tickerKeys = [
    'symbol',
    'info',
    'timestamp',
    'datetime',
    'high',
    'low',
    'bid',
    'bidVolume',
    'ask',
    'askVolume',
    'vwap',
    'open',
    'close',
    'last',
    'previousClose',
    'change',
    'percentage',
    'average',
    'baseVolume',
    'quoteVolume'
]

const orderKeys = [
    'info',
    'id',
    'clientOrderId',
    'timestamp',
    'datetime',
    'lastTradeTimestamp',
    'symbol',
    'type',
    'side',
    'price',
    'amount',
    'cost',
    'average',
    'filled',
    'remaining',
    'status',
    'trades'
]

const things = [ tradeKeys, marketKeys, currencyKeys, tickerKeys, orderKeys ]

// we generate the unique keys from the above objects
const uniqueKeys = {}
for (let i = 0; i < things.length; i++) {
    const thing = things[i];
    let everythingElse = new Set ();
    for (let j = 0; j < things.length; j++) {
        if (j === i) {
            continue
        }
        everythingElse = new Set ([...everythingElse, ...things[j]]) // set union
    }
    const difference = new Set (thing.filter (x => !everythingElse.has (x)))
    for (const item of difference) {
        uniqueKeys[item] = thing
    }
}

// these keys can be found in some places that are not where we want to inject
delete uniqueKeys['base']
delete uniqueKeys['quote']
delete uniqueKeys['name']
delete uniqueKeys['percentage']
delete uniqueKeys['open']
delete uniqueKeys['code']
delete uniqueKeys['status']
delete uniqueKeys['order']
delete uniqueKeys['filled']
delete uniqueKeys['clientOrderId']
delete uniqueKeys['remaining']
delete uniqueKeys['trades']
delete uniqueKeys['bid']
delete uniqueKeys['ask']
const unique = Object.keys (uniqueKeys)
console.log (unique)

function runAllExchanges () {
    const classNames = fs.readdirSync ('./js')
        .filter (file => file.includes ('.js')).map (className => './js/' + className)
    for (const className of classNames) {
        searchFor (className)
    }
}

function searchFor (filename) {
    const lines = fs.readFileSync (filename).toString ().split ('\n')
    for (const key of unique) {
        const regex = new RegExp ('^\\s+\'' + key + '\':')
        const toInject = []
        for (let i = 0; i < lines.length; i++) {
            const line = lines[i]
            const found = line.match (regex)
            let injected = undefined
            if (found) {
                const { top, bottom } = extractObject (lines, i)
                const sectionLines = lines.slice (top, bottom)
                const begin = sectionLines[0]
                sectionLines[0] = begin.match (/{.*/)[0]
                const end = sectionLines[sectionLines.length - 1]
                sectionLines[sectionLines.length - 1] = '}'
                const section = sectionLines.join ('\n').replace (/\/\/.*$/mg, '').replace (/':\s.*,/g, '\': undefined,')
                eval (`injected = ${section}`)
                const toAdd = []
                for (const allFields of uniqueKeys[key]) {
                    if (!(allFields in injected)) {
                        toAdd.push (allFields)
                    }
                }
                sectionLines[0] = begin
                sectionLines.length = sectionLines.length - 1
                const indent = sectionLines[sectionLines.length - 1].match (/^\s+/)[0].length
                for (const missingKey of toAdd) {
                    sectionLines.push (' '.repeat (indent) + '\'' + missingKey + '\': ' + (missingKey === 'precision' || missingKey === 'limits' ? ('this.' + missingKey) : 'undefined') + ',')
                }
                sectionLines.push (end)
                toInject.push ({top, bottom, sectionLines})
            }
        }
        // iterate through toInject backwards injeceting the stuff using splice
        for (let i = toInject.length - 1; i >= 0; i--) {
            const injector = toInject[i]
            const { top, bottom, sectionLines } = injector
            lines.splice (top, bottom - top, ...sectionLines)
        }
    }
    const output = lines.join ('\n')
    fs.writeFileSync (filename, output)
}

function extractObject (lines, i) {
    let depth = 1
    let top = i
    while (depth > 0) {
        top -= 1
        const line = lines[top]
        depth += (line.match (/}/g) || []).length
        depth -= (line.match (/{/g) || []).length
    }
    depth = 1
    let bottom = i
    while (depth > 0) {
        bottom += 1
        const line = lines[bottom]
        depth += (line.match (/{/g) || []).length
        depth -= (line.match (/}/g) || []).length

    }
    bottom += 1

    return { top, bottom }
}

runAllExchanges ()
<|endoftext|>


<commit_before>'use strict'

function scrollBottom() {
  const bottom = document.getElementById('ui_audio_load_more');
  var disp = bottom.style.display;
  while (disp !== 'none') {
    bottom.scrollIntoView();
    console.log(`display: ${bottom.style.display}`);
    disp = bottom.style.display;
  }
}

chrome.runtime.onMessage.addListener(function (mes, sen, resp) {
  console.log('lol');
  scrollBottom();
});
<commit_msg>debug version, code injecting, testing<commit_after>'use strict'

{
  var EVENT = new Event('endOfList');

  function inject() {
    console.log('Injecting script...');

    const injScript = document.createElement('script');

    injScript.textContent = '(' + function () {
      const audios = document.querySelectorAll('div.audio_row');
      audios[0].click();
      console.log(window.ap);
    } + ')();';

    document.head.appendChild(injScript); // Injecting script
    injScript.remove();
    console.log('Script injected successfuly...');
  }

  chrome.runtime.onMessage.addListener(function (mes, sen, resp) {
    const bottom = document.getElementById('ui_audio_load_more');
    bottom.addEventListener('endOfList', () => inject());
    bottom.dispatchEvent(EVENT);

  //   const label = setInterval(() => {
  //     bottom.scrollIntoView();
  //     if (bottom.style.display === 'none') {
  //       clearInterval(label);
  //       bottom.dispatchEvent(EVENT);
  //     }
  //   }, 500);
  });
}
<|endoftext|>


<commit_before>
var fs = require('fs');

module.exports = function(flowData){

  var fconsole = flowData.console;
  var cultureList = flowData.l10n.cultureList;
  var list = flowData.l10n.defList;
  var dictMap = {};
  var l10nKeys = {};
  var pathes = {}

  // collect all keys and dictionaries
  for (var i = 0, entry; entry = list[i]; i++)
  {
    var name = entry[0];
    var path = entry[1];
    var keys = entry[2];

    fconsole.log(name);
    fconsole.incDeep();

    if (dictMap[name])
      fconsole.log('[!] ' + name + ' already declared in ' + entry[3].relpath);

    pathes[path] = true;
    dictMap[name] = path;

    for (var key in keys)
    {
      if (l10nKeys[name + '.' + key])
        fconsole.log('[!] Duplicate key found: ' + name + '.' + key);

      l10nKeys[name + '.' + key] = true;
    }

    fconsole.decDeep();
  }
  fconsole.log();

  var cultureContentMap = {};
  for (var i = 0; culture = cultureList[i]; i++)
    cultureContentMap[culture] = [];

  // ok, now check out pathes and collect culture files

  for (var i = 0; culture = cultureList[i]; i++)
  {
    fconsole.log('Process ' + culture);
    fconsole.incDeep();

    for (var path in pathes)
    {
      var cultureFile = path + '/' + culture + '.json';
      if (fs.existsSync(cultureFile))
      {
        var cultureMap = cultureContentMap[culture];
        fconsole.log('[+] ' + cultureFile);
        fconsole.incDeep();
        try {
          var dictChunk = flowData.l10n.linearDictionary(JSON.parse(fs.readFileSync(cultureFile, 'utf-8')));
          for (var key in dictChunk)
          {
            if (!l10nKeys[key])
              fconsole.log('[!] Unknown key ' + key + ' (ignored)');
            else
            {
              // TODO: check for duplicates
              cultureMap[key] = dictChunk[key];
            }
          }
        } catch(e) {
          fconsole.log('[!] Can\'t parse ' + cultureFile, e);
        }
        fconsole.decDeep();
      }
    }

    fconsole.decDeep();
    fconsole.log();
  }

  // build index
  flowData.l10n.index = createDictionaryKeyMap(Object.keys(l10nKeys));

  // make culture packs
  fconsole.log('Make lang packages');
  fconsole.incDeep();
  for (var culture in cultureContentMap)
  {
    var jsRef = culture + '.json';

    fconsole.log('[+] ' + jsRef);

    flowData.files.add({
      jsRef: jsRef,
      type: 'json',
      isResource: true,
      jsResourceContent: flowData.l10n.packDictionary(cultureContentMap[culture], true)
    });
  }
}

module.exports.handlerName = '[l10n] Make dictionary';

function createDictionaryKeyMap(keys){
  keys = keys.sort();

  var stack = [];
  var res = [];
  var map = [];
  var pathMap = {};
  var keyIndex = 0;
  for (var i = 0, key; key = keys[i]; i++)
  {
    var parts = key.split('.');
    var reset = false;
    var offset = 0;

    if (stack.length && stack[0] != parts[0])
    {
      res.push('#');
      stack = [];
    }

    if (!stack.length)
      reset = true;
    else
    {
      for (; offset < parts.length; offset++){
        if (parts[offset] != stack[offset])
        {
          if (stack[offset])
          {
            reset = true;
            res.push(new Array(stack.length - offset + 1).join('<'));
            stack.splice(offset);
          }
          break;
        }
      }
    }

    while (parts[offset])
    {
      if (!reset)
        res.push('>');

      reset = false;
      res.push(parts[offset]);
      stack.push(parts[offset]);
      offset++;

      var path = stack.join('.');
      map.push(path);
    }

  }

  return {
    map: map,
    content: res.join('')
  };
}
<commit_msg>add l10n index into resource map<commit_after>
var fs = require('fs');

module.exports = function(flowData){

  var fconsole = flowData.console;
  var cultureList = flowData.l10n.cultureList;
  var list = flowData.l10n.defList;
  var dictMap = {};
  var l10nKeys = {};
  var pathes = {}

  // collect all keys and dictionaries
  for (var i = 0, entry; entry = list[i]; i++)
  {
    var name = entry[0];
    var path = entry[1];
    var keys = entry[2];

    fconsole.log(name);
    fconsole.incDeep();

    if (dictMap[name])
      fconsole.log('[!] ' + name + ' already declared in ' + entry[3].relpath);

    pathes[path] = true;
    dictMap[name] = path;

    for (var key in keys)
    {
      if (l10nKeys[name + '.' + key])
        fconsole.log('[!] Duplicate key found: ' + name + '.' + key);

      l10nKeys[name + '.' + key] = true;
    }

    fconsole.decDeep();
  }
  fconsole.log();

  var cultureContentMap = {};
  for (var i = 0; culture = cultureList[i]; i++)
    cultureContentMap[culture] = [];

  // ok, now check out pathes and collect culture files

  for (var i = 0; culture = cultureList[i]; i++)
  {
    fconsole.log('Process ' + culture);
    fconsole.incDeep();

    for (var path in pathes)
    {
      var cultureFile = path + '/' + culture + '.json';
      if (fs.existsSync(cultureFile))
      {
        var cultureMap = cultureContentMap[culture];
        fconsole.log('[+] ' + cultureFile);
        fconsole.incDeep();
        try {
          var dictChunk = flowData.l10n.linearDictionary(JSON.parse(fs.readFileSync(cultureFile, 'utf-8')));
          for (var key in dictChunk)
          {
            if (!l10nKeys[key])
              fconsole.log('[!] Unknown key ' + key + ' (ignored)');
            else
            {
              // TODO: check for duplicates
              cultureMap[key] = dictChunk[key];
            }
          }
        } catch(e) {
          fconsole.log('[!] Can\'t parse ' + cultureFile, e);
        }
        fconsole.decDeep();
      }
    }

    fconsole.decDeep();
    fconsole.log();
  }

  // build index
  fconsole.log('Build index');
  flowData.l10n.index = createDictionaryKeyMap(Object.keys(l10nKeys));

  fconsole.log('Add index into resource map');
  flowData.files.add({
    jsRef: 'l10nindex',
    type: 'text',
    isResource: true,
    jsResourceContent: flowData.l10n.index.content
  });

  fconsole.log('');

  // make culture packs
  fconsole.log('Make lang packages');
  fconsole.incDeep();
  for (var culture in cultureContentMap)
  {
    fconsole.log(culture);

    var jsRef = culture + '.json';
    var content = flowData.l10n.packDictionary(cultureContentMap[culture], true);

    fconsole.log('  [OK] Pack dictionary');

    flowData.files.add({
      jsRef: jsRef,
      type: 'json',
      isResource: true,
      jsResourceContent: content
    });

    fconsole.log('  [OK] Add to resource map');
  }
}

module.exports.handlerName = '[l10n] Make dictionary';

function createDictionaryKeyMap(keys){
  keys = keys.sort();

  var stack = [];
  var res = [];
  var map = [];
  var pathMap = {};
  var keyIndex = 0;
  for (var i = 0, key; key = keys[i]; i++)
  {
    var parts = key.split('.');
    var reset = false;
    var offset = 0;

    if (stack.length && stack[0] != parts[0])
    {
      res.push('#');
      stack = [];
    }

    if (!stack.length)
      reset = true;
    else
    {
      for (; offset < parts.length; offset++){
        if (parts[offset] != stack[offset])
        {
          if (stack[offset])
          {
            reset = true;
            res.push(new Array(stack.length - offset + 1).join('<'));
            stack.splice(offset);
          }
          break;
        }
      }
    }

    while (parts[offset])
    {
      if (!reset)
        res.push('>');

      reset = false;
      res.push(parts[offset]);
      stack.push(parts[offset]);
      offset++;

      var path = stack.join('.');
      map.push(path);
    }

  }

  return {
    map: map,
    content: res.join('')
  };
}
<|endoftext|>


<commit_before>if (location.pathname.endsWith('/unit')) {
  function convertTimeToSeconds(text) {
    return text.split(':').reduce(function(a, b) { return parseInt(a)*60 + parseInt(b) });
  }
  function convertSecondsToTime(seconds) {
    var date = new Date(null);
    date.setSeconds(seconds);
    return date.toISOString().substr(11, 8);
  }

  var totaltime = 0;
  var unfinished_total = 0;
  var count_unfinished = 0;
  $('.list').each(function() {
    var text = $(this).children().children().eq(1).text().trim();
    if ( $(this).find('.complete').size() == 0 ) {
      unfinished_total += convertTimeToSeconds(text.split(/ |　/).slice(-1)[0]);
      count_unfinished += 1;
    }
    totaltime += convertTimeToSeconds(text.split(/ |　/).slice(-1)[0]);
  });
  $('#result_unit_movie h1, #report_unit_movie h1')
    .append(' - ' + convertSecondsToTime(unfinished_total)
          + ' / ' + convertSecondsToTime(totaltime)
          + ' (' + count_unfinished + ')');
}
else {
  function parseSubjectName(subject) {
    subject = subject.replace('Ⅰ', '1').replace('Ｂ', 'B').replace('Ａ', 'A');
    if (!data[subject]) {
      console.error('[error] unknown subject:', JSON.stringify(subject));
      return null;
    }
    return subject;
  }
  function getDeadline(subject, cid) {
    var i = 0;
    for (i=0; i<months.length; i++) {
      if (data[subject][months[i]] > cid) break;
    }
    return months[i];
  }

  // labeling
  $('.list').children().each(function() {
    var subject = parseSubjectName($(this).children().eq(0).text());
    if (subject == null) return;
    var subject_ok = -1;
    $(this).find('.comp a, .normal a').each(function(cid) {
      var title = $(this).text().replace(/^\s+|\s+$/g, '');
      $(this).text('《' + getDeadline(subject, cid) + '月》 ' + title);
    });
  });

  var updateList = function(month) {
    var ret = { num_ok: 0, num_ng: 0 };
    $('.list').last().children().each(function() {
      var subject = parseSubjectName($(this).children().eq(0).text());
      if (subject == null) return;
      var subject_ok = -1;
      $(this).find('.comp a, .normal a').each(function(cid) {
        var x = $(this);
        var title = x.text().replace(/^\s+|\s+$/g, '');
        var deadline = getDeadline(subject, cid);

        if (deadline <= month) {
          var base = x.parent().parent();
          var c_status = base.find('span').text();
          if (/完了|レポート提出済み/.test(c_status)) {
            base.css('background-color', '#d9edf7');
            if (deadline == month) {
              if (subject_ok == -1) subject_ok = 1;
              ret.num_ok += 1;
            }
          }
          else {
            base.css('background-color', '#f2dede');
            if (deadline == month) {
              subject_ok = 0;
              ret.num_ng += 1;
            }
          }
        }
        else {
          var base = x.parent().parent();
          base.css('background-color', '');
        }
      });
      switch (subject_ok) {
        case -1:
          $(this).css('background-color', '');
          break;
        case 0:
          $(this).css('background-color', '#f2dede');
          break;
        case 1:
          $(this).css('background-color', '#d9edf7');
          break;
      }
    });
    return ret;
  };
  // dropdown
  var select_tag = $('<select>');
  months_rest.forEach(function(x) {
    select_tag.append( $('<option>').text( x + '月').val(x) );
  });
  select_tag.change(function() {
    updateList($('select option:selected').val());
  });
  $('.list').before(select_tag);

  var ret = updateList(months_rest[0]);
  var num_all = ret.num_ok + ret.num_ng;
  // div#contents ul.list li ul li span
  $('.list').before($('<ul>').addClass('list')
    .append($('<li>')
    .append($('<ul>')
    .append($('<li>')
      .append($('<p>').append($('<a>').text(ret.num_ok + ' / ' + num_all)))
      .append(
        $('<span>')
        .addClass('important')
        .text(Math.round(100.0 * ret.num_ok / num_all) + '%')
      )
    )
    )
    )
  );
}
<commit_msg>statusbarを更新する<commit_after>if (location.pathname.endsWith('/unit')) {
  function convertTimeToSeconds(text) {
    return text.split(':').reduce(function(a, b) { return parseInt(a)*60 + parseInt(b) });
  }
  function convertSecondsToTime(seconds) {
    var date = new Date(null);
    date.setSeconds(seconds);
    return date.toISOString().substr(11, 8);
  }

  var totaltime = 0;
  var unfinished_total = 0;
  var count_unfinished = 0;
  $('.list').each(function() {
    var text = $(this).children().children().eq(1).text().trim();
    if ( $(this).find('.complete').size() == 0 ) {
      unfinished_total += convertTimeToSeconds(text.split(/ |　/).slice(-1)[0]);
      count_unfinished += 1;
    }
    totaltime += convertTimeToSeconds(text.split(/ |　/).slice(-1)[0]);
  });
  $('#result_unit_movie h1, #report_unit_movie h1')
    .append(' - ' + convertSecondsToTime(unfinished_total)
          + ' / ' + convertSecondsToTime(totaltime)
          + ' (' + count_unfinished + ')');
}
else {
  var vt, vp;

  function parseSubjectName(subject) {
    subject = subject.replace('Ⅰ', '1').replace('Ｂ', 'B').replace('Ａ', 'A');
    if (!data[subject]) {
      console.error('[error] unknown subject:', JSON.stringify(subject));
      return null;
    }
    return subject;
  }
  function getDeadline(subject, cid) {
    var i = 0;
    for (i=0; i<months.length; i++) {
      if (data[subject][months[i]] > cid) break;
    }
    return months[i];
  }

  // labeling
  $('.list').children().each(function() {
    var subject = parseSubjectName($(this).children().eq(0).text());
    if (subject == null) return;
    var subject_ok = -1;
    $(this).find('.comp a, .normal a').each(function(cid) {
      var title = $(this).text().replace(/^\s+|\s+$/g, '');
      $(this).text('《' + getDeadline(subject, cid) + '月》 ' + title);
    });
  });

  var updateList = function(month) {
    var num_ok = 0, num_ng = 0;
    $('.list').last().children().each(function() {
      var subject = parseSubjectName($(this).children().eq(0).text());
      if (subject == null) return;
      var subject_ok = -1;
      $(this).find('.comp a, .normal a').each(function(cid) {
        var x = $(this);
        var title = x.text().replace(/^\s+|\s+$/g, '');
        var deadline = getDeadline(subject, cid);

        if (deadline <= month) {
          var base = x.parent().parent();
          var c_status = base.find('span').text();
          if (/完了|レポート提出済み/.test(c_status)) {
            base.css('background-color', '#d9edf7');
            if (deadline == month) {
              if (subject_ok == -1) subject_ok = 1;
              num_ok += 1;
            }
          }
          else {
            base.css('background-color', '#f2dede');
            if (deadline == month) {
              subject_ok = 0;
              num_ng += 1;
            }
          }
        }
        else {
          var base = x.parent().parent();
          base.css('background-color', '');
        }
      });
      switch (subject_ok) {
        case -1:
          $(this).css('background-color', '');
          break;
        case 0:
          $(this).css('background-color', '#f2dede');
          break;
        case 1:
          $(this).css('background-color', '#d9edf7');
          break;
      }
    });
    var num_all = num_ok + num_ng;
    vt.text(num_ok + ' / ' + num_all);
    vp.text(Math.round(100.0 * num_ok / num_all) + '%');
  };
  // dropdown
  var select_tag = $('<select>');
  months_rest.forEach(function(x) {
    select_tag.append( $('<option>').text( x + '月').val(x) );
  });
  select_tag.change(function() {
    updateList($('select option:selected').val());
  });
  $('.list').before(select_tag);

  vt = $('<a>').text('- / -');
  vp = $('<span>').addClass('important').text('- %');
 // div#contents ul.list li ul li span
  $('.list').before($('<ul>').addClass('list')
    .append($('<li>')
    .append($('<ul>')
    .append($('<li>').attr('id', 'statusbar')
      .append($('<p>').append(vt))
      .append(vp)
    )
    )
    )
  );
  updateList(months_rest[0]);
}
<|endoftext|>


<commit_before>/**
 * Created by Trevor on 7/3/2015.
 */
var Q = require("q");

/* var a = {};
 a["key1"] = "value1";
 a["key2"] = "value2";
 */

var LogTypeMap = {};
LogTypeMap[100] = {type: "Added Pile", callFunction:toStringDefault};
LogTypeMap[200] = {type: "Added Run", callFunction:toStringDefault};
LogTypeMap[300] = {type: "Removed Pile", callFunction:toStringDefault};
LogTypeMap[400] = {type: "Removed Run", callFunction:toStringDefault};
LogTypeMap[500] = {type: "Audited", callFunction:toStringDefault};
LogTypeMap[600] = {type: "Noted", callFunction:toStringDefault};
LogTypeMap[700] = {type: "Created New Product", callFunction:toStringDefault};
LogTypeMap[800] = {type: "Created User", callFunction:toStringDefault};

var stringLogs = [];
var jsonString = '{"logs":[';

function toStringDefault (jsonObj) {
    var logObj = JSON.parse(jsonObj);
    //console.log(jsonObj);
    var actionPiece = JSON.parse(logObj.action).value;
    //console.log("Action Piece " + actionPiece);
    return logObj.username + " on " + logObj.time + " " + LogTypeMap[logObj.logType].type + " " + actionPiece;
}

module.exports =

{
    _verifyKey :function (key) {
        return LogTypeMap[key] == undefined ? false : true;
    },

    displayLogs: function (username, callback) {
        var db = require("../imp_services/impdb.js").connect();
        //var defer = Q.defer();
       return Q.fcall(db.beginTransaction())
            .then(db.query("USE " + db.databaseName))
            .then(db.query("CALL GetLogsUserView(\'" + username + "\');"))
            .then(function (rows) {

                // We got data about the user
                if (rows[0][0].length == 0) { // No user by that username
                    return "Invalid Result!";
                }

                var jsonLogs = [];
                //console.log("---- Now to show the rows ------");
                for (var i = 0; i < rows[0][0].length; i++) {
                    var row = rows[0][0][i];
                    var logID = row.LogID;
                    var LogType = row.LogType;
                    var logUsername = row.Username;
                    var time = row.Time;
                    var actionData = row.ActionData;
                    jsonLogs[i] = '{"logID":"' + logID + '", "logType":"' + LogType +
                        '","username":"' + logUsername + '","time":"' + time + '","action":"' + actionData + '"}';

                    stringLogs[i] = LogTypeMap[LogType].callFunction(jsonLogs[i]);
                }

                for (var j = 0; j < stringLogs.length; j++) {
                    jsonString += '"' + stringLogs[j] + '"';
                    if (j + 1 < stringLogs.length) {
                        jsonString += ',';
                    }
                }
                jsonString += ']}';
                //console.log(jsonString);
               //console.log(module.exports._verifyKey(800));
                callback(jsonString);

            }) .then(db.endTransaction()) // This is called right?
            .catch(function (err) {
                Q.fcall().then(db.endTransaction())
                    .then(console.log("We had an error"))
                console.log("Error: " + err);
            }).done();
    }
};<commit_msg>No longer requires extra escape characters in db.<commit_after>/**
 * Created by Trevor on 7/3/2015.
 */
var Q = require("q");

/* var a = {};
 a["key1"] = "value1";
 a["key2"] = "value2";
 */

var LogTypeMap = {};
LogTypeMap[100] = {type: "Added Pile", callFunction:toStringDefault};
LogTypeMap[200] = {type: "Added Run", callFunction:toStringDefault};
LogTypeMap[300] = {type: "Removed Pile", callFunction:toStringDefault};
LogTypeMap[400] = {type: "Removed Run", callFunction:toStringDefault};
LogTypeMap[500] = {type: "Audited", callFunction:toStringDefault};
LogTypeMap[600] = {type: "Noted", callFunction:toStringDefault};
LogTypeMap[700] = {type: "Created New Product", callFunction:toStringDefault};
LogTypeMap[800] = {type: "Created User", callFunction:toStringDefault};

var stringLogs = [];
var jsonString = '{"logs":[';

function toStringDefault (LogType, logUsername,  time,  actionData) {
    return logUsername + " on " + time + " " + LogTypeMap[LogType].type + " " + actionData.value;
}

module.exports =

{
    _verifyKey :function (key) {
        return LogTypeMap[key] == undefined ? false : true;
    },

    displayLogs: function (username, callback) {
        var db = require("../imp_services/impdb.js").connect();
        //var defer = Q.defer();
       return Q.fcall(db.beginTransaction())
            .then(db.query("USE " + db.databaseName))
            .then(db.query("CALL GetLogsUserView(\'" + username + "\');"))
            .then(function (rows) {

                // We got data about the user
                if (rows[0][0].length == 0) { // No user by that username
                    return "Invalid Result!";
                }

                var jsonLogs = [];
                //console.log("---- Now to show the rows ------");
                for (var i = 0; i < rows[0][0].length; i++) {
                    var row = rows[0][0][i];
                    var logID = row.LogID;
                    var LogType = row.LogType;
                    var logUsername = row.Username;
                    var time = row.Time;
                    var actionData = row.ActionData;
                    console.log(actionData);
                    /*jsonLogs[i] = '{"logID":"' + logID + '", "logType":"' + LogType +
                        '","username":"' + logUsername + '","time":"' + time + '","action":"' +'"' + actionData + '"' + '"}';
                    */

                    stringLogs[i] = LogTypeMap[LogType].callFunction(LogType, logUsername,  time,  JSON.parse(actionData));
                }

                for (var j = 0; j < stringLogs.length; j++) {
                    jsonString += '"' + stringLogs[j] + '"';
                    if (j + 1 < stringLogs.length) {
                        jsonString += ',';
                    }
                }
                jsonString += ']}';
                //console.log(jsonString);
               //console.log(module.exports._verifyKey(800));
                //callback(jsonString);

               callback(jsonString);

            }) .then(db.endTransaction()) // This is called right?
            .catch(function (err) {
                Q.fcall().then(db.endTransaction())
                    .then(console.log("We had an error"))
                console.log("Error: " + err);
            }).done();
    }
};<|endoftext|>