diff --git a/README.md b/README.md index 3f41137..43adc9a 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,8 @@ -# GitBlog.md - - [![Build Status](https://img.shields.io/travis/Klemek/GitBlog.md.svg?branch=master)](https://travis-ci.org/Klemek/GitBlog.md) [![Coverage Status](https://img.shields.io/coveralls/github/Klemek/GitBlog.md.svg?branch=master)](https://coveralls.io/github/Klemek/GitBlog.md?branch=master) +# GitBlog.md + A static blog using Markdown pulled from your git repository. > Step 1 : ```$ vi 2019/06/21/index.md``` @@ -245,6 +244,9 @@ Any URL like `/year/month/day/anything/` will redirect to this article (and link It allows you to add math equations to your articles by simply writing LaTeX between `$$` for full size (and between $ for inline) (more info [here](https://www.mathjax.org/)) * **PlantUML** It allows you to add UML diagrams with PlantUML Syntax between `@startuml` and `@enduml` (more info [here](http://www.plantuml.com)) +* **fa-diagrams** + It allows you to define SVG diagrams with Font-Awesome icons in YAML between `@startfad` and `@endfad` (more info [here](https://github.com/Klemek/fa-diagrams)) + ## Configuration [back to top](#gitblog-md) @@ -273,6 +275,8 @@ Any URL like `/year/month/day/anything/` will redirect to this article (and link activate MathJax equations formatting * `plantuml` (default: true) activate PlantUML diagram rendering + * `fa-diagrams` (default: true) + activate fa-diagrams rendering * `home` * `title` (default: GitBlog.md) the title of your blog, **strongly advised to be changed** diff --git a/package-lock.json b/package-lock.json index e7cf978..356f460 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "gitblog.md", - "version": "1.1.5", + "version": "1.2.5", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -1337,7 +1337,6 @@ "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, "requires": { "sprintf-js": "~1.0.2" } @@ -3190,6 +3189,14 @@ "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" }, + "fa-diagrams": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/fa-diagrams/-/fa-diagrams-1.0.3.tgz", + "integrity": "sha512-z9pFYYnc6s1sUs4lq+DY2m5ddLCKN3jdo5O6hv450WwjiQ7otk1RoGQsvpMcwSU1kMEzPJUI6AeTVvzyIElhwQ==", + "requires": { + "xml-js": "^1.6.11" + } + }, "fast-deep-equal": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", @@ -6617,7 +6624,6 @@ "version": "3.13.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", - "dev": true, "requires": { "argparse": "^1.0.7", "esprima": "^4.0.0" @@ -6626,8 +6632,7 @@ "esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" } } }, @@ -9087,8 +9092,7 @@ "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" }, "sshpk": { "version": "1.16.1", @@ -9777,6 +9781,14 @@ "resolved": "https://registry.npmjs.org/xml/-/xml-1.0.1.tgz", "integrity": "sha1-eLpyAgApxbyHuKgaPPzXS0ovweU=" }, + "xml-js": { + "version": "1.6.11", + "resolved": "https://registry.npmjs.org/xml-js/-/xml-js-1.6.11.tgz", + "integrity": "sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g==", + "requires": { + "sax": "^1.2.4" + } + }, "xml-name-validator": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", diff --git a/package.json b/package.json index b05cb18..ebb236b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gitblog.md", - "version": "1.2.5", + "version": "1.2.6", "description": "A static blog using Markdown pulled from your git repository.", "main": "src/server.js", "dependencies": { @@ -8,6 +8,8 @@ "crypto": "^1.0.1", "ejs": "^2.6.2", "express": "^4.17.1", + "fa-diagrams": "^1.0.3", + "js-yaml": "^3.13.1", "mathjax-node": "^2.1.1", "ncp": "^2.0.0", "node-prismjs": "^0.1.2", diff --git a/sample_data/article/index.md b/sample_data/article/index.md index 2154e57..481db60 100644 --- a/sample_data/article/index.md +++ b/sample_data/article/index.md @@ -19,6 +19,7 @@ If you see this page, that means it's working * [Spoilers](#spoilers) * [Math Equations](#mathequations) * [UML](#uml) +* [Diagrams](#diagrams) * [Youtube Videos](#youtubevideos) ### Headers @@ -253,6 +254,30 @@ showdown -left-> express : 4. html express -up-> web : 5. html @enduml +### Diagrams +[Back to top](#top) + +You can use [fa-diagrams](https://github.com/Klemek/fa-diagrams) with `@startfad` and `@endfad` tags and using YAML inside + +@startuml +nodes: + - name: node1 + icon: laptop-code + color: '#4E342E' + bottom: my app + - name: node2 + icon: globe + color: '#455A64' + bottom: world +links: + - from: node1 + to: node2 + color: '#333333' + top: + icon: envelope + bottom: '"hello"' +@enduml + ### Youtube Videos [Back to top](#top) diff --git a/sample_data/home/index.ejs b/sample_data/home/index.ejs index 0517fdc..aeb4bc4 100644 --- a/sample_data/home/index.ejs +++ b/sample_data/home/index.ejs @@ -6,16 +6,18 @@
-

<%= info.title %>

+

<%= info.title %>

<%= info.description %>

Articles in this blog :

<% articles.forEach((article) => { %>
-

<%- `${article.title}` %>

- Published on <%= article.year + '-' + article.month + '-' + article.day %> + <%- `` %> +

<%- `${article.title}` %>

+ Published on  <%= article.year + '-' + ('0' + article.month).slice(-2) + '-' + ('0' + article.day).slice(-2) %> <% if(article.thumbnail){ %> <%- `thumbnail` %> <% } %> + <%- `
` %>
<% }); %> <%- include('footer'); %> diff --git a/sample_data/home/style.css b/sample_data/home/style.css index e0b0dcc..01bbc72 100644 --- a/sample_data/home/style.css +++ b/sample_data/home/style.css @@ -16,7 +16,7 @@ body { } main { - max-width: 42rem; + max-width: 45rem; padding: 2rem; margin: auto; background-color: #F0F0F0; @@ -54,6 +54,13 @@ pre { padding: 10px 16px; } +:not(pre) > code { + padding: 0.25em 0.5em; + border-radius: 0.25em; + background: #DDD; + font-size: 90%; +} + blockquote { border-left: 0.5em solid #ccc; padding-left: 1em; @@ -108,10 +115,11 @@ main.article div.header a.link-home { line-height: 2.4; } -main.article div.header h1, main.article div.header h2 { +main.article div.header h1, main.article div.header h2, .title { margin-top: 0.85em; margin-bottom: 0.25em; font-size: 2em; + font-weight: bold; } main.article div.header h1 a, main.article div.header h2 a, div.article h3 a { @@ -129,6 +137,12 @@ div.article { div.article h3 { font-size: 1.3em; margin:0; + color: #3C3CA1; +} + +div.article a { + text-decoration: none; + color: inherit; } div.article img{ @@ -138,6 +152,15 @@ div.article img{ margin-top:0.25em; } +div.article:hover { + opacity: 0.9; +} + +div.article:active { + opacity: 0.8; +} + + #text { text-align: justify; hyphens: auto; @@ -147,7 +170,7 @@ div.article img{ text-align: left; } -#text img { +#text img, #text svg { max-width: 100%; height: auto; } diff --git a/sample_data/home/template.ejs b/sample_data/home/template.ejs index e2590c1..f1f4c82 100644 --- a/sample_data/home/template.ejs +++ b/sample_data/home/template.ejs @@ -9,7 +9,7 @@

<%= article.title %>

- Published on <%= article.year + '-' + article.month + '-' + article.day %> + <%= article.draft ? 'Drafted on' : 'Published on' %>  <%= article.year + '-' + ('0' + article.month).slice(-2) + '-' + ('0' + article.day).slice(-2) %>
<%- article.content %>

diff --git a/src/config.default.json b/src/config.default.json index 63e3f92..79fe3f3 100644 --- a/src/config.default.json +++ b/src/config.default.json @@ -10,7 +10,8 @@ "webhook": true, "prism": true, "mathjax": true, - "plantuml": true + "plantuml": true, + "fa-diagrams": true }, "home": { "title": "GitBlog.md", diff --git a/src/renderer.js b/src/renderer.js index 6094f08..af8c3f5 100644 --- a/src/renderer.js +++ b/src/renderer.js @@ -5,6 +5,54 @@ const showdown = require('showdown'); module.exports = (config) => { const converter = new showdown.Converter(config['showdown']); + /** + * get parts outside of codes/scripts + * @param {string} data + * @returns {{index:number, end:number, text:string}[]} parts + */ + const getParts = (data) => { + let parts = []; + let match; + let i = 0; + while ((match = /```/m.exec(data.slice(i)))) { + parts.push({ + index: i, + text: data.slice(i, i + match.index), + }); + i += match.index + match[0].length; + } + if (i < data.length) + parts.push({ + index: i, + text: data.slice(i, data.length), + }); + + parts = parts.filter((p, i) => i % 2 === 0); //filter out code parts + + // detect scripts outside of code + parts.forEach((p, pi) => { + let i = 0; + const subParts = []; + while ((match = /(\n\n``````kenobi'; + const parts = renderer.getParts(data); + expect(parts).toEqual([ + { + index: 0, + end: 12, + text: 'Hello\nthere\n' + }, + { + index: 22, + end: 30, + text: '\ngeneral' + }, + { + index: 53, + end: 54, + text: '\n' + }, + { + index: 78, + end: 79, + text: '\n' + }, + { + index: 109, + end: 115, + text: 'kenobi' + }, + ]); + }); +}); + describe('Test Showdown', () => { test('normal', (done) => { renderer.renderShowDown('# Hello', (html) => { @@ -112,6 +155,13 @@ describe('Test PlantUML', () => { }); }); + test('plantuml ignored in code', (done) => { + renderer.renderPlantUML('code:\n```@startuml\nBob -> Alice : hello\n@enduml```\n ```@startuml``` @enduml', (data) => { + expect(data).toBe('code:\n```@startuml\nBob -> Alice : hello\n@enduml```\n ```@startuml``` @enduml'); + done(); + }); + }); + test('plantuml multiple uml', (done) => { renderer.renderPlantUML('@startuml\nBob -> Alice : hello\n@enduml\n@startuml\nBob -> Alice : hello\n@enduml', (data) => { expect(data).toBe('generated PlantUML diagram\ngenerated PlantUML diagram'); @@ -157,6 +207,12 @@ describe('Test MathJax', () => { done(); }); }); + test('no eq in code / script', (done) => { + renderer.renderMathJax('this code is ```start $a$ end $$hello$$``` beautiful \n```$no eq$```', (data) => { + expect(data).toBe('this code is ```start $a$ end $$hello$$``` beautiful \n```$no eq$```'); + done(); + }); + }); test('multiple eq', (done) => { renderer.renderMathJax('$$\n\nA\n\n$$\nstart $a$ end\n$$\n\nA\n\n$$', (data) => { expect(data).toBe('' + @@ -182,6 +238,34 @@ describe('Test MathJax', () => { }); }); +describe('Test fa-diagrams', () => { + test('no fa-diagrams', (done) => { + config['modules']['fa-diagrams'] = false; + renderer.renderFaDiagrams('@startfad\noptions:\n\trendering:\t\tcolor:red\n\n@endfad', (data) => { + expect(data).toBe('@startfad\noptions:\n\trendering:\t\tcolor:red\n\n@endfad'); + done(); + }); + }); + test('no fa-diagrams in code', (done) => { + renderer.renderFaDiagrams('code:\n```\n@startfad\noptions:\n\trendering:\t\tcolor:red\n\n@endfad\n```', (data) => { + expect(data).toBe('code:\n```\n@startfad\noptions:\n\trendering:\t\tcolor:red\n\n@endfad\n```'); + done(); + }); + }); + test('valid fa-diagrams', (done) => { + renderer.renderFaDiagrams('before\n@startfad\noptions:\n rendering:\n color: red\n@endfad\nafter', (data) => { + expect(data).toBe('before\n\nafter'); + done(); + }); + }); + test('invalid yaml', (done) => { + renderer.renderFaDiagrams('before\n@startfad\noptions:\n@endfad\nafter', (data) => { + expect(data).toBe('before\nTypeError: Cannot convert undefined or null to object\nafter'); + done(); + }); + }); +}); + describe('Test render', () => { test('invalid file', (done) => { renderer.render('invalid file', (err, html) => {