diff --git a/amanuensis/resources/editor.css b/amanuensis/resources/editor.css new file mode 100644 index 0000000..553b356 --- /dev/null +++ b/amanuensis/resources/editor.css @@ -0,0 +1,64 @@ + +html, body { + height: 100%; + margin: 0px; +} +div#wrapper { + max-width: 1128px; + height: 100%; + display:flex; + flex-direction: row; + align-items: stretch; +} +div.column { + width: 50%; +} +div#editor-left { + display: flex; + align-items: stretch; +} +div#editor-left div.contentblock { + display: flex; + flex-direction: column; + margin: 10px 10px 10px 5px; + width: 100%; + padding: 5px; +} +div#editor-left div#editor-header { + margin: 5px; + display: flex; + justify-content: space-between; +} +div#editor-left input#editor-title { + font-size: 2em; + margin: 5px; +} +textarea#editor-content { + margin: 5px; + resize: none; + flex-grow: 1; + width: initial; +} +div#editor-right { + overflow-y: scroll; +} +div#editor-right div.contentblock { + margin: 10px 5px 10px 10px; +} +@media only screen and (max-width: 816px) { + div#wrapper { + max-width: 564px; + margin: 0 auto; + padding: inherit; + flex-direction: column; + } + div.column { + width: 100%; + } + div#editor-left { + height: 100%; + } + div#editor-right { + overflow-y: inherit; + } +} diff --git a/amanuensis/resources/editor.js b/amanuensis/resources/editor.js new file mode 100644 index 0000000..16d3768 --- /dev/null +++ b/amanuensis/resources/editor.js @@ -0,0 +1,152 @@ +function onContentChange() { + // Get the new content + var articleTitle = document.getElementById("editor-title").value; + var articleBody = document.getElementById("editor-content").value; + // Pass the draft text to the parser to get the preview html and citations + var parseResult = parseLexipythonMarkdown(articleBody); + // Build the citation block + var citeTexts = [] + for (var i = 0; i < parseResult.citations.length; i++) { + var cite = parseResult.citations[i]; + citeTexts.push("[" + cite.id.toString() + "] " + cite.citeTitle); + } + var citeblockContent = citeTexts.join(" / "); + // Compute warnings and build the control block + var flagMissingSignature = !parseResult.hasSignature; + var wordCount = (parseResult.html + // Delete all HTML tags + .replace(/<[^>]+>/g, "") + .trim() + .split(/\s+/) + .length); + var controlContent = ""; + controlContent += "

Signature: " + (!flagMissingSignature).toString() + "

"; + controlContent += "

Word count: " + wordCount.toString() + "

"; + // Fill in the content blocks + document.getElementById("preview").innerHTML = ( + "

" + articleTitle + "

\n" + + parseResult.html); + document.getElementById("preview-citations").innerHTML = citeblockContent; + document.getElementById("preview-control").innerHTML = controlContent; +} + +function parseLexipythonMarkdown(text) { + // Prepare return values + var result = { + html: "", + citations: [], + hasSignature: false, + }; + // Parse the content by paragraph, extracting the citations + var paras = text.trim().split(/\n\n+/); + citationList = []; + formatId = 1; + for (var i = 0; i < paras.length; i++) { + var para = paras[i]; + // Escape angle brackets + para = para.replace(//g, ">"); + // Replace bold and italic marks with tags + para = para.replace(/\/\/([^\/]+)\/\//g, "$1"); + para = para.replace(/\*\*([^*]+)\*\*/g, "$1"); + // Replace \\LF with
LF + para = para.replace(/\\\\\n/g, "
\n"); + // Abstract citations into the citation record + linkMatch = para.match(/\[\[(([^|\[\]]+)\|)?([^|\[\]]+)\]\]/); + while (linkMatch != null) { + // Identify the citation text and cited article + citeText = linkMatch[2] != null ? linkMatch[2] : linkMatch[3]; + citeTitle = linkMatch[3].charAt(0).toUpperCase() + linkMatch[3].slice(1); + // Record the citation + result.citations.push({ + id: formatId, + citeText: citeText, + citeTitle: citeTitle, + }); + // Stitch the cite text in place of the citation, plus a cite number + para = + para.slice(0, linkMatch.index) + + "" + + citeText + + "" + + "" + + formatId.toString() + + "" + + para.slice(linkMatch.index + linkMatch[0].length); + formatId += 1; // Increment to the next format id + linkMatch = para.match(/\[\[(([^|\[\]]+)\|)?([^|\[\]]+)\]\]/); + } + // Mark signature paragraphs with a classed span + if (para.length > 0 && para[0] == "~") { + para = "

" + para.slice(1) + "

"; + result.hasSignature = true; + } else { + para = "

" + para + "

\n"; + } + result.html += para; + } + if (citationList.length > 0) { + content += "

The following articles will be cited:

\n"; + for (var i = 0; i < citationList.length; i++) { + content += "

" + citationList[i] + "

\n"; + } + } + // Calculate approximate word count + // var wordCount = text.trim().split(/\s+/).length; + // if (text.trim().length < 1) + // wordCount = 0; + // content += "

Article length: approx. " + wordCount + " words

"; + + return result; +} + +// Parse the article content and update the preview pane + + +// function download() { +// var articlePlayer = document.getElementById("article-player").value; +// var articleTurn = document.getElementById("article-turn").value; +// var articleTitle = document.getElementById("article-title").value; +// var articleBody = document.getElementById("article-body").value; +// var articleText = +// "# Player: " + articlePlayer + "\n" + +// "# Turn: " + articleTurn + "\n" + +// "# Title: " + articleTitle + "\n" + +// "\n" + +// articleBody; +// var articleFilename = articleTitle.toLowerCase().replace(/[^a-z0-9- ]/g, "").replace(/ +/g, "-"); +// var downloader = document.createElement("a"); +// downloader.setAttribute("href", "data:text/plain;charset=utf-8," + encodeURIComponent(articleText)); +// downloader.setAttribute("download", articleFilename); +// if (document.createEvent) { +// var event = document.createEvent("MouseEvents"); +// event.initEvent("click", true, true); +// downloader.dispatchEvent(event); +// } else { +// downloader.click(); +// } +// } + +window.onload = function() { + document.getElementById("editor-content").value = "\n\n" + params.defaultSignature; + this.onContentChange(); +}; + +window.addEventListener("beforeunload", function(e) { + var content = document.getElementById("editor-content").value + var hasText = content.length > 0 && content != "\n\n" + params.defaultSignature; + if (hasText) { + e.returnValue = "Are you sure?"; + } +}); + +window.addEventListener("keydown", function(event) { + if (event.ctrlKey || event.metaKey) + { + if (String.fromCharCode(event.which).toLowerCase() == 's') + { + event.preventDefault(); + document.getElementById("preview").innerHTML += "

wrong

"; + } + } +}); \ No newline at end of file diff --git a/amanuensis/server/lexicon.py b/amanuensis/server/lexicon.py index 18ab352..460ac50 100644 --- a/amanuensis/server/lexicon.py +++ b/amanuensis/server/lexicon.py @@ -134,4 +134,10 @@ def get_bp(): def stats(name): return render_template('lexicon/statistics.html') + @bp.route('/session/editor/', methods=['GET']) + @lexicon_param + @player_required + def editor(name): + return render_template('lexicon/editor.html') + return bp diff --git a/amanuensis/templates/lexicon/editor.html b/amanuensis/templates/lexicon/editor.html new file mode 100644 index 0000000..2c84dce --- /dev/null +++ b/amanuensis/templates/lexicon/editor.html @@ -0,0 +1,52 @@ +{% set characters = g.lexicon.get_characters_for_player(current_user.id) %} +{% if not characters %} +{% set characters = [g.lexicon.character.default ] %} +{% endif %} + + + + + + {% block title %}{% endblock %} + + + + + + +
+
+
+
+ + + {{ current_user.username }} + +
+ + +
+
+
+
+

This editor requires Javascript to function.

+
+
+

[1]

+
+
+

Article length:

+
+
+
+ +