Personal blog written from scratch using Node.js, Bootstrap, and MySQL. https://jrtechs.net
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.

259 lines
9.0 KiB

  1. const pandoc = require('node-pandoc');
  2. const utils = require('../utils/utils.js');
  3. const sql = require('../utils/sql');
  4. const argsFull = '--from markdown-markdown_in_html_blocks+raw_html -S --base-header-level=1 --toc --toc-depth=3 -N --normalize -s --mathjax -t html5';
  5. const argsPreview = '-S --normalize -s --mathjax -t html5';
  6. module.exports=
  7. {
  8. /**
  9. * Renders the entire blog post based on the sql data pulled
  10. * from the database.
  11. *
  12. * @param post sql data which has title, date, and header img location
  13. * @param blocks number of blocks to display for a preview or -1 for
  14. * all the blocks
  15. * @returns {Promise} async call which renders the entire blog post.
  16. */
  17. generateBlogPost: function(post, blocks)
  18. {
  19. return new Promise(function(resolve, reject)
  20. {
  21. Promise.all([module.exports.generateBlogPostHeader(post),
  22. module.exports.generateBlogPostBody(post, blocks)])
  23. .then(function()
  24. {
  25. resolve(post);
  26. }).catch(function(error)
  27. {
  28. reject(error);
  29. })
  30. });
  31. },
  32. /**
  33. * Renders the header of the blog post which contains the header image, and date
  34. * published.
  35. *
  36. * @param post sql data
  37. * @returns {string}
  38. */
  39. generateBlogPostHeader: function(post)
  40. {
  41. if(post.picture_url !== "n/a")
  42. post. hasPicture = true;
  43. post.published = post.published.toDateString();
  44. return;
  45. },
  46. /**
  47. * Method which renders the body of the blog post. This is responsible for getting
  48. * the contents of the markdown/latex file and rendering it into beautiful html.
  49. *
  50. * @param post stuff from the SQL table
  51. * @param blocks
  52. * @returns {Promise}
  53. */
  54. generateBlogPostBody: function(post, blocks)
  55. {
  56. return new Promise(function(resolve, reject)
  57. {
  58. sql.getCategory(post.category_id).then(function(category)
  59. {
  60. module.exports.generateBlogPostComponent(category[0].url, post.url, blocks).then(function(html)
  61. {
  62. post.blogBody = html;
  63. resolve();
  64. });
  65. });
  66. })
  67. },
  68. /**
  69. * Decomposition from Generate Blog Post used for the
  70. * blog previewer.
  71. *
  72. * @param categoryURL
  73. * @param postURL
  74. * @param blocks
  75. * @returns {Promise}
  76. */
  77. generateBlogPostComponent: function(categoryURL, postURL, blocks)
  78. {
  79. return new Promise(function(resolve, reject)
  80. {
  81. const pathName = "blogContent/posts/" + categoryURL + "/"
  82. + postURL + ".md";
  83. var markDown = utils.getFileContents(pathName).toString();
  84. markDown = markDown.split("(media/").join("(" + "../blogContent/posts/"
  85. + categoryURL + "/media/");
  86. module.exports.convertToHTML(markDown, blocks).then(function(result)
  87. {
  88. result = result.split("<figcaption>").join("<figcaption style=\"visibility: hidden;\">");
  89. //this line prevents older versions of pandoc from including invalid cdm scripts
  90. result = result.split("<script src=\"https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS_CHTML-full\" type=\"text/javascript\"></script>").join("");
  91. result = result.split("<script src=\"https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML\" type=\"text/javascript\"></script>").join("");
  92. //stuff for youtube videos
  93. var re = /\<youtube .*?>/;
  94. //<youtube src="" />
  95. while (result.search(re) != -1)
  96. {
  97. var ytid = result.substring(result.search(re) + 14, result.search(re)+ 11 + 14);
  98. var youtubeHTML = "<div class=\"wrapper\">\n" +
  99. "\t<div class=\"youtube\" data-embed=\"" +
  100. ytid +
  101. "\" />\n" +
  102. "\t\t<div class=\"play-button\"></div>\n" +
  103. "\t</div>\n" +
  104. "</div>\n";
  105. var original = "<youtube src=\"" + ytid + "\" />";
  106. result = result.split(original).join(youtubeHTML);
  107. }
  108. var regExp = /\<customHTML .*?>/;
  109. while (result.search(regExp) != -1)
  110. {
  111. const pathName = "blogContent/posts/" + categoryURL + "/html/"
  112. + postURL + ".html";
  113. var htmlContent = utils.getFileContents(pathName).toString();
  114. console.log(htmlContent);
  115. result = result.split("<customHTML />").join(htmlContent);
  116. }
  117. if(blocks == -1)
  118. resolve(result);
  119. const htmlBlocks = result.split("<p>");
  120. var html = "";
  121. for(var i = 0; i < blocks; i++)
  122. {
  123. html += "<p>" + htmlBlocks[i];
  124. }
  125. html += " <div style=\"\">\n" +
  126. " <p class='text-center'><button class=\"btn btn-secondary btn-lg " +
  127. "w3-padding-large w3-white w3-border\" onclick=\"location.href='" +
  128. "http://jrtechs.net/" + categoryURL + "/" + postURL +
  129. "'\"><b>READ MORE &raquo;</b></button></p>\n" +
  130. " </div>\n";
  131. resolve(html);
  132. }).catch(function(error)
  133. {
  134. reject(error);
  135. })
  136. })
  137. },
  138. /**
  139. * Converts markdown into html.
  140. *
  141. * @param markdownContents
  142. * @param type
  143. * @returns {Promise}
  144. */
  145. convertToHTML: function(markdownContents, type)
  146. {
  147. return new Promise(function(resolve, reject)
  148. {
  149. // Set your callback function
  150. callback = function (err, html)
  151. {
  152. if (err)
  153. {
  154. reject(err);
  155. }
  156. html = html.split("<img").join("<img style=\"max-width: 100%;\" ");
  157. html = html.split("<code>").join("<code class='hljs cpp'>");
  158. resolve(html);
  159. };
  160. if(type == -1)
  161. {
  162. pandoc(markdownContents, argsFull, callback);
  163. }
  164. else
  165. {
  166. pandoc(markdownContents, argsPreview, callback);
  167. }
  168. });
  169. },
  170. /**
  171. * Renders a bunch of blog post previews to the user
  172. *
  173. * @param baseURL-- url of the page
  174. * @param posts -- sql data about the blog to render
  175. * @param currentPage -- the current page to render
  176. * @param numOfPosts -- number of blog to render
  177. * @returns {Promise} renders the html of the blog
  178. */
  179. renderBatchOfPosts: function(baseURL, posts, currentPage, numOfPosts, templateContext)
  180. {
  181. if(typeof currentPage == "undefined")
  182. {
  183. currentPage = 1;
  184. }
  185. else
  186. {
  187. currentPage = Number(currentPage);
  188. }
  189. return new Promise(function(resolve, reject)
  190. {
  191. const promises = [];
  192. for(var i = (currentPage-1) * numOfPosts; i < (currentPage-1) * numOfPosts + numOfPosts; i++)
  193. {
  194. if(i < posts.length)
  195. {
  196. promises.push(new Promise(function(res, rej)
  197. {
  198. module.exports.generateBlogPost(posts[i], posts.length === 1 ? -1: 3).then(function(tempContext)
  199. {
  200. res(tempContext);
  201. }).catch(function(error)
  202. {
  203. rej();
  204. })
  205. }));
  206. }
  207. }
  208. //promises.push(require('../blog/renderNextBar').main(baseURL, currentPage, numOfPosts, blog.length));
  209. Promise.all(promises).then(function(posts)
  210. {
  211. templateContext.posts = posts;
  212. resolve();
  213. }).catch(function(error)
  214. {
  215. reject(error);
  216. });
  217. });
  218. }
  219. }