markdown-environments 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,18 @@
1
+ r"""
2
+ The base Markdown syntax defined by this extension is::
3
+
4
+ \begin{...}
5
+ ...
6
+ \end{...}
7
+
8
+ Note that there must be a blank line before each `\\begin{}` and after each `\\end{}`.
9
+ """
10
+
11
+ from .captioned_figure import CaptionedFigureExtension
12
+ from .cited_blockquote import CitedBlockquoteExtension
13
+ from .div import DivExtension
14
+ from .dropdown import DropdownExtension
15
+ from .thms import ThmsExtension
16
+
17
+
18
+ __version__ = "1.0.0"
@@ -0,0 +1,165 @@
1
+ import re
2
+ import xml.etree.ElementTree as etree
3
+
4
+ from markdown.blockprocessors import BlockProcessor
5
+ from markdown.extensions import Extension
6
+
7
+ from . import util
8
+ from .mixins import HtmlClassMixin
9
+
10
+
11
+ class CaptionedFigureProcessor(BlockProcessor, HtmlClassMixin):
12
+
13
+ RE_FIGURE_START = r"^\\begin{captioned_figure}"
14
+ RE_FIGURE_END = r"^\\end{captioned_figure}"
15
+ RE_CAPTION_START = r"^\\begin{caption}"
16
+ RE_CAPTION_END = r"^\\end{caption}"
17
+
18
+ def __init__(self, *args, html_class: str, caption_html_class: str, **kwargs):
19
+ super().__init__(*args, **kwargs)
20
+ self.init_html_class(html_class)
21
+ self.caption_html_class = caption_html_class
22
+
23
+ def test(self, parent, block):
24
+ return re.match(self.RE_FIGURE_START, block, re.MULTILINE)
25
+
26
+ def run(self, parent, blocks):
27
+ org_blocks = list(blocks)
28
+
29
+ # remove figure starting delim
30
+ blocks[0] = re.sub(self.RE_FIGURE_START, "", blocks[0], flags=re.MULTILINE)
31
+
32
+ # find and remove caption starting delim
33
+ caption_start_i = None
34
+ for i, block in enumerate(blocks):
35
+ if re.match(self.RE_CAPTION_START, block, re.MULTILINE):
36
+ # remove ending delim and note which block captions started on
37
+ # (as caption content itself is an unknown number of blocks)
38
+ caption_start_i = i
39
+ blocks[i] = re.sub(self.RE_CAPTION_START, "", block, flags=re.MULTILINE)
40
+ break
41
+
42
+ # if no starting delim for caption, restore and do nothing
43
+ if caption_start_i is None:
44
+ # `blocks = org_blocks` doesn't work since lists are passed by pointer in Python (value of reference)
45
+ # so changing the address of `blocks` only updates the local copy of it (the pointer)
46
+ # we need to change the values pointed to by `blocks` (its list elements)
47
+ blocks.clear()
48
+ blocks.extend(org_blocks)
49
+ return False
50
+
51
+ # find and remove caption ending delim, and extract element
52
+ delim_found = False
53
+ for i, block in enumerate(blocks[caption_start_i:], start=caption_start_i):
54
+ if re.search(self.RE_CAPTION_END, block, flags=re.MULTILINE):
55
+ delim_found = True
56
+ # remove ending delim
57
+ blocks[i] = re.sub(self.RE_CAPTION_END, "", block, flags=re.MULTILINE)
58
+ # build HTML for caption
59
+ elem_caption = etree.Element("figcaption")
60
+ if self.caption_html_class != "":
61
+ elem_caption.set("class", self.caption_html_class)
62
+ self.parser.parseBlocks(elem_caption, blocks[caption_start_i:i + 1])
63
+ # remove used blocks
64
+ for _ in range(caption_start_i, i + 1):
65
+ blocks.pop(caption_start_i)
66
+ break
67
+ # if no ending delim for caption, restore and do nothing
68
+ if not delim_found:
69
+ blocks.clear()
70
+ blocks.extend(org_blocks)
71
+ return False
72
+
73
+ # find and remove figure ending delim, and extract element
74
+ delim_found = False
75
+ for i, block in enumerate(blocks):
76
+ if re.search(self.RE_FIGURE_END, block, flags=re.MULTILINE):
77
+ delim_found = True
78
+ # remove ending delim
79
+ blocks[i] = re.sub(self.RE_FIGURE_END, "", block, flags=re.MULTILINE)
80
+ # build HTML for figure
81
+ elem_figure = etree.SubElement(parent, "figure")
82
+ if self.html_class != "":
83
+ elem_figure.set("class", self.html_class)
84
+ self.parser.parseBlocks(elem_figure, blocks[:i + 1])
85
+ elem_figure.append(elem_caption) # make sure captions come after everything else
86
+ # remove used blocks
87
+ for _ in range(i + 1):
88
+ blocks.pop(0)
89
+ break
90
+ # if no ending delim for figure, restore and do nothing
91
+ if not delim_found:
92
+ blocks.clear()
93
+ blocks.extend(org_blocks)
94
+ return False
95
+ return True
96
+
97
+
98
+ class CaptionedFigureExtension(Extension):
99
+ r"""
100
+ Any chunk of content, such as an image, with a caption underneath.
101
+
102
+ Example:
103
+ .. code-block:: py
104
+
105
+ import markdown
106
+ from markdown_environments import CaptionedFigureExtension
107
+
108
+ input_text = ...
109
+ output_text = markdown.markdown(input_text, extensions=[
110
+ CaptionedFigureExtension(html_class="never", caption_html_class="gonna")
111
+ ])
112
+
113
+ Markdown usage:
114
+ .. code-block:: md
115
+
116
+ \begin{captioned_figure}
117
+ <figure content>
118
+
119
+ \begin{caption}
120
+ <caption>
121
+ \end{caption}
122
+
123
+ \end{captioned_figure}
124
+
125
+ becomes…
126
+
127
+ .. code-block:: html
128
+
129
+ <figure class="[html_class]">
130
+ [figure content]
131
+ <figcaption class="[caption_html_class]">
132
+ [caption]
133
+ </figcaption>
134
+ </figure>
135
+
136
+ Note that the `caption` block can be placed anywhere within the `captioned_figure` block, as long as, of course,
137
+ there are blank lines before and after the `caption` block.
138
+ """
139
+
140
+ def __init__(self, **kwargs):
141
+ """
142
+ Initialize captioned figure extension, with configuration options passed as the following keyword arguments:
143
+
144
+ - **html_class** (*str*) -- HTML `class` attribute to add to figures (default: `""`).
145
+ - **caption_html_class** (*str*) -- HTML `class` attribute to add to captions (default: `""`).
146
+ """
147
+
148
+ self.config = {
149
+ "html_class": [
150
+ "",
151
+ "HTML `class` attribute to add to captioned figure (default: `\"\"`)."
152
+ ],
153
+ "caption_html_class": [
154
+ "",
155
+ "HTML `class` attribute to add to captioned figure's caption (default: `\"\"`)."
156
+ ]
157
+ }
158
+ util.init_extension_with_configs(self, **kwargs)
159
+
160
+ def extendMarkdown(self, md):
161
+ md.parser.blockprocessors.register(CaptionedFigureProcessor(md.parser, **self.getConfigs()), "captioned_figure", 105)
162
+
163
+
164
+ def makeExtension(**kwargs):
165
+ return CaptionedFigureExtension(**kwargs)
@@ -0,0 +1,162 @@
1
+ import re
2
+ import xml.etree.ElementTree as etree
3
+
4
+ from markdown.blockprocessors import BlockProcessor
5
+ from markdown.extensions import Extension
6
+
7
+ from . import util
8
+ from .mixins import HtmlClassMixin
9
+
10
+
11
+ class CitedBlockquoteProcessor(BlockProcessor, HtmlClassMixin):
12
+
13
+ RE_BLOCKQUOTE_START = r"^\\begin{cited_blockquote}"
14
+ RE_BLOCKQUOTE_END = r"^\\end{cited_blockquote}"
15
+ RE_CITATION_START = r"^\\begin{citation}"
16
+ RE_CITATION_END = r"^\\end{citation}"
17
+
18
+ def __init__(self, *args, html_class: str, citation_html_class: str, **kwargs):
19
+ super().__init__(*args, **kwargs)
20
+ self.init_html_class(html_class)
21
+ self.citation_html_class = citation_html_class
22
+
23
+ def test(self, parent, block):
24
+ return re.match(self.RE_BLOCKQUOTE_START, block, re.MULTILINE)
25
+
26
+ def run(self, parent, blocks):
27
+ org_blocks = list(blocks)
28
+
29
+ # remove blockquote starting delim
30
+ blocks[0] = re.sub(self.RE_BLOCKQUOTE_START, "", blocks[0], flags=re.MULTILINE)
31
+
32
+ # find and remove citation starting delim
33
+ delim_found = False
34
+ citation_start_i = None
35
+ for i, block in enumerate(blocks):
36
+ if re.match(self.RE_CITATION_START, block, re.MULTILINE):
37
+ delim_found = True
38
+ # remove ending delim and note which block citation started on
39
+ # (as citation content itself is an unknown number of blocks)
40
+ citation_start_i = i
41
+ blocks[i] = re.sub(self.RE_CITATION_START, "", block, flags=re.MULTILINE)
42
+ break
43
+ # if no starting delim for citation, restore and do nothing
44
+ if not delim_found:
45
+ blocks.clear()
46
+ blocks.extend(org_blocks)
47
+ return False
48
+
49
+ # find and remove citation ending delim (starting search from the citation start delim), and extract element
50
+ delim_found = False
51
+ for i, block in enumerate(blocks[citation_start_i:], start=citation_start_i):
52
+ if re.search(self.RE_CITATION_END, block, flags=re.MULTILINE):
53
+ delim_found = True
54
+ # remove ending delim
55
+ blocks[i] = re.sub(self.RE_CITATION_END, "", block, flags=re.MULTILINE)
56
+ # build HTML for citation
57
+ elem_citation = etree.Element("cite")
58
+ if self.citation_html_class != "":
59
+ elem_citation.set("class", self.citation_html_class)
60
+ self.parser.parseBlocks(elem_citation, blocks[citation_start_i:i + 1])
61
+ # remove used blocks
62
+ for _ in range(citation_start_i, i + 1):
63
+ blocks.pop(citation_start_i)
64
+ break
65
+ # if no ending delim for citation, restore and do nothing
66
+ if not delim_found:
67
+ blocks.clear()
68
+ blocks.extend(org_blocks)
69
+ return False
70
+
71
+ # find and remove blockquote ending delim, and extract element
72
+ delim_found = False
73
+ for i, block in enumerate(blocks):
74
+ if re.search(self.RE_BLOCKQUOTE_END, block, flags=re.MULTILINE):
75
+ delim_found = True
76
+ # remove ending delim
77
+ blocks[i] = re.sub(self.RE_BLOCKQUOTE_END, "", block, flags=re.MULTILINE)
78
+ # build HTML for blockquote
79
+ elem_blockquote = etree.SubElement(parent, "blockquote")
80
+ if self.html_class != "":
81
+ elem_blockquote.set("class", self.html_class)
82
+ self.parser.parseBlocks(elem_blockquote, blocks[:i + 1])
83
+ parent.append(elem_citation) # make sure citation comes after everything else
84
+ # remove used blocks
85
+ for _ in range(i + 1):
86
+ blocks.pop(0)
87
+ break
88
+ # if no ending delim for blockquote, restore and do nothing
89
+ if not delim_found:
90
+ blocks.clear()
91
+ blocks.extend(org_blocks)
92
+ return False
93
+ return True
94
+
95
+
96
+ class CitedBlockquoteExtension(Extension):
97
+ r"""
98
+ A blockquote with a citation/quote attribution underneath.
99
+
100
+ Example:
101
+ .. code-block:: py
102
+
103
+ import markdown
104
+ from markdown_environments import CitedBlockquoteExtension
105
+
106
+ input_text = ...
107
+ output_text = markdown.markdown(input_text, extensions=[
108
+ CitedBlockquoteExtension(html_class="give", citation_html_class="you")
109
+ ])
110
+
111
+ Markdown usage:
112
+ .. code-block:: md
113
+
114
+ \begin{cited_blockquote}
115
+ <quote>
116
+
117
+ \begin{citation}
118
+ <citation>
119
+ \end{citation}
120
+
121
+ \end{cited_blockquote}
122
+
123
+ becomes…
124
+
125
+ .. code-block:: html
126
+
127
+ <blockquote class="[html_class]">
128
+ [quote]
129
+ </blockquote>
130
+ <cite class="[citation_html_class]">
131
+ [citation]
132
+ </cite>
133
+
134
+ Note that the `citation` block can be placed anywhere within the `cited_blockquote` block.
135
+ """
136
+
137
+ def __init__(self, **kwargs):
138
+ """
139
+ Initialize cited blockquote extension, with configuration options passed as the following keyword arguments:
140
+
141
+ - **html_class** (*str*) -- HTML `class` attribute to add to blockquotes. Defaults to `""`.
142
+ - **citation_html_class** (*str*) -- HTML `class` attribute to add to captions. Defaults to `""`.
143
+ """
144
+
145
+ self.config = {
146
+ "html_class": [
147
+ "",
148
+ "HTML `class` attribute to add to cited blockquote. Defaults to `\"\"`."
149
+ ],
150
+ "citation_html_class": [
151
+ "",
152
+ "HTML `class` attribute to add to cited blockquote's citation. Defaults to `\"\"`."
153
+ ]
154
+ }
155
+ util.init_extension_with_configs(self, **kwargs)
156
+
157
+ def extendMarkdown(self, md):
158
+ md.parser.blockprocessors.register(CitedBlockquoteProcessor(md.parser, **self.getConfigs()), "cited_blockquote", 105)
159
+
160
+
161
+ def makeExtension(**kwargs):
162
+ return CitedBlockquoteExtension(**kwargs)
@@ -0,0 +1,130 @@
1
+ import re
2
+ import xml.etree.ElementTree as etree
3
+
4
+ from markdown.blockprocessors import BlockProcessor
5
+ from markdown.extensions import Extension
6
+
7
+ from . import util
8
+ from .mixins import HtmlClassMixin, ThmMixin
9
+
10
+
11
+ class DivProcessor(BlockProcessor, HtmlClassMixin, ThmMixin):
12
+
13
+ def __init__(self, *args, types: dict, html_class: str, is_thm: bool, **kwargs):
14
+ super().__init__(*args, **kwargs)
15
+ self.init_thm(types, is_thm)
16
+ self.init_html_class(html_class)
17
+
18
+ def test(self, parent, block):
19
+ return ThmMixin.test(self, parent, block)
20
+
21
+ def run(self, parent, blocks):
22
+ org_block_start = blocks[0]
23
+ # generate default thm heading if applicable
24
+ prepend = self.gen_thm_heading_md(blocks[0])
25
+ # remove starting delim (after generating thm heading from it, if applicable)
26
+ blocks[0] = re.sub(self.re_start, "", blocks[0], flags=re.MULTILINE)
27
+
28
+ # find and remove ending delim, and extract element
29
+ delim_found = False
30
+ for i, block in enumerate(blocks):
31
+ if re.search(self.re_end, block, flags=re.MULTILINE):
32
+ delim_found = True
33
+ # remove ending delim
34
+ blocks[i] = re.sub(self.re_end, "", block, flags=re.MULTILINE)
35
+ # build HTML
36
+ elem = etree.SubElement(parent, "div")
37
+ if self.html_class != "" or self.type_opts.get("html_class") != "":
38
+ elem.set("class", f"{self.html_class} {self.type_opts.get('html_class')}")
39
+ self.parser.parseBlocks(elem, blocks[0:i + 1])
40
+ # remove used blocks
41
+ for _ in range(0, i + 1):
42
+ blocks.pop(0)
43
+ break
44
+ # if no ending delim, restore and do nothing
45
+ if not delim_found:
46
+ blocks[0] = org_block_start
47
+ return False
48
+
49
+ # add thm heading if applicable
50
+ self.prepend_thm_heading_md(elem, prepend)
51
+ return True
52
+
53
+
54
+ class DivExtension(Extension):
55
+ r"""
56
+ A general-purpose `<div>` that you can tack on HTML `class` es to.
57
+
58
+ Example:
59
+ .. code-block:: py
60
+
61
+ import markdown
62
+ from markdown_environments import DivExtension
63
+
64
+ input_text = ...
65
+ output_text = markdown.markdown(input_text, extensions=[
66
+ DivExtension(html_class="up", types={
67
+ type1: {},
68
+ type2: {"html_class": "never"}
69
+ })
70
+ ])
71
+
72
+ Markdown usage:
73
+ .. code-block:: md
74
+
75
+ \begin{<type>}
76
+ <content>
77
+ \end{<type>}
78
+
79
+ becomes…
80
+
81
+ .. code-block:: html
82
+
83
+ <div class="[html_class] [type's html_class]">
84
+ [content]
85
+ </div>
86
+ """
87
+
88
+ def __init__(self, **kwargs):
89
+ r"""
90
+ Initialize div extension, with configuration options passed as the following keyword arguments:
91
+
92
+ - **types** (*dict*) -- Types of div environments to define. Defaults to `{}`.
93
+ - **html_class** (*str*) -- HTML `class` attribute to add to divs. Defaults to `""`.
94
+
95
+ The key for each type defined in `types` is inserted directly into the regex patterns that search for
96
+ `\\begin{<type>}` and `\\end{<type>}`, so anything you specify will be interpreted as regex. In addition, each
97
+ type's value is itself a dictionary with the following possible options:
98
+
99
+ - **html_class** (*str*) -- HTML `class` attribute to add to divs of that type. Defaults to `""`.
100
+ """
101
+
102
+ self.config = {
103
+ "types": [
104
+ {},
105
+ "Types of div environments to define. Defaults to `{}`."
106
+ ],
107
+ "html_class": [
108
+ "",
109
+ "HTML `class` attribute to add to div. Defaults to `\"\"`."
110
+ ],
111
+ "is_thm": [
112
+ False,
113
+ (
114
+ "Whether to use theorem logic (e.g. heading); you shouldn't have to set this value."
115
+ "Defaults to `False`."
116
+ )
117
+ ]
118
+ }
119
+ util.init_extension_with_configs(self, **kwargs)
120
+
121
+ # set default options for individual types
122
+ for type, opts in self.getConfig("types").items():
123
+ opts.setdefault("html_class", "")
124
+
125
+ def extendMarkdown(self, md):
126
+ md.parser.blockprocessors.register(DivProcessor(md.parser, **self.getConfigs()), "div", 105)
127
+
128
+
129
+ def makeExtension(**kwargs):
130
+ return DivExtension(**kwargs)
@@ -0,0 +1,196 @@
1
+ import re
2
+ import xml.etree.ElementTree as etree
3
+
4
+ from markdown.blockprocessors import BlockProcessor
5
+ from markdown.extensions import Extension
6
+
7
+ from . import util
8
+ from .mixins import HtmlClassMixin, ThmMixin
9
+
10
+
11
+ class DropdownProcessor(BlockProcessor, HtmlClassMixin, ThmMixin):
12
+
13
+ RE_SUMMARY_START = r"^\\begin{summary}"
14
+ RE_SUMMARY_END = r"^\\end{summary}"
15
+
16
+ def __init__(self, *args, types: dict, html_class: str, summary_html_class: str, content_html_class: str,
17
+ is_thm: bool, **kwargs):
18
+ super().__init__(*args, **kwargs)
19
+ self.init_thm(types, is_thm)
20
+ self.init_html_class(html_class)
21
+ self.summary_html_class = summary_html_class
22
+ self.content_html_class = content_html_class
23
+
24
+ def test(self, parent, block):
25
+ return ThmMixin.test(self, parent, block)
26
+
27
+ def run(self, parent, blocks):
28
+ org_blocks = list(blocks)
29
+ # remove summary starting delim that must immediately follow dropdown's starting delim
30
+ # if no starting delim for summary and not a thm dropdown which should provide a default, restore and do nothing
31
+ if not self.is_thm and not re.match(self.RE_SUMMARY_START, blocks[1], re.MULTILINE):
32
+ blocks.clear() # `blocks = org_blocks` doesn't work; must mutate `blocks` instead of reassigning it
33
+ blocks.extend(org_blocks)
34
+ return False
35
+ blocks[1] = re.sub(self.RE_SUMMARY_START, "", blocks[1], flags=re.MULTILINE)
36
+
37
+ # remove dropdown starting delim
38
+ # first generate theorem heading from it to use as default summary if applicable
39
+ thm_heading_md = self.gen_thm_heading_md(blocks[0])
40
+ blocks[0] = re.sub(self.re_start, "", blocks[0], flags=re.MULTILINE)
41
+
42
+ # find and remove summary ending delim, and extract element
43
+ # `elem_summary` initialized outside loop since the loop isn't guaranteed here to find & initialize it
44
+ elem_summary = etree.Element("summary")
45
+ if self.summary_html_class != "":
46
+ elem_summary.set("class", self.summary_html_class)
47
+ has_valid_summary = self.is_thm
48
+ for i, block in enumerate(blocks):
49
+ # if we haven't found summary ending delim but have found the overall dropdown ending delim,
50
+ # then don't keep going; maybe the summary was omitted as it was optional for theorems
51
+ if re.search(self.re_end, block, flags=re.MULTILINE):
52
+ break
53
+ if re.search(self.RE_SUMMARY_END, block, flags=re.MULTILINE):
54
+ has_valid_summary = True
55
+ # remove ending delim
56
+ blocks[i] = re.sub(self.RE_SUMMARY_END, "", block, flags=re.MULTILINE)
57
+ # build HTML for summary
58
+ self.parser.parseBlocks(elem_summary, blocks[:i + 1])
59
+ # remove used blocks
60
+ for _ in range(i + 1):
61
+ blocks.pop(0)
62
+ break
63
+ # if no valid summary (e.g. no ending delim with no default), restore and do nothing
64
+ if not has_valid_summary:
65
+ blocks.clear()
66
+ blocks.extend(org_blocks)
67
+ return False
68
+ # add thm heading to summary if applicable
69
+ self.prepend_thm_heading_md(elem_summary, thm_heading_md)
70
+
71
+ # find and remove dropdown ending delim, and extract element
72
+ delim_found = False
73
+ for i, block in enumerate(blocks):
74
+ if re.search(self.re_end, block, flags=re.MULTILINE):
75
+ delim_found = True
76
+ # remove ending delim
77
+ blocks[i] = re.sub(self.re_end, "", block, flags=re.MULTILINE)
78
+ # build HTML for dropdown
79
+ elem_details = etree.SubElement(parent, "details")
80
+ if self.html_class != "" or self.type_opts.get("html_class") != "":
81
+ elem_details.set("class", f"{self.html_class} {self.type_opts.get('html_class')}")
82
+ elem_details.append(elem_summary)
83
+ elem_details_content = etree.SubElement(elem_details, "div")
84
+ if self.content_html_class != "":
85
+ elem_details_content.set("class", self.content_html_class)
86
+ self.parser.parseBlocks(elem_details_content, blocks[0:i + 1])
87
+ # remove used blocks
88
+ for _ in range(0, i + 1):
89
+ blocks.pop(0)
90
+ break
91
+ # if no ending delim for dropdown, restore and do nothing
92
+ if not delim_found:
93
+ blocks.clear()
94
+ blocks.extend(org_blocks)
95
+ return False
96
+ return True
97
+
98
+
99
+ class DropdownExtension(Extension):
100
+ r"""
101
+ A dropdown that can be toggled open or closed, with only a preview portion (`<summary>`) shown when closed.
102
+
103
+ Example:
104
+ .. code-block:: py
105
+
106
+ import markdown
107
+ from markdown_environments import DropdownExtension
108
+
109
+ input_text = ...
110
+ output_text = markdown.markdown(input_text, extensions=[
111
+ DropdownExtension(
112
+ html_class="gonna", summary_html_class="let", content_html_class="you",
113
+ types={
114
+ type1: {"html_class": "down"},
115
+ type2: {}
116
+ }
117
+ )
118
+ ])
119
+
120
+ Markdown usage:
121
+ .. code-block:: md
122
+
123
+ \begin{<type>}
124
+
125
+ \begin{summary}
126
+ <summary>
127
+ \end{summary}
128
+
129
+ <collapsible content>
130
+ \end{<type>}
131
+
132
+ becomes…
133
+
134
+ .. code-block:: html
135
+
136
+ <details class="[html_class] [type's html_class]">
137
+ <summary class="[summary_html_class]">
138
+ [summary]
139
+ </summary>
140
+
141
+ <div class="[content_html_class]">
142
+ [collapsible content]
143
+ </div>
144
+ </details>
145
+ """
146
+
147
+ def __init__(self, **kwargs):
148
+ r"""
149
+ Initialize dropdown extension, with configuration options passed as the following keyword arguments:
150
+
151
+ - **types** (*dict*) -- Types of dropdown environments to define. Defaults to `{}`.
152
+ - **html_class** (*str*) -- HTML `class` attribute to add to dropdowns. Defaults to `""`.
153
+ - **summary_html_class** (*str*) -- HTML `class` attribute to add to dropdown summaries. Defaults to `""`.
154
+ - **content_html_class** (*str*) -- HTML `class` attribute to add to dropdown contents. Defaults to `""`.
155
+
156
+ The key for each type defined in `types` is inserted directly into the regex patterns that search for
157
+ `\\begin{<type>}` and `\\end{<type>}`, so anything you specify will be interpreted as regex. In addition, each
158
+ type's value is itself a dictionary with the following possible options:
159
+
160
+ - **html_class** (*str*) -- HTML `class` attribute to add to dropdowns of that type. Defaults to `""`.
161
+ """
162
+
163
+ self.config = {
164
+ "types": [
165
+ {},
166
+ "Types of dropdown environments to define. Defaults to `{}`."
167
+ ],
168
+ "html_class": [
169
+ "",
170
+ "HTML `class` attribute to add to dropdown. Defaults to `\"\"`."
171
+ ],
172
+ "summary_html_class": [
173
+ "",
174
+ "HTML `class` attribute to add to dropdown summary. Defaults to `\"\"`."
175
+ ],
176
+ "content_html_class": [
177
+ "",
178
+ "HTML `class` attribute to add to dropdown content. Defaults to `\"\"`."
179
+ ],
180
+ "is_thm": [
181
+ False,
182
+ "Whether to use theorem logic (e.g. heading); used only by `ThmExtension`. Defaults to `False`."
183
+ ]
184
+ }
185
+ util.init_extension_with_configs(self, **kwargs)
186
+
187
+ # set default options for individual types
188
+ for type, opts in self.getConfig("types").items():
189
+ opts.setdefault("html_class", "")
190
+
191
+ def extendMarkdown(self, md):
192
+ md.parser.blockprocessors.register(DropdownProcessor(md.parser, **self.getConfigs()), "dropdown", 105)
193
+
194
+
195
+ def makeExtension(**kwargs):
196
+ return DropdownExtension(**kwargs)