<commit_before>import React from 'react'
import Edgenote from './Edgenote.js'
import {Link} from 'react-router'
import gatherEdgenotes from '../gatherEdgenotes.js';
import {I18n} from './I18n.js'

function isElementInViewport (el) {

  //special bonus for those using jQuery
  if (typeof jQuery === "function" && el instanceof jQuery) {
    el = el[0];
  }

  var rect = el.getBoundingClientRect();

  return (
    rect.top >= 0 &&
      rect.left >= 0 &&
        rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && /*or $(window).height() */
        rect.right <= (window.innerWidth || document.documentElement.clientWidth) /*or $(window).width() */
  );
}

class Narrative extends React.Component {
  componentDidUpdate() {
    let top = document.getElementById('top');
    if (top && window.innerWidth < 749) {top.scrollIntoView()}
  }

  nextLink() {
    let nextChapterID = this.props.selectedPage + 2
    if (nextChapterID - 1 < this.props.pages.length) {
      return <Link className="nextLink" to={`/${nextChapterID}`}><I18n meaning="next" /> {this.props.pages[nextChapterID - 1].title}</Link>
    } else {
      return <footer><h2><I18n meaning="end" /></h2></footer>
    }
  }

  render() {
    let i = this.props.selectedPage
    if (this.props.pages.length === 0) {
      return <article />
    }
    let page = this.props.pages[i]
    return (
      <main>
        <a id="top" />
        <Page
          page={page}
          handleEdit={this.props.handleEdit}
        />
        {this.nextLink()}
      </main>
    )
  }
}

export default Narrative

class Page extends React.Component {
  render() {
    let {page} = this.props
    let cards = page.cards.map( (card, i) => {
      return <Card id={i} key={`c${i}`}
                   handleEdit={this.props.handleEdit}
                   selectedPage={page.position}
                   solid={card.solid}
                   card={card} />
    } )

    return (
      <article>
        <section><h1>{page.title}</h1></section>
        {cards}
      </article>
    )
  }
}

String.prototype.addAttributeToLinks = function (attribute) {
  return this.replace(/(data-edgenote=\"([a-zA-Z0-9-]+)\")/g, '$1 ' + attribute)
}

String.prototype.addAttributeToLinksPointingToEdgenoteID = function (id, attribute) {
  return this.replace(new RegExp('(data-edgenote=\"'+id+'\")', 'g'), '$1 ' + attribute)
}

String.prototype.addHREF = function(firstPartOfPath) {
  return this.addAttributeToLinks(`href=\"#${firstPartOfPath}/edgenotes/$2\"`)
}

class Card extends React.Component {
  constructor() {
    super()
    this.state = {
      selectedEdgenote: null,
      edgenoteSlugs: []
    }
  }

  addHoverCallbacksToParagraphText(paragraph) {
    let mouseover = 'onmouseover=\'window.handleHover'+this.props.id+'(\"$2\")\''
    let mouseout = 'onmouseout=\'window.handleHover'+this.props.id+'(null)\''
    return { __html: paragraph.addAttributeToLinks(mouseover).addAttributeToLinks(mouseout)
      .addHREF(`/${this.props.selectedPage + 1}`) }
  }

  setEdgenotes(contents) {
    let edgenoteSlugs = gatherEdgenotes(contents)
    this.setState({edgenoteSlugs: edgenoteSlugs})
  }

  componentWillMount() {
    window["handleHover"+this.props.id] = (slug) => {
      this.setState({selectedEdgenote: slug})
    }
  }

  componentDidMount() {
    this.setEdgenotes(this.props.card.content)
  }

  componentWillReceiveProps(nextProps) {
    if (this.props.selectedPage !== nextProps.selectedPage) {
      this.setState({ edgenoteSlugs: [] })
      this.setEdgenotes(nextProps.card.content)
    }
  }

  renderEdgenotes() {
    let aside
    if (this.state.edgenoteSlugs.length != 0) {
      aside = <aside className="edgenotes">
                {<commit_msg>Edgenote links from in Narrative don't advance section.<commit_after>import React from 'react'
import Edgenote from './Edgenote.js'
import {Link} from 'react-router'
import gatherEdgenotes from '../gatherEdgenotes.js';
import {I18n} from './I18n.js'

function isElementInViewport (el) {

  //special bonus for those using jQuery
  if (typeof jQuery === "function" && el instanceof jQuery) {
    el = el[0];
  }

  var rect = el.getBoundingClientRect();

  return (
    rect.top >= 0 &&
      rect.left >= 0 &&
        rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && /*or $(window).height() */
        rect.right <= (window.innerWidth || document.documentElement.clientWidth) /*or $(window).width() */
  );
}

class Narrative extends React.Component {
  componentDidUpdate() {
    let top = document.getElementById('top');
    if (top && window.innerWidth < 749) {top.scrollIntoView()}
  }

  nextLink() {
    let nextChapterID = this.props.selectedPage + 2
    if (nextChapterID - 1 < this.props.pages.length) {
      return <Link className="nextLink" to={`/${nextChapterID}`}><I18n meaning="next" /> {this.props.pages[nextChapterID - 1].title}</Link>
    } else {
      return <footer><h2><I18n meaning="end" /></h2></footer>
    }
  }

  render() {
    let i = this.props.selectedPage
    if (this.props.pages.length === 0) {
      return <article />
    }
    let page = this.props.pages[i]
    return (
      <main>
        <a id="top" />
        <Page
          page={page}
          handleEdit={this.props.handleEdit}
        />
        {this.nextLink()}
      </main>
    )
  }
}

export default Narrative

class Page extends React.Component {
  render() {
    let {page} = this.props
    let cards = page.cards.map( (card, i) => {
      return <Card id={i} key={`c${i}`}
                   handleEdit={this.props.handleEdit}
                   selectedPage={page.position}
                   solid={card.solid}
                   card={card} />
    } )

    return (
      <article>
        <section><h1>{page.title}</h1></section>
        {cards}
      </article>
    )
  }
}

String.prototype.addAttributeToLinks = function (attribute) {
  return this.replace(/(data-edgenote=\"([a-zA-Z0-9-]+)\")/g, '$1 ' + attribute)
}

String.prototype.addAttributeToLinksPointingToEdgenoteID = function (id, attribute) {
  return this.replace(new RegExp('(data-edgenote=\"'+id+'\")', 'g'), '$1 ' + attribute)
}

String.prototype.addHREF = function(firstPartOfPath) {
  return this.addAttributeToLinks(`href=\"#${firstPartOfPath}/edgenotes/$2\"`)
}

class Card extends React.Component {
  constructor() {
    super()
    this.state = {
      selectedEdgenote: null,
      edgenoteSlugs: []
    }
  }

  addHoverCallbacksToParagraphText(paragraph) {
    let mouseover = 'onmouseover=\'window.handleHover'+this.props.id+'(\"$2\")\''
    let mouseout = 'onmouseout=\'window.handleHover'+this.props.id+'(null)\''
    return { __html: paragraph.addAttributeToLinks(mouseover).addAttributeToLinks(mouseout)
      .addHREF(`/${this.props.selectedPage}`) }
  }

  setEdgenotes(contents) {
    let edgenoteSlugs = gatherEdgenotes(contents)
    this.setState({edgenoteSlugs: edgenoteSlugs})
  }

  componentWillMount() {
    window["handleHover"+this.props.id] = (slug) => {
      this.setState({selectedEdgenote: slug})
    }
  }

  componentDidMount() {
    this.setEdgenotes(this.props.card.content)
  }

  componentWillReceiveProps(nextProps) {
    if (this.props.selectedPage !== nextProps.selectedPage) {
      this.setState({ edgenoteSlugs: [] })
      this.setEdgenotes(nextProps.card.content)
    }
  }

  renderEdgenotes() {
    let aside
    if (this.state.edgenoteSlugs.length != 0) {
      aside = <aside className="edgenotes">
                {
{'fraction_non_alphanumeric': 0.15877029544849613, 'fraction_numerical': 0.007852009582113389, 'mean_word_length': 2.9242819843342036, 'pattern_counts': {'":': 0, '<': 53, '<?xml version=': 0, '>': 53, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2246761', 'n_tokens_mistral': 2513, 'n_tokens_neox': 2452, 'n_words': 430}