|
| 1 | +import logging |
1 | 2 | import re |
2 | 3 | from pathlib import Path |
3 | 4 | from typing import Dict, Tuple |
|
7 | 8 | from mkdocs.structure.pages import Page |
8 | 9 | from mkdocs.config.base import Config |
9 | 10 | from mkdocs.config.config_options import Type as PluginType |
| 11 | +from mkdocs.utils import copy_file |
10 | 12 |
|
11 | 13 | from .defaults import MARKMAP |
12 | 14 | from .utils import download |
13 | 15 |
|
14 | 16 |
|
15 | | -# todo: move this to template |
16 | | -SCRIPT_CONTENT = """ |
17 | | -const markdown{index} = '{content}'; |
18 | | -const svg{index} = document.querySelector('#{tag_id}'); |
19 | | -const root{index} = markmap_transformer.transform(markdown{index}).root; |
20 | | -var m{index} = markmap.Markmap.create(svg{index}, null, root{index}); |
21 | | -
|
22 | | -m{index}.rescale(1).then(function() {{ |
23 | | - svg{index}.parentElement.style.height = (svg{index}.getBBox().height + 10) + "px"; |
24 | | - setTimeout(function() {{ |
25 | | - // todo: this is a dirty workaround to center the mindmap within svg |
26 | | - while (svg{index}.firstChild) {{ |
27 | | - svg{index}.removeChild(svg{index}.lastChild); |
28 | | - }} |
29 | | - m{index} = markmap.Markmap.create(svg{index}, null, root{index}); |
30 | | - }}, 500); |
31 | | -}}); |
32 | | -""" |
33 | | - |
34 | | -# todo: move this to static file |
35 | | -STYLE_CONTENT = """ |
36 | | -div.markmap { |
37 | | - width: 100%; |
38 | | - min-height: 1em; |
39 | | - border: 1px solid grey; |
40 | | -} |
41 | | -.markmap > svg { |
42 | | - width: 100%; |
43 | | - height: 100%; |
44 | | -} |
45 | | -""" |
| 17 | +log = logging.getLogger('mkdocs.markmap') |
46 | 18 |
|
47 | 19 |
|
| 20 | +TEMPLATES_PATH: Path = Path(__file__).parent / 'templates' |
| 21 | +STYLE_PATH: Path = TEMPLATES_PATH / 'mkdocs-markmap.css' |
| 22 | +SCRIPT_PATH: Path = TEMPLATES_PATH / 'mkdocs-markmap.js' |
| 23 | + |
48 | 24 | class MarkmapPlugin(BasePlugin): |
49 | 25 | """ |
50 | 26 | Plugin for markmap support |
@@ -78,42 +54,52 @@ def markmap(self) -> Dict[str, str]: |
78 | 54 |
|
79 | 55 | return self._markmap |
80 | 56 |
|
| 57 | + def _load_scripts(self, soup: BeautifulSoup, script_base_url: str, js_path: Path) -> None: |
| 58 | + for script_url in self.markmap.values(): |
| 59 | + try: |
| 60 | + src: str = script_base_url + download(js_path, script_url) |
| 61 | + except Exception as e: |
| 62 | + log.error(f'unable to download script: {script_url}') |
| 63 | + src = script_url |
| 64 | + |
| 65 | + script: Tag = soup.new_tag('script', src=src, type='text/javascript') |
| 66 | + soup.head.append(script) |
| 67 | + |
| 68 | + @staticmethod |
| 69 | + def _add_statics(soup: BeautifulSoup): |
| 70 | + statics = ( |
| 71 | + (STYLE_PATH, 'style', 'text/css', 'head'), |
| 72 | + (SCRIPT_PATH, 'script', 'text/javascript', 'body'), |
| 73 | + ) |
| 74 | + |
| 75 | + for path, tag_name, text_type, attribute in statics: |
| 76 | + tag: Tag = soup.new_tag(tag_name, type=text_type) |
| 77 | + with open(path, 'r') as fp: |
| 78 | + tag.string = fp.read() |
| 79 | + getattr(soup, attribute).append(tag) |
| 80 | + |
81 | 81 | def on_post_page(self, output_content: str, config: Config, **kwargs) -> str: |
82 | 82 | soup: BeautifulSoup = BeautifulSoup(output_content, 'html.parser') |
83 | 83 | page: Page = kwargs.get('page') |
84 | 84 |
|
85 | | - script_base_url: str = re.sub(r'[^/]+?/', '../', re.sub(r'/+?', '/', page.url)) + 'js/' |
86 | | - js_path: Path = Path(config['site_dir']) / 'js' |
87 | 85 | markmaps: ResultSet = soup.find_all('code', class_='language-markmap') |
88 | | - if any(markmaps): |
89 | | - for script_url in self.markmap.values(): |
90 | | - src: str = script_base_url + download(js_path, script_url) |
91 | | - script: Tag = soup.new_tag('script', src=src, type='text/javascript') |
92 | | - soup.head.append(script) |
93 | | - |
94 | | - style: Tag = soup.new_tag('style', type='text/css') |
95 | | - style.string = STYLE_CONTENT |
96 | | - soup.head.append(style) |
| 86 | + if not any(markmaps): |
| 87 | + return output_content |
97 | 88 |
|
98 | | - script: Tag = soup.new_tag('script') |
99 | | - script.string = 'const markmap_transformer = new markmap.Transformer();' |
100 | | - soup.head.append(script) |
| 89 | + script_base_url: str = re.sub(r'[^/]+?/', '../', re.sub(r'/+?', '/', page.url)) + 'js/' |
| 90 | + js_path: Path = Path(config['site_dir']) / 'js' |
| 91 | + self._load_scripts(soup, script_base_url, js_path) |
| 92 | + self._add_statics(soup) |
101 | 93 |
|
102 | 94 | for index, markmap in enumerate(markmaps): |
103 | 95 | tag_id: str = f'markmap-{index}' |
104 | 96 | markmap.parent.name = 'div' |
105 | | - markmap.parent['class'] = markmap.parent.get('class', []) + ['markmap'] |
| 97 | + markmap.parent['class'] = markmap.parent.get('class', []) + ['mkdocs-markmap'] |
| 98 | + markmap.parent['data-markdown']=markmap.text.replace('\n', ' ') |
106 | 99 | markmap.replaceWith(soup.new_tag( |
107 | 100 | 'svg', |
108 | 101 | id=tag_id, |
109 | 102 | attrs={'class': 'markmap'}, |
110 | 103 | )) |
111 | | - script: Tag = soup.new_tag('script') |
112 | | - script.string = SCRIPT_CONTENT.format( |
113 | | - index=index, |
114 | | - tag_id=tag_id, |
115 | | - content=markmap.text.replace('\n', '\\n'), |
116 | | - ) |
117 | | - soup.body.append(script) |
118 | 104 |
|
119 | 105 | return str(soup) |
0 commit comments