1# vim: ts=8:sts=8:sw=8:noexpandtab
2
3# This file is part of python-markups module
4# License: 3-clause BSD, see LICENSE file
5# Copyright: (C) Dmitry Shachnev, 2012-2021
6
7from __future__ import annotations
8
9from typing import Any, Dict, Tuple, Optional
10
11whole_html_template = """<!doctype html>
12<html>
13<head>
14<meta http-equiv="content-type" content="text/html; charset=utf-8">
15{custom_headers}<title>{title}</title>
16{stylesheet}{javascript}</head>
17<body>
18{body}
19</body>
20</html>
21"""
22
23
24class AbstractMarkup:
25	"""Abstract class for markup languages.
26
27	:param filename: optional name of the file
28	"""
29
30	#: name of the markup visible to user
31	name: str
32	#: various attributes, like links to website and syntax documentation
33	attributes: Dict[int, Any]
34	#: indicates which file extensions are associated with the markup
35	file_extensions: Tuple[str, ...]
36	#: the default file extension
37	default_extension: str
38
39	def __init__(self, filename: Optional[str] = None):
40		self.filename = filename
41
42	@staticmethod
43	def available() -> bool:
44		"""
45		:returns: whether the markup is ready for use
46
47		          (for example, whether the required third-party
48		          modules are importable)
49		"""
50		return True
51
52	def convert(self, text: str) -> ConvertedMarkup:
53		"""
54		:returns: a ConvertedMarkup instance (or a subclass thereof)
55		          containing the markup converted to HTML
56		"""
57		raise NotImplementedError
58
59
60class ConvertedMarkup:
61	"""This class encapsulates the title, body, stylesheet and javascript
62	of a converted document.
63
64	Instances of this class are created by :meth:`.AbstractMarkup.convert`
65	method, usually it should not be instantiated directly.
66	"""
67
68	def __init__(self, body: str, title: str = '',
69	             stylesheet: str = '', javascript: str = ''):
70		self.title = title
71		self.stylesheet = stylesheet
72		self.javascript = javascript
73		self.body = body
74
75	def get_document_title(self) -> str:
76		"""
77		:returns: the document title
78		"""
79		return self.title
80
81	def get_document_body(self) -> str:
82		"""
83		:returns: the contents of the ``<body>`` HTML tag
84		"""
85		return self.body
86
87	def get_stylesheet(self) -> str:
88		"""
89		:returns: the contents of ``<style type="text/css">`` HTML tag
90		"""
91		return self.stylesheet
92
93	def get_javascript(self, webenv: bool = False) -> str:
94		"""
95		:returns: one or more HTML tags to be inserted into the document
96		          ``<head>``.
97		:param webenv: if true, the specific markups may optimize the
98		               document for being used in the World Wide Web (for
99		               example, a remote version of MathJax script can be
100		               inserted instead of the local one).
101		"""
102		return self.javascript
103
104	def get_whole_html(self, custom_headers: str = '', include_stylesheet: bool = True,
105	                   fallback_title: str = '', webenv: bool = False) -> str:
106		"""
107		:returns: the full contents of the HTML document (unless overridden
108		          this is a combination of the previous methods)
109		:param custom_headers: custom HTML to be inserted into the document
110		                       ``<head>``
111		:param include_stylesheet: if false, the stylesheet will not
112		                           be included in the document ``<head>``
113		:param fallback_title: when impossible to get the ``<title>`` from
114		                       the document, this string can be used as a
115		                       fallback
116		:param webenv: like in :meth:`~.ConvertedMarkup.get_javascript`
117		               above
118		"""
119		stylesheet = ('<style type="text/css">\n' + self.get_stylesheet()
120			+ '</style>\n' if include_stylesheet else '')
121
122		context = {
123			"body": self.get_document_body(),
124			"title": self.get_document_title() or fallback_title,
125			"javascript": self.get_javascript(webenv),
126			"stylesheet": stylesheet,
127			"custom_headers": custom_headers,
128		}
129		return whole_html_template.format(**context)
130