Github QoMar

QoMar file support for Github

  1. // ==UserScript==
  2. // @name Github QoMar
  3. // @namespace Camroku/gh-qm
  4. // @version 0.1
  5. // @description QoMar file support for Github
  6. // @author Cinar Yilmaz <cinaryilmaz.gnu@gmail.com>
  7. // @match https://github.com/*
  8. // @icon https://www.google.com/s2/favicons?sz=64&domain=github.com
  9. // @grant none
  10. // @require https://code.jquery.com/jquery-3.6.1.min.js
  11. // @license GPLv3
  12. // ==/UserScript==
  13.  
  14. String.prototype.isspace = function (c) {
  15. return c == '\t' || c == '\n' || c == '\v' || c == '\f' || c == '\r' || c == ' '
  16. }
  17.  
  18. class QomarCompiler {
  19. constructor(text) {
  20. this.text = text
  21. this.pos = 0
  22. this.current_char = this.text[this.pos]
  23. this.out = ""
  24. }
  25.  
  26. advance(characters = 1) {
  27. this.pos += characters
  28. if (this.pos >= this.text.length) { this.current_char = null }
  29. else { this.current_char = this.text[this.pos] }
  30. }
  31.  
  32. peek(characters = 1) {
  33. var peekpos = this.pos + characters
  34. if (peekpos >= this.text.length) { return null }
  35. else { return this.text[peekpos] }
  36. }
  37.  
  38. skipcomment() {
  39. this.advance(2)
  40. while (this.current_char != '*' && this.peek() != '/') { this.advance() }
  41. this.advance(2)
  42. }
  43.  
  44. code() {
  45. this.advance()
  46. var escaped = false
  47. this.out += "<code>"
  48. while (this.current_char !== null) {
  49. if (escaped) {
  50. this.out += this.current_char
  51. escaped = false
  52. } else {
  53. if (this.current_char == '`') {
  54. break
  55. } else if (this.current_char == '\\') {
  56. escaped = true
  57. } else {
  58. this.out += this.current_char
  59. }
  60. }
  61. this.advance()
  62. }
  63. }
  64.  
  65. blockcode() {
  66. this.advance(3)
  67. var escaped = false
  68. this.out += "<pre>"
  69. while (this.current_char !== null) {
  70. if (escaped) {
  71. this.out += this.current_char
  72. escaped = false
  73. } else {
  74. if (this.current_char == '`' && this.peek() == '`' && this.peek(2) == '`') {
  75. break
  76. } else if (this.current_char == '\\') {
  77. escaped = true
  78. } else {
  79. this.out += this.current_char
  80. }
  81. }
  82. this.advance()
  83. }
  84. this.advance(2)
  85. this.out += "</pre>"
  86. }
  87.  
  88. skipspace() {
  89. while (this.current_char != null && this.current_char.isspace()) {
  90. if (this.current_char == '\n' && this.peek() == '\n') {
  91. this.out += "</p><p>"
  92. this.advance()
  93. }
  94. else if (this.current_char == '\n') {
  95. this.out += "<br/>"
  96. }
  97. this.advance()
  98. }
  99. if (this.peek(-1) != '\n') { this.out += " " }
  100. }
  101.  
  102. link() {
  103. this.advance()
  104. var linkntext = ""
  105. while (this.current_char != null) {
  106. if (this.current_char == ']') { break }
  107. else { linkntext += this.current_char }
  108. this.advance()
  109. }
  110. this.advance()
  111.  
  112. var splitted = linkntext.split(' ')
  113. var link = splitted[0]
  114. var text = ""
  115. if (splitted.length > 1) { text = splitted.slice(1).join() }
  116. else { text = link }
  117. this.out += `<a href="${link}">${text}</a>`
  118. }
  119.  
  120. compile() {
  121. var escaped = false
  122. var bold = false
  123. var italic = false
  124. var header = 0
  125. var ulist = false
  126. var olist = false
  127. var blockquote = false
  128.  
  129. while (this.current_char != null) {
  130. if (this.current_char == '\n' && this.peek() == '\n' && ulist) {
  131. this.advance(2)
  132. this.out += "</ul>"
  133. ulist = false
  134. continue
  135. }
  136. else if (this.current_char == '\n' && this.peek() == '\n' && olist) {
  137. this.advance(2)
  138. this.out += "</ol>"
  139. olist = false
  140. continue
  141. }
  142. else if (this.current_char == '\n' && (olist || ulist)) {
  143. this.advance()
  144. this.out += "</li>"
  145. continue
  146. }
  147. if (this.current_char == '\n' && header != 0) {
  148. this.out += `</h${header}>`
  149. header = 0
  150. this.advance()
  151. continue
  152. }
  153. if (this.current_char == '\n' && this.peek() == '\n' && blockquote) {
  154. this.out += "</blockquote>"
  155. blockquote = false
  156. this.advance(2)
  157. continue
  158. }
  159. if (this.current_char.isspace()) {
  160. this.skipspace()
  161. continue
  162. }
  163. if (!escaped) {
  164. if (this.current_char == '/' && this.peek() == '*') { this.skipcomment() }
  165. else if (this.current_char == '`' && this.peek() == '`' && this.peek(2) == '`') { this.blockcode() }
  166. else if (this.current_char == '`') { this.code() }
  167. else if (this.current_char == '\'' && this.peek() == '\'') {
  168. if (bold) { this.out += "</b>" }
  169. else { this.out += "<b>" }
  170. bold = !bold
  171. this.advance()
  172. }
  173. else if (this.current_char == '\'') {
  174. if (italic) { this.out += "</i>" }
  175. else { this.out += "<i>" }
  176. italic = !italic
  177. }
  178. else if (this.current_char == '\\') {
  179. escaped = true
  180. this.advance()
  181. continue
  182. }
  183. else if (this.peek(-1) == '\n' && this.current_char == '-' && this.peek(1) == '>' && this.peek(2) == ' ') {
  184. this.out += "<h1 dir=\"auto\">"
  185. header = 1
  186. this.advance(2)
  187. }
  188. else if (this.peek(-1) == '\n' && this.current_char == '-' && this.peek(1) == '-' && this.peek(2) == '>' && this.peek(3) == ' ') {
  189. this.out += "<h2 dir=\"auto\">"
  190. header = 2
  191. this.advance(3)
  192. }
  193. else if (this.peek(-1) == '\n' && this.current_char == '-' && this.peek(1) == '-' && this.peek(2) == '-' && this.peek(3) == '>' && this.peek(4) == ' ') {
  194. this.out += "<h3 dir=\"auto\">"
  195. header = 3
  196. this.advance(4)
  197. }
  198. else if (this.peek(-1) == '\n' && this.current_char == '-' && this.peek(1) == '-' && this.peek(2) == '-' && this.peek(3) == '-' && this.peek(4) == '>' && this.peek(5) == ' ') {
  199. this.out += "<h4 dir=\"auto\">"
  200. header = 4
  201. this.advance(5)
  202. }
  203. else if (this.peek(-1) == '\n' && this.current_char == '-' && this.peek(1) == '-' && this.peek(2) == '-' && this.peek(3) == '-' && this.peek(4) == '-' && this.peek(5) == '>' && this.peek(6) == ' ') {
  204. this.out += "<h5 dir=\"auto\">"
  205. header = 5
  206. this.advance(6)
  207. }
  208. else if (this.peek(-1) == '\n' && this.current_char == '-' && this.peek(1) == '-' && this.peek(2) == '-' && this.peek(3) == '-' && this.peek(4) == '-' && this.peek(5) == '-' && this.peek(6) == '>' && this.peek(7) == ' ') {
  209. this.out += "<h6 dir=\"auto\">"
  210. header = 6
  211. this.advance(6)
  212. }
  213. else if (this.peek(-1) == '\n' && this.current_char == '*') {
  214. if (!ulist) {
  215. if (this.out.slice(-4) == "<br>") { this.out = this.out.slice(0, -4) }
  216. this.out += "<ul>"
  217. ulist = true
  218. }
  219. this.out += "<li>"
  220. }
  221. else if (this.peek(-1) == '\n' && this.current_char == '^') {
  222. if (!olist) {
  223. if (this.out.slice(-4) == "<br>") { this.out = this.out.slice(0, -4) }
  224. this.out += "<ol>"
  225. olist = true
  226. }
  227. this.out += "<li>"
  228. }
  229. else if (this.peek(-1) == '\n' && this.current_char == '>' && this.peek() == ' ') {
  230. this.out += "<blockquote>"
  231. blockquote = true
  232. this.advance()
  233. }
  234. else if (this.current_char == '[') {
  235. this.link()
  236. }
  237. else {
  238. this.out += this.current_char
  239. }
  240. escaped = false
  241. }
  242. else {
  243. this.out += this.current_char
  244. escaped = false
  245. }
  246. this.advance()
  247. }
  248. if (ulist) { this.out += "</ul>" }
  249. if (olist) { this.out += "</ol>" }
  250.  
  251. return this.out
  252. }
  253. }
  254.  
  255. var pathh = window.location.pathname.split('/').filter(n => n)
  256.  
  257. if (pathh.length == 2) {
  258. var content = $("#readme.qm .Box-body div pre").text()
  259. if (content !== undefined) {
  260. const compiler = new QomarCompiler("\n" + content)
  261. $("#readme.qm .Box-body").html("<article class=\"markdown-body entry-content container-lg\" itemprop=\"text\">" + compiler.compile() + "</article>")
  262. }
  263. }