chuni-net - display overpower

Display song overpower and rating on chunithm-net

Dovrai installare un'estensione come Tampermonkey, Greasemonkey o Violentmonkey per installare questo script.

You will need to install an extension such as Tampermonkey to install this script.

Dovrai installare un'estensione come Tampermonkey o Violentmonkey per installare questo script.

Dovrai installare un'estensione come Tampermonkey o Userscripts per installare questo script.

Dovrai installare un'estensione come ad esempio Tampermonkey per installare questo script.

Dovrai installare un gestore di script utente per installare questo script.

(Ho già un gestore di script utente, lasciamelo installare!)

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

(Ho già un gestore di stile utente, lasciamelo installare!)

// ==UserScript==
// @name        chuni-net - display overpower
// @namespace   esterTion
// @license     MIT
// @match       https://chunithm-net-eng.com/mobile/record/music*
// @match       https://chunithm.wahlap.com/mobile/record/music*
// @match       https://new.chunithm-net.com/chuni-mobile/html/mobile/record/music*
// @match       https://chunithm-net-eng.com/mobile/home/playerData/rating*
// @match       https://chunithm.wahlap.com/mobile/home/playerData/rating*
// @match       https://new.chunithm-net.com/chuni-mobile/html/mobile/home/playerData/rating*
// @version     1.1.6
// @author      esterTion
// @description Display song overpower and rating on chunithm-net
// @run-at      document-end
// ==/UserScript==

(async function () {

const host = location.hostname
const server = host === 'new.chunithm-net.com' ? 'jp' : host === 'chunithm-net-eng.com' ? 'ex' : host === 'chunithm.wahlap.com' ? 'cn' : ''
if (!server) throw new Error('unknown server')
const imageBase = server === 'jp' ? '/chuni-mobile/html/mobile/images' : '/mobile/images'

const disabledSongs = {
  ex: [],
  cn: [],
  jp: []
}
const PAGE_TEXT = {
  ONLY_HIGHEST: {
    ex: 'Count highest difficulty only',
    cn: '仅统计最高难度',
    jp: '最高難易度のみ集計する',
  },
  SORT_INITIAL: {
    ex: 'Initial',
    cn: '初始顺序',
    jp: '初期順',
  },
  SORT_DIFFICULTY: {
    ex: 'Difficulty',
    cn: '难度',
    jp: '難易度',
  },
  SORT_SCORE: {
    ex: 'Score',
    cn: '分数',
    jp: 'スコア',
  },
  SORT_OP_REMAIN: {
    ex: 'op remaining',
    cn: 'op 剩余',
    jp: 'op 残り',
  },
  TOTAL_SONG_COUNT: {
    ex: 'Total {} songs',
    cn: '共计 {} 曲目',
    jp: '全 {} 曲',
  },
  AVERAGE_PLAYED: {
    ex: 'Average of played songs: ',
    cn: '已游玩平均:',
    jp: 'プレイ済み平均:',
  },
  RATING_AVERAGE: {
    ex: 'Average: ',
    cn: '平均:',
    jp: '平均:',
  },
  RATING_TOTAL: {
    ex: 'Total: ',
    cn: '总计:',
    jp: '合計:',
  },
}

// createElement
function _(e,t,i){var a=null;if("text"===e)return document.createTextNode(t);a=document.createElement(e);for(var n in t)if("style"===n)for(var o in t.style)a.style[o]=t.style[o];else if("className"===n)a.className=t[n];else if("event"===n)for(var o in t.event)a.addEventListener(o,t.event[o]);else a.setAttribute(n,t[n]);if(i)if("string"==typeof i)a.innerHTML=i;else if(Array.isArray(i))for(var l=0;l<i.length;l++)null!=i[l]&&a.appendChild(i[l]);return a}

const localStorageTimeKey = 'CNDL_music_level_info_time'
const localStorageDataKey = 'CNDL_music_level_info'
const localStoragePrefHighestOnlyKey = 'CNDL_music_level_info_highest_only'
let musicLevelInfo = {}
function loadLocalInfo() {
  if (!localStorage[localStorageDataKey]) return
  musicLevelInfo = JSON.parse(localStorage[localStorageDataKey])
}
let resultCount = {
  sssp: 0,
  sss: 0,
  ssp: 0,
  ss: 0,
  sp: 0,
  s: 0,
  other: 0,

  max: 0,
  aj: 0,
  fc: 0,
  nonfc: 0,
}
const diffToggles = {
  bas: true,
  adv: true,
  exp: true,
  mas: true,
  ult: true,
  other: true,
}
const diffTogglesDisplay = {
  bas: false,
  adv: false,
  exp: false,
  mas: false,
  ult: false,
  other: false,
}
let musicOnPage
let sortForm
let countHightestOnly = localStorage[localStoragePrefHighestOnlyKey] === '1'
function initPageOverPower() {
  musicOnPage = [...document.querySelectorAll('.musiclist_box .music_title')].map(addOverPowerToList)
  musicOnPage.forEach(i => {
    if (diffTogglesDisplay[i.dif] !== undefined) {
      diffTogglesDisplay[i.dif] = true
    } else {
      diffTogglesDisplay.other = true
    }
  })
  const scoreList = document.querySelector('#scoreList_result')
  if (!scoreList) return
  scoreList.appendChild(_('div', {className: 'box01 w420 CNDO-sort-box'}, [
    _('div', {}, [ sortForm = _('form', { event: { change: applySort }}, [
      _('select', { style: { width: '160px', margin: '10px' }, name: 'key' }, [
        _('option', {value: 'initial'}, [_('text', PAGE_TEXT.SORT_INITIAL[server])]),
        _('option', {value: 'level'}, [_('text', PAGE_TEXT.SORT_DIFFICULTY[server])]),
        _('option', {value: 'score'}, [_('text', PAGE_TEXT.SORT_SCORE[server])]),
        _('option', {value: 'aj'}, [_('text', 'FC/AJ')]),
        _('option', {value: 'op_percent'}, [_('text', 'op %')]),
        _('option', {value: 'op_remain'}, [_('text', PAGE_TEXT.SORT_OP_REMAIN[server])]),
      ]),
      _('select', { style: { width: '96px', margin: '10px' }, name: 'desc' }, [
        _('option', {value: 0}, [_('text', '↑')]),
        _('option', {value: 1}, [_('text', '↓')]),
      ]),
    ])]),
  ]))
  addOverPowerToPage()
}
function addOverPowerToPage() {
  const musics = musicOnPage.filter(i => {
    if (i.disabled) return false
    let on = diffToggles[i.dif]
    on ??= diffToggles.other
    return on
  })
  const countedSize = musics.length
  const filteredSize = musics.filter(i => !countHightestOnly || !i.notHighestLevel).length
  document.querySelector('.CNDO-info-box')?.remove()
  let resultCount = {
    sssp: 0,
    sss: 0,
    ssp: 0,
    ss: 0,
    sp: 0,
    s: 0,
    other: countedSize,

    max: 0,
    aj: 0,
    fc: 0,
    nonfc: countedSize,
  }
  const total = musics.reduce((a, i) => {
    const obj = i.parsed
    if (obj.scoreRank >=8) { resultCount.other--; }
    if (obj.scoreRank >=8) { resultCount.s++; }
    if (obj.scoreRank >=9) { resultCount.sp++; }
    if (obj.scoreRank >=10) { resultCount.ss++; }
    if (obj.scoreRank >=11) { resultCount.ssp++; }
    if (obj.scoreRank >=12) { resultCount.sss++; }
    if (obj.scoreRank >=13) { resultCount.sssp++; }
    if (obj.theoryCount) { resultCount.max++; }

    if (obj.isAllJustice) { resultCount.aj++; }
    if (obj.isFullCombo) { resultCount.fc++; resultCount.nonfc--; }

    if (countHightestOnly && i.notHighestLevel) {
      return a;
    }
    return {rate: a.rate+(i.rate), max: a.max+i.max, playedmax: a.playedmax+(i.rate>0?i.max:0)}
  }, {rate: 0, max: 0, playedmax: 0})
  total.rate /= 100
  total.max /= 100
  total.playedmax /= 100
  // console.log(total, total.rate/total.max*100)

  const scoreList = document.querySelector('#scoreList_result')
  if (!scoreList) return
  let diffToggleForm
  scoreList.appendChild(_('div', {className: 'box01 w420 CNDO-info-box'}, [
    _('style', {}, `

.overpower-graph-base {
  position:relative;
  width:100%;
  height:22px;
  background:#6e6e6e;
  outline:#dddddd 1px solid
}
.overpower-graph-base .bg-gold,
.overpower-graph-base .bg-platinum,
.overpower-graph-base .bg-silver,
.overpower-graph-base .bg-white {
  position:absolute;
  left:0;
  top:0;
  bottom:0
}
.overpower-graph-base .bg-white {
  z-index:12;
  background:linear-gradient(180deg,#fff,#fff 50%,#d0d6da 50%,#e4edf3 75%,#fff)!important
}
.overpower-graph-base .bg-platinum {
  z-index:10;
  background:linear-gradient(180deg,#fff1ba,#ffeb9c 50%,#fc0 50%,#ffeca4 75%,#fff)!important
}
.overpower-graph-base .bg-gold {
  z-index:8;
  background:linear-gradient(180deg,#ffe4a3,#efae10 50%,#ff8300 50%,#ffc947 75%,#fff)!important
}
.overpower-graph-base .bg-silver {
  z-index:6;
  background:linear-gradient(180deg,#c8e7ff,#8fceff 50%,#6eb5ff 50%,#b7ddff 75%,#fff)!important
}
.CNDO-version-box.hide :not(.CNDO-version-title) {
  display:none;
}
.CNDO-version-box.hide .CNDO-version-title::before {
  content: '▶';
}
.CNDO-version-box .CNDO-version-title::before {
  content: '▼';
}

    `),
    _('div', {className: 'narrow_block clearfix', style: {whiteSpace:'pre-wrap'}}, [
      diffToggleForm = _('form', { event: { change: e => {
        diffToggles[e.target.name] = e.target.checked
        musicOnPage.forEach(i => {
          if (i.dif !== e.target.name) return
          i.box.style.display = e.target.checked ? '' : 'none'
        })
        addOverPowerToPage()
      } } }, [
        !diffTogglesDisplay.bas ? new Comment('bas toggle') : _('label', {}, [
          _('input', {type: 'checkbox', name: 'bas', [diffToggles.bas ? 'checked' : 'checked_']: '1' }),
          _('text', 'BAS'),
        ]),
        !diffTogglesDisplay.adv ? new Comment('adv toggle') : _('label', {}, [
          _('input', {type: 'checkbox', name: 'adv', [diffToggles.adv ? 'checked' : 'checked_']: '1' }),
          _('text', 'ADV'),
        ]),
        !diffTogglesDisplay.exp ? new Comment('exp toggle') : _('label', {}, [
          _('input', {type: 'checkbox', name: 'exp', [diffToggles.exp ? 'checked' : 'checked_']: '1' }),
          _('text', 'EXP'),
        ]),
        !diffTogglesDisplay.mas ? new Comment('mas toggle') : _('label', {}, [
          _('input', {type: 'checkbox', name: 'mas', [diffToggles.mas ? 'checked' : 'checked_']: '1' }),
          _('text', 'MAS'),
        ]),
        !diffTogglesDisplay.ult ? new Comment('ult toggle') : _('label', {}, [
          _('input', {type: 'checkbox', name: 'ult', [diffToggles.ult ? 'checked' : 'checked_']: '1' }),
          _('text', 'ULT'),
        ]),
        !diffTogglesDisplay.other ? new Comment('other toggle') : _('label', {}, [
          _('input', {type: 'checkbox', name: 'other', [diffToggles.other ? 'checked' : 'checked_']: '1' }),
          _('text', 'OTHER'),
        ]),
      ]),
      _('label', {}, [
        _('input', {type: 'checkbox', [countHightestOnly ? 'checked' : 'checked_']: '1', event: { change: e => {
          countHightestOnly = e.target.checked
          localStorage[localStoragePrefHighestOnlyKey] = countHightestOnly ? '1' : '0'
          addOverPowerToPage()
        } } }),
        _('text', PAGE_TEXT.ONLY_HIGHEST[server]),
      ]), _('br'),
      _('text', [
        PAGE_TEXT.TOTAL_SONG_COUNT[server].replace('{}', filteredSize),
        `${total.rate} / ${total.max}`,
        (total.rate/Math.max(total.max, 1)*100).toFixed(4)+'%',
        '',
        PAGE_TEXT.AVERAGE_PLAYED[server],
        //`${total.rate} / ${total.playedmax}`,
        (total.rate/Math.max(total.playedmax, 1)*100).toFixed(4)+'%',
        '','',
      ].join('\n')),
      _('table', { style: {width: '100%', fontSize: '16px'} }, [
        _('tr', {}, [
          _('td', {}, [_('text', 'SSS+')]),
          _('td', {}, [_('text', 'SSS')]),
          _('td', {}, [_('text', 'SS+')]),
          _('td', {}, [_('text', 'SS')]),
          _('td', {}, [_('text', 'S+')]),
          _('td', {}, [_('text', 'S')]),
          _('td', {}, [_('text', 'OTHER')]),
        ]),
        _('tr', {}, [
          _('td', {}, [_('text', resultCount.sssp)]),
          _('td', {}, [_('text', resultCount.sss - resultCount.sssp)]),
          _('td', {}, [_('text', resultCount.ssp - resultCount.sss)]),
          _('td', {}, [_('text', resultCount.ss - resultCount.ssp)]),
          _('td', {}, [_('text', resultCount.sp - resultCount.ss)]),
          _('td', {}, [_('text', resultCount.s - resultCount.sp)]),
          _('td', {}, [_('text', resultCount.other)]),
        ]),
      ]),
      _('div', {className: 'overpower-graph-base'}, [
        _('div', {className: 'bg-platinum', style: {width: `${resultCount.sss/countedSize*100}%`}}),
        _('div', {className: 'bg-gold', style: {width: `${resultCount.ss/countedSize*100}%`}}),
        _('div', {className: 'bg-silver', style: {width: `${resultCount.s/countedSize*100}%`}}),
      ]),
      _('table', { style: {width: '100%', fontSize: '16px'} }, [
        _('tr', {}, [
          _('td', {}, [_('text', 'MAX')]),
          _('td', {}, [_('text', 'AJ')]),
          _('td', {}, [_('text', 'FC')]),
          _('td', {}, [_('text', 'OTHER')]),
        ]),
        _('tr', {}, [
          _('td', {}, [_('text', resultCount.max)]),
          _('td', {}, [_('text', resultCount.aj - resultCount.max)]),
          _('td', {}, [_('text', resultCount.fc - resultCount.aj)]),
          _('td', {}, [_('text', resultCount.nonfc)]),
        ]),
      ]),
      _('div', {className: 'overpower-graph-base'}, [
        _('div', {className: 'bg-white', style: {width: `${resultCount.max/countedSize*100}%`}}),
        _('div', {className: 'bg-platinum', style: {width: `${resultCount.aj/countedSize*100}%`}}),
        _('div', {className: 'bg-gold', style: {width: `${resultCount.fc/countedSize*100}%`}}),
      ]),
    ]),
  ]))

  if (diffToggleForm.children.length === 1) {
    diffToggleForm.remove()
  }
  applySort.call(sortForm)
}
function addOverPowerToList(titleDiv, idx) {
  const dif = getDifFromClass(titleDiv.parentNode)
  if (!dif) return null
  const box = titleDiv.parentNode
  const titleText = titleDiv.lastChild.textContent.trim()
  const level = getLevelByTitleAndDif(titleText, dif) * 1
  const returnData = {
    box,
    idx,
    dif,
    level,
    rate: 0,
    disabled: disabledSongs[server].includes(titleText),
    notHighestLevel: true,
    parsed: {},
    max: (level+3) * 500
  }
  const scoreBox = box.querySelector('.play_musicdata_highscore .text_b')
  if (!scoreBox) return returnData
  if (returnData.disabled) {
    return returnData
  }
  const obj = {
    scoreMax:      scoreBox.textContent.replace(/,/g, '')*1,
    maxComboCount: 0,
    isFullCombo:   false,
    isAllJustice:  false,
    isSuccess:     0,
    fullChain:     0,
    maxChain:      0,
    scoreRank:     0,
    isLock:        false,
    theoryCount:   0,
  }
  if (obj.scoreMax === 1010000) { obj.theoryCount = 1; }
  [...box.querySelectorAll('.play_musicdata_icon img')].forEach(i => {
    const icon = i.src.replace(/.+\//, '').replace(/icon_(.+)\.png/, '$1')
    switch (icon) {
      case 'clear': { obj.isSuccess = 1; break }
      case 'hard': { obj.isSuccess = 2; break }
      case 'absolute': { obj.isSuccess = 3; break }
      case 'brave': { obj.isSuccess = 3; break }
      case 'absolutep': { obj.isSuccess = 4; break }
      case 'absolutepp': { obj.isSuccess = 5; break }
      case 'catastrophy': { obj.isSuccess = 6; break }
      
      case 'rank_1': { obj.scoreRank = 1; break }
      case 'rank_2': { obj.scoreRank = 2; break }
      case 'rank_3': { obj.scoreRank = 3; break }
      case 'rank_4': { obj.scoreRank = 4; break }
      case 'rank_5': { obj.scoreRank = 5; break }
      case 'rank_6': { obj.scoreRank = 6; break }
      case 'rank_7': { obj.scoreRank = 7; break }
      case 'rank_8': { obj.scoreRank = 8; break }
      case 'rank_9': { obj.scoreRank = 9; break }
      case 'rank_10': { obj.scoreRank = 10; break }
      case 'rank_11': { obj.scoreRank = 11; break }
      case 'rank_12': { obj.scoreRank = 12; break }
      case 'rank_13': { obj.scoreRank = 13; break }
      
      case 'alljusticecritical':
      case 'alljustice': { obj.isAllJustice = true }
      case 'fullcombo': { obj.isFullCombo = true; obj.missCount = 0; break }
      
      case 'fullchain2': { obj.fullChain = 1; break }
      case 'fullchain': { obj.fullChain = 2; break }
    }
  })
  returnData.parsed = obj

  // lmn+逻辑
  // 存在更高等级的ult,mas不计op
  if (dif === 'mas') {
    const ultLevel = getLevelByTitleAndDif(titleText, 'ult') * 1
    if (level >= ultLevel) {
      returnData.notHighestLevel = false
    }
  } else if (dif === 'ult') {
    const masLevel = getLevelByTitleAndDif(titleText, 'mas') * 1
    if (level > masLevel) {
      returnData.notHighestLevel = false
    }
  }

  returnData.rating = getRatingFromConstantAndScore(level, obj.scoreMax)
  const comboRate = obj.theoryCount ? 125 :
                    obj.isAllJustice ? 100 :
                    obj.isFullCombo ? 50 : 0
  if (obj.scoreMax <= 1007500) {
    // 低于sss
    returnData.rate = Math.floor(returnData.rating*500 * 2) / 2 + comboRate
    addOverPowerAfterScore(scoreBox, returnData)
    return returnData
  }
  const sssRate = Math.floor((obj.scoreMax - 1007500) * 0.15 * 2) / 2
  returnData.rate = (level + 2) * 500 + comboRate + sssRate
  addOverPowerAfterScore(scoreBox, returnData)
  return returnData
}
function addOverPowerAfterScore(scoreBox, returnData) {
  scoreBox.parentNode.appendChild(_('span', {style: {marginLeft: '2em', fontFamily: 'Arial'}}, [
    _('text', (Math.floor(returnData.rate)/100).toFixed(2) + ' / ' + (returnData.max/100).toFixed(2) + ' ' + (returnData.rate/returnData.max*100).toFixed(2)+'%'),
  ]))
}
function getDifFromClass(div) {
	const divClass = [...div.classList].filter(i => i.startsWith('bg_') || i.startsWith('title_'))
	if (!divClass.length) return '?'
	switch (divClass[0]) {
		case 'bg_basic': { return 'bas' }
		case 'bg_advanced': { return 'adv' }
		case 'bg_expert': { return 'exp' }
		case 'bg_master': { return 'mas' }
		case 'bg_ultima': { return 'ult' }

		case 'title_basic': { return 'bas' }
		case 'title_advanced': { return 'adv' }
		case 'title_expert': { return 'exp' }
		case 'title_master': { return 'mas' }
		case 'title_ultima': { return 'ult' }
	}
	return '?'
}
function getLevelByTitleAndDif(title, dif) {
  if (!musicLevelInfo[title]) return 0
  if (!musicLevelInfo[title][dif]) return 0
  return musicLevelInfo[title][dif].replace('+', '.5')
}
var ratingBorder = [
  [1009000, +2.15, 1],
  [1007500, +2.0, 1],
  [1005000, +1.5, 1],
  [1000000, +1.0, 1],
  [975000, +0.0, 1],
  [925000, -3.0, 1],
  [900000, -5.0, 1],
  [800000, -5.0, 2],
  [500000, 0, 1]
];
function getRatingFromConstantAndScore(constant, score) {
  if (constant == 0) return 0;
  if (score >= ratingBorder[0][0]) return constant + ratingBorder[0][1];
  for (var i = 0; i < ratingBorder.length - 1; i++) {
    if (score >= ratingBorder[i + 1][0]) {
      return (constant + ratingBorder[i + 1][1]) / ratingBorder[i + 1][2]
      + (score - ratingBorder[i + 1][0]) / (ratingBorder[i][0] - ratingBorder[i + 1][0])
      * (
      (constant + ratingBorder[i][1]) / ratingBorder[i][2] -
      (constant + ratingBorder[i + 1][1]) / ratingBorder[i + 1][2]
      )
    }
  }
  return 0;
}
function applySort() {
  const form = this
  const key = form.key.value
  const desc = form.desc.value === '1'
  musicOnPage.sort((a, b) => (desc ? -1 : 1) * (a.idx - b.idx))
  if (key !== 'initial') {
    musicOnPage.sort((a, b) => {
      let result = 0
      switch (key) {
        case 'level': { result = a.level - b.level; break }
        case 'score': { result = (a.parsed.scoreMax || 0) - (b.parsed.scoreMax || 0); break }
        case 'aj': { result = toAjScore(a) - toAjScore(b); break }
        case 'op_percent': { result = a.rate/a.max - b.rate/b.max; break }
        case 'op_remain': { result = (a.max - a.rate) - (b.max - b.rate); break }
      }
      return desc ? -result : result
    })
  }
  musicOnPage.forEach((i, idx) => {
    i.box.parentNode.parentNode.appendChild(i.box.parentNode)
  })
}
function toAjScore(i) {
  return i.parsed.theoryCount ? 3 : (
    i.parsed.isAllJustice ? 2 : (
      i.parsed.isFullCombo ? 1 : 0
    )
  )
}

const statisticJudge = [
  ["clear"              , i => i.parsed.isSuccess >= 1 ],
  ["hard"               , i => i.parsed.isSuccess >= 2 ],
  ["absolute"           , i => i.parsed.isSuccess >= 3 ],
  ["absolutep"          , i => i.parsed.isSuccess >= 4 ],
  ["catastrophy"        , i => i.parsed.isSuccess >= 6 ],
  ["rank_8"             , i => i.parsed.scoreRank >= 8 ],
  ["rank_9"             , i => i.parsed.scoreRank >= 9 ],
  ["rank_10"            , i => i.parsed.scoreRank >= 10 ],
  ["rank_11"            , i => i.parsed.scoreRank >= 11 ],
  ["rank_12"            , i => i.parsed.scoreRank >= 12 ],
  ["rank_13"            , i => i.parsed.scoreRank >= 13 ],
  ["fullcombo"          , i => i.parsed.isFullCombo ],
  ["alljustice"         , i => i.parsed.isAllJustice ],
  ["alljusticecritical" , i => i.parsed.theoryCount > 0 ],
  ["fullchain2"         , i => i.parsed.fullChain >= 1 ],
  ["fullchain"          , i => i.parsed.fullChain >= 2 ],
]
const VERSION_MUSIC_LIST = [["CHUNITHM",[3,6,18,21,23,27,33,38,41,45,46,47,48,49,50,51,53,59,63,64,65,67,68,69,70,71,74,75,76,79,80,81,82,83,88,89,91,92,94,95,96,97,98,99,100,101,103,104,105,107,108,113,114,115,117,118,120,128,132,133,134,135,136,138,140,141,142,143,144,145,146,147,148,149,150,151,152,157,158,159,160,161,163,165,166,167,168,169,170,178,179,180,181,203,204]],["CHUNITHM PLUS",[19,35,52,61,62,72,73,90,93,106,121,122,123,131,171,173,177,196,197,199,200,201,202,205,208,210,211,212,213,216,217,220,222,223,224,225,226,227,228,232,233,240,244,245,246,251,252]],["CHUNITHM AIR",[7,37,66,77,102,119,137,186,187,189,190,218,219,229,230,248,250,254,257,259,260,261,262,263,264,267,270,271,272,273,275,276,278,279,280,281,282,283,284,286,287,288,289,290,291,292,293,294,297,298,300,301,302,303,304,305,306,307,316,317,318,319,320,321,322,323,324,325,326,327,328,329,330,331,332,336]],["CHUNITHM AIR PLUS",[20,191,192,193,194,195,234,249,253,256,258,265,266,268,274,277,295,310,334,335,337,338,339,340,341,342,354,362,363,365,367,368,369,370,371,372,373,374,375,376,377,379,380,381,382,383,384,385,386,388,389,390,393,394,395,396,397,398,399,402,403,404,405,407,409,411,414,416]],["CHUNITHM STAR",[406,421,426,427,430,431,432,433,434,435,436,437,438,439,440,441,442,444,445,446,447,448,449,455,456,457,458,459,463,464,466,467,468,469,470,471,472,475,476,477,478,479,481,482,483,485,486,487,488,489,490,492,493,494,496,497,498,499,500,502,503,504,505,506,512,513,514,516,524,525,528,532,533,535,538,540]],["CHUNITHM STAR PLUS",[24,198,391,413,460,462,465,491,515,534,547,548,549,550,551,552,553,554,555,556,557,558,559,560,561,562,564,565,566,567,568,569,570,572,574,575,577,578,583,585,586,587,588,589,590,592,593,594,595,597,599,600,601,605,606,607,614,615,616,617,618,625,626,627,628,629,631,632,633,635,636,637,638]],["CHUNITHM AMAZON",[517,573,619,653,654,655,656,657,659,662,663,664,665,666,667,668,669,670,671,676,677,678,679,681,683,684,685,686,688,689,690,691,692,693,694,695,696,697,698,699,700,701,702,703,704,705,706,707,708,710,711,712,713,715,716,717,720,721,722]],["CHUNITHM AMAZON PLUS",[285,598,604,729,730,731,732,733,734,735,736,737,738,739,740,741,742,743,744,745,746,747,749,750,751,752,753,754,755,756,757,760,761,762,763,764,765,766,768,771,772,773,774,775,776,777,780,781,782,783,784,785,786,787,788,791,792,793,795,796,797,798,799,802,803,804,806,807]],["CHUNITHM CRYSTAL",[429,810,815,816,817,818,819,821,822,823,824,825,826,827,828,829,830,831,832,833,834,835,836,837,838,839,840,841,842,843,844,845,846,848,849,850,851,852,853,854,856,858,859,860,863,865,867,868,869,870,871,872,873,874,875,876,877,878,879,880,881,882,885,886,887,888,889,891,893,894,895,896,897,900,901]],["CHUNITHM CRYSTAL PLUS",[333,899,909,910,911,912,913,914,915,916,917,918,921,922,923,926,927,928,929,930,931,932,933,934,935,936,937,938,939,940,941,942,943,944,945,946,947,948,949,950,953,954,955,958,959,960,961,962,966,967,969,970,971,972,973,974,975,976,978,979,980,981,982,983,984,987]],["CHUNITHM PARADISE",[968,988,989,990,991,992,993,994,997,998,999,1002,1004,1006,1007,1008,1009,1010,1011,1012,1013,1014,1015,1016,1017,1019,1020,1021,1022,1023,1025,1026,1027,1028,1029,1030,1031,1032,1033,1035,1037,1040,1041,1042,1043,1044,1091,1092,1098,1099]],["CHUNITHM PARADISE LOST",[1005,1048,1055,1056,1057,1059,1060,1061,1062,1063,1064,1066,1067,1068,1069,1070,1071,1072,1073,1074,1075,1076,1077,1078,1079,1080,1081,1082,1083,1084,1085,1086,1087,1088,1089,1090,1096,1100,1101,1102,1103,1104,1105,1106,2001,2002,2048,2070,2071,2072,2073,2074,2077,2079,2080,2081,2082,2083,2084,2085,2086,2087,2089,2101,2102,2103,2106]],["CHUNITHM NEW",[511,1038,1058,1065,1094,2010,2017,2018,2019,2022,2023,2024,2025,2026,2028,2029,2030,2031,2032,2033,2034,2035,2036,2037,2038,2040,2042,2043,2044,2045,2046,2047,2049,2050,2052,2053,2054,2055,2056,2058,2059,2060,2061,2062,2063,2064,2065,2066,2067,2068,2069,2090,2091,2092,2093,2094,2096,2097,2098,2099,2105,2108,2109,2110,2111,2114,2115,2116,2117,2118,2119,2120,2121,2122,2124,2125,2126,2127,2134,2135,2137,2138,2140,2144,2145,2146,2147,2148,2149,2150,2151,2152,2153,2154,2155,2156,2157,2158,2160,2161,2162,2163,2164,2166,2167,2168,2169,2170]],["CHUNITHM NEW PLUS",[1039,1107,2041,2057,2128,2129,2130,2131,2132,2133,2165,2171,2172,2173,2174,2175,2176,2177,2178,2179,2180,2181,2182,2183,2184,2185,2186,2187,2188,2189,2190,2191,2192,2193,2194,2195,2196,2197,2198,2199,2200,2201,2202,2203,2204,2205,2206,2207,2208,2209,2210,2211,2212,2213,2214,2215,2216,2217,2218,2219,2220,2221,2222,2223,2224,2225,2226,2227,2228,2229,2230,2231,2232,2233,2234,2235,2236,2237,2238,2239,2240,2241,2242,2243,2244,2245,2246,2247,2248,2249,2250,2251,2252]],["CHUNITHM SUN",[428,2253,2254,2255,2257,2258,2259,2260,2261,2262,2263,2264,2265,2266,2267,2268,2269,2270,2271,2272,2273,2274,2275,2276,2277,2278,2279,2280,2281,2282,2283,2285,2286,2287,2288,2289,2290,2291,2292,2293,2294,2295,2296,2302,2303,2304,2305,2306,2307,2308,2310,2311,2312,2313,2314,2315,2316,2317,2318,2319,2320,2321,2322,2323,2324,2326,2327,2328,2329,2330,2331,2332,2333,2334,2336,2337,2338,2339,2340,2341,2342,2343,2344,2345,2346,2347,2348,2349,2350,2351,2353,2354,2355,2356,2358,2359,2360,2361,2362,2363,2364,2365,2366,2367,2368,2369,2370,2371,2372,2373,2381,2382,2383,2384,2385]],["CHUNITHM SUN PLUS",[2297,2298,2299,2300,2301,2309,2352,2386,2387,2388,2389,2390,2391,2392,2393,2394,2395,2397,2398,2399,2400,2401,2403,2404,2405,2406,2407,2408,2409,2410,2411,2412,2413,2414,2415,2416,2417,2418,2420,2421,2422,2425,2426,2427,2428,2429,2430,2431,2432,2433,2434,2435,2436,2437,2438,2439,2440,2441,2442,2443,2444,2445,2446,2447,2448,2449,2450,2451,2452,2453,2454,2455,2456,2457,2458,2459,2460,2461,2463,2464,2465,2466,2467,2468,2469,2475,2476,2477,2478,2479,2480,2481,2482,2483,2485,2486,2487,2488,2489]],["CHUNITHM LUMINOUS",[2490,2491,2492,2493,2494,2495,2496,2497,2498,2499,2500,2501,2502,2503,2504,2505,2506,2507,2508,2509,2510,2511,2512,2513,2514,2515,2517,2518,2519,2520,2521,2522,2523,2524,2525,2526,2528,2529,2530,2531,2532,2533,2534,2535,2536,2537,2538,2539,2540,2542,2543,2544,2545,2546,2547,2548,2549,2550,2552,2553,2554,2555,2556,2557,2559,2560,2561,2562,2563,2564,2565,2566,2567,2568,2569,2570,2571,2572,2573,2574,2575,2576,2577,2578,2579,2580,2581,2582,2583]],["CHUNITHM LUMINOUS PLUS",[2584,2585,2586,2587,2589,2591,2592,2593,2594,2596,2597,2598,2599,2600,2601,2602,2603,2604,2605,2606,2610,2611,2612,2613,2614,2615,2616,2618,2619,2620,2621,2622,2623,2624,2625,2629,2630,2631,2632,2633,2634,2635,2636,2637,2638,2639,2641,2642,2643,2644,2645,2646,2647,2648,2650,2651,2652,2653,2654,2657,2658,2659,2660,2661,2663,2665,2670,2671,2673,2674,2675,2676,2677,2679,2680,2681]],["CHUNITHM VERSE",[]]]
function reorderPageIntoVersion() {
  const tempKeyedMusicList = {}
  musicOnPage.forEach(i => tempKeyedMusicList[i.box.querySelector('[name=idx]').value]=i)
  const currentBoxes = [...document.querySelectorAll('.box05')]
  if (currentBoxes.length === 0) return
  const container = currentBoxes[0].parentNode
  VERSION_MUSIC_LIST.forEach(([version, list]) => {
    const statisticCount = statisticJudge.reduce((acc, [key]) => { acc[key] = 0; return acc }, {})
    const statisticBox = _('div', {className: 'box01 w420 font_0 text_black', style: {transform:'scale(0.93)',transformOrigin:'left center'}})
    const versionBox = _('div', {className: 'box05 w400 CNDO-version-box'}, [
      _('div', {className: 'genre scroll_point text_white CNDO-version-title', event: {click: e => versionBox.classList.toggle('hide')}}, [_('text', version)]),
      statisticBox,
    ])
    let versionMusicCount = 0
    list.forEach(id => {
      if (!tempKeyedMusicList[id]) return
      versionBox.appendChild(tempKeyedMusicList[id].box.parentNode)
      versionMusicCount++
      statisticJudge.forEach(([key, judge]) => {
        if (judge(tempKeyedMusicList[id])) {
          statisticCount[key]++
        }
      })
    })
    if (versionBox.children.length === 2) return
    const statisticList = statisticJudge.map(([key]) => _('div', { className: 'score_list'}, [
      _('div', {className: 'score_list_top'}, [
        _('img', {src: `${imageBase}/icon_${key}.png`})
      ]),
      _('div', {className: 'score_list_bottom'}, [
        _('div', {className: 'score_num_text'}, [_('text', statisticCount[key])]),
        _('div', {className: 'score_all_text font_small'}, [_('text', `/${versionMusicCount}`)]),
      ]),
    ]))
    statisticList.splice(11, 0, _('br'))
    statisticList.splice(5, 0, _('br'))
    statisticBox.appendChild(_('div', {className: 'score_list_block'}, statisticList))
    container.appendChild(versionBox)
  })
  currentBoxes.forEach(i => i.remove())
  setTimeout(() => applySort.call(sortForm), 50)
}

let totalRating = 0
function addRatingToPage() {
  const musics = [...document.querySelectorAll('.musiclist_box .music_title')].map(addRatingToList)
  if (!musics.length) return

  const boxInfoText = document.querySelector('.box01 .font_x-small')
  if (!boxInfoText) return
	boxInfoText.appendChild(_('br'))
  boxInfoText.appendChild(_('span', {}, [_('text', `${PAGE_TEXT.RATING_AVERAGE[server]}${(totalRating / musics.length).toFixed(3)} (${musics.length}) ${PAGE_TEXT.RATING_TOTAL[server]}${totalRating.toFixed(2)}`)]))
}
function addRatingToList(titleDiv) {
  const dif = getDifFromClass(titleDiv.parentNode)
  if (!dif) return null
  const box = titleDiv.parentNode
  const titleText = titleDiv.lastChild.textContent.trim()
  const level = getLevelByTitleAndDif(titleText, dif) * 1
  const returnData = {
    rating: 0,
    disabled: disabledSongs[server].includes(titleText),
    ratingMax: level + 2.15,
  }
  const scoreBox = box.querySelector('.play_musicdata_highscore .text_b')
  if (!scoreBox) return returnData
  const scoreMax = scoreBox.textContent.replace(/,/g, '')*1

  returnData.rating = getRatingFromConstantAndScore(level, scoreMax)
  addRatingAfterScore(scoreBox, returnData)
	totalRating += Math.floor(returnData.rating *100) /100
  return returnData
}
function addRatingAfterScore(scoreBox, returnData) {
  scoreBox.parentNode.appendChild(_('span', {style: {marginRight: '.5em', fontFamily: 'Arial', float: 'right'}}, [
    _('text', (returnData.rating === returnData.ratingMax ? returnData.rating.toFixed(2) : returnData.rating.toFixed(3)) + ' / ' + returnData.ratingMax.toFixed(2)),
  ]))
}

loadLocalInfo()
if (location.href.indexOf('home/playerData/rating') !== -1) {
  addRatingToPage()
} else {
  initPageOverPower()
  if (document.querySelector('.btn_version') === null && document.querySelector('select[name=genre] *[value="99"][selected]') !== null) {
    reorderPageIntoVersion()
  }
}

})()