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.2.1
  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. pre.remove()
  51.  
  52. data.textContent = result
  53. PR.prettyPrint()
  54. }
  55.  
  56. const finished = endTime.toDate() < new Date()
  57. if (finished) {
  58. await formatInner()
  59. return
  60. }
  61.  
  62. document.body.insertAdjacentHTML('afterbegin', `
  63. <div id="modal-${id}-format-warning" class="modal fade" tabindex="-1" role="dialog">
  64. <div class="modal-dialog" role="document">
  65. <div class="modal-content">
  66. <div class="modal-header">
  67. <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
  68. <h4 class="modal-title">フォーマットの注意</h4>
  69. </div>
  70. <div class="modal-body">
  71. <p>このコンテストはまだ終了していません。</p>
  72. <p>フォーマットを行うとソースコードが外部に送信され、スクリプトの作成者が閲覧可能な状態になります。</p>
  73. <p>まだ公開されていないコンテストではフォーマットを行わないでください。</p>
  74. </div>
  75. <div class="modal-footer">
  76. <button class="btn btn-warning" id="${id}-force-format">フォーマットする</button>
  77. <button class="btn btn-success" id="${id}-cancel">キャンセル</button>
  78. </div>
  79. </div>
  80. </div>
  81. </div>
  82. `)
  83.  
  84. $(`#modal-${id}-format-warning`).modal('show')
  85. document.getElementById(`${id}-force-format`).addEventListener('click', async () => await formatInner(true))
  86. document.getElementById(`${id}-cancel`).addEventListener('click', removeModal)
  87. }
  88.  
  89. for (const pre of document.getElementsByClassName('prettyprint')) {
  90. const next = pre.nextElementSibling
  91. if (next.className !== 'source-code-for-copy') continue
  92. const id = next.id
  93.  
  94. let adding = pre.previousElementSibling
  95. while (adding.className === 'div-btn-copy')
  96. adding = adding.previousElementSibling
  97.  
  98. const buttonClass = endTime.toDate() < new Date() ? 'btn-info' : 'btn-danger'
  99.  
  100. adding.insertAdjacentHTML(
  101. 'afterend',
  102. `
  103. <div class="btn-group" role="group">
  104. <button type="button" class="btn ${buttonClass} btn-sm" >
  105. C++
  106. </button>
  107. <button type="button" class="btn ${buttonClass} btn-sm" id="${id}-fmt-py">
  108. Python
  109. </button>
  110. <button type="button" class="btn ${buttonClass} btn-sm" id="${id}-fmt-cs">
  111. C#
  112. </button>
  113. </div>`
  114. )
  115. for (const lang of ['cpp', 'py', 'cs']) {
  116. document.getElementById(`${id}-fmt-${lang}`)
  117. .addEventListener('click', async (e) => await formatCode(e, pre, id, lang))
  118. }
  119. }
  120. })()