AtCoder Formatter

Add formatting buttons to source codes on AtCoder.

As of 2022-11-14. See the latest version.

  1. // ==UserScript==
  2. // @name AtCoder Formatter
  3. // @name:en AtCoder Formatter
  4. // @namespace
  5. // @version 1.3.2
  6. // @description AtCoder の解説コードなどをフォーマットできるようにします.
  7. // @description:en Add formatting buttons to source codes on AtCoder.
  8. // @author kichi2004
  9. // @match https://atcoder.jp/contests/*
  10. // @grant none
  11. // @namespace kichi2004.jp
  12. // @license MIT
  13. // ==/UserScript==
  14.  
  15. 'use strict';
  16.  
  17. (async function () {
  18.  
  19. const formatCode = async (event, pre, id, lang) => {
  20. const removeModal = () => {
  21. $(`#modal-${id}-format-warning`).modal('hide')
  22. }
  23.  
  24. const formatInner = async (modal = false) => {
  25. if (modal) {
  26. removeModal()
  27. }
  28.  
  29. event.target.disabled = true
  30. const data = document.getElementById(id)
  31. const res = await fetch(`https://formatter.api.kichi2004.jp/format?lang=${lang}`, {
  32. body: data.innerText,
  33. method: 'POST'
  34. })
  35. event.target.disabled = false
  36. if (!res.ok) {
  37. alert('Formatting Request Failed!')
  38. return
  39. }
  40. const json = await res.json()
  41. if (json['status'] === 'error') {
  42. alert('Formatting Error!\n' + json['error'])
  43. return
  44. }
  45. const result = json['result']
  46. const nextPre = document.createElement('pre')
  47. nextPre.textContent = result
  48. nextPre.classList.add('prettyprint', `lang-${lang}`, 'linenums')
  49. pre.before(nextPre)
  50. const preId = pre.id
  51. pre.remove()
  52. if (preId) {
  53. nextPre.id = preId
  54. }
  55.  
  56. data.textContent = result
  57. PR.prettyPrint()
  58. }
  59.  
  60. const finished = endTime.toDate() < new Date()
  61. if (finished) {
  62. await formatInner()
  63. return
  64. }
  65.  
  66. document.body.insertAdjacentHTML('afterbegin', `
  67. <div id="modal-${id}-format-warning" class="modal fade" tabindex="-1" role="dialog">
  68. <div class="modal-dialog" role="document">
  69. <div class="modal-content">
  70. <div class="modal-header">
  71. <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
  72. <h4 class="modal-title">フォーマットの注意</h4>
  73. </div>
  74. <div class="modal-body">
  75. <p>このコンテストはまだ終了していません。</p>
  76. <p>フォーマットを行うとソースコードが外部に送信され、スクリプトの作成者が閲覧可能な状態になります。</p>
  77. <p>まだ公開されていないコンテストではフォーマットを行わないでください。</p>
  78. </div>
  79. <div class="modal-footer">
  80. <button class="btn btn-warning" id="${id}-force-format">フォーマットする</button>
  81. <button class="btn btn-success" id="${id}-cancel">キャンセル</button>
  82. </div>
  83. </div>
  84. </div>
  85. </div>
  86. `)
  87.  
  88. $(`#modal-${id}-format-warning`).modal('show')
  89. document.getElementById(`${id}-force-format`).addEventListener('click', async () => await formatInner(true))
  90. document.getElementById(`${id}-cancel`).addEventListener('click', removeModal)
  91. }
  92.  
  93. for (const pre of document.getElementsByClassName('prettyprint')) {
  94. const next = pre.nextElementSibling
  95. if (next.className !== 'source-code-for-copy') continue
  96. const id = next.id
  97.  
  98. let adding = pre.previousElementSibling
  99. while (adding.className === 'div-btn-copy')
  100. adding = adding.previousElementSibling
  101.  
  102. const buttonClass = endTime.toDate() < new Date() ? 'btn-info' : 'btn-danger'
  103.  
  104. adding.insertAdjacentHTML(
  105. 'afterend',
  106. `
  107. <div class="btn-group" role="group">
  108. <button type="button" class="btn ${buttonClass} btn-sm" id="${id}-fmt-cpp">
  109. C++
  110. </button>
  111. <button type="button" class="btn ${buttonClass} btn-sm" id="${id}-fmt-py">
  112. Python
  113. </button>
  114. <button type="button" class="btn ${buttonClass} btn-sm" id="${id}-fmt-cs">
  115. C#
  116. </button>
  117. </div>`
  118. )
  119. for (const lang of ['cpp', 'py', 'cs']) {
  120. document.getElementById(`${id}-fmt-${lang}`)
  121. .addEventListener('click', async (e) => await formatCode(e, pre, id, lang))
  122. }
  123. }
  124. })()