diff --git a/config.json b/config.json index 1177083ac800006377b47e1a0cec1a63a47eacd5..7498361944c4372139e3c7f763464f46217d8a86 100644 --- a/config.json +++ b/config.json @@ -85,6 +85,9 @@ "-tex_math_double_backslash" ] }, + "text/x-tex": { + "base": "latex" + }, "application/x-tex": { "base": "latex" } diff --git a/fgs/pandoc.py b/fgs/pandoc.py index 5218451c441b926df6ee1dce286bcd50f0ec8cf4..9fb7a6eaf8ab455c947bd785031e88a0c0b0fc78 100644 --- a/fgs/pandoc.py +++ b/fgs/pandoc.py @@ -181,6 +181,12 @@ class Element(): def parse_int(self, raw_num): return raw_num # TODO + def parse_double_as_percentage(self, raw_num): + res = {} + res["raw"] = raw_num + res["percentage"] = raw_num * 100 + return res + def parse_target(self, raw_target): # For URLs res = {} rawurl = self.parse_text(raw_target[0]) @@ -308,6 +314,149 @@ class BlockContainer(Block): # Attr [Block] self.content = self.parse_blocks(pandocraw[1]) # TODO handle alerts +class BlockDefinitionList(Block): # [([Inline], [[Block]])] + etype = "definitionlist" + def parse_internal(self, pandocraw): + """ + [ + [ + [{'t': 'Str', 'c': 'AStA'}], + [ + [{'t': 'Plain', 'c': [{'t': 'Str', 'c': 'Allgemeiner'}, {'t': 'Space'}, {'t': 'Str', 'c': 'studierenden'}, {'t': 'Space'}, {'t': 'Str', 'c': 'Ausschuss'}]}] + ] + ] + ] + """ + self.items = [] + for rawitem in pandocraw: + item = {} + item["term"] = self.parse_inlines(rawitem[0]) + item["definitions"] = [] + for rawdefinition in rawitem[1]: + item["definitions"].append(self.parse_blocks(rawdefinition)) + self.items.append(item) + +class BlockTable(Block): # Attr Caption [ColSpec] TableHead [TableBody] TableFoot + etype = "table" + def parse_internal(self, pandocraw): + """ + [ + ['', [], []],# Attr + [None, []], # Caption + [ # [ColSpec] + [{'t': 'AlignLeft'}, {'t': 'ColWidth', 'c': 0.5279503105590062}], + [{'t': 'AlignCenter'}, {'t': 'ColWidth', 'c': 0.11180124223602485}], + [{'t': 'AlignCenter'}, {'t': 'ColWidth', 'c': 0.09937888198757763}], + [{'t': 'AlignCenter'}, {'t': 'ColWidth', 'c': 0.10559006211180125}], + [{'t': 'AlignCenter'}, {'t': 'ColWidth', 'c': 0.07453416149068323}], + [{'t': 'AlignCenter'}, {'t': 'ColWidth', 'c': 0.08074534161490683}] + ], + [ # TableHead + ['', [], []], + [ + [ + ['', [], []], + [ + [['', [], []], {'t': 'AlignDefault'}, 1, 1, []], + [['', [], []], {'t': 'AlignDefault'}, 1, 1, [{'t': 'Plain', 'c': [{'t': 'Str', 'c': 'Owner'}, {'t': 'Space'}, {'t': 'Str', 'c': 'read/write'}]}]], + [['', [], []], {'t': 'AlignDefault'}, 1, 1, [{'t': 'Plain', 'c': [{'t': 'Str', 'c': 'Signed-in'}, {'t': 'Space'}, {'t': 'Str', 'c': 'read'}]}]], + [['', [], []], {'t': 'AlignDefault'}, 1, 1, [{'t': 'Plain', 'c': [{'t': 'Str', 'c': 'Signed-in'}, {'t': 'Space'}, {'t': 'Str', 'c': 'write'}]}]], + [['', [], []], {'t': 'AlignDefault'}, 1, 1, [{'t': 'Plain', 'c': [{'t': 'Str', 'c': 'Guest'}, {'t': 'Space'}, {'t': 'Str', 'c': 'read'}]}]], + [['', [], []], {'t': 'AlignDefault'}, 1, 1, [{'t': 'Plain', 'c': [{'t': 'Str', 'c': 'Guest'}, {'t': 'Space'}, {'t': 'Str', 'c': 'write'}]}]] + ] + ] + ] + ], + [ # [TableBody] + [['', [], []], 0, [], [[['', [], []], [[['', [], []], {'t': 'AlignDefault'}, 1, 1, [{'t': 'Plain', 'c': [{'t': 'Span', 'c': [['', ['text-nowrap'], []], [{'t': 'RawInline', 'c': ['html', '<i class="fa fa-leaf fa-fw">']}, {'t': 'RawInline', 'c': ['html', '</i>']}, {'t': 'Space'}, {'t': 'Strong', 'c': [{'t': 'Str', 'c': 'Freely'}]}]]}]}]], [['', [], []], {'t': 'AlignDefault'}, 1, 1, [{'t': 'Plain', 'c': [{'t': 'Str', 'c': '✔'}]}]], [['', [], []], {'t': 'AlignDefault'}, 1, 1, [{'t': 'Plain', 'c': [{'t': 'Str', 'c': '✔'}]}]], [['', [], []], {'t': 'AlignDefault'}, 1, 1, [{'t': 'Plain', 'c': [{'t': 'Str', 'c': '✔'}]}]], [['', [], []], {'t': 'AlignDefault'}, 1, 1, [{'t': 'Plain', 'c': [{'t': 'Str', 'c': '✔'}]}]], [['', [], []], {'t': 'AlignDefault'}, 1, 1, [{'t': 'Plain', 'c': [{'t': 'Str', 'c': '✔'}]}]]]], [['', [], []], [[['', [], []], {'t': 'AlignDefault'}, 1, 1, [{'t': 'Plain', 'c': [{'t': 'Span', 'c': [['', ['text-nowrap'], []], [{'t': 'RawInline', 'c': ['html', '<i class="fa fa-pencil fa-fw">']}, {'t': 'RawInline', 'c': ['html', '</i>']}, {'t': 'Space'}, {'t': 'Strong', 'c': [{'t': 'Str', 'c': 'Editable'}]}]]}]}]], [['', [], []], {'t': 'AlignDefault'}, 1, 1, [{'t': 'Plain', 'c': [{'t': 'Str', 'c': '✔'}]}]], [['', [], []], {'t': 'AlignDefault'}, 1, 1, [{'t': 'Plain', 'c': [{'t': 'Str', 'c': '✔'}]}]], [['', [], []], {'t': 'AlignDefault'}, 1, 1, [{'t': 'Plain', 'c': [{'t': 'Str', 'c': '✔'}]}]], [['', [], []], {'t': 'AlignDefault'}, 1, 1, [{'t': 'Plain', 'c': [{'t': 'Str', 'c': '✔'}]}]], [['', [], []], {'t': 'AlignDefault'}, 1, 1, [{'t': 'Plain', 'c': [{'t': 'Str', 'c': '✖'}]}]]]], [['', [], []], [[['', [], []], {'t': 'AlignDefault'}, 1, 1, [{'t': 'Plain', 'c': [{'t': 'Span', 'c': [['', ['text-nowrap'], []], [{'t': 'RawInline', 'c': ['html', '<i class="fa fa-id-card fa-fw">']}, {'t': 'RawInline', 'c': ['html', '</i>']}, {'t': 'Space'}, {'t': 'Strong', 'c': [{'t': 'Str', 'c': 'Limited'}]}]]}]}]], [['', [], []], {'t': 'AlignDefault'}, 1, 1, [{'t': 'Plain', 'c': [{'t': 'Str', 'c': '✔'}]}]], [['', [], []], {'t': 'AlignDefault'}, 1, 1, [{'t': 'Plain', 'c': [{'t': 'Str', 'c': '✔'}]}]], [['', [], []], {'t': 'AlignDefault'}, 1, 1, [{'t': 'Plain', 'c': [{'t': 'Str', 'c': '✔'}]}]], [['', [], []], {'t': 'AlignDefault'}, 1, 1, [{'t': 'Plain', 'c': [{'t': 'Str', 'c': '✖'}]}]], [['', [], []], {'t': 'AlignDefault'}, 1, 1, [{'t': 'Plain', 'c': [{'t': 'Str', 'c': '✖'}]}]]]], [['', [], []], [[['', [], []], {'t': 'AlignDefault'}, 1, 1, [{'t': 'Plain', 'c': [{'t': 'Span', 'c': [['', ['text-nowrap'], []], [{'t': 'RawInline', 'c': ['html', '<i class="fa fa-lock fa-fw">']}, {'t': 'RawInline', 'c': ['html', '</i>']}, {'t': 'Space'}, {'t': 'Strong', 'c': [{'t': 'Str', 'c': 'Locked'}]}]]}]}]], [['', [], []], {'t': 'AlignDefault'}, 1, 1, [{'t': 'Plain', 'c': [{'t': 'Str', 'c': '✔'}]}]], [['', [], []], {'t': 'AlignDefault'}, 1, 1, [{'t': 'Plain', 'c': [{'t': 'Str', 'c': '✔'}]}]], [['', [], []], {'t': 'AlignDefault'}, 1, 1, [{'t': 'Plain', 'c': [{'t': 'Str', 'c': '✖'}]}]], [['', [], []], {'t': 'AlignDefault'}, 1, 1, [{'t': 'Plain', 'c': [{'t': 'Str', 'c': '✔'}]}]], [['', [], []], {'t': 'AlignDefault'}, 1, 1, [{'t': 'Plain', 'c': [{'t': 'Str', 'c': '✖'}]}]]]], [['', [], []], [[['', [], []], {'t': 'AlignDefault'}, 1, 1, [{'t': 'Plain', 'c': [{'t': 'Span', 'c': [['', ['text-nowrap'], []], [{'t': 'RawInline', 'c': ['html', '<i class="fa fa-umbrella fa-fw">']}, {'t': 'RawInline', 'c': ['html', '</i>']}, {'t': 'Space'}, {'t': 'Strong', 'c': [{'t': 'Str', 'c': 'Protected'}]}]]}]}]], [['', [], []], {'t': 'AlignDefault'}, 1, 1, [{'t': 'Plain', 'c': [{'t': 'Str', 'c': '✔'}]}]], [['', [], []], {'t': 'AlignDefault'}, 1, 1, [{'t': 'Plain', 'c': [{'t': 'Str', 'c': '✔'}]}]], [['', [], []], {'t': 'AlignDefault'}, 1, 1, [{'t': 'Plain', 'c': [{'t': 'Str', 'c': '✖'}]}]], [['', [], []], {'t': 'AlignDefault'}, 1, 1, [{'t': 'Plain', 'c': [{'t': 'Str', 'c': '✖'}]}]], [['', [], []], {'t': 'AlignDefault'}, 1, 1, [{'t': 'Plain', 'c': [{'t': 'Str', 'c': '✖'}]}]]]], [['', [], []], [[['', [], []], {'t': 'AlignDefault'}, 1, 1, [{'t': 'Plain', 'c': [{'t': 'Span', 'c': [['', ['text-nowrap'], []], [{'t': 'RawInline', 'c': ['html', '<i class="fa fa-hand-stop-o fa-fw">']}, {'t': 'RawInline', 'c': ['html', '</i>']}, {'t': 'Space'}, {'t': 'Strong', 'c': [{'t': 'Str', 'c': 'Private'}]}]]}]}]], [['', [], []], {'t': 'AlignDefault'}, 1, 1, [{'t': 'Plain', 'c': [{'t': 'Str', 'c': '✔'}]}]], [['', [], []], {'t': 'AlignDefault'}, 1, 1, [{'t': 'Plain', 'c': [{'t': 'Str', 'c': '✖'}]}]], [['', [], []], {'t': 'AlignDefault'}, 1, 1, [{'t': 'Plain', 'c': [{'t': 'Str', 'c': '✖'}]}]], [['', [], []], {'t': 'AlignDefault'}, 1, 1, [{'t': 'Plain', 'c': [{'t': 'Str', 'c': '✖'}]}]], [['', [], []], {'t': 'AlignDefault'}, 1, 1, [{'t': 'Plain', 'c': [{'t': 'Str', 'c': '✖'}]}]]]]]] + ], + [['', [], []], []] # TableFoot + ] + """ + self.attr = self.parse_attr(pandocraw[0]) + self.caption = self.parse_caption(pandocraw[1]) + self.column_specs = self.parse_column_specs(pandocraw[2]) + self.head = self.parse_head(pandocraw[3]) + self.bodies = self.parse_bodies(pandocraw[4]) + self.foot = self.parse_foot(pandocraw[5]) + + + def parse_caption(self, raw_caption): + #print("called parse_caption: ", raw_caption) + if raw_caption[0] != None: + print("Warning: Found table with unsupported short caption:", raw_caption, file=sys.stderr) + return self.parse_blocks(raw_caption[1]) + + def parse_column_specs(self, raw_column_specs): + #print("called parse_column_specs: ", raw_column_specs) + res = [] + for cur in raw_column_specs: + colspec = {} + colspec["alignment"] = self.parse_alignment(cur[0]) + if cur[1]['t'] == "ColWidthDefault": + colspec["has_width"] = False + else: + colspec["has_width"] = True + colspec["width"] = self.parse_double_as_percentage(cur[1]['c']) + res.append(colspec) + return res + + def parse_alignment(self, raw_alignment): + return self.parse_enum({ + "AlignLeft": "left", + "AlignRight": "right", + "AlignCenter": "center", + "AlignDefault": "default" + }, raw_alignment) + + def parse_cells(self, raw_cells): + cells = [] + for cur in raw_cells: + cell = {} + cell["attr"] = self.parse_attr(cur[0]) + cell["alignment"] = self.parse_alignment(cur[1]) + cell["row_span"] = self.parse_int(cur[2]) # number of rows occupied by a cell; the height of a cell + cell["column_span"] = self.parse_int(cur[3]) # number of columns occupied by a cell; the width of a cell + cell["content"] = self.parse_blocks(cur[4]) + cells.append(cell) + return cells + + def parse_rows(self, raw_rows): + rows = [] + for cur in raw_rows: + row = {} + row["attr"] = self.parse_attr(cur[0]) + row["cells"] = self.parse_cells(cur[1]) + rows.append(row) + return rows + + def parse_head(self, raw_head): + #print("called parse_head: ", raw_head) + res = {} + res["attr"] = self.parse_attr(raw_head[0]) + res["rows"] = self.parse_rows(raw_head[1]) + return res + + def parse_bodies(self, raw_bodies): + #print("called parse_bodies: ", raw_bodies) + bodies = [] + for cur in raw_bodies: + body = {} + body["attr"] = self.parse_attr(cur[0]) + body["row_head_columns"] = self.parse_int(cur[1]) # number of row header columns in the intermediate body + body["intermediate_head"] = self.parse_rows(cur[2]) + body["intermediate_body"] = self.parse_rows(cur[3]) + bodies.append(body) + return bodies + + def parse_foot(self, raw_foot): + #print("called parse_foot: ", raw_foot) + res = {} + res["attr"] = self.parse_attr(raw_foot[0]) + res["rows"] = self.parse_rows(raw_foot[1]) + return res + + ############################## INLINE ######################################### class Inline(Element): @@ -424,8 +573,8 @@ block_parsing_register = { "Div" : BlockContainer, "OrderedList" : BlockOrderedList, "HorizontalRule": BlockHorizontalRule, - "Table" : {"TODO": True,}, # Attr Caption [ColSpec] TableHead [TableBody] TableFoot - "DefinitionList": {"TODO": True,}, # [([Inline], [[Block]])] + "Table" : BlockTable, + "DefinitionList": BlockDefinitionList, #"LineBlock" :{"type":"lineblock", "TODO": True, "c" : [] }, # [[Inline]] # TODO find file that triggers LineBlock #"Null" :{"type":"nothing" }, # TODO find file that triggers Null } diff --git a/theme/static/css/main.css b/theme/static/css/main.css index cd81b63e8bc197358e838b7ab7041cf0261cd1ea..87dd766396f27444845ceee40062f7816b84238b 100644 --- a/theme/static/css/main.css +++ b/theme/static/css/main.css @@ -410,6 +410,18 @@ sup { margin: 0 0 1em 0; } +.content dt { + font-weight: bold; +} +.content dt::after { + content: ': ' +} + +.content dd { + margin-left: 1em; + margin-bottom: 0.1em; +} + .content ul, .content ol { padding-left: 2em; @@ -468,8 +480,41 @@ sup { max-width: 100%; } +.content table { + width: 100%; + display: block; + border-collapse: collapse; + border-spacing: 0; +} +.content table tr { + width: 100%; + background-color: #fff; + border: 0.1em solid #ccc; +} + +.content table tr:nth-child(2n) { + background-color: #f8f8f8; +} + +.content table th { + font-weight: bold; + text-align: left; +} + +.content table th, +.content table td { + padding: 0.4em 0.9em; + border: 0.1em solid #ddd; +} +.content table .has_recommended_width { + width: var(--recommended_width); +} +.content table .align_left { text-align: start; } +.content table .align_right { text-align: end; } +.content table .align_center { text-align: center; } +.content table .align_default { text-align: start; } diff --git a/theme/templates/macros/content_renderer.html b/theme/templates/macros/content_renderer.html index 4b0a747de846e59025c0ea22388540788d95be2a..a94c77d095ff7d2a598501472b240736fdc106a9 100644 --- a/theme/templates/macros/content_renderer.html +++ b/theme/templates/macros/content_renderer.html @@ -16,7 +16,7 @@ {%- endif -%} {%- endmacro -%} -{%- macro render_attr(attr, lang, extra_classes = [], extra = {}) -%} +{%- macro render_attr(attr, lang, extra_classes = [], extra = {}, extra_style= {}) -%} {%- set id = attr['id']|d("") -%} {%- set classes = attr['classes']|d([]) + extra_classes -%} {%- set attr_extra = attr['extra']|d({}) -%} @@ -34,9 +34,12 @@ {%- endfor -%} - {%- if style.items()|length %} style=" + {%- if style.items()|length or extra_style.items()|length %} style=" {%- for key, value in style.items() -%} {{ key }}: {{ value }}; + {%- endfor -%} + {%- for key, value in extra_style.items() -%} + {{ key }}: {{ value }}; {%- endfor -%}" {%- endif -%} {%- endmacro -%} @@ -118,6 +121,10 @@ {{ render_block_rawblock(block, lang) }} {%- elif etype == "orderedlist" -%} {{ render_block_orderedlist(block, lang) }} + {%- elif etype == "table" -%} + {{ render_block_table(block, lang) }} + {%- elif etype == "definitionlist" -%} + {{ render_block_definitionlist(block, lang) }} {%- else -%} <br><strong>ERROR: Unhandled block type: '{{ etype|e }}'</strong><br> {%- endif -%} @@ -186,14 +193,103 @@ {%- set format = block['format'] -%} {%- set raw = block['raw'] -%} {%- if format == "html" -%} - <div class="rawblock {{ format|e }}"> + {#- <div class="rawblock {{ format|e }}"> -#} {{ raw }} - </div> + {#- </div> -#} {%- else -%} <div class="rawblock {{ format|e }}">{{ raw | e }}</div> {%- endif -%} {%- endmacro -%} +{%- macro render_block_table_cell(cell, lang, is_header, column_spec) -%} + {%- set attr = cell['attr'] -%} + {%- set cell_alignment = cell['alignment'] -%} + {%- set row_span = cell['row_span'] -%} + {%- set column_span = cell['column_span'] -%} + {%- set content = cell['content'] -%} + + {%- set column_alignment = column_spec.alignment -%} + {%- set ns = namespace(alignment=column_alignment,classes=[],extra_style={}) -%} + + {%- if cell_alignment != "default" -%} + {%- set ns.alignment=cell_alignment -%} + {%- endif -%} + + {%- set alignment_class = ["align", ns.alignment]|join('_') -%} + + {%- if column_spec.has_width-%} + {%- set ns.classes = [alignment_class, "has_recommended_width"] -%} + {%- set ns.extra_style = {"--recommended_width": [column_spec.width.percentage, "%"]|join('')} -%} + {#- set ns.extra_style = {"width": [column_spec.width.percentage, "%"]|join('')} -#} + {%- else -%} + {%- set ns.classes = [alignment_class] -%} + {%- endif -%} + + <{% if is_header %}th{% else %}td{% endif %} {{ render_attr(attr, lang, extra_classes=ns.classes, extra={"rowspan": row_span, "colspan": column_span}, extra_style=ns.extra_style) }}> + {{ render_blocks(content, lang) }} + </{% if is_header %}th{% else %}td{% endif %}> +{%- endmacro -%} + +{%- macro render_block_table_rows(rows, lang, num_headers,column_specs) -%} + {%- for row in rows -%} + {{ render_block_table_row(row,lang,num_headers=num_headers,column_specs=column_specs) }} + {%- endfor -%} +{%- endmacro -%} + +{%- macro render_block_table_row(row, lang, num_headers, column_specs) -%} + {%- set attr = row['attr'] -%} + {%- set cells = row['cells'] -%} + <tr {{ render_attr(attr, lang) }}> + {%- for cell in cells -%} + {%- if loop.index <= num_headers or num_headers == -1 -%} + {{ render_block_table_cell(cell,lang,is_header=true,column_spec=column_specs[loop.index0]) }} + {%- else -%} + {{ render_block_table_cell(cell,lang,is_header=false,column_spec=column_specs[loop.index0]) }} + {%- endif -%} + {%- endfor -%} + </tr> +{%- endmacro -%} + +{%- macro render_block_table(block, lang) -%} + {#- https://developer.mozilla.org/en-US/docs/Web/HTML/Element/table -#} + {%- set attr = block['attr'] -%} + {%- set caption = block['caption'] -%} + {%- set column_specs = block['column_specs'] -%} + {%- set head = block['head'] -%} + {%- set bodies = block['bodies'] -%} + {%- set foot = block['foot'] -%} + + <table> + {%- if caption|length -%} + <caption>{{ render_blocks(caption, lang) }}</caption> + {%- endif -%} + + {#- TODO: Maybe add colgroup? -#} + + {%- if head.rows|length -%} + <thead {{ render_attr(head.attr, lang) }}> + {{ render_block_table_rows(head.rows,lang,num_headers=-1,column_specs=column_specs) }} + </thead> + {%- endif -%} + + {%- for body in bodies -%} + <tbody {{ render_attr(body.attr, lang) }}> + {%- if body.intermediate_head|length -%} + {{ render_block_table_rows(body.intermediate_head,lang,num_headers=-1,column_specs=column_specs) }} + {%- endif -%} + {{ render_block_table_rows(body.intermediate_body,lang,num_headers=body.row_head_columns,column_specs=column_specs) }} + </tbody> + {%- endfor -%} + + + {%- if foot.rows|length -%} + <tfoot {{ render_attr(foot.attr, lang) }}> + {{ render_block_table_rows(foot.rows,lang,num_headers=bodies[0].row_head_columns,column_specs=column_specs) }} + </tfoot> + {%- endif -%} + </table> +{%- endmacro -%} + {%- macro render_block_orderedlist(block, lang) -%} {%- set items = block['items'] -%} {%- set count = block['count'] -%} @@ -208,6 +304,20 @@ </ol> {%- endmacro -%} +{%- macro render_block_definitionlist(block, lang) -%} + {%- set items = block['items'] -%} + <dl> + {%- for item in items -%} + {%- set term = item['term'] -%} + {%- set definitions = item['definitions'] -%} + <dt>{{ render_inlines(term, lang) }}</dt> + {%- for def in definitions -%} + <dd>{{ render_blocks(def, lang) }}</dd> + {%- endfor -%} + {%- endfor -%} + </dl> +{%- endmacro -%} + {#- ############################ INLINES ################################## -#} @@ -293,9 +403,9 @@ {%- set format = inline['format'] -%} {%- set raw = inline['raw'] -%} {%- if format == "html" -%} - <span class="rawinline {{ format|e }}"> + {#- <span class="rawinline {{ format|e }}"> -#} {{ raw }} - </span> + {#- </span> -#} {%- else -%} <span class="rawinline {{ format|e }}">{{ raw | e }}</span> {%- endif -%}