From 339e86edb5c8ad729cf0f4cfac3c3cde081d374e Mon Sep 17 00:00:00 2001 From: Bobby Date: Wed, 16 Mar 2022 21:49:49 -0400 Subject: Function to create posts --- public/views/admin.html | 5 +- public/views/createPost.html | 185 +++++ public/views/dashboard.html | 32 +- routes/admin.js | 4 + routes/posts.js | 17 + static/assets/css/custom.css | 1 + static/assets/js/marked.js | 1401 +++++++++++++++++++++++++++++++++++ static/assets/js/pages/admin.js | 10 - static/assets/js/pages/authCheck.js | 10 + static/assets/js/pages/config.js | 10 + 10 files changed, 1639 insertions(+), 36 deletions(-) create mode 100644 public/views/createPost.html create mode 100644 static/assets/js/marked.js create mode 100644 static/assets/js/pages/authCheck.js create mode 100644 static/assets/js/pages/config.js diff --git a/public/views/admin.html b/public/views/admin.html index 2e9e485..489a736 100644 --- a/public/views/admin.html +++ b/public/views/admin.html @@ -95,8 +95,9 @@ - - + + + diff --git a/public/views/createPost.html b/public/views/createPost.html new file mode 100644 index 0000000..e01ad55 --- /dev/null +++ b/public/views/createPost.html @@ -0,0 +1,185 @@ + + + + + + + + + + + + + + + + + Dashboard - Add New Post + + + + + +
+
+
+ +
+ +
+
+
+ + +
+
+ + +
+
+

Render + Preview

+
+
+
+ + +
+
+ + +
+ + +
+
+
+
+
+
+ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/public/views/dashboard.html b/public/views/dashboard.html index 58ac028..3791678 100644 --- a/public/views/dashboard.html +++ b/public/views/dashboard.html @@ -9,7 +9,7 @@ - Document + Dashboard - All Posts @@ -36,7 +36,7 @@
@@ -52,28 +53,11 @@ - + + + + + \ No newline at end of file diff --git a/routes/admin.js b/routes/admin.js index d7b79a5..b6afab7 100644 --- a/routes/admin.js +++ b/routes/admin.js @@ -5,6 +5,10 @@ router.get("/dashboard", function (req, res) { res.render("dashboard.html"); }); +router.get("/dashboard/new", function (req, res) { + res.render("createPost.html"); +}) + router.get("/", (req, res) => { // Send admin.html from public folder res.render("admin.html"); diff --git a/routes/posts.js b/routes/posts.js index e862680..d81327f 100644 --- a/routes/posts.js +++ b/routes/posts.js @@ -20,4 +20,21 @@ router.get("/posts", (req, res) => { }); }); +router.post("/new", (req, res) => { + const { title, content, tags, publishDate } = req.body; + const store = firebase.firestore(); + const post = { + title, + content, + tags: String(tags).split(",").length > 0 ? String(tags).split(",") : [], + publishDate, + }; + let query = store.collection("posts"); + query.add(post).then(() => { + res.json({ success: true }); + }).catch((err) => { + res.json({ success: false, err }); + }); +}); + module.exports = router; diff --git a/static/assets/css/custom.css b/static/assets/css/custom.css index 446d894..709d6b0 100644 --- a/static/assets/css/custom.css +++ b/static/assets/css/custom.css @@ -7,6 +7,7 @@ button { cursor: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAzElEQVRYR+2X0Q6AIAhF5f8/2jYXZkwEjNSVvVUjDpcrGgT7FUkI2D9xRfQETwNIiWO85wfINfQUEyxBG2ArsLwC0jioGt5zFcwF4OYDPi/mBYKm4t0U8ATgRm3ThFoAqkhNgWkA0jJLvaOVSs7j3qMnSgXWBMiWPXe94QqMBMBc1VZIvaTu5u5pQewq0EqNZvIEMCmxAawK0DNkay9QmfFNAJUXfgGgUkLaE7j/h8fnASkxHTz0DGIBMCnBeeM7AArpUd3mz2x3C7wADglA8BcWMZhZAAAAAElFTkSuQmCC) 14 0, pointer !important; + outline: none !important; } .cx { diff --git a/static/assets/js/marked.js b/static/assets/js/marked.js new file mode 100644 index 0000000..2985cd0 --- /dev/null +++ b/static/assets/js/marked.js @@ -0,0 +1,1401 @@ +/** + * marked - a markdown parser + * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed) + * https://github.com/chjj/marked + */ + +(function () { + /** + * Block-Level Grammar + */ + + var block = { + newline: /^\n+/, + code: /^\n( {4}[^\n]+\n*)+/, + fences: noop, + hr: /^( *[-*_]){3,} *(?:\n+|$)/, + heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/, + nptable: noop, + lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/, + blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/, + list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/, + html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/, + def: /^ *\[([^\]]+)\]: *]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/, + table: noop, + paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/, + text: /^[^\n]+/, + }; + + block.bullet = /(?:[*+-]|\d+\.)/; + block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/; + block.item = replace(block.item, "gm")(/bull/g, block.bullet)(); + + block.list = replace(block.list)(/bull/g, block.bullet)( + "hr", + "\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))" + )("def", "\\n+(?=" + block.def.source + ")")(); + + block.blockquote = replace(block.blockquote)("def", block.def)(); + + block._tag = + "(?!(?:" + + "a|em|strong|small|s|cite|q|dfn|abbr|data|time|code" + + "|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo" + + "|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b"; + + block.html = replace(block.html)("comment", //)( + "closed", + /<(tag)[\s\S]+?<\/\1>/ + )("closing", /])*?>/)(/tag/g, block._tag)(); + + block.paragraph = replace(block.paragraph)("hr", block.hr)( + "heading", + block.heading + )("lheading", block.lheading)("blockquote", block.blockquote)( + "tag", + "<" + block._tag + )("def", block.def)(); + + /** + * Normal Block Grammar + */ + + block.normal = merge({}, block); + + /** + * GFM Block Grammar + */ + + block.gfm = merge({}, block.normal, { + fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/, + paragraph: /^/, + heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/, + }); + + block.gfm.paragraph = replace(block.paragraph)( + "(?!", + "(?!" + + block.gfm.fences.source.replace("\\1", "\\2") + + "|" + + block.list.source.replace("\\1", "\\3") + + "|" + )(); + + /** + * GFM + Tables Block Grammar + */ + + block.tables = merge({}, block.gfm, { + nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/, + table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/, + }); + + /** + * Block Lexer + */ + + function Lexer(options) { + this.tokens = []; + this.tokens.links = {}; + this.options = options || marked.defaults; + // Sort math delimiters, because parsing of $$..$$ must come before $..$ etc. + if (Array.isArray(this.options.mathDelimiters)) { + this.options.mathDelimiters.sort(function (a, b) { + return b[0].length - a[0].length; + }); + } + this.rules = block.normal; + + if (this.options.gfm) { + if (this.options.tables) { + this.rules = block.tables; + } else { + this.rules = block.gfm; + } + } + } + + /** + * Expose Block Rules + */ + + Lexer.rules = block; + + /** + * Static Lex Method + */ + + Lexer.lex = function (src, options) { + var lexer = new Lexer(options); + return lexer.lex(src); + }; + + /** + * Preprocessing + */ + + Lexer.prototype.lex = function (src) { + src = src + .replace(/\r\n|\r/g, "\n") + .replace(/\t/g, " ") + .replace(/\u00a0/g, " ") + .replace(/\u2424/g, "\n"); + + return this.token(src, true); + }; + + /** + * Lexing + */ + + Lexer.prototype.token = function (src, top, bq) { + var src = src.replace(/^ +$/gm, ""), + next, + loose, + cap, + bull, + b, + item, + space, + i, + l; + + while (src) { + // newline + if ((cap = this.rules.newline.exec(src))) { + src = src.substring(cap[0].length); + if (cap[0].length > 1) { + this.tokens.push({ + type: "space", + }); + } + } + + // code + if ((cap = this.rules.code.exec(src))) { + src = src.substring(cap[0].length); + cap = cap[0].replace(/^ {4}/gm, ""); + this.tokens.push({ + type: "code", + text: !this.options.pedantic ? cap.replace(/\n+$/, "") : cap, + }); + continue; + } + + // fences (gfm) + if ((cap = this.rules.fences.exec(src))) { + src = src.substring(cap[0].length); + this.tokens.push({ + type: "code", + lang: cap[2], + text: cap[3] || "", + }); + continue; + } + + // heading + if ((cap = this.rules.heading.exec(src))) { + src = src.substring(cap[0].length); + this.tokens.push({ + type: "heading", + depth: cap[1].length, + text: cap[2], + }); + continue; + } + + // table no leading pipe (gfm) + if (top && (cap = this.rules.nptable.exec(src))) { + src = src.substring(cap[0].length); + + item = { + type: "table", + header: cap[1].replace(/^ *| *\| *$/g, "").split(/ *\| */), + align: cap[2].replace(/^ *|\| *$/g, "").split(/ *\| */), + cells: cap[3].replace(/\n$/, "").split("\n"), + }; + + for (i = 0; i < item.align.length; i++) { + if (/^ *-+: *$/.test(item.align[i])) { + item.align[i] = "right"; + } else if (/^ *:-+: *$/.test(item.align[i])) { + item.align[i] = "center"; + } else if (/^ *:-+ *$/.test(item.align[i])) { + item.align[i] = "left"; + } else { + item.align[i] = null; + } + } + + for (i = 0; i < item.cells.length; i++) { + item.cells[i] = item.cells[i].split(/ *\| */); + } + + this.tokens.push(item); + + continue; + } + + // lheading + if ((cap = this.rules.lheading.exec(src))) { + src = src.substring(cap[0].length); + this.tokens.push({ + type: "heading", + depth: cap[2] === "=" ? 1 : 2, + text: cap[1], + }); + continue; + } + + // hr + if ((cap = this.rules.hr.exec(src))) { + src = src.substring(cap[0].length); + this.tokens.push({ + type: "hr", + }); + continue; + } + + // blockquote + if ((cap = this.rules.blockquote.exec(src))) { + src = src.substring(cap[0].length); + + this.tokens.push({ + type: "blockquote_start", + }); + + cap = cap[0].replace(/^ *> ?/gm, ""); + + // Pass `top` to keep the current + // "toplevel" state. This is exactly + // how markdown.pl works. + this.token(cap, top, true); + + this.tokens.push({ + type: "blockquote_end", + }); + + continue; + } + + // list + if ((cap = this.rules.list.exec(src))) { + src = src.substring(cap[0].length); + bull = cap[2]; + + this.tokens.push({ + type: "list_start", + ordered: bull.length > 1, + }); + + // Get each top-level item. + cap = cap[0].match(this.rules.item); + + next = false; + l = cap.length; + i = 0; + + for (; i < l; i++) { + item = cap[i]; + + // Remove the list item's bullet + // so it is seen as the next token. + space = item.length; + item = item.replace(/^ *([*+-]|\d+\.) +/, ""); + + // Outdent whatever the + // list item contains. Hacky. + if (~item.indexOf("\n ")) { + space -= item.length; + item = !this.options.pedantic + ? item.replace(new RegExp("^ {1," + space + "}", "gm"), "") + : item.replace(/^ {1,4}/gm, ""); + } + + // Determine whether the next list item belongs here. + // Backpedal if it does not belong in this list. + if (this.options.smartLists && i !== l - 1) { + b = block.bullet.exec(cap[i + 1])[0]; + if (bull !== b && !(bull.length > 1 && b.length > 1)) { + src = cap.slice(i + 1).join("\n") + src; + i = l - 1; + } + } + + // Determine whether item is loose or not. + // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/ + // for discount behavior. + loose = next || /\n\n(?!\s*$)/.test(item); + if (i !== l - 1) { + next = item.charAt(item.length - 1) === "\n"; + if (!loose) loose = next; + } + + this.tokens.push({ + type: loose ? "loose_item_start" : "list_item_start", + }); + + // Recurse. + this.token(item, false, bq); + + this.tokens.push({ + type: "list_item_end", + }); + } + + this.tokens.push({ + type: "list_end", + }); + + continue; + } + + // html + if ((cap = this.rules.html.exec(src))) { + src = src.substring(cap[0].length); + this.tokens.push({ + type: this.options.sanitize ? "paragraph" : "html", + pre: + !this.options.sanitizer && + (cap[1] === "pre" || cap[1] === "script" || cap[1] === "style"), + text: cap[0], + }); + continue; + } + + // def + if (!bq && top && (cap = this.rules.def.exec(src))) { + src = src.substring(cap[0].length); + this.tokens.links[cap[1].toLowerCase()] = { + href: cap[2], + title: cap[3], + }; + continue; + } + + // table (gfm) + if (top && (cap = this.rules.table.exec(src))) { + src = src.substring(cap[0].length); + + item = { + type: "table", + header: cap[1].replace(/^ *| *\| *$/g, "").split(/ *\| */), + align: cap[2].replace(/^ *|\| *$/g, "").split(/ *\| */), + cells: cap[3].replace(/(?: *\| *)?\n$/, "").split("\n"), + }; + + for (i = 0; i < item.align.length; i++) { + if (/^ *-+: *$/.test(item.align[i])) { + item.align[i] = "right"; + } else if (/^ *:-+: *$/.test(item.align[i])) { + item.align[i] = "center"; + } else if (/^ *:-+ *$/.test(item.align[i])) { + item.align[i] = "left"; + } else { + item.align[i] = null; + } + } + + for (i = 0; i < item.cells.length; i++) { + item.cells[i] = item.cells[i] + .replace(/^ *\| *| *\| *$/g, "") + .split(/ *\| */); + } + + this.tokens.push(item); + + continue; + } + + // top-level paragraph + if (top && (cap = this.rules.paragraph.exec(src))) { + src = src.substring(cap[0].length); + this.tokens.push({ + type: "paragraph", + text: + cap[1].charAt(cap[1].length - 1) === "\n" + ? cap[1].slice(0, -1) + : cap[1], + }); + continue; + } + + // text + if ((cap = this.rules.text.exec(src))) { + // Top-level should never reach here. + src = src.substring(cap[0].length); + this.tokens.push({ + type: "text", + text: cap[0], + }); + continue; + } + + if (src) { + throw new Error("Infinite loop on byte: " + src.charCodeAt(0)); + } + } + + return this.tokens; + }; + + /** + * Inline-Level Grammar + */ + + var inline = { + escape: /^\\([\\`*{}\[\]()#+\-.!_>])/, + autolink: /^<([^ >]+(@|:\/)[^ >]+)>/, + url: noop, + tag: /^|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/, + link: /^!?\[(inside)\]\(href\)/, + reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/, + nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/, + strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/, + em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/, + code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/, + br: /^ {2,}\n(?!\s*$)/, + del: noop, + text: /^[\s\S]+?(?=[\\?(?:\s+['"]([\s\S]*?)['"])?\s*/; + + inline.link = replace(inline.link)("inside", inline._inside)( + "href", + inline._href + )(); + + inline.reflink = replace(inline.reflink)("inside", inline._inside)(); + + /** + * Normal Inline Grammar + */ + + inline.normal = merge({}, inline); + + /** + * Pedantic Inline Grammar + */ + + inline.pedantic = merge({}, inline.normal, { + strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/, + em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/, + }); + + /** + * GFM Inline Grammar + */ + + inline.gfm = merge({}, inline.normal, { + escape: replace(inline.escape)("])", "~|])")(), + url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/, + del: /^~~(?=\S)([\s\S]*?\S)~~/, + text: replace(inline.text)("]|", "~]|")("|", "|https?://|")(), + }); + + /** + * GFM + Line Breaks Inline Grammar + */ + + inline.breaks = merge({}, inline.gfm, { + br: replace(inline.br)("{2,}", "*")(), + text: replace(inline.gfm.text)("{2,}", "*")(), + }); + + /** + * Inline Lexer & Compiler + */ + + function InlineLexer(links, options) { + this.options = options || marked.defaults; + this.links = links; + this.rules = inline.normal; + this.renderer = this.options.renderer || new Renderer(); + this.renderer.options = this.options; + + if (!this.links) { + throw new Error("Tokens array requires a `links` property."); + } + + if (this.options.gfm) { + if (this.options.breaks) { + this.rules = inline.breaks; + } else { + this.rules = inline.gfm; + } + } else if (this.options.pedantic) { + this.rules = inline.pedantic; + } + + // Make rules compatible with specified math delimiters + if ( + this.options.mathDelimiters && + Array.isArray(this.options.mathDelimiters) + ) { + var regexEscape = function (s) { + return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, "\\$&"); + }; + var openingMathDelimiters = []; + for (var i = 0; i < this.options.mathDelimiters.length; i++) { + if ( + Array.isArray(this.options.mathDelimiters[i]) && + this.options.mathDelimiters[i].length === 2 + ) { + openingMathDelimiters.push( + regexEscape(this.options.mathDelimiters[i][0]) + ); + } + } + this.rules.text = replace(this.rules.text)( + "]|", + "]|(" + openingMathDelimiters.join("|") + ")|" + )(); + } + } + + /** + * Expose Inline Rules + */ + + InlineLexer.rules = inline; + + /** + * Static Lexing/Compiling Method + */ + + InlineLexer.output = function (src, links, options) { + var inline = new InlineLexer(links, options); + return inline.output(src); + }; + + /** + * Lexing/Compiling + */ + + InlineLexer.prototype.output = function (src) { + var out = "", + link, + text, + href, + cap; + + while (src) { + // Math wrapped in specified math delimiters is passed through unprocessed, + // For use with MathJax / KaTeX etc. + if (this.options.mathDelimiters) { + for (var i = 0; i < this.options.mathDelimiters.length; i++) { + var delim = this.options.mathDelimiters[i]; + if (delim === "beginend") { + // \begin{}..\end{} + if (src.substr(0, 7) === "\\begin{") { + var idx = 7; + // traverse nested \begin{}..\end{} + var traverse = function () { + while (src.substr(idx, 5) !== "\\end{") { + if (src.substr(idx, 7) === "\\begin{") { + idx += 7; + traverse(); + } else { + idx++; + } + } + idx += 5; + while (src[idx] !== "}") { + idx++; + } + idx++; + }; + traverse(); + out += this.renderer.math(src.substring(0, idx), "begin", "end"); // TODO, extract begin and end as \begin{foo} and \end{foo}. + src = src.substring(idx); + continue; + } + } else if ( + Array.isArray(delim) && + delim.length === 2 && + src.substr(0, delim[0].length) === delim[0] + ) { + var idx = delim[0].length; + while (true) { + if (idx > src.length) { + break; + } + // Allow escaping closing delimiter + if ( + delim[1].length === 1 && + src.substr(idx, 2) === "\\" + delim[1] + ) { + idx += 2; + } else if (src.substr(idx, delim[1].length) !== delim[1]) { + idx++; + } else { + break; + } + } + idx += delim[1].length; + out += this.renderer.math( + src.substring(0, idx), + delim[0], + delim[1] + ); + src = src.substring(idx); + continue; + } + } + } + + // escape + if ((cap = this.rules.escape.exec(src))) { + src = src.substring(cap[0].length); + out += cap[1]; + continue; + } + + // autolink + if ((cap = this.rules.autolink.exec(src))) { + src = src.substring(cap[0].length); + if (cap[2] === "@") { + text = + cap[1].charAt(6) === ":" + ? this.mangle(cap[1].substring(7)) + : this.mangle(cap[1]); + href = this.mangle("mailto:") + text; + } else { + text = escape(cap[1]); + href = text; + } + out += this.renderer.link(href, null, text); + continue; + } + + // url (gfm) + if (!this.inLink && (cap = this.rules.url.exec(src))) { + src = src.substring(cap[0].length); + text = escape(cap[1]); + href = text; + out += this.renderer.link(href, null, text); + continue; + } + + // tag + if ((cap = this.rules.tag.exec(src))) { + if (!this.inLink && /^/i.test(cap[0])) { + this.inLink = false; + } + src = src.substring(cap[0].length); + out += this.options.sanitize + ? this.options.sanitizer + ? this.options.sanitizer(cap[0]) + : escape(cap[0]) + : cap[0]; + continue; + } + + // link + if ((cap = this.rules.link.exec(src))) { + src = src.substring(cap[0].length); + this.inLink = true; + out += this.outputLink(cap, { + href: cap[2], + title: cap[3], + }); + this.inLink = false; + continue; + } + + // reflink, nolink + if ( + (cap = this.rules.reflink.exec(src)) || + (cap = this.rules.nolink.exec(src)) + ) { + src = src.substring(cap[0].length); + link = (cap[2] || cap[1]).replace(/\s+/g, " "); + link = this.links[link.toLowerCase()]; + if (!link || !link.href) { + out += cap[0].charAt(0); + src = cap[0].substring(1) + src; + continue; + } + this.inLink = true; + out += this.outputLink(cap, link); + this.inLink = false; + continue; + } + + // strong + if ((cap = this.rules.strong.exec(src))) { + src = src.substring(cap[0].length); + out += this.renderer.strong(this.output(cap[2] || cap[1])); + continue; + } + + // em + if ((cap = this.rules.em.exec(src))) { + src = src.substring(cap[0].length); + out += this.renderer.em(this.output(cap[2] || cap[1])); + continue; + } + + // code + if ((cap = this.rules.code.exec(src))) { + src = src.substring(cap[0].length); + out += this.renderer.codespan(escape(cap[2], true)); + continue; + } + + // br + if ((cap = this.rules.br.exec(src))) { + src = src.substring(cap[0].length); + out += this.renderer.br(); + continue; + } + + // del (gfm) + if ((cap = this.rules.del.exec(src))) { + src = src.substring(cap[0].length); + out += this.renderer.del(this.output(cap[1])); + continue; + } + + // text + if ((cap = this.rules.text.exec(src))) { + src = src.substring(cap[0].length); + out += this.renderer.text(escape(this.smartypants(cap[0]))); + continue; + } + + if (src) { + throw new Error("Infinite loop on byte: " + src.charCodeAt(0)); + } + } + + return out; + }; + + /** + * Compile Link + */ + + InlineLexer.prototype.outputLink = function (cap, link) { + var href = escape(link.href), + title = link.title ? escape(link.title) : null; + + return cap[0].charAt(0) !== "!" + ? this.renderer.link(href, title, this.output(cap[1])) + : this.renderer.image(href, title, escape(cap[1])); + }; + + /** + * Smartypants Transformations + */ + + InlineLexer.prototype.smartypants = function (text) { + if (!this.options.smartypants) return text; + return ( + text + // em-dashes + .replace(/---/g, "\u2014") + // en-dashes + .replace(/--/g, "\u2013") + // opening singles + .replace(/(^|[-\u2014/(\[{"\s])'/g, "$1\u2018") + // closing singles & apostrophes + .replace(/'/g, "\u2019") + // opening doubles + .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, "$1\u201c") + // closing doubles + .replace(/"/g, "\u201d") + // ellipses + .replace(/\.{3}/g, "\u2026") + ); + }; + + /** + * Mangle Links + */ + + InlineLexer.prototype.mangle = function (text) { + if (!this.options.mangle) return text; + var out = "", + l = text.length, + i = 0, + ch; + + for (; i < l; i++) { + ch = text.charCodeAt(i); + if (Math.random() > 0.5) { + ch = "x" + ch.toString(16); + } + out += "&#" + ch + ";"; + } + + return out; + }; + + /** + * Renderer + */ + + function Renderer(options) { + this.options = options || {}; + } + + Renderer.prototype.code = function (code, lang, escaped) { + if (this.options.highlight) { + var out = this.options.highlight(code, lang); + if (out != null && out !== code) { + escaped = true; + code = out; + } + } + + if (!lang) { + return ( + "
" +
+        (escaped ? code : escape(code, true)) +
+        "\n
" + ); + } + + return ( + '
' +
+      (escaped ? code : escape(code, true)) +
+      "\n
\n" + ); + }; + + Renderer.prototype.blockquote = function (quote) { + return "
\n" + quote + "
\n"; + }; + + Renderer.prototype.html = function (html) { + return html; + }; + + Renderer.prototype.heading = function (text, level, raw) { + return ( + "' + + text + + "\n" + ); + }; + + Renderer.prototype.hr = function () { + return this.options.xhtml ? "
\n" : "
\n"; + }; + + Renderer.prototype.list = function (body, ordered) { + var type = ordered ? "ol" : "ul"; + return "<" + type + ">\n" + body + "\n"; + }; + + Renderer.prototype.listitem = function (text) { + return "
  • " + text + "
  • \n"; + }; + + Renderer.prototype.paragraph = function (text) { + return "

    " + text + "

    \n"; + }; + + Renderer.prototype.table = function (header, body) { + return ( + "\n" + + "\n" + + header + + "\n" + + "\n" + + body + + "\n" + + "
    \n" + ); + }; + + Renderer.prototype.tablerow = function (content) { + return "\n" + content + "\n"; + }; + + Renderer.prototype.tablecell = function (content, flags) { + var type = flags.header ? "th" : "td"; + var tag = flags.align + ? "<" + type + ' style="text-align:' + flags.align + '">' + : "<" + type + ">"; + return tag + content + "\n"; + }; + + // span level renderer + Renderer.prototype.strong = function (text) { + return "" + text + ""; + }; + + Renderer.prototype.em = function (text) { + return "" + text + ""; + }; + + Renderer.prototype.codespan = function (text) { + return "" + text + ""; + }; + + Renderer.prototype.br = function () { + return this.options.xhtml ? "
    " : "
    "; + }; + + Renderer.prototype.del = function (text) { + return "" + text + ""; + }; + + Renderer.prototype.link = function (href, title, text) { + if (this.options.sanitize) { + try { + var prot = decodeURIComponent(unescape(href)) + .replace(/[^\w:]/g, "") + .toLowerCase(); + } catch (e) { + return ""; + } + if ( + prot.indexOf("javascript:") === 0 || + prot.indexOf("vbscript:") === 0 || + prot.indexOf("data:") === 0 + ) { + return ""; + } + } + var out = '
    "; + return out; + }; + + Renderer.prototype.image = function (href, title, text) { + var out = '' + text + '" : ">"; + return out; + }; + + Renderer.prototype.text = function (text) { + return text; + }; + + Renderer.prototype.math = function (text, begin, end) { + return text; + + //// Example usage for inline TeX-processing: + // if ( begin === "$" || begin === "\\(" ) { + // return katex.renderToString(text, { displayMode: false }); + // } else if ( begin === "$$" || begin === "\\[" ) { + // return katex.renderToString(text, { displayMode: true }); + // } else { + // return katex.renderToString(begin + text + end, { displayMode: true }); + // } + }; + + /** + * Parsing & Compiling + */ + + function Parser(options) { + this.tokens = []; + this.token = null; + this.options = options || marked.defaults; + this.options.renderer = this.options.renderer || new Renderer(); + this.renderer = this.options.renderer; + this.renderer.options = this.options; + } + + /** + * Static Parse Method + */ + + Parser.parse = function (src, options, renderer) { + var parser = new Parser(options, renderer); + return parser.parse(src); + }; + + /** + * Parse Loop + */ + + Parser.prototype.parse = function (src) { + this.inline = new InlineLexer(src.links, this.options, this.renderer); + this.tokens = src.reverse(); + + var out = ""; + while (this.next()) { + out += this.tok(); + } + + return out; + }; + + /** + * Next Token + */ + + Parser.prototype.next = function () { + return (this.token = this.tokens.pop()); + }; + + /** + * Preview Next Token + */ + + Parser.prototype.peek = function () { + return this.tokens[this.tokens.length - 1] || 0; + }; + + /** + * Parse Text Tokens + */ + + Parser.prototype.parseText = function () { + var body = this.token.text; + + while (this.peek().type === "text") { + body += "\n" + this.next().text; + } + + return this.inline.output(body); + }; + + /** + * Parse Current Token + */ + + Parser.prototype.tok = function () { + switch (this.token.type) { + case "space": { + return ""; + } + case "hr": { + return this.renderer.hr(); + } + case "heading": { + return this.renderer.heading( + this.inline.output(this.token.text), + this.token.depth, + this.token.text + ); + } + case "code": { + return this.renderer.code( + this.token.text, + this.token.lang, + this.token.escaped + ); + } + case "table": { + var header = "", + body = "", + i, + row, + cell, + flags, + j; + + // header + cell = ""; + for (i = 0; i < this.token.header.length; i++) { + flags = { header: true, align: this.token.align[i] }; + cell += this.renderer.tablecell( + this.inline.output(this.token.header[i]), + { header: true, align: this.token.align[i] } + ); + } + header += this.renderer.tablerow(cell); + + for (i = 0; i < this.token.cells.length; i++) { + row = this.token.cells[i]; + + cell = ""; + for (j = 0; j < row.length; j++) { + cell += this.renderer.tablecell(this.inline.output(row[j]), { + header: false, + align: this.token.align[j], + }); + } + + body += this.renderer.tablerow(cell); + } + return this.renderer.table(header, body); + } + case "blockquote_start": { + var body = ""; + + while (this.next().type !== "blockquote_end") { + body += this.tok(); + } + + return this.renderer.blockquote(body); + } + case "list_start": { + var body = "", + ordered = this.token.ordered; + + while (this.next().type !== "list_end") { + body += this.tok(); + } + + return this.renderer.list(body, ordered); + } + case "list_item_start": { + var body = ""; + + while (this.next().type !== "list_item_end") { + body += this.token.type === "text" ? this.parseText() : this.tok(); + } + + return this.renderer.listitem(body); + } + case "loose_item_start": { + var body = ""; + + while (this.next().type !== "list_item_end") { + body += this.tok(); + } + + return this.renderer.listitem(body); + } + case "html": { + var html = + !this.token.pre && !this.options.pedantic + ? this.inline.output(this.token.text) + : this.token.text; + return this.renderer.html(html); + } + case "paragraph": { + return this.renderer.paragraph(this.inline.output(this.token.text)); + } + case "text": { + return this.renderer.paragraph(this.parseText()); + } + } + }; + + /** + * Helpers + */ + + function escape(html, encode) { + return html + .replace(!encode ? /&(?!#?\w+;)/g : /&/g, "&") + .replace(//g, ">") + .replace(/"/g, """) + .replace(/'/g, "'"); + } + + function unescape(html) { + // explicitly match decimal, hex, and named HTML entities + return html.replace( + /&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g, + function (_, n) { + n = n.toLowerCase(); + if (n === "colon") return ":"; + if (n.charAt(0) === "#") { + return n.charAt(1) === "x" + ? String.fromCharCode(parseInt(n.substring(2), 16)) + : String.fromCharCode(+n.substring(1)); + } + return ""; + } + ); + } + + function replace(regex, opt) { + regex = regex.source; + opt = opt || ""; + return function self(name, val) { + if (!name) return new RegExp(regex, opt); + val = val.source || val; + val = val.replace(/(^|[^\[])\^/g, "$1"); + regex = regex.replace(name, val); + return self; + }; + } + + function noop() {} + noop.exec = noop; + + function merge(obj) { + var i = 1, + target, + key; + + for (; i < arguments.length; i++) { + target = arguments[i]; + for (key in target) { + if (Object.prototype.hasOwnProperty.call(target, key)) { + obj[key] = target[key]; + } + } + } + + return obj; + } + + /** + * Marked + */ + + function marked(src, opt, callback) { + if (callback || typeof opt === "function") { + if (!callback) { + callback = opt; + opt = null; + } + + opt = merge({}, marked.defaults, opt || {}); + + var highlight = opt.highlight, + tokens, + pending, + i = 0; + + try { + tokens = Lexer.lex(src, opt); + } catch (e) { + return callback(e); + } + + pending = tokens.length; + + var done = function (err) { + if (err) { + opt.highlight = highlight; + return callback(err); + } + + var out; + + try { + out = Parser.parse(tokens, opt); + } catch (e) { + err = e; + } + + opt.highlight = highlight; + + return err ? callback(err) : callback(null, out); + }; + + if (!highlight || highlight.length < 3) { + return done(); + } + + delete opt.highlight; + + if (!pending) return done(); + + for (; i < tokens.length; i++) { + (function (token) { + if (token.type !== "code") { + return --pending || done(); + } + return highlight(token.text, token.lang, function (err, code) { + if (err) return done(err); + if (code == null || code === token.text) { + return --pending || done(); + } + token.text = code; + token.escaped = true; + --pending || done(); + }); + })(tokens[i]); + } + + return; + } + try { + if (opt) opt = merge({}, marked.defaults, opt); + return Parser.parse(Lexer.lex(src, opt), opt); + } catch (e) { + e.message += "\nPlease report this to https://github.com/chjj/marked."; + if ((opt || marked.defaults).silent) { + return ( + "

    An error occured:

    " +
    +          escape(e.message + "", true) +
    +          "
    " + ); + } + throw e; + } + } + + /** + * Options + */ + + marked.options = marked.setOptions = function (opt) { + merge(marked.defaults, opt); + return marked; + }; + + marked.defaults = { + gfm: true, + tables: true, + breaks: false, + pedantic: false, + sanitize: false, + sanitizer: null, + mangle: true, + smartLists: false, + silent: false, + highlight: null, + langPrefix: "lang-", + smartypants: false, + headerPrefix: "", + mathDelimiters: null, + renderer: new Renderer(), + xhtml: false, + }; + + /** + * Expose + */ + + marked.Parser = Parser; + marked.parser = Parser.parse; + + marked.Renderer = Renderer; + + marked.Lexer = Lexer; + marked.lexer = Lexer.lex; + + marked.InlineLexer = InlineLexer; + marked.inlineLexer = InlineLexer.output; + + marked.parse = marked; + + if (typeof module !== "undefined" && typeof exports === "object") { + module.exports = marked; + } else if (typeof define === "function" && define.amd) { + define(function () { + return marked; + }); + } else { + this.marked = marked; + } +}.call( + (function () { + return this || (typeof window !== "undefined" ? window : global); + })() +)); diff --git a/static/assets/js/pages/admin.js b/static/assets/js/pages/admin.js index 8dc8578..028601a 100644 --- a/static/assets/js/pages/admin.js +++ b/static/assets/js/pages/admin.js @@ -2,16 +2,6 @@ const loginInfo = document.getElementById("login-info"); const loginForm = document.getElementById("login-page"); window.addEventListener("DOMContentLoaded", () => { - const firebaseConfig = { - apiKey: "AIzaSyBCmKUnEmm8hLR9ZcFWPYbYiplbP6yUzfU", - authDomain: "thatcomputerscientist-e9cf2.firebaseapp.com", - projectId: "thatcomputerscientist-e9cf2", - storageBucket: "thatcomputerscientist-e9cf2.appspot.com", - messagingSenderId: "178402875544", - appId: "1:178402875544:web:8c9d8880d3ef495a5658ed", - measurementId: "G-JECWWZG5J6", - }; - firebase.initializeApp(firebaseConfig); const signInButton = document.getElementById("signInButton"); signInButton.addEventListener("click", () => { const email = document.getElementById("inputEmail").value; diff --git a/static/assets/js/pages/authCheck.js b/static/assets/js/pages/authCheck.js new file mode 100644 index 0000000..722fe0c --- /dev/null +++ b/static/assets/js/pages/authCheck.js @@ -0,0 +1,10 @@ +firebase.auth().onAuthStateChanged((user) => { + if (!user) { + window.location.assign("/admin/"); + } +}); +document.getElementById("logout").addEventListener("click", () => { + firebase.auth().signOut().then(() => { + window.location.assign("/admin/"); + }); +}); diff --git a/static/assets/js/pages/config.js b/static/assets/js/pages/config.js new file mode 100644 index 0000000..256173a --- /dev/null +++ b/static/assets/js/pages/config.js @@ -0,0 +1,10 @@ +const firebaseConfig = { + apiKey: "AIzaSyBCmKUnEmm8hLR9ZcFWPYbYiplbP6yUzfU", + authDomain: "thatcomputerscientist-e9cf2.firebaseapp.com", + projectId: "thatcomputerscientist-e9cf2", + storageBucket: "thatcomputerscientist-e9cf2.appspot.com", + messagingSenderId: "178402875544", + appId: "1:178402875544:web:8c9d8880d3ef495a5658ed", + measurementId: "G-JECWWZG5J6", +}; +firebase.initializeApp(firebaseConfig); -- cgit v1.2.3