Pandoc As A Service
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

script.js 3.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. var pdfjsLib = window['pdfjs-dist/build/pdf'];
  2. pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdn.jsdelivr.net/npm/pdfjs-dist@2.1.266/build/pdf.worker.min.js';
  3. class PdfRenderer {
  4. constructor(canvas) {
  5. this.numPages = 1;
  6. this.currentPage = 0;
  7. this.scale = 2;
  8. this.canvas = canvas;
  9. this.ctx = this.canvas.getContext("2d");
  10. this.offScreenCanvas = document.createElement("canvas");
  11. this.offScreenCtx = this.offScreenCanvas.getContext("2d");
  12. this.dpr = window.devicePixelRatio || 1;
  13. this.pdf = null;
  14. this.rendering = false;
  15. this.renderPending = false;
  16. }
  17. update(b64, cb) {
  18. if (b64 == "") {
  19. this.pdf = null;
  20. this.numPages = 1;
  21. this.currentPage = 0;
  22. this.render();
  23. cb();
  24. return;
  25. } else if (b64 == null) {
  26. cb();
  27. return;
  28. }
  29. let data = atob(b64);
  30. let loadingTask = pdfjsLib.getDocument({ data });
  31. loadingTask.promise.then(pdf => {
  32. this.pdf = pdf;
  33. this.numPages = this.pdf.numPages;
  34. if (this.currentPage >= this.numPages)
  35. this.currentPage = this.numPages - 1;
  36. this.render();
  37. cb();
  38. });
  39. }
  40. render() {
  41. if (this.rendering) {
  42. this.renderPending = true;
  43. return;
  44. }
  45. if (this.pdf == null) {
  46. this.canvas.width = this.canvas.width;
  47. return;
  48. }
  49. this.rendering = true;
  50. this.pdf.getPage(this.currentPage + 1).then(page => {
  51. let viewport = page.getViewport({ scale: this.scale });
  52. this.offScreenCanvas.height = viewport.height * this.dpr;
  53. this.offScreenCanvas.width = viewport.width * this.dpr;
  54. this.offScreenCanvas.style.width = viewport.width + "px";
  55. this.offScreenCanvas.style.height = viewport.height + "px";
  56. this.offScreenCtx.scale(this.dpr, this.dpr);
  57. let renderTask = page.render({ canvasContext: this.offScreenCtx, viewport });
  58. renderTask.promise.then(() => {
  59. this.canvas.width = this.offScreenCanvas.width;
  60. this.canvas.height = this.offScreenCanvas.height;
  61. this.canvas.style.width = this.offScreenCanvas.style.width;
  62. this.canvas.style.height = this.offScreenCanvas.style.height;
  63. this.ctx.drawImage(this.offScreenCanvas, 0, 0);
  64. this.rendering = false;
  65. if (this.renderPending) {
  66. this.renderPending = false;
  67. this.render();
  68. }
  69. });
  70. });
  71. }
  72. }
  73. let renderer = new PdfRenderer(document.getElementById("preview"));
  74. let previewState = document.getElementById("preview-state");
  75. let renderOutput = document.getElementById("pandoc-output");
  76. function updatePreviewState() {
  77. previewState.innerText = (renderer.currentPage + 1) + "/" + renderer.numPages;
  78. }
  79. let markdownReqNum = 0;
  80. let markdownReqLast = 0;
  81. function renderMarkdown(canvas, text) {
  82. let num = markdownReqNum++;
  83. fetch("/pandoc", {
  84. method: "POST",
  85. body: editor.value(),
  86. }).then(res => res.json()).then(obj => {
  87. if (num < markdownReqLast)
  88. return;
  89. markdownReqLast = num;
  90. renderOutput.innerText = obj.output;
  91. renderer.update(obj.pdf, () => {
  92. updatePreviewState();
  93. });
  94. });
  95. }
  96. function nextPage() {
  97. if (renderer.currentPage < renderer.numPages - 1) {
  98. renderer.currentPage += 1;
  99. renderer.render();
  100. updatePreviewState();
  101. }
  102. }
  103. function prevPage() {
  104. if (renderer.currentPage > 0) {
  105. renderer.currentPage -= 1;
  106. renderer.render();
  107. updatePreviewState();
  108. }
  109. }
  110. function debounce(ms, cb) {
  111. let timeout = null;
  112. return function() {
  113. if (timeout != null) {
  114. clearTimeout(timeout);
  115. timout = null;
  116. }
  117. timeout = setTimeout(cb, ms);
  118. }
  119. }
  120. let editor = new SimpleMDE({
  121. element: document.getElementById("editor"),
  122. hideIcons: [ "side-by-side", "preview" ],
  123. spellChecker: false,
  124. autosave: true,
  125. });
  126. let onChange = debounce(100, () => renderMarkdown(preview, editor.value()));
  127. editor.codemirror.on("change", onChange);
  128. renderMarkdown(preview, editor.value());
  129. document.getElementById("preview-prev-btn").addEventListener("click", prevPage);
  130. document.getElementById("preview-next-btn").addEventListener("click", nextPage);