diff --git a/amanuensis/parser/__init__.py b/amanuensis/parser/__init__.py
index 9aa9584..1de2c5d 100644
--- a/amanuensis/parser/__init__.py
+++ b/amanuensis/parser/__init__.py
@@ -2,14 +2,16 @@
Module encapsulating all markdown parsing functionality.
"""
-from .analyze import FeatureCounter, GetCitations
+from .analyze import ConstraintAnalysis, GetCitations
+from .core import normalize_title
from .helpers import titlesort, filesafe_title
from .parsing import parse_raw_markdown
from .render import PreviewHtmlRenderer, HtmlRenderer
__all__ = [
- FeatureCounter.__name__,
+ ConstraintAnalysis.__name__,
GetCitations.__name__,
+ normalize_title.__name__,
titlesort.__name__,
filesafe_title.__name__,
parse_raw_markdown.__name__,
diff --git a/amanuensis/parser/analyze.py b/amanuensis/parser/analyze.py
index f581353..79ad079 100644
--- a/amanuensis/parser/analyze.py
+++ b/amanuensis/parser/analyze.py
@@ -4,6 +4,9 @@ for verification against constraints.
"""
import re
+from typing import Iterable
+
+from amanuensis.models import LexiconModel
from .core import RenderableVisitor
@@ -21,12 +24,25 @@ class GetCitations(RenderableVisitor):
return self
-class FeatureCounter(RenderableVisitor):
- def __init__(self):
+class ConstraintAnalysis(RenderableVisitor):
+ def __init__(self, lexicon: LexiconModel):
+ self.info: Iterable[str] = []
+ self.warning: Iterable[str] = []
+ self.error: Iterable[str] = []
+
self.word_count = 0
self.citation_count = 0
self.has_signature = False
+ def ParsedArticle(self, span):
+ # Execute over the article tree
+ span.recurse(self)
+ # Perform analysis
+ self.info.append(f'Word count: {self.word_count}')
+ if not self.has_signature:
+ self.warning.append('Missing signature')
+ return self
+
def TextSpan(self, span):
self.word_count += len(re.split(r'\s+', span.innertext.strip()))
return self
diff --git a/amanuensis/parser/render.py b/amanuensis/parser/render.py
index 03c2913..9313c07 100644
--- a/amanuensis/parser/render.py
+++ b/amanuensis/parser/render.py
@@ -5,10 +5,11 @@ readable formats.
from typing import Iterable
+from .core import RenderableVisitor
from .helpers import filesafe_title
-class HtmlRenderer():
+class HtmlRenderer(RenderableVisitor):
"""
Renders an article token tree into published article HTML.
"""
@@ -55,13 +56,15 @@ class HtmlRenderer():
return f'{"".join(span.recurse(self))}'
-class PreviewHtmlRenderer():
+class PreviewHtmlRenderer(RenderableVisitor):
def __init__(self, lexicon):
with lexicon.ctx.read('info') as info:
self.article_map = {
title: article.character
for title, article in info.items()
}
+ self.citations = []
+ self.contents = ""
def TextSpan(self, span):
return span.innertext
@@ -70,7 +73,8 @@ class PreviewHtmlRenderer():
return '
'
def ParsedArticle(self, span):
- return '\n'.join(span.recurse(self))
+ self.contents = '\n'.join(span.recurse(self))
+ return self
def BodyParagraph(self, span):
return f'
{"".join(span.recurse(self))}
' @@ -91,9 +95,10 @@ class PreviewHtmlRenderer(): def CitationSpan(self, span): if span.cite_target in self.article_map: if self.article_map.get(span.cite_target): - link_class = ' style="color:#0000ff"' + link_class = '[extant]' else: - link_class = ' style="color:#ff0000"' + link_class = '[phantom]' else: - link_class = ' style="color:#008000"' - return f'{"".join(span.recurse(self))}' + link_class = '[new]' + self.citations.append(f'{span.cite_target} {link_class}') + return f'{"".join(span.recurse(self))}[{len(self.citations)}]' diff --git a/amanuensis/resources/editor.css b/amanuensis/resources/editor.css index cc9f925..fb0e52b 100644 --- a/amanuensis/resources/editor.css +++ b/amanuensis/resources/editor.css @@ -51,15 +51,12 @@ div#editor-right { div#editor-right div.contentblock { margin: 10px 5px 10px 10px; } -div#editor-right p#editor-warnings { +span.message-warning { color: #808000; } -div#editor-right p#editor-errors { +span.message-error { color: #ff0000; } -span.new { - color: #008000; -} @media only screen and (max-width: 816px) { div#wrapper { max-width: 564px; diff --git a/amanuensis/resources/editor.js b/amanuensis/resources/editor.js index aed7f26..83ea028 100644 --- a/amanuensis/resources/editor.js +++ b/amanuensis/resources/editor.js @@ -7,7 +7,8 @@ function ifNoFurtherChanges(callback, timeout=2000) { nonce = nonce_local; setTimeout(() => { if (nonce == nonce_local) { - callback() + callback(); + nonce = 0; } }, timeout); } @@ -17,11 +18,6 @@ window.onload = function() { // Kill noscript message first document.getElementById("preview").innerHTML = ""; - if (params.article != null) { - document.getElementById("editor-title").value = params.article.title; - document.getElementById("editor-content").value = params.article.contents; - } - onContentChange(0); }; @@ -30,10 +26,7 @@ function buildArticleObject() { var contents = document.getElementById("editor-content").value; return { aid: params.article.aid, - lexicon: params.article.lexicon, - character: params.article.character, title: title, - turn: params.article.turn, status: params.article.status, contents: contents }; @@ -47,11 +40,12 @@ function update(article) { req.onreadystatechange = function () { if (req.readyState == 4 && req.status == 200) { // Update internal state with the returned article object - params.article = req.response.article; + params.status = req.response.status; + document.getElementById("editor-title").value = req.response.title; // Set editor editability based on article status updateEditorStatus(); // Update the preview with the parse information - updatePreview(req.response.info); + updatePreview(req.response); } }; var payload = { article: article }; @@ -66,11 +60,31 @@ function updateEditorStatus() { submitButton.innerText = ready ? "Edit article" : "Submit article"; } -function updatePreview(info) { - var title = document.getElementById("editor-title").value; - var previewHtml = "