<commit_before>###
# Copyright 2015 ppy Pty. Ltd.
#
# This file is part of osu!web. osu!web is distributed with the hope of
# attracting more community contributions to the core ecosystem of osu!.
#
# osu!web is free software: you can redistribute it and/or modify
# it under the terms of the Affero GNU General Public License version 3
# as published by the Free Software Foundation.
#
# osu!web is distributed WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with osu!web.  If not, see <http://www.gnu.org/licenses/>.
###
{div} = React.DOM
el = React.createElement

class ProfilePage.Extra extends React.Component
  constructor: (props) ->
    withMePage = props.userPage.html != '' || props.withEdit

    if not withMePage
      index = props.profileOrder.indexOf 'me'
      props.profileOrder.splice index, 1

    super props

    @state =
      tabsSticky: false
      profileOrder: @props.profileOrder
      draggingEnabled: false

  componentDidMount: =>
    @_removeListeners()
    $.subscribe 'profilePageExtra:tab.profileContentsExtra', @_modeSwitch
    $.subscribe 'stickyHeader.profileContentsExtra', @_tabsStick
    $.subscribe 'profilePageExtra:toggleDragging', @_toggleDragging
    $(window).on 'throttled-scroll.profileContentsExtra', @_modeScan
    osu.pageChange()
    @_modeScan()

    $('#profile-extra-list').sortable({
      disabled: true,
      cursor: 'move',
      revert: 150,
      scrollSpeed: 10,
      update: (event, ui) =>
        newOrder = $('#profile-extra-list').sortable('toArray')
        @setState profileOrder: newOrder
      })
  componentWillUnmount: =>
    @_removeListeners()


  componentWillReceiveProps: =>
    osu.pageChange()


  _modeScan: =>
    return if @_scrolling

    pages = document.getElementsByClassName('js-profile-page-extra--scrollspy')
    return unless pages.length

    currentPage = null
    anchorHeight = window.innerHeight * 0.5

    if osu.bottomPage()
      @setState mode: _.last(pages).getAttribute('id')
      return

    # FIXME: I don't remember why this one scans from bottom while
    # the one in forum.refreshCounter does it from top.
    for page in pages by -1
      pageTop = page.getBoundingClientRect().top
      continue unless pageTop <= anchorHeight

      @setState mode: page.getAttribute('id')
      return

    @setState mode: page.getAttribute('id')


  _modeSwitch: (_e, mode) =>
    # Don't bother scanning the current position.
    # The result will be wrong when target page is too short anyway.
    @_scrolling = true

    $.scrollTo "##{mode}", 500,
      onAfter: =>
        # Manually set the mode to avoid confusion (wrong highlight).
        # Scrolling will obviously break it but that's unfortunate result
        # from having the scrollspy marker at middle of page.
        @setState mode: mode, =>
          # Doesn't work:
          # - part of state (callback, part of mode setting)
          # - simple variable in callback
          # Both still change the switch too soon.
          setTimeout (=> @_scrolling = false), 100
      # count for the tabs height
      offset: @refs.tabs.getBoundingClientRect().height * -1


  _removeListeners: ->
    $.unsubscribe '.profileContentsExtra'
    $(window).off '.profileContentsExtra'


  _tabsStick: (_e, target) =>
    @setState tabsSticky: (target == 'profile-extra-tabs')

  _toggleDragging: =>
    if @state.draggingEnabled

      csrfParam = $('meta[name=csrf-param]').attr('content')
      csrfToken = $('meta[name=csrf-token]').attr('content')

      osu.showLoadingOverlay

      $.ajax '/account/update-profile', {
        method: 'PUT',
        dataType: 'JSON',
        data: {
          csrfParam: csrfToken,
          'order': @state.profileOrder
        },
        success: (data, textStatus, jqHXR) ->
          osu.hideLoadingOverlay
          if data.status == 'error'<commit_msg>Removed redundant csrf token in ajax<commit_after>###
# Copyright 2015 ppy Pty. Ltd.
#
# This file is part of osu!web. osu!web is distributed with the hope of
# attracting more community contributions to the core ecosystem of osu!.
#
# osu!web is free software: you can redistribute it and/or modify
# it under the terms of the Affero GNU General Public License version 3
# as published by the Free Software Foundation.
#
# osu!web is distributed WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with osu!web.  If not, see <http://www.gnu.org/licenses/>.
###
{div} = React.DOM
el = React.createElement

class ProfilePage.Extra extends React.Component
  constructor: (props) ->
    withMePage = props.userPage.html != '' || props.withEdit

    if not withMePage
      index = props.profileOrder.indexOf 'me'
      props.profileOrder.splice index, 1

    super props

    @state =
      tabsSticky: false
      profileOrder: @props.profileOrder
      draggingEnabled: false

  componentDidMount: =>
    @_removeListeners()
    $.subscribe 'profilePageExtra:tab.profileContentsExtra', @_modeSwitch
    $.subscribe 'stickyHeader.profileContentsExtra', @_tabsStick
    $.subscribe 'profilePageExtra:toggleDragging', @_toggleDragging
    $(window).on 'throttled-scroll.profileContentsExtra', @_modeScan
    osu.pageChange()
    @_modeScan()

    $('#profile-extra-list').sortable({
      disabled: true,
      cursor: 'move',
      revert: 150,
      scrollSpeed: 10,
      update: (event, ui) =>
        newOrder = $('#profile-extra-list').sortable('toArray')
        @setState profileOrder: newOrder
      })
  componentWillUnmount: =>
    @_removeListeners()


  componentWillReceiveProps: =>
    osu.pageChange()


  _modeScan: =>
    return if @_scrolling

    pages = document.getElementsByClassName('js-profile-page-extra--scrollspy')
    return unless pages.length

    currentPage = null
    anchorHeight = window.innerHeight * 0.5

    if osu.bottomPage()
      @setState mode: _.last(pages).getAttribute('id')
      return

    # FIXME: I don't remember why this one scans from bottom while
    # the one in forum.refreshCounter does it from top.
    for page in pages by -1
      pageTop = page.getBoundingClientRect().top
      continue unless pageTop <= anchorHeight

      @setState mode: page.getAttribute('id')
      return

    @setState mode: page.getAttribute('id')


  _modeSwitch: (_e, mode) =>
    # Don't bother scanning the current position.
    # The result will be wrong when target page is too short anyway.
    @_scrolling = true

    $.scrollTo "##{mode}", 500,
      onAfter: =>
        # Manually set the mode to avoid confusion (wrong highlight).
        # Scrolling will obviously break it but that's unfortunate result
        # from having the scrollspy marker at middle of page.
        @setState mode: mode, =>
          # Doesn't work:
          # - part of state (callback, part of mode setting)
          # - simple variable in callback
          # Both still change the switch too soon.
          setTimeout (=> @_scrolling = false), 100
      # count for the tabs height
      offset: @refs.tabs.getBoundingClientRect().height * -1


  _removeListeners: ->
    $.unsubscribe '.profileContentsExtra'
    $(window).off '.profileContentsExtra'


  _tabsStick: (_e, target) =>
    @setState tabsSticky: (target == 'profile-extra-tabs')

  _toggleDragging: =>
    if @state.draggingEnabled

      osu.showLoadingOverlay

      $.ajax '/account/update-profile', {
        method: 'PUT',
        dataType: 'JSON',
        data: {
          'order': @state.profileOrder,
        },
        success: (data, textStatus, jqHXR) ->
          osu.hideLoadingOverlay
          if data.status == 'error'
{'fraction_non_alphanumeric': 0.10331043098063711, 'fraction_numerical': 0.005246720799500312, 'mean_word_length': 2.9614052449282533, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 33, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, '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': '4819978', 'n_tokens_mistral': 2321, 'n_tokens_neox': 2232, 'n_words': 724}