diff options
| -rw-r--r-- | .editorconfig | 4 | ||||
| -rw-r--r-- | site.tmpl | 136 | ||||
| -rw-r--r-- | src/comments.html | 100 | ||||
| -rw-r--r-- | src/static/comments.js | 76 |
4 files changed, 212 insertions, 104 deletions
diff --git a/.editorconfig b/.editorconfig index 1923d41..eebf0a8 100644 --- a/.editorconfig +++ b/.editorconfig | |||
| @@ -6,3 +6,7 @@ indent_size = 2 | |||
| 6 | charset = utf-8 | 6 | charset = utf-8 |
| 7 | trim_trailing_whitespace = true | 7 | trim_trailing_whitespace = true |
| 8 | insert_final_newline = true | 8 | insert_final_newline = true |
| 9 | |||
| 10 | [Makefile] | ||
| 11 | indent_style = tab | ||
| 12 | indent_size = 4 | ||
| @@ -1,6 +1,7 @@ | |||
| 1 | {{ define "header" }} | 1 | {{ define "header" }} |
| 2 | <!doctype html> | 2 | <!doctype html> |
| 3 | <html lang="en"> | 3 | <html lang="en"> |
| 4 | |||
| 4 | <head> | 5 | <head> |
| 5 | <meta charset="utf-8"> | 6 | <meta charset="utf-8"> |
| 6 | <meta name="viewport" content="width=device-width, initial-scale=1.0"> | 7 | <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| @@ -25,74 +26,75 @@ | |||
| 25 | <meta name="twitter:card" content="summary"> | 26 | <meta name="twitter:card" content="summary"> |
| 26 | <meta name="twitter:site" content="{{ html .Site.Other.Twitter_handle }}"> | 27 | <meta name="twitter:site" content="{{ html .Site.Other.Twitter_handle }}"> |
| 27 | </head> | 28 | </head> |
| 28 | <body> | ||
| 29 | <main> | ||
| 30 | {{ end }} | ||
| 31 | 29 | ||
| 32 | {{ define "footer" }} | 30 | <body> |
| 33 | </main> | 31 | <main> |
| 34 | {{ template "ga" . }} | 32 | {{ end }} |
| 33 | {{ define "footer" }} | ||
| 34 | </main> | ||
| 35 | {{ template "ga" . }} | ||
| 35 | </body> | 36 | </body> |
| 36 | </html> | 37 | |
| 38 | </html> | ||
| 37 | {{ end }} | 39 | {{ end }} |
| 38 | 40 | ||
| 39 | {{ define "navigation" }} | 41 | {{ define "navigation" }} |
| 40 | <nav> | 42 | <nav> |
| 41 | <a href="/">{{ html .Site.Other.Author }}</a> | 43 | <a href="/">{{ html .Site.Other.Author }}</a> |
| 42 | <a href="/curriculum-vitae.html">CV</a> | 44 | <a href="/curriculum-vitae.html">CV</a> |
| 43 | <a href="https://twitter.com/mitjafelicijan" target="_blank" rel="noopener nofollow">Twitter</a> | 45 | <a href="https://twitter.com/mitjafelicijan" target="_blank" rel="noopener nofollow">Twitter</a> |
| 44 | <a href="https://github.com/mitjafelicijan" target="_blank" rel="noopener nofollow">Github</a> | 46 | <a href="https://github.com/mitjafelicijan" target="_blank" rel="noopener nofollow">Github</a> |
| 45 | </nav> | 47 | </nav> |
| 46 | {{ end }} | 48 | {{ end }} |
| 47 | 49 | ||
| 48 | {{ define "author" }} | 50 | {{ define "author" }} |
| 49 | <span>by {{ html .Site.Other.Author }}</span> | 51 | <span>by {{ html .Site.Other.Author }}</span> |
| 50 | {{ end }} | 52 | {{ end }} |
| 51 | 53 | ||
| 52 | {{define "date"}} | 54 | {{define "date"}} |
| 53 | <time datetime="{{ .Format "2006-01-02T15:04:05Z07:00" }}"> | 55 | <time datetime="{{ .Format "2006-01-02T15:04:05Z07:00" }}"> |
| 54 | {{ .Format "2006, January 02" }} | 56 | {{ .Format "2006, January 02" }} |
| 55 | </time> | 57 | </time> |
| 56 | {{end}} | 58 | {{end}} |
| 57 | 59 | ||
| 58 | {{define "modified"}} | 60 | {{define "modified"}} |
| 59 | <p class="modified">Modified on {{ .ModTime.Format "2006-01-02T15:04:05" }}</p> | 61 | <p class="modified">Modified on {{ .ModTime.Format "2006-01-02T15:04:05" }}</p> |
| 60 | {{end}} | 62 | {{end}} |
| 61 | 63 | ||
| 62 | {{ define "page" }} | 64 | {{ define "page" }} |
| 63 | {{ template "header" . }} | 65 | {{ template "header" . }} |
| 64 | {{ template "navigation" . }} | 66 | {{ template "navigation" . }} |
| 65 | {{ .Content }} | 67 | {{ .Content }} |
| 66 | {{ template "prism" . }} | 68 | {{ template "prism" . }} |
| 67 | {{ template "mathjax" . }} | 69 | {{ template "mathjax" . }} |
| 68 | {{ template "footer" . }} | 70 | {{ template "footer" . }} |
| 69 | {{ end }} | 71 | {{ end }} |
| 70 | 72 | ||
| 71 | {{ define "post" }} | 73 | {{ define "post" }} |
| 72 | <article> | 74 | <article> |
| 73 | <header> | 75 | <header> |
| 74 | <h1>{{ .Title }}</h1> | 76 | <h1>{{ .Title }}</h1> |
| 75 | {{ if eq .Other.Type "post" }} | 77 | {{ if eq .Other.Type "post" }} |
| 76 | <div class="info"> | 78 | <div class="info"> |
| 77 | {{ template "date" .Date }} | 79 | {{ template "date" .Date }} |
| 78 | {{ template "author" . }} | 80 | {{ template "author" . }} |
| 79 | </div> | 81 | </div> |
| 80 | {{ end }} | 82 | {{ end }} |
| 81 | </header> | 83 | </header> |
| 82 | <section> | 84 | <section> |
| 83 | {{ .Content }} | 85 | {{ .Content }} |
| 84 | </section> | 86 | </section> |
| 85 | </article> | 87 | </article> |
| 86 | 88 | ||
| 87 | {{ if eq .Other.Type "post" }} | 89 | {{ if eq .Other.Type "post" }} |
| 88 | {{ template "comments" . }} | 90 | {{ template "comments" . }} |
| 89 | {{ end }} | 91 | {{ end }} |
| 90 | 92 | ||
| 91 | {{ template "modified" . }} | 93 | {{ template "modified" . }} |
| 92 | {{ end }} | 94 | {{ end }} |
| 93 | 95 | ||
| 94 | {{ define "mathjax" }} | 96 | {{ define "mathjax" }} |
| 95 | <script type="text/x-mathjax-config"> | 97 | <script type="text/x-mathjax-config"> |
| 96 | MathJax.Hub.Config({ | 98 | MathJax.Hub.Config({ |
| 97 | TeX: { equationNumbers: { autoNumber: "AMS" } }, | 99 | TeX: { equationNumbers: { autoNumber: "AMS" } }, |
| 98 | tex2jax: { | 100 | tex2jax: { |
| @@ -101,38 +103,38 @@ | |||
| 101 | processEscapes: true | 103 | processEscapes: true |
| 102 | }}); | 104 | }}); |
| 103 | </script> | 105 | </script> |
| 104 | <script src="//cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/MathJax.js?config=TeX-AMS-MML_HTMLorMML" async="async"></script> | 106 | <script src="//cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/MathJax.js?config=TeX-AMS-MML_HTMLorMML" async="async"></script> |
| 105 | {{ end }} | 107 | {{ end }} |
| 106 | 108 | ||
| 107 | {{ define "ga" }} | 109 | {{ define "ga" }} |
| 108 | <script async="async" src="https://www.googletagmanager.com/gtag/js?id=UA-12769079-10"></script> | 110 | <script async="async" src="https://www.googletagmanager.com/gtag/js?id=UA-12769079-10"></script> |
| 109 | <script> | 111 | <script> |
| 110 | window.dataLayer = window.dataLayer || []; | 112 | window.dataLayer = window.dataLayer || []; |
| 111 | function gtag() { | 113 | function gtag() { |
| 112 | dataLayer.push(arguments); | 114 | dataLayer.push(arguments); |
| 113 | } | 115 | } |
| 114 | gtag('js', new Date()); | 116 | gtag('js', new Date()); |
| 115 | gtag('config', 'UA-12769079-10'); | 117 | gtag('config', 'UA-12769079-10'); |
| 116 | </script> | 118 | </script> |
| 117 | {{ end }} | 119 | {{ end }} |
| 118 | 120 | ||
| 119 | {{ define "prism" }} | 121 | {{ define "prism" }} |
| 120 | <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.17.1/components/prism-core.min.js"></script> | 122 | <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.17.1/components/prism-core.min.js"></script> |
| 121 | <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.17.1/plugins/autoloader/prism-autoloader.min.js"></script> | 123 | <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.17.1/plugins/autoloader/prism-autoloader.min.js"></script> |
| 122 | {{ end }} | 124 | {{ end }} |
| 123 | 125 | ||
| 124 | {{ define "comments" }} | 126 | {{ define "comments" }} |
| 125 | <div class="comments"> | 127 | <div class="comments"> |
| 126 | <h3>Comments</h3> | 128 | <h3>Comments</h3> |
| 127 | <div class="form"> | 129 | <div class="form"> |
| 128 | <div><input id="name" placeholder="Your name" maxlength="20"></div> | 130 | <div><input id="name" placeholder="Your name" maxlength="20"></div> |
| 129 | <div><textarea id="comment" placeholder="Your comment" maxlength="500"></textarea></div> | 131 | <div><textarea id="comment" placeholder="Your comment" maxlength="500"></textarea></div> |
| 130 | <div><button id="submit">Post a comment</button></div> | 132 | <div><button id="submit">Post a comment</button></div> |
| 131 | </div> | ||
| 132 | <ul></ul> | ||
| 133 | </div> | 133 | </div> |
| 134 | <ul></ul> | ||
| 135 | </div> | ||
| 134 | 136 | ||
| 135 | <script src="https://www.gstatic.com/firebasejs/7.2.1/firebase-app.js"></script> | 137 | <script src="https://www.gstatic.com/firebasejs/7.2.1/firebase-app.js"></script> |
| 136 | <script src="https://www.gstatic.com/firebasejs/7.2.1/firebase-database.js"></script> | 138 | <script src="https://www.gstatic.com/firebasejs/7.2.1/firebase-database.js"></script> |
| 137 | <script src="{{ .Rel "static/comments.js" }}?v={{ .ModTime.Format "20060102150405" }}" async></script> | 139 | <script src="{{ .Rel "static/comments.js" }}?v={{ .ModTime.Format "20060102150405" }}" async></script> |
| 138 | {{ end }} | 140 | {{ end }} |
diff --git a/src/comments.html b/src/comments.html new file mode 100644 index 0000000..3d990da --- /dev/null +++ b/src/comments.html | |||
| @@ -0,0 +1,100 @@ | |||
| 1 | <!DOCTYPE html> | ||
| 2 | <html lang="en"> | ||
| 3 | |||
| 4 | <head> | ||
| 5 | <meta charset="utf-8"> | ||
| 6 | <meta name="theme-color" content="#ffffff"> | ||
| 7 | <meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||
| 8 | <meta http-equiv="X-UA-Compatible" content="ie=edge"> | ||
| 9 | <title>Commenta dashboard</title> | ||
| 10 | |||
| 11 | <style> | ||
| 12 | th { | ||
| 13 | text-align: left; | ||
| 14 | } | ||
| 15 | |||
| 16 | .comment-item { | ||
| 17 | padding: 10px 0 30px 0; | ||
| 18 | border-bottom: 2px inset gray; | ||
| 19 | } | ||
| 20 | </style> | ||
| 21 | |||
| 22 | </head> | ||
| 23 | |||
| 24 | <body> | ||
| 25 | |||
| 26 | <h1>Comments</h1> | ||
| 27 | <div id="results"></div> | ||
| 28 | |||
| 29 | <script src="https://www.gstatic.com/firebasejs/7.2.1/firebase-app.js"></script> | ||
| 30 | <script src="https://www.gstatic.com/firebasejs/7.2.1/firebase-database.js"></script> | ||
| 31 | <script src="static/comments.js"></script> | ||
| 32 | |||
| 33 | <script> | ||
| 34 | |||
| 35 | // var tableResults = document.querySelector('#results tbody'); | ||
| 36 | var resultsPlaceholder = document.querySelector('#results'); | ||
| 37 | |||
| 38 | function snapshotToArray(snapshot) { | ||
| 39 | var returnArr = []; | ||
| 40 | snapshot.forEach(function (childSnapshot) { | ||
| 41 | var arrItem = childSnapshot.val(); | ||
| 42 | arrItem.key = childSnapshot.key; | ||
| 43 | returnArr.push(arrItem); | ||
| 44 | }); | ||
| 45 | |||
| 46 | var comments = []; | ||
| 47 | returnArr.forEach(function (item) { | ||
| 48 | var keys = Object.keys(item); | ||
| 49 | for (var key of keys) { | ||
| 50 | let group = item.key; | ||
| 51 | if (key !== 'key') { | ||
| 52 | for (var itemKey of Object.keys(item[key])) { | ||
| 53 | item[key][itemKey].slug = key; | ||
| 54 | item[key][itemKey].group = group; | ||
| 55 | item[key][itemKey].itemKey = itemKey | ||
| 56 | comments.push(item[key][itemKey]); | ||
| 57 | } | ||
| 58 | } | ||
| 59 | } | ||
| 60 | }); | ||
| 61 | |||
| 62 | return comments; | ||
| 63 | }; | ||
| 64 | |||
| 65 | var path = window.location.hostname.replace('.', '-') + '/comments'; | ||
| 66 | var ref = firebase.database().ref(path); | ||
| 67 | ref.on("value", function (snap) { | ||
| 68 | resultsPlaceholder.innerHTML = ''; | ||
| 69 | var comments = snapshotToArray(snap); | ||
| 70 | comments.forEach(function (item) { | ||
| 71 | var commentContent = document.createElement('div'); | ||
| 72 | |||
| 73 | commentContent.classList.add('comment-item'); | ||
| 74 | commentContent.innerHTML = ` | ||
| 75 | <p><b>${item.name} (${item.published})</b><br> | ||
| 76 | <i>${item.group}/${item.slug}</i></p> | ||
| 77 | <p>${item.comment}</p> | ||
| 78 | `; | ||
| 79 | |||
| 80 | var buttonDelete = document.createElement('button'); | ||
| 81 | buttonDelete.innerText = 'Delete comment'; | ||
| 82 | buttonDelete.dataset.id = `${window.location.hostname.replace('.', '-')}/comments/${item.group}/${item.slug}/${item.itemKey}`; | ||
| 83 | buttonDelete.addEventListener('click', function (evt) { | ||
| 84 | if (confirm('Are you sure you want to delete this comment?')) { | ||
| 85 | firebase.database().ref(evt.target.dataset.id).remove(); | ||
| 86 | } | ||
| 87 | }); | ||
| 88 | |||
| 89 | commentContent.appendChild(buttonDelete); | ||
| 90 | resultsPlaceholder.appendChild(commentContent); | ||
| 91 | }); | ||
| 92 | }, function (errorObject) { | ||
| 93 | console.log(`The read failed: ${errorObject.code}`); | ||
| 94 | }); | ||
| 95 | |||
| 96 | </script> | ||
| 97 | |||
| 98 | </body> | ||
| 99 | |||
| 100 | </html> | ||
diff --git a/src/static/comments.js b/src/static/comments.js index c8234d2..3f373e5 100644 --- a/src/static/comments.js +++ b/src/static/comments.js | |||
| @@ -10,7 +10,7 @@ var firebaseConfig = { | |||
| 10 | firebase.initializeApp(firebaseConfig); | 10 | firebase.initializeApp(firebaseConfig); |
| 11 | 11 | ||
| 12 | var database = firebase.database(); | 12 | var database = firebase.database(); |
| 13 | var docPath = 'comments' + window.location.pathname.replace('.html', ''); | 13 | var docPath = window.location.hostname.replace('.', '-') + '/comments' + window.location.pathname.replace('.html', ''); |
| 14 | var submit = document.querySelector('#submit'); | 14 | var submit = document.querySelector('#submit'); |
| 15 | var comments = document.querySelector('.comments ul'); | 15 | var comments = document.querySelector('.comments ul'); |
| 16 | var textName = document.querySelector('#name'); | 16 | var textName = document.querySelector('#name'); |
| @@ -21,43 +21,45 @@ function encodeHTML(s) { | |||
| 21 | return s.replace(/&/g, '&').replace(/</g, '<').replace(/"/g, '"'); | 21 | return s.replace(/&/g, '&').replace(/</g, '<').replace(/"/g, '"'); |
| 22 | } | 22 | } |
| 23 | 23 | ||
| 24 | ref.on("value", function (snapshot) { | 24 | if (submit) { |
| 25 | comments.innerHTML = ''; | 25 | ref.on("value", function (snapshot) { |
| 26 | var commentList = Array(); | 26 | comments.innerHTML = ''; |
| 27 | var commentList = Array(); | ||
| 27 | 28 | ||
| 28 | // generating normal array | 29 | // generating normal array |
| 29 | snapshot.forEach(function (item) { | 30 | snapshot.forEach(function (item) { |
| 30 | commentList.push(item.val()) | 31 | commentList.push(item.val()) |
| 31 | }); | 32 | }); |
| 32 | 33 | ||
| 33 | // rendering html | 34 | // rendering html |
| 34 | commentList.reverse().forEach(function (item) { | 35 | commentList.reverse().forEach(function (item) { |
| 35 | var liItem = `<li> | 36 | var liItem = `<li> |
| 36 | <div><b>${encodeHTML(item.name)}</b> - ${encodeHTML(item.published)}</div> | 37 | <div><b>${encodeHTML(item.name)}</b> - ${encodeHTML(item.published)}</div> |
| 37 | <div>${encodeHTML(item.comment)}</div> | 38 | <div>${encodeHTML(item.comment)}</div> |
| 38 | </li>`; | 39 | </li>`; |
| 39 | comments.innerHTML += liItem; | 40 | comments.innerHTML += liItem; |
| 40 | }); | 41 | }); |
| 41 | 42 | ||
| 42 | }, function (errorObject) { | 43 | }, function (errorObject) { |
| 43 | console.log("The read failed: " + errorObject.code); | 44 | console.log("The read failed: " + errorObject.code); |
| 44 | }); | 45 | }); |
| 45 | 46 | ||
| 46 | submit.addEventListener('click', function (evt) { | 47 | submit.addEventListener('click', function (evt) { |
| 47 | if (textName.value && textComment.value) { | 48 | if (textName.value && textComment.value) { |
| 48 | submit.disabled = true; | 49 | submit.disabled = true; |
| 49 | firebase.database().ref(docPath + '/' + Date.now()).set({ | 50 | firebase.database().ref(docPath + '/' + Date.now()).set({ |
| 50 | name: textName.value, | 51 | name: textName.value, |
| 51 | comment: textComment.value, | 52 | comment: textComment.value, |
| 52 | published: new Date().toISOString().slice(0, 16).replace('T', ' '), | 53 | published: new Date().toISOString().slice(0, 16).replace('T', ' '), |
| 53 | }, function (error) { | 54 | }, function (error) { |
| 54 | if (error) { | 55 | if (error) { |
| 55 | alert('Data could not be saved.' + error); | 56 | alert('Data could not be saved.' + error); |
| 56 | } else { | 57 | } else { |
| 57 | textName.value = ''; | 58 | textName.value = ''; |
| 58 | textComment.value = ''; | 59 | textComment.value = ''; |
| 59 | submit.disabled = false; | 60 | submit.disabled = false; |
| 60 | } | 61 | } |
| 61 | }); | 62 | }); |
| 62 | } | 63 | } |
| 63 | }); | 64 | }); |
| 65 | } | ||
