<commit_before>LogLine = require './LogLine'
Loader = require './Loader'

Contents = React.createClass

  # ============================================================================
  # Lifecycle Methods                                                          |
  # ============================================================================

  getInitialState: ->
    @state =
      contentsHeight: Math.max(document.documentElement.clientHeight, window.innerHeight || 0) - 180
      isLoading: false
      loadingText: ''

  componentWillMount: ->
    $(window).on 'resize orientationChange', @handleResize

  componentDidMount: ->
    @scrollNode = @refs.scrollContainer.getDOMNode()

  componentDidUpdate: (prevProps, prevState) ->
    # If loading without an offset, start tailing immediately
    if !@props.offset and @props.logLines.length > 0 and prevProps.logLines.length is 0
      @scrollToBottom()
    else if @tailingPoll
      @scrollToBottom()

  componentWillUnmount: ->
    $(window).off 'resize orientationChange', @handleResize

  # ============================================================================
  # Event Handlers                                                             |
  # ============================================================================

  handleResize: ->
    @setState
      contentsHeight: Math.max(document.documentElement.clientHeight, window.innerHeight || 0) - 180

  handleScroll: (node) ->
    # Are we at the bottom?
    if $(node).scrollTop() + $(node).innerHeight() >= node.scrollHeight
        @startTailingPoll(node)
    # Or the top?
    else if $(node).scrollTop() is 0
        @props.fetchPrevious(node.scrollHeight)
    else
      @stopTailingPoll()

  startTailingPoll: ->
    # Make sure there isn't one already running
    @stopTailingPoll()

    @setState
      isLoading: true
      loadingText: 'Tailing...'
    @props.fetchNext()
    @tailingPoll = setInterval @props.fetchNext, 1000

  stopTailingPoll: ->
    @setState
      isLoading: false
      loadingText: ''
    clearInterval @tailingPoll
    @tailingPoll = null

  # ============================================================================
  # Rendering                                                                  |
  # ============================================================================

  renderError: ->
    if @props.ajaxError.get("present")
      <div className="lines-wrapper">
          <div className="empty-table-message">
              <p>{@props.ajaxError.message}</p>
          </div>
      </div>

  renderLines: ->
    if @props.logLines
      @props.logLines.map((l) =>
        <LogLine content={l.data} offset={l.offset} key={l.offset} highlighted={l.offset is parseInt @props.offset} />
      )

  render: ->
    <div className="contents-container">
      <div className="tail-contents">
        {@renderError()}
        <Infinite
          ref="scrollContainer"
          className="infinite"
          containerHeight={@state.contentsHeight}
          elementHeight={20}
          handleScroll={_.throttle @handleScroll, 200}>
          {@renderLines()}
        </Infinite>
      </div>
      <Loader isVisable={@state.isLoading} text={@state.loadingText} />
    </div>

  # ============================================================================
  # Utility Methods                                                            |
  # ============================================================================

  setScrollHeight: (height) ->
    $(@scrollNode).scrollTop(height);

  scrollToTop: ->
    @setState
      isLoading: true
    @stopTailingPoll()
    @props.fetchPrevious().done =>
      @setScrollHeight(0)
      @setState
        isLoading: false

  scrollToBottom: ->
    @setScrollHeight(@scrollNode.scrollHeight)

module.exports = Contents<commit_msg>Set state after stopping polling<commit_after>LogLine = require './LogLine'
Loader = require './Loader'

Contents = React.createClass

  # ============================================================================
  # Lifecycle Methods                                                          |
  # ============================================================================

  getInitialState: ->
    @state =
      contentsHeight: Math.max(document.documentElement.clientHeight, window.innerHeight || 0) - 180
      isLoading: false
      loadingText: ''

  componentWillMount: ->
    $(window).on 'resize orientationChange', @handleResize

  componentDidMount: ->
    @scrollNode = @refs.scrollContainer.getDOMNode()

  componentDidUpdate: (prevProps, prevState) ->
    # If loading without an offset, start tailing immediately
    if !@props.offset and @props.logLines.length > 0 and prevProps.logLines.length is 0
      @scrollToBottom()
    else if @tailingPoll
      @scrollToBottom()

  componentWillUnmount: ->
    $(window).off 'resize orientationChange', @handleResize

  # ============================================================================
  # Event Handlers                                                             |
  # ============================================================================

  handleResize: ->
    @setState
      contentsHeight: Math.max(document.documentElement.clientHeight, window.innerHeight || 0) - 180

  handleScroll: (node) ->
    # Are we at the bottom?
    if $(node).scrollTop() + $(node).innerHeight() >= node.scrollHeight
        @startTailingPoll(node)
    # Or the top?
    else if $(node).scrollTop() is 0
        @props.fetchPrevious(node.scrollHeight)
    else
      @stopTailingPoll()

  startTailingPoll: ->
    # Make sure there isn't one already running
    @stopTailingPoll()

    @setState
      isLoading: true
      loadingText: 'Tailing...'
    @props.fetchNext()
    @tailingPoll = setInterval @props.fetchNext, 1000

  stopTailingPoll: ->
    @setState
      isLoading: false
      loadingText: ''
    clearInterval @tailingPoll
    @tailingPoll = null

  # ============================================================================
  # Rendering                                                                  |
  # ============================================================================

  renderError: ->
    if @props.ajaxError.get("present")
      <div className="lines-wrapper">
          <div className="empty-table-message">
              <p>{@props.ajaxError.message}</p>
          </div>
      </div>

  renderLines: ->
    if @props.logLines
      @props.logLines.map((l) =>
        <LogLine content={l.data} offset={l.offset} key={l.offset} highlighted={l.offset is parseInt @props.offset} />
      )

  render: ->
    <div className="contents-container">
      <div className="tail-contents">
        {@renderError()}
        <Infinite
          ref="scrollContainer"
          className="infinite"
          containerHeight={@state.contentsHeight}
          elementHeight={20}
          handleScroll={_.throttle @handleScroll, 200}>
          {@renderLines()}
        </Infinite>
      </div>
      <Loader isVisable={@state.isLoading} text={@state.loadingText} />
    </div>

  # ============================================================================
  # Utility Methods                                                            |
  # ============================================================================

  setScrollHeight: (height) ->
    $(@scrollNode).scrollTop(height);

  scrollToTop: ->
    @stopTailingPoll()
    @setState
      isLoading: true
    @props.fetchPrevious().done =>
      @setScrollHeight(0)
      @setState
        isLoading: false

  scrollToBottom: ->
    @setScrollHeight(@scrollNode.scrollHeight)

module.exports = Contents
{'fraction_non_alphanumeric': 0.257479601087942, 'fraction_numerical': 0.005439709882139619, 'mean_word_length': 2.6965055050263285, 'pattern_counts': {'":': 0, '<': 31, '<?xml version=': 0, '>': 69, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 16, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3105008', 'n_tokens_mistral': 1980, 'n_tokens_neox': 1832, 'n_words': 365}