Browse Source

Merge remote-tracking branch 'origin/dev' into dev

Liang Ding 5 years ago
parent
commit
7eda52faba
100 changed files with 6937 additions and 7654 deletions
  1. 113 9
      CHANGELOG.md
  2. 11 14
      README.md
  3. 3932 3932
      __test__/markdown/commonmark-0.29.ts
  4. 16 16
      __test__/markdown/lute.test.ts
  5. 112 112
      __test__/methods/index.spec.ts
  6. 76 42
      __test__/util/Options.test.ts
  7. 22 22
      __test__/util/textareaPosition.spec.ts
  8. 11 4
      demo/index.html
  9. 10 1
      demo/index.js
  10. 2 2
      demo/markdown/zh_CN.md
  11. 12 14
      demo/render.html
  12. 4 4
      demo/render.js
  13. 3 1
      jest.config.js
  14. 227 1470
      package-lock.json
  15. 19 18
      package.json
  16. 3 0
      src/assets/icons/delete-column.svg
  17. 3 0
      src/assets/icons/delete-row.svg
  18. 3 0
      src/assets/icons/insert-column.svg
  19. 3 0
      src/assets/icons/insert-row.svg
  20. 3 0
      src/assets/icons/zhihu.svg
  21. 0 44
      src/assets/scss/_content.scss
  22. 27 21
      src/assets/scss/_ir.scss
  23. 1 0
      src/assets/scss/_panel.scss
  24. 13 7
      src/assets/scss/_reset.scss
  25. 124 0
      src/assets/scss/_sv.scss
  26. 4 0
      src/assets/scss/_toolbar.scss
  27. 17 17
      src/assets/scss/_wysiwyg.scss
  28. 2 1
      src/assets/scss/index.scss
  29. 2 1
      src/css/content-theme/dark.css
  30. 109 0
      src/css/content-theme/light.css
  31. 59 51
      src/index.ts
  32. 0 0
      src/js/lute/lute.min.js
  33. 0 1
      src/js/mermaid/mermaid.min.js
  34. 45 52
      src/ts/constants.ts
  35. 2 2
      src/ts/export/index.ts
  36. 49 67
      src/ts/hint/index.ts
  37. 12 3
      src/ts/i18n/index.ts
  38. 18 0
      src/ts/ir/expandMarker.ts
  39. 21 0
      src/ts/ir/highlightToolbarIR.ts
  40. 17 27
      src/ts/ir/index.ts
  41. 18 5
      src/ts/ir/input.ts
  42. 26 20
      src/ts/ir/process.ts
  43. 9 6
      src/ts/ir/processKeydown.ts
  44. 3 4
      src/ts/markdown/abcRender.ts
  45. 7 4
      src/ts/markdown/chartRender.ts
  46. 2 2
      src/ts/markdown/codeRender.ts
  47. 0 1
      src/ts/markdown/getMarkdown.ts
  48. 2 2
      src/ts/markdown/graphvizRender.ts
  49. 3 3
      src/ts/markdown/highlightRender.ts
  50. 2 2
      src/ts/markdown/mathRender.ts
  51. 6 1
      src/ts/markdown/mediaRender.ts
  52. 2 2
      src/ts/markdown/mermaidRender.ts
  53. 7 4
      src/ts/markdown/mindmapRender.ts
  54. 8 5
      src/ts/markdown/outlineRender.ts
  55. 13 43
      src/ts/markdown/previewRender.ts
  56. 2 1
      src/ts/markdown/setLute.ts
  57. 1 1
      src/ts/markdown/speechRender.ts
  58. 46 16
      src/ts/preview/index.ts
  59. 0 49
      src/ts/sv/formatRender.ts
  60. 0 36
      src/ts/sv/getCurrentLinePosition.ts
  61. 15 0
      src/ts/sv/highlightToolbarSV.ts
  62. 0 56
      src/ts/sv/html2md.ts
  63. 54 87
      src/ts/sv/index.ts
  64. 169 23
      src/ts/sv/inputEvent.ts
  65. 0 39
      src/ts/sv/insertText.ts
  66. 209 0
      src/ts/sv/process.ts
  67. 177 100
      src/ts/sv/processKeydown.ts
  68. 0 20
      src/ts/sv/setSelection.ts
  69. 4 5
      src/ts/toolbar/ContentTheme.ts
  70. 18 27
      src/ts/toolbar/EditMode.ts
  71. 17 23
      src/ts/toolbar/Emoji.ts
  72. 0 16
      src/ts/toolbar/Format.ts
  73. 14 17
      src/ts/toolbar/Headings.ts
  74. 5 5
      src/ts/toolbar/MenuItem.ts
  75. 1 1
      src/ts/toolbar/Preview.ts
  76. 1 1
      src/ts/toolbar/Record.ts
  77. 1 7
      src/ts/toolbar/Redo.ts
  78. 1 7
      src/ts/toolbar/Undo.ts
  79. 6 3
      src/ts/toolbar/Upload.ts
  80. 0 4
      src/ts/toolbar/index.ts
  81. 6 4
      src/ts/ui/initUI.ts
  82. 2 3
      src/ts/ui/setCodeTheme.ts
  83. 5 16
      src/ts/ui/setContentTheme.ts
  84. 0 165
      src/ts/undo/IRUndo.ts
  85. 0 176
      src/ts/undo/WysiwygUndo.ts
  86. 177 74
      src/ts/undo/index.ts
  87. 13 19
      src/ts/upload/index.ts
  88. 210 279
      src/ts/util/Options.ts
  89. 0 0
      src/ts/util/RecordMedia.ts
  90. 8 0
      src/ts/util/compatibility.ts
  91. 30 36
      src/ts/util/editorCommonEvent.ts
  92. 231 142
      src/ts/util/fixBrowserBehavior.ts
  93. 1 1
      src/ts/util/getSelectText.ts
  94. 40 37
      src/ts/util/hasClosest.ts
  95. 48 35
      src/ts/util/highlightToolbar.ts
  96. 18 0
      src/ts/util/merge.ts
  97. 14 8
      src/ts/util/processCode.ts
  98. 51 36
      src/ts/util/selection.ts
  99. 4 5
      src/ts/wysiwyg/afterRenderEvent.ts
  100. 123 15
      src/ts/wysiwyg/highlightToolbarWYSIWYG.ts

+ 113 - 9
CHANGELOG.md

@@ -1,6 +1,8 @@
 ## Vditor change log
 
 ### 升级
+* v3.2
+  * IPreviewOptions.anchor 从 `boolean` 类型修改为 `number` 类型
 * v3.1
   * `options.counter` 修改为 `counter?: { enable: boolean; max?: number; type: "markdown" | "text"; }`
   * `options.hideToolbar` 修改为 `toolbarConfig: { hide?: boolean, pin?: boolean }`
@@ -64,10 +66,111 @@
 * [open issues](https://github.com/Vanessa219/vditor/issues)
 * [346](https://github.com/Vanessa219/vditor/issues/346) 内容主题推荐(长期有效) `改进功能`
 
-### v3.2.9 / 2020-05-xx
-
-### v3.2.8 / 2020-05-20
-
+### v3.3.7 / 2020-07-xx
+
+* [570](https://github.com/Vanessa219/vditor/issues/570) no placeholder after(SV) `修复缺陷`
+* [569](https://github.com/Vanessa219/vditor/issues/569) 预览区域工具栏配置 `引入特性`
+* [565](https://github.com/Vanessa219/vditor/issues/565) SV 模式支持缩进代码块 `改进功能`
+* [566](https://github.com/Vanessa219/vditor/issues/566) 行级代码解析渲染 `修复缺陷`
+* options.mode 默认值修改为 `ir`
+* 工具栏配置:默认值:["desktop", "tablet", "mobile", "mp-wechat", "zhihu"] 
+
+### v3.3.6 / 2020-07-09
+
+* [555](https://github.com/Vanessa219/vditor/issues/555) SV 模式段落块拆分问题 `修复缺陷`
+* [556](https://github.com/Vanessa219/vditor/issues/556) options.preview.markdown.setext 默认值修改为 true `改进功能`
+* [554](https://github.com/Vanessa219/vditor/issues/554) SV 模式 Ctrl+u 代码块生成光标初始定位优化 `改进功能`
+* [558](https://github.com/Vanessa219/vditor/issues/558) [suggestion] shift+enter and end the lists `改进功能`
+* [547](https://github.com/Vanessa219/vditor/issues/547) 文件上传 format 第一个参数 files 为空数组 `修复缺陷`
+* [562](https://github.com/Vanessa219/vditor/issues/562) 为上传添加 fieldName 选项 `引入特性`
+* [553](https://github.com/Vanessa219/vditor/issues/553) 子任务列表回车问题 `改进功能`
+* [534](https://github.com/Vanessa219/vditor/issues/534) 支持导出到知乎 `引入特性`
+* [552](https://github.com/Vanessa219/vditor/issues/552) blockquote 换行需逐层跳出 `修复缺陷`
+* [551](https://github.com/Vanessa219/vditor/issues/551) Tab and ctrl+shift+J (checkbox) `修复缺陷`
+* [550](https://github.com/Vanessa219/vditor/issues/550) [Mermaid] State diagram rendering of multiple same node loops `修复缺陷`
+* [549](https://github.com/Vanessa219/vditor/issues/549) 粘贴 HTML 内容失败 `修复缺陷`
+* [548](https://github.com/Vanessa219/vditor/issues/548) 添加文件上传多选/单选配置 `引入特性`
+* [545](https://github.com/Vanessa219/vditor/issues/545) SV 模式列表项下输入代码块问题 `修复缺陷`
+* [544](https://github.com/Vanessa219/vditor/issues/544) SV 模式有序列表缩进问题 `修复缺陷`
+* [543](https://github.com/Vanessa219/vditor/issues/543) SV 模式删除选择文本问题 `修复缺陷`
+* [541](https://github.com/Vanessa219/vditor/issues/541) SV 模式列表和引用快捷键问题 `修复缺陷`
+* [546](https://github.com/Vanessa219/vditor/issues/546) SV 模式丢失 headingID `修复缺陷`
+* [539](https://github.com/Vanessa219/vditor/pull/539) 即时渲染模式 setValue 后,没有及时渲染 Code 区域 `修复缺陷`
+* [536](https://github.com/Vanessa219/vditor/issues/536) 文件上传检查后缀忽略大小写 `改进功能`
+* [537](https://github.com/Vanessa219/vditor/issues/537) 添加 destroy 方法 `引入特新`
+* [532](https://github.com/Vanessa219/vditor/issues/532) 中文输入过程中不应记录 UndoStack `修复缺陷`
+* [519](https://github.com/Vanessa219/vditor/issues/519) 扩展 markdown 主题 `改进功能`
+* [533](https://github.com/Vanessa219/vditor/issues/533) 中文字符串中间插入 \ 字符导致乱码 `修复缺陷`
+* [528](https://github.com/Vanessa219/vditor/issues/528) wysiwyg & ir 列表中有两个代码块时的错误 `修复缺陷`
+* [531](https://github.com/Vanessa219/vditor/issues/531) 移除 options.keymap `改进功能`
+* [529](https://github.com/Vanessa219/vditor/issues/529) 移除格式化按钮 `改进功能`
+* [526](https://github.com/Vanessa219/vditor/issues/526) 字符串中带有 \ 符号时,从 \ 后面,但不是行尾 Backspace 删除字符到达 \ 时处理异常 `修复缺陷`
+* [517](https://github.com/Vanessa219/vditor/issues/517) Duplicate CSS(render.html) `修复缺陷`
+* [522](https://github.com/Vanessa219/vditor/issues/522) 从 WPS Office 复制文本,粘贴异常 `修复缺陷`
+* [524](https://github.com/Vanessa219/vditor/issues/524) no background-color change(dark mode-abcjs) `修复缺陷`
+* [516](https://github.com/Vanessa219/vditor/issues/516) 高亮自动填写功能未生效问题 `修复缺陷`
+* [355](https://github.com/Vanessa219/vditor/issues/355) 新增 VditorSVDOM 渲染器 `引入特新`
+* [499](https://github.com/Vanessa219/vditor/issues/499) Chrome 浏览器,Up 方向键 光标位置移动异常 `修复缺陷`
+* [514](https://github.com/Vanessa219/vditor/issues/514) 只读没有禁用场景菜单 `修复缺陷`
+* [501](https://github.com/Vanessa219/vditor/issues/501) 反斜杠 \ 转义问题 `改进功能`
+* [508](https://github.com/Vanessa219/vditor/issues/508) 软键盘 bug `修复缺陷`
+* [504](https://github.com/Vanessa219/vditor/issues/504) 解决编辑器中 esc 推出 hint 和 options.esc 冲突 `修复缺陷`
+* [500](https://github.com/Vanessa219/vditor/issues/500) wysiwyg 模式下打字数学公式会闪 `改进功能`
+* [498](https://github.com/Vanessa219/vditor/issues/498) 修复 <summary> <details> 标签编辑问题 `修复缺陷`
+* [488](https://github.com/Vanessa219/vditor/pull/488) setValue 增加 clearStack 参数,添加 clearStack 方法 `引入特新`
+* [478](https://github.com/Vanessa219/vditor/issues/478) add tip to footnote `改进功能`
+* [492](https://github.com/Vanessa219/vditor/issues/492) 列表内含有代码块,修改列表中的文字时,代码块会闪 `修复缺陷`
+* [496](https://github.com/Vanessa219/vditor/issues/496) 行级 HTML 空格问题 `修复缺陷`
+* [495](https://github.com/Vanessa219/vditor/issues/495) 行级 HTML 退格问题 `修复缺陷`
+* [494](https://github.com/Vanessa219/vditor/issues/494) 三种模式支持 list-style 样式 `改进功能`
+* [489](https://github.com/Vanessa219/vditor/issues/489) 为 vditor.b3log.org/demo 添加源码 `改进功能`
+* [493](https://github.com/Vanessa219/vditor/issues/493) 修复 XSS 过滤 `修复缺陷`
+* [486](https://github.com/Vanessa219/vditor/pull/486) getCursorPosition 针对空行会得到错误数据 `修复缺陷`
+* [485](https://github.com/Vanessa219/vditor/issues/485) IR 模式下,<details>的编辑问题 `修复缺陷`
+* [484](https://github.com/Vanessa219/vditor/issues/484) 添加 TED 视频支持 `改进功能`
+* [480](https://github.com/Vanessa219/vditor/issues/480) Mindmap 和 Echarts 内容为空时会报错 `修复缺陷`
+* 文档修改
+  * `options.markdown.listMarker` 修改为 `options.markdown.listStyle`
+  * 为 `setValue` 方法添加 `clearStack` 参数;新增 `clearStack` 方法
+  * 移除 `options.keymap`
+  * 移除工具栏上的格式化功能
+  * `setContentTheme` 参数修改为 `contentTheme: string, path: string`
+  * `options.preview.markdow.theme` 修改为 `options.preview.theme`
+  * `setTheme` 参数修改为 `theme: "dark" | "classic", contentTheme?: string, codeTheme?: string, contentThemePath?: string`
+  * 移除 `setSelection`
+  * 添加 `destory` 方法
+  * 添加 `options.upload.multiple`
+  * 添加 `options.upload.fieldName`
+  * `options.preview.markdown.setext` 默认值修改为 `true`
+
+### v3.2.12 / 2020-06-07
+
+* [475](https://github.com/Vanessa219/vditor/issues/475) 从网页和其它 md 编辑器中复制的 iframe 和 视频标签 无法粘贴到 Vditor `修复缺陷`
+* [455](https://github.com/Vanessa219/vditor/issues/455) HTML 实体在切换编辑模式时的转义问题 `修复缺陷`
+* [466](https://github.com/Vanessa219/vditor/issues/466) Tab is not working inside Quote(safari) `修复缺陷`
+* [467](https://github.com/Vanessa219/vditor/issues/467) 为表格添加行列增删按钮 `引入特性`
+* [472](https://github.com/Vanessa219/vditor/issues/472) Add details preview in ir mode `引入特性`
+* [471](https://github.com/Vanessa219/vditor/issues/471) 移动端子工具栏箭头没对齐 `修复缺陷`
+* [473](https://github.com/Vanessa219/vditor/issues/473) different between press "Ctrl+b" and click "B" icon. `修复缺陷`
+* [463](https://github.com/Vanessa219/vditor/issues/463) 在行内代码中进行粘贴时光标位置不对及报错处理 `修复缺陷`
+* [462](https://github.com/Vanessa219/vditor/issues/462) ctrl+z ir 模式没有渲染代码块 `修复缺陷`
+* [456](https://github.com/Vanessa219/vditor/pull/456) 根据逗号/制表符生成表格 `引入特性`
+* [453](https://github.com/Vanessa219/vditor/issues/453) bold 位于子菜单中无作用 `修复缺陷`
+* [449](https://github.com/Vanessa219/vditor/issues/449) table(IR mode) `修复缺陷`
+* [443](https://github.com/Vanessa219/vditor/issues/443) 快捷键中文版 `文档相关`
+* [438](https://github.com/Vanessa219/vditor/issues/438) one empty line when ctrl+shift+h `改进功能`
+* [439](https://github.com/Vanessa219/vditor/issues/439) not working ctrl+shift+x at links `修复缺陷`
+* [444](https://github.com/Vanessa219/vditor/pull/444) delete tables when cells are empty `改进功能`
+* [447](https://github.com/Vanessa219/vditor/pull/447) run vditor.insertValue('abc') throw error `修复缺陷`
+* [435](https://github.com/Vanessa219/vditor/issues/435) task list 合并会添加 p 标签 `修复缺陷`
+* [432](https://github.com/Vanessa219/vditor/issues/432) 代码块中输入 &pars 解析问题 `修复缺陷`
+* [433](https://github.com/Vanessa219/vditor/issues/433) language position(code block, wysiwyg mode) `修复缺陷`
+* [431](https://github.com/Vanessa219/vditor/issues/431) 链接、图片相对路径支持 `引入特性`
+* [430](https://github.com/Vanessa219/vditor/issues/430) cursor location when press ctrl+b after checkbox `修复缺陷`
+* [429](https://github.com/Vanessa219/vditor/issues/429) localStorage被禁用时,报错 `修复缺陷`
+* [427](https://github.com/Vanessa219/vditor/issues/427) headings 大小调整 `改进功能`
+* [422](https://github.com/Vanessa219/vditor/issues/422) inserted one line before table block(windows/Firefox) `修复缺陷`
+* [425](https://github.com/Vanessa219/vditor/issues/425) 支持 flac 音频格式解析 `改进功能`
 * [409](https://github.com/Vanessa219/vditor/issues/409) codeblock(mac safari) `修复缺陷`
 * [407](https://github.com/Vanessa219/vditor/issues/407) cursor moving at tables(safari & firefox) `修复缺陷`
 * [421](https://github.com/Vanessa219/vditor/issues/421) 添加 bilibili 视频网站解析 `改进功能`
@@ -113,12 +216,13 @@
 * [369](https://github.com/Vanessa219/vditor/issues/369) when paste, code can not highlight `修复缺陷`
 * [370](https://github.com/Vanessa219/vditor/issues/370) Copy Paste multiline at markdown mode `改进功能`
 * 文档修改
-  * 为 `options.upload` 添加 `extraData`
+  * 为 `options.upload` 添加 `extraData` 配置
   * 添加静态方法 `mindmapRender`
-  * 为 `IMarkdownConfig` 添加 `sanitize`, `listMarker` 配置
-  * IPreviewOptions.anchor 从 boolean 修改为 number
-  * static-preview.html => preview.html,static.html => vditor.b3log.org/demo/render.html,添加 vditor.b3log.org
-  * 添加 options.cache.after(markdown:string)
+  * 为 `IMarkdownConfig` 添加 `sanitize`, `listMarker`, `linkBase` 配置
+  * IPreviewOptions.anchor 从 `boolean` 类型修改为 `number` 类型
+  * 示例代码地址修改:static-preview.html => preview.html,static.html => vditor.b3log.org/demo/render.html
+  * 添加 [Vditor](https://vditor.b3log.org) 官方首页
+  * 添加缓存回调 `options.cache.after(markdown:string)`
   
 
 ### v3.1.23 / 2020-05-05

File diff suppressed because it is too large
+ 11 - 14
README.md


+ 3932 - 3932
__test__/markdown/commonmark-0.29.ts

@@ -1,5194 +1,5194 @@
 export const spec = [
     {
-        "markdown": "\tfoo\tbaz\t\tbim\n",
-        "html": "<pre><code>foo\tbaz\t\tbim\n</code></pre>\n",
-        "example": 1,
-        "start_line": 352,
-        "end_line": 357,
-        "section": "Tabs"
+        markdown: "\tfoo\tbaz\t\tbim\n",
+        html: "<pre><code>foo\tbaz\t\tbim\n</code></pre>\n",
+        example: 1,
+        start_line: 352,
+        end_line: 357,
+        section: "Tabs",
     },
-    {
-        "markdown": "  \tfoo\tbaz\t\tbim\n",
-        "html": "<pre><code>foo\tbaz\t\tbim\n</code></pre>\n",
-        "example": 2,
-        "start_line": 359,
-        "end_line": 364,
-        "section": "Tabs"
+    {
+        markdown: "  \tfoo\tbaz\t\tbim\n",
+        html: "<pre><code>foo\tbaz\t\tbim\n</code></pre>\n",
+        example: 2,
+        start_line: 359,
+        end_line: 364,
+        section: "Tabs",
     },
-    {
-        "markdown": "    a\ta\n    ὐ\ta\n",
-        "html": "<pre><code>a\ta\nὐ\ta\n</code></pre>\n",
-        "example": 3,
-        "start_line": 366,
-        "end_line": 373,
-        "section": "Tabs"
+    {
+        markdown: "    a\ta\n    ὐ\ta\n",
+        html: "<pre><code>a\ta\nὐ\ta\n</code></pre>\n",
+        example: 3,
+        start_line: 366,
+        end_line: 373,
+        section: "Tabs",
     },
-    {
-        "markdown": "  - foo\n\n\tbar\n",
-        "html": "<ul>\n<li>\n<p>foo</p>\n<p>bar</p>\n</li>\n</ul>\n",
-        "example": 4,
-        "start_line": 379,
-        "end_line": 390,
-        "section": "Tabs"
+    {
+        markdown: "  - foo\n\n\tbar\n",
+        html: "<ul>\n<li>\n<p>foo</p>\n<p>bar</p>\n</li>\n</ul>\n",
+        example: 4,
+        start_line: 379,
+        end_line: 390,
+        section: "Tabs",
     },
-    {
-        "markdown": "- foo\n\n\t\tbar\n",
-        "html": "<ul>\n<li>\n<p>foo</p>\n<pre><code>  bar\n</code></pre>\n</li>\n</ul>\n",
-        "example": 5,
-        "start_line": 392,
-        "end_line": 404,
-        "section": "Tabs"
+    {
+        markdown: "- foo\n\n\t\tbar\n",
+        html: "<ul>\n<li>\n<p>foo</p>\n<pre><code>  bar\n</code></pre>\n</li>\n</ul>\n",
+        example: 5,
+        start_line: 392,
+        end_line: 404,
+        section: "Tabs",
     },
-    {
-        "markdown": ">\t\tfoo\n",
-        "html": "<blockquote>\n<pre><code>  foo\n</code></pre>\n</blockquote>\n",
-        "example": 6,
-        "start_line": 415,
-        "end_line": 422,
-        "section": "Tabs"
+    {
+        markdown: ">\t\tfoo\n",
+        html: "<blockquote>\n<pre><code>  foo\n</code></pre>\n</blockquote>\n",
+        example: 6,
+        start_line: 415,
+        end_line: 422,
+        section: "Tabs",
     },
-    {
-        "markdown": "-\t\tfoo\n",
-        "html": "<ul>\n<li>\n<pre><code>  foo\n</code></pre>\n</li>\n</ul>\n",
-        "example": 7,
-        "start_line": 424,
-        "end_line": 433,
-        "section": "Tabs"
+    {
+        markdown: "-\t\tfoo\n",
+        html: "<ul>\n<li>\n<pre><code>  foo\n</code></pre>\n</li>\n</ul>\n",
+        example: 7,
+        start_line: 424,
+        end_line: 433,
+        section: "Tabs",
     },
-    {
-        "markdown": "    foo\n\tbar\n",
-        "html": "<pre><code>foo\nbar\n</code></pre>\n",
-        "example": 8,
-        "start_line": 436,
-        "end_line": 443,
-        "section": "Tabs"
+    {
+        markdown: "    foo\n\tbar\n",
+        html: "<pre><code>foo\nbar\n</code></pre>\n",
+        example: 8,
+        start_line: 436,
+        end_line: 443,
+        section: "Tabs",
     },
-    {
-        "markdown": " - foo\n   - bar\n\t - baz\n",
-        "html": "<ul>\n<li>foo\n<ul>\n<li>bar\n<ul>\n<li>baz</li>\n</ul>\n</li>\n</ul>\n</li>\n</ul>\n",
-        "example": 9,
-        "start_line": 445,
-        "end_line": 461,
-        "section": "Tabs"
+    {
+        markdown: " - foo\n   - bar\n\t - baz\n",
+        html: "<ul>\n<li>foo\n<ul>\n<li>bar\n<ul>\n<li>baz</li>\n</ul>\n</li>\n</ul>\n</li>\n</ul>\n",
+        example: 9,
+        start_line: 445,
+        end_line: 461,
+        section: "Tabs",
     },
-    {
-        "markdown": "#\tFoo\n",
-        "html": "<h1>Foo</h1>\n",
-        "example": 10,
-        "start_line": 463,
-        "end_line": 467,
-        "section": "Tabs"
+    {
+        markdown: "#\tFoo\n",
+        html: "<h1>Foo</h1>\n",
+        example: 10,
+        start_line: 463,
+        end_line: 467,
+        section: "Tabs",
     },
-    {
-        "markdown": "*\t*\t*\t\n",
-        "html": "<hr />\n",
-        "example": 11,
-        "start_line": 469,
-        "end_line": 473,
-        "section": "Tabs"
+    {
+        markdown: "*\t*\t*\t\n",
+        html: "<hr />\n",
+        example: 11,
+        start_line: 469,
+        end_line: 473,
+        section: "Tabs",
     },
-    {
-        "markdown": "- `one\n- two`\n",
-        "html": "<ul>\n<li>`one</li>\n<li>two`</li>\n</ul>\n",
-        "example": 12,
-        "start_line": 496,
-        "end_line": 504,
-        "section": "Precedence"
+    {
+        markdown: "- `one\n- two`\n",
+        html: "<ul>\n<li>`one</li>\n<li>two`</li>\n</ul>\n",
+        example: 12,
+        start_line: 496,
+        end_line: 504,
+        section: "Precedence",
     },
-    {
-        "markdown": "***\n---\n___\n",
-        "html": "<hr />\n<hr />\n<hr />\n",
-        "example": 13,
-        "start_line": 535,
-        "end_line": 543,
-        "section": "Thematic breaks"
+    {
+        markdown: "***\n---\n___\n",
+        html: "<hr />\n<hr />\n<hr />\n",
+        example: 13,
+        start_line: 535,
+        end_line: 543,
+        section: "Thematic breaks",
     },
     {
-        "markdown": "+++\n",
-        "html": "<p>+++</p>\n",
-        "example": 14,
-        "start_line": 548,
-        "end_line": 552,
-        "section": "Thematic breaks"
+        markdown: "+++\n",
+        html: "<p>+++</p>\n",
+        example: 14,
+        start_line: 548,
+        end_line: 552,
+        section: "Thematic breaks",
     },
     {
-        "markdown": "===\n",
-        "html": "<p>===</p>\n",
-        "example": 15,
-        "start_line": 555,
-        "end_line": 559,
-        "section": "Thematic breaks"
+        markdown: "===\n",
+        html: "<p>===</p>\n",
+        example: 15,
+        start_line: 555,
+        end_line: 559,
+        section: "Thematic breaks",
     },
     {
-        "markdown": "--\n**\n__\n",
-        "html": "<p>--\n**\n__</p>\n",
-        "example": 16,
-        "start_line": 564,
-        "end_line": 572,
-        "section": "Thematic breaks"
+        markdown: "--\n**\n__\n",
+        html: "<p>--\n**\n__</p>\n",
+        example: 16,
+        start_line: 564,
+        end_line: 572,
+        section: "Thematic breaks",
     },
     {
-        "markdown": " ***\n  ***\n   ***\n",
-        "html": "<hr />\n<hr />\n<hr />\n",
-        "example": 17,
-        "start_line": 577,
-        "end_line": 585,
-        "section": "Thematic breaks"
+        markdown: " ***\n  ***\n   ***\n",
+        html: "<hr />\n<hr />\n<hr />\n",
+        example: 17,
+        start_line: 577,
+        end_line: 585,
+        section: "Thematic breaks",
     },
     {
-        "markdown": "    ***\n",
-        "html": "<pre><code>***\n</code></pre>\n",
-        "example": 18,
-        "start_line": 590,
-        "end_line": 595,
-        "section": "Thematic breaks"
+        markdown: "    ***\n",
+        html: "<pre><code>***\n</code></pre>\n",
+        example: 18,
+        start_line: 590,
+        end_line: 595,
+        section: "Thematic breaks",
     },
     {
-        "markdown": "Foo\n    ***\n",
-        "html": "<p>Foo\n***</p>\n",
-        "example": 19,
-        "start_line": 598,
-        "end_line": 604,
-        "section": "Thematic breaks"
+        markdown: "Foo\n    ***\n",
+        html: "<p>Foo\n***</p>\n",
+        example: 19,
+        start_line: 598,
+        end_line: 604,
+        section: "Thematic breaks",
     },
     {
-        "markdown": "_____________________________________\n",
-        "html": "<hr />\n",
-        "example": 20,
-        "start_line": 609,
-        "end_line": 613,
-        "section": "Thematic breaks"
+        markdown: "_____________________________________\n",
+        html: "<hr />\n",
+        example: 20,
+        start_line: 609,
+        end_line: 613,
+        section: "Thematic breaks",
     },
     {
-        "markdown": " - - -\n",
-        "html": "<hr />\n",
-        "example": 21,
-        "start_line": 618,
-        "end_line": 622,
-        "section": "Thematic breaks"
+        markdown: " - - -\n",
+        html: "<hr />\n",
+        example: 21,
+        start_line: 618,
+        end_line: 622,
+        section: "Thematic breaks",
     },
     {
-        "markdown": " **  * ** * ** * **\n",
-        "html": "<hr />\n",
-        "example": 22,
-        "start_line": 625,
-        "end_line": 629,
-        "section": "Thematic breaks"
+        markdown: " **  * ** * ** * **\n",
+        html: "<hr />\n",
+        example: 22,
+        start_line: 625,
+        end_line: 629,
+        section: "Thematic breaks",
     },
     {
-        "markdown": "-     -      -      -\n",
-        "html": "<hr />\n",
-        "example": 23,
-        "start_line": 632,
-        "end_line": 636,
-        "section": "Thematic breaks"
+        markdown: "-     -      -      -\n",
+        html: "<hr />\n",
+        example: 23,
+        start_line: 632,
+        end_line: 636,
+        section: "Thematic breaks",
     },
     {
-        "markdown": "- - - -    \n",
-        "html": "<hr />\n",
-        "example": 24,
-        "start_line": 641,
-        "end_line": 645,
-        "section": "Thematic breaks"
+        markdown: "- - - -    \n",
+        html: "<hr />\n",
+        example: 24,
+        start_line: 641,
+        end_line: 645,
+        section: "Thematic breaks",
     },
     {
-        "markdown": "_ _ _ _ a\n\na------\n\n---a---\n",
-        "html": "<p>_ _ _ _ a</p>\n<p>a------</p>\n<p>---a---</p>\n",
-        "example": 25,
-        "start_line": 650,
-        "end_line": 660,
-        "section": "Thematic breaks"
+        markdown: "_ _ _ _ a\n\na------\n\n---a---\n",
+        html: "<p>_ _ _ _ a</p>\n<p>a------</p>\n<p>---a---</p>\n",
+        example: 25,
+        start_line: 650,
+        end_line: 660,
+        section: "Thematic breaks",
     },
     {
-        "markdown": " *-*\n",
-        "html": "<p><em>-</em></p>\n",
-        "example": 26,
-        "start_line": 666,
-        "end_line": 670,
-        "section": "Thematic breaks"
+        markdown: " *-*\n",
+        html: "<p><em>-</em></p>\n",
+        example: 26,
+        start_line: 666,
+        end_line: 670,
+        section: "Thematic breaks",
     },
     {
-        "markdown": "- foo\n***\n- bar\n",
-        "html": "<ul>\n<li>foo</li>\n</ul>\n<hr />\n<ul>\n<li>bar</li>\n</ul>\n",
-        "example": 27,
-        "start_line": 675,
-        "end_line": 687,
-        "section": "Thematic breaks"
+        markdown: "- foo\n***\n- bar\n",
+        html: "<ul>\n<li>foo</li>\n</ul>\n<hr />\n<ul>\n<li>bar</li>\n</ul>\n",
+        example: 27,
+        start_line: 675,
+        end_line: 687,
+        section: "Thematic breaks",
     },
     {
-        "markdown": "Foo\n***\nbar\n",
-        "html": "<p>Foo</p>\n<hr />\n<p>bar</p>\n",
-        "example": 28,
-        "start_line": 692,
-        "end_line": 700,
-        "section": "Thematic breaks"
+        markdown: "Foo\n***\nbar\n",
+        html: "<p>Foo</p>\n<hr />\n<p>bar</p>\n",
+        example: 28,
+        start_line: 692,
+        end_line: 700,
+        section: "Thematic breaks",
     },
     {
-        "markdown": "Foo\n---\nbar\n",
-        "html": "<h2>Foo</h2>\n<p>bar</p>\n",
-        "example": 29,
-        "start_line": 709,
-        "end_line": 716,
-        "section": "Thematic breaks"
+        markdown: "Foo\n---\nbar\n",
+        html: "<h2>Foo</h2>\n<p>bar</p>\n",
+        example: 29,
+        start_line: 709,
+        end_line: 716,
+        section: "Thematic breaks",
     },
     {
-        "markdown": "* Foo\n* * *\n* Bar\n",
-        "html": "<ul>\n<li>Foo</li>\n</ul>\n<hr />\n<ul>\n<li>Bar</li>\n</ul>\n",
-        "example": 30,
-        "start_line": 722,
-        "end_line": 734,
-        "section": "Thematic breaks"
+        markdown: "* Foo\n* * *\n* Bar\n",
+        html: "<ul>\n<li>Foo</li>\n</ul>\n<hr />\n<ul>\n<li>Bar</li>\n</ul>\n",
+        example: 30,
+        start_line: 722,
+        end_line: 734,
+        section: "Thematic breaks",
     },
     {
-        "markdown": "- Foo\n- * * *\n",
-        "html": "<ul>\n<li>Foo</li>\n<li>\n<hr />\n</li>\n</ul>\n",
-        "example": 31,
-        "start_line": 739,
-        "end_line": 749,
-        "section": "Thematic breaks"
+        markdown: "- Foo\n- * * *\n",
+        html: "<ul>\n<li>Foo</li>\n<li>\n<hr />\n</li>\n</ul>\n",
+        example: 31,
+        start_line: 739,
+        end_line: 749,
+        section: "Thematic breaks",
     },
     {
-        "markdown": "# foo\n## foo\n### foo\n#### foo\n##### foo\n###### foo\n",
-        "html": "<h1>foo</h1>\n<h2>foo</h2>\n<h3>foo</h3>\n<h4>foo</h4>\n<h5>foo</h5>\n<h6>foo</h6>\n",
-        "example": 32,
-        "start_line": 768,
-        "end_line": 782,
-        "section": "ATX headings"
+        markdown: "# foo\n## foo\n### foo\n#### foo\n##### foo\n###### foo\n",
+        html: "<h1>foo</h1>\n<h2>foo</h2>\n<h3>foo</h3>\n<h4>foo</h4>\n<h5>foo</h5>\n<h6>foo</h6>\n",
+        example: 32,
+        start_line: 768,
+        end_line: 782,
+        section: "ATX headings",
     },
     {
-        "markdown": "####### foo\n",
-        "html": "<p>####### foo</p>\n",
-        "example": 33,
-        "start_line": 787,
-        "end_line": 791,
-        "section": "ATX headings"
+        markdown: "####### foo\n",
+        html: "<p>####### foo</p>\n",
+        example: 33,
+        start_line: 787,
+        end_line: 791,
+        section: "ATX headings",
     },
     {
-        "markdown": "#5 bolt\n\n#hashtag\n",
-        "html": "<p>#5 bolt</p>\n<p>#hashtag</p>\n",
-        "example": 34,
-        "start_line": 802,
-        "end_line": 809,
-        "section": "ATX headings"
+        markdown: "#5 bolt\n\n#hashtag\n",
+        html: "<p>#5 bolt</p>\n<p>#hashtag</p>\n",
+        example: 34,
+        start_line: 802,
+        end_line: 809,
+        section: "ATX headings",
     },
     {
-        "markdown": "\\## foo\n",
-        "html": "<p>## foo</p>\n",
-        "example": 35,
-        "start_line": 814,
-        "end_line": 818,
-        "section": "ATX headings"
+        markdown: "\\## foo\n",
+        html: "<p>## foo</p>\n",
+        example: 35,
+        start_line: 814,
+        end_line: 818,
+        section: "ATX headings",
     },
     {
-        "markdown": "# foo *bar* \\*baz\\*\n",
-        "html": "<h1>foo <em>bar</em> *baz*</h1>\n",
-        "example": 36,
-        "start_line": 823,
-        "end_line": 827,
-        "section": "ATX headings"
+        markdown: "# foo *bar* \\*baz\\*\n",
+        html: "<h1>foo <em>bar</em> *baz*</h1>\n",
+        example: 36,
+        start_line: 823,
+        end_line: 827,
+        section: "ATX headings",
     },
     {
-        "markdown": "#                  foo                     \n",
-        "html": "<h1>foo</h1>\n",
-        "example": 37,
-        "start_line": 832,
-        "end_line": 836,
-        "section": "ATX headings"
+        markdown: "#                  foo                     \n",
+        html: "<h1>foo</h1>\n",
+        example: 37,
+        start_line: 832,
+        end_line: 836,
+        section: "ATX headings",
     },
     {
-        "markdown": " ### foo\n  ## foo\n   # foo\n",
-        "html": "<h3>foo</h3>\n<h2>foo</h2>\n<h1>foo</h1>\n",
-        "example": 38,
-        "start_line": 841,
-        "end_line": 849,
-        "section": "ATX headings"
+        markdown: " ### foo\n  ## foo\n   # foo\n",
+        html: "<h3>foo</h3>\n<h2>foo</h2>\n<h1>foo</h1>\n",
+        example: 38,
+        start_line: 841,
+        end_line: 849,
+        section: "ATX headings",
     },
     {
-        "markdown": "    # foo\n",
-        "html": "<pre><code># foo\n</code></pre>\n",
-        "example": 39,
-        "start_line": 854,
-        "end_line": 859,
-        "section": "ATX headings"
+        markdown: "    # foo\n",
+        html: "<pre><code># foo\n</code></pre>\n",
+        example: 39,
+        start_line: 854,
+        end_line: 859,
+        section: "ATX headings",
     },
     {
-        "markdown": "foo\n    # bar\n",
-        "html": "<p>foo\n# bar</p>\n",
-        "example": 40,
-        "start_line": 862,
-        "end_line": 868,
-        "section": "ATX headings"
+        markdown: "foo\n    # bar\n",
+        html: "<p>foo\n# bar</p>\n",
+        example: 40,
+        start_line: 862,
+        end_line: 868,
+        section: "ATX headings",
     },
     {
-        "markdown": "## foo ##\n  ###   bar    ###\n",
-        "html": "<h2>foo</h2>\n<h3>bar</h3>\n",
-        "example": 41,
-        "start_line": 873,
-        "end_line": 879,
-        "section": "ATX headings"
+        markdown: "## foo ##\n  ###   bar    ###\n",
+        html: "<h2>foo</h2>\n<h3>bar</h3>\n",
+        example: 41,
+        start_line: 873,
+        end_line: 879,
+        section: "ATX headings",
     },
     {
-        "markdown": "# foo ##################################\n##### foo ##\n",
-        "html": "<h1>foo</h1>\n<h5>foo</h5>\n",
-        "example": 42,
-        "start_line": 884,
-        "end_line": 890,
-        "section": "ATX headings"
+        markdown: "# foo ##################################\n##### foo ##\n",
+        html: "<h1>foo</h1>\n<h5>foo</h5>\n",
+        example: 42,
+        start_line: 884,
+        end_line: 890,
+        section: "ATX headings",
     },
     {
-        "markdown": "### foo ###     \n",
-        "html": "<h3>foo</h3>\n",
-        "example": 43,
-        "start_line": 895,
-        "end_line": 899,
-        "section": "ATX headings"
+        markdown: "### foo ###     \n",
+        html: "<h3>foo</h3>\n",
+        example: 43,
+        start_line: 895,
+        end_line: 899,
+        section: "ATX headings",
     },
     {
-        "markdown": "### foo ### b\n",
-        "html": "<h3>foo ### b</h3>\n",
-        "example": 44,
-        "start_line": 906,
-        "end_line": 910,
-        "section": "ATX headings"
+        markdown: "### foo ### b\n",
+        html: "<h3>foo ### b</h3>\n",
+        example: 44,
+        start_line: 906,
+        end_line: 910,
+        section: "ATX headings",
     },
     {
-        "markdown": "# foo#\n",
-        "html": "<h1>foo#</h1>\n",
-        "example": 45,
-        "start_line": 915,
-        "end_line": 919,
-        "section": "ATX headings"
+        markdown: "# foo#\n",
+        html: "<h1>foo#</h1>\n",
+        example: 45,
+        start_line: 915,
+        end_line: 919,
+        section: "ATX headings",
     },
     {
-        "markdown": "### foo \\###\n## foo #\\##\n# foo \\#\n",
-        "html": "<h3>foo ###</h3>\n<h2>foo ###</h2>\n<h1>foo #</h1>\n",
-        "example": 46,
-        "start_line": 925,
-        "end_line": 933,
-        "section": "ATX headings"
+        markdown: "### foo \\###\n## foo #\\##\n# foo \\#\n",
+        html: "<h3>foo ###</h3>\n<h2>foo ###</h2>\n<h1>foo #</h1>\n",
+        example: 46,
+        start_line: 925,
+        end_line: 933,
+        section: "ATX headings",
     },
     {
-        "markdown": "****\n## foo\n****\n",
-        "html": "<hr />\n<h2>foo</h2>\n<hr />\n",
-        "example": 47,
-        "start_line": 939,
-        "end_line": 947,
-        "section": "ATX headings"
+        markdown: "****\n## foo\n****\n",
+        html: "<hr />\n<h2>foo</h2>\n<hr />\n",
+        example: 47,
+        start_line: 939,
+        end_line: 947,
+        section: "ATX headings",
     },
     {
-        "markdown": "Foo bar\n# baz\nBar foo\n",
-        "html": "<p>Foo bar</p>\n<h1>baz</h1>\n<p>Bar foo</p>\n",
-        "example": 48,
-        "start_line": 950,
-        "end_line": 958,
-        "section": "ATX headings"
+        markdown: "Foo bar\n# baz\nBar foo\n",
+        html: "<p>Foo bar</p>\n<h1>baz</h1>\n<p>Bar foo</p>\n",
+        example: 48,
+        start_line: 950,
+        end_line: 958,
+        section: "ATX headings",
     },
     {
-        "markdown": "## \n#\n### ###\n",
-        "html": "<h2></h2>\n<h1></h1>\n<h3></h3>\n",
-        "example": 49,
-        "start_line": 963,
-        "end_line": 971,
-        "section": "ATX headings"
+        markdown: "## \n#\n### ###\n",
+        html: "<h2></h2>\n<h1></h1>\n<h3></h3>\n",
+        example: 49,
+        start_line: 963,
+        end_line: 971,
+        section: "ATX headings",
     },
     {
-        "markdown": "Foo *bar*\n=========\n\nFoo *bar*\n---------\n",
-        "html": "<h1>Foo <em>bar</em></h1>\n<h2>Foo <em>bar</em></h2>\n",
-        "example": 50,
-        "start_line": 1006,
-        "end_line": 1015,
-        "section": "Setext headings"
+        markdown: "Foo *bar*\n=========\n\nFoo *bar*\n---------\n",
+        html: "<h1>Foo <em>bar</em></h1>\n<h2>Foo <em>bar</em></h2>\n",
+        example: 50,
+        start_line: 1006,
+        end_line: 1015,
+        section: "Setext headings",
     },
     {
-        "markdown": "Foo *bar\nbaz*\n====\n",
-        "html": "<h1>Foo <em>bar\nbaz</em></h1>\n",
-        "example": 51,
-        "start_line": 1020,
-        "end_line": 1027,
-        "section": "Setext headings"
+        markdown: "Foo *bar\nbaz*\n====\n",
+        html: "<h1>Foo <em>bar\nbaz</em></h1>\n",
+        example: 51,
+        start_line: 1020,
+        end_line: 1027,
+        section: "Setext headings",
     },
     {
-        "markdown": "  Foo *bar\nbaz*\t\n====\n",
-        "html": "<h1>Foo <em>bar\nbaz</em></h1>\n",
-        "example": 52,
-        "start_line": 1034,
-        "end_line": 1041,
-        "section": "Setext headings"
+        markdown: "  Foo *bar\nbaz*\t\n====\n",
+        html: "<h1>Foo <em>bar\nbaz</em></h1>\n",
+        example: 52,
+        start_line: 1034,
+        end_line: 1041,
+        section: "Setext headings",
     },
     {
-        "markdown": "Foo\n-------------------------\n\nFoo\n=\n",
-        "html": "<h2>Foo</h2>\n<h1>Foo</h1>\n",
-        "example": 53,
-        "start_line": 1046,
-        "end_line": 1055,
-        "section": "Setext headings"
+        markdown: "Foo\n-------------------------\n\nFoo\n=\n",
+        html: "<h2>Foo</h2>\n<h1>Foo</h1>\n",
+        example: 53,
+        start_line: 1046,
+        end_line: 1055,
+        section: "Setext headings",
     },
     {
-        "markdown": "   Foo\n---\n\n  Foo\n-----\n\n  Foo\n  ===\n",
-        "html": "<h2>Foo</h2>\n<h2>Foo</h2>\n<h1>Foo</h1>\n",
-        "example": 54,
-        "start_line": 1061,
-        "end_line": 1074,
-        "section": "Setext headings"
+        markdown: "   Foo\n---\n\n  Foo\n-----\n\n  Foo\n  ===\n",
+        html: "<h2>Foo</h2>\n<h2>Foo</h2>\n<h1>Foo</h1>\n",
+        example: 54,
+        start_line: 1061,
+        end_line: 1074,
+        section: "Setext headings",
     },
     {
-        "markdown": "    Foo\n    ---\n\n    Foo\n---\n",
-        "html": "<pre><code>Foo\n---\n\nFoo\n</code></pre>\n<hr />\n",
-        "example": 55,
-        "start_line": 1079,
-        "end_line": 1092,
-        "section": "Setext headings"
+        markdown: "    Foo\n    ---\n\n    Foo\n---\n",
+        html: "<pre><code>Foo\n---\n\nFoo\n</code></pre>\n<hr />\n",
+        example: 55,
+        start_line: 1079,
+        end_line: 1092,
+        section: "Setext headings",
     },
     {
-        "markdown": "Foo\n   ----      \n",
-        "html": "<h2>Foo</h2>\n",
-        "example": 56,
-        "start_line": 1098,
-        "end_line": 1103,
-        "section": "Setext headings"
+        markdown: "Foo\n   ----      \n",
+        html: "<h2>Foo</h2>\n",
+        example: 56,
+        start_line: 1098,
+        end_line: 1103,
+        section: "Setext headings",
     },
     {
-        "markdown": "Foo\n    ---\n",
-        "html": "<p>Foo\n---</p>\n",
-        "example": 57,
-        "start_line": 1108,
-        "end_line": 1114,
-        "section": "Setext headings"
+        markdown: "Foo\n    ---\n",
+        html: "<p>Foo\n---</p>\n",
+        example: 57,
+        start_line: 1108,
+        end_line: 1114,
+        section: "Setext headings",
     },
     {
-        "markdown": "Foo\n= =\n\nFoo\n--- -\n",
-        "html": "<p>Foo\n= =</p>\n<p>Foo</p>\n<hr />\n",
-        "example": 58,
-        "start_line": 1119,
-        "end_line": 1130,
-        "section": "Setext headings"
+        markdown: "Foo\n= =\n\nFoo\n--- -\n",
+        html: "<p>Foo\n= =</p>\n<p>Foo</p>\n<hr />\n",
+        example: 58,
+        start_line: 1119,
+        end_line: 1130,
+        section: "Setext headings",
     },
     {
-        "markdown": "Foo  \n-----\n",
-        "html": "<h2>Foo</h2>\n",
-        "example": 59,
-        "start_line": 1135,
-        "end_line": 1140,
-        "section": "Setext headings"
+        markdown: "Foo  \n-----\n",
+        html: "<h2>Foo</h2>\n",
+        example: 59,
+        start_line: 1135,
+        end_line: 1140,
+        section: "Setext headings",
     },
     {
-        "markdown": "Foo\\\n----\n",
-        "html": "<h2>Foo\\</h2>\n",
-        "example": 60,
-        "start_line": 1145,
-        "end_line": 1150,
-        "section": "Setext headings"
+        markdown: "Foo\\\n----\n",
+        html: "<h2>Foo\\</h2>\n",
+        example: 60,
+        start_line: 1145,
+        end_line: 1150,
+        section: "Setext headings",
     },
     {
-        "markdown": "`Foo\n----\n`\n\n<a title=\"a lot\n---\nof dashes\"/>\n",
-        "html": "<h2>`Foo</h2>\n<p>`</p>\n<h2>&lt;a title=&quot;a lot</h2>\n<p>of dashes&quot;/&gt;</p>\n",
-        "example": 61,
-        "start_line": 1156,
-        "end_line": 1169,
-        "section": "Setext headings"
+        markdown: "`Foo\n----\n`\n\n<a title=\"a lot\n---\nof dashes\"/>\n",
+        html: "<h2>`Foo</h2>\n<p>`</p>\n<h2>&lt;a title=&quot;a lot</h2>\n<p>of dashes&quot;/&gt;</p>\n",
+        example: 61,
+        start_line: 1156,
+        end_line: 1169,
+        section: "Setext headings",
     },
     {
-        "markdown": "> Foo\n---\n",
-        "html": "<blockquote>\n<p>Foo</p>\n</blockquote>\n<hr />\n",
-        "example": 62,
-        "start_line": 1175,
-        "end_line": 1183,
-        "section": "Setext headings"
+        markdown: "> Foo\n---\n",
+        html: "<blockquote>\n<p>Foo</p>\n</blockquote>\n<hr />\n",
+        example: 62,
+        start_line: 1175,
+        end_line: 1183,
+        section: "Setext headings",
     },
     {
-        "markdown": "> foo\nbar\n===\n",
-        "html": "<blockquote>\n<p>foo\nbar\n===</p>\n</blockquote>\n",
-        "example": 63,
-        "start_line": 1186,
-        "end_line": 1196,
-        "section": "Setext headings"
+        markdown: "> foo\nbar\n===\n",
+        html: "<blockquote>\n<p>foo\nbar\n===</p>\n</blockquote>\n",
+        example: 63,
+        start_line: 1186,
+        end_line: 1196,
+        section: "Setext headings",
     },
     {
-        "markdown": "- Foo\n---\n",
-        "html": "<ul>\n<li>Foo</li>\n</ul>\n<hr />\n",
-        "example": 64,
-        "start_line": 1199,
-        "end_line": 1207,
-        "section": "Setext headings"
+        markdown: "- Foo\n---\n",
+        html: "<ul>\n<li>Foo</li>\n</ul>\n<hr />\n",
+        example: 64,
+        start_line: 1199,
+        end_line: 1207,
+        section: "Setext headings",
     },
     {
-        "markdown": "Foo\nBar\n---\n",
-        "html": "<h2>Foo\nBar</h2>\n",
-        "example": 65,
-        "start_line": 1214,
-        "end_line": 1221,
-        "section": "Setext headings"
+        markdown: "Foo\nBar\n---\n",
+        html: "<h2>Foo\nBar</h2>\n",
+        example: 65,
+        start_line: 1214,
+        end_line: 1221,
+        section: "Setext headings",
     },
     {
-        "markdown": "---\nFoo\n---\nBar\n---\nBaz\n",
-        "html": "<hr />\n<h2>Foo</h2>\n<h2>Bar</h2>\n<p>Baz</p>\n",
-        "example": 66,
-        "start_line": 1227,
-        "end_line": 1239,
-        "section": "Setext headings"
+        markdown: "---\nFoo\n---\nBar\n---\nBaz\n",
+        html: "<hr />\n<h2>Foo</h2>\n<h2>Bar</h2>\n<p>Baz</p>\n",
+        example: 66,
+        start_line: 1227,
+        end_line: 1239,
+        section: "Setext headings",
     },
     {
-        "markdown": "\n====\n",
-        "html": "<p>====</p>\n",
-        "example": 67,
-        "start_line": 1244,
-        "end_line": 1249,
-        "section": "Setext headings"
+        markdown: "\n====\n",
+        html: "<p>====</p>\n",
+        example: 67,
+        start_line: 1244,
+        end_line: 1249,
+        section: "Setext headings",
     },
     {
-        "markdown": "---\n---\n",
-        "html": "<hr />\n<hr />\n",
-        "example": 68,
-        "start_line": 1256,
-        "end_line": 1262,
-        "section": "Setext headings"
+        markdown: "---\n---\n",
+        html: "<hr />\n<hr />\n",
+        example: 68,
+        start_line: 1256,
+        end_line: 1262,
+        section: "Setext headings",
     },
     {
-        "markdown": "- foo\n-----\n",
-        "html": "<ul>\n<li>foo</li>\n</ul>\n<hr />\n",
-        "example": 69,
-        "start_line": 1265,
-        "end_line": 1273,
-        "section": "Setext headings"
+        markdown: "- foo\n-----\n",
+        html: "<ul>\n<li>foo</li>\n</ul>\n<hr />\n",
+        example: 69,
+        start_line: 1265,
+        end_line: 1273,
+        section: "Setext headings",
     },
     {
-        "markdown": "    foo\n---\n",
-        "html": "<pre><code>foo\n</code></pre>\n<hr />\n",
-        "example": 70,
-        "start_line": 1276,
-        "end_line": 1283,
-        "section": "Setext headings"
+        markdown: "    foo\n---\n",
+        html: "<pre><code>foo\n</code></pre>\n<hr />\n",
+        example: 70,
+        start_line: 1276,
+        end_line: 1283,
+        section: "Setext headings",
     },
     {
-        "markdown": "> foo\n-----\n",
-        "html": "<blockquote>\n<p>foo</p>\n</blockquote>\n<hr />\n",
-        "example": 71,
-        "start_line": 1286,
-        "end_line": 1294,
-        "section": "Setext headings"
+        markdown: "> foo\n-----\n",
+        html: "<blockquote>\n<p>foo</p>\n</blockquote>\n<hr />\n",
+        example: 71,
+        start_line: 1286,
+        end_line: 1294,
+        section: "Setext headings",
     },
     {
-        "markdown": "\\> foo\n------\n",
-        "html": "<h2>&gt; foo</h2>\n",
-        "example": 72,
-        "start_line": 1300,
-        "end_line": 1305,
-        "section": "Setext headings"
+        markdown: "\\> foo\n------\n",
+        html: "<h2>&gt; foo</h2>\n",
+        example: 72,
+        start_line: 1300,
+        end_line: 1305,
+        section: "Setext headings",
     },
     {
-        "markdown": "Foo\n\nbar\n---\nbaz\n",
-        "html": "<p>Foo</p>\n<h2>bar</h2>\n<p>baz</p>\n",
-        "example": 73,
-        "start_line": 1331,
-        "end_line": 1341,
-        "section": "Setext headings"
+        markdown: "Foo\n\nbar\n---\nbaz\n",
+        html: "<p>Foo</p>\n<h2>bar</h2>\n<p>baz</p>\n",
+        example: 73,
+        start_line: 1331,
+        end_line: 1341,
+        section: "Setext headings",
     },
     {
-        "markdown": "Foo\nbar\n\n---\n\nbaz\n",
-        "html": "<p>Foo\nbar</p>\n<hr />\n<p>baz</p>\n",
-        "example": 74,
-        "start_line": 1347,
-        "end_line": 1359,
-        "section": "Setext headings"
+        markdown: "Foo\nbar\n\n---\n\nbaz\n",
+        html: "<p>Foo\nbar</p>\n<hr />\n<p>baz</p>\n",
+        example: 74,
+        start_line: 1347,
+        end_line: 1359,
+        section: "Setext headings",
     },
     {
-        "markdown": "Foo\nbar\n* * *\nbaz\n",
-        "html": "<p>Foo\nbar</p>\n<hr />\n<p>baz</p>\n",
-        "example": 75,
-        "start_line": 1365,
-        "end_line": 1375,
-        "section": "Setext headings"
+        markdown: "Foo\nbar\n* * *\nbaz\n",
+        html: "<p>Foo\nbar</p>\n<hr />\n<p>baz</p>\n",
+        example: 75,
+        start_line: 1365,
+        end_line: 1375,
+        section: "Setext headings",
     },
     {
-        "markdown": "Foo\nbar\n\\---\nbaz\n",
-        "html": "<p>Foo\nbar\n---\nbaz</p>\n",
-        "example": 76,
-        "start_line": 1380,
-        "end_line": 1390,
-        "section": "Setext headings"
+        markdown: "Foo\nbar\n\\---\nbaz\n",
+        html: "<p>Foo\nbar\n---\nbaz</p>\n",
+        example: 76,
+        start_line: 1380,
+        end_line: 1390,
+        section: "Setext headings",
     },
     {
-        "markdown": "    a simple\n      indented code block\n",
-        "html": "<pre><code>a simple\n  indented code block\n</code></pre>\n",
-        "example": 77,
-        "start_line": 1408,
-        "end_line": 1415,
-        "section": "Indented code blocks"
+        markdown: "    a simple\n      indented code block\n",
+        html: "<pre><code>a simple\n  indented code block\n</code></pre>\n",
+        example: 77,
+        start_line: 1408,
+        end_line: 1415,
+        section: "Indented code blocks",
     },
     {
-        "markdown": "  - foo\n\n    bar\n",
-        "html": "<ul>\n<li>\n<p>foo</p>\n<p>bar</p>\n</li>\n</ul>\n",
-        "example": 78,
-        "start_line": 1422,
-        "end_line": 1433,
-        "section": "Indented code blocks"
+        markdown: "  - foo\n\n    bar\n",
+        html: "<ul>\n<li>\n<p>foo</p>\n<p>bar</p>\n</li>\n</ul>\n",
+        example: 78,
+        start_line: 1422,
+        end_line: 1433,
+        section: "Indented code blocks",
     },
     {
-        "markdown": "1.  foo\n\n    - bar\n",
-        "html": "<ol>\n<li>\n<p>foo</p>\n<ul>\n<li>bar</li>\n</ul>\n</li>\n</ol>\n",
-        "example": 79,
-        "start_line": 1436,
-        "end_line": 1449,
-        "section": "Indented code blocks"
+        markdown: "1.  foo\n\n    - bar\n",
+        html: "<ol>\n<li>\n<p>foo</p>\n<ul>\n<li>bar</li>\n</ul>\n</li>\n</ol>\n",
+        example: 79,
+        start_line: 1436,
+        end_line: 1449,
+        section: "Indented code blocks",
     },
     {
-        "markdown": "    <a/>\n    *hi*\n\n    - one\n",
-        "html": "<pre><code>&lt;a/&gt;\n*hi*\n\n- one\n</code></pre>\n",
-        "example": 80,
-        "start_line": 1456,
-        "end_line": 1467,
-        "section": "Indented code blocks"
+        markdown: "    <a/>\n    *hi*\n\n    - one\n",
+        html: "<pre><code>&lt;a/&gt;\n*hi*\n\n- one\n</code></pre>\n",
+        example: 80,
+        start_line: 1456,
+        end_line: 1467,
+        section: "Indented code blocks",
     },
     {
-        "markdown": "    chunk1\n\n    chunk2\n  \n \n \n    chunk3\n",
-        "html": "<pre><code>chunk1\n\nchunk2\n\n\n\nchunk3\n</code></pre>\n",
-        "example": 81,
-        "start_line": 1472,
-        "end_line": 1489,
-        "section": "Indented code blocks"
+        markdown: "    chunk1\n\n    chunk2\n  \n \n \n    chunk3\n",
+        html: "<pre><code>chunk1\n\nchunk2\n\n\n\nchunk3\n</code></pre>\n",
+        example: 81,
+        start_line: 1472,
+        end_line: 1489,
+        section: "Indented code blocks",
     },
     {
-        "markdown": "    chunk1\n      \n      chunk2\n",
-        "html": "<pre><code>chunk1\n  \n  chunk2\n</code></pre>\n",
-        "example": 82,
-        "start_line": 1495,
-        "end_line": 1504,
-        "section": "Indented code blocks"
+        markdown: "    chunk1\n      \n      chunk2\n",
+        html: "<pre><code>chunk1\n  \n  chunk2\n</code></pre>\n",
+        example: 82,
+        start_line: 1495,
+        end_line: 1504,
+        section: "Indented code blocks",
     },
     {
-        "markdown": "Foo\n    bar\n\n",
-        "html": "<p>Foo\nbar</p>\n",
-        "example": 83,
-        "start_line": 1510,
-        "end_line": 1517,
-        "section": "Indented code blocks"
+        markdown: "Foo\n    bar\n\n",
+        html: "<p>Foo\nbar</p>\n",
+        example: 83,
+        start_line: 1510,
+        end_line: 1517,
+        section: "Indented code blocks",
     },
     {
-        "markdown": "    foo\nbar\n",
-        "html": "<pre><code>foo\n</code></pre>\n<p>bar</p>\n",
-        "example": 84,
-        "start_line": 1524,
-        "end_line": 1531,
-        "section": "Indented code blocks"
+        markdown: "    foo\nbar\n",
+        html: "<pre><code>foo\n</code></pre>\n<p>bar</p>\n",
+        example: 84,
+        start_line: 1524,
+        end_line: 1531,
+        section: "Indented code blocks",
     },
     {
-        "markdown": "# Heading\n    foo\nHeading\n------\n    foo\n----\n",
-        "html": "<h1>Heading</h1>\n<pre><code>foo\n</code></pre>\n<h2>Heading</h2>\n<pre><code>foo\n</code></pre>\n<hr />\n",
-        "example": 85,
-        "start_line": 1537,
-        "end_line": 1552,
-        "section": "Indented code blocks"
+        markdown: "# Heading\n    foo\nHeading\n------\n    foo\n----\n",
+        html: "<h1>Heading</h1>\n<pre><code>foo\n</code></pre>\n<h2>Heading</h2>\n<pre><code>foo\n</code></pre>\n<hr />\n",
+        example: 85,
+        start_line: 1537,
+        end_line: 1552,
+        section: "Indented code blocks",
     },
     {
-        "markdown": "        foo\n    bar\n",
-        "html": "<pre><code>    foo\nbar\n</code></pre>\n",
-        "example": 86,
-        "start_line": 1557,
-        "end_line": 1564,
-        "section": "Indented code blocks"
+        markdown: "        foo\n    bar\n",
+        html: "<pre><code>    foo\nbar\n</code></pre>\n",
+        example: 86,
+        start_line: 1557,
+        end_line: 1564,
+        section: "Indented code blocks",
     },
     {
-        "markdown": "\n    \n    foo\n    \n\n",
-        "html": "<pre><code>foo\n</code></pre>\n",
-        "example": 87,
-        "start_line": 1570,
-        "end_line": 1579,
-        "section": "Indented code blocks"
+        markdown: "\n    \n    foo\n    \n\n",
+        html: "<pre><code>foo\n</code></pre>\n",
+        example: 87,
+        start_line: 1570,
+        end_line: 1579,
+        section: "Indented code blocks",
     },
     {
-        "markdown": "    foo  \n",
-        "html": "<pre><code>foo  \n</code></pre>\n",
-        "example": 88,
-        "start_line": 1584,
-        "end_line": 1589,
-        "section": "Indented code blocks"
+        markdown: "    foo  \n",
+        html: "<pre><code>foo  \n</code></pre>\n",
+        example: 88,
+        start_line: 1584,
+        end_line: 1589,
+        section: "Indented code blocks",
     },
     {
-        "markdown": "```\n<\n >\n```\n",
-        "html": "<pre><code>&lt;\n &gt;\n</code></pre>\n",
-        "example": 89,
-        "start_line": 1639,
-        "end_line": 1648,
-        "section": "Fenced code blocks"
+        markdown: "```\n<\n >\n```\n",
+        html: "<pre><code>&lt;\n &gt;\n</code></pre>\n",
+        example: 89,
+        start_line: 1639,
+        end_line: 1648,
+        section: "Fenced code blocks",
     },
     {
-        "markdown": "~~~\n<\n >\n~~~\n",
-        "html": "<pre><code>&lt;\n &gt;\n</code></pre>\n",
-        "example": 90,
-        "start_line": 1653,
-        "end_line": 1662,
-        "section": "Fenced code blocks"
+        markdown: "~~~\n<\n >\n~~~\n",
+        html: "<pre><code>&lt;\n &gt;\n</code></pre>\n",
+        example: 90,
+        start_line: 1653,
+        end_line: 1662,
+        section: "Fenced code blocks",
     },
     {
-        "markdown": "``\nfoo\n``\n",
-        "html": "<p><code>foo</code></p>\n",
-        "example": 91,
-        "start_line": 1666,
-        "end_line": 1672,
-        "section": "Fenced code blocks"
+        markdown: "``\nfoo\n``\n",
+        html: "<p><code>foo</code></p>\n",
+        example: 91,
+        start_line: 1666,
+        end_line: 1672,
+        section: "Fenced code blocks",
     },
     {
-        "markdown": "```\naaa\n~~~\n```\n",
-        "html": "<pre><code>aaa\n~~~\n</code></pre>\n",
-        "example": 92,
-        "start_line": 1677,
-        "end_line": 1686,
-        "section": "Fenced code blocks"
+        markdown: "```\naaa\n~~~\n```\n",
+        html: "<pre><code>aaa\n~~~\n</code></pre>\n",
+        example: 92,
+        start_line: 1677,
+        end_line: 1686,
+        section: "Fenced code blocks",
     },
     {
-        "markdown": "~~~\naaa\n```\n~~~\n",
-        "html": "<pre><code>aaa\n```\n</code></pre>\n",
-        "example": 93,
-        "start_line": 1689,
-        "end_line": 1698,
-        "section": "Fenced code blocks"
+        markdown: "~~~\naaa\n```\n~~~\n",
+        html: "<pre><code>aaa\n```\n</code></pre>\n",
+        example: 93,
+        start_line: 1689,
+        end_line: 1698,
+        section: "Fenced code blocks",
     },
     {
-        "markdown": "````\naaa\n```\n``````\n",
-        "html": "<pre><code>aaa\n```\n</code></pre>\n",
-        "example": 94,
-        "start_line": 1703,
-        "end_line": 1712,
-        "section": "Fenced code blocks"
+        markdown: "````\naaa\n```\n``````\n",
+        html: "<pre><code>aaa\n```\n</code></pre>\n",
+        example: 94,
+        start_line: 1703,
+        end_line: 1712,
+        section: "Fenced code blocks",
     },
     {
-        "markdown": "~~~~\naaa\n~~~\n~~~~\n",
-        "html": "<pre><code>aaa\n~~~\n</code></pre>\n",
-        "example": 95,
-        "start_line": 1715,
-        "end_line": 1724,
-        "section": "Fenced code blocks"
+        markdown: "~~~~\naaa\n~~~\n~~~~\n",
+        html: "<pre><code>aaa\n~~~\n</code></pre>\n",
+        example: 95,
+        start_line: 1715,
+        end_line: 1724,
+        section: "Fenced code blocks",
     },
     {
-        "markdown": "```\n",
-        "html": "<pre><code></code></pre>\n",
-        "example": 96,
-        "start_line": 1730,
-        "end_line": 1734,
-        "section": "Fenced code blocks"
+        markdown: "```\n",
+        html: "<pre><code></code></pre>\n",
+        example: 96,
+        start_line: 1730,
+        end_line: 1734,
+        section: "Fenced code blocks",
     },
     {
-        "markdown": "`````\n\n```\naaa\n",
-        "html": "<pre><code>\n```\naaa\n</code></pre>\n",
-        "example": 97,
-        "start_line": 1737,
-        "end_line": 1747,
-        "section": "Fenced code blocks"
+        markdown: "`````\n\n```\naaa\n",
+        html: "<pre><code>\n```\naaa\n</code></pre>\n",
+        example: 97,
+        start_line: 1737,
+        end_line: 1747,
+        section: "Fenced code blocks",
     },
     {
-        "markdown": "> ```\n> aaa\n\nbbb\n",
-        "html": "<blockquote>\n<pre><code>aaa\n</code></pre>\n</blockquote>\n<p>bbb</p>\n",
-        "example": 98,
-        "start_line": 1750,
-        "end_line": 1761,
-        "section": "Fenced code blocks"
+        markdown: "> ```\n> aaa\n\nbbb\n",
+        html: "<blockquote>\n<pre><code>aaa\n</code></pre>\n</blockquote>\n<p>bbb</p>\n",
+        example: 98,
+        start_line: 1750,
+        end_line: 1761,
+        section: "Fenced code blocks",
     },
     {
-        "markdown": "```\n\n  \n```\n",
-        "html": "<pre><code>\n  \n</code></pre>\n",
-        "example": 99,
-        "start_line": 1766,
-        "end_line": 1775,
-        "section": "Fenced code blocks"
+        markdown: "```\n\n  \n```\n",
+        html: "<pre><code>\n  \n</code></pre>\n",
+        example: 99,
+        start_line: 1766,
+        end_line: 1775,
+        section: "Fenced code blocks",
     },
     {
-        "markdown": "```\n```\n",
-        "html": "<pre><code></code></pre>\n",
-        "example": 100,
-        "start_line": 1780,
-        "end_line": 1785,
-        "section": "Fenced code blocks"
+        markdown: "```\n```\n",
+        html: "<pre><code></code></pre>\n",
+        example: 100,
+        start_line: 1780,
+        end_line: 1785,
+        section: "Fenced code blocks",
     },
     {
-        "markdown": " ```\n aaa\naaa\n```\n",
-        "html": "<pre><code>aaa\naaa\n</code></pre>\n",
-        "example": 101,
-        "start_line": 1792,
-        "end_line": 1801,
-        "section": "Fenced code blocks"
+        markdown: " ```\n aaa\naaa\n```\n",
+        html: "<pre><code>aaa\naaa\n</code></pre>\n",
+        example: 101,
+        start_line: 1792,
+        end_line: 1801,
+        section: "Fenced code blocks",
     },
     {
-        "markdown": "  ```\naaa\n  aaa\naaa\n  ```\n",
-        "html": "<pre><code>aaa\naaa\naaa\n</code></pre>\n",
-        "example": 102,
-        "start_line": 1804,
-        "end_line": 1815,
-        "section": "Fenced code blocks"
+        markdown: "  ```\naaa\n  aaa\naaa\n  ```\n",
+        html: "<pre><code>aaa\naaa\naaa\n</code></pre>\n",
+        example: 102,
+        start_line: 1804,
+        end_line: 1815,
+        section: "Fenced code blocks",
     },
     {
-        "markdown": "   ```\n   aaa\n    aaa\n  aaa\n   ```\n",
-        "html": "<pre><code>aaa\n aaa\naaa\n</code></pre>\n",
-        "example": 103,
-        "start_line": 1818,
-        "end_line": 1829,
-        "section": "Fenced code blocks"
+        markdown: "   ```\n   aaa\n    aaa\n  aaa\n   ```\n",
+        html: "<pre><code>aaa\n aaa\naaa\n</code></pre>\n",
+        example: 103,
+        start_line: 1818,
+        end_line: 1829,
+        section: "Fenced code blocks",
     },
     {
-        "markdown": "    ```\n    aaa\n    ```\n",
-        "html": "<pre><code>```\naaa\n```\n</code></pre>\n",
-        "example": 104,
-        "start_line": 1834,
-        "end_line": 1843,
-        "section": "Fenced code blocks"
+        markdown: "    ```\n    aaa\n    ```\n",
+        html: "<pre><code>```\naaa\n```\n</code></pre>\n",
+        example: 104,
+        start_line: 1834,
+        end_line: 1843,
+        section: "Fenced code blocks",
     },
     {
-        "markdown": "```\naaa\n  ```\n",
-        "html": "<pre><code>aaa\n</code></pre>\n",
-        "example": 105,
-        "start_line": 1849,
-        "end_line": 1856,
-        "section": "Fenced code blocks"
+        markdown: "```\naaa\n  ```\n",
+        html: "<pre><code>aaa\n</code></pre>\n",
+        example: 105,
+        start_line: 1849,
+        end_line: 1856,
+        section: "Fenced code blocks",
     },
     {
-        "markdown": "   ```\naaa\n  ```\n",
-        "html": "<pre><code>aaa\n</code></pre>\n",
-        "example": 106,
-        "start_line": 1859,
-        "end_line": 1866,
-        "section": "Fenced code blocks"
+        markdown: "   ```\naaa\n  ```\n",
+        html: "<pre><code>aaa\n</code></pre>\n",
+        example: 106,
+        start_line: 1859,
+        end_line: 1866,
+        section: "Fenced code blocks",
     },
     {
-        "markdown": "```\naaa\n    ```\n",
-        "html": "<pre><code>aaa\n    ```\n</code></pre>\n",
-        "example": 107,
-        "start_line": 1871,
-        "end_line": 1879,
-        "section": "Fenced code blocks"
+        markdown: "```\naaa\n    ```\n",
+        html: "<pre><code>aaa\n    ```\n</code></pre>\n",
+        example: 107,
+        start_line: 1871,
+        end_line: 1879,
+        section: "Fenced code blocks",
     },
     {
-        "markdown": "``` ```\naaa\n",
-        "html": "<p><code> </code>\naaa</p>\n",
-        "example": 108,
-        "start_line": 1885,
-        "end_line": 1891,
-        "section": "Fenced code blocks"
+        markdown: "``` ```\naaa\n",
+        html: "<p><code> </code>\naaa</p>\n",
+        example: 108,
+        start_line: 1885,
+        end_line: 1891,
+        section: "Fenced code blocks",
     },
     {
-        "markdown": "~~~~~~\naaa\n~~~ ~~\n",
-        "html": "<pre><code>aaa\n~~~ ~~\n</code></pre>\n",
-        "example": 109,
-        "start_line": 1894,
-        "end_line": 1902,
-        "section": "Fenced code blocks"
+        markdown: "~~~~~~\naaa\n~~~ ~~\n",
+        html: "<pre><code>aaa\n~~~ ~~\n</code></pre>\n",
+        example: 109,
+        start_line: 1894,
+        end_line: 1902,
+        section: "Fenced code blocks",
     },
     {
-        "markdown": "foo\n```\nbar\n```\nbaz\n",
-        "html": "<p>foo</p>\n<pre><code>bar\n</code></pre>\n<p>baz</p>\n",
-        "example": 110,
-        "start_line": 1908,
-        "end_line": 1919,
-        "section": "Fenced code blocks"
+        markdown: "foo\n```\nbar\n```\nbaz\n",
+        html: "<p>foo</p>\n<pre><code>bar\n</code></pre>\n<p>baz</p>\n",
+        example: 110,
+        start_line: 1908,
+        end_line: 1919,
+        section: "Fenced code blocks",
     },
     {
-        "markdown": "foo\n---\n~~~\nbar\n~~~\n# baz\n",
-        "html": "<h2>foo</h2>\n<pre><code>bar\n</code></pre>\n<h1>baz</h1>\n",
-        "example": 111,
-        "start_line": 1925,
-        "end_line": 1937,
-        "section": "Fenced code blocks"
+        markdown: "foo\n---\n~~~\nbar\n~~~\n# baz\n",
+        html: "<h2>foo</h2>\n<pre><code>bar\n</code></pre>\n<h1>baz</h1>\n",
+        example: 111,
+        start_line: 1925,
+        end_line: 1937,
+        section: "Fenced code blocks",
     },
     {
-        "markdown": "```ruby\ndef foo(x)\n  return 3\nend\n```\n",
-        "html": "<pre><code class=\"language-ruby\">def foo(x)\n  return 3\nend\n</code></pre>\n",
-        "example": 112,
-        "start_line": 1947,
-        "end_line": 1958,
-        "section": "Fenced code blocks"
+        markdown: "```ruby\ndef foo(x)\n  return 3\nend\n```\n",
+        html: "<pre><code class=\"language-ruby\">def foo(x)\n  return 3\nend\n</code></pre>\n",
+        example: 112,
+        start_line: 1947,
+        end_line: 1958,
+        section: "Fenced code blocks",
     },
     {
-        "markdown": "~~~~    ruby startline=3 $%@#$\ndef foo(x)\n  return 3\nend\n~~~~~~~\n",
-        "html": "<pre><code class=\"language-ruby\">def foo(x)\n  return 3\nend\n</code></pre>\n",
-        "example": 113,
-        "start_line": 1961,
-        "end_line": 1972,
-        "section": "Fenced code blocks"
+        markdown: "~~~~    ruby startline=3 $%@#$\ndef foo(x)\n  return 3\nend\n~~~~~~~\n",
+        html: "<pre><code class=\"language-ruby\">def foo(x)\n  return 3\nend\n</code></pre>\n",
+        example: 113,
+        start_line: 1961,
+        end_line: 1972,
+        section: "Fenced code blocks",
     },
     {
-        "markdown": "````;\n````\n",
-        "html": "<pre><code class=\"language-;\"></code></pre>\n",
-        "example": 114,
-        "start_line": 1975,
-        "end_line": 1980,
-        "section": "Fenced code blocks"
+        markdown: "````;\n````\n",
+        html: "<pre><code class=\"language-;\"></code></pre>\n",
+        example: 114,
+        start_line: 1975,
+        end_line: 1980,
+        section: "Fenced code blocks",
     },
     {
-        "markdown": "``` aa ```\nfoo\n",
-        "html": "<p><code>aa</code>\nfoo</p>\n",
-        "example": 115,
-        "start_line": 1985,
-        "end_line": 1991,
-        "section": "Fenced code blocks"
+        markdown: "``` aa ```\nfoo\n",
+        html: "<p><code>aa</code>\nfoo</p>\n",
+        example: 115,
+        start_line: 1985,
+        end_line: 1991,
+        section: "Fenced code blocks",
     },
     {
-        "markdown": "~~~ aa ``` ~~~\nfoo\n~~~\n",
-        "html": "<pre><code class=\"language-aa\">foo\n</code></pre>\n",
-        "example": 116,
-        "start_line": 1996,
-        "end_line": 2003,
-        "section": "Fenced code blocks"
+        markdown: "~~~ aa ``` ~~~\nfoo\n~~~\n",
+        html: "<pre><code class=\"language-aa\">foo\n</code></pre>\n",
+        example: 116,
+        start_line: 1996,
+        end_line: 2003,
+        section: "Fenced code blocks",
     },
     {
-        "markdown": "```\n``` aaa\n```\n",
-        "html": "<pre><code>``` aaa\n</code></pre>\n",
-        "example": 117,
-        "start_line": 2008,
-        "end_line": 2015,
-        "section": "Fenced code blocks"
+        markdown: "```\n``` aaa\n```\n",
+        html: "<pre><code>``` aaa\n</code></pre>\n",
+        example: 117,
+        start_line: 2008,
+        end_line: 2015,
+        section: "Fenced code blocks",
     },
     {
-        "markdown": "<table><tr><td>\n<pre>\n**Hello**,\n\n_world_.\n</pre>\n</td></tr></table>\n",
-        "html": "<table><tr><td>\n<pre>\n**Hello**,\n<p><em>world</em>.\n</pre></p>\n</td></tr></table>\n",
-        "example": 118,
-        "start_line": 2087,
-        "end_line": 2102,
-        "section": "HTML blocks"
+        markdown: "<table><tr><td>\n<pre>\n**Hello**,\n\n_world_.\n</pre>\n</td></tr></table>\n",
+        html: "<table><tr><td>\n<pre>\n**Hello**,\n<p><em>world</em>.\n</pre></p>\n</td></tr></table>\n",
+        example: 118,
+        start_line: 2087,
+        end_line: 2102,
+        section: "HTML blocks",
     },
     {
-        "markdown": "<table>\n  <tr>\n    <td>\n           hi\n    </td>\n  </tr>\n</table>\n\nokay.\n",
-        "html": "<table>\n  <tr>\n    <td>\n           hi\n    </td>\n  </tr>\n</table>\n<p>okay.</p>\n",
-        "example": 119,
-        "start_line": 2116,
-        "end_line": 2135,
-        "section": "HTML blocks"
+        markdown: "<table>\n  <tr>\n    <td>\n           hi\n    </td>\n  </tr>\n</table>\n\nokay.\n",
+        html: "<table>\n  <tr>\n    <td>\n           hi\n    </td>\n  </tr>\n</table>\n<p>okay.</p>\n",
+        example: 119,
+        start_line: 2116,
+        end_line: 2135,
+        section: "HTML blocks",
     },
     {
-        "markdown": " <div>\n  *hello*\n         <foo><a>\n",
-        "html": " <div>\n  *hello*\n         <foo><a>\n",
-        "example": 120,
-        "start_line": 2138,
-        "end_line": 2146,
-        "section": "HTML blocks"
+        markdown: " <div>\n  *hello*\n         <foo><a>\n",
+        html: " <div>\n  *hello*\n         <foo><a>\n",
+        example: 120,
+        start_line: 2138,
+        end_line: 2146,
+        section: "HTML blocks",
     },
     {
-        "markdown": "</div>\n*foo*\n",
-        "html": "</div>\n*foo*\n",
-        "example": 121,
-        "start_line": 2151,
-        "end_line": 2157,
-        "section": "HTML blocks"
+        markdown: "</div>\n*foo*\n",
+        html: "</div>\n*foo*\n",
+        example: 121,
+        start_line: 2151,
+        end_line: 2157,
+        section: "HTML blocks",
     },
     {
-        "markdown": "<DIV CLASS=\"foo\">\n\n*Markdown*\n\n</DIV>\n",
-        "html": "<DIV CLASS=\"foo\">\n<p><em>Markdown</em></p>\n</DIV>\n",
-        "example": 122,
-        "start_line": 2162,
-        "end_line": 2172,
-        "section": "HTML blocks"
+        markdown: "<DIV CLASS=\"foo\">\n\n*Markdown*\n\n</DIV>\n",
+        html: "<DIV CLASS=\"foo\">\n<p><em>Markdown</em></p>\n</DIV>\n",
+        example: 122,
+        start_line: 2162,
+        end_line: 2172,
+        section: "HTML blocks",
     },
     {
-        "markdown": "<div id=\"foo\"\n  class=\"bar\">\n</div>\n",
-        "html": "<div id=\"foo\"\n  class=\"bar\">\n</div>\n",
-        "example": 123,
-        "start_line": 2178,
-        "end_line": 2186,
-        "section": "HTML blocks"
+        markdown: "<div id=\"foo\"\n  class=\"bar\">\n</div>\n",
+        html: "<div id=\"foo\"\n  class=\"bar\">\n</div>\n",
+        example: 123,
+        start_line: 2178,
+        end_line: 2186,
+        section: "HTML blocks",
     },
     {
-        "markdown": "<div id=\"foo\" class=\"bar\n  baz\">\n</div>\n",
-        "html": "<div id=\"foo\" class=\"bar\n  baz\">\n</div>\n",
-        "example": 124,
-        "start_line": 2189,
-        "end_line": 2197,
-        "section": "HTML blocks"
+        markdown: "<div id=\"foo\" class=\"bar\n  baz\">\n</div>\n",
+        html: "<div id=\"foo\" class=\"bar\n  baz\">\n</div>\n",
+        example: 124,
+        start_line: 2189,
+        end_line: 2197,
+        section: "HTML blocks",
     },
     {
-        "markdown": "<div>\n*foo*\n\n*bar*\n",
-        "html": "<div>\n*foo*\n<p><em>bar</em></p>\n",
-        "example": 125,
-        "start_line": 2201,
-        "end_line": 2210,
-        "section": "HTML blocks"
+        markdown: "<div>\n*foo*\n\n*bar*\n",
+        html: "<div>\n*foo*\n<p><em>bar</em></p>\n",
+        example: 125,
+        start_line: 2201,
+        end_line: 2210,
+        section: "HTML blocks",
     },
     {
-        "markdown": "<div id=\"foo\"\n*hi*\n",
-        "html": "<div id=\"foo\"\n*hi*\n",
-        "example": 126,
-        "start_line": 2217,
-        "end_line": 2223,
-        "section": "HTML blocks"
+        markdown: "<div id=\"foo\"\n*hi*\n",
+        html: "<div id=\"foo\"\n*hi*\n",
+        example: 126,
+        start_line: 2217,
+        end_line: 2223,
+        section: "HTML blocks",
     },
     {
-        "markdown": "<div class\nfoo\n",
-        "html": "<div class\nfoo\n",
-        "example": 127,
-        "start_line": 2226,
-        "end_line": 2232,
-        "section": "HTML blocks"
+        markdown: "<div class\nfoo\n",
+        html: "<div class\nfoo\n",
+        example: 127,
+        start_line: 2226,
+        end_line: 2232,
+        section: "HTML blocks",
     },
     {
-        "markdown": "<div *???-&&&-<---\n*foo*\n",
-        "html": "<div *???-&&&-<---\n*foo*\n",
-        "example": 128,
-        "start_line": 2238,
-        "end_line": 2244,
-        "section": "HTML blocks"
+        markdown: "<div *???-&&&-<---\n*foo*\n",
+        html: "<div *???-&&&-<---\n*foo*\n",
+        example: 128,
+        start_line: 2238,
+        end_line: 2244,
+        section: "HTML blocks",
     },
     {
-        "markdown": "<div><a href=\"bar\">*foo*</a></div>\n",
-        "html": "<div><a href=\"bar\">*foo*</a></div>\n",
-        "example": 129,
-        "start_line": 2250,
-        "end_line": 2254,
-        "section": "HTML blocks"
+        markdown: "<div><a href=\"bar\">*foo*</a></div>\n",
+        html: "<div><a href=\"bar\">*foo*</a></div>\n",
+        example: 129,
+        start_line: 2250,
+        end_line: 2254,
+        section: "HTML blocks",
     },
     {
-        "markdown": "<table><tr><td>\nfoo\n</td></tr></table>\n",
-        "html": "<table><tr><td>\nfoo\n</td></tr></table>\n",
-        "example": 130,
-        "start_line": 2257,
-        "end_line": 2265,
-        "section": "HTML blocks"
+        markdown: "<table><tr><td>\nfoo\n</td></tr></table>\n",
+        html: "<table><tr><td>\nfoo\n</td></tr></table>\n",
+        example: 130,
+        start_line: 2257,
+        end_line: 2265,
+        section: "HTML blocks",
     },
     {
-        "markdown": "<div></div>\n``` c\nint x = 33;\n```\n",
-        "html": "<div></div>\n``` c\nint x = 33;\n```\n",
-        "example": 131,
-        "start_line": 2274,
-        "end_line": 2284,
-        "section": "HTML blocks"
+        markdown: "<div></div>\n``` c\nint x = 33;\n```\n",
+        html: "<div></div>\n``` c\nint x = 33;\n```\n",
+        example: 131,
+        start_line: 2274,
+        end_line: 2284,
+        section: "HTML blocks",
     },
     {
-        "markdown": "<a href=\"foo\">\n*bar*\n</a>\n",
-        "html": "<a href=\"foo\">\n*bar*\n</a>\n",
-        "example": 132,
-        "start_line": 2291,
-        "end_line": 2299,
-        "section": "HTML blocks"
+        markdown: "<a href=\"foo\">\n*bar*\n</a>\n",
+        html: "<a href=\"foo\">\n*bar*\n</a>\n",
+        example: 132,
+        start_line: 2291,
+        end_line: 2299,
+        section: "HTML blocks",
     },
     {
-        "markdown": "<Warning>\n*bar*\n</Warning>\n",
-        "html": "<Warning>\n*bar*\n</Warning>\n",
-        "example": 133,
-        "start_line": 2304,
-        "end_line": 2312,
-        "section": "HTML blocks"
+        markdown: "<Warning>\n*bar*\n</Warning>\n",
+        html: "<Warning>\n*bar*\n</Warning>\n",
+        example: 133,
+        start_line: 2304,
+        end_line: 2312,
+        section: "HTML blocks",
     },
     {
-        "markdown": "<i class=\"foo\">\n*bar*\n</i>\n",
-        "html": "<i class=\"foo\">\n*bar*\n</i>\n",
-        "example": 134,
-        "start_line": 2315,
-        "end_line": 2323,
-        "section": "HTML blocks"
+        markdown: "<i class=\"foo\">\n*bar*\n</i>\n",
+        html: "<i class=\"foo\">\n*bar*\n</i>\n",
+        example: 134,
+        start_line: 2315,
+        end_line: 2323,
+        section: "HTML blocks",
     },
     {
-        "markdown": "</ins>\n*bar*\n",
-        "html": "</ins>\n*bar*\n",
-        "example": 135,
-        "start_line": 2326,
-        "end_line": 2332,
-        "section": "HTML blocks"
+        markdown: "</ins>\n*bar*\n",
+        html: "</ins>\n*bar*\n",
+        example: 135,
+        start_line: 2326,
+        end_line: 2332,
+        section: "HTML blocks",
     },
     {
-        "markdown": "<del>\n*foo*\n</del>\n",
-        "html": "<del>\n*foo*\n</del>\n",
-        "example": 136,
-        "start_line": 2341,
-        "end_line": 2349,
-        "section": "HTML blocks"
+        markdown: "<del>\n*foo*\n</del>\n",
+        html: "<del>\n*foo*\n</del>\n",
+        example: 136,
+        start_line: 2341,
+        end_line: 2349,
+        section: "HTML blocks",
     },
     {
-        "markdown": "<del>\n\n*foo*\n\n</del>\n",
-        "html": "<del>\n<p><em>foo</em></p>\n</del>\n",
-        "example": 137,
-        "start_line": 2356,
-        "end_line": 2366,
-        "section": "HTML blocks"
+        markdown: "<del>\n\n*foo*\n\n</del>\n",
+        html: "<del>\n<p><em>foo</em></p>\n</del>\n",
+        example: 137,
+        start_line: 2356,
+        end_line: 2366,
+        section: "HTML blocks",
     },
     {
-        "markdown": "<del>*foo*</del>\n",
-        "html": "<p><del><em>foo</em></del></p>\n",
-        "example": 138,
-        "start_line": 2374,
-        "end_line": 2378,
-        "section": "HTML blocks"
+        markdown: "<del>*foo*</del>\n",
+        html: "<p><del><em>foo</em></del></p>\n",
+        example: 138,
+        start_line: 2374,
+        end_line: 2378,
+        section: "HTML blocks",
     },
     {
-        "markdown": "<pre language=\"haskell\"><code>\nimport Text.HTML.TagSoup\n\nmain :: IO ()\nmain = print $ parseTags tags\n</code></pre>\nokay\n",
-        "html": "<pre language=\"haskell\"><code>\nimport Text.HTML.TagSoup\n\nmain :: IO ()\nmain = print $ parseTags tags\n</code></pre>\n<p>okay</p>\n",
-        "example": 139,
-        "start_line": 2390,
-        "end_line": 2406,
-        "section": "HTML blocks"
+        markdown: "<pre language=\"haskell\"><code>\nimport Text.HTML.TagSoup\n\nmain :: IO ()\nmain = print $ parseTags tags\n</code></pre>\nokay\n",
+        html: "<pre language=\"haskell\"><code>\nimport Text.HTML.TagSoup\n\nmain :: IO ()\nmain = print $ parseTags tags\n</code></pre>\n<p>okay</p>\n",
+        example: 139,
+        start_line: 2390,
+        end_line: 2406,
+        section: "HTML blocks",
     },
     {
-        "markdown": "<script type=\"text/javascript\">\n// JavaScript example\n\ndocument.getElementById(\"demo\").innerHTML = \"Hello JavaScript!\";\n</script>\nokay\n",
-        "html": "<script type=\"text/javascript\">\n// JavaScript example\n\ndocument.getElementById(\"demo\").innerHTML = \"Hello JavaScript!\";\n</script>\n<p>okay</p>\n",
-        "example": 140,
-        "start_line": 2411,
-        "end_line": 2425,
-        "section": "HTML blocks"
+        markdown: "<script type=\"text/javascript\">\n// JavaScript example\n\ndocument.getElementById(\"demo\").innerHTML = \"Hello JavaScript!\";\n</script>\nokay\n",
+        html: "<script type=\"text/javascript\">\n// JavaScript example\n\ndocument.getElementById(\"demo\").innerHTML = \"Hello JavaScript!\";\n</script>\n<p>okay</p>\n",
+        example: 140,
+        start_line: 2411,
+        end_line: 2425,
+        section: "HTML blocks",
     },
     {
-        "markdown": "<style\n  type=\"text/css\">\nh1 {color:red;}\n\np {color:blue;}\n</style>\nokay\n",
-        "html": "<style\n  type=\"text/css\">\nh1 {color:red;}\n\np {color:blue;}\n</style>\n<p>okay</p>\n",
-        "example": 141,
-        "start_line": 2430,
-        "end_line": 2446,
-        "section": "HTML blocks"
+        markdown: "<style\n  type=\"text/css\">\nh1 {color:red;}\n\np {color:blue;}\n</style>\nokay\n",
+        html: "<style\n  type=\"text/css\">\nh1 {color:red;}\n\np {color:blue;}\n</style>\n<p>okay</p>\n",
+        example: 141,
+        start_line: 2430,
+        end_line: 2446,
+        section: "HTML blocks",
     },
     {
-        "markdown": "<style\n  type=\"text/css\">\n\nfoo\n",
-        "html": "<style\n  type=\"text/css\">\n\nfoo\n",
-        "example": 142,
-        "start_line": 2453,
-        "end_line": 2463,
-        "section": "HTML blocks"
+        markdown: "<style\n  type=\"text/css\">\n\nfoo\n",
+        html: "<style\n  type=\"text/css\">\n\nfoo\n",
+        example: 142,
+        start_line: 2453,
+        end_line: 2463,
+        section: "HTML blocks",
     },
     {
-        "markdown": "> <div>\n> foo\n\nbar\n",
-        "html": "<blockquote>\n<div>\nfoo\n</blockquote>\n<p>bar</p>\n",
-        "example": 143,
-        "start_line": 2466,
-        "end_line": 2477,
-        "section": "HTML blocks"
+        markdown: "> <div>\n> foo\n\nbar\n",
+        html: "<blockquote>\n<div>\nfoo\n</blockquote>\n<p>bar</p>\n",
+        example: 143,
+        start_line: 2466,
+        end_line: 2477,
+        section: "HTML blocks",
     },
     {
-        "markdown": "- <div>\n- foo\n",
-        "html": "<ul>\n<li>\n<div>\n</li>\n<li>foo</li>\n</ul>\n",
-        "example": 144,
-        "start_line": 2480,
-        "end_line": 2490,
-        "section": "HTML blocks"
+        markdown: "- <div>\n- foo\n",
+        html: "<ul>\n<li>\n<div>\n</li>\n<li>foo</li>\n</ul>\n",
+        example: 144,
+        start_line: 2480,
+        end_line: 2490,
+        section: "HTML blocks",
     },
     {
-        "markdown": "<style>p{color:red;}</style>\n*foo*\n",
-        "html": "<style>p{color:red;}</style>\n<p><em>foo</em></p>\n",
-        "example": 145,
-        "start_line": 2495,
-        "end_line": 2501,
-        "section": "HTML blocks"
+        markdown: "<style>p{color:red;}</style>\n*foo*\n",
+        html: "<style>p{color:red;}</style>\n<p><em>foo</em></p>\n",
+        example: 145,
+        start_line: 2495,
+        end_line: 2501,
+        section: "HTML blocks",
     },
     {
-        "markdown": "<!-- foo -->*bar*\n*baz*\n",
-        "html": "<!-- foo -->*bar*\n<p><em>baz</em></p>\n",
-        "example": 146,
-        "start_line": 2504,
-        "end_line": 2510,
-        "section": "HTML blocks"
+        markdown: "<!-- foo -->*bar*\n*baz*\n",
+        html: "<!-- foo -->*bar*\n<p><em>baz</em></p>\n",
+        example: 146,
+        start_line: 2504,
+        end_line: 2510,
+        section: "HTML blocks",
     },
     {
-        "markdown": "<script>\nfoo\n</script>1. *bar*\n",
-        "html": "<script>\nfoo\n</script>1. *bar*\n",
-        "example": 147,
-        "start_line": 2516,
-        "end_line": 2524,
-        "section": "HTML blocks"
+        markdown: "<script>\nfoo\n</script>1. *bar*\n",
+        html: "<script>\nfoo\n</script>1. *bar*\n",
+        example: 147,
+        start_line: 2516,
+        end_line: 2524,
+        section: "HTML blocks",
     },
     {
-        "markdown": "<!-- Foo\n\nbar\n   baz -->\nokay\n",
-        "html": "<!-- Foo\n\nbar\n   baz -->\n<p>okay</p>\n",
-        "example": 148,
-        "start_line": 2529,
-        "end_line": 2541,
-        "section": "HTML blocks"
+        markdown: "<!-- Foo\n\nbar\n   baz -->\nokay\n",
+        html: "<!-- Foo\n\nbar\n   baz -->\n<p>okay</p>\n",
+        example: 148,
+        start_line: 2529,
+        end_line: 2541,
+        section: "HTML blocks",
     },
     {
-        "markdown": "<?php\n\n  echo '>';\n\n?>\nokay\n",
-        "html": "<?php\n\n  echo '>';\n\n?>\n<p>okay</p>\n",
-        "example": 149,
-        "start_line": 2547,
-        "end_line": 2561,
-        "section": "HTML blocks"
+        markdown: "<?php\n\n  echo '>';\n\n?>\nokay\n",
+        html: "<?php\n\n  echo '>';\n\n?>\n<p>okay</p>\n",
+        example: 149,
+        start_line: 2547,
+        end_line: 2561,
+        section: "HTML blocks",
     },
     {
-        "markdown": "<!DOCTYPE html>\n",
-        "html": "<!DOCTYPE html>\n",
-        "example": 150,
-        "start_line": 2566,
-        "end_line": 2570,
-        "section": "HTML blocks"
+        markdown: "<!DOCTYPE html>\n",
+        html: "<!DOCTYPE html>\n",
+        example: 150,
+        start_line: 2566,
+        end_line: 2570,
+        section: "HTML blocks",
     },
     {
-        "markdown": "<![CDATA[\nfunction matchwo(a,b)\n{\n  if (a < b && a < 0) then {\n    return 1;\n\n  } else {\n\n    return 0;\n  }\n}\n]]>\nokay\n",
-        "html": "<![CDATA[\nfunction matchwo(a,b)\n{\n  if (a < b && a < 0) then {\n    return 1;\n\n  } else {\n\n    return 0;\n  }\n}\n]]>\n<p>okay</p>\n",
-        "example": 151,
-        "start_line": 2575,
-        "end_line": 2603,
-        "section": "HTML blocks"
+        markdown: "<![CDATA[\nfunction matchwo(a,b)\n{\n  if (a < b && a < 0) then {\n    return 1;\n\n  } else {\n\n    return 0;\n  }\n}\n]]>\nokay\n",
+        html: "<![CDATA[\nfunction matchwo(a,b)\n{\n  if (a < b && a < 0) then {\n    return 1;\n\n  } else {\n\n    return 0;\n  }\n}\n]]>\n<p>okay</p>\n",
+        example: 151,
+        start_line: 2575,
+        end_line: 2603,
+        section: "HTML blocks",
     },
     {
-        "markdown": "  <!-- foo -->\n\n    <!-- foo -->\n",
-        "html": "  <!-- foo -->\n<pre><code>&lt;!-- foo --&gt;\n</code></pre>\n",
-        "example": 152,
-        "start_line": 2608,
-        "end_line": 2616,
-        "section": "HTML blocks"
+        markdown: "  <!-- foo -->\n\n    <!-- foo -->\n",
+        html: "  <!-- foo -->\n<pre><code>&lt;!-- foo --&gt;\n</code></pre>\n",
+        example: 152,
+        start_line: 2608,
+        end_line: 2616,
+        section: "HTML blocks",
     },
     {
-        "markdown": "  <div>\n\n    <div>\n",
-        "html": "  <div>\n<pre><code>&lt;div&gt;\n</code></pre>\n",
-        "example": 153,
-        "start_line": 2619,
-        "end_line": 2627,
-        "section": "HTML blocks"
+        markdown: "  <div>\n\n    <div>\n",
+        html: "  <div>\n<pre><code>&lt;div&gt;\n</code></pre>\n",
+        example: 153,
+        start_line: 2619,
+        end_line: 2627,
+        section: "HTML blocks",
     },
     {
-        "markdown": "Foo\n<div>\nbar\n</div>\n",
-        "html": "<p>Foo</p>\n<div>\nbar\n</div>\n",
-        "example": 154,
-        "start_line": 2633,
-        "end_line": 2643,
-        "section": "HTML blocks"
+        markdown: "Foo\n<div>\nbar\n</div>\n",
+        html: "<p>Foo</p>\n<div>\nbar\n</div>\n",
+        example: 154,
+        start_line: 2633,
+        end_line: 2643,
+        section: "HTML blocks",
     },
     {
-        "markdown": "<div>\nbar\n</div>\n*foo*\n",
-        "html": "<div>\nbar\n</div>\n*foo*\n",
-        "example": 155,
-        "start_line": 2650,
-        "end_line": 2660,
-        "section": "HTML blocks"
+        markdown: "<div>\nbar\n</div>\n*foo*\n",
+        html: "<div>\nbar\n</div>\n*foo*\n",
+        example: 155,
+        start_line: 2650,
+        end_line: 2660,
+        section: "HTML blocks",
     },
     {
-        "markdown": "Foo\n<a href=\"bar\">\nbaz\n",
-        "html": "<p>Foo\n<a href=\"bar\">\nbaz</p>\n",
-        "example": 156,
-        "start_line": 2665,
-        "end_line": 2673,
-        "section": "HTML blocks"
+        markdown: "Foo\n<a href=\"bar\">\nbaz\n",
+        html: "<p>Foo\n<a href=\"bar\">\nbaz</p>\n",
+        example: 156,
+        start_line: 2665,
+        end_line: 2673,
+        section: "HTML blocks",
     },
     {
-        "markdown": "<div>\n\n*Emphasized* text.\n\n</div>\n",
-        "html": "<div>\n<p><em>Emphasized</em> text.</p>\n</div>\n",
-        "example": 157,
-        "start_line": 2706,
-        "end_line": 2716,
-        "section": "HTML blocks"
+        markdown: "<div>\n\n*Emphasized* text.\n\n</div>\n",
+        html: "<div>\n<p><em>Emphasized</em> text.</p>\n</div>\n",
+        example: 157,
+        start_line: 2706,
+        end_line: 2716,
+        section: "HTML blocks",
     },
     {
-        "markdown": "<div>\n*Emphasized* text.\n</div>\n",
-        "html": "<div>\n*Emphasized* text.\n</div>\n",
-        "example": 158,
-        "start_line": 2719,
-        "end_line": 2727,
-        "section": "HTML blocks"
+        markdown: "<div>\n*Emphasized* text.\n</div>\n",
+        html: "<div>\n*Emphasized* text.\n</div>\n",
+        example: 158,
+        start_line: 2719,
+        end_line: 2727,
+        section: "HTML blocks",
     },
     {
-        "markdown": "<table>\n\n<tr>\n\n<td>\nHi\n</td>\n\n</tr>\n\n</table>\n",
-        "html": "<table>\n<tr>\n<td>\nHi\n</td>\n</tr>\n</table>\n",
-        "example": 159,
-        "start_line": 2741,
-        "end_line": 2761,
-        "section": "HTML blocks"
+        markdown: "<table>\n\n<tr>\n\n<td>\nHi\n</td>\n\n</tr>\n\n</table>\n",
+        html: "<table>\n<tr>\n<td>\nHi\n</td>\n</tr>\n</table>\n",
+        example: 159,
+        start_line: 2741,
+        end_line: 2761,
+        section: "HTML blocks",
     },
     {
-        "markdown": "<table>\n\n  <tr>\n\n    <td>\n      Hi\n    </td>\n\n  </tr>\n\n</table>\n",
-        "html": "<table>\n  <tr>\n<pre><code>&lt;td&gt;\n  Hi\n&lt;/td&gt;\n</code></pre>\n  </tr>\n</table>\n",
-        "example": 160,
-        "start_line": 2768,
-        "end_line": 2789,
-        "section": "HTML blocks"
+        markdown: "<table>\n\n  <tr>\n\n    <td>\n      Hi\n    </td>\n\n  </tr>\n\n</table>\n",
+        html: "<table>\n  <tr>\n<pre><code>&lt;td&gt;\n  Hi\n&lt;/td&gt;\n</code></pre>\n  </tr>\n</table>\n",
+        example: 160,
+        start_line: 2768,
+        end_line: 2789,
+        section: "HTML blocks",
     },
     {
-        "markdown": "[foo]: /url \"title\"\n\n[foo]\n",
-        "html": "<p><a href=\"/url\" title=\"title\">foo</a></p>\n",
-        "example": 161,
-        "start_line": 2816,
-        "end_line": 2822,
-        "section": "Link reference definitions"
+        markdown: "[foo]: /url \"title\"\n\n[foo]\n",
+        html: "<p><a href=\"/url\" title=\"title\">foo</a></p>\n",
+        example: 161,
+        start_line: 2816,
+        end_line: 2822,
+        section: "Link reference definitions",
     },
     {
-        "markdown": "   [foo]: \n      /url  \n           'the title'  \n\n[foo]\n",
-        "html": "<p><a href=\"/url\" title=\"the title\">foo</a></p>\n",
-        "example": 162,
-        "start_line": 2825,
-        "end_line": 2833,
-        "section": "Link reference definitions"
+        markdown: "   [foo]: \n      /url  \n           'the title'  \n\n[foo]\n",
+        html: "<p><a href=\"/url\" title=\"the title\">foo</a></p>\n",
+        example: 162,
+        start_line: 2825,
+        end_line: 2833,
+        section: "Link reference definitions",
     },
     {
-        "markdown": "[Foo*bar\\]]:my_(url) 'title (with parens)'\n\n[Foo*bar\\]]\n",
-        "html": "<p><a href=\"my_(url)\" title=\"title (with parens)\">Foo*bar]</a></p>\n",
-        "example": 163,
-        "start_line": 2836,
-        "end_line": 2842,
-        "section": "Link reference definitions"
+        markdown: "[Foo*bar\\]]:my_(url) 'title (with parens)'\n\n[Foo*bar\\]]\n",
+        html: "<p><a href=\"my_(url)\" title=\"title (with parens)\">Foo*bar]</a></p>\n",
+        example: 163,
+        start_line: 2836,
+        end_line: 2842,
+        section: "Link reference definitions",
     },
     {
-        "markdown": "[Foo bar]:\n<my url>\n'title'\n\n[Foo bar]\n",
-        "html": "<p><a href=\"my%20url\" title=\"title\">Foo bar</a></p>\n",
-        "example": 164,
-        "start_line": 2845,
-        "end_line": 2853,
-        "section": "Link reference definitions"
+        markdown: "[Foo bar]:\n<my url>\n'title'\n\n[Foo bar]\n",
+        html: "<p><a href=\"my%20url\" title=\"title\">Foo bar</a></p>\n",
+        example: 164,
+        start_line: 2845,
+        end_line: 2853,
+        section: "Link reference definitions",
     },
     {
-        "markdown": "[foo]: /url '\ntitle\nline1\nline2\n'\n\n[foo]\n",
-        "html": "<p><a href=\"/url\" title=\"\ntitle\nline1\nline2\n\">foo</a></p>\n",
-        "example": 165,
-        "start_line": 2858,
-        "end_line": 2872,
-        "section": "Link reference definitions"
+        markdown: "[foo]: /url '\ntitle\nline1\nline2\n'\n\n[foo]\n",
+        html: "<p><a href=\"/url\" title=\"\ntitle\nline1\nline2\n\">foo</a></p>\n",
+        example: 165,
+        start_line: 2858,
+        end_line: 2872,
+        section: "Link reference definitions",
     },
     {
-        "markdown": "[foo]: /url 'title\n\nwith blank line'\n\n[foo]\n",
-        "html": "<p>[foo]: /url 'title</p>\n<p>with blank line'</p>\n<p>[foo]</p>\n",
-        "example": 166,
-        "start_line": 2877,
-        "end_line": 2887,
-        "section": "Link reference definitions"
+        markdown: "[foo]: /url 'title\n\nwith blank line'\n\n[foo]\n",
+        html: "<p>[foo]: /url 'title</p>\n<p>with blank line'</p>\n<p>[foo]</p>\n",
+        example: 166,
+        start_line: 2877,
+        end_line: 2887,
+        section: "Link reference definitions",
     },
     {
-        "markdown": "[foo]:\n/url\n\n[foo]\n",
-        "html": "<p><a href=\"/url\">foo</a></p>\n",
-        "example": 167,
-        "start_line": 2892,
-        "end_line": 2899,
-        "section": "Link reference definitions"
+        markdown: "[foo]:\n/url\n\n[foo]\n",
+        html: "<p><a href=\"/url\">foo</a></p>\n",
+        example: 167,
+        start_line: 2892,
+        end_line: 2899,
+        section: "Link reference definitions",
     },
     {
-        "markdown": "[foo]:\n\n[foo]\n",
-        "html": "<p>[foo]:</p>\n<p>[foo]</p>\n",
-        "example": 168,
-        "start_line": 2904,
-        "end_line": 2911,
-        "section": "Link reference definitions"
+        markdown: "[foo]:\n\n[foo]\n",
+        html: "<p>[foo]:</p>\n<p>[foo]</p>\n",
+        example: 168,
+        start_line: 2904,
+        end_line: 2911,
+        section: "Link reference definitions",
     },
     {
-        "markdown": "[foo]: <>\n\n[foo]\n",
-        "html": "<p><a href=\"\">foo</a></p>\n",
-        "example": 169,
-        "start_line": 2916,
-        "end_line": 2922,
-        "section": "Link reference definitions"
+        markdown: "[foo]: <>\n\n[foo]\n",
+        html: "<p><a href=\"\">foo</a></p>\n",
+        example: 169,
+        start_line: 2916,
+        end_line: 2922,
+        section: "Link reference definitions",
     },
     {
-        "markdown": "[foo]: <bar>(baz)\n\n[foo]\n",
-        "html": "<p>[foo]: <bar>(baz)</p>\n<p>[foo]</p>\n",
-        "example": 170,
-        "start_line": 2927,
-        "end_line": 2934,
-        "section": "Link reference definitions"
+        markdown: "[foo]: <bar>(baz)\n\n[foo]\n",
+        html: "<p>[foo]: <bar>(baz)</p>\n<p>[foo]</p>\n",
+        example: 170,
+        start_line: 2927,
+        end_line: 2934,
+        section: "Link reference definitions",
     },
     {
-        "markdown": "[foo]: /url\\bar\\*baz \"foo\\\"bar\\baz\"\n\n[foo]\n",
-        "html": "<p><a href=\"/url%5Cbar*baz\" title=\"foo&quot;bar\\baz\">foo</a></p>\n",
-        "example": 171,
-        "start_line": 2940,
-        "end_line": 2946,
-        "section": "Link reference definitions"
+        markdown: "[foo]: /url\\bar\\*baz \"foo\\\"bar\\baz\"\n\n[foo]\n",
+        html: "<p><a href=\"/url%5Cbar*baz\" title=\"foo&quot;bar\\baz\">foo</a></p>\n",
+        example: 171,
+        start_line: 2940,
+        end_line: 2946,
+        section: "Link reference definitions",
     },
     {
-        "markdown": "[foo]\n\n[foo]: url\n",
-        "html": "<p><a href=\"url\">foo</a></p>\n",
-        "example": 172,
-        "start_line": 2951,
-        "end_line": 2957,
-        "section": "Link reference definitions"
+        markdown: "[foo]\n\n[foo]: url\n",
+        html: "<p><a href=\"url\">foo</a></p>\n",
+        example: 172,
+        start_line: 2951,
+        end_line: 2957,
+        section: "Link reference definitions",
     },
     {
-        "markdown": "[foo]\n\n[foo]: first\n[foo]: second\n",
-        "html": "<p><a href=\"first\">foo</a></p>\n",
-        "example": 173,
-        "start_line": 2963,
-        "end_line": 2970,
-        "section": "Link reference definitions"
+        markdown: "[foo]\n\n[foo]: first\n[foo]: second\n",
+        html: "<p><a href=\"first\">foo</a></p>\n",
+        example: 173,
+        start_line: 2963,
+        end_line: 2970,
+        section: "Link reference definitions",
     },
     {
-        "markdown": "[FOO]: /url\n\n[Foo]\n",
-        "html": "<p><a href=\"/url\">Foo</a></p>\n",
-        "example": 174,
-        "start_line": 2976,
-        "end_line": 2982,
-        "section": "Link reference definitions"
+        markdown: "[FOO]: /url\n\n[Foo]\n",
+        html: "<p><a href=\"/url\">Foo</a></p>\n",
+        example: 174,
+        start_line: 2976,
+        end_line: 2982,
+        section: "Link reference definitions",
     },
     {
-        "markdown": "[ΑΓΩ]: /φου\n\n[αγω]\n",
-        "html": "<p><a href=\"/%CF%86%CE%BF%CF%85\">αγω</a></p>\n",
-        "example": 175,
-        "start_line": 2985,
-        "end_line": 2991,
-        "section": "Link reference definitions"
+        markdown: "[ΑΓΩ]: /φου\n\n[αγω]\n",
+        html: "<p><a href=\"/%CF%86%CE%BF%CF%85\">αγω</a></p>\n",
+        example: 175,
+        start_line: 2985,
+        end_line: 2991,
+        section: "Link reference definitions",
     },
     {
-        "markdown": "[foo]: /url\n",
-        "html": "",
-        "example": 176,
-        "start_line": 2997,
-        "end_line": 3000,
-        "section": "Link reference definitions"
+        markdown: "[foo]: /url\n",
+        html: "",
+        example: 176,
+        start_line: 2997,
+        end_line: 3000,
+        section: "Link reference definitions",
     },
     {
-        "markdown": "[\nfoo\n]: /url\nbar\n",
-        "html": "<p>bar</p>\n",
-        "example": 177,
-        "start_line": 3005,
-        "end_line": 3012,
-        "section": "Link reference definitions"
+        markdown: "[\nfoo\n]: /url\nbar\n",
+        html: "<p>bar</p>\n",
+        example: 177,
+        start_line: 3005,
+        end_line: 3012,
+        section: "Link reference definitions",
     },
     {
-        "markdown": "[foo]: /url \"title\" ok\n",
-        "html": "<p>[foo]: /url &quot;title&quot; ok</p>\n",
-        "example": 178,
-        "start_line": 3018,
-        "end_line": 3022,
-        "section": "Link reference definitions"
+        markdown: "[foo]: /url \"title\" ok\n",
+        html: "<p>[foo]: /url &quot;title&quot; ok</p>\n",
+        example: 178,
+        start_line: 3018,
+        end_line: 3022,
+        section: "Link reference definitions",
     },
     {
-        "markdown": "[foo]: /url\n\"title\" ok\n",
-        "html": "<p>&quot;title&quot; ok</p>\n",
-        "example": 179,
-        "start_line": 3027,
-        "end_line": 3032,
-        "section": "Link reference definitions"
+        markdown: "[foo]: /url\n\"title\" ok\n",
+        html: "<p>&quot;title&quot; ok</p>\n",
+        example: 179,
+        start_line: 3027,
+        end_line: 3032,
+        section: "Link reference definitions",
     },
     {
-        "markdown": "    [foo]: /url \"title\"\n\n[foo]\n",
-        "html": "<pre><code>[foo]: /url &quot;title&quot;\n</code></pre>\n<p>[foo]</p>\n",
-        "example": 180,
-        "start_line": 3038,
-        "end_line": 3046,
-        "section": "Link reference definitions"
+        markdown: "    [foo]: /url \"title\"\n\n[foo]\n",
+        html: "<pre><code>[foo]: /url &quot;title&quot;\n</code></pre>\n<p>[foo]</p>\n",
+        example: 180,
+        start_line: 3038,
+        end_line: 3046,
+        section: "Link reference definitions",
     },
     {
-        "markdown": "```\n[foo]: /url\n```\n\n[foo]\n",
-        "html": "<pre><code>[foo]: /url\n</code></pre>\n<p>[foo]</p>\n",
-        "example": 181,
-        "start_line": 3052,
-        "end_line": 3062,
-        "section": "Link reference definitions"
+        markdown: "```\n[foo]: /url\n```\n\n[foo]\n",
+        html: "<pre><code>[foo]: /url\n</code></pre>\n<p>[foo]</p>\n",
+        example: 181,
+        start_line: 3052,
+        end_line: 3062,
+        section: "Link reference definitions",
     },
     {
-        "markdown": "Foo\n[bar]: /baz\n\n[bar]\n",
-        "html": "<p>Foo\n[bar]: /baz</p>\n<p>[bar]</p>\n",
-        "example": 182,
-        "start_line": 3067,
-        "end_line": 3076,
-        "section": "Link reference definitions"
+        markdown: "Foo\n[bar]: /baz\n\n[bar]\n",
+        html: "<p>Foo\n[bar]: /baz</p>\n<p>[bar]</p>\n",
+        example: 182,
+        start_line: 3067,
+        end_line: 3076,
+        section: "Link reference definitions",
     },
     {
-        "markdown": "# [Foo]\n[foo]: /url\n> bar\n",
-        "html": "<h1><a href=\"/url\">Foo</a></h1>\n<blockquote>\n<p>bar</p>\n</blockquote>\n",
-        "example": 183,
-        "start_line": 3082,
-        "end_line": 3091,
-        "section": "Link reference definitions"
+        markdown: "# [Foo]\n[foo]: /url\n> bar\n",
+        html: "<h1><a href=\"/url\">Foo</a></h1>\n<blockquote>\n<p>bar</p>\n</blockquote>\n",
+        example: 183,
+        start_line: 3082,
+        end_line: 3091,
+        section: "Link reference definitions",
     },
     {
-        "markdown": "[foo]: /url\nbar\n===\n[foo]\n",
-        "html": "<h1>bar</h1>\n<p><a href=\"/url\">foo</a></p>\n",
-        "example": 184,
-        "start_line": 3093,
-        "end_line": 3101,
-        "section": "Link reference definitions"
+        markdown: "[foo]: /url\nbar\n===\n[foo]\n",
+        html: "<h1>bar</h1>\n<p><a href=\"/url\">foo</a></p>\n",
+        example: 184,
+        start_line: 3093,
+        end_line: 3101,
+        section: "Link reference definitions",
     },
     {
-        "markdown": "[foo]: /url\n===\n[foo]\n",
-        "html": "<p>===\n<a href=\"/url\">foo</a></p>\n",
-        "example": 185,
-        "start_line": 3103,
-        "end_line": 3110,
-        "section": "Link reference definitions"
+        markdown: "[foo]: /url\n===\n[foo]\n",
+        html: "<p>===\n<a href=\"/url\">foo</a></p>\n",
+        example: 185,
+        start_line: 3103,
+        end_line: 3110,
+        section: "Link reference definitions",
     },
     {
-        "markdown": "[foo]: /foo-url \"foo\"\n[bar]: /bar-url\n  \"bar\"\n[baz]: /baz-url\n\n[foo],\n[bar],\n[baz]\n",
-        "html": "<p><a href=\"/foo-url\" title=\"foo\">foo</a>,\n<a href=\"/bar-url\" title=\"bar\">bar</a>,\n<a href=\"/baz-url\">baz</a></p>\n",
-        "example": 186,
-        "start_line": 3116,
-        "end_line": 3129,
-        "section": "Link reference definitions"
+        markdown: "[foo]: /foo-url \"foo\"\n[bar]: /bar-url\n  \"bar\"\n[baz]: /baz-url\n\n[foo],\n[bar],\n[baz]\n",
+        html: "<p><a href=\"/foo-url\" title=\"foo\">foo</a>,\n<a href=\"/bar-url\" title=\"bar\">bar</a>,\n<a href=\"/baz-url\">baz</a></p>\n",
+        example: 186,
+        start_line: 3116,
+        end_line: 3129,
+        section: "Link reference definitions",
     },
     {
-        "markdown": "[foo]\n\n> [foo]: /url\n",
-        "html": "<p><a href=\"/url\">foo</a></p>\n<blockquote>\n</blockquote>\n",
-        "example": 187,
-        "start_line": 3137,
-        "end_line": 3145,
-        "section": "Link reference definitions"
+        markdown: "[foo]\n\n> [foo]: /url\n",
+        html: "<p><a href=\"/url\">foo</a></p>\n<blockquote>\n</blockquote>\n",
+        example: 187,
+        start_line: 3137,
+        end_line: 3145,
+        section: "Link reference definitions",
     },
     {
-        "markdown": "[foo]: /url\n",
-        "html": "",
-        "example": 188,
-        "start_line": 3154,
-        "end_line": 3157,
-        "section": "Link reference definitions"
+        markdown: "[foo]: /url\n",
+        html: "",
+        example: 188,
+        start_line: 3154,
+        end_line: 3157,
+        section: "Link reference definitions",
     },
     {
-        "markdown": "aaa\n\nbbb\n",
-        "html": "<p>aaa</p>\n<p>bbb</p>\n",
-        "example": 189,
-        "start_line": 3171,
-        "end_line": 3178,
-        "section": "Paragraphs"
+        markdown: "aaa\n\nbbb\n",
+        html: "<p>aaa</p>\n<p>bbb</p>\n",
+        example: 189,
+        start_line: 3171,
+        end_line: 3178,
+        section: "Paragraphs",
     },
     {
-        "markdown": "aaa\nbbb\n\nccc\nddd\n",
-        "html": "<p>aaa\nbbb</p>\n<p>ccc\nddd</p>\n",
-        "example": 190,
-        "start_line": 3183,
-        "end_line": 3194,
-        "section": "Paragraphs"
+        markdown: "aaa\nbbb\n\nccc\nddd\n",
+        html: "<p>aaa\nbbb</p>\n<p>ccc\nddd</p>\n",
+        example: 190,
+        start_line: 3183,
+        end_line: 3194,
+        section: "Paragraphs",
     },
     {
-        "markdown": "aaa\n\n\nbbb\n",
-        "html": "<p>aaa</p>\n<p>bbb</p>\n",
-        "example": 191,
-        "start_line": 3199,
-        "end_line": 3207,
-        "section": "Paragraphs"
+        markdown: "aaa\n\n\nbbb\n",
+        html: "<p>aaa</p>\n<p>bbb</p>\n",
+        example: 191,
+        start_line: 3199,
+        end_line: 3207,
+        section: "Paragraphs",
     },
     {
-        "markdown": "  aaa\n bbb\n",
-        "html": "<p>aaa\nbbb</p>\n",
-        "example": 192,
-        "start_line": 3212,
-        "end_line": 3218,
-        "section": "Paragraphs"
+        markdown: "  aaa\n bbb\n",
+        html: "<p>aaa\nbbb</p>\n",
+        example: 192,
+        start_line: 3212,
+        end_line: 3218,
+        section: "Paragraphs",
     },
     {
-        "markdown": "aaa\n             bbb\n                                       ccc\n",
-        "html": "<p>aaa\nbbb\nccc</p>\n",
-        "example": 193,
-        "start_line": 3224,
-        "end_line": 3232,
-        "section": "Paragraphs"
+        markdown: "aaa\n             bbb\n                                       ccc\n",
+        html: "<p>aaa\nbbb\nccc</p>\n",
+        example: 193,
+        start_line: 3224,
+        end_line: 3232,
+        section: "Paragraphs",
     },
     {
-        "markdown": "   aaa\nbbb\n",
-        "html": "<p>aaa\nbbb</p>\n",
-        "example": 194,
-        "start_line": 3238,
-        "end_line": 3244,
-        "section": "Paragraphs"
+        markdown: "   aaa\nbbb\n",
+        html: "<p>aaa\nbbb</p>\n",
+        example: 194,
+        start_line: 3238,
+        end_line: 3244,
+        section: "Paragraphs",
     },
     {
-        "markdown": "    aaa\nbbb\n",
-        "html": "<pre><code>aaa\n</code></pre>\n<p>bbb</p>\n",
-        "example": 195,
-        "start_line": 3247,
-        "end_line": 3254,
-        "section": "Paragraphs"
+        markdown: "    aaa\nbbb\n",
+        html: "<pre><code>aaa\n</code></pre>\n<p>bbb</p>\n",
+        example: 195,
+        start_line: 3247,
+        end_line: 3254,
+        section: "Paragraphs",
     },
     {
-        "markdown": "aaa     \nbbb     \n",
-        "html": "<p>aaa<br />\nbbb</p>\n",
-        "example": 196,
-        "start_line": 3261,
-        "end_line": 3267,
-        "section": "Paragraphs"
+        markdown: "aaa     \nbbb     \n",
+        html: "<p>aaa<br />\nbbb</p>\n",
+        example: 196,
+        start_line: 3261,
+        end_line: 3267,
+        section: "Paragraphs",
     },
     {
-        "markdown": "  \n\naaa\n  \n\n# aaa\n\n  \n",
-        "html": "<p>aaa</p>\n<h1>aaa</h1>\n",
-        "example": 197,
-        "start_line": 3278,
-        "end_line": 3290,
-        "section": "Blank lines"
+        markdown: "  \n\naaa\n  \n\n# aaa\n\n  \n",
+        html: "<p>aaa</p>\n<h1>aaa</h1>\n",
+        example: 197,
+        start_line: 3278,
+        end_line: 3290,
+        section: "Blank lines",
     },
     {
-        "markdown": "> # Foo\n> bar\n> baz\n",
-        "html": "<blockquote>\n<h1>Foo</h1>\n<p>bar\nbaz</p>\n</blockquote>\n",
-        "example": 198,
-        "start_line": 3344,
-        "end_line": 3354,
-        "section": "Block quotes"
+        markdown: "> # Foo\n> bar\n> baz\n",
+        html: "<blockquote>\n<h1>Foo</h1>\n<p>bar\nbaz</p>\n</blockquote>\n",
+        example: 198,
+        start_line: 3344,
+        end_line: 3354,
+        section: "Block quotes",
     },
     {
-        "markdown": "># Foo\n>bar\n> baz\n",
-        "html": "<blockquote>\n<h1>Foo</h1>\n<p>bar\nbaz</p>\n</blockquote>\n",
-        "example": 199,
-        "start_line": 3359,
-        "end_line": 3369,
-        "section": "Block quotes"
+        markdown: "># Foo\n>bar\n> baz\n",
+        html: "<blockquote>\n<h1>Foo</h1>\n<p>bar\nbaz</p>\n</blockquote>\n",
+        example: 199,
+        start_line: 3359,
+        end_line: 3369,
+        section: "Block quotes",
     },
     {
-        "markdown": "   > # Foo\n   > bar\n > baz\n",
-        "html": "<blockquote>\n<h1>Foo</h1>\n<p>bar\nbaz</p>\n</blockquote>\n",
-        "example": 200,
-        "start_line": 3374,
-        "end_line": 3384,
-        "section": "Block quotes"
+        markdown: "   > # Foo\n   > bar\n > baz\n",
+        html: "<blockquote>\n<h1>Foo</h1>\n<p>bar\nbaz</p>\n</blockquote>\n",
+        example: 200,
+        start_line: 3374,
+        end_line: 3384,
+        section: "Block quotes",
     },
     {
-        "markdown": "    > # Foo\n    > bar\n    > baz\n",
-        "html": "<pre><code>&gt; # Foo\n&gt; bar\n&gt; baz\n</code></pre>\n",
-        "example": 201,
-        "start_line": 3389,
-        "end_line": 3398,
-        "section": "Block quotes"
+        markdown: "    > # Foo\n    > bar\n    > baz\n",
+        html: "<pre><code>&gt; # Foo\n&gt; bar\n&gt; baz\n</code></pre>\n",
+        example: 201,
+        start_line: 3389,
+        end_line: 3398,
+        section: "Block quotes",
     },
     {
-        "markdown": "> # Foo\n> bar\nbaz\n",
-        "html": "<blockquote>\n<h1>Foo</h1>\n<p>bar\nbaz</p>\n</blockquote>\n",
-        "example": 202,
-        "start_line": 3404,
-        "end_line": 3414,
-        "section": "Block quotes"
+        markdown: "> # Foo\n> bar\nbaz\n",
+        html: "<blockquote>\n<h1>Foo</h1>\n<p>bar\nbaz</p>\n</blockquote>\n",
+        example: 202,
+        start_line: 3404,
+        end_line: 3414,
+        section: "Block quotes",
     },
     {
-        "markdown": "> bar\nbaz\n> foo\n",
-        "html": "<blockquote>\n<p>bar\nbaz\nfoo</p>\n</blockquote>\n",
-        "example": 203,
-        "start_line": 3420,
-        "end_line": 3430,
-        "section": "Block quotes"
+        markdown: "> bar\nbaz\n> foo\n",
+        html: "<blockquote>\n<p>bar\nbaz\nfoo</p>\n</blockquote>\n",
+        example: 203,
+        start_line: 3420,
+        end_line: 3430,
+        section: "Block quotes",
     },
     {
-        "markdown": "> foo\n---\n",
-        "html": "<blockquote>\n<p>foo</p>\n</blockquote>\n<hr />\n",
-        "example": 204,
-        "start_line": 3444,
-        "end_line": 3452,
-        "section": "Block quotes"
+        markdown: "> foo\n---\n",
+        html: "<blockquote>\n<p>foo</p>\n</blockquote>\n<hr />\n",
+        example: 204,
+        start_line: 3444,
+        end_line: 3452,
+        section: "Block quotes",
     },
     {
-        "markdown": "> - foo\n- bar\n",
-        "html": "<blockquote>\n<ul>\n<li>foo</li>\n</ul>\n</blockquote>\n<ul>\n<li>bar</li>\n</ul>\n",
-        "example": 205,
-        "start_line": 3464,
-        "end_line": 3476,
-        "section": "Block quotes"
+        markdown: "> - foo\n- bar\n",
+        html: "<blockquote>\n<ul>\n<li>foo</li>\n</ul>\n</blockquote>\n<ul>\n<li>bar</li>\n</ul>\n",
+        example: 205,
+        start_line: 3464,
+        end_line: 3476,
+        section: "Block quotes",
     },
     {
-        "markdown": ">     foo\n    bar\n",
-        "html": "<blockquote>\n<pre><code>foo\n</code></pre>\n</blockquote>\n<pre><code>bar\n</code></pre>\n",
-        "example": 206,
-        "start_line": 3482,
-        "end_line": 3492,
-        "section": "Block quotes"
+        markdown: ">     foo\n    bar\n",
+        html: "<blockquote>\n<pre><code>foo\n</code></pre>\n</blockquote>\n<pre><code>bar\n</code></pre>\n",
+        example: 206,
+        start_line: 3482,
+        end_line: 3492,
+        section: "Block quotes",
     },
     {
-        "markdown": "> ```\nfoo\n```\n",
-        "html": "<blockquote>\n<pre><code></code></pre>\n</blockquote>\n<p>foo</p>\n<pre><code></code></pre>\n",
-        "example": 207,
-        "start_line": 3495,
-        "end_line": 3505,
-        "section": "Block quotes"
+        markdown: "> ```\nfoo\n```\n",
+        html: "<blockquote>\n<pre><code></code></pre>\n</blockquote>\n<p>foo</p>\n<pre><code></code></pre>\n",
+        example: 207,
+        start_line: 3495,
+        end_line: 3505,
+        section: "Block quotes",
     },
     {
-        "markdown": "> foo\n    - bar\n",
-        "html": "<blockquote>\n<p>foo\n- bar</p>\n</blockquote>\n",
-        "example": 208,
-        "start_line": 3511,
-        "end_line": 3519,
-        "section": "Block quotes"
+        markdown: "> foo\n    - bar\n",
+        html: "<blockquote>\n<p>foo\n- bar</p>\n</blockquote>\n",
+        example: 208,
+        start_line: 3511,
+        end_line: 3519,
+        section: "Block quotes",
     },
     {
-        "markdown": ">\n",
-        "html": "<blockquote>\n</blockquote>\n",
-        "example": 209,
-        "start_line": 3535,
-        "end_line": 3540,
-        "section": "Block quotes"
+        markdown: ">\n",
+        html: "<blockquote>\n</blockquote>\n",
+        example: 209,
+        start_line: 3535,
+        end_line: 3540,
+        section: "Block quotes",
     },
     {
-        "markdown": ">\n>  \n> \n",
-        "html": "<blockquote>\n</blockquote>\n",
-        "example": 210,
-        "start_line": 3543,
-        "end_line": 3550,
-        "section": "Block quotes"
+        markdown: ">\n>  \n> \n",
+        html: "<blockquote>\n</blockquote>\n",
+        example: 210,
+        start_line: 3543,
+        end_line: 3550,
+        section: "Block quotes",
     },
     {
-        "markdown": ">\n> foo\n>  \n",
-        "html": "<blockquote>\n<p>foo</p>\n</blockquote>\n",
-        "example": 211,
-        "start_line": 3555,
-        "end_line": 3563,
-        "section": "Block quotes"
+        markdown: ">\n> foo\n>  \n",
+        html: "<blockquote>\n<p>foo</p>\n</blockquote>\n",
+        example: 211,
+        start_line: 3555,
+        end_line: 3563,
+        section: "Block quotes",
     },
     {
-        "markdown": "> foo\n\n> bar\n",
-        "html": "<blockquote>\n<p>foo</p>\n</blockquote>\n<blockquote>\n<p>bar</p>\n</blockquote>\n",
-        "example": 212,
-        "start_line": 3568,
-        "end_line": 3579,
-        "section": "Block quotes"
+        markdown: "> foo\n\n> bar\n",
+        html: "<blockquote>\n<p>foo</p>\n</blockquote>\n<blockquote>\n<p>bar</p>\n</blockquote>\n",
+        example: 212,
+        start_line: 3568,
+        end_line: 3579,
+        section: "Block quotes",
     },
     {
-        "markdown": "> foo\n> bar\n",
-        "html": "<blockquote>\n<p>foo\nbar</p>\n</blockquote>\n",
-        "example": 213,
-        "start_line": 3590,
-        "end_line": 3598,
-        "section": "Block quotes"
+        markdown: "> foo\n> bar\n",
+        html: "<blockquote>\n<p>foo\nbar</p>\n</blockquote>\n",
+        example: 213,
+        start_line: 3590,
+        end_line: 3598,
+        section: "Block quotes",
     },
     {
-        "markdown": "> foo\n>\n> bar\n",
-        "html": "<blockquote>\n<p>foo</p>\n<p>bar</p>\n</blockquote>\n",
-        "example": 214,
-        "start_line": 3603,
-        "end_line": 3612,
-        "section": "Block quotes"
+        markdown: "> foo\n>\n> bar\n",
+        html: "<blockquote>\n<p>foo</p>\n<p>bar</p>\n</blockquote>\n",
+        example: 214,
+        start_line: 3603,
+        end_line: 3612,
+        section: "Block quotes",
     },
     {
-        "markdown": "foo\n> bar\n",
-        "html": "<p>foo</p>\n<blockquote>\n<p>bar</p>\n</blockquote>\n",
-        "example": 215,
-        "start_line": 3617,
-        "end_line": 3625,
-        "section": "Block quotes"
+        markdown: "foo\n> bar\n",
+        html: "<p>foo</p>\n<blockquote>\n<p>bar</p>\n</blockquote>\n",
+        example: 215,
+        start_line: 3617,
+        end_line: 3625,
+        section: "Block quotes",
     },
     {
-        "markdown": "> aaa\n***\n> bbb\n",
-        "html": "<blockquote>\n<p>aaa</p>\n</blockquote>\n<hr />\n<blockquote>\n<p>bbb</p>\n</blockquote>\n",
-        "example": 216,
-        "start_line": 3631,
-        "end_line": 3643,
-        "section": "Block quotes"
+        markdown: "> aaa\n***\n> bbb\n",
+        html: "<blockquote>\n<p>aaa</p>\n</blockquote>\n<hr />\n<blockquote>\n<p>bbb</p>\n</blockquote>\n",
+        example: 216,
+        start_line: 3631,
+        end_line: 3643,
+        section: "Block quotes",
     },
     {
-        "markdown": "> bar\nbaz\n",
-        "html": "<blockquote>\n<p>bar\nbaz</p>\n</blockquote>\n",
-        "example": 217,
-        "start_line": 3649,
-        "end_line": 3657,
-        "section": "Block quotes"
+        markdown: "> bar\nbaz\n",
+        html: "<blockquote>\n<p>bar\nbaz</p>\n</blockquote>\n",
+        example: 217,
+        start_line: 3649,
+        end_line: 3657,
+        section: "Block quotes",
     },
     {
-        "markdown": "> bar\n\nbaz\n",
-        "html": "<blockquote>\n<p>bar</p>\n</blockquote>\n<p>baz</p>\n",
-        "example": 218,
-        "start_line": 3660,
-        "end_line": 3669,
-        "section": "Block quotes"
+        markdown: "> bar\n\nbaz\n",
+        html: "<blockquote>\n<p>bar</p>\n</blockquote>\n<p>baz</p>\n",
+        example: 218,
+        start_line: 3660,
+        end_line: 3669,
+        section: "Block quotes",
     },
     {
-        "markdown": "> bar\n>\nbaz\n",
-        "html": "<blockquote>\n<p>bar</p>\n</blockquote>\n<p>baz</p>\n",
-        "example": 219,
-        "start_line": 3672,
-        "end_line": 3681,
-        "section": "Block quotes"
+        markdown: "> bar\n>\nbaz\n",
+        html: "<blockquote>\n<p>bar</p>\n</blockquote>\n<p>baz</p>\n",
+        example: 219,
+        start_line: 3672,
+        end_line: 3681,
+        section: "Block quotes",
     },
     {
-        "markdown": "> > > foo\nbar\n",
-        "html": "<blockquote>\n<blockquote>\n<blockquote>\n<p>foo\nbar</p>\n</blockquote>\n</blockquote>\n</blockquote>\n",
-        "example": 220,
-        "start_line": 3688,
-        "end_line": 3700,
-        "section": "Block quotes"
+        markdown: "> > > foo\nbar\n",
+        html: "<blockquote>\n<blockquote>\n<blockquote>\n<p>foo\nbar</p>\n</blockquote>\n</blockquote>\n</blockquote>\n",
+        example: 220,
+        start_line: 3688,
+        end_line: 3700,
+        section: "Block quotes",
     },
     {
-        "markdown": ">>> foo\n> bar\n>>baz\n",
-        "html": "<blockquote>\n<blockquote>\n<blockquote>\n<p>foo\nbar\nbaz</p>\n</blockquote>\n</blockquote>\n</blockquote>\n",
-        "example": 221,
-        "start_line": 3703,
-        "end_line": 3717,
-        "section": "Block quotes"
+        markdown: ">>> foo\n> bar\n>>baz\n",
+        html: "<blockquote>\n<blockquote>\n<blockquote>\n<p>foo\nbar\nbaz</p>\n</blockquote>\n</blockquote>\n</blockquote>\n",
+        example: 221,
+        start_line: 3703,
+        end_line: 3717,
+        section: "Block quotes",
     },
     {
-        "markdown": ">     code\n\n>    not code\n",
-        "html": "<blockquote>\n<pre><code>code\n</code></pre>\n</blockquote>\n<blockquote>\n<p>not code</p>\n</blockquote>\n",
-        "example": 222,
-        "start_line": 3725,
-        "end_line": 3737,
-        "section": "Block quotes"
+        markdown: ">     code\n\n>    not code\n",
+        html: "<blockquote>\n<pre><code>code\n</code></pre>\n</blockquote>\n<blockquote>\n<p>not code</p>\n</blockquote>\n",
+        example: 222,
+        start_line: 3725,
+        end_line: 3737,
+        section: "Block quotes",
     },
     {
-        "markdown": "A paragraph\nwith two lines.\n\n    indented code\n\n> A block quote.\n",
-        "html": "<p>A paragraph\nwith two lines.</p>\n<pre><code>indented code\n</code></pre>\n<blockquote>\n<p>A block quote.</p>\n</blockquote>\n",
-        "example": 223,
-        "start_line": 3779,
-        "end_line": 3794,
-        "section": "List items"
+        markdown: "A paragraph\nwith two lines.\n\n    indented code\n\n> A block quote.\n",
+        html: "<p>A paragraph\nwith two lines.</p>\n<pre><code>indented code\n</code></pre>\n<blockquote>\n<p>A block quote.</p>\n</blockquote>\n",
+        example: 223,
+        start_line: 3779,
+        end_line: 3794,
+        section: "List items",
     },
     {
-        "markdown": "1.  A paragraph\n    with two lines.\n\n        indented code\n\n    > A block quote.\n",
-        "html": "<ol>\n<li>\n<p>A paragraph\nwith two lines.</p>\n<pre><code>indented code\n</code></pre>\n<blockquote>\n<p>A block quote.</p>\n</blockquote>\n</li>\n</ol>\n",
-        "example": 224,
-        "start_line": 3801,
-        "end_line": 3820,
-        "section": "List items"
+        markdown: "1.  A paragraph\n    with two lines.\n\n        indented code\n\n    > A block quote.\n",
+        html: "<ol>\n<li>\n<p>A paragraph\nwith two lines.</p>\n<pre><code>indented code\n</code></pre>\n<blockquote>\n<p>A block quote.</p>\n</blockquote>\n</li>\n</ol>\n",
+        example: 224,
+        start_line: 3801,
+        end_line: 3820,
+        section: "List items",
     },
     {
-        "markdown": "- one\n\n two\n",
-        "html": "<ul>\n<li>one</li>\n</ul>\n<p>two</p>\n",
-        "example": 225,
-        "start_line": 3834,
-        "end_line": 3843,
-        "section": "List items"
+        markdown: "- one\n\n two\n",
+        html: "<ul>\n<li>one</li>\n</ul>\n<p>two</p>\n",
+        example: 225,
+        start_line: 3834,
+        end_line: 3843,
+        section: "List items",
     },
     {
-        "markdown": "- one\n\n  two\n",
-        "html": "<ul>\n<li>\n<p>one</p>\n<p>two</p>\n</li>\n</ul>\n",
-        "example": 226,
-        "start_line": 3846,
-        "end_line": 3857,
-        "section": "List items"
+        markdown: "- one\n\n  two\n",
+        html: "<ul>\n<li>\n<p>one</p>\n<p>two</p>\n</li>\n</ul>\n",
+        example: 226,
+        start_line: 3846,
+        end_line: 3857,
+        section: "List items",
     },
     {
-        "markdown": " -    one\n\n     two\n",
-        "html": "<ul>\n<li>one</li>\n</ul>\n<pre><code> two\n</code></pre>\n",
-        "example": 227,
-        "start_line": 3860,
-        "end_line": 3870,
-        "section": "List items"
+        markdown: " -    one\n\n     two\n",
+        html: "<ul>\n<li>one</li>\n</ul>\n<pre><code> two\n</code></pre>\n",
+        example: 227,
+        start_line: 3860,
+        end_line: 3870,
+        section: "List items",
     },
     {
-        "markdown": " -    one\n\n      two\n",
-        "html": "<ul>\n<li>\n<p>one</p>\n<p>two</p>\n</li>\n</ul>\n",
-        "example": 228,
-        "start_line": 3873,
-        "end_line": 3884,
-        "section": "List items"
+        markdown: " -    one\n\n      two\n",
+        html: "<ul>\n<li>\n<p>one</p>\n<p>two</p>\n</li>\n</ul>\n",
+        example: 228,
+        start_line: 3873,
+        end_line: 3884,
+        section: "List items",
     },
     {
-        "markdown": "   > > 1.  one\n>>\n>>     two\n",
-        "html": "<blockquote>\n<blockquote>\n<ol>\n<li>\n<p>one</p>\n<p>two</p>\n</li>\n</ol>\n</blockquote>\n</blockquote>\n",
-        "example": 229,
-        "start_line": 3895,
-        "end_line": 3910,
-        "section": "List items"
+        markdown: "   > > 1.  one\n>>\n>>     two\n",
+        html: "<blockquote>\n<blockquote>\n<ol>\n<li>\n<p>one</p>\n<p>two</p>\n</li>\n</ol>\n</blockquote>\n</blockquote>\n",
+        example: 229,
+        start_line: 3895,
+        end_line: 3910,
+        section: "List items",
     },
     {
-        "markdown": ">>- one\n>>\n  >  > two\n",
-        "html": "<blockquote>\n<blockquote>\n<ul>\n<li>one</li>\n</ul>\n<p>two</p>\n</blockquote>\n</blockquote>\n",
-        "example": 230,
-        "start_line": 3922,
-        "end_line": 3935,
-        "section": "List items"
+        markdown: ">>- one\n>>\n  >  > two\n",
+        html: "<blockquote>\n<blockquote>\n<ul>\n<li>one</li>\n</ul>\n<p>two</p>\n</blockquote>\n</blockquote>\n",
+        example: 230,
+        start_line: 3922,
+        end_line: 3935,
+        section: "List items",
     },
     {
-        "markdown": "-one\n\n2.two\n",
-        "html": "<p>-one</p>\n<p>2.two</p>\n",
-        "example": 231,
-        "start_line": 3941,
-        "end_line": 3948,
-        "section": "List items"
+        markdown: "-one\n\n2.two\n",
+        html: "<p>-one</p>\n<p>2.two</p>\n",
+        example: 231,
+        start_line: 3941,
+        end_line: 3948,
+        section: "List items",
     },
     {
-        "markdown": "- foo\n\n\n  bar\n",
-        "html": "<ul>\n<li>\n<p>foo</p>\n<p>bar</p>\n</li>\n</ul>\n",
-        "example": 232,
-        "start_line": 3954,
-        "end_line": 3966,
-        "section": "List items"
+        markdown: "- foo\n\n\n  bar\n",
+        html: "<ul>\n<li>\n<p>foo</p>\n<p>bar</p>\n</li>\n</ul>\n",
+        example: 232,
+        start_line: 3954,
+        end_line: 3966,
+        section: "List items",
     },
     {
-        "markdown": "1.  foo\n\n    ```\n    bar\n    ```\n\n    baz\n\n    > bam\n",
-        "html": "<ol>\n<li>\n<p>foo</p>\n<pre><code>bar\n</code></pre>\n<p>baz</p>\n<blockquote>\n<p>bam</p>\n</blockquote>\n</li>\n</ol>\n",
-        "example": 233,
-        "start_line": 3971,
-        "end_line": 3993,
-        "section": "List items"
+        markdown: "1.  foo\n\n    ```\n    bar\n    ```\n\n    baz\n\n    > bam\n",
+        html: "<ol>\n<li>\n<p>foo</p>\n<pre><code>bar\n</code></pre>\n<p>baz</p>\n<blockquote>\n<p>bam</p>\n</blockquote>\n</li>\n</ol>\n",
+        example: 233,
+        start_line: 3971,
+        end_line: 3993,
+        section: "List items",
     },
     {
-        "markdown": "- Foo\n\n      bar\n\n\n      baz\n",
-        "html": "<ul>\n<li>\n<p>Foo</p>\n<pre><code>bar\n\n\nbaz\n</code></pre>\n</li>\n</ul>\n",
-        "example": 234,
-        "start_line": 3999,
-        "end_line": 4017,
-        "section": "List items"
+        markdown: "- Foo\n\n      bar\n\n\n      baz\n",
+        html: "<ul>\n<li>\n<p>Foo</p>\n<pre><code>bar\n\n\nbaz\n</code></pre>\n</li>\n</ul>\n",
+        example: 234,
+        start_line: 3999,
+        end_line: 4017,
+        section: "List items",
     },
     {
-        "markdown": "123456789. ok\n",
-        "html": "<ol start=\"123456789\">\n<li>ok</li>\n</ol>\n",
-        "example": 235,
-        "start_line": 4021,
-        "end_line": 4027,
-        "section": "List items"
+        markdown: "123456789. ok\n",
+        html: "<ol start=\"123456789\">\n<li>ok</li>\n</ol>\n",
+        example: 235,
+        start_line: 4021,
+        end_line: 4027,
+        section: "List items",
     },
     {
-        "markdown": "1234567890. not ok\n",
-        "html": "<p>1234567890. not ok</p>\n",
-        "example": 236,
-        "start_line": 4030,
-        "end_line": 4034,
-        "section": "List items"
+        markdown: "1234567890. not ok\n",
+        html: "<p>1234567890. not ok</p>\n",
+        example: 236,
+        start_line: 4030,
+        end_line: 4034,
+        section: "List items",
     },
     {
-        "markdown": "0. ok\n",
-        "html": "<ol start=\"0\">\n<li>ok</li>\n</ol>\n",
-        "example": 237,
-        "start_line": 4039,
-        "end_line": 4045,
-        "section": "List items"
+        markdown: "0. ok\n",
+        html: "<ol start=\"0\">\n<li>ok</li>\n</ol>\n",
+        example: 237,
+        start_line: 4039,
+        end_line: 4045,
+        section: "List items",
     },
     {
-        "markdown": "003. ok\n",
-        "html": "<ol start=\"3\">\n<li>ok</li>\n</ol>\n",
-        "example": 238,
-        "start_line": 4048,
-        "end_line": 4054,
-        "section": "List items"
+        markdown: "003. ok\n",
+        html: "<ol start=\"3\">\n<li>ok</li>\n</ol>\n",
+        example: 238,
+        start_line: 4048,
+        end_line: 4054,
+        section: "List items",
     },
     {
-        "markdown": "-1. not ok\n",
-        "html": "<p>-1. not ok</p>\n",
-        "example": 239,
-        "start_line": 4059,
-        "end_line": 4063,
-        "section": "List items"
+        markdown: "-1. not ok\n",
+        html: "<p>-1. not ok</p>\n",
+        example: 239,
+        start_line: 4059,
+        end_line: 4063,
+        section: "List items",
     },
     {
-        "markdown": "- foo\n\n      bar\n",
-        "html": "<ul>\n<li>\n<p>foo</p>\n<pre><code>bar\n</code></pre>\n</li>\n</ul>\n",
-        "example": 240,
-        "start_line": 4082,
-        "end_line": 4094,
-        "section": "List items"
+        markdown: "- foo\n\n      bar\n",
+        html: "<ul>\n<li>\n<p>foo</p>\n<pre><code>bar\n</code></pre>\n</li>\n</ul>\n",
+        example: 240,
+        start_line: 4082,
+        end_line: 4094,
+        section: "List items",
     },
     {
-        "markdown": "  10.  foo\n\n           bar\n",
-        "html": "<ol start=\"10\">\n<li>\n<p>foo</p>\n<pre><code>bar\n</code></pre>\n</li>\n</ol>\n",
-        "example": 241,
-        "start_line": 4099,
-        "end_line": 4111,
-        "section": "List items"
+        markdown: "  10.  foo\n\n           bar\n",
+        html: "<ol start=\"10\">\n<li>\n<p>foo</p>\n<pre><code>bar\n</code></pre>\n</li>\n</ol>\n",
+        example: 241,
+        start_line: 4099,
+        end_line: 4111,
+        section: "List items",
     },
     {
-        "markdown": "    indented code\n\nparagraph\n\n    more code\n",
-        "html": "<pre><code>indented code\n</code></pre>\n<p>paragraph</p>\n<pre><code>more code\n</code></pre>\n",
-        "example": 242,
-        "start_line": 4118,
-        "end_line": 4130,
-        "section": "List items"
+        markdown: "    indented code\n\nparagraph\n\n    more code\n",
+        html: "<pre><code>indented code\n</code></pre>\n<p>paragraph</p>\n<pre><code>more code\n</code></pre>\n",
+        example: 242,
+        start_line: 4118,
+        end_line: 4130,
+        section: "List items",
     },
     {
-        "markdown": "1.     indented code\n\n   paragraph\n\n       more code\n",
-        "html": "<ol>\n<li>\n<pre><code>indented code\n</code></pre>\n<p>paragraph</p>\n<pre><code>more code\n</code></pre>\n</li>\n</ol>\n",
-        "example": 243,
-        "start_line": 4133,
-        "end_line": 4149,
-        "section": "List items"
+        markdown: "1.     indented code\n\n   paragraph\n\n       more code\n",
+        html: "<ol>\n<li>\n<pre><code>indented code\n</code></pre>\n<p>paragraph</p>\n<pre><code>more code\n</code></pre>\n</li>\n</ol>\n",
+        example: 243,
+        start_line: 4133,
+        end_line: 4149,
+        section: "List items",
     },
     {
-        "markdown": "1.      indented code\n\n   paragraph\n\n       more code\n",
-        "html": "<ol>\n<li>\n<pre><code> indented code\n</code></pre>\n<p>paragraph</p>\n<pre><code>more code\n</code></pre>\n</li>\n</ol>\n",
-        "example": 244,
-        "start_line": 4155,
-        "end_line": 4171,
-        "section": "List items"
+        markdown: "1.      indented code\n\n   paragraph\n\n       more code\n",
+        html: "<ol>\n<li>\n<pre><code> indented code\n</code></pre>\n<p>paragraph</p>\n<pre><code>more code\n</code></pre>\n</li>\n</ol>\n",
+        example: 244,
+        start_line: 4155,
+        end_line: 4171,
+        section: "List items",
     },
     {
-        "markdown": "   foo\n\nbar\n",
-        "html": "<p>foo</p>\n<p>bar</p>\n",
-        "example": 245,
-        "start_line": 4182,
-        "end_line": 4189,
-        "section": "List items"
+        markdown: "   foo\n\nbar\n",
+        html: "<p>foo</p>\n<p>bar</p>\n",
+        example: 245,
+        start_line: 4182,
+        end_line: 4189,
+        section: "List items",
     },
     {
-        "markdown": "-    foo\n\n  bar\n",
-        "html": "<ul>\n<li>foo</li>\n</ul>\n<p>bar</p>\n",
-        "example": 246,
-        "start_line": 4192,
-        "end_line": 4201,
-        "section": "List items"
+        markdown: "-    foo\n\n  bar\n",
+        html: "<ul>\n<li>foo</li>\n</ul>\n<p>bar</p>\n",
+        example: 246,
+        start_line: 4192,
+        end_line: 4201,
+        section: "List items",
     },
     {
-        "markdown": "-  foo\n\n   bar\n",
-        "html": "<ul>\n<li>\n<p>foo</p>\n<p>bar</p>\n</li>\n</ul>\n",
-        "example": 247,
-        "start_line": 4209,
-        "end_line": 4220,
-        "section": "List items"
+        markdown: "-  foo\n\n   bar\n",
+        html: "<ul>\n<li>\n<p>foo</p>\n<p>bar</p>\n</li>\n</ul>\n",
+        example: 247,
+        start_line: 4209,
+        end_line: 4220,
+        section: "List items",
     },
     {
-        "markdown": "-\n  foo\n-\n  ```\n  bar\n  ```\n-\n      baz\n",
-        "html": "<ul>\n<li>foo</li>\n<li>\n<pre><code>bar\n</code></pre>\n</li>\n<li>\n<pre><code>baz\n</code></pre>\n</li>\n</ul>\n",
-        "example": 248,
-        "start_line": 4237,
-        "end_line": 4258,
-        "section": "List items"
+        markdown: "-\n  foo\n-\n  ```\n  bar\n  ```\n-\n      baz\n",
+        html: "<ul>\n<li>foo</li>\n<li>\n<pre><code>bar\n</code></pre>\n</li>\n<li>\n<pre><code>baz\n</code></pre>\n</li>\n</ul>\n",
+        example: 248,
+        start_line: 4237,
+        end_line: 4258,
+        section: "List items",
     },
     {
-        "markdown": "-   \n  foo\n",
-        "html": "<ul>\n<li>foo</li>\n</ul>\n",
-        "example": 249,
-        "start_line": 4263,
-        "end_line": 4270,
-        "section": "List items"
+        markdown: "-   \n  foo\n",
+        html: "<ul>\n<li>foo</li>\n</ul>\n",
+        example: 249,
+        start_line: 4263,
+        end_line: 4270,
+        section: "List items",
     },
     {
-        "markdown": "-\n\n  foo\n",
-        "html": "<ul>\n<li></li>\n</ul>\n<p>foo</p>\n",
-        "example": 250,
-        "start_line": 4277,
-        "end_line": 4286,
-        "section": "List items"
+        markdown: "-\n\n  foo\n",
+        html: "<ul>\n<li></li>\n</ul>\n<p>foo</p>\n",
+        example: 250,
+        start_line: 4277,
+        end_line: 4286,
+        section: "List items",
     },
     {
-        "markdown": "- foo\n-\n- bar\n",
-        "html": "<ul>\n<li>foo</li>\n<li></li>\n<li>bar</li>\n</ul>\n",
-        "example": 251,
-        "start_line": 4291,
-        "end_line": 4301,
-        "section": "List items"
+        markdown: "- foo\n-\n- bar\n",
+        html: "<ul>\n<li>foo</li>\n<li></li>\n<li>bar</li>\n</ul>\n",
+        example: 251,
+        start_line: 4291,
+        end_line: 4301,
+        section: "List items",
     },
     {
-        "markdown": "- foo\n-   \n- bar\n",
-        "html": "<ul>\n<li>foo</li>\n<li></li>\n<li>bar</li>\n</ul>\n",
-        "example": 252,
-        "start_line": 4306,
-        "end_line": 4316,
-        "section": "List items"
+        markdown: "- foo\n-   \n- bar\n",
+        html: "<ul>\n<li>foo</li>\n<li></li>\n<li>bar</li>\n</ul>\n",
+        example: 252,
+        start_line: 4306,
+        end_line: 4316,
+        section: "List items",
     },
     {
-        "markdown": "1. foo\n2.\n3. bar\n",
-        "html": "<ol>\n<li>foo</li>\n<li></li>\n<li>bar</li>\n</ol>\n",
-        "example": 253,
-        "start_line": 4321,
-        "end_line": 4331,
-        "section": "List items"
+        markdown: "1. foo\n2.\n3. bar\n",
+        html: "<ol>\n<li>foo</li>\n<li></li>\n<li>bar</li>\n</ol>\n",
+        example: 253,
+        start_line: 4321,
+        end_line: 4331,
+        section: "List items",
     },
     {
-        "markdown": "*\n",
-        "html": "<ul>\n<li></li>\n</ul>\n",
-        "example": 254,
-        "start_line": 4336,
-        "end_line": 4342,
-        "section": "List items"
+        markdown: "*\n",
+        html: "<ul>\n<li></li>\n</ul>\n",
+        example: 254,
+        start_line: 4336,
+        end_line: 4342,
+        section: "List items",
     },
     {
-        "markdown": "foo\n*\n\nfoo\n1.\n",
-        "html": "<p>foo\n*</p>\n<p>foo\n1.</p>\n",
-        "example": 255,
-        "start_line": 4346,
-        "end_line": 4357,
-        "section": "List items"
+        markdown: "foo\n*\n\nfoo\n1.\n",
+        html: "<p>foo\n*</p>\n<p>foo\n1.</p>\n",
+        example: 255,
+        start_line: 4346,
+        end_line: 4357,
+        section: "List items",
     },
     {
-        "markdown": " 1.  A paragraph\n     with two lines.\n\n         indented code\n\n     > A block quote.\n",
-        "html": "<ol>\n<li>\n<p>A paragraph\nwith two lines.</p>\n<pre><code>indented code\n</code></pre>\n<blockquote>\n<p>A block quote.</p>\n</blockquote>\n</li>\n</ol>\n",
-        "example": 256,
-        "start_line": 4368,
-        "end_line": 4387,
-        "section": "List items"
+        markdown: " 1.  A paragraph\n     with two lines.\n\n         indented code\n\n     > A block quote.\n",
+        html: "<ol>\n<li>\n<p>A paragraph\nwith two lines.</p>\n<pre><code>indented code\n</code></pre>\n<blockquote>\n<p>A block quote.</p>\n</blockquote>\n</li>\n</ol>\n",
+        example: 256,
+        start_line: 4368,
+        end_line: 4387,
+        section: "List items",
     },
     {
-        "markdown": "  1.  A paragraph\n      with two lines.\n\n          indented code\n\n      > A block quote.\n",
-        "html": "<ol>\n<li>\n<p>A paragraph\nwith two lines.</p>\n<pre><code>indented code\n</code></pre>\n<blockquote>\n<p>A block quote.</p>\n</blockquote>\n</li>\n</ol>\n",
-        "example": 257,
-        "start_line": 4392,
-        "end_line": 4411,
-        "section": "List items"
+        markdown: "  1.  A paragraph\n      with two lines.\n\n          indented code\n\n      > A block quote.\n",
+        html: "<ol>\n<li>\n<p>A paragraph\nwith two lines.</p>\n<pre><code>indented code\n</code></pre>\n<blockquote>\n<p>A block quote.</p>\n</blockquote>\n</li>\n</ol>\n",
+        example: 257,
+        start_line: 4392,
+        end_line: 4411,
+        section: "List items",
     },
     {
-        "markdown": "   1.  A paragraph\n       with two lines.\n\n           indented code\n\n       > A block quote.\n",
-        "html": "<ol>\n<li>\n<p>A paragraph\nwith two lines.</p>\n<pre><code>indented code\n</code></pre>\n<blockquote>\n<p>A block quote.</p>\n</blockquote>\n</li>\n</ol>\n",
-        "example": 258,
-        "start_line": 4416,
-        "end_line": 4435,
-        "section": "List items"
+        markdown: "   1.  A paragraph\n       with two lines.\n\n           indented code\n\n       > A block quote.\n",
+        html: "<ol>\n<li>\n<p>A paragraph\nwith two lines.</p>\n<pre><code>indented code\n</code></pre>\n<blockquote>\n<p>A block quote.</p>\n</blockquote>\n</li>\n</ol>\n",
+        example: 258,
+        start_line: 4416,
+        end_line: 4435,
+        section: "List items",
     },
     {
-        "markdown": "    1.  A paragraph\n        with two lines.\n\n            indented code\n\n        > A block quote.\n",
-        "html": "<pre><code>1.  A paragraph\n    with two lines.\n\n        indented code\n\n    &gt; A block quote.\n</code></pre>\n",
-        "example": 259,
-        "start_line": 4440,
-        "end_line": 4455,
-        "section": "List items"
+        markdown: "    1.  A paragraph\n        with two lines.\n\n            indented code\n\n        > A block quote.\n",
+        html: "<pre><code>1.  A paragraph\n    with two lines.\n\n        indented code\n\n    &gt; A block quote.\n</code></pre>\n",
+        example: 259,
+        start_line: 4440,
+        end_line: 4455,
+        section: "List items",
     },
     {
-        "markdown": "  1.  A paragraph\nwith two lines.\n\n          indented code\n\n      > A block quote.\n",
-        "html": "<ol>\n<li>\n<p>A paragraph\nwith two lines.</p>\n<pre><code>indented code\n</code></pre>\n<blockquote>\n<p>A block quote.</p>\n</blockquote>\n</li>\n</ol>\n",
-        "example": 260,
-        "start_line": 4470,
-        "end_line": 4489,
-        "section": "List items"
+        markdown: "  1.  A paragraph\nwith two lines.\n\n          indented code\n\n      > A block quote.\n",
+        html: "<ol>\n<li>\n<p>A paragraph\nwith two lines.</p>\n<pre><code>indented code\n</code></pre>\n<blockquote>\n<p>A block quote.</p>\n</blockquote>\n</li>\n</ol>\n",
+        example: 260,
+        start_line: 4470,
+        end_line: 4489,
+        section: "List items",
     },
     {
-        "markdown": "  1.  A paragraph\n    with two lines.\n",
-        "html": "<ol>\n<li>A paragraph\nwith two lines.</li>\n</ol>\n",
-        "example": 261,
-        "start_line": 4494,
-        "end_line": 4502,
-        "section": "List items"
+        markdown: "  1.  A paragraph\n    with two lines.\n",
+        html: "<ol>\n<li>A paragraph\nwith two lines.</li>\n</ol>\n",
+        example: 261,
+        start_line: 4494,
+        end_line: 4502,
+        section: "List items",
     },
     {
-        "markdown": "> 1. > Blockquote\ncontinued here.\n",
-        "html": "<blockquote>\n<ol>\n<li>\n<blockquote>\n<p>Blockquote\ncontinued here.</p>\n</blockquote>\n</li>\n</ol>\n</blockquote>\n",
-        "example": 262,
-        "start_line": 4507,
-        "end_line": 4521,
-        "section": "List items"
+        markdown: "> 1. > Blockquote\ncontinued here.\n",
+        html: "<blockquote>\n<ol>\n<li>\n<blockquote>\n<p>Blockquote\ncontinued here.</p>\n</blockquote>\n</li>\n</ol>\n</blockquote>\n",
+        example: 262,
+        start_line: 4507,
+        end_line: 4521,
+        section: "List items",
     },
     {
-        "markdown": "> 1. > Blockquote\n> continued here.\n",
-        "html": "<blockquote>\n<ol>\n<li>\n<blockquote>\n<p>Blockquote\ncontinued here.</p>\n</blockquote>\n</li>\n</ol>\n</blockquote>\n",
-        "example": 263,
-        "start_line": 4524,
-        "end_line": 4538,
-        "section": "List items"
+        markdown: "> 1. > Blockquote\n> continued here.\n",
+        html: "<blockquote>\n<ol>\n<li>\n<blockquote>\n<p>Blockquote\ncontinued here.</p>\n</blockquote>\n</li>\n</ol>\n</blockquote>\n",
+        example: 263,
+        start_line: 4524,
+        end_line: 4538,
+        section: "List items",
     },
     {
-        "markdown": "- foo\n  - bar\n    - baz\n      - boo\n",
-        "html": "<ul>\n<li>foo\n<ul>\n<li>bar\n<ul>\n<li>baz\n<ul>\n<li>boo</li>\n</ul>\n</li>\n</ul>\n</li>\n</ul>\n</li>\n</ul>\n",
-        "example": 264,
-        "start_line": 4552,
-        "end_line": 4573,
-        "section": "List items"
+        markdown: "- foo\n  - bar\n    - baz\n      - boo\n",
+        html: "<ul>\n<li>foo\n<ul>\n<li>bar\n<ul>\n<li>baz\n<ul>\n<li>boo</li>\n</ul>\n</li>\n</ul>\n</li>\n</ul>\n</li>\n</ul>\n",
+        example: 264,
+        start_line: 4552,
+        end_line: 4573,
+        section: "List items",
     },
     {
-        "markdown": "- foo\n - bar\n  - baz\n   - boo\n",
-        "html": "<ul>\n<li>foo</li>\n<li>bar</li>\n<li>baz</li>\n<li>boo</li>\n</ul>\n",
-        "example": 265,
-        "start_line": 4578,
-        "end_line": 4590,
-        "section": "List items"
+        markdown: "- foo\n - bar\n  - baz\n   - boo\n",
+        html: "<ul>\n<li>foo</li>\n<li>bar</li>\n<li>baz</li>\n<li>boo</li>\n</ul>\n",
+        example: 265,
+        start_line: 4578,
+        end_line: 4590,
+        section: "List items",
     },
     {
-        "markdown": "10) foo\n    - bar\n",
-        "html": "<ol start=\"10\">\n<li>foo\n<ul>\n<li>bar</li>\n</ul>\n</li>\n</ol>\n",
-        "example": 266,
-        "start_line": 4595,
-        "end_line": 4606,
-        "section": "List items"
+        markdown: "10) foo\n    - bar\n",
+        html: "<ol start=\"10\">\n<li>foo\n<ul>\n<li>bar</li>\n</ul>\n</li>\n</ol>\n",
+        example: 266,
+        start_line: 4595,
+        end_line: 4606,
+        section: "List items",
     },
     {
-        "markdown": "10) foo\n   - bar\n",
-        "html": "<ol start=\"10\">\n<li>foo</li>\n</ol>\n<ul>\n<li>bar</li>\n</ul>\n",
-        "example": 267,
-        "start_line": 4611,
-        "end_line": 4621,
-        "section": "List items"
+        markdown: "10) foo\n   - bar\n",
+        html: "<ol start=\"10\">\n<li>foo</li>\n</ol>\n<ul>\n<li>bar</li>\n</ul>\n",
+        example: 267,
+        start_line: 4611,
+        end_line: 4621,
+        section: "List items",
     },
     {
-        "markdown": "- - foo\n",
-        "html": "<ul>\n<li>\n<ul>\n<li>foo</li>\n</ul>\n</li>\n</ul>\n",
-        "example": 268,
-        "start_line": 4626,
-        "end_line": 4636,
-        "section": "List items"
+        markdown: "- - foo\n",
+        html: "<ul>\n<li>\n<ul>\n<li>foo</li>\n</ul>\n</li>\n</ul>\n",
+        example: 268,
+        start_line: 4626,
+        end_line: 4636,
+        section: "List items",
     },
     {
-        "markdown": "1. - 2. foo\n",
-        "html": "<ol>\n<li>\n<ul>\n<li>\n<ol start=\"2\">\n<li>foo</li>\n</ol>\n</li>\n</ul>\n</li>\n</ol>\n",
-        "example": 269,
-        "start_line": 4639,
-        "end_line": 4653,
-        "section": "List items"
+        markdown: "1. - 2. foo\n",
+        html: "<ol>\n<li>\n<ul>\n<li>\n<ol start=\"2\">\n<li>foo</li>\n</ol>\n</li>\n</ul>\n</li>\n</ol>\n",
+        example: 269,
+        start_line: 4639,
+        end_line: 4653,
+        section: "List items",
     },
     {
-        "markdown": "- # Foo\n- Bar\n  ---\n  baz\n",
-        "html": "<ul>\n<li>\n<h1>Foo</h1>\n</li>\n<li>\n<h2>Bar</h2>\nbaz</li>\n</ul>\n",
-        "example": 270,
-        "start_line": 4658,
-        "end_line": 4672,
-        "section": "List items"
+        markdown: "- # Foo\n- Bar\n  ---\n  baz\n",
+        html: "<ul>\n<li>\n<h1>Foo</h1>\n</li>\n<li>\n<h2>Bar</h2>\nbaz</li>\n</ul>\n",
+        example: 270,
+        start_line: 4658,
+        end_line: 4672,
+        section: "List items",
     },
     {
-        "markdown": "- foo\n- bar\n+ baz\n",
-        "html": "<ul>\n<li>foo</li>\n<li>bar</li>\n</ul>\n<ul>\n<li>baz</li>\n</ul>\n",
-        "example": 271,
-        "start_line": 4894,
-        "end_line": 4906,
-        "section": "Lists"
+        markdown: "- foo\n- bar\n+ baz\n",
+        html: "<ul>\n<li>foo</li>\n<li>bar</li>\n</ul>\n<ul>\n<li>baz</li>\n</ul>\n",
+        example: 271,
+        start_line: 4894,
+        end_line: 4906,
+        section: "Lists",
     },
     {
-        "markdown": "1. foo\n2. bar\n3) baz\n",
-        "html": "<ol>\n<li>foo</li>\n<li>bar</li>\n</ol>\n<ol start=\"3\">\n<li>baz</li>\n</ol>\n",
-        "example": 272,
-        "start_line": 4909,
-        "end_line": 4921,
-        "section": "Lists"
+        markdown: "1. foo\n2. bar\n3) baz\n",
+        html: "<ol>\n<li>foo</li>\n<li>bar</li>\n</ol>\n<ol start=\"3\">\n<li>baz</li>\n</ol>\n",
+        example: 272,
+        start_line: 4909,
+        end_line: 4921,
+        section: "Lists",
     },
     {
-        "markdown": "Foo\n- bar\n- baz\n",
-        "html": "<p>Foo</p>\n<ul>\n<li>bar</li>\n<li>baz</li>\n</ul>\n",
-        "example": 273,
-        "start_line": 4928,
-        "end_line": 4938,
-        "section": "Lists"
+        markdown: "Foo\n- bar\n- baz\n",
+        html: "<p>Foo</p>\n<ul>\n<li>bar</li>\n<li>baz</li>\n</ul>\n",
+        example: 273,
+        start_line: 4928,
+        end_line: 4938,
+        section: "Lists",
     },
     {
-        "markdown": "The number of windows in my house is\n14.  The number of doors is 6.\n",
-        "html": "<p>The number of windows in my house is\n14.  The number of doors is 6.</p>\n",
-        "example": 274,
-        "start_line": 5005,
-        "end_line": 5011,
-        "section": "Lists"
+        markdown: "The number of windows in my house is\n14.  The number of doors is 6.\n",
+        html: "<p>The number of windows in my house is\n14.  The number of doors is 6.</p>\n",
+        example: 274,
+        start_line: 5005,
+        end_line: 5011,
+        section: "Lists",
     },
     {
-        "markdown": "The number of windows in my house is\n1.  The number of doors is 6.\n",
-        "html": "<p>The number of windows in my house is</p>\n<ol>\n<li>The number of doors is 6.</li>\n</ol>\n",
-        "example": 275,
-        "start_line": 5015,
-        "end_line": 5023,
-        "section": "Lists"
+        markdown: "The number of windows in my house is\n1.  The number of doors is 6.\n",
+        html: "<p>The number of windows in my house is</p>\n<ol>\n<li>The number of doors is 6.</li>\n</ol>\n",
+        example: 275,
+        start_line: 5015,
+        end_line: 5023,
+        section: "Lists",
     },
     {
-        "markdown": "- foo\n\n- bar\n\n\n- baz\n",
-        "html": "<ul>\n<li>\n<p>foo</p>\n</li>\n<li>\n<p>bar</p>\n</li>\n<li>\n<p>baz</p>\n</li>\n</ul>\n",
-        "example": 276,
-        "start_line": 5029,
-        "end_line": 5048,
-        "section": "Lists"
+        markdown: "- foo\n\n- bar\n\n\n- baz\n",
+        html: "<ul>\n<li>\n<p>foo</p>\n</li>\n<li>\n<p>bar</p>\n</li>\n<li>\n<p>baz</p>\n</li>\n</ul>\n",
+        example: 276,
+        start_line: 5029,
+        end_line: 5048,
+        section: "Lists",
     },
     {
-        "markdown": "- foo\n  - bar\n    - baz\n\n\n      bim\n",
-        "html": "<ul>\n<li>foo\n<ul>\n<li>bar\n<ul>\n<li>\n<p>baz</p>\n<p>bim</p>\n</li>\n</ul>\n</li>\n</ul>\n</li>\n</ul>\n",
-        "example": 277,
-        "start_line": 5050,
-        "end_line": 5072,
-        "section": "Lists"
+        markdown: "- foo\n  - bar\n    - baz\n\n\n      bim\n",
+        html: "<ul>\n<li>foo\n<ul>\n<li>bar\n<ul>\n<li>\n<p>baz</p>\n<p>bim</p>\n</li>\n</ul>\n</li>\n</ul>\n</li>\n</ul>\n",
+        example: 277,
+        start_line: 5050,
+        end_line: 5072,
+        section: "Lists",
     },
     {
-        "markdown": "- foo\n- bar\n\n<!-- -->\n\n- baz\n- bim\n",
-        "html": "<ul>\n<li>foo</li>\n<li>bar</li>\n</ul>\n<!-- -->\n<ul>\n<li>baz</li>\n<li>bim</li>\n</ul>\n",
-        "example": 278,
-        "start_line": 5080,
-        "end_line": 5098,
-        "section": "Lists"
+        markdown: "- foo\n- bar\n\n<!-- -->\n\n- baz\n- bim\n",
+        html: "<ul>\n<li>foo</li>\n<li>bar</li>\n</ul>\n<!-- -->\n<ul>\n<li>baz</li>\n<li>bim</li>\n</ul>\n",
+        example: 278,
+        start_line: 5080,
+        end_line: 5098,
+        section: "Lists",
     },
     {
-        "markdown": "-   foo\n\n    notcode\n\n-   foo\n\n<!-- -->\n\n    code\n",
-        "html": "<ul>\n<li>\n<p>foo</p>\n<p>notcode</p>\n</li>\n<li>\n<p>foo</p>\n</li>\n</ul>\n<!-- -->\n<pre><code>code\n</code></pre>\n",
-        "example": 279,
-        "start_line": 5101,
-        "end_line": 5124,
-        "section": "Lists"
+        markdown: "-   foo\n\n    notcode\n\n-   foo\n\n<!-- -->\n\n    code\n",
+        html: "<ul>\n<li>\n<p>foo</p>\n<p>notcode</p>\n</li>\n<li>\n<p>foo</p>\n</li>\n</ul>\n<!-- -->\n<pre><code>code\n</code></pre>\n",
+        example: 279,
+        start_line: 5101,
+        end_line: 5124,
+        section: "Lists",
     },
     {
-        "markdown": "- a\n - b\n  - c\n   - d\n  - e\n - f\n- g\n",
-        "html": "<ul>\n<li>a</li>\n<li>b</li>\n<li>c</li>\n<li>d</li>\n<li>e</li>\n<li>f</li>\n<li>g</li>\n</ul>\n",
-        "example": 280,
-        "start_line": 5132,
-        "end_line": 5150,
-        "section": "Lists"
+        markdown: "- a\n - b\n  - c\n   - d\n  - e\n - f\n- g\n",
+        html: "<ul>\n<li>a</li>\n<li>b</li>\n<li>c</li>\n<li>d</li>\n<li>e</li>\n<li>f</li>\n<li>g</li>\n</ul>\n",
+        example: 280,
+        start_line: 5132,
+        end_line: 5150,
+        section: "Lists",
     },
     {
-        "markdown": "1. a\n\n  2. b\n\n   3. c\n",
-        "html": "<ol>\n<li>\n<p>a</p>\n</li>\n<li>\n<p>b</p>\n</li>\n<li>\n<p>c</p>\n</li>\n</ol>\n",
-        "example": 281,
-        "start_line": 5153,
-        "end_line": 5171,
-        "section": "Lists"
+        markdown: "1. a\n\n  2. b\n\n   3. c\n",
+        html: "<ol>\n<li>\n<p>a</p>\n</li>\n<li>\n<p>b</p>\n</li>\n<li>\n<p>c</p>\n</li>\n</ol>\n",
+        example: 281,
+        start_line: 5153,
+        end_line: 5171,
+        section: "Lists",
     },
     {
-        "markdown": "- a\n - b\n  - c\n   - d\n    - e\n",
-        "html": "<ul>\n<li>a</li>\n<li>b</li>\n<li>c</li>\n<li>d\n- e</li>\n</ul>\n",
-        "example": 282,
-        "start_line": 5177,
-        "end_line": 5191,
-        "section": "Lists"
+        markdown: "- a\n - b\n  - c\n   - d\n    - e\n",
+        html: "<ul>\n<li>a</li>\n<li>b</li>\n<li>c</li>\n<li>d\n- e</li>\n</ul>\n",
+        example: 282,
+        start_line: 5177,
+        end_line: 5191,
+        section: "Lists",
     },
     {
-        "markdown": "1. a\n\n  2. b\n\n    3. c\n",
-        "html": "<ol>\n<li>\n<p>a</p>\n</li>\n<li>\n<p>b</p>\n</li>\n</ol>\n<pre><code>3. c\n</code></pre>\n",
-        "example": 283,
-        "start_line": 5197,
-        "end_line": 5214,
-        "section": "Lists"
+        markdown: "1. a\n\n  2. b\n\n    3. c\n",
+        html: "<ol>\n<li>\n<p>a</p>\n</li>\n<li>\n<p>b</p>\n</li>\n</ol>\n<pre><code>3. c\n</code></pre>\n",
+        example: 283,
+        start_line: 5197,
+        end_line: 5214,
+        section: "Lists",
     },
     {
-        "markdown": "- a\n- b\n\n- c\n",
-        "html": "<ul>\n<li>\n<p>a</p>\n</li>\n<li>\n<p>b</p>\n</li>\n<li>\n<p>c</p>\n</li>\n</ul>\n",
-        "example": 284,
-        "start_line": 5220,
-        "end_line": 5237,
-        "section": "Lists"
+        markdown: "- a\n- b\n\n- c\n",
+        html: "<ul>\n<li>\n<p>a</p>\n</li>\n<li>\n<p>b</p>\n</li>\n<li>\n<p>c</p>\n</li>\n</ul>\n",
+        example: 284,
+        start_line: 5220,
+        end_line: 5237,
+        section: "Lists",
     },
     {
-        "markdown": "* a\n*\n\n* c\n",
-        "html": "<ul>\n<li>\n<p>a</p>\n</li>\n<li></li>\n<li>\n<p>c</p>\n</li>\n</ul>\n",
-        "example": 285,
-        "start_line": 5242,
-        "end_line": 5257,
-        "section": "Lists"
+        markdown: "* a\n*\n\n* c\n",
+        html: "<ul>\n<li>\n<p>a</p>\n</li>\n<li></li>\n<li>\n<p>c</p>\n</li>\n</ul>\n",
+        example: 285,
+        start_line: 5242,
+        end_line: 5257,
+        section: "Lists",
     },
     {
-        "markdown": "- a\n- b\n\n  c\n- d\n",
-        "html": "<ul>\n<li>\n<p>a</p>\n</li>\n<li>\n<p>b</p>\n<p>c</p>\n</li>\n<li>\n<p>d</p>\n</li>\n</ul>\n",
-        "example": 286,
-        "start_line": 5264,
-        "end_line": 5283,
-        "section": "Lists"
+        markdown: "- a\n- b\n\n  c\n- d\n",
+        html: "<ul>\n<li>\n<p>a</p>\n</li>\n<li>\n<p>b</p>\n<p>c</p>\n</li>\n<li>\n<p>d</p>\n</li>\n</ul>\n",
+        example: 286,
+        start_line: 5264,
+        end_line: 5283,
+        section: "Lists",
     },
     {
-        "markdown": "- a\n- b\n\n  [ref]: /url\n- d\n",
-        "html": "<ul>\n<li>\n<p>a</p>\n</li>\n<li>\n<p>b</p>\n</li>\n<li>\n<p>d</p>\n</li>\n</ul>\n",
-        "example": 287,
-        "start_line": 5286,
-        "end_line": 5304,
-        "section": "Lists"
+        markdown: "- a\n- b\n\n  [ref]: /url\n- d\n",
+        html: "<ul>\n<li>\n<p>a</p>\n</li>\n<li>\n<p>b</p>\n</li>\n<li>\n<p>d</p>\n</li>\n</ul>\n",
+        example: 287,
+        start_line: 5286,
+        end_line: 5304,
+        section: "Lists",
     },
     {
-        "markdown": "- a\n- ```\n  b\n\n\n  ```\n- c\n",
-        "html": "<ul>\n<li>a</li>\n<li>\n<pre><code>b\n\n\n</code></pre>\n</li>\n<li>c</li>\n</ul>\n",
-        "example": 288,
-        "start_line": 5309,
-        "end_line": 5328,
-        "section": "Lists"
+        markdown: "- a\n- ```\n  b\n\n\n  ```\n- c\n",
+        html: "<ul>\n<li>a</li>\n<li>\n<pre><code>b\n\n\n</code></pre>\n</li>\n<li>c</li>\n</ul>\n",
+        example: 288,
+        start_line: 5309,
+        end_line: 5328,
+        section: "Lists",
     },
     {
-        "markdown": "- a\n  - b\n\n    c\n- d\n",
-        "html": "<ul>\n<li>a\n<ul>\n<li>\n<p>b</p>\n<p>c</p>\n</li>\n</ul>\n</li>\n<li>d</li>\n</ul>\n",
-        "example": 289,
-        "start_line": 5335,
-        "end_line": 5353,
-        "section": "Lists"
+        markdown: "- a\n  - b\n\n    c\n- d\n",
+        html: "<ul>\n<li>a\n<ul>\n<li>\n<p>b</p>\n<p>c</p>\n</li>\n</ul>\n</li>\n<li>d</li>\n</ul>\n",
+        example: 289,
+        start_line: 5335,
+        end_line: 5353,
+        section: "Lists",
     },
     {
-        "markdown": "* a\n  > b\n  >\n* c\n",
-        "html": "<ul>\n<li>a\n<blockquote>\n<p>b</p>\n</blockquote>\n</li>\n<li>c</li>\n</ul>\n",
-        "example": 290,
-        "start_line": 5359,
-        "end_line": 5373,
-        "section": "Lists"
+        markdown: "* a\n  > b\n  >\n* c\n",
+        html: "<ul>\n<li>a\n<blockquote>\n<p>b</p>\n</blockquote>\n</li>\n<li>c</li>\n</ul>\n",
+        example: 290,
+        start_line: 5359,
+        end_line: 5373,
+        section: "Lists",
     },
     {
-        "markdown": "- a\n  > b\n  ```\n  c\n  ```\n- d\n",
-        "html": "<ul>\n<li>a\n<blockquote>\n<p>b</p>\n</blockquote>\n<pre><code>c\n</code></pre>\n</li>\n<li>d</li>\n</ul>\n",
-        "example": 291,
-        "start_line": 5379,
-        "end_line": 5397,
-        "section": "Lists"
+        markdown: "- a\n  > b\n  ```\n  c\n  ```\n- d\n",
+        html: "<ul>\n<li>a\n<blockquote>\n<p>b</p>\n</blockquote>\n<pre><code>c\n</code></pre>\n</li>\n<li>d</li>\n</ul>\n",
+        example: 291,
+        start_line: 5379,
+        end_line: 5397,
+        section: "Lists",
     },
     {
-        "markdown": "- a\n",
-        "html": "<ul>\n<li>a</li>\n</ul>\n",
-        "example": 292,
-        "start_line": 5402,
-        "end_line": 5408,
-        "section": "Lists"
+        markdown: "- a\n",
+        html: "<ul>\n<li>a</li>\n</ul>\n",
+        example: 292,
+        start_line: 5402,
+        end_line: 5408,
+        section: "Lists",
     },
     {
-        "markdown": "- a\n  - b\n",
-        "html": "<ul>\n<li>a\n<ul>\n<li>b</li>\n</ul>\n</li>\n</ul>\n",
-        "example": 293,
-        "start_line": 5411,
-        "end_line": 5422,
-        "section": "Lists"
+        markdown: "- a\n  - b\n",
+        html: "<ul>\n<li>a\n<ul>\n<li>b</li>\n</ul>\n</li>\n</ul>\n",
+        example: 293,
+        start_line: 5411,
+        end_line: 5422,
+        section: "Lists",
     },
     {
-        "markdown": "1. ```\n   foo\n   ```\n\n   bar\n",
-        "html": "<ol>\n<li>\n<pre><code>foo\n</code></pre>\n<p>bar</p>\n</li>\n</ol>\n",
-        "example": 294,
-        "start_line": 5428,
-        "end_line": 5442,
-        "section": "Lists"
+        markdown: "1. ```\n   foo\n   ```\n\n   bar\n",
+        html: "<ol>\n<li>\n<pre><code>foo\n</code></pre>\n<p>bar</p>\n</li>\n</ol>\n",
+        example: 294,
+        start_line: 5428,
+        end_line: 5442,
+        section: "Lists",
     },
     {
-        "markdown": "* foo\n  * bar\n\n  baz\n",
-        "html": "<ul>\n<li>\n<p>foo</p>\n<ul>\n<li>bar</li>\n</ul>\n<p>baz</p>\n</li>\n</ul>\n",
-        "example": 295,
-        "start_line": 5447,
-        "end_line": 5462,
-        "section": "Lists"
+        markdown: "* foo\n  * bar\n\n  baz\n",
+        html: "<ul>\n<li>\n<p>foo</p>\n<ul>\n<li>bar</li>\n</ul>\n<p>baz</p>\n</li>\n</ul>\n",
+        example: 295,
+        start_line: 5447,
+        end_line: 5462,
+        section: "Lists",
     },
     {
-        "markdown": "- a\n  - b\n  - c\n\n- d\n  - e\n  - f\n",
-        "html": "<ul>\n<li>\n<p>a</p>\n<ul>\n<li>b</li>\n<li>c</li>\n</ul>\n</li>\n<li>\n<p>d</p>\n<ul>\n<li>e</li>\n<li>f</li>\n</ul>\n</li>\n</ul>\n",
-        "example": 296,
-        "start_line": 5465,
-        "end_line": 5490,
-        "section": "Lists"
+        markdown: "- a\n  - b\n  - c\n\n- d\n  - e\n  - f\n",
+        html: "<ul>\n<li>\n<p>a</p>\n<ul>\n<li>b</li>\n<li>c</li>\n</ul>\n</li>\n<li>\n<p>d</p>\n<ul>\n<li>e</li>\n<li>f</li>\n</ul>\n</li>\n</ul>\n",
+        example: 296,
+        start_line: 5465,
+        end_line: 5490,
+        section: "Lists",
     },
     {
-        "markdown": "`hi`lo`\n",
-        "html": "<p><code>hi</code>lo`</p>\n",
-        "example": 297,
-        "start_line": 5499,
-        "end_line": 5503,
-        "section": "Inlines"
+        markdown: "`hi`lo`\n",
+        html: "<p><code>hi</code>lo`</p>\n",
+        example: 297,
+        start_line: 5499,
+        end_line: 5503,
+        section: "Inlines",
     },
     {
-        "markdown": "\\!\\\"\\#\\$\\%\\&\\'\\(\\)\\*\\+\\,\\-\\.\\/\\:\\;\\<\\=\\>\\?\\@\\[\\\\\\]\\^\\_\\`\\{\\|\\}\\~\n",
-        "html": "<p>!&quot;#$%&amp;'()*+,-./:;&lt;=&gt;?@[\\]^_`{|}~</p>\n",
-        "example": 298,
-        "start_line": 5513,
-        "end_line": 5517,
-        "section": "Backslash escapes"
+        markdown: "\\!\\\"\\#\\$\\%\\&\\'\\(\\)\\*\\+\\,\\-\\.\\/\\:\\;\\<\\=\\>\\?\\@\\[\\\\\\]\\^\\_\\`\\{\\|\\}\\~\n",
+        html: "<p>!&quot;#$%&amp;'()*+,-./:;&lt;=&gt;?@[\\]^_`{|}~</p>\n",
+        example: 298,
+        start_line: 5513,
+        end_line: 5517,
+        section: "Backslash escapes",
     },
     {
-        "markdown": "\\\t\\A\\a\\ \\3\\φ\\«\n",
-        "html": "<p>\\\t\\A\\a\\ \\3\\φ\\«</p>\n",
-        "example": 299,
-        "start_line": 5523,
-        "end_line": 5527,
-        "section": "Backslash escapes"
+        markdown: "\\\t\\A\\a\\ \\3\\φ\\«\n",
+        html: "<p>\\\t\\A\\a\\ \\3\\φ\\«</p>\n",
+        example: 299,
+        start_line: 5523,
+        end_line: 5527,
+        section: "Backslash escapes",
     },
     {
-        "markdown": "\\*not emphasized*\n\\<br/> not a tag\n\\[not a link](/foo)\n\\`not code`\n1\\. not a list\n\\* not a list\n\\# not a heading\n\\[foo]: /url \"not a reference\"\n\\&ouml; not a character entity\n",
-        "html": "<p>*not emphasized*\n&lt;br/&gt; not a tag\n[not a link](/foo)\n`not code`\n1. not a list\n* not a list\n# not a heading\n[foo]: /url &quot;not a reference&quot;\n&amp;ouml; not a character entity</p>\n",
-        "example": 300,
-        "start_line": 5533,
-        "end_line": 5553,
-        "section": "Backslash escapes"
+        markdown: "\\*not emphasized*\n\\<br/> not a tag\n\\[not a link](/foo)\n\\`not code`\n1\\. not a list\n\\* not a list\n\\# not a heading\n\\[foo]: /url \"not a reference\"\n\\&ouml; not a character entity\n",
+        html: "<p>*not emphasized*\n&lt;br/&gt; not a tag\n[not a link](/foo)\n`not code`\n1. not a list\n* not a list\n# not a heading\n[foo]: /url &quot;not a reference&quot;\n&amp;ouml; not a character entity</p>\n",
+        example: 300,
+        start_line: 5533,
+        end_line: 5553,
+        section: "Backslash escapes",
     },
     {
-        "markdown": "\\\\*emphasis*\n",
-        "html": "<p>\\<em>emphasis</em></p>\n",
-        "example": 301,
-        "start_line": 5558,
-        "end_line": 5562,
-        "section": "Backslash escapes"
+        markdown: "\\\\*emphasis*\n",
+        html: "<p>\\<em>emphasis</em></p>\n",
+        example: 301,
+        start_line: 5558,
+        end_line: 5562,
+        section: "Backslash escapes",
     },
     {
-        "markdown": "foo\\\nbar\n",
-        "html": "<p>foo<br />\nbar</p>\n",
-        "example": 302,
-        "start_line": 5567,
-        "end_line": 5573,
-        "section": "Backslash escapes"
+        markdown: "foo\\\nbar\n",
+        html: "<p>foo<br />\nbar</p>\n",
+        example: 302,
+        start_line: 5567,
+        end_line: 5573,
+        section: "Backslash escapes",
     },
     {
-        "markdown": "`` \\[\\` ``\n",
-        "html": "<p><code>\\[\\`</code></p>\n",
-        "example": 303,
-        "start_line": 5579,
-        "end_line": 5583,
-        "section": "Backslash escapes"
+        markdown: "`` \\[\\` ``\n",
+        html: "<p><code>\\[\\`</code></p>\n",
+        example: 303,
+        start_line: 5579,
+        end_line: 5583,
+        section: "Backslash escapes",
     },
     {
-        "markdown": "    \\[\\]\n",
-        "html": "<pre><code>\\[\\]\n</code></pre>\n",
-        "example": 304,
-        "start_line": 5586,
-        "end_line": 5591,
-        "section": "Backslash escapes"
+        markdown: "    \\[\\]\n",
+        html: "<pre><code>\\[\\]\n</code></pre>\n",
+        example: 304,
+        start_line: 5586,
+        end_line: 5591,
+        section: "Backslash escapes",
     },
     {
-        "markdown": "~~~\n\\[\\]\n~~~\n",
-        "html": "<pre><code>\\[\\]\n</code></pre>\n",
-        "example": 305,
-        "start_line": 5594,
-        "end_line": 5601,
-        "section": "Backslash escapes"
+        markdown: "~~~\n\\[\\]\n~~~\n",
+        html: "<pre><code>\\[\\]\n</code></pre>\n",
+        example: 305,
+        start_line: 5594,
+        end_line: 5601,
+        section: "Backslash escapes",
     },
     {
-        "markdown": "<http://example.com?find=\\*>\n",
-        "html": "<p><a href=\"http://example.com?find=%5C*\">http://example.com?find=\\*</a></p>\n",
-        "example": 306,
-        "start_line": 5604,
-        "end_line": 5608,
-        "section": "Backslash escapes"
+        markdown: "<http://example.com?find=\\*>\n",
+        html: "<p><a href=\"http://example.com?find=%5C*\">http://example.com?find=\\*</a></p>\n",
+        example: 306,
+        start_line: 5604,
+        end_line: 5608,
+        section: "Backslash escapes",
     },
     {
-        "markdown": "<a href=\"/bar\\/)\">\n",
-        "html": "<a href=\"/bar\\/)\">\n",
-        "example": 307,
-        "start_line": 5611,
-        "end_line": 5615,
-        "section": "Backslash escapes"
+        markdown: "<a href=\"/bar\\/)\">\n",
+        html: "<a href=\"/bar\\/)\">\n",
+        example: 307,
+        start_line: 5611,
+        end_line: 5615,
+        section: "Backslash escapes",
     },
     {
-        "markdown": "[foo](/bar\\* \"ti\\*tle\")\n",
-        "html": "<p><a href=\"/bar*\" title=\"ti*tle\">foo</a></p>\n",
-        "example": 308,
-        "start_line": 5621,
-        "end_line": 5625,
-        "section": "Backslash escapes"
+        markdown: "[foo](/bar\\* \"ti\\*tle\")\n",
+        html: "<p><a href=\"/bar*\" title=\"ti*tle\">foo</a></p>\n",
+        example: 308,
+        start_line: 5621,
+        end_line: 5625,
+        section: "Backslash escapes",
     },
     {
-        "markdown": "[foo]\n\n[foo]: /bar\\* \"ti\\*tle\"\n",
-        "html": "<p><a href=\"/bar*\" title=\"ti*tle\">foo</a></p>\n",
-        "example": 309,
-        "start_line": 5628,
-        "end_line": 5634,
-        "section": "Backslash escapes"
+        markdown: "[foo]\n\n[foo]: /bar\\* \"ti\\*tle\"\n",
+        html: "<p><a href=\"/bar*\" title=\"ti*tle\">foo</a></p>\n",
+        example: 309,
+        start_line: 5628,
+        end_line: 5634,
+        section: "Backslash escapes",
     },
     {
-        "markdown": "``` foo\\+bar\nfoo\n```\n",
-        "html": "<pre><code class=\"language-foo+bar\">foo\n</code></pre>\n",
-        "example": 310,
-        "start_line": 5637,
-        "end_line": 5644,
-        "section": "Backslash escapes"
+        markdown: "``` foo\\+bar\nfoo\n```\n",
+        html: "<pre><code class=\"language-foo+bar\">foo\n</code></pre>\n",
+        example: 310,
+        start_line: 5637,
+        end_line: 5644,
+        section: "Backslash escapes",
     },
     {
-        "markdown": "&nbsp; &amp; &copy; &AElig; &Dcaron;\n&frac34; &HilbertSpace; &DifferentialD;\n&ClockwiseContourIntegral; &ngE;\n",
-        "html": "<p>  &amp; © Æ Ď\n¾ ℋ ⅆ\n∲ ≧̸</p>\n",
-        "example": 311,
-        "start_line": 5674,
-        "end_line": 5682,
-        "section": "Entity and numeric character references"
+        markdown: "&nbsp; &amp; &copy; &AElig; &Dcaron;\n&frac34; &HilbertSpace; &DifferentialD;\n&ClockwiseContourIntegral; &ngE;\n",
+        html: "<p>  &amp; © Æ Ď\n¾ ℋ ⅆ\n∲ ≧̸</p>\n",
+        example: 311,
+        start_line: 5674,
+        end_line: 5682,
+        section: "Entity and numeric character references",
     },
     {
-        "markdown": "&#35; &#1234; &#992; &#0;\n",
-        "html": "<p># Ӓ Ϡ �</p>\n",
-        "example": 312,
-        "start_line": 5693,
-        "end_line": 5697,
-        "section": "Entity and numeric character references"
+        markdown: "&#35; &#1234; &#992; &#0;\n",
+        html: "<p># Ӓ Ϡ �</p>\n",
+        example: 312,
+        start_line: 5693,
+        end_line: 5697,
+        section: "Entity and numeric character references",
     },
     {
-        "markdown": "&#X22; &#XD06; &#xcab;\n",
-        "html": "<p>&quot; ആ ಫ</p>\n",
-        "example": 313,
-        "start_line": 5706,
-        "end_line": 5710,
-        "section": "Entity and numeric character references"
+        markdown: "&#X22; &#XD06; &#xcab;\n",
+        html: "<p>&quot; ആ ಫ</p>\n",
+        example: 313,
+        start_line: 5706,
+        end_line: 5710,
+        section: "Entity and numeric character references",
     },
     {
-        "markdown": "&nbsp &x; &#; &#x;\n&#987654321;\n&#abcdef0;\n&ThisIsNotDefined; &hi?;\n",
-        "html": "<p>&amp;nbsp &amp;x; &amp;#; &amp;#x;\n&amp;#987654321;\n&amp;#abcdef0;\n&amp;ThisIsNotDefined; &amp;hi?;</p>\n",
-        "example": 314,
-        "start_line": 5715,
-        "end_line": 5725,
-        "section": "Entity and numeric character references"
+        markdown: "&nbsp &x; &#; &#x;\n&#987654321;\n&#abcdef0;\n&ThisIsNotDefined; &hi?;\n",
+        html: "<p>&amp;nbsp &amp;x; &amp;#; &amp;#x;\n&amp;#987654321;\n&amp;#abcdef0;\n&amp;ThisIsNotDefined; &amp;hi?;</p>\n",
+        example: 314,
+        start_line: 5715,
+        end_line: 5725,
+        section: "Entity and numeric character references",
     },
     {
-        "markdown": "&copy\n",
-        "html": "<p>&amp;copy</p>\n",
-        "example": 315,
-        "start_line": 5732,
-        "end_line": 5736,
-        "section": "Entity and numeric character references"
+        markdown: "&copy\n",
+        html: "<p>&amp;copy</p>\n",
+        example: 315,
+        start_line: 5732,
+        end_line: 5736,
+        section: "Entity and numeric character references",
     },
     {
-        "markdown": "&MadeUpEntity;\n",
-        "html": "<p>&amp;MadeUpEntity;</p>\n",
-        "example": 316,
-        "start_line": 5742,
-        "end_line": 5746,
-        "section": "Entity and numeric character references"
+        markdown: "&MadeUpEntity;\n",
+        html: "<p>&amp;MadeUpEntity;</p>\n",
+        example: 316,
+        start_line: 5742,
+        end_line: 5746,
+        section: "Entity and numeric character references",
     },
     {
-        "markdown": "<a href=\"&ouml;&ouml;.html\">\n",
-        "html": "<a href=\"&ouml;&ouml;.html\">\n",
-        "example": 317,
-        "start_line": 5753,
-        "end_line": 5757,
-        "section": "Entity and numeric character references"
+        markdown: "<a href=\"&ouml;&ouml;.html\">\n",
+        html: "<a href=\"&ouml;&ouml;.html\">\n",
+        example: 317,
+        start_line: 5753,
+        end_line: 5757,
+        section: "Entity and numeric character references",
     },
     {
-        "markdown": "[foo](/f&ouml;&ouml; \"f&ouml;&ouml;\")\n",
-        "html": "<p><a href=\"/f%C3%B6%C3%B6\" title=\"föö\">foo</a></p>\n",
-        "example": 318,
-        "start_line": 5760,
-        "end_line": 5764,
-        "section": "Entity and numeric character references"
+        markdown: "[foo](/f&ouml;&ouml; \"f&ouml;&ouml;\")\n",
+        html: "<p><a href=\"/f%C3%B6%C3%B6\" title=\"föö\">foo</a></p>\n",
+        example: 318,
+        start_line: 5760,
+        end_line: 5764,
+        section: "Entity and numeric character references",
     },
     {
-        "markdown": "[foo]\n\n[foo]: /f&ouml;&ouml; \"f&ouml;&ouml;\"\n",
-        "html": "<p><a href=\"/f%C3%B6%C3%B6\" title=\"föö\">foo</a></p>\n",
-        "example": 319,
-        "start_line": 5767,
-        "end_line": 5773,
-        "section": "Entity and numeric character references"
+        markdown: "[foo]\n\n[foo]: /f&ouml;&ouml; \"f&ouml;&ouml;\"\n",
+        html: "<p><a href=\"/f%C3%B6%C3%B6\" title=\"föö\">foo</a></p>\n",
+        example: 319,
+        start_line: 5767,
+        end_line: 5773,
+        section: "Entity and numeric character references",
     },
     {
-        "markdown": "``` f&ouml;&ouml;\nfoo\n```\n",
-        "html": "<pre><code class=\"language-föö\">foo\n</code></pre>\n",
-        "example": 320,
-        "start_line": 5776,
-        "end_line": 5783,
-        "section": "Entity and numeric character references"
+        markdown: "``` f&ouml;&ouml;\nfoo\n```\n",
+        html: "<pre><code class=\"language-föö\">foo\n</code></pre>\n",
+        example: 320,
+        start_line: 5776,
+        end_line: 5783,
+        section: "Entity and numeric character references",
     },
     {
-        "markdown": "`f&ouml;&ouml;`\n",
-        "html": "<p><code>f&amp;ouml;&amp;ouml;</code></p>\n",
-        "example": 321,
-        "start_line": 5789,
-        "end_line": 5793,
-        "section": "Entity and numeric character references"
+        markdown: "`f&ouml;&ouml;`\n",
+        html: "<p><code>f&amp;ouml;&amp;ouml;</code></p>\n",
+        example: 321,
+        start_line: 5789,
+        end_line: 5793,
+        section: "Entity and numeric character references",
     },
     {
-        "markdown": "    f&ouml;f&ouml;\n",
-        "html": "<pre><code>f&amp;ouml;f&amp;ouml;\n</code></pre>\n",
-        "example": 322,
-        "start_line": 5796,
-        "end_line": 5801,
-        "section": "Entity and numeric character references"
+        markdown: "    f&ouml;f&ouml;\n",
+        html: "<pre><code>f&amp;ouml;f&amp;ouml;\n</code></pre>\n",
+        example: 322,
+        start_line: 5796,
+        end_line: 5801,
+        section: "Entity and numeric character references",
     },
     {
-        "markdown": "&#42;foo&#42;\n*foo*\n",
-        "html": "<p>*foo*\n<em>foo</em></p>\n",
-        "example": 323,
-        "start_line": 5808,
-        "end_line": 5814,
-        "section": "Entity and numeric character references"
+        markdown: "&#42;foo&#42;\n*foo*\n",
+        html: "<p>*foo*\n<em>foo</em></p>\n",
+        example: 323,
+        start_line: 5808,
+        end_line: 5814,
+        section: "Entity and numeric character references",
     },
     {
-        "markdown": "&#42; foo\n\n* foo\n",
-        "html": "<p>* foo</p>\n<ul>\n<li>foo</li>\n</ul>\n",
-        "example": 324,
-        "start_line": 5816,
-        "end_line": 5825,
-        "section": "Entity and numeric character references"
+        markdown: "&#42; foo\n\n* foo\n",
+        html: "<p>* foo</p>\n<ul>\n<li>foo</li>\n</ul>\n",
+        example: 324,
+        start_line: 5816,
+        end_line: 5825,
+        section: "Entity and numeric character references",
     },
     {
-        "markdown": "foo&#10;&#10;bar\n",
-        "html": "<p>foo\n\nbar</p>\n",
-        "example": 325,
-        "start_line": 5827,
-        "end_line": 5833,
-        "section": "Entity and numeric character references"
+        markdown: "foo&#10;&#10;bar\n",
+        html: "<p>foo\n\nbar</p>\n",
+        example: 325,
+        start_line: 5827,
+        end_line: 5833,
+        section: "Entity and numeric character references",
     },
     {
-        "markdown": "&#9;foo\n",
-        "html": "<p>\tfoo</p>\n",
-        "example": 326,
-        "start_line": 5835,
-        "end_line": 5839,
-        "section": "Entity and numeric character references"
+        markdown: "&#9;foo\n",
+        html: "<p>\tfoo</p>\n",
+        example: 326,
+        start_line: 5835,
+        end_line: 5839,
+        section: "Entity and numeric character references",
     },
     {
-        "markdown": "[a](url &quot;tit&quot;)\n",
-        "html": "<p>[a](url &quot;tit&quot;)</p>\n",
-        "example": 327,
-        "start_line": 5842,
-        "end_line": 5846,
-        "section": "Entity and numeric character references"
+        markdown: "[a](url &quot;tit&quot;)\n",
+        html: "<p>[a](url &quot;tit&quot;)</p>\n",
+        example: 327,
+        start_line: 5842,
+        end_line: 5846,
+        section: "Entity and numeric character references",
     },
     {
-        "markdown": "`foo`\n",
-        "html": "<p><code>foo</code></p>\n",
-        "example": 328,
-        "start_line": 5870,
-        "end_line": 5874,
-        "section": "Code spans"
+        markdown: "`foo`\n",
+        html: "<p><code>foo</code></p>\n",
+        example: 328,
+        start_line: 5870,
+        end_line: 5874,
+        section: "Code spans",
     },
     {
-        "markdown": "`` foo ` bar ``\n",
-        "html": "<p><code>foo ` bar</code></p>\n",
-        "example": 329,
-        "start_line": 5881,
-        "end_line": 5885,
-        "section": "Code spans"
+        markdown: "`` foo ` bar ``\n",
+        html: "<p><code>foo ` bar</code></p>\n",
+        example: 329,
+        start_line: 5881,
+        end_line: 5885,
+        section: "Code spans",
     },
     {
-        "markdown": "` `` `\n",
-        "html": "<p><code>``</code></p>\n",
-        "example": 330,
-        "start_line": 5891,
-        "end_line": 5895,
-        "section": "Code spans"
+        markdown: "` `` `\n",
+        html: "<p><code>``</code></p>\n",
+        example: 330,
+        start_line: 5891,
+        end_line: 5895,
+        section: "Code spans",
     },
     {
-        "markdown": "`  ``  `\n",
-        "html": "<p><code> `` </code></p>\n",
-        "example": 331,
-        "start_line": 5899,
-        "end_line": 5903,
-        "section": "Code spans"
+        markdown: "`  ``  `\n",
+        html: "<p><code> `` </code></p>\n",
+        example: 331,
+        start_line: 5899,
+        end_line: 5903,
+        section: "Code spans",
     },
     {
-        "markdown": "` a`\n",
-        "html": "<p><code> a</code></p>\n",
-        "example": 332,
-        "start_line": 5908,
-        "end_line": 5912,
-        "section": "Code spans"
+        markdown: "` a`\n",
+        html: "<p><code> a</code></p>\n",
+        example: 332,
+        start_line: 5908,
+        end_line: 5912,
+        section: "Code spans",
     },
     {
-        "markdown": "` b `\n",
-        "html": "<p><code> b </code></p>\n",
-        "example": 333,
-        "start_line": 5917,
-        "end_line": 5921,
-        "section": "Code spans"
+        markdown: "` b `\n",
+        html: "<p><code> b </code></p>\n",
+        example: 333,
+        start_line: 5917,
+        end_line: 5921,
+        section: "Code spans",
     },
     {
-        "markdown": "` `\n`  `\n",
-        "html": "<p><code> </code>\n<code>  </code></p>\n",
-        "example": 334,
-        "start_line": 5925,
-        "end_line": 5931,
-        "section": "Code spans"
+        markdown: "` `\n`  `\n",
+        html: "<p><code> </code>\n<code>  </code></p>\n",
+        example: 334,
+        start_line: 5925,
+        end_line: 5931,
+        section: "Code spans",
     },
     {
-        "markdown": "``\nfoo\nbar  \nbaz\n``\n",
-        "html": "<p><code>foo bar   baz</code></p>\n",
-        "example": 335,
-        "start_line": 5936,
-        "end_line": 5944,
-        "section": "Code spans"
+        markdown: "``\nfoo\nbar  \nbaz\n``\n",
+        html: "<p><code>foo bar   baz</code></p>\n",
+        example: 335,
+        start_line: 5936,
+        end_line: 5944,
+        section: "Code spans",
     },
     {
-        "markdown": "``\nfoo \n``\n",
-        "html": "<p><code>foo </code></p>\n",
-        "example": 336,
-        "start_line": 5946,
-        "end_line": 5952,
-        "section": "Code spans"
+        markdown: "``\nfoo \n``\n",
+        html: "<p><code>foo </code></p>\n",
+        example: 336,
+        start_line: 5946,
+        end_line: 5952,
+        section: "Code spans",
     },
     {
-        "markdown": "`foo   bar \nbaz`\n",
-        "html": "<p><code>foo   bar  baz</code></p>\n",
-        "example": 337,
-        "start_line": 5957,
-        "end_line": 5962,
-        "section": "Code spans"
+        markdown: "`foo   bar \nbaz`\n",
+        html: "<p><code>foo   bar  baz</code></p>\n",
+        example: 337,
+        start_line: 5957,
+        end_line: 5962,
+        section: "Code spans",
     },
     {
-        "markdown": "`foo\\`bar`\n",
-        "html": "<p><code>foo\\</code>bar`</p>\n",
-        "example": 338,
-        "start_line": 5974,
-        "end_line": 5978,
-        "section": "Code spans"
+        markdown: "`foo\\`bar`\n",
+        html: "<p><code>foo\\</code>bar`</p>\n",
+        example: 338,
+        start_line: 5974,
+        end_line: 5978,
+        section: "Code spans",
     },
     {
-        "markdown": "``foo`bar``\n",
-        "html": "<p><code>foo`bar</code></p>\n",
-        "example": 339,
-        "start_line": 5985,
-        "end_line": 5989,
-        "section": "Code spans"
+        markdown: "``foo`bar``\n",
+        html: "<p><code>foo`bar</code></p>\n",
+        example: 339,
+        start_line: 5985,
+        end_line: 5989,
+        section: "Code spans",
     },
     {
-        "markdown": "` foo `` bar `\n",
-        "html": "<p><code>foo `` bar</code></p>\n",
-        "example": 340,
-        "start_line": 5991,
-        "end_line": 5995,
-        "section": "Code spans"
+        markdown: "` foo `` bar `\n",
+        html: "<p><code>foo `` bar</code></p>\n",
+        example: 340,
+        start_line: 5991,
+        end_line: 5995,
+        section: "Code spans",
     },
     {
-        "markdown": "*foo`*`\n",
-        "html": "<p>*foo<code>*</code></p>\n",
-        "example": 341,
-        "start_line": 6003,
-        "end_line": 6007,
-        "section": "Code spans"
+        markdown: "*foo`*`\n",
+        html: "<p>*foo<code>*</code></p>\n",
+        example: 341,
+        start_line: 6003,
+        end_line: 6007,
+        section: "Code spans",
     },
     {
-        "markdown": "[not a `link](/foo`)\n",
-        "html": "<p>[not a <code>link](/foo</code>)</p>\n",
-        "example": 342,
-        "start_line": 6012,
-        "end_line": 6016,
-        "section": "Code spans"
+        markdown: "[not a `link](/foo`)\n",
+        html: "<p>[not a <code>link](/foo</code>)</p>\n",
+        example: 342,
+        start_line: 6012,
+        end_line: 6016,
+        section: "Code spans",
     },
     {
-        "markdown": "`<a href=\"`\">`\n",
-        "html": "<p><code>&lt;a href=&quot;</code>&quot;&gt;`</p>\n",
-        "example": 343,
-        "start_line": 6022,
-        "end_line": 6026,
-        "section": "Code spans"
+        markdown: "`<a href=\"`\">`\n",
+        html: "<p><code>&lt;a href=&quot;</code>&quot;&gt;`</p>\n",
+        example: 343,
+        start_line: 6022,
+        end_line: 6026,
+        section: "Code spans",
     },
     {
-        "markdown": "<a href=\"`\">`\n",
-        "html": "<p><a href=\"`\">`</p>\n",
-        "example": 344,
-        "start_line": 6031,
-        "end_line": 6035,
-        "section": "Code spans"
+        markdown: "<a href=\"`\">`\n",
+        html: "<p><a href=\"`\">`</p>\n",
+        example: 344,
+        start_line: 6031,
+        end_line: 6035,
+        section: "Code spans",
     },
     {
-        "markdown": "`<http://foo.bar.`baz>`\n",
-        "html": "<p><code>&lt;http://foo.bar.</code>baz&gt;`</p>\n",
-        "example": 345,
-        "start_line": 6040,
-        "end_line": 6044,
-        "section": "Code spans"
+        markdown: "`<http://foo.bar.`baz>`\n",
+        html: "<p><code>&lt;http://foo.bar.</code>baz&gt;`</p>\n",
+        example: 345,
+        start_line: 6040,
+        end_line: 6044,
+        section: "Code spans",
     },
     {
-        "markdown": "<http://foo.bar.`baz>`\n",
-        "html": "<p><a href=\"http://foo.bar.%60baz\">http://foo.bar.`baz</a>`</p>\n",
-        "example": 346,
-        "start_line": 6049,
-        "end_line": 6053,
-        "section": "Code spans"
+        markdown: "<http://foo.bar.`baz>`\n",
+        html: "<p><a href=\"http://foo.bar.%60baz\">http://foo.bar.`baz</a>`</p>\n",
+        example: 346,
+        start_line: 6049,
+        end_line: 6053,
+        section: "Code spans",
     },
     {
-        "markdown": "```foo``\n",
-        "html": "<p>```foo``</p>\n",
-        "example": 347,
-        "start_line": 6059,
-        "end_line": 6063,
-        "section": "Code spans"
+        markdown: "```foo``\n",
+        html: "<p>```foo``</p>\n",
+        example: 347,
+        start_line: 6059,
+        end_line: 6063,
+        section: "Code spans",
     },
     {
-        "markdown": "`foo\n",
-        "html": "<p>`foo</p>\n",
-        "example": 348,
-        "start_line": 6066,
-        "end_line": 6070,
-        "section": "Code spans"
+        markdown: "`foo\n",
+        html: "<p>`foo</p>\n",
+        example: 348,
+        start_line: 6066,
+        end_line: 6070,
+        section: "Code spans",
     },
     {
-        "markdown": "`foo``bar``\n",
-        "html": "<p>`foo<code>bar</code></p>\n",
-        "example": 349,
-        "start_line": 6075,
-        "end_line": 6079,
-        "section": "Code spans"
+        markdown: "`foo``bar``\n",
+        html: "<p>`foo<code>bar</code></p>\n",
+        example: 349,
+        start_line: 6075,
+        end_line: 6079,
+        section: "Code spans",
     },
     {
-        "markdown": "*foo bar*\n",
-        "html": "<p><em>foo bar</em></p>\n",
-        "example": 350,
-        "start_line": 6292,
-        "end_line": 6296,
-        "section": "Emphasis and strong emphasis"
+        markdown: "*foo bar*\n",
+        html: "<p><em>foo bar</em></p>\n",
+        example: 350,
+        start_line: 6292,
+        end_line: 6296,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "a * foo bar*\n",
-        "html": "<p>a * foo bar*</p>\n",
-        "example": 351,
-        "start_line": 6302,
-        "end_line": 6306,
-        "section": "Emphasis and strong emphasis"
+        markdown: "a * foo bar*\n",
+        html: "<p>a * foo bar*</p>\n",
+        example: 351,
+        start_line: 6302,
+        end_line: 6306,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "a*\"foo\"*\n",
-        "html": "<p>a*&quot;foo&quot;*</p>\n",
-        "example": 352,
-        "start_line": 6313,
-        "end_line": 6317,
-        "section": "Emphasis and strong emphasis"
+        markdown: "a*\"foo\"*\n",
+        html: "<p>a*&quot;foo&quot;*</p>\n",
+        example: 352,
+        start_line: 6313,
+        end_line: 6317,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "* a *\n",
-        "html": "<p>* a *</p>\n",
-        "example": 353,
-        "start_line": 6322,
-        "end_line": 6326,
-        "section": "Emphasis and strong emphasis"
+        markdown: "* a *\n",
+        html: "<p>* a *</p>\n",
+        example: 353,
+        start_line: 6322,
+        end_line: 6326,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "foo*bar*\n",
-        "html": "<p>foo<em>bar</em></p>\n",
-        "example": 354,
-        "start_line": 6331,
-        "end_line": 6335,
-        "section": "Emphasis and strong emphasis"
+        markdown: "foo*bar*\n",
+        html: "<p>foo<em>bar</em></p>\n",
+        example: 354,
+        start_line: 6331,
+        end_line: 6335,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "5*6*78\n",
-        "html": "<p>5<em>6</em>78</p>\n",
-        "example": 355,
-        "start_line": 6338,
-        "end_line": 6342,
-        "section": "Emphasis and strong emphasis"
+        markdown: "5*6*78\n",
+        html: "<p>5<em>6</em>78</p>\n",
+        example: 355,
+        start_line: 6338,
+        end_line: 6342,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "_foo bar_\n",
-        "html": "<p><em>foo bar</em></p>\n",
-        "example": 356,
-        "start_line": 6347,
-        "end_line": 6351,
-        "section": "Emphasis and strong emphasis"
+        markdown: "_foo bar_\n",
+        html: "<p><em>foo bar</em></p>\n",
+        example: 356,
+        start_line: 6347,
+        end_line: 6351,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "_ foo bar_\n",
-        "html": "<p>_ foo bar_</p>\n",
-        "example": 357,
-        "start_line": 6357,
-        "end_line": 6361,
-        "section": "Emphasis and strong emphasis"
+        markdown: "_ foo bar_\n",
+        html: "<p>_ foo bar_</p>\n",
+        example: 357,
+        start_line: 6357,
+        end_line: 6361,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "a_\"foo\"_\n",
-        "html": "<p>a_&quot;foo&quot;_</p>\n",
-        "example": 358,
-        "start_line": 6367,
-        "end_line": 6371,
-        "section": "Emphasis and strong emphasis"
+        markdown: "a_\"foo\"_\n",
+        html: "<p>a_&quot;foo&quot;_</p>\n",
+        example: 358,
+        start_line: 6367,
+        end_line: 6371,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "foo_bar_\n",
-        "html": "<p>foo_bar_</p>\n",
-        "example": 359,
-        "start_line": 6376,
-        "end_line": 6380,
-        "section": "Emphasis and strong emphasis"
+        markdown: "foo_bar_\n",
+        html: "<p>foo_bar_</p>\n",
+        example: 359,
+        start_line: 6376,
+        end_line: 6380,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "5_6_78\n",
-        "html": "<p>5_6_78</p>\n",
-        "example": 360,
-        "start_line": 6383,
-        "end_line": 6387,
-        "section": "Emphasis and strong emphasis"
+        markdown: "5_6_78\n",
+        html: "<p>5_6_78</p>\n",
+        example: 360,
+        start_line: 6383,
+        end_line: 6387,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "пристаням_стремятся_\n",
-        "html": "<p>пристаням_стремятся_</p>\n",
-        "example": 361,
-        "start_line": 6390,
-        "end_line": 6394,
-        "section": "Emphasis and strong emphasis"
+        markdown: "пристаням_стремятся_\n",
+        html: "<p>пристаням_стремятся_</p>\n",
+        example: 361,
+        start_line: 6390,
+        end_line: 6394,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "aa_\"bb\"_cc\n",
-        "html": "<p>aa_&quot;bb&quot;_cc</p>\n",
-        "example": 362,
-        "start_line": 6400,
-        "end_line": 6404,
-        "section": "Emphasis and strong emphasis"
+        markdown: "aa_\"bb\"_cc\n",
+        html: "<p>aa_&quot;bb&quot;_cc</p>\n",
+        example: 362,
+        start_line: 6400,
+        end_line: 6404,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "foo-_(bar)_\n",
-        "html": "<p>foo-<em>(bar)</em></p>\n",
-        "example": 363,
-        "start_line": 6411,
-        "end_line": 6415,
-        "section": "Emphasis and strong emphasis"
+        markdown: "foo-_(bar)_\n",
+        html: "<p>foo-<em>(bar)</em></p>\n",
+        example: 363,
+        start_line: 6411,
+        end_line: 6415,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "_foo*\n",
-        "html": "<p>_foo*</p>\n",
-        "example": 364,
-        "start_line": 6423,
-        "end_line": 6427,
-        "section": "Emphasis and strong emphasis"
+        markdown: "_foo*\n",
+        html: "<p>_foo*</p>\n",
+        example: 364,
+        start_line: 6423,
+        end_line: 6427,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "*foo bar *\n",
-        "html": "<p>*foo bar *</p>\n",
-        "example": 365,
-        "start_line": 6433,
-        "end_line": 6437,
-        "section": "Emphasis and strong emphasis"
+        markdown: "*foo bar *\n",
+        html: "<p>*foo bar *</p>\n",
+        example: 365,
+        start_line: 6433,
+        end_line: 6437,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "*foo bar\n*\n",
-        "html": "<p>*foo bar\n*</p>\n",
-        "example": 366,
-        "start_line": 6442,
-        "end_line": 6448,
-        "section": "Emphasis and strong emphasis"
+        markdown: "*foo bar\n*\n",
+        html: "<p>*foo bar\n*</p>\n",
+        example: 366,
+        start_line: 6442,
+        end_line: 6448,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "*(*foo)\n",
-        "html": "<p>*(*foo)</p>\n",
-        "example": 367,
-        "start_line": 6455,
-        "end_line": 6459,
-        "section": "Emphasis and strong emphasis"
+        markdown: "*(*foo)\n",
+        html: "<p>*(*foo)</p>\n",
+        example: 367,
+        start_line: 6455,
+        end_line: 6459,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "*(*foo*)*\n",
-        "html": "<p><em>(<em>foo</em>)</em></p>\n",
-        "example": 368,
-        "start_line": 6465,
-        "end_line": 6469,
-        "section": "Emphasis and strong emphasis"
+        markdown: "*(*foo*)*\n",
+        html: "<p><em>(<em>foo</em>)</em></p>\n",
+        example: 368,
+        start_line: 6465,
+        end_line: 6469,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "*foo*bar\n",
-        "html": "<p><em>foo</em>bar</p>\n",
-        "example": 369,
-        "start_line": 6474,
-        "end_line": 6478,
-        "section": "Emphasis and strong emphasis"
+        markdown: "*foo*bar\n",
+        html: "<p><em>foo</em>bar</p>\n",
+        example: 369,
+        start_line: 6474,
+        end_line: 6478,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "_foo bar _\n",
-        "html": "<p>_foo bar _</p>\n",
-        "example": 370,
-        "start_line": 6487,
-        "end_line": 6491,
-        "section": "Emphasis and strong emphasis"
+        markdown: "_foo bar _\n",
+        html: "<p>_foo bar _</p>\n",
+        example: 370,
+        start_line: 6487,
+        end_line: 6491,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "_(_foo)\n",
-        "html": "<p>_(_foo)</p>\n",
-        "example": 371,
-        "start_line": 6497,
-        "end_line": 6501,
-        "section": "Emphasis and strong emphasis"
+        markdown: "_(_foo)\n",
+        html: "<p>_(_foo)</p>\n",
+        example: 371,
+        start_line: 6497,
+        end_line: 6501,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "_(_foo_)_\n",
-        "html": "<p><em>(<em>foo</em>)</em></p>\n",
-        "example": 372,
-        "start_line": 6506,
-        "end_line": 6510,
-        "section": "Emphasis and strong emphasis"
+        markdown: "_(_foo_)_\n",
+        html: "<p><em>(<em>foo</em>)</em></p>\n",
+        example: 372,
+        start_line: 6506,
+        end_line: 6510,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "_foo_bar\n",
-        "html": "<p>_foo_bar</p>\n",
-        "example": 373,
-        "start_line": 6515,
-        "end_line": 6519,
-        "section": "Emphasis and strong emphasis"
+        markdown: "_foo_bar\n",
+        html: "<p>_foo_bar</p>\n",
+        example: 373,
+        start_line: 6515,
+        end_line: 6519,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "_пристаням_стремятся\n",
-        "html": "<p>_пристаням_стремятся</p>\n",
-        "example": 374,
-        "start_line": 6522,
-        "end_line": 6526,
-        "section": "Emphasis and strong emphasis"
+        markdown: "_пристаням_стремятся\n",
+        html: "<p>_пристаням_стремятся</p>\n",
+        example: 374,
+        start_line: 6522,
+        end_line: 6526,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "_foo_bar_baz_\n",
-        "html": "<p><em>foo_bar_baz</em></p>\n",
-        "example": 375,
-        "start_line": 6529,
-        "end_line": 6533,
-        "section": "Emphasis and strong emphasis"
+        markdown: "_foo_bar_baz_\n",
+        html: "<p><em>foo_bar_baz</em></p>\n",
+        example: 375,
+        start_line: 6529,
+        end_line: 6533,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "_(bar)_.\n",
-        "html": "<p><em>(bar)</em>.</p>\n",
-        "example": 376,
-        "start_line": 6540,
-        "end_line": 6544,
-        "section": "Emphasis and strong emphasis"
+        markdown: "_(bar)_.\n",
+        html: "<p><em>(bar)</em>.</p>\n",
+        example: 376,
+        start_line: 6540,
+        end_line: 6544,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "**foo bar**\n",
-        "html": "<p><strong>foo bar</strong></p>\n",
-        "example": 377,
-        "start_line": 6549,
-        "end_line": 6553,
-        "section": "Emphasis and strong emphasis"
+        markdown: "**foo bar**\n",
+        html: "<p><strong>foo bar</strong></p>\n",
+        example: 377,
+        start_line: 6549,
+        end_line: 6553,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "** foo bar**\n",
-        "html": "<p>** foo bar**</p>\n",
-        "example": 378,
-        "start_line": 6559,
-        "end_line": 6563,
-        "section": "Emphasis and strong emphasis"
+        markdown: "** foo bar**\n",
+        html: "<p>** foo bar**</p>\n",
+        example: 378,
+        start_line: 6559,
+        end_line: 6563,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "a**\"foo\"**\n",
-        "html": "<p>a**&quot;foo&quot;**</p>\n",
-        "example": 379,
-        "start_line": 6570,
-        "end_line": 6574,
-        "section": "Emphasis and strong emphasis"
+        markdown: "a**\"foo\"**\n",
+        html: "<p>a**&quot;foo&quot;**</p>\n",
+        example: 379,
+        start_line: 6570,
+        end_line: 6574,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "foo**bar**\n",
-        "html": "<p>foo<strong>bar</strong></p>\n",
-        "example": 380,
-        "start_line": 6579,
-        "end_line": 6583,
-        "section": "Emphasis and strong emphasis"
+        markdown: "foo**bar**\n",
+        html: "<p>foo<strong>bar</strong></p>\n",
+        example: 380,
+        start_line: 6579,
+        end_line: 6583,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "__foo bar__\n",
-        "html": "<p><strong>foo bar</strong></p>\n",
-        "example": 381,
-        "start_line": 6588,
-        "end_line": 6592,
-        "section": "Emphasis and strong emphasis"
+        markdown: "__foo bar__\n",
+        html: "<p><strong>foo bar</strong></p>\n",
+        example: 381,
+        start_line: 6588,
+        end_line: 6592,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "__ foo bar__\n",
-        "html": "<p>__ foo bar__</p>\n",
-        "example": 382,
-        "start_line": 6598,
-        "end_line": 6602,
-        "section": "Emphasis and strong emphasis"
+        markdown: "__ foo bar__\n",
+        html: "<p>__ foo bar__</p>\n",
+        example: 382,
+        start_line: 6598,
+        end_line: 6602,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "__\nfoo bar__\n",
-        "html": "<p>__\nfoo bar__</p>\n",
-        "example": 383,
-        "start_line": 6606,
-        "end_line": 6612,
-        "section": "Emphasis and strong emphasis"
+        markdown: "__\nfoo bar__\n",
+        html: "<p>__\nfoo bar__</p>\n",
+        example: 383,
+        start_line: 6606,
+        end_line: 6612,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "a__\"foo\"__\n",
-        "html": "<p>a__&quot;foo&quot;__</p>\n",
-        "example": 384,
-        "start_line": 6618,
-        "end_line": 6622,
-        "section": "Emphasis and strong emphasis"
+        markdown: "a__\"foo\"__\n",
+        html: "<p>a__&quot;foo&quot;__</p>\n",
+        example: 384,
+        start_line: 6618,
+        end_line: 6622,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "foo__bar__\n",
-        "html": "<p>foo__bar__</p>\n",
-        "example": 385,
-        "start_line": 6627,
-        "end_line": 6631,
-        "section": "Emphasis and strong emphasis"
+        markdown: "foo__bar__\n",
+        html: "<p>foo__bar__</p>\n",
+        example: 385,
+        start_line: 6627,
+        end_line: 6631,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "5__6__78\n",
-        "html": "<p>5__6__78</p>\n",
-        "example": 386,
-        "start_line": 6634,
-        "end_line": 6638,
-        "section": "Emphasis and strong emphasis"
+        markdown: "5__6__78\n",
+        html: "<p>5__6__78</p>\n",
+        example: 386,
+        start_line: 6634,
+        end_line: 6638,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "пристаням__стремятся__\n",
-        "html": "<p>пристаням__стремятся__</p>\n",
-        "example": 387,
-        "start_line": 6641,
-        "end_line": 6645,
-        "section": "Emphasis and strong emphasis"
+        markdown: "пристаням__стремятся__\n",
+        html: "<p>пристаням__стремятся__</p>\n",
+        example: 387,
+        start_line: 6641,
+        end_line: 6645,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "__foo, __bar__, baz__\n",
-        "html": "<p><strong>foo, <strong>bar</strong>, baz</strong></p>\n",
-        "example": 388,
-        "start_line": 6648,
-        "end_line": 6652,
-        "section": "Emphasis and strong emphasis"
+        markdown: "__foo, __bar__, baz__\n",
+        html: "<p><strong>foo, <strong>bar</strong>, baz</strong></p>\n",
+        example: 388,
+        start_line: 6648,
+        end_line: 6652,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "foo-__(bar)__\n",
-        "html": "<p>foo-<strong>(bar)</strong></p>\n",
-        "example": 389,
-        "start_line": 6659,
-        "end_line": 6663,
-        "section": "Emphasis and strong emphasis"
+        markdown: "foo-__(bar)__\n",
+        html: "<p>foo-<strong>(bar)</strong></p>\n",
+        example: 389,
+        start_line: 6659,
+        end_line: 6663,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "**foo bar **\n",
-        "html": "<p>**foo bar **</p>\n",
-        "example": 390,
-        "start_line": 6672,
-        "end_line": 6676,
-        "section": "Emphasis and strong emphasis"
+        markdown: "**foo bar **\n",
+        html: "<p>**foo bar **</p>\n",
+        example: 390,
+        start_line: 6672,
+        end_line: 6676,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "**(**foo)\n",
-        "html": "<p>**(**foo)</p>\n",
-        "example": 391,
-        "start_line": 6685,
-        "end_line": 6689,
-        "section": "Emphasis and strong emphasis"
+        markdown: "**(**foo)\n",
+        html: "<p>**(**foo)</p>\n",
+        example: 391,
+        start_line: 6685,
+        end_line: 6689,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "*(**foo**)*\n",
-        "html": "<p><em>(<strong>foo</strong>)</em></p>\n",
-        "example": 392,
-        "start_line": 6695,
-        "end_line": 6699,
-        "section": "Emphasis and strong emphasis"
+        markdown: "*(**foo**)*\n",
+        html: "<p><em>(<strong>foo</strong>)</em></p>\n",
+        example: 392,
+        start_line: 6695,
+        end_line: 6699,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "**Gomphocarpus (*Gomphocarpus physocarpus*, syn.\n*Asclepias physocarpa*)**\n",
-        "html": "<p><strong>Gomphocarpus (<em>Gomphocarpus physocarpus</em>, syn.\n<em>Asclepias physocarpa</em>)</strong></p>\n",
-        "example": 393,
-        "start_line": 6702,
-        "end_line": 6708,
-        "section": "Emphasis and strong emphasis"
+        markdown: "**Gomphocarpus (*Gomphocarpus physocarpus*, syn.\n*Asclepias physocarpa*)**\n",
+        html: "<p><strong>Gomphocarpus (<em>Gomphocarpus physocarpus</em>, syn.\n<em>Asclepias physocarpa</em>)</strong></p>\n",
+        example: 393,
+        start_line: 6702,
+        end_line: 6708,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "**foo \"*bar*\" foo**\n",
-        "html": "<p><strong>foo &quot;<em>bar</em>&quot; foo</strong></p>\n",
-        "example": 394,
-        "start_line": 6711,
-        "end_line": 6715,
-        "section": "Emphasis and strong emphasis"
+        markdown: "**foo \"*bar*\" foo**\n",
+        html: "<p><strong>foo &quot;<em>bar</em>&quot; foo</strong></p>\n",
+        example: 394,
+        start_line: 6711,
+        end_line: 6715,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "**foo**bar\n",
-        "html": "<p><strong>foo</strong>bar</p>\n",
-        "example": 395,
-        "start_line": 6720,
-        "end_line": 6724,
-        "section": "Emphasis and strong emphasis"
+        markdown: "**foo**bar\n",
+        html: "<p><strong>foo</strong>bar</p>\n",
+        example: 395,
+        start_line: 6720,
+        end_line: 6724,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "__foo bar __\n",
-        "html": "<p>__foo bar __</p>\n",
-        "example": 396,
-        "start_line": 6732,
-        "end_line": 6736,
-        "section": "Emphasis and strong emphasis"
+        markdown: "__foo bar __\n",
+        html: "<p>__foo bar __</p>\n",
+        example: 396,
+        start_line: 6732,
+        end_line: 6736,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "__(__foo)\n",
-        "html": "<p>__(__foo)</p>\n",
-        "example": 397,
-        "start_line": 6742,
-        "end_line": 6746,
-        "section": "Emphasis and strong emphasis"
+        markdown: "__(__foo)\n",
+        html: "<p>__(__foo)</p>\n",
+        example: 397,
+        start_line: 6742,
+        end_line: 6746,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "_(__foo__)_\n",
-        "html": "<p><em>(<strong>foo</strong>)</em></p>\n",
-        "example": 398,
-        "start_line": 6752,
-        "end_line": 6756,
-        "section": "Emphasis and strong emphasis"
+        markdown: "_(__foo__)_\n",
+        html: "<p><em>(<strong>foo</strong>)</em></p>\n",
+        example: 398,
+        start_line: 6752,
+        end_line: 6756,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "__foo__bar\n",
-        "html": "<p>__foo__bar</p>\n",
-        "example": 399,
-        "start_line": 6761,
-        "end_line": 6765,
-        "section": "Emphasis and strong emphasis"
+        markdown: "__foo__bar\n",
+        html: "<p>__foo__bar</p>\n",
+        example: 399,
+        start_line: 6761,
+        end_line: 6765,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "__пристаням__стремятся\n",
-        "html": "<p>__пристаням__стремятся</p>\n",
-        "example": 400,
-        "start_line": 6768,
-        "end_line": 6772,
-        "section": "Emphasis and strong emphasis"
+        markdown: "__пристаням__стремятся\n",
+        html: "<p>__пристаням__стремятся</p>\n",
+        example: 400,
+        start_line: 6768,
+        end_line: 6772,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "__foo__bar__baz__\n",
-        "html": "<p><strong>foo__bar__baz</strong></p>\n",
-        "example": 401,
-        "start_line": 6775,
-        "end_line": 6779,
-        "section": "Emphasis and strong emphasis"
+        markdown: "__foo__bar__baz__\n",
+        html: "<p><strong>foo__bar__baz</strong></p>\n",
+        example: 401,
+        start_line: 6775,
+        end_line: 6779,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "__(bar)__.\n",
-        "html": "<p><strong>(bar)</strong>.</p>\n",
-        "example": 402,
-        "start_line": 6786,
-        "end_line": 6790,
-        "section": "Emphasis and strong emphasis"
+        markdown: "__(bar)__.\n",
+        html: "<p><strong>(bar)</strong>.</p>\n",
+        example: 402,
+        start_line: 6786,
+        end_line: 6790,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "*foo [bar](/url)*\n",
-        "html": "<p><em>foo <a href=\"/url\">bar</a></em></p>\n",
-        "example": 403,
-        "start_line": 6798,
-        "end_line": 6802,
-        "section": "Emphasis and strong emphasis"
+        markdown: "*foo [bar](/url)*\n",
+        html: "<p><em>foo <a href=\"/url\">bar</a></em></p>\n",
+        example: 403,
+        start_line: 6798,
+        end_line: 6802,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "*foo\nbar*\n",
-        "html": "<p><em>foo\nbar</em></p>\n",
-        "example": 404,
-        "start_line": 6805,
-        "end_line": 6811,
-        "section": "Emphasis and strong emphasis"
+        markdown: "*foo\nbar*\n",
+        html: "<p><em>foo\nbar</em></p>\n",
+        example: 404,
+        start_line: 6805,
+        end_line: 6811,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "_foo __bar__ baz_\n",
-        "html": "<p><em>foo <strong>bar</strong> baz</em></p>\n",
-        "example": 405,
-        "start_line": 6817,
-        "end_line": 6821,
-        "section": "Emphasis and strong emphasis"
+        markdown: "_foo __bar__ baz_\n",
+        html: "<p><em>foo <strong>bar</strong> baz</em></p>\n",
+        example: 405,
+        start_line: 6817,
+        end_line: 6821,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "_foo _bar_ baz_\n",
-        "html": "<p><em>foo <em>bar</em> baz</em></p>\n",
-        "example": 406,
-        "start_line": 6824,
-        "end_line": 6828,
-        "section": "Emphasis and strong emphasis"
+        markdown: "_foo _bar_ baz_\n",
+        html: "<p><em>foo <em>bar</em> baz</em></p>\n",
+        example: 406,
+        start_line: 6824,
+        end_line: 6828,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "__foo_ bar_\n",
-        "html": "<p><em><em>foo</em> bar</em></p>\n",
-        "example": 407,
-        "start_line": 6831,
-        "end_line": 6835,
-        "section": "Emphasis and strong emphasis"
+        markdown: "__foo_ bar_\n",
+        html: "<p><em><em>foo</em> bar</em></p>\n",
+        example: 407,
+        start_line: 6831,
+        end_line: 6835,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "*foo *bar**\n",
-        "html": "<p><em>foo <em>bar</em></em></p>\n",
-        "example": 408,
-        "start_line": 6838,
-        "end_line": 6842,
-        "section": "Emphasis and strong emphasis"
+        markdown: "*foo *bar**\n",
+        html: "<p><em>foo <em>bar</em></em></p>\n",
+        example: 408,
+        start_line: 6838,
+        end_line: 6842,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "*foo **bar** baz*\n",
-        "html": "<p><em>foo <strong>bar</strong> baz</em></p>\n",
-        "example": 409,
-        "start_line": 6845,
-        "end_line": 6849,
-        "section": "Emphasis and strong emphasis"
+        markdown: "*foo **bar** baz*\n",
+        html: "<p><em>foo <strong>bar</strong> baz</em></p>\n",
+        example: 409,
+        start_line: 6845,
+        end_line: 6849,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "*foo**bar**baz*\n",
-        "html": "<p><em>foo<strong>bar</strong>baz</em></p>\n",
-        "example": 410,
-        "start_line": 6851,
-        "end_line": 6855,
-        "section": "Emphasis and strong emphasis"
+        markdown: "*foo**bar**baz*\n",
+        html: "<p><em>foo<strong>bar</strong>baz</em></p>\n",
+        example: 410,
+        start_line: 6851,
+        end_line: 6855,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "*foo**bar*\n",
-        "html": "<p><em>foo**bar</em></p>\n",
-        "example": 411,
-        "start_line": 6875,
-        "end_line": 6879,
-        "section": "Emphasis and strong emphasis"
+        markdown: "*foo**bar*\n",
+        html: "<p><em>foo**bar</em></p>\n",
+        example: 411,
+        start_line: 6875,
+        end_line: 6879,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "***foo** bar*\n",
-        "html": "<p><em><strong>foo</strong> bar</em></p>\n",
-        "example": 412,
-        "start_line": 6888,
-        "end_line": 6892,
-        "section": "Emphasis and strong emphasis"
+        markdown: "***foo** bar*\n",
+        html: "<p><em><strong>foo</strong> bar</em></p>\n",
+        example: 412,
+        start_line: 6888,
+        end_line: 6892,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "*foo **bar***\n",
-        "html": "<p><em>foo <strong>bar</strong></em></p>\n",
-        "example": 413,
-        "start_line": 6895,
-        "end_line": 6899,
-        "section": "Emphasis and strong emphasis"
+        markdown: "*foo **bar***\n",
+        html: "<p><em>foo <strong>bar</strong></em></p>\n",
+        example: 413,
+        start_line: 6895,
+        end_line: 6899,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "*foo**bar***\n",
-        "html": "<p><em>foo<strong>bar</strong></em></p>\n",
-        "example": 414,
-        "start_line": 6902,
-        "end_line": 6906,
-        "section": "Emphasis and strong emphasis"
+        markdown: "*foo**bar***\n",
+        html: "<p><em>foo<strong>bar</strong></em></p>\n",
+        example: 414,
+        start_line: 6902,
+        end_line: 6906,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "foo***bar***baz\n",
-        "html": "<p>foo<em><strong>bar</strong></em>baz</p>\n",
-        "example": 415,
-        "start_line": 6913,
-        "end_line": 6917,
-        "section": "Emphasis and strong emphasis"
+        markdown: "foo***bar***baz\n",
+        html: "<p>foo<em><strong>bar</strong></em>baz</p>\n",
+        example: 415,
+        start_line: 6913,
+        end_line: 6917,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "foo******bar*********baz\n",
-        "html": "<p>foo<strong><strong><strong>bar</strong></strong></strong>***baz</p>\n",
-        "example": 416,
-        "start_line": 6919,
-        "end_line": 6923,
-        "section": "Emphasis and strong emphasis"
+        markdown: "foo******bar*********baz\n",
+        html: "<p>foo<strong><strong><strong>bar</strong></strong></strong>***baz</p>\n",
+        example: 416,
+        start_line: 6919,
+        end_line: 6923,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "*foo **bar *baz* bim** bop*\n",
-        "html": "<p><em>foo <strong>bar <em>baz</em> bim</strong> bop</em></p>\n",
-        "example": 417,
-        "start_line": 6928,
-        "end_line": 6932,
-        "section": "Emphasis and strong emphasis"
+        markdown: "*foo **bar *baz* bim** bop*\n",
+        html: "<p><em>foo <strong>bar <em>baz</em> bim</strong> bop</em></p>\n",
+        example: 417,
+        start_line: 6928,
+        end_line: 6932,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "*foo [*bar*](/url)*\n",
-        "html": "<p><em>foo <a href=\"/url\"><em>bar</em></a></em></p>\n",
-        "example": 418,
-        "start_line": 6935,
-        "end_line": 6939,
-        "section": "Emphasis and strong emphasis"
+        markdown: "*foo [*bar*](/url)*\n",
+        html: "<p><em>foo <a href=\"/url\"><em>bar</em></a></em></p>\n",
+        example: 418,
+        start_line: 6935,
+        end_line: 6939,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "** is not an empty emphasis\n",
-        "html": "<p>** is not an empty emphasis</p>\n",
-        "example": 419,
-        "start_line": 6944,
-        "end_line": 6948,
-        "section": "Emphasis and strong emphasis"
+        markdown: "** is not an empty emphasis\n",
+        html: "<p>** is not an empty emphasis</p>\n",
+        example: 419,
+        start_line: 6944,
+        end_line: 6948,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "**** is not an empty strong emphasis\n",
-        "html": "<p>**** is not an empty strong emphasis</p>\n",
-        "example": 420,
-        "start_line": 6951,
-        "end_line": 6955,
-        "section": "Emphasis and strong emphasis"
+        markdown: "**** is not an empty strong emphasis\n",
+        html: "<p>**** is not an empty strong emphasis</p>\n",
+        example: 420,
+        start_line: 6951,
+        end_line: 6955,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "**foo [bar](/url)**\n",
-        "html": "<p><strong>foo <a href=\"/url\">bar</a></strong></p>\n",
-        "example": 421,
-        "start_line": 6964,
-        "end_line": 6968,
-        "section": "Emphasis and strong emphasis"
+        markdown: "**foo [bar](/url)**\n",
+        html: "<p><strong>foo <a href=\"/url\">bar</a></strong></p>\n",
+        example: 421,
+        start_line: 6964,
+        end_line: 6968,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "**foo\nbar**\n",
-        "html": "<p><strong>foo\nbar</strong></p>\n",
-        "example": 422,
-        "start_line": 6971,
-        "end_line": 6977,
-        "section": "Emphasis and strong emphasis"
+        markdown: "**foo\nbar**\n",
+        html: "<p><strong>foo\nbar</strong></p>\n",
+        example: 422,
+        start_line: 6971,
+        end_line: 6977,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "__foo _bar_ baz__\n",
-        "html": "<p><strong>foo <em>bar</em> baz</strong></p>\n",
-        "example": 423,
-        "start_line": 6983,
-        "end_line": 6987,
-        "section": "Emphasis and strong emphasis"
+        markdown: "__foo _bar_ baz__\n",
+        html: "<p><strong>foo <em>bar</em> baz</strong></p>\n",
+        example: 423,
+        start_line: 6983,
+        end_line: 6987,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "__foo __bar__ baz__\n",
-        "html": "<p><strong>foo <strong>bar</strong> baz</strong></p>\n",
-        "example": 424,
-        "start_line": 6990,
-        "end_line": 6994,
-        "section": "Emphasis and strong emphasis"
+        markdown: "__foo __bar__ baz__\n",
+        html: "<p><strong>foo <strong>bar</strong> baz</strong></p>\n",
+        example: 424,
+        start_line: 6990,
+        end_line: 6994,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "____foo__ bar__\n",
-        "html": "<p><strong><strong>foo</strong> bar</strong></p>\n",
-        "example": 425,
-        "start_line": 6997,
-        "end_line": 7001,
-        "section": "Emphasis and strong emphasis"
+        markdown: "____foo__ bar__\n",
+        html: "<p><strong><strong>foo</strong> bar</strong></p>\n",
+        example: 425,
+        start_line: 6997,
+        end_line: 7001,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "**foo **bar****\n",
-        "html": "<p><strong>foo <strong>bar</strong></strong></p>\n",
-        "example": 426,
-        "start_line": 7004,
-        "end_line": 7008,
-        "section": "Emphasis and strong emphasis"
+        markdown: "**foo **bar****\n",
+        html: "<p><strong>foo <strong>bar</strong></strong></p>\n",
+        example: 426,
+        start_line: 7004,
+        end_line: 7008,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "**foo *bar* baz**\n",
-        "html": "<p><strong>foo <em>bar</em> baz</strong></p>\n",
-        "example": 427,
-        "start_line": 7011,
-        "end_line": 7015,
-        "section": "Emphasis and strong emphasis"
+        markdown: "**foo *bar* baz**\n",
+        html: "<p><strong>foo <em>bar</em> baz</strong></p>\n",
+        example: 427,
+        start_line: 7011,
+        end_line: 7015,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "**foo*bar*baz**\n",
-        "html": "<p><strong>foo<em>bar</em>baz</strong></p>\n",
-        "example": 428,
-        "start_line": 7018,
-        "end_line": 7022,
-        "section": "Emphasis and strong emphasis"
+        markdown: "**foo*bar*baz**\n",
+        html: "<p><strong>foo<em>bar</em>baz</strong></p>\n",
+        example: 428,
+        start_line: 7018,
+        end_line: 7022,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "***foo* bar**\n",
-        "html": "<p><strong><em>foo</em> bar</strong></p>\n",
-        "example": 429,
-        "start_line": 7025,
-        "end_line": 7029,
-        "section": "Emphasis and strong emphasis"
+        markdown: "***foo* bar**\n",
+        html: "<p><strong><em>foo</em> bar</strong></p>\n",
+        example: 429,
+        start_line: 7025,
+        end_line: 7029,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "**foo *bar***\n",
-        "html": "<p><strong>foo <em>bar</em></strong></p>\n",
-        "example": 430,
-        "start_line": 7032,
-        "end_line": 7036,
-        "section": "Emphasis and strong emphasis"
+        markdown: "**foo *bar***\n",
+        html: "<p><strong>foo <em>bar</em></strong></p>\n",
+        example: 430,
+        start_line: 7032,
+        end_line: 7036,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "**foo *bar **baz**\nbim* bop**\n",
-        "html": "<p><strong>foo <em>bar <strong>baz</strong>\nbim</em> bop</strong></p>\n",
-        "example": 431,
-        "start_line": 7041,
-        "end_line": 7047,
-        "section": "Emphasis and strong emphasis"
+        markdown: "**foo *bar **baz**\nbim* bop**\n",
+        html: "<p><strong>foo <em>bar <strong>baz</strong>\nbim</em> bop</strong></p>\n",
+        example: 431,
+        start_line: 7041,
+        end_line: 7047,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "**foo [*bar*](/url)**\n",
-        "html": "<p><strong>foo <a href=\"/url\"><em>bar</em></a></strong></p>\n",
-        "example": 432,
-        "start_line": 7050,
-        "end_line": 7054,
-        "section": "Emphasis and strong emphasis"
+        markdown: "**foo [*bar*](/url)**\n",
+        html: "<p><strong>foo <a href=\"/url\"><em>bar</em></a></strong></p>\n",
+        example: 432,
+        start_line: 7050,
+        end_line: 7054,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "__ is not an empty emphasis\n",
-        "html": "<p>__ is not an empty emphasis</p>\n",
-        "example": 433,
-        "start_line": 7059,
-        "end_line": 7063,
-        "section": "Emphasis and strong emphasis"
+        markdown: "__ is not an empty emphasis\n",
+        html: "<p>__ is not an empty emphasis</p>\n",
+        example: 433,
+        start_line: 7059,
+        end_line: 7063,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "____ is not an empty strong emphasis\n",
-        "html": "<p>____ is not an empty strong emphasis</p>\n",
-        "example": 434,
-        "start_line": 7066,
-        "end_line": 7070,
-        "section": "Emphasis and strong emphasis"
+        markdown: "____ is not an empty strong emphasis\n",
+        html: "<p>____ is not an empty strong emphasis</p>\n",
+        example: 434,
+        start_line: 7066,
+        end_line: 7070,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "foo ***\n",
-        "html": "<p>foo ***</p>\n",
-        "example": 435,
-        "start_line": 7076,
-        "end_line": 7080,
-        "section": "Emphasis and strong emphasis"
+        markdown: "foo ***\n",
+        html: "<p>foo ***</p>\n",
+        example: 435,
+        start_line: 7076,
+        end_line: 7080,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "foo *\\**\n",
-        "html": "<p>foo <em>*</em></p>\n",
-        "example": 436,
-        "start_line": 7083,
-        "end_line": 7087,
-        "section": "Emphasis and strong emphasis"
+        markdown: "foo *\\**\n",
+        html: "<p>foo <em>*</em></p>\n",
+        example: 436,
+        start_line: 7083,
+        end_line: 7087,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "foo *_*\n",
-        "html": "<p>foo <em>_</em></p>\n",
-        "example": 437,
-        "start_line": 7090,
-        "end_line": 7094,
-        "section": "Emphasis and strong emphasis"
+        markdown: "foo *_*\n",
+        html: "<p>foo <em>_</em></p>\n",
+        example: 437,
+        start_line: 7090,
+        end_line: 7094,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "foo *****\n",
-        "html": "<p>foo *****</p>\n",
-        "example": 438,
-        "start_line": 7097,
-        "end_line": 7101,
-        "section": "Emphasis and strong emphasis"
+        markdown: "foo *****\n",
+        html: "<p>foo *****</p>\n",
+        example: 438,
+        start_line: 7097,
+        end_line: 7101,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "foo **\\***\n",
-        "html": "<p>foo <strong>*</strong></p>\n",
-        "example": 439,
-        "start_line": 7104,
-        "end_line": 7108,
-        "section": "Emphasis and strong emphasis"
+        markdown: "foo **\\***\n",
+        html: "<p>foo <strong>*</strong></p>\n",
+        example: 439,
+        start_line: 7104,
+        end_line: 7108,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "foo **_**\n",
-        "html": "<p>foo <strong>_</strong></p>\n",
-        "example": 440,
-        "start_line": 7111,
-        "end_line": 7115,
-        "section": "Emphasis and strong emphasis"
+        markdown: "foo **_**\n",
+        html: "<p>foo <strong>_</strong></p>\n",
+        example: 440,
+        start_line: 7111,
+        end_line: 7115,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "**foo*\n",
-        "html": "<p>*<em>foo</em></p>\n",
-        "example": 441,
-        "start_line": 7122,
-        "end_line": 7126,
-        "section": "Emphasis and strong emphasis"
+        markdown: "**foo*\n",
+        html: "<p>*<em>foo</em></p>\n",
+        example: 441,
+        start_line: 7122,
+        end_line: 7126,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "*foo**\n",
-        "html": "<p><em>foo</em>*</p>\n",
-        "example": 442,
-        "start_line": 7129,
-        "end_line": 7133,
-        "section": "Emphasis and strong emphasis"
+        markdown: "*foo**\n",
+        html: "<p><em>foo</em>*</p>\n",
+        example: 442,
+        start_line: 7129,
+        end_line: 7133,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "***foo**\n",
-        "html": "<p>*<strong>foo</strong></p>\n",
-        "example": 443,
-        "start_line": 7136,
-        "end_line": 7140,
-        "section": "Emphasis and strong emphasis"
+        markdown: "***foo**\n",
+        html: "<p>*<strong>foo</strong></p>\n",
+        example: 443,
+        start_line: 7136,
+        end_line: 7140,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "****foo*\n",
-        "html": "<p>***<em>foo</em></p>\n",
-        "example": 444,
-        "start_line": 7143,
-        "end_line": 7147,
-        "section": "Emphasis and strong emphasis"
+        markdown: "****foo*\n",
+        html: "<p>***<em>foo</em></p>\n",
+        example: 444,
+        start_line: 7143,
+        end_line: 7147,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "**foo***\n",
-        "html": "<p><strong>foo</strong>*</p>\n",
-        "example": 445,
-        "start_line": 7150,
-        "end_line": 7154,
-        "section": "Emphasis and strong emphasis"
+        markdown: "**foo***\n",
+        html: "<p><strong>foo</strong>*</p>\n",
+        example: 445,
+        start_line: 7150,
+        end_line: 7154,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "*foo****\n",
-        "html": "<p><em>foo</em>***</p>\n",
-        "example": 446,
-        "start_line": 7157,
-        "end_line": 7161,
-        "section": "Emphasis and strong emphasis"
+        markdown: "*foo****\n",
+        html: "<p><em>foo</em>***</p>\n",
+        example: 446,
+        start_line: 7157,
+        end_line: 7161,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "foo ___\n",
-        "html": "<p>foo ___</p>\n",
-        "example": 447,
-        "start_line": 7167,
-        "end_line": 7171,
-        "section": "Emphasis and strong emphasis"
+        markdown: "foo ___\n",
+        html: "<p>foo ___</p>\n",
+        example: 447,
+        start_line: 7167,
+        end_line: 7171,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "foo _\\__\n",
-        "html": "<p>foo <em>_</em></p>\n",
-        "example": 448,
-        "start_line": 7174,
-        "end_line": 7178,
-        "section": "Emphasis and strong emphasis"
+        markdown: "foo _\\__\n",
+        html: "<p>foo <em>_</em></p>\n",
+        example: 448,
+        start_line: 7174,
+        end_line: 7178,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "foo _*_\n",
-        "html": "<p>foo <em>*</em></p>\n",
-        "example": 449,
-        "start_line": 7181,
-        "end_line": 7185,
-        "section": "Emphasis and strong emphasis"
+        markdown: "foo _*_\n",
+        html: "<p>foo <em>*</em></p>\n",
+        example: 449,
+        start_line: 7181,
+        end_line: 7185,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "foo _____\n",
-        "html": "<p>foo _____</p>\n",
-        "example": 450,
-        "start_line": 7188,
-        "end_line": 7192,
-        "section": "Emphasis and strong emphasis"
+        markdown: "foo _____\n",
+        html: "<p>foo _____</p>\n",
+        example: 450,
+        start_line: 7188,
+        end_line: 7192,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "foo __\\___\n",
-        "html": "<p>foo <strong>_</strong></p>\n",
-        "example": 451,
-        "start_line": 7195,
-        "end_line": 7199,
-        "section": "Emphasis and strong emphasis"
+        markdown: "foo __\\___\n",
+        html: "<p>foo <strong>_</strong></p>\n",
+        example: 451,
+        start_line: 7195,
+        end_line: 7199,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "foo __*__\n",
-        "html": "<p>foo <strong>*</strong></p>\n",
-        "example": 452,
-        "start_line": 7202,
-        "end_line": 7206,
-        "section": "Emphasis and strong emphasis"
+        markdown: "foo __*__\n",
+        html: "<p>foo <strong>*</strong></p>\n",
+        example: 452,
+        start_line: 7202,
+        end_line: 7206,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "__foo_\n",
-        "html": "<p>_<em>foo</em></p>\n",
-        "example": 453,
-        "start_line": 7209,
-        "end_line": 7213,
-        "section": "Emphasis and strong emphasis"
+        markdown: "__foo_\n",
+        html: "<p>_<em>foo</em></p>\n",
+        example: 453,
+        start_line: 7209,
+        end_line: 7213,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "_foo__\n",
-        "html": "<p><em>foo</em>_</p>\n",
-        "example": 454,
-        "start_line": 7220,
-        "end_line": 7224,
-        "section": "Emphasis and strong emphasis"
+        markdown: "_foo__\n",
+        html: "<p><em>foo</em>_</p>\n",
+        example: 454,
+        start_line: 7220,
+        end_line: 7224,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "___foo__\n",
-        "html": "<p>_<strong>foo</strong></p>\n",
-        "example": 455,
-        "start_line": 7227,
-        "end_line": 7231,
-        "section": "Emphasis and strong emphasis"
+        markdown: "___foo__\n",
+        html: "<p>_<strong>foo</strong></p>\n",
+        example: 455,
+        start_line: 7227,
+        end_line: 7231,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "____foo_\n",
-        "html": "<p>___<em>foo</em></p>\n",
-        "example": 456,
-        "start_line": 7234,
-        "end_line": 7238,
-        "section": "Emphasis and strong emphasis"
+        markdown: "____foo_\n",
+        html: "<p>___<em>foo</em></p>\n",
+        example: 456,
+        start_line: 7234,
+        end_line: 7238,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "__foo___\n",
-        "html": "<p><strong>foo</strong>_</p>\n",
-        "example": 457,
-        "start_line": 7241,
-        "end_line": 7245,
-        "section": "Emphasis and strong emphasis"
+        markdown: "__foo___\n",
+        html: "<p><strong>foo</strong>_</p>\n",
+        example: 457,
+        start_line: 7241,
+        end_line: 7245,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "_foo____\n",
-        "html": "<p><em>foo</em>___</p>\n",
-        "example": 458,
-        "start_line": 7248,
-        "end_line": 7252,
-        "section": "Emphasis and strong emphasis"
+        markdown: "_foo____\n",
+        html: "<p><em>foo</em>___</p>\n",
+        example: 458,
+        start_line: 7248,
+        end_line: 7252,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "**foo**\n",
-        "html": "<p><strong>foo</strong></p>\n",
-        "example": 459,
-        "start_line": 7258,
-        "end_line": 7262,
-        "section": "Emphasis and strong emphasis"
+        markdown: "**foo**\n",
+        html: "<p><strong>foo</strong></p>\n",
+        example: 459,
+        start_line: 7258,
+        end_line: 7262,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "*_foo_*\n",
-        "html": "<p><em><em>foo</em></em></p>\n",
-        "example": 460,
-        "start_line": 7265,
-        "end_line": 7269,
-        "section": "Emphasis and strong emphasis"
+        markdown: "*_foo_*\n",
+        html: "<p><em><em>foo</em></em></p>\n",
+        example: 460,
+        start_line: 7265,
+        end_line: 7269,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "__foo__\n",
-        "html": "<p><strong>foo</strong></p>\n",
-        "example": 461,
-        "start_line": 7272,
-        "end_line": 7276,
-        "section": "Emphasis and strong emphasis"
+        markdown: "__foo__\n",
+        html: "<p><strong>foo</strong></p>\n",
+        example: 461,
+        start_line: 7272,
+        end_line: 7276,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "_*foo*_\n",
-        "html": "<p><em><em>foo</em></em></p>\n",
-        "example": 462,
-        "start_line": 7279,
-        "end_line": 7283,
-        "section": "Emphasis and strong emphasis"
+        markdown: "_*foo*_\n",
+        html: "<p><em><em>foo</em></em></p>\n",
+        example: 462,
+        start_line: 7279,
+        end_line: 7283,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "****foo****\n",
-        "html": "<p><strong><strong>foo</strong></strong></p>\n",
-        "example": 463,
-        "start_line": 7289,
-        "end_line": 7293,
-        "section": "Emphasis and strong emphasis"
+        markdown: "****foo****\n",
+        html: "<p><strong><strong>foo</strong></strong></p>\n",
+        example: 463,
+        start_line: 7289,
+        end_line: 7293,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "____foo____\n",
-        "html": "<p><strong><strong>foo</strong></strong></p>\n",
-        "example": 464,
-        "start_line": 7296,
-        "end_line": 7300,
-        "section": "Emphasis and strong emphasis"
+        markdown: "____foo____\n",
+        html: "<p><strong><strong>foo</strong></strong></p>\n",
+        example: 464,
+        start_line: 7296,
+        end_line: 7300,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "******foo******\n",
-        "html": "<p><strong><strong><strong>foo</strong></strong></strong></p>\n",
-        "example": 465,
-        "start_line": 7307,
-        "end_line": 7311,
-        "section": "Emphasis and strong emphasis"
+        markdown: "******foo******\n",
+        html: "<p><strong><strong><strong>foo</strong></strong></strong></p>\n",
+        example: 465,
+        start_line: 7307,
+        end_line: 7311,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "***foo***\n",
-        "html": "<p><em><strong>foo</strong></em></p>\n",
-        "example": 466,
-        "start_line": 7316,
-        "end_line": 7320,
-        "section": "Emphasis and strong emphasis"
+        markdown: "***foo***\n",
+        html: "<p><em><strong>foo</strong></em></p>\n",
+        example: 466,
+        start_line: 7316,
+        end_line: 7320,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "_____foo_____\n",
-        "html": "<p><em><strong><strong>foo</strong></strong></em></p>\n",
-        "example": 467,
-        "start_line": 7323,
-        "end_line": 7327,
-        "section": "Emphasis and strong emphasis"
+        markdown: "_____foo_____\n",
+        html: "<p><em><strong><strong>foo</strong></strong></em></p>\n",
+        example: 467,
+        start_line: 7323,
+        end_line: 7327,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "*foo _bar* baz_\n",
-        "html": "<p><em>foo _bar</em> baz_</p>\n",
-        "example": 468,
-        "start_line": 7332,
-        "end_line": 7336,
-        "section": "Emphasis and strong emphasis"
+        markdown: "*foo _bar* baz_\n",
+        html: "<p><em>foo _bar</em> baz_</p>\n",
+        example: 468,
+        start_line: 7332,
+        end_line: 7336,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "*foo __bar *baz bim__ bam*\n",
-        "html": "<p><em>foo <strong>bar *baz bim</strong> bam</em></p>\n",
-        "example": 469,
-        "start_line": 7339,
-        "end_line": 7343,
-        "section": "Emphasis and strong emphasis"
+        markdown: "*foo __bar *baz bim__ bam*\n",
+        html: "<p><em>foo <strong>bar *baz bim</strong> bam</em></p>\n",
+        example: 469,
+        start_line: 7339,
+        end_line: 7343,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "**foo **bar baz**\n",
-        "html": "<p>**foo <strong>bar baz</strong></p>\n",
-        "example": 470,
-        "start_line": 7348,
-        "end_line": 7352,
-        "section": "Emphasis and strong emphasis"
+        markdown: "**foo **bar baz**\n",
+        html: "<p>**foo <strong>bar baz</strong></p>\n",
+        example: 470,
+        start_line: 7348,
+        end_line: 7352,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "*foo *bar baz*\n",
-        "html": "<p>*foo <em>bar baz</em></p>\n",
-        "example": 471,
-        "start_line": 7355,
-        "end_line": 7359,
-        "section": "Emphasis and strong emphasis"
+        markdown: "*foo *bar baz*\n",
+        html: "<p>*foo <em>bar baz</em></p>\n",
+        example: 471,
+        start_line: 7355,
+        end_line: 7359,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "*[bar*](/url)\n",
-        "html": "<p>*<a href=\"/url\">bar*</a></p>\n",
-        "example": 472,
-        "start_line": 7364,
-        "end_line": 7368,
-        "section": "Emphasis and strong emphasis"
+        markdown: "*[bar*](/url)\n",
+        html: "<p>*<a href=\"/url\">bar*</a></p>\n",
+        example: 472,
+        start_line: 7364,
+        end_line: 7368,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "_foo [bar_](/url)\n",
-        "html": "<p>_foo <a href=\"/url\">bar_</a></p>\n",
-        "example": 473,
-        "start_line": 7371,
-        "end_line": 7375,
-        "section": "Emphasis and strong emphasis"
+        markdown: "_foo [bar_](/url)\n",
+        html: "<p>_foo <a href=\"/url\">bar_</a></p>\n",
+        example: 473,
+        start_line: 7371,
+        end_line: 7375,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "*<img src=\"foo\" title=\"*\"/>\n",
-        "html": "<p>*<img src=\"foo\" title=\"*\"/></p>\n",
-        "example": 474,
-        "start_line": 7378,
-        "end_line": 7382,
-        "section": "Emphasis and strong emphasis"
+        markdown: "*<img src=\"foo\" title=\"*\"/>\n",
+        html: "<p>*<img src=\"foo\" title=\"*\"/></p>\n",
+        example: 474,
+        start_line: 7378,
+        end_line: 7382,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "**<a href=\"**\">\n",
-        "html": "<p>**<a href=\"**\"></p>\n",
-        "example": 475,
-        "start_line": 7385,
-        "end_line": 7389,
-        "section": "Emphasis and strong emphasis"
+        markdown: "**<a href=\"**\">\n",
+        html: "<p>**<a href=\"**\"></p>\n",
+        example: 475,
+        start_line: 7385,
+        end_line: 7389,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "__<a href=\"__\">\n",
-        "html": "<p>__<a href=\"__\"></p>\n",
-        "example": 476,
-        "start_line": 7392,
-        "end_line": 7396,
-        "section": "Emphasis and strong emphasis"
+        markdown: "__<a href=\"__\">\n",
+        html: "<p>__<a href=\"__\"></p>\n",
+        example: 476,
+        start_line: 7392,
+        end_line: 7396,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "*a `*`*\n",
-        "html": "<p><em>a <code>*</code></em></p>\n",
-        "example": 477,
-        "start_line": 7399,
-        "end_line": 7403,
-        "section": "Emphasis and strong emphasis"
+        markdown: "*a `*`*\n",
+        html: "<p><em>a <code>*</code></em></p>\n",
+        example: 477,
+        start_line: 7399,
+        end_line: 7403,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "_a `_`_\n",
-        "html": "<p><em>a <code>_</code></em></p>\n",
-        "example": 478,
-        "start_line": 7406,
-        "end_line": 7410,
-        "section": "Emphasis and strong emphasis"
+        markdown: "_a `_`_\n",
+        html: "<p><em>a <code>_</code></em></p>\n",
+        example: 478,
+        start_line: 7406,
+        end_line: 7410,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "**a<http://foo.bar/?q=**>\n",
-        "html": "<p>**a<a href=\"http://foo.bar/?q=**\">http://foo.bar/?q=**</a></p>\n",
-        "example": 479,
-        "start_line": 7413,
-        "end_line": 7417,
-        "section": "Emphasis and strong emphasis"
+        markdown: "**a<http://foo.bar/?q=**>\n",
+        html: "<p>**a<a href=\"http://foo.bar/?q=**\">http://foo.bar/?q=**</a></p>\n",
+        example: 479,
+        start_line: 7413,
+        end_line: 7417,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "__a<http://foo.bar/?q=__>\n",
-        "html": "<p>__a<a href=\"http://foo.bar/?q=__\">http://foo.bar/?q=__</a></p>\n",
-        "example": 480,
-        "start_line": 7420,
-        "end_line": 7424,
-        "section": "Emphasis and strong emphasis"
+        markdown: "__a<http://foo.bar/?q=__>\n",
+        html: "<p>__a<a href=\"http://foo.bar/?q=__\">http://foo.bar/?q=__</a></p>\n",
+        example: 480,
+        start_line: 7420,
+        end_line: 7424,
+        section: "Emphasis and strong emphasis",
     },
     {
-        "markdown": "[link](/uri \"title\")\n",
-        "html": "<p><a href=\"/uri\" title=\"title\">link</a></p>\n",
-        "example": 481,
-        "start_line": 7503,
-        "end_line": 7507,
-        "section": "Links"
+        markdown: "[link](/uri \"title\")\n",
+        html: "<p><a href=\"/uri\" title=\"title\">link</a></p>\n",
+        example: 481,
+        start_line: 7503,
+        end_line: 7507,
+        section: "Links",
     },
     {
-        "markdown": "[link](/uri)\n",
-        "html": "<p><a href=\"/uri\">link</a></p>\n",
-        "example": 482,
-        "start_line": 7512,
-        "end_line": 7516,
-        "section": "Links"
+        markdown: "[link](/uri)\n",
+        html: "<p><a href=\"/uri\">link</a></p>\n",
+        example: 482,
+        start_line: 7512,
+        end_line: 7516,
+        section: "Links",
     },
     {
-        "markdown": "[link]()\n",
-        "html": "<p><a href=\"\">link</a></p>\n",
-        "example": 483,
-        "start_line": 7521,
-        "end_line": 7525,
-        "section": "Links"
+        markdown: "[link]()\n",
+        html: "<p><a href=\"\">link</a></p>\n",
+        example: 483,
+        start_line: 7521,
+        end_line: 7525,
+        section: "Links",
     },
     {
-        "markdown": "[link](<>)\n",
-        "html": "<p><a href=\"\">link</a></p>\n",
-        "example": 484,
-        "start_line": 7528,
-        "end_line": 7532,
-        "section": "Links"
+        markdown: "[link](<>)\n",
+        html: "<p><a href=\"\">link</a></p>\n",
+        example: 484,
+        start_line: 7528,
+        end_line: 7532,
+        section: "Links",
     },
     {
-        "markdown": "[link](/my uri)\n",
-        "html": "<p>[link](/my uri)</p>\n",
-        "example": 485,
-        "start_line": 7537,
-        "end_line": 7541,
-        "section": "Links"
+        markdown: "[link](/my uri)\n",
+        html: "<p>[link](/my uri)</p>\n",
+        example: 485,
+        start_line: 7537,
+        end_line: 7541,
+        section: "Links",
     },
     {
-        "markdown": "[link](</my uri>)\n",
-        "html": "<p><a href=\"/my%20uri\">link</a></p>\n",
-        "example": 486,
-        "start_line": 7543,
-        "end_line": 7547,
-        "section": "Links"
+        markdown: "[link](</my uri>)\n",
+        html: "<p><a href=\"/my%20uri\">link</a></p>\n",
+        example: 486,
+        start_line: 7543,
+        end_line: 7547,
+        section: "Links",
     },
     {
-        "markdown": "[link](foo\nbar)\n",
-        "html": "<p>[link](foo\nbar)</p>\n",
-        "example": 487,
-        "start_line": 7552,
-        "end_line": 7558,
-        "section": "Links"
+        markdown: "[link](foo\nbar)\n",
+        html: "<p>[link](foo\nbar)</p>\n",
+        example: 487,
+        start_line: 7552,
+        end_line: 7558,
+        section: "Links",
     },
     {
-        "markdown": "[link](<foo\nbar>)\n",
-        "html": "<p>[link](<foo\nbar>)</p>\n",
-        "example": 488,
-        "start_line": 7560,
-        "end_line": 7566,
-        "section": "Links"
+        markdown: "[link](<foo\nbar>)\n",
+        html: "<p>[link](<foo\nbar>)</p>\n",
+        example: 488,
+        start_line: 7560,
+        end_line: 7566,
+        section: "Links",
     },
     {
-        "markdown": "[a](<b)c>)\n",
-        "html": "<p><a href=\"b)c\">a</a></p>\n",
-        "example": 489,
-        "start_line": 7571,
-        "end_line": 7575,
-        "section": "Links"
+        markdown: "[a](<b)c>)\n",
+        html: "<p><a href=\"b)c\">a</a></p>\n",
+        example: 489,
+        start_line: 7571,
+        end_line: 7575,
+        section: "Links",
     },
     {
-        "markdown": "[link](<foo\\>)\n",
-        "html": "<p>[link](&lt;foo&gt;)</p>\n",
-        "example": 490,
-        "start_line": 7579,
-        "end_line": 7583,
-        "section": "Links"
+        markdown: "[link](<foo\\>)\n",
+        html: "<p>[link](&lt;foo&gt;)</p>\n",
+        example: 490,
+        start_line: 7579,
+        end_line: 7583,
+        section: "Links",
     },
     {
-        "markdown": "[a](<b)c\n[a](<b)c>\n[a](<b>c)\n",
-        "html": "<p>[a](&lt;b)c\n[a](&lt;b)c&gt;\n[a](<b>c)</p>\n",
-        "example": 491,
-        "start_line": 7588,
-        "end_line": 7596,
-        "section": "Links"
+        markdown: "[a](<b)c\n[a](<b)c>\n[a](<b>c)\n",
+        html: "<p>[a](&lt;b)c\n[a](&lt;b)c&gt;\n[a](<b>c)</p>\n",
+        example: 491,
+        start_line: 7588,
+        end_line: 7596,
+        section: "Links",
     },
     {
-        "markdown": "[link](\\(foo\\))\n",
-        "html": "<p><a href=\"(foo)\">link</a></p>\n",
-        "example": 492,
-        "start_line": 7600,
-        "end_line": 7604,
-        "section": "Links"
+        markdown: "[link](\\(foo\\))\n",
+        html: "<p><a href=\"(foo)\">link</a></p>\n",
+        example: 492,
+        start_line: 7600,
+        end_line: 7604,
+        section: "Links",
     },
     {
-        "markdown": "[link](foo(and(bar)))\n",
-        "html": "<p><a href=\"foo(and(bar))\">link</a></p>\n",
-        "example": 493,
-        "start_line": 7609,
-        "end_line": 7613,
-        "section": "Links"
+        markdown: "[link](foo(and(bar)))\n",
+        html: "<p><a href=\"foo(and(bar))\">link</a></p>\n",
+        example: 493,
+        start_line: 7609,
+        end_line: 7613,
+        section: "Links",
     },
     {
-        "markdown": "[link](foo\\(and\\(bar\\))\n",
-        "html": "<p><a href=\"foo(and(bar)\">link</a></p>\n",
-        "example": 494,
-        "start_line": 7618,
-        "end_line": 7622,
-        "section": "Links"
+        markdown: "[link](foo\\(and\\(bar\\))\n",
+        html: "<p><a href=\"foo(and(bar)\">link</a></p>\n",
+        example: 494,
+        start_line: 7618,
+        end_line: 7622,
+        section: "Links",
     },
     {
-        "markdown": "[link](<foo(and(bar)>)\n",
-        "html": "<p><a href=\"foo(and(bar)\">link</a></p>\n",
-        "example": 495,
-        "start_line": 7625,
-        "end_line": 7629,
-        "section": "Links"
+        markdown: "[link](<foo(and(bar)>)\n",
+        html: "<p><a href=\"foo(and(bar)\">link</a></p>\n",
+        example: 495,
+        start_line: 7625,
+        end_line: 7629,
+        section: "Links",
     },
     {
-        "markdown": "[link](foo\\)\\:)\n",
-        "html": "<p><a href=\"foo):\">link</a></p>\n",
-        "example": 496,
-        "start_line": 7635,
-        "end_line": 7639,
-        "section": "Links"
+        markdown: "[link](foo\\)\\:)\n",
+        html: "<p><a href=\"foo):\">link</a></p>\n",
+        example: 496,
+        start_line: 7635,
+        end_line: 7639,
+        section: "Links",
     },
     {
-        "markdown": "[link](#fragment)\n\n[link](http://example.com#fragment)\n\n[link](http://example.com?foo=3#frag)\n",
-        "html": "<p><a href=\"#fragment\">link</a></p>\n<p><a href=\"http://example.com#fragment\">link</a></p>\n<p><a href=\"http://example.com?foo=3#frag\">link</a></p>\n",
-        "example": 497,
-        "start_line": 7644,
-        "end_line": 7654,
-        "section": "Links"
+        markdown: "[link](#fragment)\n\n[link](http://example.com#fragment)\n\n[link](http://example.com?foo=3#frag)\n",
+        html: "<p><a href=\"#fragment\">link</a></p>\n<p><a href=\"http://example.com#fragment\">link</a></p>\n<p><a href=\"http://example.com?foo=3#frag\">link</a></p>\n",
+        example: 497,
+        start_line: 7644,
+        end_line: 7654,
+        section: "Links",
     },
     {
-        "markdown": "[link](foo\\bar)\n",
-        "html": "<p><a href=\"foo%5Cbar\">link</a></p>\n",
-        "example": 498,
-        "start_line": 7660,
-        "end_line": 7664,
-        "section": "Links"
+        markdown: "[link](foo\\bar)\n",
+        html: "<p><a href=\"foo%5Cbar\">link</a></p>\n",
+        example: 498,
+        start_line: 7660,
+        end_line: 7664,
+        section: "Links",
     },
     {
-        "markdown": "[link](foo%20b&auml;)\n",
-        "html": "<p><a href=\"foo%20b%C3%A4\">link</a></p>\n",
-        "example": 499,
-        "start_line": 7676,
-        "end_line": 7680,
-        "section": "Links"
+        markdown: "[link](foo%20b&auml;)\n",
+        html: "<p><a href=\"foo%20b%C3%A4\">link</a></p>\n",
+        example: 499,
+        start_line: 7676,
+        end_line: 7680,
+        section: "Links",
     },
     {
-        "markdown": "[link](\"title\")\n",
-        "html": "<p><a href=\"%22title%22\">link</a></p>\n",
-        "example": 500,
-        "start_line": 7687,
-        "end_line": 7691,
-        "section": "Links"
+        markdown: "[link](\"title\")\n",
+        html: "<p><a href=\"%22title%22\">link</a></p>\n",
+        example: 500,
+        start_line: 7687,
+        end_line: 7691,
+        section: "Links",
     },
     {
-        "markdown": "[link](/url \"title\")\n[link](/url 'title')\n[link](/url (title))\n",
-        "html": "<p><a href=\"/url\" title=\"title\">link</a>\n<a href=\"/url\" title=\"title\">link</a>\n<a href=\"/url\" title=\"title\">link</a></p>\n",
-        "example": 501,
-        "start_line": 7696,
-        "end_line": 7704,
-        "section": "Links"
+        markdown: "[link](/url \"title\")\n[link](/url 'title')\n[link](/url (title))\n",
+        html: "<p><a href=\"/url\" title=\"title\">link</a>\n<a href=\"/url\" title=\"title\">link</a>\n<a href=\"/url\" title=\"title\">link</a></p>\n",
+        example: 501,
+        start_line: 7696,
+        end_line: 7704,
+        section: "Links",
     },
     {
-        "markdown": "[link](/url \"title \\\"&quot;\")\n",
-        "html": "<p><a href=\"/url\" title=\"title &quot;&quot;\">link</a></p>\n",
-        "example": 502,
-        "start_line": 7710,
-        "end_line": 7714,
-        "section": "Links"
+        markdown: "[link](/url \"title \\\"&quot;\")\n",
+        html: "<p><a href=\"/url\" title=\"title &quot;&quot;\">link</a></p>\n",
+        example: 502,
+        start_line: 7710,
+        end_line: 7714,
+        section: "Links",
     },
     {
-        "markdown": "[link](/url \"title\")\n",
-        "html": "<p><a href=\"/url%C2%A0%22title%22\">link</a></p>\n",
-        "example": 503,
-        "start_line": 7720,
-        "end_line": 7724,
-        "section": "Links"
+        markdown: "[link](/url \"title\")\n",
+        html: "<p><a href=\"/url%C2%A0%22title%22\">link</a></p>\n",
+        example: 503,
+        start_line: 7720,
+        end_line: 7724,
+        section: "Links",
     },
     {
-        "markdown": "[link](/url \"title \"and\" title\")\n",
-        "html": "<p>[link](/url &quot;title &quot;and&quot; title&quot;)</p>\n",
-        "example": 504,
-        "start_line": 7729,
-        "end_line": 7733,
-        "section": "Links"
+        markdown: "[link](/url \"title \"and\" title\")\n",
+        html: "<p>[link](/url &quot;title &quot;and&quot; title&quot;)</p>\n",
+        example: 504,
+        start_line: 7729,
+        end_line: 7733,
+        section: "Links",
     },
     {
-        "markdown": "[link](/url 'title \"and\" title')\n",
-        "html": "<p><a href=\"/url\" title=\"title &quot;and&quot; title\">link</a></p>\n",
-        "example": 505,
-        "start_line": 7738,
-        "end_line": 7742,
-        "section": "Links"
+        markdown: "[link](/url 'title \"and\" title')\n",
+        html: "<p><a href=\"/url\" title=\"title &quot;and&quot; title\">link</a></p>\n",
+        example: 505,
+        start_line: 7738,
+        end_line: 7742,
+        section: "Links",
     },
     {
-        "markdown": "[link](   /uri\n  \"title\"  )\n",
-        "html": "<p><a href=\"/uri\" title=\"title\">link</a></p>\n",
-        "example": 506,
-        "start_line": 7762,
-        "end_line": 7767,
-        "section": "Links"
+        markdown: "[link](   /uri\n  \"title\"  )\n",
+        html: "<p><a href=\"/uri\" title=\"title\">link</a></p>\n",
+        example: 506,
+        start_line: 7762,
+        end_line: 7767,
+        section: "Links",
     },
     {
-        "markdown": "[link] (/uri)\n",
-        "html": "<p>[link] (/uri)</p>\n",
-        "example": 507,
-        "start_line": 7773,
-        "end_line": 7777,
-        "section": "Links"
+        markdown: "[link] (/uri)\n",
+        html: "<p>[link] (/uri)</p>\n",
+        example: 507,
+        start_line: 7773,
+        end_line: 7777,
+        section: "Links",
     },
     {
-        "markdown": "[link [foo [bar]]](/uri)\n",
-        "html": "<p><a href=\"/uri\">link [foo [bar]]</a></p>\n",
-        "example": 508,
-        "start_line": 7783,
-        "end_line": 7787,
-        "section": "Links"
+        markdown: "[link [foo [bar]]](/uri)\n",
+        html: "<p><a href=\"/uri\">link [foo [bar]]</a></p>\n",
+        example: 508,
+        start_line: 7783,
+        end_line: 7787,
+        section: "Links",
     },
     {
-        "markdown": "[link] bar](/uri)\n",
-        "html": "<p>[link] bar](/uri)</p>\n",
-        "example": 509,
-        "start_line": 7790,
-        "end_line": 7794,
-        "section": "Links"
+        markdown: "[link] bar](/uri)\n",
+        html: "<p>[link] bar](/uri)</p>\n",
+        example: 509,
+        start_line: 7790,
+        end_line: 7794,
+        section: "Links",
     },
     {
-        "markdown": "[link [bar](/uri)\n",
-        "html": "<p>[link <a href=\"/uri\">bar</a></p>\n",
-        "example": 510,
-        "start_line": 7797,
-        "end_line": 7801,
-        "section": "Links"
+        markdown: "[link [bar](/uri)\n",
+        html: "<p>[link <a href=\"/uri\">bar</a></p>\n",
+        example: 510,
+        start_line: 7797,
+        end_line: 7801,
+        section: "Links",
     },
     {
-        "markdown": "[link \\[bar](/uri)\n",
-        "html": "<p><a href=\"/uri\">link [bar</a></p>\n",
-        "example": 511,
-        "start_line": 7804,
-        "end_line": 7808,
-        "section": "Links"
+        markdown: "[link \\[bar](/uri)\n",
+        html: "<p><a href=\"/uri\">link [bar</a></p>\n",
+        example: 511,
+        start_line: 7804,
+        end_line: 7808,
+        section: "Links",
     },
     {
-        "markdown": "[link *foo **bar** `#`*](/uri)\n",
-        "html": "<p><a href=\"/uri\">link <em>foo <strong>bar</strong> <code>#</code></em></a></p>\n",
-        "example": 512,
-        "start_line": 7813,
-        "end_line": 7817,
-        "section": "Links"
+        markdown: "[link *foo **bar** `#`*](/uri)\n",
+        html: "<p><a href=\"/uri\">link <em>foo <strong>bar</strong> <code>#</code></em></a></p>\n",
+        example: 512,
+        start_line: 7813,
+        end_line: 7817,
+        section: "Links",
     },
     {
-        "markdown": "[![moon](moon.jpg)](/uri)\n",
-        "html": "<p><a href=\"/uri\"><img src=\"moon.jpg\" alt=\"moon\" /></a></p>\n",
-        "example": 513,
-        "start_line": 7820,
-        "end_line": 7824,
-        "section": "Links"
+        markdown: "[![moon](moon.jpg)](/uri)\n",
+        html: "<p><a href=\"/uri\"><img src=\"moon.jpg\" alt=\"moon\" /></a></p>\n",
+        example: 513,
+        start_line: 7820,
+        end_line: 7824,
+        section: "Links",
     },
     {
-        "markdown": "[foo [bar](/uri)](/uri)\n",
-        "html": "<p>[foo <a href=\"/uri\">bar</a>](/uri)</p>\n",
-        "example": 514,
-        "start_line": 7829,
-        "end_line": 7833,
-        "section": "Links"
+        markdown: "[foo [bar](/uri)](/uri)\n",
+        html: "<p>[foo <a href=\"/uri\">bar</a>](/uri)</p>\n",
+        example: 514,
+        start_line: 7829,
+        end_line: 7833,
+        section: "Links",
     },
     {
-        "markdown": "[foo *[bar [baz](/uri)](/uri)*](/uri)\n",
-        "html": "<p>[foo <em>[bar <a href=\"/uri\">baz</a>](/uri)</em>](/uri)</p>\n",
-        "example": 515,
-        "start_line": 7836,
-        "end_line": 7840,
-        "section": "Links"
+        markdown: "[foo *[bar [baz](/uri)](/uri)*](/uri)\n",
+        html: "<p>[foo <em>[bar <a href=\"/uri\">baz</a>](/uri)</em>](/uri)</p>\n",
+        example: 515,
+        start_line: 7836,
+        end_line: 7840,
+        section: "Links",
     },
     {
-        "markdown": "![[[foo](uri1)](uri2)](uri3)\n",
-        "html": "<p><img src=\"uri3\" alt=\"[foo](uri2)\" /></p>\n",
-        "example": 516,
-        "start_line": 7843,
-        "end_line": 7847,
-        "section": "Links"
+        markdown: "![[[foo](uri1)](uri2)](uri3)\n",
+        html: "<p><img src=\"uri3\" alt=\"[foo](uri2)\" /></p>\n",
+        example: 516,
+        start_line: 7843,
+        end_line: 7847,
+        section: "Links",
     },
     {
-        "markdown": "*[foo*](/uri)\n",
-        "html": "<p>*<a href=\"/uri\">foo*</a></p>\n",
-        "example": 517,
-        "start_line": 7853,
-        "end_line": 7857,
-        "section": "Links"
+        markdown: "*[foo*](/uri)\n",
+        html: "<p>*<a href=\"/uri\">foo*</a></p>\n",
+        example: 517,
+        start_line: 7853,
+        end_line: 7857,
+        section: "Links",
     },
     {
-        "markdown": "[foo *bar](baz*)\n",
-        "html": "<p><a href=\"baz*\">foo *bar</a></p>\n",
-        "example": 518,
-        "start_line": 7860,
-        "end_line": 7864,
-        "section": "Links"
+        markdown: "[foo *bar](baz*)\n",
+        html: "<p><a href=\"baz*\">foo *bar</a></p>\n",
+        example: 518,
+        start_line: 7860,
+        end_line: 7864,
+        section: "Links",
     },
     {
-        "markdown": "*foo [bar* baz]\n",
-        "html": "<p><em>foo [bar</em> baz]</p>\n",
-        "example": 519,
-        "start_line": 7870,
-        "end_line": 7874,
-        "section": "Links"
+        markdown: "*foo [bar* baz]\n",
+        html: "<p><em>foo [bar</em> baz]</p>\n",
+        example: 519,
+        start_line: 7870,
+        end_line: 7874,
+        section: "Links",
     },
     {
-        "markdown": "[foo <bar attr=\"](baz)\">\n",
-        "html": "<p>[foo <bar attr=\"](baz)\"></p>\n",
-        "example": 520,
-        "start_line": 7880,
-        "end_line": 7884,
-        "section": "Links"
+        markdown: "[foo <bar attr=\"](baz)\">\n",
+        html: "<p>[foo <bar attr=\"](baz)\"></p>\n",
+        example: 520,
+        start_line: 7880,
+        end_line: 7884,
+        section: "Links",
     },
     {
-        "markdown": "[foo`](/uri)`\n",
-        "html": "<p>[foo<code>](/uri)</code></p>\n",
-        "example": 521,
-        "start_line": 7887,
-        "end_line": 7891,
-        "section": "Links"
+        markdown: "[foo`](/uri)`\n",
+        html: "<p>[foo<code>](/uri)</code></p>\n",
+        example: 521,
+        start_line: 7887,
+        end_line: 7891,
+        section: "Links",
     },
     {
-        "markdown": "[foo<http://example.com/?search=](uri)>\n",
-        "html": "<p>[foo<a href=\"http://example.com/?search=%5D(uri)\">http://example.com/?search=](uri)</a></p>\n",
-        "example": 522,
-        "start_line": 7894,
-        "end_line": 7898,
-        "section": "Links"
+        markdown: "[foo<http://example.com/?search=](uri)>\n",
+        html: "<p>[foo<a href=\"http://example.com/?search=%5D(uri)\">http://example.com/?search=](uri)</a></p>\n",
+        example: 522,
+        start_line: 7894,
+        end_line: 7898,
+        section: "Links",
     },
     {
-        "markdown": "[foo][bar]\n\n[bar]: /url \"title\"\n",
-        "html": "<p><a href=\"/url\" title=\"title\">foo</a></p>\n",
-        "example": 523,
-        "start_line": 7932,
-        "end_line": 7938,
-        "section": "Links"
+        markdown: "[foo][bar]\n\n[bar]: /url \"title\"\n",
+        html: "<p><a href=\"/url\" title=\"title\">foo</a></p>\n",
+        example: 523,
+        start_line: 7932,
+        end_line: 7938,
+        section: "Links",
     },
     {
-        "markdown": "[link [foo [bar]]][ref]\n\n[ref]: /uri\n",
-        "html": "<p><a href=\"/uri\">link [foo [bar]]</a></p>\n",
-        "example": 524,
-        "start_line": 7947,
-        "end_line": 7953,
-        "section": "Links"
+        markdown: "[link [foo [bar]]][ref]\n\n[ref]: /uri\n",
+        html: "<p><a href=\"/uri\">link [foo [bar]]</a></p>\n",
+        example: 524,
+        start_line: 7947,
+        end_line: 7953,
+        section: "Links",
     },
     {
-        "markdown": "[link \\[bar][ref]\n\n[ref]: /uri\n",
-        "html": "<p><a href=\"/uri\">link [bar</a></p>\n",
-        "example": 525,
-        "start_line": 7956,
-        "end_line": 7962,
-        "section": "Links"
+        markdown: "[link \\[bar][ref]\n\n[ref]: /uri\n",
+        html: "<p><a href=\"/uri\">link [bar</a></p>\n",
+        example: 525,
+        start_line: 7956,
+        end_line: 7962,
+        section: "Links",
     },
     {
-        "markdown": "[link *foo **bar** `#`*][ref]\n\n[ref]: /uri\n",
-        "html": "<p><a href=\"/uri\">link <em>foo <strong>bar</strong> <code>#</code></em></a></p>\n",
-        "example": 526,
-        "start_line": 7967,
-        "end_line": 7973,
-        "section": "Links"
+        markdown: "[link *foo **bar** `#`*][ref]\n\n[ref]: /uri\n",
+        html: "<p><a href=\"/uri\">link <em>foo <strong>bar</strong> <code>#</code></em></a></p>\n",
+        example: 526,
+        start_line: 7967,
+        end_line: 7973,
+        section: "Links",
     },
     {
-        "markdown": "[![moon](moon.jpg)][ref]\n\n[ref]: /uri\n",
-        "html": "<p><a href=\"/uri\"><img src=\"moon.jpg\" alt=\"moon\" /></a></p>\n",
-        "example": 527,
-        "start_line": 7976,
-        "end_line": 7982,
-        "section": "Links"
+        markdown: "[![moon](moon.jpg)][ref]\n\n[ref]: /uri\n",
+        html: "<p><a href=\"/uri\"><img src=\"moon.jpg\" alt=\"moon\" /></a></p>\n",
+        example: 527,
+        start_line: 7976,
+        end_line: 7982,
+        section: "Links",
     },
     {
-        "markdown": "[foo [bar](/uri)][ref]\n\n[ref]: /uri\n",
-        "html": "<p>[foo <a href=\"/uri\">bar</a>]<a href=\"/uri\">ref</a></p>\n",
-        "example": 528,
-        "start_line": 7987,
-        "end_line": 7993,
-        "section": "Links"
+        markdown: "[foo [bar](/uri)][ref]\n\n[ref]: /uri\n",
+        html: "<p>[foo <a href=\"/uri\">bar</a>]<a href=\"/uri\">ref</a></p>\n",
+        example: 528,
+        start_line: 7987,
+        end_line: 7993,
+        section: "Links",
     },
     {
-        "markdown": "[foo *bar [baz][ref]*][ref]\n\n[ref]: /uri\n",
-        "html": "<p>[foo <em>bar <a href=\"/uri\">baz</a></em>]<a href=\"/uri\">ref</a></p>\n",
-        "example": 529,
-        "start_line": 7996,
-        "end_line": 8002,
-        "section": "Links"
+        markdown: "[foo *bar [baz][ref]*][ref]\n\n[ref]: /uri\n",
+        html: "<p>[foo <em>bar <a href=\"/uri\">baz</a></em>]<a href=\"/uri\">ref</a></p>\n",
+        example: 529,
+        start_line: 7996,
+        end_line: 8002,
+        section: "Links",
     },
     {
-        "markdown": "*[foo*][ref]\n\n[ref]: /uri\n",
-        "html": "<p>*<a href=\"/uri\">foo*</a></p>\n",
-        "example": 530,
-        "start_line": 8011,
-        "end_line": 8017,
-        "section": "Links"
+        markdown: "*[foo*][ref]\n\n[ref]: /uri\n",
+        html: "<p>*<a href=\"/uri\">foo*</a></p>\n",
+        example: 530,
+        start_line: 8011,
+        end_line: 8017,
+        section: "Links",
     },
     {
-        "markdown": "[foo *bar][ref]\n\n[ref]: /uri\n",
-        "html": "<p><a href=\"/uri\">foo *bar</a></p>\n",
-        "example": 531,
-        "start_line": 8020,
-        "end_line": 8026,
-        "section": "Links"
+        markdown: "[foo *bar][ref]\n\n[ref]: /uri\n",
+        html: "<p><a href=\"/uri\">foo *bar</a></p>\n",
+        example: 531,
+        start_line: 8020,
+        end_line: 8026,
+        section: "Links",
     },
     {
-        "markdown": "[foo <bar attr=\"][ref]\">\n\n[ref]: /uri\n",
-        "html": "<p>[foo <bar attr=\"][ref]\"></p>\n",
-        "example": 532,
-        "start_line": 8032,
-        "end_line": 8038,
-        "section": "Links"
+        markdown: "[foo <bar attr=\"][ref]\">\n\n[ref]: /uri\n",
+        html: "<p>[foo <bar attr=\"][ref]\"></p>\n",
+        example: 532,
+        start_line: 8032,
+        end_line: 8038,
+        section: "Links",
     },
     {
-        "markdown": "[foo`][ref]`\n\n[ref]: /uri\n",
-        "html": "<p>[foo<code>][ref]</code></p>\n",
-        "example": 533,
-        "start_line": 8041,
-        "end_line": 8047,
-        "section": "Links"
+        markdown: "[foo`][ref]`\n\n[ref]: /uri\n",
+        html: "<p>[foo<code>][ref]</code></p>\n",
+        example: 533,
+        start_line: 8041,
+        end_line: 8047,
+        section: "Links",
     },
     {
-        "markdown": "[foo<http://example.com/?search=][ref]>\n\n[ref]: /uri\n",
-        "html": "<p>[foo<a href=\"http://example.com/?search=%5D%5Bref%5D\">http://example.com/?search=][ref]</a></p>\n",
-        "example": 534,
-        "start_line": 8050,
-        "end_line": 8056,
-        "section": "Links"
+        markdown: "[foo<http://example.com/?search=][ref]>\n\n[ref]: /uri\n",
+        html: "<p>[foo<a href=\"http://example.com/?search=%5D%5Bref%5D\">http://example.com/?search=][ref]</a></p>\n",
+        example: 534,
+        start_line: 8050,
+        end_line: 8056,
+        section: "Links",
     },
     {
-        "markdown": "[foo][BaR]\n\n[bar]: /url \"title\"\n",
-        "html": "<p><a href=\"/url\" title=\"title\">foo</a></p>\n",
-        "example": 535,
-        "start_line": 8061,
-        "end_line": 8067,
-        "section": "Links"
+        markdown: "[foo][BaR]\n\n[bar]: /url \"title\"\n",
+        html: "<p><a href=\"/url\" title=\"title\">foo</a></p>\n",
+        example: 535,
+        start_line: 8061,
+        end_line: 8067,
+        section: "Links",
     },
     {
-        "markdown": "[Толпой][Толпой] is a Russian word.\n\n[ТОЛПОЙ]: /url\n",
-        "html": "<p><a href=\"/url\">Толпой</a> is a Russian word.</p>\n",
-        "example": 536,
-        "start_line": 8072,
-        "end_line": 8078,
-        "section": "Links"
+        markdown: "[Толпой][Толпой] is a Russian word.\n\n[ТОЛПОЙ]: /url\n",
+        html: "<p><a href=\"/url\">Толпой</a> is a Russian word.</p>\n",
+        example: 536,
+        start_line: 8072,
+        end_line: 8078,
+        section: "Links",
     },
     {
-        "markdown": "[Foo\n  bar]: /url\n\n[Baz][Foo bar]\n",
-        "html": "<p><a href=\"/url\">Baz</a></p>\n",
-        "example": 537,
-        "start_line": 8084,
-        "end_line": 8091,
-        "section": "Links"
+        markdown: "[Foo\n  bar]: /url\n\n[Baz][Foo bar]\n",
+        html: "<p><a href=\"/url\">Baz</a></p>\n",
+        example: 537,
+        start_line: 8084,
+        end_line: 8091,
+        section: "Links",
     },
     {
-        "markdown": "[foo] [bar]\n\n[bar]: /url \"title\"\n",
-        "html": "<p>[foo] <a href=\"/url\" title=\"title\">bar</a></p>\n",
-        "example": 538,
-        "start_line": 8097,
-        "end_line": 8103,
-        "section": "Links"
+        markdown: "[foo] [bar]\n\n[bar]: /url \"title\"\n",
+        html: "<p>[foo] <a href=\"/url\" title=\"title\">bar</a></p>\n",
+        example: 538,
+        start_line: 8097,
+        end_line: 8103,
+        section: "Links",
     },
     {
-        "markdown": "[foo]\n[bar]\n\n[bar]: /url \"title\"\n",
-        "html": "<p>[foo]\n<a href=\"/url\" title=\"title\">bar</a></p>\n",
-        "example": 539,
-        "start_line": 8106,
-        "end_line": 8114,
-        "section": "Links"
+        markdown: "[foo]\n[bar]\n\n[bar]: /url \"title\"\n",
+        html: "<p>[foo]\n<a href=\"/url\" title=\"title\">bar</a></p>\n",
+        example: 539,
+        start_line: 8106,
+        end_line: 8114,
+        section: "Links",
     },
     {
-        "markdown": "[foo]: /url1\n\n[foo]: /url2\n\n[bar][foo]\n",
-        "html": "<p><a href=\"/url1\">bar</a></p>\n",
-        "example": 540,
-        "start_line": 8147,
-        "end_line": 8155,
-        "section": "Links"
+        markdown: "[foo]: /url1\n\n[foo]: /url2\n\n[bar][foo]\n",
+        html: "<p><a href=\"/url1\">bar</a></p>\n",
+        example: 540,
+        start_line: 8147,
+        end_line: 8155,
+        section: "Links",
     },
     {
-        "markdown": "[bar][foo\\!]\n\n[foo!]: /url\n",
-        "html": "<p>[bar][foo!]</p>\n",
-        "example": 541,
-        "start_line": 8162,
-        "end_line": 8168,
-        "section": "Links"
+        markdown: "[bar][foo\\!]\n\n[foo!]: /url\n",
+        html: "<p>[bar][foo!]</p>\n",
+        example: 541,
+        start_line: 8162,
+        end_line: 8168,
+        section: "Links",
     },
     {
-        "markdown": "[foo][ref[]\n\n[ref[]: /uri\n",
-        "html": "<p>[foo][ref[]</p>\n<p>[ref[]: /uri</p>\n",
-        "example": 542,
-        "start_line": 8174,
-        "end_line": 8181,
-        "section": "Links"
+        markdown: "[foo][ref[]\n\n[ref[]: /uri\n",
+        html: "<p>[foo][ref[]</p>\n<p>[ref[]: /uri</p>\n",
+        example: 542,
+        start_line: 8174,
+        end_line: 8181,
+        section: "Links",
     },
     {
-        "markdown": "[foo][ref[bar]]\n\n[ref[bar]]: /uri\n",
-        "html": "<p>[foo][ref[bar]]</p>\n<p>[ref[bar]]: /uri</p>\n",
-        "example": 543,
-        "start_line": 8184,
-        "end_line": 8191,
-        "section": "Links"
+        markdown: "[foo][ref[bar]]\n\n[ref[bar]]: /uri\n",
+        html: "<p>[foo][ref[bar]]</p>\n<p>[ref[bar]]: /uri</p>\n",
+        example: 543,
+        start_line: 8184,
+        end_line: 8191,
+        section: "Links",
     },
     {
-        "markdown": "[[[foo]]]\n\n[[[foo]]]: /url\n",
-        "html": "<p>[[[foo]]]</p>\n<p>[[[foo]]]: /url</p>\n",
-        "example": 544,
-        "start_line": 8194,
-        "end_line": 8201,
-        "section": "Links"
+        markdown: "[[[foo]]]\n\n[[[foo]]]: /url\n",
+        html: "<p>[[[foo]]]</p>\n<p>[[[foo]]]: /url</p>\n",
+        example: 544,
+        start_line: 8194,
+        end_line: 8201,
+        section: "Links",
     },
     {
-        "markdown": "[foo][ref\\[]\n\n[ref\\[]: /uri\n",
-        "html": "<p><a href=\"/uri\">foo</a></p>\n",
-        "example": 545,
-        "start_line": 8204,
-        "end_line": 8210,
-        "section": "Links"
+        markdown: "[foo][ref\\[]\n\n[ref\\[]: /uri\n",
+        html: "<p><a href=\"/uri\">foo</a></p>\n",
+        example: 545,
+        start_line: 8204,
+        end_line: 8210,
+        section: "Links",
     },
     {
-        "markdown": "[bar\\\\]: /uri\n\n[bar\\\\]\n",
-        "html": "<p><a href=\"/uri\">bar\\</a></p>\n",
-        "example": 546,
-        "start_line": 8215,
-        "end_line": 8221,
-        "section": "Links"
+        markdown: "[bar\\\\]: /uri\n\n[bar\\\\]\n",
+        html: "<p><a href=\"/uri\">bar\\</a></p>\n",
+        example: 546,
+        start_line: 8215,
+        end_line: 8221,
+        section: "Links",
     },
     {
-        "markdown": "[]\n\n[]: /uri\n",
-        "html": "<p>[]</p>\n<p>[]: /uri</p>\n",
-        "example": 547,
-        "start_line": 8226,
-        "end_line": 8233,
-        "section": "Links"
+        markdown: "[]\n\n[]: /uri\n",
+        html: "<p>[]</p>\n<p>[]: /uri</p>\n",
+        example: 547,
+        start_line: 8226,
+        end_line: 8233,
+        section: "Links",
     },
     {
-        "markdown": "[\n ]\n\n[\n ]: /uri\n",
-        "html": "<p>[\n]</p>\n<p>[\n]: /uri</p>\n",
-        "example": 548,
-        "start_line": 8236,
-        "end_line": 8247,
-        "section": "Links"
+        markdown: "[\n ]\n\n[\n ]: /uri\n",
+        html: "<p>[\n]</p>\n<p>[\n]: /uri</p>\n",
+        example: 548,
+        start_line: 8236,
+        end_line: 8247,
+        section: "Links",
     },
     {
-        "markdown": "[foo][]\n\n[foo]: /url \"title\"\n",
-        "html": "<p><a href=\"/url\" title=\"title\">foo</a></p>\n",
-        "example": 549,
-        "start_line": 8259,
-        "end_line": 8265,
-        "section": "Links"
+        markdown: "[foo][]\n\n[foo]: /url \"title\"\n",
+        html: "<p><a href=\"/url\" title=\"title\">foo</a></p>\n",
+        example: 549,
+        start_line: 8259,
+        end_line: 8265,
+        section: "Links",
     },
     {
-        "markdown": "[*foo* bar][]\n\n[*foo* bar]: /url \"title\"\n",
-        "html": "<p><a href=\"/url\" title=\"title\"><em>foo</em> bar</a></p>\n",
-        "example": 550,
-        "start_line": 8268,
-        "end_line": 8274,
-        "section": "Links"
+        markdown: "[*foo* bar][]\n\n[*foo* bar]: /url \"title\"\n",
+        html: "<p><a href=\"/url\" title=\"title\"><em>foo</em> bar</a></p>\n",
+        example: 550,
+        start_line: 8268,
+        end_line: 8274,
+        section: "Links",
     },
     {
-        "markdown": "[Foo][]\n\n[foo]: /url \"title\"\n",
-        "html": "<p><a href=\"/url\" title=\"title\">Foo</a></p>\n",
-        "example": 551,
-        "start_line": 8279,
-        "end_line": 8285,
-        "section": "Links"
+        markdown: "[Foo][]\n\n[foo]: /url \"title\"\n",
+        html: "<p><a href=\"/url\" title=\"title\">Foo</a></p>\n",
+        example: 551,
+        start_line: 8279,
+        end_line: 8285,
+        section: "Links",
     },
     {
-        "markdown": "[foo] \n[]\n\n[foo]: /url \"title\"\n",
-        "html": "<p><a href=\"/url\" title=\"title\">foo</a>\n[]</p>\n",
-        "example": 552,
-        "start_line": 8292,
-        "end_line": 8300,
-        "section": "Links"
+        markdown: "[foo] \n[]\n\n[foo]: /url \"title\"\n",
+        html: "<p><a href=\"/url\" title=\"title\">foo</a>\n[]</p>\n",
+        example: 552,
+        start_line: 8292,
+        end_line: 8300,
+        section: "Links",
     },
     {
-        "markdown": "[foo]\n\n[foo]: /url \"title\"\n",
-        "html": "<p><a href=\"/url\" title=\"title\">foo</a></p>\n",
-        "example": 553,
-        "start_line": 8312,
-        "end_line": 8318,
-        "section": "Links"
+        markdown: "[foo]\n\n[foo]: /url \"title\"\n",
+        html: "<p><a href=\"/url\" title=\"title\">foo</a></p>\n",
+        example: 553,
+        start_line: 8312,
+        end_line: 8318,
+        section: "Links",
     },
     {
-        "markdown": "[*foo* bar]\n\n[*foo* bar]: /url \"title\"\n",
-        "html": "<p><a href=\"/url\" title=\"title\"><em>foo</em> bar</a></p>\n",
-        "example": 554,
-        "start_line": 8321,
-        "end_line": 8327,
-        "section": "Links"
+        markdown: "[*foo* bar]\n\n[*foo* bar]: /url \"title\"\n",
+        html: "<p><a href=\"/url\" title=\"title\"><em>foo</em> bar</a></p>\n",
+        example: 554,
+        start_line: 8321,
+        end_line: 8327,
+        section: "Links",
     },
     {
-        "markdown": "[[*foo* bar]]\n\n[*foo* bar]: /url \"title\"\n",
-        "html": "<p>[<a href=\"/url\" title=\"title\"><em>foo</em> bar</a>]</p>\n",
-        "example": 555,
-        "start_line": 8330,
-        "end_line": 8336,
-        "section": "Links"
+        markdown: "[[*foo* bar]]\n\n[*foo* bar]: /url \"title\"\n",
+        html: "<p>[<a href=\"/url\" title=\"title\"><em>foo</em> bar</a>]</p>\n",
+        example: 555,
+        start_line: 8330,
+        end_line: 8336,
+        section: "Links",
     },
     {
-        "markdown": "[[bar [foo]\n\n[foo]: /url\n",
-        "html": "<p>[[bar <a href=\"/url\">foo</a></p>\n",
-        "example": 556,
-        "start_line": 8339,
-        "end_line": 8345,
-        "section": "Links"
+        markdown: "[[bar [foo]\n\n[foo]: /url\n",
+        html: "<p>[[bar <a href=\"/url\">foo</a></p>\n",
+        example: 556,
+        start_line: 8339,
+        end_line: 8345,
+        section: "Links",
     },
     {
-        "markdown": "[Foo]\n\n[foo]: /url \"title\"\n",
-        "html": "<p><a href=\"/url\" title=\"title\">Foo</a></p>\n",
-        "example": 557,
-        "start_line": 8350,
-        "end_line": 8356,
-        "section": "Links"
+        markdown: "[Foo]\n\n[foo]: /url \"title\"\n",
+        html: "<p><a href=\"/url\" title=\"title\">Foo</a></p>\n",
+        example: 557,
+        start_line: 8350,
+        end_line: 8356,
+        section: "Links",
     },
     {
-        "markdown": "[foo] bar\n\n[foo]: /url\n",
-        "html": "<p><a href=\"/url\">foo</a> bar</p>\n",
-        "example": 558,
-        "start_line": 8361,
-        "end_line": 8367,
-        "section": "Links"
+        markdown: "[foo] bar\n\n[foo]: /url\n",
+        html: "<p><a href=\"/url\">foo</a> bar</p>\n",
+        example: 558,
+        start_line: 8361,
+        end_line: 8367,
+        section: "Links",
     },
     {
-        "markdown": "\\[foo]\n\n[foo]: /url \"title\"\n",
-        "html": "<p>[foo]</p>\n",
-        "example": 559,
-        "start_line": 8373,
-        "end_line": 8379,
-        "section": "Links"
+        markdown: "\\[foo]\n\n[foo]: /url \"title\"\n",
+        html: "<p>[foo]</p>\n",
+        example: 559,
+        start_line: 8373,
+        end_line: 8379,
+        section: "Links",
     },
     {
-        "markdown": "[foo*]: /url\n\n*[foo*]\n",
-        "html": "<p>*<a href=\"/url\">foo*</a></p>\n",
-        "example": 560,
-        "start_line": 8385,
-        "end_line": 8391,
-        "section": "Links"
+        markdown: "[foo*]: /url\n\n*[foo*]\n",
+        html: "<p>*<a href=\"/url\">foo*</a></p>\n",
+        example: 560,
+        start_line: 8385,
+        end_line: 8391,
+        section: "Links",
     },
     {
-        "markdown": "[foo][bar]\n\n[foo]: /url1\n[bar]: /url2\n",
-        "html": "<p><a href=\"/url2\">foo</a></p>\n",
-        "example": 561,
-        "start_line": 8397,
-        "end_line": 8404,
-        "section": "Links"
+        markdown: "[foo][bar]\n\n[foo]: /url1\n[bar]: /url2\n",
+        html: "<p><a href=\"/url2\">foo</a></p>\n",
+        example: 561,
+        start_line: 8397,
+        end_line: 8404,
+        section: "Links",
     },
     {
-        "markdown": "[foo][]\n\n[foo]: /url1\n",
-        "html": "<p><a href=\"/url1\">foo</a></p>\n",
-        "example": 562,
-        "start_line": 8406,
-        "end_line": 8412,
-        "section": "Links"
+        markdown: "[foo][]\n\n[foo]: /url1\n",
+        html: "<p><a href=\"/url1\">foo</a></p>\n",
+        example: 562,
+        start_line: 8406,
+        end_line: 8412,
+        section: "Links",
     },
     {
-        "markdown": "[foo]()\n\n[foo]: /url1\n",
-        "html": "<p><a href=\"\">foo</a></p>\n",
-        "example": 563,
-        "start_line": 8416,
-        "end_line": 8422,
-        "section": "Links"
+        markdown: "[foo]()\n\n[foo]: /url1\n",
+        html: "<p><a href=\"\">foo</a></p>\n",
+        example: 563,
+        start_line: 8416,
+        end_line: 8422,
+        section: "Links",
     },
     {
-        "markdown": "[foo](not a link)\n\n[foo]: /url1\n",
-        "html": "<p><a href=\"/url1\">foo</a>(not a link)</p>\n",
-        "example": 564,
-        "start_line": 8424,
-        "end_line": 8430,
-        "section": "Links"
+        markdown: "[foo](not a link)\n\n[foo]: /url1\n",
+        html: "<p><a href=\"/url1\">foo</a>(not a link)</p>\n",
+        example: 564,
+        start_line: 8424,
+        end_line: 8430,
+        section: "Links",
     },
     {
-        "markdown": "[foo][bar][baz]\n\n[baz]: /url\n",
-        "html": "<p>[foo]<a href=\"/url\">bar</a></p>\n",
-        "example": 565,
-        "start_line": 8435,
-        "end_line": 8441,
-        "section": "Links"
+        markdown: "[foo][bar][baz]\n\n[baz]: /url\n",
+        html: "<p>[foo]<a href=\"/url\">bar</a></p>\n",
+        example: 565,
+        start_line: 8435,
+        end_line: 8441,
+        section: "Links",
     },
     {
-        "markdown": "[foo][bar][baz]\n\n[baz]: /url1\n[bar]: /url2\n",
-        "html": "<p><a href=\"/url2\">foo</a><a href=\"/url1\">baz</a></p>\n",
-        "example": 566,
-        "start_line": 8447,
-        "end_line": 8454,
-        "section": "Links"
+        markdown: "[foo][bar][baz]\n\n[baz]: /url1\n[bar]: /url2\n",
+        html: "<p><a href=\"/url2\">foo</a><a href=\"/url1\">baz</a></p>\n",
+        example: 566,
+        start_line: 8447,
+        end_line: 8454,
+        section: "Links",
     },
     {
-        "markdown": "[foo][bar][baz]\n\n[baz]: /url1\n[foo]: /url2\n",
-        "html": "<p>[foo]<a href=\"/url1\">bar</a></p>\n",
-        "example": 567,
-        "start_line": 8460,
-        "end_line": 8467,
-        "section": "Links"
+        markdown: "[foo][bar][baz]\n\n[baz]: /url1\n[foo]: /url2\n",
+        html: "<p>[foo]<a href=\"/url1\">bar</a></p>\n",
+        example: 567,
+        start_line: 8460,
+        end_line: 8467,
+        section: "Links",
     },
     {
-        "markdown": "![foo](/url \"title\")\n",
-        "html": "<p><img src=\"/url\" alt=\"foo\" title=\"title\" /></p>\n",
-        "example": 568,
-        "start_line": 8483,
-        "end_line": 8487,
-        "section": "Images"
+        markdown: "![foo](/url \"title\")\n",
+        html: "<p><img src=\"/url\" alt=\"foo\" title=\"title\" /></p>\n",
+        example: 568,
+        start_line: 8483,
+        end_line: 8487,
+        section: "Images",
     },
     {
-        "markdown": "![foo *bar*]\n\n[foo *bar*]: train.jpg \"train & tracks\"\n",
-        "html": "<p><img src=\"train.jpg\" alt=\"foo bar\" title=\"train &amp; tracks\" /></p>\n",
-        "example": 569,
-        "start_line": 8490,
-        "end_line": 8496,
-        "section": "Images"
+        markdown: "![foo *bar*]\n\n[foo *bar*]: train.jpg \"train & tracks\"\n",
+        html: "<p><img src=\"train.jpg\" alt=\"foo bar\" title=\"train &amp; tracks\" /></p>\n",
+        example: 569,
+        start_line: 8490,
+        end_line: 8496,
+        section: "Images",
     },
     {
-        "markdown": "![foo ![bar](/url)](/url2)\n",
-        "html": "<p><img src=\"/url2\" alt=\"foo bar\" /></p>\n",
-        "example": 570,
-        "start_line": 8499,
-        "end_line": 8503,
-        "section": "Images"
+        markdown: "![foo ![bar](/url)](/url2)\n",
+        html: "<p><img src=\"/url2\" alt=\"foo bar\" /></p>\n",
+        example: 570,
+        start_line: 8499,
+        end_line: 8503,
+        section: "Images",
     },
     {
-        "markdown": "![foo [bar](/url)](/url2)\n",
-        "html": "<p><img src=\"/url2\" alt=\"foo bar\" /></p>\n",
-        "example": 571,
-        "start_line": 8506,
-        "end_line": 8510,
-        "section": "Images"
+        markdown: "![foo [bar](/url)](/url2)\n",
+        html: "<p><img src=\"/url2\" alt=\"foo bar\" /></p>\n",
+        example: 571,
+        start_line: 8506,
+        end_line: 8510,
+        section: "Images",
     },
     {
-        "markdown": "![foo *bar*][]\n\n[foo *bar*]: train.jpg \"train & tracks\"\n",
-        "html": "<p><img src=\"train.jpg\" alt=\"foo bar\" title=\"train &amp; tracks\" /></p>\n",
-        "example": 572,
-        "start_line": 8520,
-        "end_line": 8526,
-        "section": "Images"
+        markdown: "![foo *bar*][]\n\n[foo *bar*]: train.jpg \"train & tracks\"\n",
+        html: "<p><img src=\"train.jpg\" alt=\"foo bar\" title=\"train &amp; tracks\" /></p>\n",
+        example: 572,
+        start_line: 8520,
+        end_line: 8526,
+        section: "Images",
     },
     {
-        "markdown": "![foo *bar*][foobar]\n\n[FOOBAR]: train.jpg \"train & tracks\"\n",
-        "html": "<p><img src=\"train.jpg\" alt=\"foo bar\" title=\"train &amp; tracks\" /></p>\n",
-        "example": 573,
-        "start_line": 8529,
-        "end_line": 8535,
-        "section": "Images"
+        markdown: "![foo *bar*][foobar]\n\n[FOOBAR]: train.jpg \"train & tracks\"\n",
+        html: "<p><img src=\"train.jpg\" alt=\"foo bar\" title=\"train &amp; tracks\" /></p>\n",
+        example: 573,
+        start_line: 8529,
+        end_line: 8535,
+        section: "Images",
     },
     {
-        "markdown": "![foo](train.jpg)\n",
-        "html": "<p><img src=\"train.jpg\" alt=\"foo\" /></p>\n",
-        "example": 574,
-        "start_line": 8538,
-        "end_line": 8542,
-        "section": "Images"
+        markdown: "![foo](train.jpg)\n",
+        html: "<p><img src=\"train.jpg\" alt=\"foo\" /></p>\n",
+        example: 574,
+        start_line: 8538,
+        end_line: 8542,
+        section: "Images",
     },
     {
-        "markdown": "My ![foo bar](/path/to/train.jpg  \"title\"   )\n",
-        "html": "<p>My <img src=\"/path/to/train.jpg\" alt=\"foo bar\" title=\"title\" /></p>\n",
-        "example": 575,
-        "start_line": 8545,
-        "end_line": 8549,
-        "section": "Images"
+        markdown: "My ![foo bar](/path/to/train.jpg  \"title\"   )\n",
+        html: "<p>My <img src=\"/path/to/train.jpg\" alt=\"foo bar\" title=\"title\" /></p>\n",
+        example: 575,
+        start_line: 8545,
+        end_line: 8549,
+        section: "Images",
     },
     {
-        "markdown": "![foo](<url>)\n",
-        "html": "<p><img src=\"url\" alt=\"foo\" /></p>\n",
-        "example": 576,
-        "start_line": 8552,
-        "end_line": 8556,
-        "section": "Images"
+        markdown: "![foo](<url>)\n",
+        html: "<p><img src=\"url\" alt=\"foo\" /></p>\n",
+        example: 576,
+        start_line: 8552,
+        end_line: 8556,
+        section: "Images",
     },
     {
-        "markdown": "![](/url)\n",
-        "html": "<p><img src=\"/url\" alt=\"\" /></p>\n",
-        "example": 577,
-        "start_line": 8559,
-        "end_line": 8563,
-        "section": "Images"
+        markdown: "![](/url)\n",
+        html: "<p><img src=\"/url\" alt=\"\" /></p>\n",
+        example: 577,
+        start_line: 8559,
+        end_line: 8563,
+        section: "Images",
     },
     {
-        "markdown": "![foo][bar]\n\n[bar]: /url\n",
-        "html": "<p><img src=\"/url\" alt=\"foo\" /></p>\n",
-        "example": 578,
-        "start_line": 8568,
-        "end_line": 8574,
-        "section": "Images"
+        markdown: "![foo][bar]\n\n[bar]: /url\n",
+        html: "<p><img src=\"/url\" alt=\"foo\" /></p>\n",
+        example: 578,
+        start_line: 8568,
+        end_line: 8574,
+        section: "Images",
     },
     {
-        "markdown": "![foo][bar]\n\n[BAR]: /url\n",
-        "html": "<p><img src=\"/url\" alt=\"foo\" /></p>\n",
-        "example": 579,
-        "start_line": 8577,
-        "end_line": 8583,
-        "section": "Images"
+        markdown: "![foo][bar]\n\n[BAR]: /url\n",
+        html: "<p><img src=\"/url\" alt=\"foo\" /></p>\n",
+        example: 579,
+        start_line: 8577,
+        end_line: 8583,
+        section: "Images",
     },
     {
-        "markdown": "![foo][]\n\n[foo]: /url \"title\"\n",
-        "html": "<p><img src=\"/url\" alt=\"foo\" title=\"title\" /></p>\n",
-        "example": 580,
-        "start_line": 8588,
-        "end_line": 8594,
-        "section": "Images"
+        markdown: "![foo][]\n\n[foo]: /url \"title\"\n",
+        html: "<p><img src=\"/url\" alt=\"foo\" title=\"title\" /></p>\n",
+        example: 580,
+        start_line: 8588,
+        end_line: 8594,
+        section: "Images",
     },
     {
-        "markdown": "![*foo* bar][]\n\n[*foo* bar]: /url \"title\"\n",
-        "html": "<p><img src=\"/url\" alt=\"foo bar\" title=\"title\" /></p>\n",
-        "example": 581,
-        "start_line": 8597,
-        "end_line": 8603,
-        "section": "Images"
+        markdown: "![*foo* bar][]\n\n[*foo* bar]: /url \"title\"\n",
+        html: "<p><img src=\"/url\" alt=\"foo bar\" title=\"title\" /></p>\n",
+        example: 581,
+        start_line: 8597,
+        end_line: 8603,
+        section: "Images",
     },
     {
-        "markdown": "![Foo][]\n\n[foo]: /url \"title\"\n",
-        "html": "<p><img src=\"/url\" alt=\"Foo\" title=\"title\" /></p>\n",
-        "example": 582,
-        "start_line": 8608,
-        "end_line": 8614,
-        "section": "Images"
+        markdown: "![Foo][]\n\n[foo]: /url \"title\"\n",
+        html: "<p><img src=\"/url\" alt=\"Foo\" title=\"title\" /></p>\n",
+        example: 582,
+        start_line: 8608,
+        end_line: 8614,
+        section: "Images",
     },
     {
-        "markdown": "![foo] \n[]\n\n[foo]: /url \"title\"\n",
-        "html": "<p><img src=\"/url\" alt=\"foo\" title=\"title\" />\n[]</p>\n",
-        "example": 583,
-        "start_line": 8620,
-        "end_line": 8628,
-        "section": "Images"
+        markdown: "![foo] \n[]\n\n[foo]: /url \"title\"\n",
+        html: "<p><img src=\"/url\" alt=\"foo\" title=\"title\" />\n[]</p>\n",
+        example: 583,
+        start_line: 8620,
+        end_line: 8628,
+        section: "Images",
     },
     {
-        "markdown": "![foo]\n\n[foo]: /url \"title\"\n",
-        "html": "<p><img src=\"/url\" alt=\"foo\" title=\"title\" /></p>\n",
-        "example": 584,
-        "start_line": 8633,
-        "end_line": 8639,
-        "section": "Images"
+        markdown: "![foo]\n\n[foo]: /url \"title\"\n",
+        html: "<p><img src=\"/url\" alt=\"foo\" title=\"title\" /></p>\n",
+        example: 584,
+        start_line: 8633,
+        end_line: 8639,
+        section: "Images",
     },
     {
-        "markdown": "![*foo* bar]\n\n[*foo* bar]: /url \"title\"\n",
-        "html": "<p><img src=\"/url\" alt=\"foo bar\" title=\"title\" /></p>\n",
-        "example": 585,
-        "start_line": 8642,
-        "end_line": 8648,
-        "section": "Images"
+        markdown: "![*foo* bar]\n\n[*foo* bar]: /url \"title\"\n",
+        html: "<p><img src=\"/url\" alt=\"foo bar\" title=\"title\" /></p>\n",
+        example: 585,
+        start_line: 8642,
+        end_line: 8648,
+        section: "Images",
     },
     {
-        "markdown": "![[foo]]\n\n[[foo]]: /url \"title\"\n",
-        "html": "<p>![[foo]]</p>\n<p>[[foo]]: /url &quot;title&quot;</p>\n",
-        "example": 586,
-        "start_line": 8653,
-        "end_line": 8660,
-        "section": "Images"
+        markdown: "![[foo]]\n\n[[foo]]: /url \"title\"\n",
+        html: "<p>![[foo]]</p>\n<p>[[foo]]: /url &quot;title&quot;</p>\n",
+        example: 586,
+        start_line: 8653,
+        end_line: 8660,
+        section: "Images",
     },
     {
-        "markdown": "![Foo]\n\n[foo]: /url \"title\"\n",
-        "html": "<p><img src=\"/url\" alt=\"Foo\" title=\"title\" /></p>\n",
-        "example": 587,
-        "start_line": 8665,
-        "end_line": 8671,
-        "section": "Images"
+        markdown: "![Foo]\n\n[foo]: /url \"title\"\n",
+        html: "<p><img src=\"/url\" alt=\"Foo\" title=\"title\" /></p>\n",
+        example: 587,
+        start_line: 8665,
+        end_line: 8671,
+        section: "Images",
     },
     {
-        "markdown": "!\\[foo]\n\n[foo]: /url \"title\"\n",
-        "html": "<p>![foo]</p>\n",
-        "example": 588,
-        "start_line": 8677,
-        "end_line": 8683,
-        "section": "Images"
+        markdown: "!\\[foo]\n\n[foo]: /url \"title\"\n",
+        html: "<p>![foo]</p>\n",
+        example: 588,
+        start_line: 8677,
+        end_line: 8683,
+        section: "Images",
     },
     {
-        "markdown": "\\![foo]\n\n[foo]: /url \"title\"\n",
-        "html": "<p>!<a href=\"/url\" title=\"title\">foo</a></p>\n",
-        "example": 589,
-        "start_line": 8689,
-        "end_line": 8695,
-        "section": "Images"
+        markdown: "\\![foo]\n\n[foo]: /url \"title\"\n",
+        html: "<p>!<a href=\"/url\" title=\"title\">foo</a></p>\n",
+        example: 589,
+        start_line: 8689,
+        end_line: 8695,
+        section: "Images",
     },
     {
-        "markdown": "<http://foo.bar.baz>\n",
-        "html": "<p><a href=\"http://foo.bar.baz\">http://foo.bar.baz</a></p>\n",
-        "example": 590,
-        "start_line": 8722,
-        "end_line": 8726,
-        "section": "Autolinks"
+        markdown: "<http://foo.bar.baz>\n",
+        html: "<p><a href=\"http://foo.bar.baz\">http://foo.bar.baz</a></p>\n",
+        example: 590,
+        start_line: 8722,
+        end_line: 8726,
+        section: "Autolinks",
     },
     {
-        "markdown": "<http://foo.bar.baz/test?q=hello&id=22&boolean>\n",
-        "html": "<p><a href=\"http://foo.bar.baz/test?q=hello&amp;id=22&amp;boolean\">http://foo.bar.baz/test?q=hello&amp;id=22&amp;boolean</a></p>\n",
-        "example": 591,
-        "start_line": 8729,
-        "end_line": 8733,
-        "section": "Autolinks"
+        markdown: "<http://foo.bar.baz/test?q=hello&id=22&boolean>\n",
+        html: "<p><a href=\"http://foo.bar.baz/test?q=hello&amp;id=22&amp;boolean\">http://foo.bar.baz/test?q=hello&amp;id=22&amp;boolean</a></p>\n",
+        example: 591,
+        start_line: 8729,
+        end_line: 8733,
+        section: "Autolinks",
     },
     {
-        "markdown": "<irc://foo.bar:2233/baz>\n",
-        "html": "<p><a href=\"irc://foo.bar:2233/baz\">irc://foo.bar:2233/baz</a></p>\n",
-        "example": 592,
-        "start_line": 8736,
-        "end_line": 8740,
-        "section": "Autolinks"
+        markdown: "<irc://foo.bar:2233/baz>\n",
+        html: "<p><a href=\"irc://foo.bar:2233/baz\">irc://foo.bar:2233/baz</a></p>\n",
+        example: 592,
+        start_line: 8736,
+        end_line: 8740,
+        section: "Autolinks",
     },
     {
-        "markdown": "<MAILTO:[email protected]>\n",
-        "html": "<p><a href=\"MAILTO:[email protected]\">MAILTO:[email protected]</a></p>\n",
-        "example": 593,
-        "start_line": 8745,
-        "end_line": 8749,
-        "section": "Autolinks"
+        markdown: "<MAILTO:[email protected]>\n",
+        html: "<p><a href=\"MAILTO:[email protected]\">MAILTO:[email protected]</a></p>\n",
+        example: 593,
+        start_line: 8745,
+        end_line: 8749,
+        section: "Autolinks",
     },
     {
-        "markdown": "<a+b+c:d>\n",
-        "html": "<p><a href=\"a+b+c:d\">a+b+c:d</a></p>\n",
-        "example": 594,
-        "start_line": 8757,
-        "end_line": 8761,
-        "section": "Autolinks"
+        markdown: "<a+b+c:d>\n",
+        html: "<p><a href=\"a+b+c:d\">a+b+c:d</a></p>\n",
+        example: 594,
+        start_line: 8757,
+        end_line: 8761,
+        section: "Autolinks",
     },
     {
-        "markdown": "<made-up-scheme://foo,bar>\n",
-        "html": "<p><a href=\"made-up-scheme://foo,bar\">made-up-scheme://foo,bar</a></p>\n",
-        "example": 595,
-        "start_line": 8764,
-        "end_line": 8768,
-        "section": "Autolinks"
+        markdown: "<made-up-scheme://foo,bar>\n",
+        html: "<p><a href=\"made-up-scheme://foo,bar\">made-up-scheme://foo,bar</a></p>\n",
+        example: 595,
+        start_line: 8764,
+        end_line: 8768,
+        section: "Autolinks",
     },
     {
-        "markdown": "<http://../>\n",
-        "html": "<p><a href=\"http://../\">http://../</a></p>\n",
-        "example": 596,
-        "start_line": 8771,
-        "end_line": 8775,
-        "section": "Autolinks"
+        markdown: "<http://../>\n",
+        html: "<p><a href=\"http://../\">http://../</a></p>\n",
+        example: 596,
+        start_line: 8771,
+        end_line: 8775,
+        section: "Autolinks",
     },
     {
-        "markdown": "<localhost:5001/foo>\n",
-        "html": "<p><a href=\"localhost:5001/foo\">localhost:5001/foo</a></p>\n",
-        "example": 597,
-        "start_line": 8778,
-        "end_line": 8782,
-        "section": "Autolinks"
+        markdown: "<localhost:5001/foo>\n",
+        html: "<p><a href=\"localhost:5001/foo\">localhost:5001/foo</a></p>\n",
+        example: 597,
+        start_line: 8778,
+        end_line: 8782,
+        section: "Autolinks",
     },
     {
-        "markdown": "<http://foo.bar/baz bim>\n",
-        "html": "<p>&lt;http://foo.bar/baz bim&gt;</p>\n",
-        "example": 598,
-        "start_line": 8787,
-        "end_line": 8791,
-        "section": "Autolinks"
+        markdown: "<http://foo.bar/baz bim>\n",
+        html: "<p>&lt;http://foo.bar/baz bim&gt;</p>\n",
+        example: 598,
+        start_line: 8787,
+        end_line: 8791,
+        section: "Autolinks",
     },
     {
-        "markdown": "<http://example.com/\\[\\>\n",
-        "html": "<p><a href=\"http://example.com/%5C%5B%5C\">http://example.com/\\[\\</a></p>\n",
-        "example": 599,
-        "start_line": 8796,
-        "end_line": 8800,
-        "section": "Autolinks"
+        markdown: "<http://example.com/\\[\\>\n",
+        html: "<p><a href=\"http://example.com/%5C%5B%5C\">http://example.com/\\[\\</a></p>\n",
+        example: 599,
+        start_line: 8796,
+        end_line: 8800,
+        section: "Autolinks",
     },
     {
-        "markdown": "<[email protected]>\n",
-        "html": "<p><a href=\"mailto:[email protected]\">[email protected]</a></p>\n",
-        "example": 600,
-        "start_line": 8818,
-        "end_line": 8822,
-        "section": "Autolinks"
+        markdown: "<[email protected]>\n",
+        html: "<p><a href=\"mailto:[email protected]\">[email protected]</a></p>\n",
+        example: 600,
+        start_line: 8818,
+        end_line: 8822,
+        section: "Autolinks",
     },
     {
-        "markdown": "<[email protected]>\n",
-        "html": "<p><a href=\"mailto:[email protected]\">[email protected]</a></p>\n",
-        "example": 601,
-        "start_line": 8825,
-        "end_line": 8829,
-        "section": "Autolinks"
+        markdown: "<[email protected]>\n",
+        html: "<p><a href=\"mailto:[email protected]\">[email protected]</a></p>\n",
+        example: 601,
+        start_line: 8825,
+        end_line: 8829,
+        section: "Autolinks",
     },
     {
-        "markdown": "<foo\\[email protected]>\n",
-        "html": "<p>&lt;[email protected]&gt;</p>\n",
-        "example": 602,
-        "start_line": 8834,
-        "end_line": 8838,
-        "section": "Autolinks"
+        markdown: "<foo\\[email protected]>\n",
+        html: "<p>&lt;[email protected]&gt;</p>\n",
+        example: 602,
+        start_line: 8834,
+        end_line: 8838,
+        section: "Autolinks",
     },
     {
-        "markdown": "<>\n",
-        "html": "<p>&lt;&gt;</p>\n",
-        "example": 603,
-        "start_line": 8843,
-        "end_line": 8847,
-        "section": "Autolinks"
+        markdown: "<>\n",
+        html: "<p>&lt;&gt;</p>\n",
+        example: 603,
+        start_line: 8843,
+        end_line: 8847,
+        section: "Autolinks",
     },
     {
-        "markdown": "< http://foo.bar >\n",
-        "html": "<p>&lt; http://foo.bar &gt;</p>\n",
-        "example": 604,
-        "start_line": 8850,
-        "end_line": 8854,
-        "section": "Autolinks"
+        markdown: "< http://foo.bar >\n",
+        html: "<p>&lt; http://foo.bar &gt;</p>\n",
+        example: 604,
+        start_line: 8850,
+        end_line: 8854,
+        section: "Autolinks",
     },
     {
-        "markdown": "<m:abc>\n",
-        "html": "<p>&lt;m:abc&gt;</p>\n",
-        "example": 605,
-        "start_line": 8857,
-        "end_line": 8861,
-        "section": "Autolinks"
+        markdown: "<m:abc>\n",
+        html: "<p>&lt;m:abc&gt;</p>\n",
+        example: 605,
+        start_line: 8857,
+        end_line: 8861,
+        section: "Autolinks",
     },
     {
-        "markdown": "<foo.bar.baz>\n",
-        "html": "<p>&lt;foo.bar.baz&gt;</p>\n",
-        "example": 606,
-        "start_line": 8864,
-        "end_line": 8868,
-        "section": "Autolinks"
+        markdown: "<foo.bar.baz>\n",
+        html: "<p>&lt;foo.bar.baz&gt;</p>\n",
+        example: 606,
+        start_line: 8864,
+        end_line: 8868,
+        section: "Autolinks",
     },
     {
-        "markdown": "http://example.com\n",
-        "html": "<p>http://example.com</p>\n",
-        "example": 607,
-        "start_line": 8871,
-        "end_line": 8875,
-        "section": "Autolinks"
+        markdown: "http://example.com\n",
+        html: "<p>http://example.com</p>\n",
+        example: 607,
+        start_line: 8871,
+        end_line: 8875,
+        section: "Autolinks",
     },
     {
-        "markdown": "[email protected]\n",
-        "html": "<p>[email protected]</p>\n",
-        "example": 608,
-        "start_line": 8878,
-        "end_line": 8882,
-        "section": "Autolinks"
+        markdown: "[email protected]\n",
+        html: "<p>[email protected]</p>\n",
+        example: 608,
+        start_line: 8878,
+        end_line: 8882,
+        section: "Autolinks",
     },
     {
-        "markdown": "<a><bab><c2c>\n",
-        "html": "<p><a><bab><c2c></p>\n",
-        "example": 609,
-        "start_line": 8960,
-        "end_line": 8964,
-        "section": "Raw HTML"
+        markdown: "<a><bab><c2c>\n",
+        html: "<p><a><bab><c2c></p>\n",
+        example: 609,
+        start_line: 8960,
+        end_line: 8964,
+        section: "Raw HTML",
     },
     {
-        "markdown": "<a/><b2/>\n",
-        "html": "<p><a/><b2/></p>\n",
-        "example": 610,
-        "start_line": 8969,
-        "end_line": 8973,
-        "section": "Raw HTML"
+        markdown: "<a/><b2/>\n",
+        html: "<p><a/><b2/></p>\n",
+        example: 610,
+        start_line: 8969,
+        end_line: 8973,
+        section: "Raw HTML",
     },
     {
-        "markdown": "<a  /><b2\ndata=\"foo\" >\n",
-        "html": "<p><a  /><b2\ndata=\"foo\" ></p>\n",
-        "example": 611,
-        "start_line": 8978,
-        "end_line": 8984,
-        "section": "Raw HTML"
+        markdown: "<a  /><b2\ndata=\"foo\" >\n",
+        html: "<p><a  /><b2\ndata=\"foo\" ></p>\n",
+        example: 611,
+        start_line: 8978,
+        end_line: 8984,
+        section: "Raw HTML",
     },
     {
-        "markdown": "<a foo=\"bar\" bam = 'baz <em>\"</em>'\n_boolean zoop:33=zoop:33 />\n",
-        "html": "<p><a foo=\"bar\" bam = 'baz <em>\"</em>'\n_boolean zoop:33=zoop:33 /></p>\n",
-        "example": 612,
-        "start_line": 8989,
-        "end_line": 8995,
-        "section": "Raw HTML"
+        markdown: "<a foo=\"bar\" bam = 'baz <em>\"</em>'\n_boolean zoop:33=zoop:33 />\n",
+        html: "<p><a foo=\"bar\" bam = 'baz <em>\"</em>'\n_boolean zoop:33=zoop:33 /></p>\n",
+        example: 612,
+        start_line: 8989,
+        end_line: 8995,
+        section: "Raw HTML",
     },
     {
-        "markdown": "Foo <responsive-image src=\"foo.jpg\" />\n",
-        "html": "<p>Foo <responsive-image src=\"foo.jpg\" /></p>\n",
-        "example": 613,
-        "start_line": 9000,
-        "end_line": 9004,
-        "section": "Raw HTML"
+        markdown: "Foo <responsive-image src=\"foo.jpg\" />\n",
+        html: "<p>Foo <responsive-image src=\"foo.jpg\" /></p>\n",
+        example: 613,
+        start_line: 9000,
+        end_line: 9004,
+        section: "Raw HTML",
     },
     {
-        "markdown": "<33> <__>\n",
-        "html": "<p>&lt;33&gt; &lt;__&gt;</p>\n",
-        "example": 614,
-        "start_line": 9009,
-        "end_line": 9013,
-        "section": "Raw HTML"
+        markdown: "<33> <__>\n",
+        html: "<p>&lt;33&gt; &lt;__&gt;</p>\n",
+        example: 614,
+        start_line: 9009,
+        end_line: 9013,
+        section: "Raw HTML",
     },
     {
-        "markdown": "<a h*#ref=\"hi\">\n",
-        "html": "<p>&lt;a h*#ref=&quot;hi&quot;&gt;</p>\n",
-        "example": 615,
-        "start_line": 9018,
-        "end_line": 9022,
-        "section": "Raw HTML"
+        markdown: "<a h*#ref=\"hi\">\n",
+        html: "<p>&lt;a h*#ref=&quot;hi&quot;&gt;</p>\n",
+        example: 615,
+        start_line: 9018,
+        end_line: 9022,
+        section: "Raw HTML",
     },
     {
-        "markdown": "<a href=\"hi'> <a href=hi'>\n",
-        "html": "<p>&lt;a href=&quot;hi'&gt; &lt;a href=hi'&gt;</p>\n",
-        "example": 616,
-        "start_line": 9027,
-        "end_line": 9031,
-        "section": "Raw HTML"
+        markdown: "<a href=\"hi'> <a href=hi'>\n",
+        html: "<p>&lt;a href=&quot;hi'&gt; &lt;a href=hi'&gt;</p>\n",
+        example: 616,
+        start_line: 9027,
+        end_line: 9031,
+        section: "Raw HTML",
     },
     {
-        "markdown": "< a><\nfoo><bar/ >\n<foo bar=baz\nbim!bop />\n",
-        "html": "<p>&lt; a&gt;&lt;\nfoo&gt;&lt;bar/ &gt;\n&lt;foo bar=baz\nbim!bop /&gt;</p>\n",
-        "example": 617,
-        "start_line": 9036,
-        "end_line": 9046,
-        "section": "Raw HTML"
+        markdown: "< a><\nfoo><bar/ >\n<foo bar=baz\nbim!bop />\n",
+        html: "<p>&lt; a&gt;&lt;\nfoo&gt;&lt;bar/ &gt;\n&lt;foo bar=baz\nbim!bop /&gt;</p>\n",
+        example: 617,
+        start_line: 9036,
+        end_line: 9046,
+        section: "Raw HTML",
     },
     {
-        "markdown": "<a href='bar'title=title>\n",
-        "html": "<p>&lt;a href='bar'title=title&gt;</p>\n",
-        "example": 618,
-        "start_line": 9051,
-        "end_line": 9055,
-        "section": "Raw HTML"
+        markdown: "<a href='bar'title=title>\n",
+        html: "<p>&lt;a href='bar'title=title&gt;</p>\n",
+        example: 618,
+        start_line: 9051,
+        end_line: 9055,
+        section: "Raw HTML",
     },
     {
-        "markdown": "</a></foo >\n",
-        "html": "<p></a></foo ></p>\n",
-        "example": 619,
-        "start_line": 9060,
-        "end_line": 9064,
-        "section": "Raw HTML"
+        markdown: "</a></foo >\n",
+        html: "<p></a></foo ></p>\n",
+        example: 619,
+        start_line: 9060,
+        end_line: 9064,
+        section: "Raw HTML",
     },
     {
-        "markdown": "</a href=\"foo\">\n",
-        "html": "<p>&lt;/a href=&quot;foo&quot;&gt;</p>\n",
-        "example": 620,
-        "start_line": 9069,
-        "end_line": 9073,
-        "section": "Raw HTML"
+        markdown: "</a href=\"foo\">\n",
+        html: "<p>&lt;/a href=&quot;foo&quot;&gt;</p>\n",
+        example: 620,
+        start_line: 9069,
+        end_line: 9073,
+        section: "Raw HTML",
     },
     {
-        "markdown": "foo <!-- this is a\ncomment - with hyphen -->\n",
-        "html": "<p>foo <!-- this is a\ncomment - with hyphen --></p>\n",
-        "example": 621,
-        "start_line": 9078,
-        "end_line": 9084,
-        "section": "Raw HTML"
+        markdown: "foo <!-- this is a\ncomment - with hyphen -->\n",
+        html: "<p>foo <!-- this is a\ncomment - with hyphen --></p>\n",
+        example: 621,
+        start_line: 9078,
+        end_line: 9084,
+        section: "Raw HTML",
     },
     {
-        "markdown": "foo <!-- not a comment -- two hyphens -->\n",
-        "html": "<p>foo &lt;!-- not a comment -- two hyphens --&gt;</p>\n",
-        "example": 622,
-        "start_line": 9087,
-        "end_line": 9091,
-        "section": "Raw HTML"
+        markdown: "foo <!-- not a comment -- two hyphens -->\n",
+        html: "<p>foo &lt;!-- not a comment -- two hyphens --&gt;</p>\n",
+        example: 622,
+        start_line: 9087,
+        end_line: 9091,
+        section: "Raw HTML",
     },
     {
-        "markdown": "foo <!--> foo -->\n\nfoo <!-- foo--->\n",
-        "html": "<p>foo &lt;!--&gt; foo --&gt;</p>\n<p>foo &lt;!-- foo---&gt;</p>\n",
-        "example": 623,
-        "start_line": 9096,
-        "end_line": 9103,
-        "section": "Raw HTML"
+        markdown: "foo <!--> foo -->\n\nfoo <!-- foo--->\n",
+        html: "<p>foo &lt;!--&gt; foo --&gt;</p>\n<p>foo &lt;!-- foo---&gt;</p>\n",
+        example: 623,
+        start_line: 9096,
+        end_line: 9103,
+        section: "Raw HTML",
     },
     {
-        "markdown": "foo <?php echo $a; ?>\n",
-        "html": "<p>foo <?php echo $a; ?></p>\n",
-        "example": 624,
-        "start_line": 9108,
-        "end_line": 9112,
-        "section": "Raw HTML"
+        markdown: "foo <?php echo $a; ?>\n",
+        html: "<p>foo <?php echo $a; ?></p>\n",
+        example: 624,
+        start_line: 9108,
+        end_line: 9112,
+        section: "Raw HTML",
     },
     {
-        "markdown": "foo <!ELEMENT br EMPTY>\n",
-        "html": "<p>foo <!ELEMENT br EMPTY></p>\n",
-        "example": 625,
-        "start_line": 9117,
-        "end_line": 9121,
-        "section": "Raw HTML"
+        markdown: "foo <!ELEMENT br EMPTY>\n",
+        html: "<p>foo <!ELEMENT br EMPTY></p>\n",
+        example: 625,
+        start_line: 9117,
+        end_line: 9121,
+        section: "Raw HTML",
     },
     {
-        "markdown": "foo <![CDATA[>&<]]>\n",
-        "html": "<p>foo <![CDATA[>&<]]></p>\n",
-        "example": 626,
-        "start_line": 9126,
-        "end_line": 9130,
-        "section": "Raw HTML"
+        markdown: "foo <![CDATA[>&<]]>\n",
+        html: "<p>foo <![CDATA[>&<]]></p>\n",
+        example: 626,
+        start_line: 9126,
+        end_line: 9130,
+        section: "Raw HTML",
     },
     {
-        "markdown": "foo <a href=\"&ouml;\">\n",
-        "html": "<p>foo <a href=\"&ouml;\"></p>\n",
-        "example": 627,
-        "start_line": 9136,
-        "end_line": 9140,
-        "section": "Raw HTML"
+        markdown: "foo <a href=\"&ouml;\">\n",
+        html: "<p>foo <a href=\"&ouml;\"></p>\n",
+        example: 627,
+        start_line: 9136,
+        end_line: 9140,
+        section: "Raw HTML",
     },
     {
-        "markdown": "foo <a href=\"\\*\">\n",
-        "html": "<p>foo <a href=\"\\*\"></p>\n",
-        "example": 628,
-        "start_line": 9145,
-        "end_line": 9149,
-        "section": "Raw HTML"
+        markdown: "foo <a href=\"\\*\">\n",
+        html: "<p>foo <a href=\"\\*\"></p>\n",
+        example: 628,
+        start_line: 9145,
+        end_line: 9149,
+        section: "Raw HTML",
     },
     {
-        "markdown": "<a href=\"\\\"\">\n",
-        "html": "<p>&lt;a href=&quot;&quot;&quot;&gt;</p>\n",
-        "example": 629,
-        "start_line": 9152,
-        "end_line": 9156,
-        "section": "Raw HTML"
+        markdown: "<a href=\"\\\"\">\n",
+        html: "<p>&lt;a href=&quot;&quot;&quot;&gt;</p>\n",
+        example: 629,
+        start_line: 9152,
+        end_line: 9156,
+        section: "Raw HTML",
     },
     {
-        "markdown": "foo  \nbaz\n",
-        "html": "<p>foo<br />\nbaz</p>\n",
-        "example": 630,
-        "start_line": 9166,
-        "end_line": 9172,
-        "section": "Hard line breaks"
+        markdown: "foo  \nbaz\n",
+        html: "<p>foo<br />\nbaz</p>\n",
+        example: 630,
+        start_line: 9166,
+        end_line: 9172,
+        section: "Hard line breaks",
     },
     {
-        "markdown": "foo\\\nbaz\n",
-        "html": "<p>foo<br />\nbaz</p>\n",
-        "example": 631,
-        "start_line": 9178,
-        "end_line": 9184,
-        "section": "Hard line breaks"
+        markdown: "foo\\\nbaz\n",
+        html: "<p>foo<br />\nbaz</p>\n",
+        example: 631,
+        start_line: 9178,
+        end_line: 9184,
+        section: "Hard line breaks",
     },
     {
-        "markdown": "foo       \nbaz\n",
-        "html": "<p>foo<br />\nbaz</p>\n",
-        "example": 632,
-        "start_line": 9189,
-        "end_line": 9195,
-        "section": "Hard line breaks"
+        markdown: "foo       \nbaz\n",
+        html: "<p>foo<br />\nbaz</p>\n",
+        example: 632,
+        start_line: 9189,
+        end_line: 9195,
+        section: "Hard line breaks",
     },
     {
-        "markdown": "foo  \n     bar\n",
-        "html": "<p>foo<br />\nbar</p>\n",
-        "example": 633,
-        "start_line": 9200,
-        "end_line": 9206,
-        "section": "Hard line breaks"
+        markdown: "foo  \n     bar\n",
+        html: "<p>foo<br />\nbar</p>\n",
+        example: 633,
+        start_line: 9200,
+        end_line: 9206,
+        section: "Hard line breaks",
     },
     {
-        "markdown": "foo\\\n     bar\n",
-        "html": "<p>foo<br />\nbar</p>\n",
-        "example": 634,
-        "start_line": 9209,
-        "end_line": 9215,
-        "section": "Hard line breaks"
+        markdown: "foo\\\n     bar\n",
+        html: "<p>foo<br />\nbar</p>\n",
+        example: 634,
+        start_line: 9209,
+        end_line: 9215,
+        section: "Hard line breaks",
     },
     {
-        "markdown": "*foo  \nbar*\n",
-        "html": "<p><em>foo<br />\nbar</em></p>\n",
-        "example": 635,
-        "start_line": 9221,
-        "end_line": 9227,
-        "section": "Hard line breaks"
+        markdown: "*foo  \nbar*\n",
+        html: "<p><em>foo<br />\nbar</em></p>\n",
+        example: 635,
+        start_line: 9221,
+        end_line: 9227,
+        section: "Hard line breaks",
     },
     {
-        "markdown": "*foo\\\nbar*\n",
-        "html": "<p><em>foo<br />\nbar</em></p>\n",
-        "example": 636,
-        "start_line": 9230,
-        "end_line": 9236,
-        "section": "Hard line breaks"
+        markdown: "*foo\\\nbar*\n",
+        html: "<p><em>foo<br />\nbar</em></p>\n",
+        example: 636,
+        start_line: 9230,
+        end_line: 9236,
+        section: "Hard line breaks",
     },
     {
-        "markdown": "`code \nspan`\n",
-        "html": "<p><code>code  span</code></p>\n",
-        "example": 637,
-        "start_line": 9241,
-        "end_line": 9246,
-        "section": "Hard line breaks"
-    },
-    {
-        "markdown": "`code\\\nspan`\n",
-        "html": "<p><code>code\\ span</code></p>\n",
-        "example": 638,
-        "start_line": 9249,
-        "end_line": 9254,
-        "section": "Hard line breaks"
-    },
-    {
-        "markdown": "<a href=\"foo  \nbar\">\n",
-        "html": "<p><a href=\"foo  \nbar\"></p>\n",
-        "example": 639,
-        "start_line": 9259,
-        "end_line": 9265,
-        "section": "Hard line breaks"
-    },
-    {
-        "markdown": "<a href=\"foo\\\nbar\">\n",
-        "html": "<p><a href=\"foo\\\nbar\"></p>\n",
-        "example": 640,
-        "start_line": 9268,
-        "end_line": 9274,
-        "section": "Hard line breaks"
-    },
-    {
-        "markdown": "foo\\\n",
-        "html": "<p>foo\\</p>\n",
-        "example": 641,
-        "start_line": 9281,
-        "end_line": 9285,
-        "section": "Hard line breaks"
-    },
-    {
-        "markdown": "foo  \n",
-        "html": "<p>foo</p>\n",
-        "example": 642,
-        "start_line": 9288,
-        "end_line": 9292,
-        "section": "Hard line breaks"
-    },
-    {
-        "markdown": "### foo\\\n",
-        "html": "<h3>foo\\</h3>\n",
-        "example": 643,
-        "start_line": 9295,
-        "end_line": 9299,
-        "section": "Hard line breaks"
-    },
-    {
-        "markdown": "### foo  \n",
-        "html": "<h3>foo</h3>\n",
-        "example": 644,
-        "start_line": 9302,
-        "end_line": 9306,
-        "section": "Hard line breaks"
-    },
-    {
-        "markdown": "foo\nbaz\n",
-        "html": "<p>foo\nbaz</p>\n",
-        "example": 645,
-        "start_line": 9317,
-        "end_line": 9323,
-        "section": "Soft line breaks"
-    },
-    {
-        "markdown": "foo \n baz\n",
-        "html": "<p>foo\nbaz</p>\n",
-        "example": 646,
-        "start_line": 9329,
-        "end_line": 9335,
-        "section": "Soft line breaks"
-    },
-    {
-        "markdown": "hello $.;'there\n",
-        "html": "<p>hello $.;'there</p>\n",
-        "example": 647,
-        "start_line": 9349,
-        "end_line": 9353,
-        "section": "Textual content"
-    },
-    {
-        "markdown": "Foo χρῆν\n",
-        "html": "<p>Foo χρῆν</p>\n",
-        "example": 648,
-        "start_line": 9356,
-        "end_line": 9360,
-        "section": "Textual content"
-    },
-    {
-        "markdown": "Multiple     spaces\n",
-        "html": "<p>Multiple     spaces</p>\n",
-        "example": 649,
-        "start_line": 9365,
-        "end_line": 9369,
-        "section": "Textual content"
-    }
-]
+        markdown: "`code \nspan`\n",
+        html: "<p><code>code  span</code></p>\n",
+        example: 637,
+        start_line: 9241,
+        end_line: 9246,
+        section: "Hard line breaks",
+    },
+    {
+        markdown: "`code\\\nspan`\n",
+        html: "<p><code>code\\ span</code></p>\n",
+        example: 638,
+        start_line: 9249,
+        end_line: 9254,
+        section: "Hard line breaks",
+    },
+    {
+        markdown: "<a href=\"foo  \nbar\">\n",
+        html: "<p><a href=\"foo  \nbar\"></p>\n",
+        example: 639,
+        start_line: 9259,
+        end_line: 9265,
+        section: "Hard line breaks",
+    },
+    {
+        markdown: "<a href=\"foo\\\nbar\">\n",
+        html: "<p><a href=\"foo\\\nbar\"></p>\n",
+        example: 640,
+        start_line: 9268,
+        end_line: 9274,
+        section: "Hard line breaks",
+    },
+    {
+        markdown: "foo\\\n",
+        html: "<p>foo\\</p>\n",
+        example: 641,
+        start_line: 9281,
+        end_line: 9285,
+        section: "Hard line breaks",
+    },
+    {
+        markdown: "foo  \n",
+        html: "<p>foo</p>\n",
+        example: 642,
+        start_line: 9288,
+        end_line: 9292,
+        section: "Hard line breaks",
+    },
+    {
+        markdown: "### foo\\\n",
+        html: "<h3>foo\\</h3>\n",
+        example: 643,
+        start_line: 9295,
+        end_line: 9299,
+        section: "Hard line breaks",
+    },
+    {
+        markdown: "### foo  \n",
+        html: "<h3>foo</h3>\n",
+        example: 644,
+        start_line: 9302,
+        end_line: 9306,
+        section: "Hard line breaks",
+    },
+    {
+        markdown: "foo\nbaz\n",
+        html: "<p>foo\nbaz</p>\n",
+        example: 645,
+        start_line: 9317,
+        end_line: 9323,
+        section: "Soft line breaks",
+    },
+    {
+        markdown: "foo \n baz\n",
+        html: "<p>foo\nbaz</p>\n",
+        example: 646,
+        start_line: 9329,
+        end_line: 9335,
+        section: "Soft line breaks",
+    },
+    {
+        markdown: "hello $.;'there\n",
+        html: "<p>hello $.;'there</p>\n",
+        example: 647,
+        start_line: 9349,
+        end_line: 9353,
+        section: "Textual content",
+    },
+    {
+        markdown: "Foo χρῆν\n",
+        html: "<p>Foo χρῆν</p>\n",
+        example: 648,
+        start_line: 9356,
+        end_line: 9360,
+        section: "Textual content",
+    },
+    {
+        markdown: "Multiple     spaces\n",
+        html: "<p>Multiple     spaces</p>\n",
+        example: 649,
+        start_line: 9365,
+        end_line: 9369,
+        section: "Textual content",
+    },
+];

+ 16 - 16
__test__/markdown/lute.test.ts

@@ -1,21 +1,21 @@
-import {spec} from './commonmark-0.29'
+import {spec} from "./commonmark-0.29";
 
-require('../../src/js/lute/lute.min.js')
+require("../../src/js/lute/lute.min.js");
 
 const globalAny: any = global;
 
-it('MarkdownIt', () => {
+it("MarkdownIt", () => {
     spec.forEach(async (item: any) => {
-        const lute = globalAny.Lute.New()
-        lute.SetGFMAutoLink(false)
-        lute.SetGFMStrikethrough(false)
-        lute.SetGFMTable(false)
-        lute.SetGFMTaskListItem(false)
-        lute.SetSoftBreak2HardBreak(false)
-        lute.SetAutoSpace(false)
-        lute.SetFixTermTypo(false)
-        lute.SetEmoji(false)
-        const result = lute.MarkdownStr("", item.markdown)
-        expect(result[0]).toBe(item.html)
-    })
-})
+        const lute = globalAny.Lute.New();
+        lute.SetGFMAutoLink(false);
+        lute.SetGFMStrikethrough(false);
+        lute.SetGFMTable(false);
+        lute.SetGFMTaskListItem(false);
+        lute.SetSoftBreak2HardBreak(false);
+        lute.SetAutoSpace(false);
+        lute.SetFixTermTypo(false);
+        lute.SetEmoji(false);
+        const result = lute.MarkdownStr("", item.markdown);
+        expect(result[0]).toBe(item.html);
+    });
+});

+ 112 - 112
__test__/methods/index.spec.ts

@@ -1,157 +1,157 @@
-import puppeteer from 'puppeteer'
+import puppeteer from "puppeteer";
 
 declare let vditorTest: any;
 
-describe('use puppeteer to test methods', () => {
-    let browser: any
-    let page: any
+describe("use puppeteer to test methods", () => {
+    let browser: any;
+    let page: any;
     const defaultValue = `下一代的 Markdown 编辑器,为未来而构建
-[Vditor](https://github.com/Vanessa219/vditor) 是一款浏览器端的 Markdown 编辑器,使用 TypeScript 实现。`
-    const insertValue = '于是,Vditor 就这样诞生了。'
-    const updateValue = '* [Vditor 使用指南](https://hacpai.com/article/1549638745630?r=Vanessa)'
+[Vditor](https://github.com/Vanessa219/vditor) 是一款浏览器端的 Markdown 编辑器,使用 TypeScript 实现。`;
+    const insertValue = "于是,Vditor 就这样诞生了。";
+    const updateValue = "* [Vditor 使用指南](https://hacpai.com/article/1549638745630?r=Vanessa)";
 
     beforeAll(async () => {
-        browser = await puppeteer.launch()
-        page = await browser.newPage()
+        browser = await puppeteer.launch();
+        page = await browser.newPage();
         await Promise.all([
             page.coverage.startJSCoverage(),
             page.coverage.startCSSCoverage(),
-        ])
-        await page.goto('http://localhost:9000')
-    })
+        ]);
+        await page.goto("http://localhost:9000");
+    });
 
-    it('method: getValue', async () => {
+    it("method: getValue", async () => {
         const result = await page.evaluate(() => {
             vditorTest.setValue(`下一代的 Markdown 编辑器,为未来而构建
-[Vditor](https://github.com/Vanessa219/vditor) 是一款浏览器端的 Markdown 编辑器,使用 TypeScript 实现。`)
-            return vditorTest.getValue()
-        })
-        expect(result).toBe(defaultValue + '\n')
-    })
+[Vditor](https://github.com/Vanessa219/vditor) 是一款浏览器端的 Markdown 编辑器,使用 TypeScript 实现。`);
+            return vditorTest.getValue();
+        });
+        expect(result).toBe(defaultValue + "\n");
+    });
 
-    it('method: insertValue', async () => {
+    it("method: insertValue", async () => {
         const result = await page.evaluate(() => {
-            vditorTest.insertValue('于是,Vditor 就这样诞生了。')
-            return vditorTest.getValue()
-        })
-        expect(result).toBe(defaultValue + insertValue + '\n')
-    })
+            vditorTest.insertValue("于是,Vditor 就这样诞生了。");
+            return vditorTest.getValue();
+        });
+        expect(result).toBe(defaultValue + insertValue + "\n");
+    });
 
-    it('method: focus', async () => {
+    it("method: focus", async () => {
         const result = await page.evaluate(() => {
-            vditorTest.focus()
-            return document.activeElement === vditorTest.vditor.editor.element
-        })
-        expect(result).toBeTruthy()
-    })
+            vditorTest.focus();
+            return document.activeElement === vditorTest.vditor.editor.element;
+        });
+        expect(result).toBeTruthy();
+    });
 
-    it('method: blur', async () => {
+    it("method: blur", async () => {
         const result = await page.evaluate(() => {
-            vditorTest.blur()
-            return document.activeElement === vditorTest.vditor.editor.element
-        })
-        expect(result).toBeFalsy()
-    })
+            vditorTest.blur();
+            return document.activeElement === vditorTest.vditor.editor.element;
+        });
+        expect(result).toBeFalsy();
+    });
 
-    it('method: disabled', async () => {
+    it("method: disabled", async () => {
         const result = await page.evaluate(() => {
-            vditorTest.disabled()
-            return vditorTest.vditor.editor.element.getAttribute('contenteditable')
-        })
-        expect(result).toBe("false")
-    })
+            vditorTest.disabled();
+            return vditorTest.vditor.editor.element.getAttribute("contenteditable");
+        });
+        expect(result).toBe("false");
+    });
 
-    it('method: enable', async () => {
+    it("method: enable", async () => {
         const result = await page.evaluate(() => {
-            vditorTest.enable()
-            return vditorTest.vditor.editor.element.getAttribute('contenteditable')
-        })
-        expect(result).toBeTruthy()
-    })
+            vditorTest.enable();
+            return vditorTest.vditor.editor.element.getAttribute("contenteditable");
+        });
+        expect(result).toBeTruthy();
+    });
 
-    it('method: setSelection and getSelection', async () => {
+    it("method: setSelection and getSelection", async () => {
         const result = await page.evaluate(() => {
-            vditorTest.setSelection(25, 66)
-            return vditorTest.getSelection()
-        })
-        expect(result).toBe('[Vditor](https://github.com/Vanessa219/vditor)')
-    })
+            vditorTest.setSelection(25, 66);
+            return vditorTest.getSelection();
+        });
+        expect(result).toBe("[Vditor](https://github.com/Vanessa219/vditor)");
+    });
 
-    it('method: setValue', async () => {
+    it("method: setValue", async () => {
         const result = await page.evaluate(() => {
-            vditorTest.setValue('于是,Vditor 就这样诞生了。')
-            return vditorTest.getValue()
-        })
-        expect(result).toBe(insertValue + '\n')
-    })
+            vditorTest.setValue("于是,Vditor 就这样诞生了。");
+            return vditorTest.getValue();
+        });
+        expect(result).toBe(insertValue + "\n");
+    });
 
-    it('method: deleteValue and disabledCache', async () => {
+    it("method: deleteValue and disabledCache", async () => {
         const result = await page.evaluate(() => {
-            vditorTest.disabledCache()
-            vditorTest.setSelection(0, 3)
-            vditorTest.deleteValue()
+            vditorTest.disabledCache();
+            vditorTest.setSelection(0, 3);
+            vditorTest.deleteValue();
             return {
-                cache: localStorage.getItem('vditorvditorTest'),
-                value: vditorTest.getValue()
-            }
-        })
-        expect(result.value).toBe('Vditor 就这样诞生了。\n')
-        expect(result.cache).toBe(insertValue + '\n')
-    })
-
-    it('method: deleteValue null', async () => {
+                cache: localStorage.getItem("vditorvditorTest"),
+                value: vditorTest.getValue(),
+            };
+        });
+        expect(result.value).toBe("Vditor 就这样诞生了。\n");
+        expect(result.cache).toBe(insertValue + "\n");
+    });
+
+    it("method: deleteValue null", async () => {
         const result = await page.evaluate(() => {
-            vditorTest.deleteValue()
-            return vditorTest.getValue()
-        })
-        expect(result).toBe('Vditor 就这样诞生了。\n')
-    })
+            vditorTest.deleteValue();
+            return vditorTest.getValue();
+        });
+        expect(result).toBe("Vditor 就这样诞生了。\n");
+    });
 
-    it('method: updateValue and enableCache', async () => {
+    it("method: updateValue and enableCache", async () => {
         const result = await page.evaluate(() => {
-            vditorTest.enableCache()
-            vditorTest.setSelection(0, 14)
-            vditorTest.updateValue('* [Vditor 使用指南](https://hacpai.com/article/1549638745630?r=Vanessa)')
+            vditorTest.enableCache();
+            vditorTest.setSelection(0, 14);
+            vditorTest.updateValue("* [Vditor 使用指南](https://hacpai.com/article/1549638745630?r=Vanessa)");
             return {
                 value: vditorTest.getValue(),
-                cache: localStorage.getItem('vditorvditorTest'),
-            }
-        })
-        expect(result.value).toBe(updateValue + '\n')
-        expect(result.cache).toBe(updateValue + '\n')
-    })
-
-    it('method: clearCache', async () => {
+                cache: localStorage.getItem("vditorvditorTest"),
+            };
+        });
+        expect(result.value).toBe(updateValue + "\n");
+        expect(result.cache).toBe(updateValue + "\n");
+    });
+
+    it("method: clearCache", async () => {
         const result = await page.evaluate(() => {
-            vditorTest.clearCache()
-            return localStorage.getItem('vditorvditorTest')
-        })
-        expect(result).toBeNull()
-    })
+            vditorTest.clearCache();
+            return localStorage.getItem("vditorvditorTest");
+        });
+        expect(result).toBeNull();
+    });
 
-    it('method: html2md', async () => {
+    it("method: html2md", async () => {
         const result = await page.evaluate(() => {
-            return vditorTest.html2md('<a href="https://hacpai.com/tag/vditor">讨论区</a>')
-        })
-        expect(result).toBe('[讨论区](https://hacpai.com/tag/vditor)')
-    })
+            return vditorTest.html2md('<a href="https://hacpai.com/tag/vditor">讨论区</a>');
+        });
+        expect(result).toBe("[讨论区](https://hacpai.com/tag/vditor)");
+    });
 
-    it('method: isUploading false', async () => {
+    it("method: isUploading false", async () => {
         const result = await page.evaluate(() => {
-            return vditorTest.isUploading()
-        })
-        expect(result).toBeFalsy()
-    })
+            return vditorTest.isUploading();
+        });
+        expect(result).toBeFalsy();
+    });
 
-    it('method: isUploading true', async () => {
+    it("method: isUploading true", async () => {
         // TODO
-    })
+    });
 
-    it('method: renderPreview', async () => {
+    it("method: renderPreview", async () => {
         // TODO
-    })
+    });
 
     afterAll(async () => {
-        await browser.close()
-    })
-})
+        await browser.close();
+    });
+});

+ 76 - 42
__test__/util/Options.test.ts

@@ -1,18 +1,22 @@
 const globalAny: any = global;
-globalAny.VDITOR_VERSION = 'version'
-globalAny.CDN_PATH = 'cdn/path'
-import {Options} from '../../src/ts/util/Options';
+globalAny.VDITOR_VERSION = "version";
+import {Options} from "../../src/ts/util/Options";
 
-describe('Options', () => {
-
-    test('Options toolbar', () => {
+describe("Options", () => {
+    const cache = {
+        enable: true,
+        id: "vditorTest",
+    };
+    test("Options toolbar", () => {
         const options = new Options({
-            toolbar: ['br', 'fullscreen', {
+            cache,
+            toolbar: ["br", "fullscreen", {
                 hotkey: "⌘-a",
                 name: "preview",
-            }]
+            }],
         });
         expect(options.merge()).toMatchObject({
+            cache,
             toolbar: [{
                 name: "br",
             }, {
@@ -24,108 +28,138 @@ describe('Options', () => {
                 name: "preview",
                 tipPosition: "nw",
             }],
-        })
+        });
     });
 
-    test('Options upload', () => {
+    test("Options upload", () => {
         const options = new Options({
+            cache,
             upload: {
-                accept: '.jpg'
-            }
+                accept: ".jpg",
+            },
         });
         expect(options.merge()).toMatchObject({
+            cache,
             upload: {
+                accept: ".jpg",
                 filename: expect.anything(),
                 linkToImgUrl: "",
                 max: 10 * 1024 * 1024,
                 url: "",
-                accept: '.jpg'
             },
-        })
-    })
+        });
+    });
 
-    test('Options classes', () => {
+    test("Options classes", () => {
         const options = new Options({
+            cache,
             classes: {
                 preview: "content-reset",
             },
         });
         expect(options.merge()).toMatchObject({
+            cache,
             classes: {
                 preview: "content-reset",
             },
-        })
+        });
     });
 
-
-    test('Options preview', () => {
+    test("Options preview", () => {
         const options = new Options({
+            cache,
             preview: {
-                url: 'https://hacpai.com/md',
-                mode: 'both',
+                mode: "both",
+                url: "https://hacpai.com/md",
             },
         });
         expect(options.merge()).toMatchObject({
+            cache,
             preview: {
-                url: 'https://hacpai.com/md',
                 delay: 1000,
-                mode: 'both',
+                mode: "both",
+                url: "https://hacpai.com/md",
             },
-        })
+        });
     });
 
-    test('Options preview hljs', () => {
+    test("Options preview hljs", () => {
         const options = new Options({
+            cache,
             preview: {
-                mode: 'both',
                 hljs: {
-                    style: 'github'
-                }
+                    style: "github",
+                },
+                mode: "both",
             },
         });
         expect(options.merge().preview).toEqual({
             delay: 1000,
-            mode: 'both',
-            maxWidth: 768,
             hljs: {
-                style: 'github',
                 enable: true,
-            }
-        })
+                lineNumber: false,
+                style: "github",
+            },
+            markdown: {
+                autoSpace: false,
+                chinesePunct: false,
+                codeBlockPreview: true,
+                fixTermTypo: false,
+                footnotes: true,
+                linkBase: "",
+                listStyle: false,
+                sanitize: true,
+                setext: false,
+                toc: false,
+            },
+            math: {
+                engine: "KaTeX",
+                inlineDigit: false,
+                macros: {},
+            },
+            maxWidth: 800,
+            mode: "both",
+            theme: "light",
+            themes: {dark: "", light: "", wechat: ""},
+        });
     });
 
-    test('Options hint', () => {
+    test("Options hint", () => {
         const options = new Options({
+            cache,
             hint: {
-                emojiTail: '前往设置',
                 emoji: {
                     "+1": "👍",
                 },
+                emojiTail: "前往设置",
             },
         });
         expect(options.merge()).toMatchObject({
+            cache,
             hint: {
                 delay: 200,
-                emojiTail: '前往设置',
                 emoji: {
                     "+1": "👍",
                 },
-                emojiPath: globalAny.CDN_PATH + "/vditor/dist/images/emoji",
+                emojiPath: "https://cdn.jsdelivr.net/npm/vditor@version/dist/images/emoji",
+                emojiTail: "前往设置",
             },
-        })
+        });
     });
 
-    test('Options resize', () => {
+    test("Options resize", () => {
         const options = new Options({
+            cache,
             resize: {
                 enable: true,
             },
-        })
+        });
         expect(options.merge()).toMatchObject({
+            cache,
             resize: {
                 enable: true,
                 position: "bottom",
             },
-        })
+        });
     });
-})
+});

+ 22 - 22
__test__/util/textareaPosition.spec.ts

@@ -1,35 +1,35 @@
-import puppeteer from 'puppeteer'
+import puppeteer from "puppeteer";
 
 declare let vditorTest: any;
 
-describe('use puppeteer to test getTextareaPosition', () => {
-    let browser: any
-    let page: any
+describe("use puppeteer to test getTextareaPosition", () => {
+    let browser: any;
+    let page: any;
     beforeAll(async () => {
-        browser = await puppeteer.launch()
-        page = await browser.newPage()
+        browser = await puppeteer.launch();
+        page = await browser.newPage();
         await Promise.all([
             page.coverage.startJSCoverage(),
             page.coverage.startCSSCoverage(),
-        ])
-        await page.goto('http://localhost:9000/')
-    })
+        ]);
+        await page.goto("http://localhost:9000/");
+    });
 
-    it('getTextareaPosition', async () => {
+    it("getTextareaPosition", async () => {
         await page.evaluate(() => {
-            vditorTest.setValue('vditorvditorvditorvditorvditorvditorvditorvditorvditorvditorvditorvditor for jest puppeteer :')
-        })
+            vditorTest.setValue("vditorvditorvditorvditorvditorvditorvditorvditorvditorvditorvditorvditor for jest puppeteer :");
+        });
 
-        await page.waitFor(1000)
+        await page.waitFor(1000);
 
-        let result = await page.evaluate(() => {
-            return vditorTest.vditor.hint.element.getAttribute('style')
-        })
-        expect(result).toContain('top: -61px;')
-        expect(result).toContain('left: 191px;')
-    })
+        const result = await page.evaluate(() => {
+            return vditorTest.vditor.hint.element.getAttribute("style");
+        });
+        expect(result).toContain("top: -61px;");
+        expect(result).toContain("left: 191px;");
+    });
 
     afterAll(async () => {
-        await browser.close()
-    })
-})
+        await browser.close();
+    });
+});

+ 11 - 4
demo/index.html

@@ -8,7 +8,8 @@
     <meta name="apple-mobile-web-app-capable" content="yes">
     <meta name="mobile-web-app-capable" content="yes"/>
     <meta name="apple-mobile-web-app-status-bar-style" content="black">
-    <title>Vditor: ♏ An In-browser Markdown editor, support WYSIWYG,  Instant Rendering (Typora-like) and Split View modes. 一款浏览器端的 Markdown 编辑器,支持所见即所得、即时渲染(类似 Typora)和分屏预览模式。</title>
+    <title>Vditor: ♏ An In-browser Markdown editor, support WYSIWYG, Instant Rendering (Typora-like) and Split View
+        modes. 一款浏览器端的 Markdown 编辑器,支持所见即所得、即时渲染(类似 Typora)和分屏预览模式。</title>
     <meta name="description"
           content="Vditor 支持三种所见即所得(wysiwyg)、即时渲染(ir)、分屏预览(sv)模式,支持大纲、数学公式、脑图、图表、流程图、甘特图、时序图、五线谱、多媒体、语音阅读、标题锚点、代码高亮及复制、graphviz 渲染。"/>
     <meta property="og:description"
@@ -23,7 +24,8 @@
     <meta name="copyright" content="B3log"/>
     <meta http-equiv="Window-target" content="_top"/>
     <meta property="og:locale" content="zh-cmn-Hans"/>
-    <meta property="og:title" content="Vditor: ♏ An In-browser Markdown editor, support WYSIWYG,  Instant Rendering (Typora-like) and Split View modes. 一款浏览器端的 Markdown 编辑器,支持所见即所得、即时渲染(类似 Typora)和分屏预览模式。"/>
+    <meta property="og:title"
+          content="Vditor: ♏ An In-browser Markdown editor, support WYSIWYG,  Instant Rendering (Typora-like) and Split View modes. 一款浏览器端的 Markdown 编辑器,支持所见即所得、即时渲染(类似 Typora)和分屏预览模式。"/>
     <meta property="og:site_name" content="Blog-vditor"/>
     <meta property="og:url" content="https://vditor.b3log.org"/>
     <meta name="twitter:card" content="summary"/>
@@ -39,13 +41,18 @@
             color: #4285f4;
         }
     </style>
+    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vconsole.min.js"></script>
 </head>
 <body>
 <h2>
     <a href="https://vditor.b3log.org" target="_blank">官网</a> |
     <a href="/render.html">Render</a> |
-    <button onclick="window.vditor.setTheme('dark', 'dark',  'native');document.querySelector('body').style.backgroundColor='#2f363d'">Dark Mode</button>
-    <button onclick="window.vditor.setTheme('light', 'light', 'github');document.querySelector('body').style.backgroundColor=''">Light Mode</button>
+    <button onclick="window.vditor.setTheme('dark', 'dark',  'native');document.querySelector('body').style.backgroundColor='#2f363d'">
+        Dark Mode
+    </button>
+    <button onclick="window.vditor.setTheme('light', 'light', 'github');document.querySelector('body').style.backgroundColor=''">
+        Light Mode
+    </button>
 </h2>
 <div id="vditor">
 <h1>Vditor</h1>

+ 10 - 1
demo/index.js

@@ -1,6 +1,8 @@
 import Vditor from '../src/index'
 import '../src/assets/scss/index.scss'
 
+// new VConsole()
+
 let toolbar
 if (window.innerWidth < 768) {
   toolbar = [
@@ -40,7 +42,6 @@ if (window.innerWidth < 768) {
       toolbar: [
         'fullscreen',
         'both',
-        'format',
         'preview',
         'info',
         'help',
@@ -50,6 +51,7 @@ if (window.innerWidth < 768) {
 
 window.vditor = new Vditor('vditor', {
   toolbar,
+  mode: 'sv',
   height: window.innerHeight + 100,
   outline: true,
   debugger: true,
@@ -74,6 +76,13 @@ window.vditor = new Vditor('vditor', {
       'sd': '💔',
       'j': 'https://unpkg.com/[email protected]/dist/images/emoji/j.png',
     },
+    at: (key) => {
+      return [
+        {
+          value: '@Vanessa',
+          html: '<img src="https://avatars0.githubusercontent.com/u/970828?s=60&v=4"/> Vanessa',
+        }]
+    },
   },
   tab: '\t',
   upload: {

+ 2 - 2
demo/markdown/zh_CN.md

@@ -349,13 +349,13 @@ https://v.qq.com/x/cover/zf2z0xpqcculhcz/y0016tj0qvh.html
 
     缩进对齐的段落包含在这个脚注定义内。
 
-    ```
+    ```text
     可以使用代码块。
     ```
 
     还有其他行级排版语法,比如**加粗**和[链接](https://b3log.org)。
 
-```
+```text
 这里是一个脚注引用[^1],这里是另一个脚注引用[^bignote]。
 [^1]: 第一个脚注定义。
 [^bignote]: 脚注定义可使用多段内容。

+ 12 - 14
demo/render.html

@@ -36,8 +36,6 @@
     <meta name="twitter:url" content="https://vditor.b3log.org"/>
     <meta property="og:image" content="https://cdn.jsdelivr.net/npm/vditor/src/assets/images/logo.png"/>
     <meta name="twitter:image" content="https://cdn.jsdelivr.net/npm/vditor/src/assets/images/logo.png"/>
-    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/index.css"/>
-    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/method.min.js"></script>
     <style>
         .link {
             cursor: pointer;
@@ -92,51 +90,51 @@
             background-color: var(--hover-background-color) !important;
         }
 
-        .vditor-reset ul[data-marker="*"] {
+        .vditor-reset ul[data-style="*"] {
             list-style-type: disc
         }
 
-        .vditor-reset ul[data-marker="*"] ul {
+        .vditor-reset ul[data-style="*"] ul {
             list-style-type: circle
         }
 
-        .vditor-reset ul[data-marker="*"] ul ul {
+        .vditor-reset ul[data-style="*"] ul ul {
             list-style-type: square
         }
 
-        .vditor-reset ul[data-marker="+"] {
+        .vditor-reset ul[data-style="+"] {
             list-style-type: "\2764";
         }
 
-        .vditor-reset ul[data-marker="+"] ul {
+        .vditor-reset ul[data-style="+"] ul {
             list-style-type: "\1f49a"
         }
 
-        .vditor-reset ul[data-marker="+"] ul ul {
+        .vditor-reset ul[data-style="+"] ul ul {
             list-style-type: "\1f49b"
         }
 
-        .vditor-reset ul[data-marker="-"] {
+        .vditor-reset ul[data-style="-"] {
             list-style-type: korean-hangul-formal
         }
 
-        .vditor-reset ul[data-marker="-"] ul {
+        .vditor-reset ul[data-style="-"] ul {
             list-style-type: decimal-leading-zero
         }
 
-        .vditor-reset ul[data-marker="-"] ul ul {
+        .vditor-reset ul[data-style="-"] ul ul {
             list-style-type: lower-alpha
         }
 
-        .vditor-reset ol[data-marker="1)"] {
+        .vditor-reset ol[data-style="1)"] {
             list-style-type: simp-chinese-formal
         }
 
-        .vditor-reset ol[data-marker="1)"] ol {
+        .vditor-reset ol[data-style="1)"] ol {
             list-style-type: simp-chinese-informal
         }
 
-        .vditor-reset ol[data-marker="1)"] ol ol {
+        .vditor-reset ol[data-style="1)"] ol ol {
             list-style-type: trad-chinese-formal
         }
 

+ 4 - 4
demo/render.js

@@ -16,7 +16,7 @@ const render = (fileName) => {
         markdown, {
           markdown: {
             toc: true,
-            listMarker: fileName === 'cute-list',
+            listStyle: fileName === 'cute-list',
           },
           speech: {
             enable: true,
@@ -24,7 +24,7 @@ const render = (fileName) => {
           anchor: 1,
           after () {
             if (window.innerWidth <= 768) {
-              return;
+              return
             }
             const outlineElement = document.getElementById('outline')
             Vditor.outlineRender(document.getElementById('preview'),
@@ -58,12 +58,12 @@ window.setTheme = (theme) => {
   const outlineElement = document.getElementById('outline')
   if (theme === 'dark') {
     Vditor.setCodeTheme('native')
-    Vditor.setContentTheme('dark')
+    Vditor.setContentTheme('dark', 'https://cdn.jsdelivr.net/npm/vditor/dist/css/content-theme')
     outlineElement.classList.add('dark')
     document.querySelector('html').style.backgroundColor = '#2f363d'
   } else {
     Vditor.setCodeTheme('github')
-    Vditor.setContentTheme('light')
+    Vditor.setContentTheme('light', 'https://cdn.jsdelivr.net/npm/vditor/dist/css/content-theme')
     outlineElement.classList.remove('dark')
     document.querySelector('html').style.backgroundColor = '#fff'
   }

+ 3 - 1
jest.config.js

@@ -76,7 +76,9 @@ module.exports = {
   // ],
 
   // A map from regular expressions to module names that allow to stub out resources with a single module
-  // moduleNameMapper: {},
+  moduleNameMapper: {
+    ".+\\.(svg|png|jpg)$": "identity-obj-proxy"
+  },
 
   // An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader
   // modulePathIgnorePatterns: [],

File diff suppressed because it is too large
+ 227 - 1470
package-lock.json


+ 19 - 18
package.json

@@ -1,9 +1,9 @@
 {
   "name": "vditor",
-  "version": "3.2.8",
+  "version": "3.3.6",
   "description": "♏ An In-browser Markdown editor, support WYSIWYG, Instant Rendering (Typora-like) and Split View modes.",
   "author": "Vanessa <[email protected]> (http://vanessa.b3log.org)",
-  "homepage": "https://hacpai.com/tag/vditor",
+  "homepage": "https://vditor.b3log.org",
   "jsdelivr": "dist/index.min.js",
   "main": "dist/index.min.js",
   "funding": "https://hacpai.com/sponsor",
@@ -15,23 +15,24 @@
     "src/assets/*"
   ],
   "dependencies": {
-    "diff-match-patch": "^1.0.4"
+    "diff-match-patch": "^1.0.5"
   },
   "types": "dist/index.d.ts",
   "devDependencies": {
-    "@babel/core": "^7.8.7",
-    "@babel/preset-env": "^7.8.7",
+    "@babel/core": "^7.10.4",
+    "@babel/preset-env": "^7.10.4",
     "@types/diff-match-patch": "^1.0.32",
-    "@types/jest": "^25.1.4",
-    "@types/puppeteer": "^2.0.1",
-    "autoprefixer": "^9.7.4",
-    "babel-loader": "^8.0.6",
+    "@types/jest": "^25.2.3",
+    "@types/puppeteer": "^2.1.1",
+    "autoprefixer": "^9.8.4",
+    "babel-loader": "^8.1.0",
     "copy-webpack-plugin": "^5.1.1",
-    "css-loader": "^3.4.2",
+    "css-loader": "^3.6.0",
     "file-loader": "^4.3.0",
     "html-loader": "^0.5.5",
     "html-webpack-plugin": "^3.2.0",
-    "jest": "^25.1.0",
+    "identity-obj-proxy": "^3.0.0",
+    "jest": "^25.5.4",
     "mini-css-extract-plugin": "^0.8.2",
     "node-sass": "^4.14.1",
     "on-build-webpack": "^0.1.0",
@@ -40,14 +41,14 @@
     "puppeteer": "^1.20.0",
     "rimraf": "^3.0.2",
     "sass-loader": "^7.3.1",
-    "style-loader": "^1.1.3",
-    "ts-jest": "^25.4.0",
-    "ts-loader": "^6.2.1",
+    "style-loader": "^1.2.1",
+    "ts-jest": "^25.5.1",
+    "ts-loader": "^6.2.2",
     "tslint": "^5.20.1",
-    "typescript": "^3.8.3",
-    "webpack": "^4.42.0",
-    "webpack-bundle-analyzer": "^3.6.1",
-    "webpack-cli": "^3.3.11",
+    "typescript": "^3.9.6",
+    "webpack": "^4.43.0",
+    "webpack-bundle-analyzer": "^3.8.0",
+    "webpack-cli": "^3.3.12",
     "webpack-dev-server": "^3.11.0"
   },
   "license": "MIT",

+ 3 - 0
src/assets/icons/delete-column.svg

@@ -0,0 +1,3 @@
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
+<path d="M9.598 29.866v-16.005c0-0.589-0.478-1.067-1.067-1.067s-1.067 0.478-1.067 1.067v1.067h-4.268v-4.268h1.067c0.589 0 1.067-0.478 1.067-1.067s-0.478-1.067-1.067-1.067h-1.067c-1.178 0-2.134 0.954-2.134 2.134v19.206c0 1.18 0.956 2.134 2.134 2.134h4.268c1.178 0 2.134-0.954 2.134-2.134zM3.196 23.464v-6.402h4.268v6.402h-4.268zM3.196 29.866v-4.268h4.268v4.268h-4.268zM20.268 9.593c0 0.589 0.478 1.067 1.067 1.067h1.067v4.268h-4.268v-1.067c0-0.589-0.478-1.067-1.067-1.067s-1.067 0.478-1.067 1.067v16.005c0 1.18 0.956 2.134 2.134 2.134h10.67c1.18 0 2.134-0.954 2.134-2.134v-19.206c0-1.18-0.954-2.134-2.134-2.134h-7.469c-0.589 0-1.067 0.478-1.067 1.067zM28.804 25.598v4.268h-4.268v-4.268h4.268zM22.402 25.598v4.268h-4.268v-4.268h4.268zM28.804 17.062v6.402h-4.268v-6.402h4.268zM22.402 17.062v6.402h-4.268v-6.402h4.268zM28.804 10.66v4.268h-4.268v-4.268h4.268zM12.799 3.951l-3.651-3.651c-0.397-0.397-1.041-0.397-1.438 0-0.399 0.399-0.399 1.041 0 1.44l3.651 3.651-3.587 3.587c-0.397 0.397-0.397 1.041 0 1.438 0.397 0.399 1.041 0.399 1.438 0l3.587-3.589 3.587 3.587c0.397 0.399 1.041 0.399 1.438 0 0.397-0.397 0.397-1.041 0-1.438l-3.587-3.589 3.649-3.649c0.399-0.399 0.399-1.041 0-1.44-0.397-0.397-1.041-0.397-1.438 0l-3.649 3.653z"></path>
+</svg>

+ 3 - 0
src/assets/icons/delete-row.svg

@@ -0,0 +1,3 @@
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
+<path d="M2.134 9.598h16.005c0.589 0 1.067-0.478 1.067-1.067s-0.478-1.067-1.067-1.067h-1.067v-4.268h4.268v1.067c0 0.589 0.478 1.067 1.067 1.067s1.067-0.478 1.067-1.067v-1.067c0-1.178-0.954-2.134-2.134-2.134h-19.206c-1.18 0-2.134 0.956-2.134 2.134v4.268c0 1.178 0.954 2.134 2.134 2.134zM8.536 3.196h6.402v4.268h-6.402v-4.268zM2.134 3.196h4.268v4.268h-4.268v-4.268zM22.407 20.268c-0.589 0-1.067 0.478-1.067 1.067v1.067h-4.268v-4.268h1.067c0.589 0 1.067-0.478 1.067-1.067s-0.478-1.067-1.067-1.067h-16.005c-1.18 0-2.134 0.956-2.134 2.134v10.67c0 1.18 0.954 2.134 2.134 2.134h19.206c1.18 0 2.134-0.954 2.134-2.134v-7.469c0-0.589-0.478-1.067-1.067-1.067zM6.402 28.804h-4.268v-4.268h4.268v4.268zM6.402 22.402h-4.268v-4.268h4.268v4.268zM14.938 28.804h-6.402v-4.268h6.402v4.268zM14.938 22.402h-6.402v-4.268h6.402v4.268zM21.34 28.804h-4.268v-4.268h4.268v4.268zM28.049 12.799l3.651-3.651c0.397-0.397 0.397-1.041 0-1.438-0.399-0.399-1.041-0.399-1.44 0l-3.651 3.651-3.587-3.587c-0.397-0.397-1.041-0.397-1.438 0-0.399 0.397-0.399 1.041 0 1.438l3.589 3.587-3.587 3.587c-0.399 0.397-0.399 1.041 0 1.438 0.397 0.397 1.041 0.397 1.438 0l3.589-3.587 3.649 3.649c0.399 0.399 1.041 0.399 1.44 0 0.397-0.397 0.397-1.041 0-1.438l-3.653-3.649z"></path>
+</svg>

+ 3 - 0
src/assets/icons/insert-column.svg

@@ -0,0 +1,3 @@
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
+<path d="M9.601 29.867v-19.197c0-1.18-0.956-2.133-2.133-2.133h-4.266c-1.177 0-2.133 0.953-2.133 2.133v19.197c0 1.18 0.956 2.133 2.133 2.133h4.266c1.177 0 2.133-0.953 2.133-2.133zM3.202 14.936v-4.266h4.266v4.266h-4.266zM3.202 23.468v-6.399h4.266v6.399h-4.266zM3.202 29.867v-4.266h4.266v4.266h-4.266zM16 10.669v19.197c0 1.18 0.956 2.133 2.133 2.133h10.665c1.18 0 2.133-0.953 2.133-2.133v-19.197c0-1.18-0.953-2.133-2.133-2.133h-10.665c-1.177 0-2.133 0.953-2.133 2.133zM28.798 25.601v4.266h-4.266v-4.266h4.266zM22.399 25.601v4.266h-4.266v-4.266h4.266zM28.798 17.069v6.399h-4.266v-6.399h4.266zM22.399 17.069v6.399h-4.266v-6.399h4.266zM28.798 10.669v4.266h-4.266v-4.266h4.266zM22.399 10.669v4.266h-4.266v-4.266h4.266zM7.713 2.276l3.75 5.606c0.621 0.926 2.058 0.926 2.679 0l3.75-5.606c0.661-0.99-0.094-2.276-1.34-2.276h-7.5c-1.246 0-2.001 1.288-1.34 2.276z"></path>
+</svg>

+ 3 - 0
src/assets/icons/insert-row.svg

@@ -0,0 +1,3 @@
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
+<path d="M2.133 9.601h19.197c1.18 0 2.133-0.956 2.133-2.133v-4.266c0-1.177-0.953-2.133-2.133-2.133h-19.197c-1.18 0-2.133 0.956-2.133 2.133v4.266c0 1.177 0.953 2.133 2.133 2.133zM17.064 3.202h4.266v4.266h-4.266v-4.266zM8.532 3.202h6.399v4.266h-6.399v-4.266zM2.133 3.202h4.266v4.266h-4.266v-4.266zM21.331 16h-19.197c-1.18 0-2.133 0.956-2.133 2.133v10.665c0 1.18 0.953 2.133 2.133 2.133h19.197c1.18 0 2.133-0.953 2.133-2.133v-10.665c0-1.177-0.953-2.133-2.133-2.133zM6.399 28.798h-4.266v-4.266h4.266v4.266zM6.399 22.399h-4.266v-4.266h4.266v4.266zM14.931 28.798h-6.399v-4.266h6.399v4.266zM14.931 22.399h-6.399v-4.266h6.399v4.266zM21.331 28.798h-4.266v-4.266h4.266v4.266zM21.331 22.399h-4.266v-4.266h4.266v4.266zM29.724 7.713l-5.606 3.75c-0.926 0.621-0.926 2.058 0 2.679l5.606 3.75c0.99 0.661 2.276-0.094 2.276-1.34v-7.5c0-1.246-1.288-2.001-2.276-1.34z"></path>
+</svg>

+ 3 - 0
src/assets/icons/zhihu.svg

@@ -0,0 +1,3 @@
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
+<path d="M17.167 17.769s0-2.583-1.25-2.667c-1.25-0.167-5.167 0-5.167 0v-8h5.833s-0.083-2.667-1.167-2.667h-9.5l1.583-4.25s-2.333 0.167-3.25 1.667c-0.833 1.5-3.5 9.167-3.5 9.167s0.917 0.417 2.417-0.75c1.5-1.083 2-3.083 2-3.083l2.75-0.167 0.083 8.083s-4.917-0.083-5.833 0c-1 0.083-1.5 2.667-1.5 2.667h7.417s-0.667 4.583-2.5 7.75c-1.917 3.333-5.583 5.917-5.583 5.917s2.583 1.083 5.167-0.417 4.417-8.083 4.417-8.083l5.917 7.417s0.5-3.5-0.083-4.583c-0.667-1-4.167-5-4.167-5l-1.5 1.333 1.083-4.417 6.333 0.083zM18.667 4.269l-0.083 24h2.417l0.833 2.917 4.25-2.917h5.917v-24h-13.333zM29.333 25.602h-2.75l-3.5 2.667-0.75-2.667h-0.75v-18.583h7.75v18.583z"></path>
+</svg>

+ 0 - 44
src/assets/scss/_content.scss

@@ -39,50 +39,6 @@
     position: relative;
   }
 
-  &-textarea {
-    font-family: $font-family-base;
-    margin: 0 1px 0 0;
-    // https://github.com/Vanessa219/vditor/issues/328 height: 100%;
-    overflow: auto;
-    width: 100%;
-    flex: 1;
-    min-width: 1px;
-    border: 0;
-    resize: none;
-    padding: 10px 9px 10px 10px;
-    box-sizing: border-box;
-    background-color: var(--panel-background-color);
-    outline: 0 none;
-    font-size: 16px;
-    line-height: 22px;
-    color: var(--textarea-text-color);
-    border-radius: 0 0 3px 3px;
-    font-variant-ligatures: no-common-ligatures;
-    white-space: pre-wrap;
-    word-break: break-word;
-    word-wrap: break-word;
-
-    &[contenteditable="false"] {
-      opacity: 0.3;
-      cursor: not-allowed;
-    }
-
-    &:empty::before {
-      content: attr(placeholder);
-      color: var(--second-color);
-    }
-
-    &:focus {
-      background-color: var(--textarea-background-color);
-    }
-
-    &:after {
-      content: "";
-      height: var(--editor-bottom);
-      display: block;
-    }
-  }
-
   &-preview {
     flex: 1;
     min-width: 1px;

+ 27 - 21
src/assets/scss/_ir.scss

@@ -5,10 +5,6 @@
   position: relative;
   width: 100%;
 
-  &__block[data-type="html-block"] pre {
-    margin: 1em 0 !important;
-  }
-
   &__node {
     &[data-type="code-block"]:before,
     &[data-type="code-block"]:after,
@@ -27,6 +23,10 @@
       color: $blurColor;
     }
 
+    &[data-type="html-block"] {
+      margin-bottom: 1em;
+    }
+
     .vditor-ir__marker {
       width: 0;
       overflow: hidden;
@@ -118,6 +118,12 @@
     }
   }
 
+  &__preview {
+    cursor: pointer;
+    white-space: initial;
+    min-height: 27px;
+  }
+
   &__link {
     color: var(--ir-bracket-color);
     text-decoration: underline;
@@ -165,22 +171,22 @@
   blockquote:empty::before,
   pre > code:empty::before,
   p:empty::before,
-  & > h1:empty::after,
-  & > h2:empty::after,
-  & > h3:empty::after,
-  & > h4:empty::after,
-  & > h5:empty::after,
-  & > h6:empty::after {
+  h1:empty::after,
+  h2:empty::after,
+  h3:empty::after,
+  h4:empty::after,
+  h5:empty::after,
+  h6:empty::after {
     content: ' ';
   }
 
   // block title
-  & > h1:before,
-  & > h2:before,
-  & > h3:before,
-  & > h4:before,
-  & > h5:before,
-  & > h6:before,
+  & .vditor-reset > h1:before,
+  & .vditor-reset > h2:before,
+  & .vditor-reset > h3:before,
+  & .vditor-reset > h4:before,
+  & .vditor-reset > h5:before,
+  & .vditor-reset > h6:before,
   div[data-type="link-ref-defs-block"]:before,
   div[data-type="footnotes-block"]:before,
   .vditor-toc:before {
@@ -193,23 +199,23 @@
     color: var(--second-color);
   }
 
-  & > h2:before {
+  & .vditor-reset > h2:before {
     content: 'H2';
   }
 
-  & > h3:before {
+  & .vditor-reset > h3:before {
     content: 'H3';
   }
 
-  & > h4:before {
+  & .vditor-reset > h4:before {
     content: 'H4';
   }
 
-  & > h5:before {
+  & .vditor-reset > h5:before {
     content: 'H5';
   }
 
-  & > h6:before {
+  & .vditor-reset > h6:before {
     content: 'H6';
   }
 

+ 1 - 0
src/assets/scss/_panel.scss

@@ -40,6 +40,7 @@
       min-width: auto;
       max-width: none;
       white-space: nowrap;
+      opacity: .86;
     }
 
     &--arrow:before {

+ 13 - 7
src/assets/scss/_reset.scss

@@ -84,30 +84,30 @@
 
     h1 {
       padding-bottom: 0.3em;
-      font-size: 1.7em;
+      font-size: 1.75em;
       border-bottom: 1px solid #eaecef;
     }
 
     h2 {
       padding-bottom: 0.3em;
-      font-size: 1.5em;
+      font-size: 1.55em;
       border-bottom: 1px solid #eaecef;
     }
 
     h3 {
-      font-size: 1.25em;
+      font-size: 1.38em;
     }
 
     h4 {
-      font-size: 1em
+      font-size: 1.25em
     }
 
     h5 {
-      font-size: 0.875em;
+      font-size: 1.13em;
     }
 
     h6 {
-      font-size: 0.85em;
+      font-size: 1em;
     }
 
     hr {
@@ -199,6 +199,11 @@
       &.language-mindmap {
         background-color: rgba(27, 31, 35, .02);
       }
+
+    }
+
+    .language-abc {
+      background-color: rgba(27, 31, 35, .02);
     }
 
     pre {
@@ -270,6 +275,7 @@
       polygon {
         fill: transparent;
       }
+
       parsererror {
         overflow: auto;
       }
@@ -320,7 +326,7 @@
     &__rows {
       position: absolute;
       pointer-events: none;
-      top: 0.2em;
+      top: 0.5em;
       left: 0;
       width: 3em; /* works for line-numbers below 1000 lines */
       user-select: none;

+ 124 - 0
src/assets/scss/_sv.scss

@@ -0,0 +1,124 @@
+.vditor-sv {
+
+  font-family: $font-family-base;
+  margin: 0 1px 0 0;
+  overflow: auto;
+  width: 100%;
+  flex: 1;
+  min-width: 1px;
+  border: 0;
+  resize: none;
+  padding: 10px 9px 10px 10px;
+  box-sizing: border-box;
+  background-color: var(--panel-background-color);
+  outline: 0 none;
+  font-size: 16px;
+  line-height: 22px;
+  color: var(--textarea-text-color);
+  border-radius: 0 0 3px 3px;
+  font-variant-ligatures: no-common-ligatures;
+  white-space: pre-wrap;
+  word-break: break-word;
+  word-wrap: break-word;
+
+  &[contenteditable="false"] {
+    opacity: 0.3;
+    cursor: not-allowed;
+  }
+
+  &:empty::before {
+    content: attr(placeholder);
+    color: var(--second-color);
+  }
+
+  &:focus {
+    background-color: var(--textarea-background-color);
+  }
+
+  &:after {
+    content: "";
+    height: var(--editor-bottom);
+    display: block;
+  }
+
+  .sup {
+    vertical-align: super;
+    font-size: smaller;
+  }
+
+  .strong {
+    font-weight: bold;
+  }
+
+  .em {
+    font-style: italic;
+  }
+
+  .s {
+    text-decoration: line-through;
+  }
+
+  .h1 {
+    font-size: 1.75em;
+    line-height: 44px;
+  }
+
+  .h2 {
+    font-size: 1.55em;
+    line-height: 38px;
+  }
+
+  .h3 {
+    font-size: 1.38em;
+    line-height: 27px;
+  }
+
+  .h4 {
+    font-size: 1.25em;
+    line-height: 25px;
+  }
+
+  .h5 {
+    font-size: 1.13em;
+  }
+
+  .h6 {
+    font-size: 1em;
+  }
+
+  &__marker {
+    color: var(--second-color);
+
+    &--heading {
+      color: var(--ir-heading-color);
+    }
+
+    &--bi {
+      color: var(--ir-bi-color);
+    }
+
+    &--link {
+      color: var(--ir-link-color);
+    }
+
+    &--title {
+      color: var(--ir-title-color);
+    }
+
+    &--bracket {
+      color: var(--ir-bracket-color);
+    }
+
+    &--paren {
+      color: var(--ir-paren-color);
+    }
+
+    &--info {
+      color: var(--ir-heading-color);
+    }
+
+    &--strong {
+      font-weight: bold;
+    }
+  }
+}

+ 4 - 0
src/assets/scss/_toolbar.scss

@@ -35,6 +35,7 @@
         height: 35px;
         width: 25px;
         box-sizing: border-box;
+        font-size: 0;
 
         &:focus {
           outline: none;
@@ -166,6 +167,9 @@
   .vditor-toolbar__item {
     padding: 0 12px;
   }
+  .vditor-panel--left.vditor-panel--arrow:before {
+    right: 17px;
+  }
 }
 
 @media(hover: hover) and (pointer: fine) {

+ 17 - 17
src/assets/scss/_wysiwyg.scss

@@ -37,12 +37,12 @@
   blockquote:empty::before,
   pre > code:empty::before,
   p:empty::before,
-  & > h1:empty::after,
-  & > h2:empty::after,
-  & >  h3:empty::after,
-  & > h4:empty::after,
-  & > h5:empty::after,
-  & > h6:empty::after {
+  h1:empty::after,
+  h2:empty::after,
+  h3:empty::after,
+  h4:empty::after,
+  h5:empty::after,
+  h6:empty::after {
     content: ' ';
   }
 
@@ -76,12 +76,12 @@
     }
   }
 
-  & > h1:before,
-  & > h2:before,
-  & >  h3:before,
-  & > h4:before,
-  & > h5:before,
-  & > h6:before,
+  & > .vditor-reset > h1:before,
+  & > .vditor-reset > h2:before,
+  & > .vditor-reset > h3:before,
+  & > .vditor-reset > h4:before,
+  & > .vditor-reset > h5:before,
+  & > .vditor-reset > h6:before,
   div.vditor-wysiwyg__block:before,
   div[data-type="link-ref-defs-block"]:before,
   div[data-type="footnotes-block"]:before,
@@ -95,23 +95,23 @@
     color: var(--second-color);
   }
 
-  & > h2:before {
+  & > .vditor-reset > h2:before {
     content: 'H2';
   }
 
-  & > h3:before {
+  & > .vditor-reset > h3:before {
     content: 'H3';
   }
 
-  & > h4:before {
+  & > .vditor-reset > h4:before {
     content: 'H4';
   }
 
-  & > h5:before {
+  & > .vditor-reset > h5:before {
     content: 'H5';
   }
 
-  & > h6:before {
+  & > .vditor-reset > h6:before {
     content: 'H6';
   }
 

+ 2 - 1
src/assets/scss/index.scss

@@ -34,7 +34,7 @@ $max-width: 520px !default;
 
   --ir-heading-color: #660e7a;
   --ir-title-color: #808080;
-  --ir-bi-color: #000080;
+  --ir-bi-color: #0033b3;
   --ir-link-color: #008000;
   --ir-bracket-color: #0000ff;
   --ir-paren-color: #008000;
@@ -80,3 +80,4 @@ $max-width: 520px !default;
 @import "reset";
 @import "wysiwyg";
 @import "ir";
+@import "sv";

+ 2 - 1
src/css/content-theme/dark.css

@@ -70,7 +70,8 @@
 .vditor-reset code:not(.hljs):not(.highlight-chroma).language-mindmap,
 .vditor-reset code:not(.hljs):not(.highlight-chroma).language-graphviz,
 .vditor-reset code:not(.hljs):not(.highlight-chroma).language-math,
-.vditor-reset code:not(.hljs):not(.highlight-chroma).language-mermaid {
+.vditor-reset code:not(.hljs):not(.highlight-chroma).language-mermaid,
+.vditor-reset .language-abc {
     background-color: rgba(120, 146, 190, .55)
 }
 

+ 109 - 0
src/css/content-theme/light.css

@@ -0,0 +1,109 @@
+/*!
+ * Vditor - A markdown editor written in TypeScript.
+ *
+ * MIT License
+ *
+ * Copyright (c) 2018-present B3log 开源, b3log.org
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ */
+.vditor-reset h1, .vditor-reset h2 {
+    border-bottom: 1px solid #eaecef;
+}
+
+.vditor-reset hr {
+    background-color: #eaecef;
+}
+
+.vditor-reset blockquote {
+    color: #6a737d;
+    border-left: .25em solid #eaecef
+}
+
+.vditor-reset iframe {
+    border: 1px solid #d1d5da
+}
+
+.vditor-reset table tr {
+    border-top: 1px solid #c6cbd1;
+    background-color: #fafbfc
+}
+
+.vditor-reset table td, .vditor-reset table th {
+    border: 1px solid #dfe2e5
+}
+
+.vditor-reset table tbody tr:nth-child(2n) {
+    background-color: #fff
+}
+
+.vditor-reset code:not(.hljs):not(.highlight-chroma) {
+    background-color: rgba(27, 31, 35, .05);
+}
+
+.vditor-reset code:not(.hljs):not(.highlight-chroma).language-echarts,
+.vditor-reset code:not(.hljs):not(.highlight-chroma).language-mindmap,
+.vditor-reset code:not(.hljs):not(.highlight-chroma).language-graphviz,
+.vditor-reset code:not(.hljs):not(.highlight-chroma).language-math,
+.vditor-reset code:not(.hljs):not(.highlight-chroma).language-mermaid,
+.vditor-reset .language-abc {
+    background-color: rgba(27, 31, 35, .02);
+}
+
+.vditor-reset kbd {
+    color: #24292e;
+    background-color: #fafbfc;
+    border: solid 1px #d1d5da;
+    box-shadow: inset 0 -1px 0 #d1d5da;
+}
+
+.vditor-speech {
+    background-color: #f6f8fa;
+    border: 1px solid #d1d5da;
+    color: #586069;
+}
+
+.vditor-speech--current, .vditor-speech:hover {
+    color: #4285f4;
+}
+
+.vditor-linkcard a {
+    background-color: #f6f8fa;
+}
+
+.vditor-linkcard a:visited .vditor-linkcard__abstract {
+    color: rgba(88, 96, 105, 0.36);
+}
+
+.vditor-linkcard__title {
+    color: #24292e;
+}
+
+.vditor-linkcard__abstract {
+    color: #586069;
+}
+
+.vditor-linkcard__site {
+    color: #4285f4;
+}
+
+.vditor-linkcard__image {
+    background-color: rgba(88, 96, 105, 0.36);
+}

+ 59 - 51
src/index.ts

@@ -12,11 +12,9 @@ import {setLute} from "./ts/markdown/setLute";
 import {Outline} from "./ts/outline";
 import {Preview} from "./ts/preview/index";
 import {Resize} from "./ts/resize/index";
-import {formatRender} from "./ts/sv/formatRender";
-import {getSelectText} from "./ts/sv/getSelectText";
-import {html2md} from "./ts/sv/html2md";
 import {Editor} from "./ts/sv/index";
-import {insertText} from "./ts/sv/insertText";
+import {inputEvent} from "./ts/sv/inputEvent";
+import {processAfterRender as processSVAfterRender} from "./ts/sv/process";
 import {Tip} from "./ts/tip";
 import {Toolbar} from "./ts/toolbar/index";
 import {disableToolbar, hidePanel} from "./ts/toolbar/setToolbar";
@@ -27,12 +25,12 @@ import {setContentTheme} from "./ts/ui/setContentTheme";
 import {setPreviewMode} from "./ts/ui/setPreviewMode";
 import {setTheme} from "./ts/ui/setTheme";
 import {Undo} from "./ts/undo";
-import {IRUndo} from "./ts/undo/IRUndo";
-import {WysiwygUndo} from "./ts/undo/WysiwygUndo";
 import {Upload} from "./ts/upload/index";
 import {addScript} from "./ts/util/addScript";
+import {getSelectText} from "./ts/util/getSelectText";
 import {Options} from "./ts/util/Options";
-import {getCursorPosition, getEditorRange, setSelectionByPosition} from "./ts/util/selection";
+import {processCodeRender} from "./ts/util/processCode";
+import {getCursorPosition, getEditorRange} from "./ts/util/selection";
 import {WYSIWYG} from "./ts/wysiwyg";
 import {input} from "./ts/wysiwyg/input";
 import {renderDomByMd} from "./ts/wysiwyg/renderDomByMd";
@@ -51,10 +49,14 @@ class Vditor extends VditorMethod {
         this.version = VDITOR_VERSION;
 
         if (typeof id === "string") {
-            if (!options.cache) {
-                options.cache = {
-                    id: `vditor${id}`,
+            if (!options) {
+                options = {
+                    cache: {
+                        id: `vditor${id}`,
+                    },
                 };
+            } else if (!options.cache) {
+                options.cache = {id: `vditor${id}`};
             } else if (!options.cache.id) {
                 options.cache.id = `vditor${id}`;
             }
@@ -82,8 +84,6 @@ class Vditor extends VditorMethod {
         this.vditor.sv = new Editor(this.vditor);
         this.vditor.undo = new Undo();
         this.vditor.wysiwyg = new WYSIWYG(this.vditor);
-        this.vditor.wysiwygUndo = new WysiwygUndo();
-        this.vditor.irUndo = new IRUndo();
         this.vditor.ir = new IR(this.vditor);
         this.vditor.toolbar = new Toolbar(this.vditor);
 
@@ -99,7 +99,7 @@ class Vditor extends VditorMethod {
             this.vditor.upload = new Upload();
         }
 
-        // const lutePath = `http://192.168.2.248:9090/lute.min.js?${new Date().getTime()}`;
+        // const lutePath = `http://192.168.0.107:9090/lute.min.js?${new Date().getTime()}`;
         // const lutePath = "src/js/lute/lute.min.js";
         const lutePath = `${mergedOptions.cdn}/dist/js/lute/lute.min.js`;
         addScript(lutePath, "vditorLuteScript").then(() => {
@@ -113,7 +113,8 @@ class Vditor extends VditorMethod {
                 footnotes: this.vditor.options.preview.markdown.footnotes,
                 headingAnchor: false,
                 inlineMathDigit: this.vditor.options.preview.math.inlineDigit,
-                listMarker: this.vditor.options.preview.markdown.listMarker,
+                linkBase: this.vditor.options.preview.markdown.linkBase,
+                listStyle: this.vditor.options.preview.markdown.listStyle,
                 paragraphBeginningSpace: this.vditor.options.preview.markdown.paragraphBeginningSpace,
                 sanitize: this.vditor.options.preview.markdown.sanitize,
                 setext: this.vditor.options.preview.markdown.setext,
@@ -131,12 +132,12 @@ class Vditor extends VditorMethod {
     }
 
     /** 设置主题 */
-    public setTheme(theme: "dark" | "classic", contentTheme?: string, codeTheme?: string) {
+    public setTheme(theme: "dark" | "classic", contentTheme?: string, codeTheme?: string, contentThemePath?: string) {
         this.vditor.options.theme = theme;
         setTheme(this.vditor);
         if (contentTheme) {
-            this.vditor.options.preview.markdown.theme = contentTheme;
-            setContentTheme(contentTheme, this.vditor.options.cdn);
+            this.vditor.options.preview.theme.current = contentTheme;
+            setContentTheme(contentTheme, contentThemePath || this.vditor.options.preview.theme.path);
         }
         if (codeTheme) {
             this.vditor.options.preview.hljs.style = codeTheme;
@@ -178,6 +179,7 @@ class Vditor extends VditorMethod {
 
     /** 禁用编辑器 */
     public disabled() {
+        hidePanel(this.vditor, ["subToolbar", "hint", "popover"]);
         disableToolbar(this.vditor.toolbar.elements, Constants.EDIT_TOOLBARS.concat(["undo", "redo", "fullscreen",
             "edit-mode"]));
         this.vditor[this.vditor.currentMode].element.setAttribute("contenteditable", "false");
@@ -188,19 +190,9 @@ class Vditor extends VditorMethod {
         enableToolbar(this.vditor.toolbar.elements, Constants.EDIT_TOOLBARS.concat(["undo", "redo", "fullscreen",
             "edit-mode"]));
         this.vditor.undo.resetIcon(this.vditor);
-        this.vditor.wysiwygUndo.resetIcon(this.vditor);
         this.vditor[this.vditor.currentMode].element.setAttribute("contenteditable", "true");
     }
 
-    /** 选中从 start 开始到 end 结束的字符串,不支持 wysiwyg & ir 模式 */
-    public setSelection(start: number, end: number) {
-        if (this.vditor.currentMode !== "sv") {
-            console.error("所见即所得模式暂不支持该方法");
-        } else {
-            setSelectionByPosition(start, end, this.vditor.sv.element);
-        }
-    }
-
     /** 返回选中的字符串 */
     public getSelection() {
         if (this.vditor.currentMode === "wysiwyg") {
@@ -248,7 +240,7 @@ class Vditor extends VditorMethod {
 
     /** HTML 转 md */
     public html2md(value: string) {
-        return html2md(this.vditor, value);
+        return  this.vditor.lute.HTML2Md(value);
     }
 
     /** 获取 HTML */
@@ -271,37 +263,31 @@ class Vditor extends VditorMethod {
         if (window.getSelection().isCollapsed) {
             return;
         }
-        if (this.vditor.currentMode === "sv") {
-            insertText(this.vditor, "", "", true);
-        } else {
-            document.execCommand("delete", false);
-        }
+        document.execCommand("delete", false);
     }
 
     /** 更新选中内容 */
     public updateValue(value: string) {
-        if (this.vditor.currentMode === "sv") {
-            insertText(this.vditor, value, "", true);
-        } else {
-            document.execCommand("insertHTML", false, value);
-        }
+        document.execCommand("insertHTML", false, value);
     }
 
     /** 在焦点处插入内容,并默认进行 Markdown 渲染 */
     public insertValue(value: string, render = true) {
+        const range = getEditorRange(this.vditor[this.vditor.currentMode].element);
+        range.collapse(true);
         if (this.vditor.currentMode === "sv") {
-            insertText(this.vditor, value, "");
+            this.vditor.sv.preventInput = true;
+            document.execCommand("insertHTML", false, value);
+            if (render) {
+                inputEvent(this.vditor);
+            }
         } else if (this.vditor.currentMode === "wysiwyg") {
-            const range = getEditorRange(this.vditor.wysiwyg.element);
-            range.collapse(true);
             this.vditor.wysiwyg.preventInput = true;
             document.execCommand("insertHTML", false, value);
             if (render) {
                 input(this.vditor, getSelection().getRangeAt(0));
             }
         } else if (this.vditor.currentMode === "ir") {
-            const range = getEditorRange(this.vditor.ir.element);
-            range.collapse(true);
             this.vditor.ir.preventInput = true;
             document.execCommand("insertHTML", false, value);
             if (render) {
@@ -311,22 +297,31 @@ class Vditor extends VditorMethod {
     }
 
     /** 设置编辑器内容 */
-    public setValue(markdown: string) {
+    public setValue(markdown: string, clearStack = false) {
+        if (clearStack) {
+            this.clearStack();
+        }
         if (this.vditor.currentMode === "sv") {
-            formatRender(this.vditor, markdown, {
-                end: markdown.length,
-                start: markdown.length,
-            }, {
-                enableAddUndoStack: true,
+            this.vditor.sv.element.innerHTML = this.vditor.lute.SpinVditorSVDOM(markdown);
+            processSVAfterRender(this.vditor, {
+                enableAddUndoStack: !clearStack,
                 enableHint: false,
                 enableInput: false,
             });
         } else if (this.vditor.currentMode === "wysiwyg") {
-            renderDomByMd(this.vditor, markdown, false);
+            renderDomByMd(this.vditor, markdown, {
+                enableAddUndoStack: !clearStack,
+                enableHint: false,
+                enableInput: false,
+            });
         } else {
             this.vditor.ir.element.innerHTML = this.vditor.lute.Md2VditorIRDOM(markdown);
+            this.vditor.ir.element.querySelectorAll(".vditor-ir__preview[data-render='2']").forEach(
+                (item: HTMLElement) => {
+                    processCodeRender(item, this.vditor);
+                });
             processAfterRender(this.vditor, {
-                enableAddUndoStack: true,
+                enableAddUndoStack: !clearStack,
                 enableHint: false,
                 enableInput: false,
             });
@@ -342,6 +337,19 @@ class Vditor extends VditorMethod {
             this.clearCache();
         }
     }
+
+    /** 清空 undo & redo 栈 */
+    public clearStack() {
+        this.vditor.undo.clearStack(this.vditor);
+    }
+
+    /** 销毁编辑器 */
+    public destroy() {
+        this.vditor.element.innerHTML = this.vditor.originalInnerHTML;
+        this.vditor.element.classList.remove("vditor");
+        this.vditor.element.removeAttribute("style");
+        this.clearCache();
+    }
 }
 
 export default Vditor;

File diff suppressed because it is too large
+ 0 - 0
src/js/lute/lute.min.js


File diff suppressed because it is too large
+ 0 - 1
src/js/mermaid/mermaid.min.js


+ 45 - 52
src/ts/constants.ts

@@ -9,56 +9,49 @@ export abstract class Constants {
     public static readonly MOBILE_WIDTH: number = 520;
     public static readonly CLASS_MENU_DISABLED: string = "vditor-menu--disabled";
     public static readonly EDIT_TOOLBARS: string[] = ["emoji", "headings", "bold", "italic", "strike", "link", "list",
-        "ordered-list", "outdent", "indent", "check", "line", "quote", "code", "inline-code", "insert-after", "insert-before", "upload", "record", "table"];
-    public static readonly CONTENT_THEME: string[] = ["dark", "light", "wechat"];
-    public static readonly CODE_THEME: string[] = ["abap", "algol", "algol_nu", "arduino", "autumn", "borland", "bw", "colorful", "dracula",
-        "emacs", "friendly", "fruity", "github", "igor", "lovelace", "manni", "monokai", "monokailight", "murphy",
-        "native", "paraiso-dark", "paraiso-light", "pastie", "perldoc", "pygments", "rainbow_dash", "rrt",
-        "solarized-dark", "solarized-dark256", "solarized-light", "swapoff", "tango", "trac", "vim", "vs", "xcode"];
-    public static readonly CODE_LANGUAGES: string[] = [
-        "mermaid",
-        "echarts",
-        "mindmap",
-        "abc",
-        "graphviz",
-        "apache",
-        "bash",
-        "cs",
-        "cpp",
-        "css",
-        "coffeescript",
-        "diff",
-        "xml",
-        "http",
-        "ini",
-        "json",
-        "java",
-        "javascript",
-        "js",
-        "makefile",
-        "markdown",
-        "nginx",
-        "objectivec",
-        "php",
-        "perl",
-        "properties",
-        "python",
-        "ruby",
-        "sql",
-        "shell",
-        "dart",
-        "erb",
-        "go",
-        "gradle",
-        "julia",
-        "kotlin",
-        "less",
-        "lua",
-        "matlab",
-        "rust",
-        "scss",
-        "typescript",
-        "ts",
-        "yaml",
-    ];
+        "ordered-list", "outdent", "indent", "check", "line", "quote", "code", "inline-code", "insert-after",
+        "insert-before", "upload", "record", "table"];
+    public static readonly CODE_THEME: string[] = ["abap", "algol", "algol_nu", "arduino", "autumn", "borland", "bw",
+        "colorful", "dracula", "emacs", "friendly", "fruity", "github", "igor", "lovelace", "manni", "monokai",
+        "monokailight", "murphy", "native", "paraiso-dark", "paraiso-light", "pastie", "perldoc", "pygments",
+        "rainbow_dash", "rrt", "solarized-dark", "solarized-dark256", "solarized-light", "swapoff", "tango", "trac",
+        "vim", "vs", "xcode"];
+    public static readonly CODE_LANGUAGES: string[] = ["mermaid", "echarts", "mindmap", "abc", "graphviz", "apache",
+        "bash", "cs", "cpp", "css", "coffeescript", "diff", "xml", "http", "ini", "json", "java", "javascript", "js",
+        "makefile", "markdown", "nginx", "objectivec", "php", "perl", "properties", "python", "ruby", "sql", "shell",
+        "dart", "erb", "go", "gradle", "julia", "kotlin", "less", "lua", "matlab", "rust", "scss", "typescript", "ts",
+        "yaml"];
+    public static readonly CDN = `https://cdn.jsdelivr.net/npm/vditor@${VDITOR_VERSION}`;
+    public static readonly MARKDOWN_OPTIONS = {
+        autoSpace: false,
+        chinesePunct: false,
+        codeBlockPreview: true,
+        fixTermTypo: false,
+        footnotes: true,
+        linkBase: "",
+        listStyle: false,
+        paragraphBeginningSpace: false,
+        sanitize: true,
+        setext: true,
+        toc: false,
+    };
+    public static readonly HLJS_OPTIONS = {
+        enable: true,
+        lineNumber: false,
+        style: "github",
+    };
+    public static readonly MATH_OPTIONS: IMath = {
+        engine: "KaTeX",
+        inlineDigit: false,
+        macros: {},
+    };
+    public static readonly THEME_OPTIONS = {
+        current: "light",
+        list: {
+            dark: "Dark",
+            light: "Light",
+            wechat: "WeChat",
+        },
+        path: `${Constants.CDN}/dist/css/content-theme`,
+    };
 }

+ 2 - 2
src/ts/export/index.ts

@@ -36,7 +36,7 @@ window.addEventListener("message", (e) => {
   }
   Vditor.preview(document.getElementById('preview'), e.data, {
     markdown: {
-      theme: "${vditor.options.preview.markdown.theme}"
+      theme: "${vditor.options.preview.theme}"
     },
     hljs: {
       style: "${vditor.options.preview.hljs.style}"
@@ -60,7 +60,7 @@ export const exportHTML = (vditor: IVditor) => {
 <body><div class="vditor-reset" id="preview">${content}</div>
 <script>
     const previewElement = document.getElementById('preview')
-    Vditor.setContentTheme('${vditor.options.preview.markdown.theme}', '${vditor.options.cdn}');
+    Vditor.setContentTheme('${vditor.options.preview.theme.current}', '${vditor.options.preview.theme.path}');
     Vditor.codeRender(previewElement, '${vditor.options.lang}');
     Vditor.highlightRender(${JSON.stringify(vditor.options.preview.hljs)}, previewElement, '${vditor.options.cdn}');
     Vditor.mathRender(previewElement, {

+ 49 - 67
src/ts/hint/index.ts

@@ -1,12 +1,11 @@
 import {Constants} from "../constants";
 import {processAfterRender} from "../ir/process";
-import {getMarkdown} from "../markdown/getMarkdown";
-import {formatRender} from "../sv/formatRender";
 import {code160to32} from "../util/code160to32";
+import {isCtrl} from "../util/compatibility";
 import {execAfterRender} from "../util/fixBrowserBehavior";
 import {hasClosestByAttribute, hasClosestByClassName} from "../util/hasClosest";
 import {processCodeRender} from "../util/processCode";
-import {getCursorPosition, getSelectPosition, insertHTML, setSelectionFocus} from "../util/selection";
+import {getCursorPosition, insertHTML, setSelectionFocus} from "../util/selection";
 
 export class Hint {
     public timeId: number;
@@ -24,16 +23,9 @@ export class Hint {
         if (!window.getSelection().focusNode) {
             return;
         }
-        const position = getSelectPosition(vditor.currentMode === "wysiwyg" ?
-            vditor.wysiwyg.element : vditor.sv.element);
         let currentLineValue: string;
-        if (vditor.currentMode !== "sv") {
-            const range = getSelection().getRangeAt(0);
-            currentLineValue = range.startContainer.textContent.substring(0, range.startOffset) || "";
-        } else {
-            currentLineValue = getMarkdown(vditor)
-                .substring(0, position.end).split("\n").slice(-1).pop();
-        }
+        const range = getSelection().getRangeAt(0);
+        currentLineValue = range.startContainer.textContent.substring(0, range.startOffset) || "";
 
         let key = this.getKey(currentLineValue, ":");
         let isAt = false;
@@ -135,68 +127,58 @@ ${i === 0 ? "class='vditor-hint--current'" : ""}> ${html}</button>`;
         const splitChar = value.indexOf("@") === 0 ? "@" : ":";
         const range: Range = window.getSelection().getRangeAt(0);
 
-        if (vditor.currentMode !== "sv") {
-            if (vditor.currentMode === "ir") {
-                const preBeforeElement = hasClosestByAttribute(range.startContainer, "data-type", "code-block-info");
-                if (preBeforeElement) {
-                    preBeforeElement.textContent = Constants.ZWSP + value.trimRight();
-                    range.selectNodeContents(preBeforeElement);
-                    range.collapse(false);
-                    processAfterRender(vditor);
-                    preBeforeElement.parentElement.querySelectorAll("code").forEach((item) => {
-                        item.className = "language-" + value.trimRight();
-                    });
-                    processCodeRender(preBeforeElement.parentElement.querySelector(".vditor-ir__preview"), vditor);
-                    this.recentLanguage = value.trimRight();
-                    return;
-                }
-            }
-            if (vditor.currentMode === "wysiwyg" && range.startContainer.nodeType !== 3 &&
-                (range.startContainer as HTMLElement).firstElementChild.classList.contains("vditor-input")) {
-                const inputElement = (range.startContainer as HTMLElement).firstElementChild as HTMLInputElement;
-                inputElement.value = value.trimRight();
-                range.selectNodeContents(inputElement);
+        // 代码提示
+        if (vditor.currentMode === "ir") {
+            const preBeforeElement = hasClosestByAttribute(range.startContainer, "data-type", "code-block-info");
+            if (preBeforeElement) {
+                preBeforeElement.textContent = Constants.ZWSP + value.trimRight();
+                range.selectNodeContents(preBeforeElement);
                 range.collapse(false);
-                inputElement.dispatchEvent(new CustomEvent("input"));
+                processAfterRender(vditor);
+                preBeforeElement.parentElement.querySelectorAll("code").forEach((item) => {
+                    item.className = "language-" + value.trimRight();
+                });
+                processCodeRender(preBeforeElement.parentElement.querySelector(".vditor-ir__preview"), vditor);
                 this.recentLanguage = value.trimRight();
                 return;
             }
-
-            range.setStart(range.startContainer, range.startContainer.textContent.lastIndexOf(splitChar));
-            range.deleteContents();
-            if (value.indexOf(":") > -1) {
-                insertHTML(vditor.lute.SpinVditorDOM(value), vditor);
-                range.insertNode(document.createTextNode(" "));
-            } else {
-                range.insertNode(document.createTextNode(value));
-            }
+        }
+        if (vditor.currentMode === "wysiwyg" && range.startContainer.nodeType !== 3 &&
+            (range.startContainer as HTMLElement).firstElementChild.classList.contains("vditor-input")) {
+            const inputElement = (range.startContainer as HTMLElement).firstElementChild as HTMLInputElement;
+            inputElement.value = value.trimRight();
+            range.selectNodeContents(inputElement);
             range.collapse(false);
-            setSelectionFocus(range);
+            inputElement.dispatchEvent(new CustomEvent("input"));
+            this.recentLanguage = value.trimRight();
+            return;
+        }
 
-            if (vditor.currentMode === "wysiwyg") {
-                const preElement = hasClosestByClassName(range.startContainer, "vditor-wysiwyg__block");
-                if (preElement && preElement.lastElementChild.classList.contains("vditor-wysiwyg__preview")) {
-                    preElement.lastElementChild.innerHTML = preElement.firstElementChild.innerHTML;
-                    processCodeRender(preElement.lastElementChild as HTMLElement, vditor);
-                }
-            } else {
-                const preElement = hasClosestByClassName(range.startContainer, "vditor-ir__marker--pre");
-                if (preElement && preElement.nextElementSibling.classList.contains("vditor-ir__preview")) {
-                    preElement.nextElementSibling.innerHTML = preElement.innerHTML;
-                    processCodeRender(preElement.nextElementSibling as HTMLElement, vditor);
-                }
-            }
-            execAfterRender(vditor);
+        range.setStart(range.startContainer, range.startContainer.textContent.lastIndexOf(splitChar));
+        range.deleteContents();
+        if (value.indexOf(":") > -1 && vditor.currentMode !== "sv") {
+            insertHTML(vditor.lute.SpinVditorDOM(value), vditor);
+            range.insertNode(document.createTextNode(" "));
         } else {
-            const position = getSelectPosition(vditor.sv.element, range);
-            const text = getMarkdown(vditor);
-            const preText = text.substring(0, text.substring(0, position.start).lastIndexOf(splitChar));
-            formatRender(vditor, preText + value + text.substring(position.start),
-                {
-                    end: (preText + value).length,
-                    start: (preText + value).length,
-                });
+            range.insertNode(document.createTextNode(value));
+        }
+        range.collapse(false);
+        setSelectionFocus(range);
+
+        if (vditor.currentMode === "wysiwyg") {
+            const preElement = hasClosestByClassName(range.startContainer, "vditor-wysiwyg__block");
+            if (preElement && preElement.lastElementChild.classList.contains("vditor-wysiwyg__preview")) {
+                preElement.lastElementChild.innerHTML = preElement.firstElementChild.innerHTML;
+                processCodeRender(preElement.lastElementChild as HTMLElement, vditor);
+            }
+        } else if (vditor.currentMode === "ir") {
+            const preElement = hasClosestByClassName(range.startContainer, "vditor-ir__marker--pre");
+            if (preElement && preElement.nextElementSibling.classList.contains("vditor-ir__preview")) {
+                preElement.nextElementSibling.innerHTML = preElement.innerHTML;
+                processCodeRender(preElement.nextElementSibling as HTMLElement, vditor);
+            }
         }
+        execAfterRender(vditor);
     }
 
     public select(event: KeyboardEvent, vditor: IVditor) {
@@ -229,7 +211,7 @@ ${i === 0 ? "class='vditor-hint--current'" : ""}> ${html}</button>`;
                 currentHintElement.previousElementSibling.className = "vditor-hint--current";
             }
             return true;
-        } else if (event.key === "Enter") {
+        } else if (!isCtrl(event) && !event.shiftKey && !event.altKey && event.key === "Enter") {
             event.preventDefault();
             event.stopPropagation();
             this.fillEmoji(currentHintElement, vditor);

+ 12 - 3
src/ts/i18n/index.ts

@@ -14,6 +14,8 @@ export const i18n: II18n = {
         "content-theme": "Content Theme Preview",
         "copied": "Copied",
         "copy": "Copy",
+        "delete-column": "Delete Row",
+        "delete-row": "Delete Column",
         "devtools": "DevTools",
         "down": "Down",
         "downloadTip": "The browser does not support the download function",
@@ -23,7 +25,6 @@ export const i18n: II18n = {
         "export": "Export",
         "fileTypeError": "file type is error",
         "footnoteRef": "Footnote Ref",
-        "format": "Format",
         "fullscreen": "Toggle Fullscreen",
         "generate": "Generating",
         "headings": "Headings",
@@ -34,6 +35,8 @@ export const i18n: II18n = {
         "inline-code": "Inline Code",
         "insert-after": "Insert line after",
         "insert-before": "Insert line Before",
+        "insert-column": "Insert Column",
+        "insert-row": "Insert Row",
         "instantRendering": "Instant Rendering",
         "italic": "Italic",
         "language": "Language",
@@ -84,6 +87,8 @@ export const i18n: II18n = {
         "content-theme": "컨텐츠테마",
         "copied": "복사완료",
         "copy": "복사",
+        "delete-column": "열 삭제",
+        "delete-row": "행 삭제",
         "devtools": "개발툴",
         "down": "다운",
         "downloadTip": "브라우저가 다운로드 기능을 지원하지 않습니다",
@@ -93,7 +98,6 @@ export const i18n: II18n = {
         "export": "내보내기",
         "fileTypeError": "지원하지않습니다.",
         "footnoteRef": "각주참조",
-        "format": "형식",
         "fullscreen": "전체화면",
         "generate": "생성",
         "headings": "제목크기",
@@ -104,6 +108,8 @@ export const i18n: II18n = {
         "inline-code": "인라인코드",
         "insert-after": "블락 뒤로 입력",
         "insert-before": "블락 앞으로 입력",
+        "insert-column": "열 삽입",
+        "insert-row": "행 삽입",
         "instantRendering": "타이포라",
         "italic": "기울임꼴",
         "language": "언어",
@@ -154,6 +160,8 @@ export const i18n: II18n = {
         "content-theme": "内容主题预览",
         "copied": "已复制",
         "copy": "复制",
+        "delete-column": "删除列",
+        "delete-row": "删除行",
         "devtools": "开发者工具",
         "down": "下",
         "downloadTip": "该浏览器不支持下载功能",
@@ -163,7 +171,6 @@ export const i18n: II18n = {
         "export": "导出",
         "fileTypeError": "文件类型不允许上传",
         "footnoteRef": "脚注标识",
-        "format": "格式化",
         "fullscreen": "全屏切换",
         "generate": "生成中",
         "headings": "标题",
@@ -174,6 +181,8 @@ export const i18n: II18n = {
         "inline-code": "行内代码",
         "insert-after": "末尾插入行",
         "insert-before": "起始插入行",
+        "insert-column": "插入列",
+        "insert-row": "插入行",
         "instantRendering": "即时渲染",
         "italic": "斜体",
         "language": "语言",

+ 18 - 0
src/ts/ir/expandMarker.ts

@@ -32,6 +32,17 @@ const nextIsNode = (range: Range) => {
     return false;
 };
 
+const previousIsNode = (range: Range) => {
+    const startContainer = range.startContainer;
+    const previousNode = startContainer.previousSibling as HTMLElement;
+    if (startContainer.nodeType === 3 && range.startOffset === 0 && previousNode && previousNode.nodeType !== 3 &&
+        // *em*|text
+        previousNode.classList.contains("vditor-ir__node") && !previousNode.getAttribute("data-block")) {
+        return previousNode;
+    }
+    return false;
+};
+
 export const expandMarker = (range: Range, vditor: IVditor) => {
     vditor.ir.element.querySelectorAll(".vditor-ir__node--expand").forEach((item) => {
         item.classList.remove("vditor-ir__node--expand");
@@ -49,4 +60,11 @@ export const expandMarker = (range: Range, vditor: IVditor) => {
         nextNode.classList.remove("vditor-ir__node--hidden");
         return;
     }
+
+    const previousNode = previousIsNode(range);
+    if (previousNode) {
+        previousNode.classList.add("vditor-ir__node--expand");
+        previousNode.classList.remove("vditor-ir__node--hidden");
+        return;
+    }
 };

+ 21 - 0
src/ts/ir/highlightToolbarIR.ts

@@ -0,0 +1,21 @@
+import {disableToolbar, enableToolbar, setCurrentToolbar} from "../toolbar/setToolbar";
+import {hasClosestByMatchTag} from "../util/hasClosest";
+import {highlightToolbarIRSV} from "../util/highlightToolbar";
+
+export const highlightToolbarIR = (vditor: IVditor) => {
+    highlightToolbarIRSV(vditor, (typeElement: HTMLElement) => {
+        const liElement = hasClosestByMatchTag(typeElement, "LI");
+        if (liElement) {
+            if (liElement.classList.contains("vditor-task")) {
+                setCurrentToolbar(vditor.toolbar.elements, ["check"]);
+            } else if (liElement.parentElement.tagName === "OL") {
+                setCurrentToolbar(vditor.toolbar.elements, ["ordered-list"]);
+            } else if (liElement.parentElement.tagName === "UL") {
+                setCurrentToolbar(vditor.toolbar.elements, ["list"]);
+            }
+            enableToolbar(vditor.toolbar.elements, ["outdent", "indent"]);
+        } else {
+            disableToolbar(vditor.toolbar.elements, ["outdent", "indent"]);
+        }
+    });
+};

+ 17 - 27
src/ts/ir/index.ts

@@ -1,7 +1,6 @@
 import {Constants} from "../constants";
-import {uploadFiles} from "../upload";
 import {isCtrl, isFirefox} from "../util/compatibility";
-import {blurEvent, focusEvent, hotkeyEvent, scrollCenter, selectEvent} from "../util/editorCommonEvent";
+import {blurEvent, dropEvent, focusEvent, hotkeyEvent, scrollCenter, selectEvent} from "../util/editorCommonEvent";
 import {paste} from "../util/fixBrowserBehavior";
 import {hasClosestByClassName} from "../util/hasClosest";
 import {
@@ -9,7 +8,7 @@ import {
     setSelectionFocus,
 } from "../util/selection";
 import {expandMarker} from "./expandMarker";
-import {highlightToolbar} from "./highlightToolbar";
+import {highlightToolbarIR} from "./highlightToolbarIR";
 import {input} from "./input";
 import {processAfterRender, processHint} from "./process";
 
@@ -31,12 +30,11 @@ class IR {
 
         this.bindEvent(vditor);
 
-        document.execCommand("DefaultParagraphSeparator", false, "p");
-
         focusEvent(vditor, this.element);
         blurEvent(vditor, this.element);
         hotkeyEvent(vditor, this.element);
         selectEvent(vditor, this.element);
+        dropEvent(vditor, this.element);
     }
 
     private bindEvent(vditor: IVditor) {
@@ -63,28 +61,17 @@ class IR {
             });
         });
 
-        if (vditor.options.upload.url || vditor.options.upload.handler) {
-            this.element.addEventListener("drop",
-                (event: CustomEvent & { dataTransfer?: DataTransfer, target: HTMLElement }) => {
-                    if (event.dataTransfer.types[0] !== "Files") {
-                        return;
-                    }
-                    const files = event.dataTransfer.items;
-                    if (files.length > 0) {
-                        uploadFiles(vditor, files);
-                    }
-                    event.preventDefault();
-                });
-        }
-
-        this.element.addEventListener("compositionend", (event: InputEvent) => {
-            input(vditor, getSelection().getRangeAt(0).cloneRange());
-        });
-
         this.element.addEventListener("compositionstart", (event: InputEvent) => {
             this.composingLock = true;
         });
 
+        this.element.addEventListener("compositionend", (event: InputEvent) => {
+            if (!isFirefox()) {
+                input(vditor, getSelection().getRangeAt(0).cloneRange());
+            }
+            this.composingLock = false;
+        });
+
         this.element.addEventListener("input", (event: InputEvent) => {
             if (this.preventInput) {
                 this.preventInput = false;
@@ -125,9 +112,6 @@ class IR {
                 }
             }
 
-            expandMarker(range, vditor);
-            highlightToolbar(vditor);
-
             // 点击后光标落于预览区
             let previewElement = hasClosestByClassName(event.target, "vditor-ir__preview");
             if (!previewElement) {
@@ -145,6 +129,9 @@ class IR {
                 setSelectionFocus(range);
                 scrollCenter(vditor);
             }
+
+            expandMarker(range, vditor);
+            highlightToolbarIR(vditor);
         });
 
         this.element.addEventListener("keyup", (event) => {
@@ -154,7 +141,7 @@ class IR {
             if (event.key === "Enter") {
                 scrollCenter(vditor);
             }
-            highlightToolbar(vditor);
+            highlightToolbarIR(vditor);
             if ((event.key === "Backspace" || event.key === "Delete") &&
                 vditor.ir.element.innerHTML !== "" && vditor.ir.element.childNodes.length === 1 &&
                 vditor.ir.element.firstElementChild && vditor.ir.element.firstElementChild.tagName === "P"
@@ -183,6 +170,9 @@ class IR {
                     processHint(vditor);
                 }
                 expandMarker(range, vditor);
+            } else if (event.keyCode === 229 && event.code === "" && event.key === "Unidentified") {
+                // https://github.com/Vanessa219/vditor/issues/508 IR 删除到节点需展开
+                expandMarker(range, vditor);
             }
 
             const previewRenderElement = hasClosestByClassName(range.startContainer, "vditor-ir__preview");

+ 18 - 5
src/ts/ir/input.ts

@@ -14,12 +14,13 @@ export const input = (vditor: IVditor, range: Range, ignoreSpace = false) => {
     let blockElement = hasClosestBlock(range.startContainer);
     // 前后可以输入空格
     if (blockElement && !ignoreSpace) {
-        if (isHrMD(blockElement.innerHTML) || isHeadingMD(blockElement.innerHTML)) {
+        if (isHrMD(blockElement.innerHTML) ||
+            isHeadingMD(blockElement.innerHTML, vditor.options.preview.markdown.setext)) {
             return;
         }
 
         // 前后空格处理
-        const startOffset = getSelectPosition(blockElement, range).start;
+        const startOffset = getSelectPosition(blockElement, vditor.ir.element, range).start;
 
         // 开始可以输入空格
         let startSpace = true;
@@ -100,6 +101,7 @@ export const input = (vditor: IVditor, range: Range, ignoreSpace = false) => {
     }
 
     const isIRElement = blockElement.isEqualNode(vditor.ir.element);
+    const footnoteElement = hasClosestByAttribute(blockElement, "data-type", "footnotes-block");
     let html = "";
     if (!isIRElement) {
         // 列表需要到最顶层
@@ -115,7 +117,6 @@ export const input = (vditor: IVditor, range: Range, ignoreSpace = false) => {
         }
 
         // 修改脚注
-        const footnoteElement = hasClosestByAttribute(blockElement, "data-type", "footnotes-block");
         if (footnoteElement) {
             blockElement = footnoteElement;
         }
@@ -141,7 +142,6 @@ export const input = (vditor: IVditor, range: Range, ignoreSpace = false) => {
             html = blockElement.previousElementSibling.outerHTML + html;
             blockElement.previousElementSibling.remove();
         }
-
         // 添加链接引用
         const allLinkRefDefsElement = vditor.ir.element.querySelector("[data-type='link-ref-defs-block']");
         if (allLinkRefDefsElement && !blockElement.isEqualNode(allLinkRefDefsElement)) {
@@ -176,8 +176,21 @@ export const input = (vditor: IVditor, range: Range, ignoreSpace = false) => {
         if (allFootnoteElement) {
             vditor.ir.element.insertAdjacentElement("beforeend", allFootnoteElement);
         }
-    }
 
+        // 更新正文中的 tip
+        if (footnoteElement) {
+            const footnoteItemElement = hasClosestByAttribute(vditor.ir.element.querySelector("wbr"),
+                "data-type", "footnotes-def");
+            if (footnoteItemElement) {
+                const footnoteItemText = footnoteItemElement.textContent;
+                const marker = footnoteItemText.substring(1, footnoteItemText.indexOf("]:"));
+                const footnoteRefElement = vditor.ir.element.querySelector(`sup[data-type="footnotes-ref"][data-footnotes-label="${marker}"]`);
+                if (footnoteRefElement) {
+                    footnoteRefElement.setAttribute("aria-label", footnoteItemText.substr(marker.length + 3).trim());
+                }
+            }
+        }
+    }
     setRangeByWbr(vditor.ir.element, range);
 
     vditor.ir.element.querySelectorAll(".vditor-ir__preview[data-render='2']").forEach((item: HTMLElement) => {

+ 26 - 20
src/ts/ir/process.ts

@@ -1,10 +1,10 @@
 import {Constants} from "../constants";
 import {getMarkdown} from "../markdown/getMarkdown";
-import {isSafari} from "../util/compatibility";
+import {accessLocalStorage} from "../util/compatibility";
 import {listToggle, renderToc} from "../util/fixBrowserBehavior";
 import {hasClosestBlock, hasClosestByAttribute, hasClosestByClassName, hasClosestByMatchTag} from "../util/hasClosest";
-import {getEditorRange, getSelectPosition, setRangeByWbr} from "../util/selection";
-import {highlightToolbar} from "./highlightToolbar";
+import {getEditorRange, getSelectPosition, setRangeByWbr, setSelectionFocus} from "../util/selection";
+import {highlightToolbarIR} from "./highlightToolbarIR";
 
 export const processHint = (vditor: IVditor) => {
     vditor.hint.render(vditor);
@@ -18,8 +18,9 @@ export const processHint = (vditor: IVditor) => {
             range.selectNodeContents(preBeforeElement);
         } else {
             const matchLangData: IHintData[] = [];
-            const key = preBeforeElement.textContent.substring(0, getSelectPosition(preBeforeElement).start)
-                .replace(Constants.ZWSP, "");
+            const key =
+                preBeforeElement.textContent.substring(0, getSelectPosition(preBeforeElement, vditor.ir.element).start)
+                    .replace(Constants.ZWSP, "");
             Constants.CODE_LANGUAGES.forEach((keyName) => {
                 if (keyName.indexOf(key.toLowerCase()) > -1) {
                     matchLangData.push({
@@ -44,8 +45,7 @@ export const processAfterRender = (vditor: IVditor, options = {
 
     clearTimeout(vditor.ir.processTimeoutId);
     vditor.ir.processTimeoutId = window.setTimeout(() => {
-        if (vditor.ir.composingLock && isSafari()) {
-            // safari 中文输入遇到 addToUndoStack 会影响下一次的中文输入
+        if (vditor.ir.composingLock) {
             return;
         }
         const text = getMarkdown(vditor);
@@ -57,7 +57,7 @@ export const processAfterRender = (vditor: IVditor, options = {
             vditor.counter.render(vditor, text);
         }
 
-        if (vditor.options.cache.enable) {
+        if (vditor.options.cache.enable && accessLocalStorage()) {
             localStorage.setItem(vditor.options.cache.id, text);
             if (vditor.options.cache.after) {
                 vditor.options.cache.after(text);
@@ -69,13 +69,13 @@ export const processAfterRender = (vditor: IVditor, options = {
         }
 
         if (options.enableAddUndoStack) {
-            vditor.irUndo.addToUndoStack(vditor);
+            vditor.undo.addToUndoStack(vditor);
         }
     }, 800);
 };
 
 export const processHeading = (vditor: IVditor, value: string) => {
-    const range = getSelection().getRangeAt(0);
+    const range = getEditorRange(vditor.ir.element);
     const headingElement = hasClosestBlock(range.startContainer) || range.startContainer as HTMLElement;
     if (headingElement) {
         if (value === "") {
@@ -87,7 +87,7 @@ export const processHeading = (vditor: IVditor, value: string) => {
             range.collapse(true);
             document.execCommand("insertHTML", false, value);
         }
-        highlightToolbar(vditor);
+        highlightToolbarIR(vditor);
         renderToc(vditor);
     }
 };
@@ -148,15 +148,17 @@ export const processToolbar = (vditor: IVditor, actionBtn: Element, prefix: stri
             vditor.ir.element.innerHTML = '<p data-block="0"><wbr></p>';
             setRangeByWbr(vditor.ir.element, range);
         }
-
+        const blockElement = hasClosestBlock(range.startContainer);
         if (commandName === "line") {
-            if (typeElement.classList.contains("vditor-reset")) {
-                typeElement.innerHTML = '<hr data-block="0"><p data-block="0">\n<wbr></p>';
-            } else {
-                typeElement.insertAdjacentHTML("afterend", '<hr data-block="0"><p data-block="0">\n<wbr></p>');
+            if (blockElement) {
+                const hrHTML = '<hr data-block="0"><p data-block="0"><wbr>\n</p>';
+                if (blockElement.innerHTML.trim() === "") {
+                    blockElement.outerHTML = hrHTML;
+                } else {
+                    blockElement.insertAdjacentHTML("afterend", hrHTML);
+                }
             }
         } else if (commandName === "quote") {
-            const blockElement = hasClosestBlock(range.startContainer);
             if (blockElement) {
                 range.insertNode(document.createElement("wbr"));
                 blockElement.outerHTML = `<blockquote data-block="0">${blockElement.outerHTML}</blockquote>`;
@@ -175,17 +177,21 @@ export const processToolbar = (vditor: IVditor, actionBtn: Element, prefix: stri
             if (range.toString() === "") {
                 html = `${prefix}<wbr>${suffix}`;
             } else {
-                html = `${prefix}${range.toString()}<wbr>${prefix}`;
+                html = `${prefix}${range.toString()}<wbr>${suffix}`;
             }
             if (commandName === "table" || commandName === "code") {
-                html = "\n" + html + "\n";
+                html = "\n" + html;
             }
             document.execCommand("insertHTML", false, html);
+            if (commandName === "table") {
+                range.selectNodeContents(getSelection().getRangeAt(0).startContainer.parentElement);
+                setSelectionFocus(range);
+            }
         } else if (commandName === "check" || commandName === "list" || commandName === "ordered-list") {
             listToggle(vditor, range, commandName, false);
         }
     }
     setRangeByWbr(vditor.ir.element, range);
     processAfterRender(vditor);
-    highlightToolbar(vditor);
+    highlightToolbarIR(vditor);
 };

+ 9 - 6
src/ts/ir/processKeydown.ts

@@ -3,7 +3,7 @@ import {isCtrl} from "../util/compatibility";
 import {
     fixBlockquote, fixCJKPosition,
     fixCodeBlock, fixCursorDownInlineMath,
-    fixDelete, fixFirefoxArrowUpTable, fixHR,
+    fixDelete, fixFirefoxArrowUpTable, fixGSKeyBackspace, fixHR,
     fixList,
     fixMarkdown,
     fixTab,
@@ -28,13 +28,17 @@ export const processKeydown = (vditor: IVditor, event: KeyboardEvent) => {
 
     // 添加第一次记录 undo 的光标
     if (event.key.indexOf("Arrow") === -1) {
-        vditor.irUndo.recordFirstWbr(vditor, event);
+        vditor.undo.recordFirstPosition(vditor, event);
     }
 
     const range = getEditorRange(vditor.ir.element);
     const startContainer = range.startContainer;
 
-    fixCJKPosition(range, event);
+    if (!fixGSKeyBackspace(event, vditor, startContainer)) {
+        return false;
+    }
+
+    fixCJKPosition(range, vditor, event);
 
     fixHR(range);
 
@@ -80,7 +84,6 @@ export const processKeydown = (vditor: IVditor, event: KeyboardEvent) => {
             return true;
         }
     }
-
     // 代码块
     const preRenderElement = hasClosestByClassName(startContainer, "vditor-ir__marker--pre");
     if (preRenderElement && preRenderElement.tagName === "PRE") {
@@ -111,7 +114,7 @@ export const processKeydown = (vditor: IVditor, event: KeyboardEvent) => {
         }
 
         if (event.key === "Backspace") {
-            const start = getSelectPosition(preBeforeElement).start;
+            const start = getSelectPosition(preBeforeElement, vditor.ir.element).start;
             if (start === 1) { // 删除零宽空格
                 range.setStart(startContainer, 0);
             }
@@ -162,7 +165,7 @@ export const processKeydown = (vditor: IVditor, event: KeyboardEvent) => {
         const headingElement = hasClosestByHeadings(startContainer);
         if (headingElement) {
             const headingLength = headingElement.firstElementChild.textContent.length;
-            if (getSelectPosition(headingElement).start === headingLength) {
+            if (getSelectPosition(headingElement, vditor.ir.element).start === headingLength) {
                 range.setStart(headingElement.firstElementChild.firstChild, headingLength - 1);
                 range.collapse(true);
             }

+ 3 - 4
src/ts/markdown/abcRender.ts

@@ -1,18 +1,17 @@
-import {VDITOR_VERSION} from "../constants";
+import {Constants} from "../constants";
 import {addScript} from "../util/addScript";
 
 declare const ABCJS: {
     renderAbc(element: HTMLElement, text: string): void;
 };
 
-export const abcRender = (element: (HTMLElement | Document) = document,
-                          cdn = `https://cdn.jsdelivr.net/npm/vditor@${VDITOR_VERSION}`) => {
+export const abcRender = (element: (HTMLElement | Document) = document, cdn = Constants.CDN) => {
     const abcElements = element.querySelectorAll(".language-abc");
     if (abcElements.length > 0) {
         addScript(`${cdn}/dist/js/abcjs/abcjs_basic.min.js`, "vditorAbcjsScript").then(() => {
             abcElements.forEach((e: HTMLDivElement) => {
                 const divElement = document.createElement("div");
-                divElement.style.backgroundColor = "var(--preview-background-color)";
+                divElement.className = "language-abc";
                 e.parentNode.replaceChild(divElement, e);
                 ABCJS.renderAbc(divElement, e.textContent.trim());
                 divElement.style.overflowX = "auto";

+ 7 - 4
src/ts/markdown/chartRender.ts

@@ -1,21 +1,24 @@
-import {VDITOR_VERSION} from "../constants";
+import {Constants} from "../constants";
 import {addScript} from "../util/addScript";
 
 declare const echarts: {
     init(element: HTMLElement): IEChart;
 };
 
-export const chartRender = (element: (HTMLElement | Document) = document,
-                            cdn = `https://cdn.jsdelivr.net/npm/vditor@${VDITOR_VERSION}`) => {
+export const chartRender = (element: (HTMLElement | Document) = document, cdn = Constants.CDN) => {
     const echartsElements = element.querySelectorAll(".language-echarts");
     if (echartsElements.length > 0) {
         addScript(`${cdn}/dist/js/echarts/echarts.min.js`, "vditorEchartsScript").then(() => {
             echartsElements.forEach((e: HTMLDivElement) => {
+                const text = e.innerText.trim();
+                if (!text) {
+                    return;
+                }
                 try {
                     if (e.getAttribute("data-processed") === "true") {
                         return;
                     }
-                    const option = JSON.parse(e.innerText.trim());
+                    const option = JSON.parse(text);
                     echarts.init(e).setOption(option);
                     e.setAttribute("data-processed", "true");
                 } catch (error) {

+ 2 - 2
src/ts/markdown/codeRender.ts

@@ -2,10 +2,10 @@ import copySVG from "../../assets/icons/copy.svg";
 import {i18n} from "../i18n/index";
 import {code160to32} from "../util/code160to32";
 
-export const codeRender = (element: HTMLElement, lang: (keyof II18nLang) = "zh_CN") => {
+export const codeRender = (element: HTMLElement, lang: keyof II18n = "zh_CN") => {
     element.querySelectorAll("pre > code").forEach((e: HTMLElement, index: number) => {
         if (e.classList.contains("language-mermaid") || e.classList.contains("language-echarts") ||
-            e.classList.contains("language-mindmap")  || e.classList.contains("language-abc") ||
+            e.classList.contains("language-mindmap") || e.classList.contains("language-abc") ||
             e.classList.contains("language-graphviz")) {
             return;
         }

+ 0 - 1
src/ts/markdown/getMarkdown.ts

@@ -2,7 +2,6 @@ import {code160to32} from "../util/code160to32";
 
 export const getMarkdown = (vditor: IVditor) => {
     if (vditor.currentMode === "sv") {
-        // last char must be a `\n`.
         return code160to32(`${vditor.sv.element.textContent}\n`.replace(/\n\n$/, "\n"));
     } else if (vditor.currentMode === "wysiwyg") {
         return vditor.lute.VditorDOM2Md(vditor.wysiwyg.element.innerHTML);

+ 2 - 2
src/ts/markdown/graphvizRender.ts

@@ -1,4 +1,4 @@
-import {VDITOR_VERSION} from "../constants";
+import {Constants} from "../constants";
 import {addScript} from "../util/addScript";
 
 declare class Viz {
@@ -7,7 +7,7 @@ declare class Viz {
     constructor({}: { worker: Worker });
 }
 
-export const graphvizRender = (element: HTMLElement, cdn = `https://cdn.jsdelivr.net/npm/vditor@${VDITOR_VERSION}`) => {
+export const graphvizRender = (element: HTMLElement, cdn = Constants.CDN) => {
     const graphvizElements = element.querySelectorAll(".language-graphviz");
 
     if (graphvizElements.length === 0) {

+ 3 - 3
src/ts/markdown/highlightRender.ts

@@ -1,4 +1,4 @@
-import {Constants, VDITOR_VERSION} from "../constants";
+import {Constants} from "../constants";
 import {addScript} from "../util/addScript";
 import {addStyle} from "../util/addStyle";
 
@@ -7,7 +7,7 @@ declare const hljs: {
 };
 
 export const highlightRender = (hljsOption?: IHljs, element: HTMLElement | Document = document,
-                                cdn = `https://cdn.jsdelivr.net/npm/vditor@${VDITOR_VERSION}`) => {
+                                cdn = Constants.CDN) => {
     let style = hljsOption.style;
     if (!Constants.CODE_THEME.includes(style)) {
         style = "github";
@@ -19,7 +19,7 @@ export const highlightRender = (hljsOption?: IHljs, element: HTMLElement | Docum
     }
     addStyle(`${cdn}/dist/js/highlight.js/styles/${style}.css`, "vditorHljsStyle");
 
-    if (!hljsOption.enable) {
+    if (hljsOption.enable === false) {
         return;
     }
 

+ 2 - 2
src/ts/markdown/mathRender.ts

@@ -1,4 +1,4 @@
-import {VDITOR_VERSION} from "../constants";
+import {Constants} from "../constants";
 import {addScript, addScriptSync} from "../util/addScript";
 import {addStyle} from "../util/addStyle";
 import {code160to32} from "../util/code160to32";
@@ -24,7 +24,7 @@ export const mathRender = (element: HTMLElement, options?: { cdn?: string, math?
     }
 
     const defaultOptions = {
-        cdn: `https://cdn.jsdelivr.net/npm/vditor@${VDITOR_VERSION}`,
+        cdn: Constants.CDN,
         math: {
             engine: "KaTeX",
             inlineDigit: false,

+ 6 - 1
src/ts/markdown/mediaRender.ts

@@ -16,6 +16,7 @@ const iframeRender = (element: HTMLElement, url: string) => {
     const facebookMatch = url.match(/(?:www\.|\/\/)facebook\.com\/([^\/]+)\/videos\/([0-9]+)/);
     const dailymotionMatch = url.match(/.+dailymotion.com\/(video|hub)\/(\w+)\?/);
     const bilibiliMatch = url.match(/(?:www\.|\/\/)bilibili\.com\/video\/(\w+)/);
+    const tedMatch = url.match(/(?:www\.|\/\/)ted\.com\/talks\/(\w+)/);
 
     if (youtubeMatch && youtubeMatch[1].length === 11) {
         element.insertAdjacentHTML("afterend",
@@ -50,6 +51,10 @@ const iframeRender = (element: HTMLElement, url: string) => {
             `<iframe class="iframe__video"
  src="//player.bilibili.com/player.html?bvid=${bilibiliMatch[1]}"></iframe>`);
         element.remove();
+    } else if (tedMatch && tedMatch[1]) {
+        element.insertAdjacentHTML("afterend",
+            `<iframe class="iframe__video" src="//embed.ted.com/talks/${tedMatch[1]}"></iframe>`);
+        element.remove();
     }
 };
 
@@ -64,7 +69,7 @@ export const mediaRender = (element: HTMLElement) => {
         }
         if (url.match(/^.+.(mp4|m4v|ogg|ogv|webm)$/)) {
             videoRender(aElement, url);
-        } else if (url.match(/^.+.(mp3|wav)$/)) {
+        } else if (url.match(/^.+.(mp3|wav|flac)$/)) {
             audioRender(aElement, url);
         } else {
             iframeRender(aElement, url);

+ 2 - 2
src/ts/markdown/mermaidRender.ts

@@ -1,4 +1,4 @@
-import {VDITOR_VERSION} from "../constants";
+import {Constants} from "../constants";
 import {addScript} from "../util/addScript";
 
 declare const mermaid: {
@@ -6,7 +6,7 @@ declare const mermaid: {
 };
 
 export const mermaidRender = (element: HTMLElement, className = ".language-mermaid",
-                              cdn = `https://cdn.jsdelivr.net/npm/vditor@${VDITOR_VERSION}`) => {
+                              cdn = Constants.CDN) => {
     if (element.querySelectorAll(className).length === 0) {
         return;
     }

+ 7 - 4
src/ts/markdown/mindmapRender.ts

@@ -1,16 +1,19 @@
-import {VDITOR_VERSION} from "../constants";
+import {Constants} from "../constants";
 import {addScript} from "../util/addScript";
 
 declare const echarts: {
     init(element: HTMLElement): IEChart;
 };
 
-export const mindmapRender = (element: (HTMLElement | Document) = document,
-                              cdn = `https://cdn.jsdelivr.net/npm/vditor@${VDITOR_VERSION}`) => {
+export const mindmapRender = (element: (HTMLElement | Document) = document, cdn = Constants.CDN) => {
     const mindmapElements = element.querySelectorAll(".language-mindmap");
     if (mindmapElements.length > 0) {
         addScript(`${cdn}/dist/js/echarts/echarts.min.js`, "vditorEchartsScript").then(() => {
             mindmapElements.forEach((e: HTMLDivElement) => {
+                const text = e.getAttribute("data-code");
+                if (!text) {
+                    return;
+                }
                 try {
                     if (e.getAttribute("data-processed") === "true") {
                         return;
@@ -18,7 +21,7 @@ export const mindmapRender = (element: (HTMLElement | Document) = document,
                     const option = {
                         series: [
                             {
-                                data: [JSON.parse(decodeURIComponent(e.getAttribute("data-code")))],
+                                data: [JSON.parse(decodeURIComponent(text))],
                                 initialTreeDepth: -1,
                                 itemStyle: {
                                     borderWidth: 0,

+ 8 - 5
src/ts/markdown/outlineRender.ts

@@ -21,10 +21,13 @@ export const outlineRender = (contentElement: HTMLElement, targetElement: Elemen
     targetElement.innerHTML = tocHTML;
     targetElement.querySelectorAll(".vditor-outline__item").forEach((item) => {
         item.addEventListener("click", (event: Event & { target: HTMLElement }) => {
-            const id = item.getAttribute("data-id");
+            const idElement = document.getElementById(item.getAttribute("data-id"));
+            if (!idElement) {
+                return;
+            }
             if (vditor) {
                 if (vditor.options.height === "auto") {
-                    let windowScrollY = document.getElementById(id).offsetTop + vditor.element.offsetTop;
+                    let windowScrollY = idElement.offsetTop + vditor.element.offsetTop;
                     if (!vditor.options.toolbarConfig.pin) {
                         windowScrollY += vditor.toolbar.element.offsetHeight;
                     }
@@ -34,13 +37,13 @@ export const outlineRender = (contentElement: HTMLElement, targetElement: Elemen
                         window.scrollTo(window.scrollX, vditor.element.offsetTop);
                     }
                     if (vditor.preview.element.contains(contentElement)) {
-                        contentElement.parentElement.scrollTop = document.getElementById(id).offsetTop;
+                        contentElement.parentElement.scrollTop = idElement.offsetTop;
                     } else {
-                        contentElement.scrollTop = document.getElementById(id).offsetTop;
+                        contentElement.scrollTop = idElement.offsetTop;
                     }
                 }
             } else {
-                window.scrollTo(window.scrollX, document.getElementById(id).offsetTop);
+                window.scrollTo(window.scrollX, idElement.offsetTop);
             }
             targetElement.querySelectorAll(".vditor-outline__item").forEach((subItem) => {
                 subItem.classList.remove("vditor-outline__item--current");

+ 13 - 43
src/ts/markdown/previewRender.ts

@@ -1,6 +1,7 @@
-import {VDITOR_VERSION} from "../constants";
+import {Constants} from "../constants";
 import {setContentTheme} from "../ui/setContentTheme";
 import {addScript} from "../util/addScript";
+import {merge} from "../util/merge";
 import {abcRender} from "./abcRender";
 import {anchorRender} from "./anchorRender";
 import {chartRender} from "./chartRender";
@@ -16,53 +17,21 @@ import {setLute} from "./setLute";
 import {speechRender} from "./speechRender";
 
 const mergeOptions = (options?: IPreviewOptions) => {
-    const defaultOption = {
+    const defaultOption: IPreviewOptions = {
         anchor: 0,
-        cdn: `https://cdn.jsdelivr.net/npm/vditor@${VDITOR_VERSION}`,
+        cdn: Constants.CDN,
         customEmoji: {},
-        emojiPath: `${(options && options.emojiPath) ||
-        `https://cdn.jsdelivr.net/npm/vditor@${VDITOR_VERSION}`}/dist/images/emoji`,
-        hljs: {
-            enable: true,
-            lineNumber: false,
-            style: "github",
-        },
+        emojiPath: `${(options && options.emojiPath) || Constants.CDN}/dist/images/emoji`,
+        hljs: Constants.HLJS_OPTIONS,
         lang: "zh_CN",
-        markdown: {
-            autoSpace: false,
-            chinesePunct: false,
-            codeBlockPreview: true,
-            fixTermTypo: false,
-            footnotes: true,
-            listMarker: false,
-            paragraphBeginningSpace: false,
-            sanitize: true,
-            setext: false,
-            theme: "light",
-            toc: false,
-        },
-        math: {
-            engine: "KaTeX",
-            inlineDigit: false,
-            macros: {},
-        },
+        markdown: Constants.MARKDOWN_OPTIONS,
+        math: Constants.MATH_OPTIONS,
         speech: {
             enable: false,
         },
+        theme: Constants.THEME_OPTIONS,
     };
-    if (options?.hljs) {
-        options.hljs = Object.assign({}, defaultOption.hljs, options.hljs);
-    }
-    if (options?.speech) {
-        options.speech = Object.assign({}, defaultOption.speech, options.speech);
-    }
-    if (options?.math) {
-        options.math = Object.assign({}, defaultOption.math, options.math);
-    }
-    if (options?.markdown) {
-        options.markdown = Object.assign({}, defaultOption.markdown, options.markdown);
-    }
-    return Object.assign(defaultOption, options);
+    return merge(defaultOption, options);
 };
 
 export const md2html = (mdText: string, options?: IPreviewOptions) => {
@@ -79,7 +48,8 @@ export const md2html = (mdText: string, options?: IPreviewOptions) => {
             headingAnchor: mergedOptions.anchor !== 0,
             inlineMathDigit: mergedOptions.math.inlineDigit,
             lazyLoadImage: mergedOptions.lazyLoadImage,
-            listMarker: mergedOptions.markdown.listMarker,
+            linkBase: mergedOptions.markdown.linkBase,
+            listStyle: mergedOptions.markdown.listStyle,
             paragraphBeginningSpace: mergedOptions.markdown.paragraphBeginningSpace,
             sanitize: mergedOptions.markdown.sanitize,
             setext: mergedOptions.markdown.setext,
@@ -104,7 +74,7 @@ export const previewRender = async (previewElement: HTMLDivElement, markdown: st
     }
     previewElement.innerHTML = html;
     previewElement.classList.add("vditor-reset");
-    setContentTheme(mergedOptions.markdown.theme, mergedOptions.cdn);
+    setContentTheme(mergedOptions.theme.current, mergedOptions.theme.path);
     if (mergedOptions.anchor === 1) {
         previewElement.classList.add("vditor-reset--anchor");
     }

+ 2 - 1
src/ts/markdown/setLute.ts

@@ -13,7 +13,8 @@ export const setLute = (options: ILuteOptions) => {
     lute.SetSetext(options.setext);
     lute.SetSanitize(options.sanitize);
     lute.SetChineseParagraphBeginningSpace(options.paragraphBeginningSpace);
-    lute.SetRenderListMarker(options.listMarker);
+    lute.SetRenderListStyle(options.listStyle);
+    lute.SetLinkBase(options.linkBase);
     if (options.lazyLoadImage) {
         lute.SetImageLazyLoading(options.lazyLoadImage);
     }

+ 1 - 1
src/ts/markdown/speechRender.ts

@@ -7,7 +7,7 @@ declare global {
         vditorSpeechRange: Range;
     }
 }
-export const speechRender = (element: HTMLElement, lang: (keyof II18nLang) = "zh_CN") => {
+export const speechRender = (element: HTMLElement, lang: keyof II18n = "zh_CN") => {
     if (typeof speechSynthesis === "undefined" || typeof SpeechSynthesisUtterance === "undefined") {
         return;
     }

+ 46 - 16
src/ts/preview/index.ts

@@ -1,4 +1,5 @@
 import mpWechatSVG from "../../assets/icons/mp-wechat.svg";
+import zhihutSVG from "../../assets/icons/zhihu.svg";
 import {i18n} from "../i18n/index";
 import {abcRender} from "../markdown/abcRender";
 import {chartRender} from "../markdown/chartRender";
@@ -33,16 +34,40 @@ export class Preview {
             tempElement.className = "vditor-reset";
             tempElement.appendChild(getSelection().getRangeAt(0).cloneContents());
 
-            this.copyToWechat(vditor, tempElement);
+            this.copyToX(vditor, tempElement);
             event.preventDefault();
         });
 
+        const actions = vditor.options.preview.actions;
         const actionElement = document.createElement("div");
         actionElement.className = "vditor-preview__action";
-        actionElement.innerHTML = `<button class="vditor-preview__action--current" data-type="desktop">Desktop</button>
-<button data-type="tablet">Tablet</button>
-<button data-type="mobile">Mobile/Wechat</button>
-<button data-type="mp-wechat" class="vditor-tooltipped vditor-tooltipped__w" aria-label="复制到公众号">${mpWechatSVG}</button>`;
+        const actionHtml: string[] = [];
+        for (let i = 0; i < actions.length; i++) {
+            const action = actions[i];
+            if (typeof action === "object") {
+                actionHtml.push(`
+                    <button data-type="${action.key}" class="${action.className}">${action.text}</button>`);
+                continue;
+            }
+            switch (action) {
+                case "desktop":
+                    actionHtml.push(`<button class="vditor-preview__action--current" data-type="desktop">Desktop</button>`);
+                    break;
+                case "tablet":
+                    actionHtml.push(`<button data-type="tablet">Tablet</button>`);
+                    break;
+                case "mobile":
+                    actionHtml.push(`<button data-type="mobile">Mobile/Wechat</button>`);
+                    break;
+                case "mp-wechat":
+                    actionHtml.push(`<button data-type="mp-wechat" class="vditor-tooltipped vditor-tooltipped__w" aria-label="复制到公众号">${mpWechatSVG}</button>`);
+                    break;
+                case "zhihu":
+                    actionHtml.push(`<button data-type="zhihu" class="vditor-tooltipped vditor-tooltipped__w" aria-label="复制到知乎">${zhihutSVG}</button>`);
+                    break;
+            }
+        }
+        actionElement.innerHTML = actionHtml.join("");
         this.element.appendChild(actionElement);
         this.element.appendChild(previewElement);
 
@@ -51,14 +76,15 @@ export class Preview {
             if (!btn) {
                 return;
             }
-
             const type = btn.getAttribute("data-type");
-            if (type === actionElement.querySelector(".vditor-preview__action--current").getAttribute("data-type")) {
+            const actionCustom = actions.find((w: IPreviewActionCustom) => w?.key === type) as IPreviewActionCustom;
+            if (actionCustom) {
+                actionCustom.click(type);
                 return;
             }
 
-            if (type === "mp-wechat") {
-                this.copyToWechat(vditor, this.element.lastElementChild.cloneNode(true) as HTMLElement);
+            if (type === "mp-wechat" || type === "zhihu") {
+                this.copyToX(vditor, this.element.lastElementChild.cloneNode(true) as HTMLElement, type);
                 return;
             }
 
@@ -173,11 +199,17 @@ export class Preview {
         mediaRender(vditor.preview.element.lastElementChild as HTMLElement);
     }
 
-    private copyToWechat(vditor: IVditor, copyElement: HTMLElement) {
+    private copyToX(vditor: IVditor, copyElement: HTMLElement, type = "mp-wechat") {
         // fix math render
-        copyElement.querySelectorAll(".katex-html .base").forEach((item: HTMLElement) => {
-            item.style.display = "initial";
-        });
+        if (type !== "zhihu") {
+            copyElement.querySelectorAll(".katex-html .base").forEach((item: HTMLElement) => {
+                item.style.display = "initial";
+            });
+        } else {
+            copyElement.querySelectorAll(".vditor-math").forEach((item: HTMLElement) => {
+                item.outerHTML = `<img class="Formula-image" data-eeimg="true" src="//www.zhihu.com/equation?tex=" alt="${item.getAttribute("data-math")}\\" style="display: block; margin: 0 auto; max-width: 100%;">`;
+            });
+        }
         // 防止背景色被粘贴到公众号中
         copyElement.style.backgroundColor = "#fff";
         // 代码背景
@@ -185,13 +217,11 @@ export class Preview {
             item.style.backgroundImage = "none";
         });
         this.element.append(copyElement);
-
         const range = copyElement.ownerDocument.createRange();
         range.selectNode(copyElement);
         setSelectionFocus(range);
         document.execCommand("copy");
-
         this.element.lastElementChild.remove();
-        vditor.tip.show("已复制,可到微信公众号平台进行粘贴");
+        vditor.tip.show(`已复制,可到${type === "zhihu" ? "知乎" : "微信公众号平台"}进行粘贴`);
     }
 }

+ 0 - 49
src/ts/sv/formatRender.ts

@@ -1,49 +0,0 @@
-import {code160to32} from "../util/code160to32";
-import {setSelectionByPosition} from "../util/selection";
-import {inputEvent} from "./inputEvent";
-
-export const formatRender = (vditor: IVditor, content: string, position?: { start: number, end: number }, options = {
-    enableAddUndoStack: true,
-    enableHint: false,
-    enableInput: true,
-}) => {
-    const textList = content.replace(/\r\n/g, "\n").replace(/\r/g, "\n").split("\n");
-    let html = "";
-    const newLine = '<span><br><span style="display: none">\n</span></span>';
-
-    let isEmpty = true;
-    textList.forEach((text, index) => {
-        if (text !== "") {
-            isEmpty = false;
-        }
-
-        if (index === textList.length - 1 && text === "") {
-            // 空行行末尾不需要
-            return;
-        }
-
-        if (text) {
-            html += `<span>${code160to32(text.replace(/&/g, "&amp;").replace(/</g, "&lt;"))}</span>${newLine}`;
-        } else {
-            html += newLine;
-        }
-    });
-
-    if (textList.length <= 2 && isEmpty) {
-        // 当内容等于空或 \n 时把编辑器内部元素置空,显示 placeholder 文字
-        vditor.sv.element.innerHTML = "";
-    } else {
-        // TODO: 使用虚拟 Dom
-        vditor.sv.element.innerHTML = html || newLine;
-    }
-
-    if (position) {
-        setSelectionByPosition(position.start, position.end, vditor.sv.element);
-    }
-
-    inputEvent(vditor, {
-        enableAddUndoStack: options.enableAddUndoStack,
-        enableHint: options.enableHint,
-        enableInput: options.enableInput,
-    });
-};

+ 0 - 36
src/ts/sv/getCurrentLinePosition.ts

@@ -1,36 +0,0 @@
-export const getCurrentLinePosition = (position: { start: number, end: number }, text: string) => {
-
-    // find start
-    let start = position.start - 1;
-    let findStart = false;
-    while (!findStart && start > -1) {
-        // 防止光标在末尾
-        if (text.charAt(start) === "\n" && text.length !== start + 1) {
-            start++;
-            findStart = true;
-        } else if (start === 0) {
-            findStart = true;
-        } else {
-            start--;
-        }
-    }
-
-    // find end
-    let end = position.end;
-    let findEnd = false;
-    while (!findEnd && end <= text.length) {
-        if (text.charAt(end) === "\n") {
-            end++;
-            findEnd = true;
-        } else if (end === text.length) {
-            findEnd = true;
-        } else {
-            end++;
-        }
-    }
-
-    return {
-        end: Math.min(end, text.length),
-        start: Math.max(0, start),
-    };
-};

+ 15 - 0
src/ts/sv/highlightToolbarSV.ts

@@ -0,0 +1,15 @@
+import {setCurrentToolbar} from "../toolbar/setToolbar";
+import {hasClosestByAttribute} from "../util/hasClosest";
+import {highlightToolbarIRSV} from "../util/highlightToolbar";
+
+export const highlightToolbarSV = (vditor: IVditor) => {
+    highlightToolbarIRSV(vditor, (typeElement: HTMLElement) => {
+        if (hasClosestByAttribute(typeElement, "data-type", "ul")) {
+            setCurrentToolbar(vditor.toolbar.elements, ["list"]);
+        } else if (hasClosestByAttribute(typeElement, "data-type", "ol")) {
+            setCurrentToolbar(vditor.toolbar.elements, ["ordered-list"]);
+        } else if (hasClosestByAttribute(typeElement, "data-type", "task")) {
+            setCurrentToolbar(vditor.toolbar.elements, ["check"]);
+        }
+    });
+};

+ 0 - 56
src/ts/sv/html2md.ts

@@ -1,56 +0,0 @@
-import {setHeaders} from "../upload/setHeaders";
-import {processPasteCode} from "../util/processCode";
-import {insertText} from "./insertText";
-import {setSelectionByInlineText} from "./setSelection";
-
-// 直接使用 API 或 setOriginal 时不需要对图片进行服务器上传,直接转换。
-// 目前使用 textPlain 判断是否来自 API 或 setOriginal
-export const html2md = (vditor: IVditor, textHTML: string, textPlain?: string) => {
-    // process word
-    const doc = new DOMParser().parseFromString(textHTML, "text/html");
-    if (doc.body) {
-        textHTML = doc.body.innerHTML;
-    }
-
-    // process copy from IDE
-    const code = processPasteCode(textHTML, textPlain);
-
-    if (code) {
-        return code;
-    }
-
-    vditor.lute.SetJSRenderers({
-        renderers: {
-            HTML2Md: {
-                renderLinkDest: (node) => {
-                    const src = node.TokensStr();
-                    if (node.__internal_object__.Parent.Type === 34 && src && src.indexOf("file://") === -1
-                        && vditor.options.upload.linkToImgUrl && typeof textPlain !== "undefined") {
-                        const xhr = new XMLHttpRequest();
-                        xhr.open("POST", vditor.options.upload.linkToImgUrl);
-                        setHeaders(vditor, xhr);
-                        xhr.onreadystatechange = () => {
-                            if (xhr.readyState === XMLHttpRequest.DONE) {
-                                if (xhr.status === 200) {
-                                    const responseJSON = JSON.parse(xhr.responseText);
-                                    if (responseJSON.code !== 0) {
-                                        vditor.tip.show(responseJSON.msg);
-                                        return;
-                                    }
-                                    const original = responseJSON.data.originalURL;
-                                    setSelectionByInlineText(original, vditor.sv.element.childNodes);
-                                    insertText(vditor, responseJSON.data.url, "", true);
-                                } else {
-                                    vditor.tip.show(xhr.responseText);
-                                }
-                            }
-                        };
-                        xhr.send(JSON.stringify({url: src}));
-                    }
-                    return [node.TokensStr(), Lute.WalkStop];
-                },
-            },
-        },
-    });
-    return vditor.lute.HTML2Md(textHTML);
-};

+ 54 - 87
src/ts/sv/index.ts

@@ -1,19 +1,20 @@
-import {getMarkdown} from "../markdown/getMarkdown";
-import {uploadFiles} from "../upload/index";
-import {isCtrl} from "../util/compatibility";
-import {focusEvent, hotkeyEvent, scrollCenter, selectEvent} from "../util/editorCommonEvent";
-import {getSelectText} from "./getSelectText";
-import {html2md} from "./html2md";
+import {isCtrl, isFirefox} from "../util/compatibility";
+import {blurEvent, dropEvent, focusEvent, hotkeyEvent, selectEvent} from "../util/editorCommonEvent";
+import {paste} from "../util/fixBrowserBehavior";
+import {getSelectText} from "../util/getSelectText";
+import {highlightToolbarSV} from "./highlightToolbarSV";
 import {inputEvent} from "./inputEvent";
-import {insertText} from "./insertText";
 
 class Editor {
     public element: HTMLPreElement;
-    public range: Range;
+    public composingLock: boolean = false;
+    public processTimeoutId: number;
+    public hlToolbarTimeoutId: number;
+    public preventInput: boolean;
 
     constructor(vditor: IVditor) {
         this.element = document.createElement("pre");
-        this.element.className = "vditor-textarea";
+        this.element.className = "vditor-sv vditor-reset";
         this.element.setAttribute("placeholder", vditor.options.placeholder);
         this.element.setAttribute("contenteditable", "true");
         this.element.setAttribute("spellcheck", "false");
@@ -21,8 +22,10 @@ class Editor {
         this.bindEvent(vditor);
 
         focusEvent(vditor, this.element);
+        blurEvent(vditor, this.element);
         hotkeyEvent(vditor, this.element);
         selectEvent(vditor, this.element);
+        dropEvent(vditor, this.element);
     }
 
     private bindEvent(vditor: IVditor) {
@@ -32,32 +35,12 @@ class Editor {
             event.clipboardData.setData("text/plain", getSelectText(this.element));
         });
 
-        this.element.addEventListener("keypress", (event: KeyboardEvent) => {
-            if (!isCtrl(event) && event.key === "Enter") {
-                insertText(vditor, "\n", "", true);
-                scrollCenter(vditor);
-                event.preventDefault();
-            }
-        });
-
-        this.element.addEventListener("input", () => {
-            inputEvent(vditor, {
-                enableAddUndoStack: true,
-                enableHint: true,
-                enableInput: true,
+        this.element.addEventListener("paste", (event: ClipboardEvent & { target: HTMLElement }) => {
+            paste(vditor, event, {
+                pasteCode: (code: string) => {
+                    document.execCommand("insertHTML", false, code);
+                },
             });
-            // 选中多行后输入任意字符,br 后无 \n
-            this.element.querySelectorAll("br").forEach((br) => {
-                if (!br.nextElementSibling) {
-                    br.insertAdjacentHTML("afterend", '<span style="display: none">\n</span>');
-                }
-            });
-        });
-
-        this.element.addEventListener("blur", () => {
-            if (vditor.options.blur) {
-                vditor.options.blur(getMarkdown(vditor));
-            }
         });
 
         this.element.addEventListener("scroll", () => {
@@ -77,62 +60,46 @@ class Editor {
             }
         });
 
-        if (vditor.options.upload.url || vditor.options.upload.handler) {
-            this.element.addEventListener("drop", (event: CustomEvent & { dataTransfer?: DataTransfer }) => {
-                if (event.dataTransfer.types[0] !== "Files") {
-                    insertText(vditor, getSelection().toString(), "", false);
-                    event.preventDefault();
-                    return;
-                }
-                const files = event.dataTransfer.items;
-                if (files.length === 0) {
-                    return;
-                }
-                uploadFiles(vditor, files);
-                event.preventDefault();
-            });
-        }
+        this.element.addEventListener("compositionstart", (event: InputEvent) => {
+            this.composingLock = true;
+        });
 
-        this.element.addEventListener("paste", (event: ClipboardEvent) => {
-            const textHTML = event.clipboardData.getData("text/html");
-            const textPlain = event.clipboardData.getData("text/plain");
-            event.stopPropagation();
-            event.preventDefault();
-            if (textHTML.trim() !== "") {
-                if (textHTML.replace(/<(|\/)(html|body|meta)[^>]*?>/ig, "").trim() ===
-                    `<a href="${textPlain}">${textPlain}</a>` ||
-                    textHTML.replace(/<(|\/)(html|body|meta)[^>]*?>/ig, "").trim() ===
-                    `<!--StartFragment--><a href="${textPlain}">${textPlain}</a><!--EndFragment-->`) {
-                    // https://github.com/b3log/vditor/issues/37
-                } else {
-                    const tempElement = document.createElement("div");
-                    tempElement.innerHTML = textHTML;
-                    tempElement.querySelectorAll("[style]").forEach((e) => {
-                        e.removeAttribute("style");
-                    });
-                    tempElement.querySelectorAll(".vditor-copy").forEach((e) => {
-                        e.remove();
-                    });
-                    tempElement.querySelectorAll(".vditor-anchor").forEach((e) => {
-                        e.remove();
-                    });
-                    const mdValue = html2md(vditor, tempElement.innerHTML, textPlain).trimRight();
-                    insertText(vditor, mdValue, "", true);
-                    return;
-                }
-            } else if (textPlain.trim() !== "" && event.clipboardData.files.length === 0) {
-                // https://github.com/b3log/vditor/issues/67
-            } else if (event.clipboardData.files.length > 0) {
-                // upload file
-                if (!(vditor.options.upload.url || vditor.options.upload.handler)) {
-                    return;
-                }
-                // NOTE: not work in Safari.
-                // maybe the browser considered local filesystem as the same domain as the pasted data
-                uploadFiles(vditor, event.clipboardData.files);
+        this.element.addEventListener("compositionend", (event: InputEvent) => {
+            if (!isFirefox()) {
+                inputEvent(vditor, event);
+            }
+            this.composingLock = false;
+        });
+
+        this.element.addEventListener("input", (event: InputEvent) => {
+            if (this.composingLock) {
+                return;
+            }
+            if (this.preventInput) {
+                this.preventInput = false;
+                return;
+            }
+            inputEvent(vditor, event);
+        });
+
+        this.element.addEventListener("click", (event: InputEvent) => {
+            highlightToolbarSV(vditor);
+        });
+
+        this.element.addEventListener("keyup", (event) => {
+            if (event.isComposing || isCtrl(event)) {
+                return;
+            }
+            highlightToolbarSV(vditor);
+            if ((event.key === "Backspace" || event.key === "Delete") &&
+                vditor.sv.element.innerHTML !== "" && vditor.sv.element.childNodes.length === 1 &&
+                vditor.sv.element.firstElementChild && vditor.sv.element.firstElementChild.tagName === "DIV"
+                && vditor.sv.element.firstElementChild.childElementCount === 2
+                && (vditor.sv.element.firstElementChild.textContent === "" || vditor.sv.element.textContent === "\n")) {
+                // 为空时显示 placeholder
+                vditor.sv.element.innerHTML = "";
                 return;
             }
-            insertText(vditor, textPlain, "", true);
         });
     }
 }

+ 169 - 23
src/ts/sv/inputEvent.ts

@@ -1,32 +1,178 @@
-import {getMarkdown} from "../markdown/getMarkdown";
+import {scrollCenter} from "../util/editorCommonEvent";
+import {hasClosestByAttribute, hasTopClosestByAttribute} from "../util/hasClosest";
+import {log} from "../util/log";
+import {getSelectPosition, setRangeByWbr} from "../util/selection";
+import {processAfterRender} from "./process";
 
-export const inputEvent = (vditor: IVditor, options = {
-    enableAddUndoStack: true,
-    enableHint: false,
-    enableInput: true,
-}) => {
-    const text = getMarkdown(vditor);
-    if (vditor.options.counter.enable) {
-        vditor.counter.render(vditor, text);
+export const inputEvent = (vditor: IVditor, event?: InputEvent) => {
+    const range = getSelection().getRangeAt(0).cloneRange();
+    let blockElement = hasClosestByAttribute(range.startContainer, "data-block", "0");
+    // 不调用 lute 解析
+    if (blockElement && event && (event.inputType === "deleteContentBackward" || event.data === " ")) {
+        // 开始可以输入空格
+        const startOffset = getSelectPosition(blockElement, vditor.sv.element, range).start;
+        let startSpace = true;
+        for (let i = startOffset - 1;
+            // 软换行后有空格
+             i > blockElement.textContent.substr(0, startOffset).lastIndexOf("\n"); i--) {
+            if (blockElement.textContent.charAt(i) !== " " &&
+                // 多个 tab 前删除不形成代码块 https://github.com/Vanessa219/vditor/issues/162 1
+                blockElement.textContent.charAt(i) !== "\t") {
+                startSpace = false;
+                break;
+            }
+        }
+        if (startOffset === 0) {
+            startSpace = false;
+        }
+        if (startSpace) {
+            processAfterRender(vditor);
+            return;
+        }
+        //  list item marker 删除或空格
+        const listElement = hasClosestByAttribute(range.startContainer, "data-type", "li");
+        if (listElement) {
+            if (event.data === " " &&
+                (hasClosestByAttribute(range.startContainer, "data-type", "li-marker") ||
+                    hasClosestByAttribute(range.startContainer, "data-type", "task-marker"))) {
+                processAfterRender(vditor);
+                return;
+            }
+            const liMarkerElement = listElement.querySelector('[data-type="li-marker"]');
+            if (event.inputType === "deleteContentBackward" &&
+                getSelectPosition(listElement, vditor.sv.element, range).start <=
+                listElement.getAttribute("data-space").length +
+                (liMarkerElement ? liMarkerElement.textContent.length : 0) +
+                (listElement.querySelector('[data-type="task-marker"]') ? 4 : 0)
+            ) {
+                processAfterRender(vditor);
+                return;
+            }
+        }
+        // heading marker 删除或空格
+        const headingElement = hasClosestByAttribute(range.startContainer, "data-type", "heading-marker");
+        if (headingElement && (event.data === " " || event.inputType === "deleteContentBackward")) {
+            processAfterRender(vditor);
+            return;
+        }
+        // blockquote marker 删除或空格
+        const blockquoteElement = hasClosestByAttribute(range.startContainer, "data-type", "blockquote-marker");
+        if (blockquoteElement && (event.data === " " || event.inputType === "deleteContentBackward")) {
+            processAfterRender(vditor);
+            return;
+        }
+        // block code marker 删除
+        const blockCodeElement =
+            hasClosestByAttribute(range.startContainer, "data-type", "code-block");
+        if (blockCodeElement && event.inputType === "deleteContentBackward") {
+            const startIndex = getSelectPosition(blockElement, vditor.sv.element, range).start;
+            if (startIndex <= 2 || startIndex === blockCodeElement.textContent.length - 1) {
+                if (blockElement.querySelectorAll(".vditor-sv__marker").length === 1) {
+                    blockElement.querySelector(".vditor-sv__marker").remove();
+                    blockElement.setAttribute("data-type", "p");
+                }
+                processAfterRender(vditor);
+                return;
+            }
+        }
     }
-    if (typeof vditor.options.input === "function" && options.enableInput) {
-        vditor.options.input(text, vditor.preview.element);
+    if (blockElement && blockElement.textContent.trimRight() === "$$") {
+        // 内联数学公式
+        processAfterRender(vditor);
+        return;
     }
-    if (options.enableHint) {
-        vditor.hint.render(vditor);
+    if (!blockElement) {
+        blockElement = vditor.sv.element;
     }
-    if (vditor.options.cache.enable) {
-        localStorage.setItem(vditor.options.cache.id, text);
-        if (vditor.options.cache.after) {
-            vditor.options.cache.after(text);
-        }
+    const footnotesElement = hasClosestByAttribute(range.startContainer, "data-type", "footnotes-block");
+    if (footnotesElement) {
+        // 修改脚注
+        blockElement = footnotesElement;
+    }
+    if (blockElement.getAttribute("data-type") === "link-ref-defs-block") {
+        // 修改链接引用
+        blockElement = vditor.sv.element;
+    }
+    if (hasClosestByAttribute(range.startContainer, "data-type", "footnotes-link")) {
+        // 修改脚注角标
+        blockElement = vditor.sv.element;
+    }
+    // 列表需到顶层
+    const topListElement = hasTopClosestByAttribute(blockElement, "data-type", "ol") ||
+        hasTopClosestByAttribute(blockElement, "data-type", "ul") ||
+        hasTopClosestByAttribute(blockElement, "data-type", "task");
+    if (topListElement) {
+        blockElement = topListElement;
     }
-    vditor.preview.render(vditor);
-    if (options.enableAddUndoStack) {
-        vditor.undo.addToUndoStack(vditor);
+    // 添加光标位置
+    if (blockElement.textContent.indexOf(Lute.Caret) === -1) {
+        // 点击工具栏会插入 Caret
+        range.insertNode(document.createTextNode(Lute.Caret));
     }
+    // 清除浏览器自带的样式
+    blockElement.querySelectorAll("[style]").forEach((item) => { // 不可前置,否则会影响 newline 的样式
+        item.removeAttribute("style");
+    });
+    blockElement.querySelectorAll("font").forEach((item) => { // 不可前置,否则会影响光标的位置
+        item.outerHTML = item.innerHTML;
+    });
+    let html = blockElement.textContent;
+    if (event?.inputType === "insertParagraph" && blockElement.previousElementSibling
+        && blockElement.previousElementSibling.textContent.trim() !== "") {
+        // 在粗体中换行
+        html = blockElement.previousElementSibling.outerHTML + html;
+        blockElement.previousElementSibling.remove();
+    }
+    const isSVElement = blockElement.isEqualNode(vditor.sv.element);
+    if (isSVElement) {
+        html = blockElement.textContent;
+    } else {
+        if (blockElement.previousElementSibling) {
+            html = blockElement.previousElementSibling.textContent + html;
+            blockElement.previousElementSibling.remove();
+        }
+        if (blockElement.nextElementSibling) {
+            html = html + blockElement.nextElementSibling.textContent;
+            blockElement.nextElementSibling.remove();
+        }
+        // 添加链接引用
+        const allLinkRefDefsElement = vditor.sv.element.querySelector("[data-type='link-ref-defs-block']");
+        if (allLinkRefDefsElement && !blockElement.isEqualNode(allLinkRefDefsElement)) {
+            html += allLinkRefDefsElement.textContent;
+            allLinkRefDefsElement.remove();
+        }
+        // 添加脚注
+        const allFootnoteElement = vditor.sv.element.querySelector("[data-type='footnotes-block']");
+        if (allFootnoteElement && !blockElement.isEqualNode(allFootnoteElement)) {
+            html += allFootnoteElement.textContent;
+            allFootnoteElement.remove();
+        }
+    }
+    log("SpinVditorSVDOM", html, "argument", vditor.options.debugger);
+    html = vditor.lute.SpinVditorSVDOM(html);
+    log("SpinVditorSVDOM", html, "result", vditor.options.debugger);
+    if (isSVElement) {
+        blockElement.innerHTML = html;
+    } else {
+        blockElement.outerHTML = html;
 
-    if (vditor.devtools) {
-        vditor.devtools.renderEchart(vditor);
+        const allLinkRefDefsElement = vditor.sv.element.querySelector("[data-type='link-ref-defs-block']");
+        if (allLinkRefDefsElement) {
+            vditor.sv.element.insertAdjacentElement("beforeend", allLinkRefDefsElement);
+        }
+
+        const allFootnoteElement = vditor.sv.element.querySelector("[data-type='footnotes-block']");
+        if (allFootnoteElement) {
+            vditor.sv.element.insertAdjacentElement("beforeend", allFootnoteElement);
+        }
     }
+    setRangeByWbr(vditor.sv.element, range);
+
+    scrollCenter(vditor);
+
+    processAfterRender(vditor, {
+        enableAddUndoStack: true,
+        enableHint: true,
+        enableInput: true,
+    });
 };

+ 0 - 39
src/ts/sv/insertText.ts

@@ -1,39 +0,0 @@
-import {getMarkdown} from "../markdown/getMarkdown";
-import {getEditorRange, getSelectPosition} from "../util/selection";
-import {formatRender} from "./formatRender";
-
-export const insertText = (vditor: IVditor, prefix: string, suffix: string, replace: boolean = false,
-                           toggle: boolean = false) => {
-    const range = getEditorRange(vditor.sv.element);
-
-    const position = getSelectPosition(vditor.sv.element, range);
-    const content = getMarkdown(vditor);
-
-    // select none || select something and need replace
-    if (range.collapsed || (!range.collapsed && replace)) {
-        const text = prefix + suffix;
-        formatRender(vditor, content.substring(0, position.start) + text + content.substring(position.end),
-            {
-                end: position.start + prefix.length,
-                start: position.start + prefix.length,
-            });
-    } else {
-        const selectText = content.substring(position.start, position.end);
-        if (toggle && content.substring(position.start - prefix.length, position.start) === prefix
-            && content.substring(position.end, position.end + suffix.length) === suffix) {
-            formatRender(vditor, content.substring(0, position.start - prefix.length)
-                + selectText + content.substring(position.end + suffix.length),
-                {
-                    end: position.start - prefix.length + selectText.length,
-                    start: position.start - prefix.length,
-                });
-        } else {
-            const text = prefix + selectText + suffix;
-            formatRender(vditor, content.substring(0, position.start) + text + content.substring(position.end),
-                {
-                    end: position.start + prefix.length + selectText.length,
-                    start: position.start + prefix.length,
-                });
-        }
-    }
-};

+ 209 - 0
src/ts/sv/process.ts

@@ -0,0 +1,209 @@
+import {getMarkdown} from "../markdown/getMarkdown";
+import {accessLocalStorage} from "../util/compatibility";
+import {hasClosestBlock, hasClosestByAttribute} from "../util/hasClosest";
+import {getEditorRange, setRangeByWbr} from "../util/selection";
+import {highlightToolbarSV} from "./highlightToolbarSV";
+import {inputEvent} from "./inputEvent";
+
+export const processAfterRender = (vditor: IVditor, options = {
+    enableAddUndoStack: true,
+    enableHint: false,
+    enableInput: true,
+}) => {
+    if (options.enableHint) {
+        vditor.hint.render(vditor);
+    }
+
+    clearTimeout(vditor.sv.processTimeoutId);
+    vditor.sv.processTimeoutId = window.setTimeout(() => {
+        if (vditor.sv.composingLock) {
+            return;
+        }
+
+        const text = getMarkdown(vditor);
+        if (typeof vditor.options.input === "function" && options.enableInput) {
+            vditor.options.input(text);
+        }
+
+        if (vditor.options.counter.enable) {
+            vditor.counter.render(vditor, text);
+        }
+
+        if (vditor.options.cache.enable && accessLocalStorage()) {
+            localStorage.setItem(vditor.options.cache.id, text);
+            if (vditor.options.cache.after) {
+                vditor.options.cache.after(text);
+            }
+        }
+
+        if (vditor.devtools) {
+            vditor.devtools.renderEchart(vditor);
+        }
+        vditor.preview.render(vditor);
+        if (options.enableAddUndoStack) {
+            vditor.undo.addToUndoStack(vditor);
+        }
+    }, 800);
+};
+
+export const processHeading = (vditor: IVditor, value: string) => {
+    const range = getEditorRange(vditor.sv.element);
+    const headingElement = hasClosestByAttribute(range.startContainer, "data-type", "heading") ||
+        range.startContainer as HTMLElement;
+    if (headingElement) {
+        const headingMarkerElement = headingElement.querySelector(".vditor-sv__marker--heading");
+        if (headingMarkerElement) {
+            range.selectNodeContents(headingMarkerElement);
+        }
+        if (value === "") {
+            document.execCommand("delete");
+        } else {
+            document.execCommand("insertHTML", false, value);
+        }
+        highlightToolbarSV(vditor);
+    }
+};
+
+const removeInline = (range: Range, vditor: IVditor, type: string) => {
+    const inlineElement = hasClosestByAttribute(range.startContainer, "data-type", type) as HTMLElement;
+    if (inlineElement) {
+        inlineElement.firstElementChild.remove();
+        inlineElement.lastElementChild.remove();
+        range.insertNode(document.createElement("wbr"));
+        inlineElement.outerHTML = inlineElement.firstElementChild.innerHTML;
+    }
+};
+
+export const processToolbar = (vditor: IVditor, actionBtn: Element, prefix: string, suffix: string) => {
+    const range = getEditorRange(vditor.sv.element);
+    const commandName = actionBtn.getAttribute("data-type");
+    let typeElement = range.startContainer as HTMLElement;
+    if (typeElement.nodeType === 3) {
+        typeElement = typeElement.parentElement;
+    }
+    // 移除
+    if (actionBtn.classList.contains("vditor-menu--current")) {
+        if (commandName === "quote") {
+            const quoteElement = hasClosestByAttribute(range.startContainer, "data-type", "blockquote");
+            if (quoteElement) {
+                quoteElement.querySelectorAll('[data-type="blockquote-line"]').forEach((item: HTMLElement) => {
+                    item.firstElementChild.remove();
+                });
+                inputEvent(vditor);
+                highlightToolbarSV(vditor);
+                return;
+            }
+        } else if (commandName === "link") {
+            const aElement = hasClosestByAttribute(range.startContainer, "data-type", "a") as HTMLElement;
+            if (aElement) {
+                const aTextElement = hasClosestByAttribute(range.startContainer, "data-type", "link-text");
+                if (aTextElement) {
+                    range.insertNode(document.createElement("wbr"));
+                    aElement.outerHTML = aTextElement.innerHTML;
+                } else {
+                    aElement.outerHTML = aElement.querySelector('[data-type="link-text"]').innerHTML + "<wbr>";
+                }
+            }
+        } else if (commandName === "italic") {
+            removeInline(range, vditor, "em");
+        } else if (commandName === "bold") {
+            removeInline(range, vditor, "strong");
+        } else if (commandName === "strike") {
+            removeInline(range, vditor, "s");
+        } else if (commandName === "inline-code") {
+            removeInline(range, vditor, "code");
+        } else if (commandName === "check" || commandName === "list" || commandName === "ordered-list") {
+            const listElement = hasClosestBlock(range.startContainer);
+            if (listElement) {
+                listElement.querySelectorAll('[data-type="li-marker"').forEach((item: HTMLElement) => {
+                    item.remove();
+                });
+                listElement.querySelectorAll('[data-type="task-marker"').forEach((item: HTMLElement) => {
+                    item.remove();
+                });
+                inputEvent(vditor);
+                highlightToolbarSV(vditor);
+                return;
+            }
+        }
+    } else {
+        // 添加
+        if (vditor.sv.element.childNodes.length === 0) {
+            vditor.sv.element.innerHTML = `<div data-type="p" data-block="0"><span data-type="text"><wbr></span><span data-type="newline"><br><span style="display: none">
+</span></span></div>`;
+            setRangeByWbr(vditor.sv.element, range);
+        }
+        const blockElement = hasClosestBlock(range.startContainer);
+        if (commandName === "line") {
+            if (blockElement) {
+                const hrHTML = `<div data-type="thematic-break" class="vditor-sv__marker"><span class="vditor-sv__marker">---
+
+</span></div><wbr>`;
+                if (blockElement.textContent.trim() === "") {
+                    blockElement.outerHTML = hrHTML;
+                } else {
+                    blockElement.insertAdjacentHTML("afterend", hrHTML);
+                }
+            }
+        } else if (commandName === "quote") {
+            if (blockElement) {
+                blockElement.insertAdjacentText("afterbegin", "> ");
+                inputEvent(vditor);
+                highlightToolbarSV(vditor);
+                return;
+            }
+        } else if (commandName === "link") {
+            let html;
+            if (range.toString() === "") {
+                html = `${prefix}${Lute.Caret}${suffix}`;
+            } else {
+                html = `${prefix}${range.toString()}${suffix.replace(")", Lute.Caret + ")")}`;
+            }
+            document.execCommand("insertHTML", false, html);
+            highlightToolbarSV(vditor);
+            return;
+        } else if (commandName === "italic" || commandName === "bold" || commandName === "strike"
+            || commandName === "inline-code" || commandName === "code" || commandName === "table") {
+            let html;
+            if (range.toString() === "") {
+                html = `${prefix}${Lute.Caret}${suffix}`;
+            } else {
+                html = `${prefix}${range.toString()}${Lute.Caret}${suffix}`;
+            }
+            if (commandName === "table" || commandName === "code") {
+                html = "\n" + html;
+            }
+            document.execCommand("insertHTML", false, html);
+            highlightToolbarSV(vditor);
+            return;
+        } else if (commandName === "check" || commandName === "list" || commandName === "ordered-list") {
+            if (blockElement) {
+                const type = blockElement.getAttribute("data-type");
+                let listMarker = "* ";
+                if (commandName === "check") {
+                    listMarker = "* [ ] ";
+                } else if (commandName === "ordered-list") {
+                    listMarker = "1. ";
+                }
+                if (type !== "ul" && type !== "ol" && type !== "task") {
+                    blockElement.insertAdjacentText("afterbegin", listMarker);
+                } else {
+                    blockElement.querySelectorAll('[data-type="li-marker"').forEach((item: HTMLElement) => {
+                        item.textContent = listMarker;
+                    });
+                    if (commandName !== "check") {
+                        blockElement.querySelectorAll('[data-type="task-marker"').forEach((item: HTMLElement) => {
+                            item.remove();
+                        });
+                    }
+                }
+                inputEvent(vditor);
+                highlightToolbarSV(vditor);
+                return;
+            }
+        }
+    }
+    setRangeByWbr(vditor.sv.element, range);
+    processAfterRender(vditor);
+    highlightToolbarSV(vditor);
+};

+ 177 - 100
src/ts/sv/processKeydown.ts

@@ -1,125 +1,202 @@
-import {getMarkdown} from "../markdown/getMarkdown";
 import {isCtrl} from "../util/compatibility";
+import {fixTab} from "../util/fixBrowserBehavior";
+import {hasClosestByAttribute} from "../util/hasClosest";
 import {matchHotKey} from "../util/hotKey";
-import {getSelectPosition} from "../util/selection";
-import {formatRender} from "./formatRender";
-import {getCurrentLinePosition} from "./getCurrentLinePosition";
-import {insertText} from "./insertText";
+import {getEditorRange, getSelectPosition} from "../util/selection";
+import {inputEvent} from "./inputEvent";
+import {processAfterRender} from "./process";
 
 export const processKeydown = (vditor: IVditor, event: KeyboardEvent) => {
-    vditor.undo.recordFirstPosition(vditor);
-
-    const editorElement = vditor.sv.element;
-    const position = getSelectPosition(editorElement);
-    const text = getMarkdown(vditor);
-    // tab and shift + tab
-    if (vditor.options.tab && event.key === "Tab") {
-        event.preventDefault();
-        event.stopPropagation();
+    vditor.sv.composingLock = event.isComposing;
+    if (event.isComposing) {
+        return false;
+    }
+    if (event.key.indexOf("Arrow") === -1) {
+        vditor.undo.recordFirstPosition(vditor, event);
+    }
+    // 仅处理以下快捷键操作
+    if (event.key !== "Enter" && event.key !== "Tab" && event.key !== "Backspace" && event.key.indexOf("Arrow") === -1
+        && !isCtrl(event) && event.key !== "Escape") {
+        return false;
+    }
+    const range = getEditorRange(vditor.sv.element);
+    const startContainer = range.startContainer;
 
-        const selectLinePosition = getCurrentLinePosition(position, text);
-        const selectLineList = text.substring(selectLinePosition.start, selectLinePosition.end - 1).split("\n");
+    // blockquote
+    const blockquoteLineElement = hasClosestByAttribute(startContainer, "data-type", "blockquote-line");
+    if (blockquoteLineElement) {
+        const startIndex = getSelectPosition(blockquoteLineElement, vditor.sv.element, range).start;
+        if (event.key === "Enter" && !isCtrl(event) && !event.altKey && startIndex !== 0) {
+            let markerLength = 0;
+            blockquoteLineElement.querySelectorAll('[data-type="blockquote-marker"').forEach((item: HTMLElement) => {
+                markerLength += item.textContent.length;
+            });
+            if (startIndex === markerLength && markerLength > 1) {
+                // 在 marker 中换行,删除 marker 标记
+                blockquoteLineElement.firstElementChild.remove();
+                event.preventDefault();
+                processAfterRender(vditor);
+                return true;
+            } else if (blockquoteLineElement.textContent.trim() !== "") {
+                // 换行应延续 >
+                range.insertNode(document.createTextNode("\n>"));
+                range.collapse(false);
+                inputEvent(vditor);
+                event.preventDefault();
+                return true;
+            }
+        }
+        // 光标在每一行的第一个字符后删除
+        if (event.key === "Backspace" && !isCtrl(event) && !event.altKey && !event.shiftKey && startIndex === 1) {
+            range.setStart(startContainer, 0);
+            range.extractContents();
+            if (blockquoteLineElement.firstElementChild.textContent === "") {
+                blockquoteLineElement.firstElementChild.remove();
+            }
+            processAfterRender(vditor);
+            event.preventDefault();
+            return true;
+        }
+    }
 
-        if (event.shiftKey) {
-            let shiftCount = 0;
-            let startIsShift = false;
-            const selectionShiftResult = selectLineList.map((value, index) => {
-                let shiftLineValue = value;
-                if (value.indexOf(vditor.options.tab) === 0) {
-                    if (index === 0) {
-                        startIsShift = true;
-                    }
-                    shiftCount++;
-                    shiftLineValue = value.replace(vditor.options.tab, "");
+    // list item
+    const listElement = hasClosestByAttribute(startContainer, "data-type", "li");
+    if (listElement) {
+        const markerElement = listElement.querySelector('[data-type="li-marker"]');
+        const startIndex = getSelectPosition(listElement, vditor.sv.element, range).start;
+        const space = listElement.getAttribute("data-space");
+        const taskMarkerElements = listElement.querySelectorAll('[data-type="task-marker"]');
+        // 回车
+        if (event.key === "Enter" && !isCtrl(event) && !event.altKey && !event.shiftKey && startIndex !== 0) {
+            // enter
+            if (markerElement && startIndex ===
+                markerElement.textContent.length + space.length + (taskMarkerElements.length > 0 ? 4 : 0)) {
+                let addUndoStack = true;
+                // 清空列表标记符
+                if (space === "") {
+                    markerElement.remove();
+                } else {
+                    markerElement.previousElementSibling.remove();
+                    inputEvent(vditor);
+                    addUndoStack = false;
                 }
-                return shiftLineValue;
-            }).join("\n");
-
-            formatRender(vditor, text.substring(0, selectLinePosition.start) +
-                selectionShiftResult + text.substring(selectLinePosition.end - 1),
-                {
-                    end: position.end - shiftCount * vditor.options.tab.length,
-                    start: position.start - (startIsShift ? vditor.options.tab.length : 0),
-                });
+                if (taskMarkerElements.length > 0) {
+                    listElement.querySelectorAll('[data-type="task-marker"]').forEach((item: HTMLElement) => {
+                        item.remove();
+                    });
+                }
+                if (addUndoStack) {
+                    processAfterRender(vditor);
+                }
+            } else {
+                // 添加标记符号
+                let newMarker = "\n";
+                if (markerElement) {
+                    newMarker += space + markerElement.textContent;
+                }
+                if (taskMarkerElements.length > 0) {
+                    newMarker += "[ ] ";
+                }
+                range.insertNode(document.createTextNode(newMarker));
+                range.collapse(false);
+                inputEvent(vditor);
+            }
+            event.preventDefault();
             return true;
         }
-
-        if (position.start === position.end) {
-            insertText(vditor, vditor.options.tab, "");
+        // 光标在每一行的第一个字符后删除
+        if (event.key === "Backspace" && !isCtrl(event) && !event.altKey && !event.shiftKey) {
+            const firstElement = hasClosestByAttribute(startContainer, "data-type", "li-marker") ||
+                hasClosestByAttribute(startContainer, "data-type", "li-space");
+            if (firstElement && startIndex === 1) {
+                range.setStart(startContainer, 0);
+                range.extractContents();
+                if (firstElement.textContent === "") {
+                    firstElement.remove();
+                }
+                processAfterRender(vditor);
+                event.preventDefault();
+                return true;
+            }
+        }
+        // 第一个 marker 后 tab 进行缩进
+        if (event.key === "Tab" && markerElement && startIndex === markerElement.textContent.length + space.length
+            + (taskMarkerElements.length > 0 ? taskMarkerElements[1].textContent.length + 3 : 0)) {
+            if (/^\d/.test(markerElement.textContent)) {
+                markerElement.textContent = "1. ";
+                range.selectNodeContents(markerElement.firstChild);
+                range.collapse(false);
+            }
+            markerElement.insertAdjacentHTML("beforebegin",
+                `<span data-type="li-space">${markerElement.textContent.replace(/\S/g, " ")}</span>`);
+            inputEvent(vditor);
+            event.preventDefault();
             return true;
         }
-        const selectionResult = selectLineList.map((value) => {
-            return vditor.options.tab + value;
-        }).join("\n");
-        formatRender(vditor, text.substring(0, selectLinePosition.start) + selectionResult +
-            text.substring(selectLinePosition.end - 1),
-            {
-                end: position.end + selectLineList.length * vditor.options.tab.length,
-                start: position.start + vditor.options.tab.length,
-            });
-        return true;
-    }
-
-    // delete
-    if (!isCtrl(event) && !event.shiftKey && event.keyCode === 8) {
-        if (position.start !== position.end) {
-            insertText(vditor, "", "", true);
-        } else {
-            // delete emoji
-            const emojiMatch = text.substring(0, position.start).match(/([\u{1F300}-\u{1F5FF}][\u{2000}-\u{206F}][\u{2700}-\u{27BF}]|([\u{1F900}-\u{1F9FF}]|[\u{1F300}-\u{1F5FF}]|[\u{1F680}-\u{1F6FF}]|[\u{1F600}-\u{1F64F}])[\u{2000}-\u{206F}][\u{2600}-\u{26FF}]|[\u{1F300}-\u{1F5FF}]|[\u{1F100}-\u{1F1FF}]|[\u{1F600}-\u{1F64F}]|[\u{1F680}-\u{1F6FF}]|[\u{1F200}-\u{1F2FF}]|[\u{1F900}-\u{1F9FF}]|[\u{1F000}-\u{1F02F}]|[\u{FE00}-\u{FE0F}]|[\u{1F0A0}-\u{1F0FF}]|[\u{0000}-\u{007F}][\u{20D0}-\u{20FF}]|[\u{0000}-\u{007F}][\u{FE00}-\u{FE0F}][\u{20D0}-\u{20FF}])$/u);
-            const deleteChar = emojiMatch ? emojiMatch[0].length : 1;
-            formatRender(vditor,
-                text.substring(0, position.start - deleteChar) + text.substring(position.start),
-                {
-                    end: position.start - deleteChar,
-                    start: position.start - deleteChar,
-                }, {
-                    enableAddUndoStack: true,
-                    enableHint: true,
-                    enableInput: true,
-                });
+        // toggle checked
+        if (taskMarkerElements.length > 0 && matchHotKey("⌘-⇧-J", event)) {
+            if (taskMarkerElements[1].textContent === " ") {
+                taskMarkerElements[1].textContent = "x";
+            } else {
+                taskMarkerElements[1].textContent = " ";
+            }
+            processAfterRender(vditor);
+            event.preventDefault();
+            return true;
         }
-        event.preventDefault();
-        event.stopPropagation();
-        return true;
     }
 
-    // hotkey command + delete
-    if (vditor.options.keymap.deleteLine && matchHotKey(vditor.options.keymap.deleteLine, event)) {
-        const linePosition = getCurrentLinePosition(position, text);
-        const deletedText = text.substring(0, linePosition.start) + text.substring(linePosition.end);
-        const startIndex = Math.min(deletedText.length, position.start);
-        formatRender(vditor, deletedText, {
-            end: startIndex,
-            start: startIndex,
-        });
-        event.preventDefault();
+    // tab
+    if (fixTab(vditor, range, event)) {
         return true;
     }
+    const blockElement = hasClosestByAttribute(startContainer, "data-block", "0");
 
-    // hotkey command + d
-    if (vditor.options.keymap.duplicate && matchHotKey(vditor.options.keymap.duplicate, event)) {
-        let lineText = text.substring(position.start, position.end);
-        if (position.start === position.end) {
-            const linePosition = getCurrentLinePosition(position, text);
-            lineText = text.substring(linePosition.start, linePosition.end);
-            formatRender(vditor,
-                text.substring(0, linePosition.end) + lineText + text.substring(linePosition.end),
-                {
-                    end: position.end + lineText.length,
-                    start: position.start + lineText.length,
-                });
+    // 回车,除 list item,blockquote 的 marker 延续和清除外
+    if (event.key === "Enter" && !isCtrl(event) && !event.altKey) {
+        let isFirst = false;
+        if (blockElement && getSelectPosition(blockElement, vditor.sv.element).start === 0) {
+            // 允许段落开始换行
+            isFirst = true;
+        }
+        // 添加 \n
+        range.insertNode(document.createTextNode("\n"));
+        range.collapse(false);
+        if ((!blockElement || blockElement?.textContent.trim() !== "") && !isFirst) {
+            inputEvent(vditor);
         } else {
-            formatRender(vditor,
-                text.substring(0, position.end) + lineText + text.substring(position.end),
-                {
-                    end: position.end + lineText.length,
-                    start: position.start + lineText.length,
-                });
+            processAfterRender(vditor);
         }
         event.preventDefault();
         return true;
     }
 
+    // 删除后光标前有 newline 的处理
+    if (blockElement && event.key === "Backspace" && !isCtrl(event) && !event.altKey && !event.shiftKey) {
+        const startIndex = getSelectPosition(blockElement, vditor.sv.element, range).start;
+        // 光标在每一行的开始位置
+        if (startIndex === 0 && blockElement.previousElementSibling &&
+            blockElement.previousElementSibling.lastElementChild.getAttribute("data-type") === "newline") {
+            blockElement.previousElementSibling.lastElementChild.remove();
+            range.extractContents();
+            if (blockElement.textContent.trim() !== "") {
+                inputEvent(vditor);
+            } else {
+                processAfterRender(vditor);
+            }
+            event.preventDefault();
+            return true;
+        }
+        // 光标在每一行的第一个字符后, list item、blockquote line 处理在上方
+        const textElement = hasClosestByAttribute(startContainer, "data-type", "text");
+        if (textElement && range.startOffset === 1 && textElement.previousElementSibling &&
+            textElement.previousElementSibling.getAttribute("data-type") === "newline") {
+            range.setStart(startContainer, 0);
+            range.extractContents();
+            processAfterRender(vditor);
+            event.preventDefault();
+            return true;
+        }
+    }
     return false;
 };

+ 0 - 20
src/ts/sv/setSelection.ts

@@ -1,20 +0,0 @@
-import {setSelectionFocus} from "../util/selection";
-
-export const setSelectionByInlineText = (text: string, childNodes: NodeListOf<ChildNode>) => {
-    let offset = 0;
-    let startIndex = 0;
-    Array.from(childNodes).some((node: HTMLElement, index: number) => {
-        startIndex = node.textContent.indexOf(text);
-        if (startIndex > -1 && childNodes[index].childNodes[0].nodeType === 3) {
-            offset = index;
-            return true;
-        }
-    });
-    if (startIndex < 0) {
-        return;
-    }
-    const range = document.createRange();
-    range.setStart(childNodes[offset].childNodes[0], startIndex);
-    range.setEnd(childNodes[offset].childNodes[0], startIndex + text.length);
-    setSelectionFocus(range);
-};

+ 4 - 5
src/ts/toolbar/ContentTheme.ts

@@ -1,4 +1,3 @@
-import {Constants} from "../constants";
 import {setContentTheme} from "../ui/setContentTheme";
 import {getEventName} from "../util/compatibility";
 import {MenuItem} from "./MenuItem";
@@ -15,16 +14,16 @@ export class ContentTheme extends MenuItem {
         const panelElement = document.createElement("div");
         panelElement.className = `vditor-hint${menuItem.level === 2 ? "" : " vditor-panel--arrow"}`;
         let innerHTML = "";
-        Constants.CONTENT_THEME.forEach((theme) => {
-            innerHTML += `<button>${theme}</button>`;
+        Object.keys(vditor.options.preview.theme.list).forEach((key) => {
+            innerHTML += `<button data-type="${key}">${vditor.options.preview.theme.list[key]}</button>`;
         });
         panelElement.innerHTML =
             `<div style="overflow: auto;max-height:${window.innerHeight / 2}px">${innerHTML}</div>`;
         panelElement.addEventListener(getEventName(), (event: MouseEvent & { target: HTMLElement }) => {
             if (event.target.tagName === "BUTTON") {
                 hidePanel(vditor, ["subToolbar"]);
-                vditor.options.preview.markdown.theme = event.target.textContent;
-                setContentTheme(event.target.textContent, vditor.options.cdn);
+                vditor.options.preview.theme.current = event.target.getAttribute("data-type");
+                setContentTheme(vditor.options.preview.theme.current, vditor.options.preview.theme.path);
                 event.preventDefault();
                 event.stopPropagation();
             }

+ 18 - 27
src/ts/toolbar/EditMode.ts

@@ -1,13 +1,12 @@
 import {Constants} from "../constants";
 import {i18n} from "../i18n";
-import {highlightToolbar as IRHighlightToolbar} from "../ir/highlightToolbar";
 import {processAfterRender} from "../ir/process";
 import {getMarkdown} from "../markdown/getMarkdown";
-import {formatRender} from "../sv/formatRender";
+import {processAfterRender as processSVAfterRender} from "../sv/process";
 import {setPadding, setTypewriterPosition} from "../ui/initUI";
 import {getEventName, updateHotkeyTip} from "../util/compatibility";
+import {highlightToolbar} from "../util/highlightToolbar";
 import {processCodeRender} from "../util/processCode";
-import {highlightToolbar} from "../wysiwyg/highlightToolbar";
 import {renderDomByMd} from "../wysiwyg/renderDomByMd";
 import {MenuItem} from "./MenuItem";
 import {
@@ -45,9 +44,8 @@ export const setEditMode = (vditor: IVditor, type: string, event: Event | string
     disableToolbar(vditor.toolbar.elements, ["outdent", "indent"]);
 
     if (type === "ir") {
-        hideToolbar(vditor.toolbar.elements, ["format", "both"]);
+        hideToolbar(vditor.toolbar.elements, ["both"]);
         showToolbar(vditor.toolbar.elements, ["outdent", "indent", "outline", "insert-before", "insert-after"]);
-        vditor.irUndo.resetIcon(vditor);
         vditor.sv.element.style.display = "none";
         vditor.wysiwyg.element.parentElement.style.display = "none";
         vditor.ir.element.parentElement.style.display = "block";
@@ -65,16 +63,9 @@ export const setEditMode = (vditor: IVditor, type: string, event: Event | string
         vditor.ir.element.querySelectorAll(".vditor-ir__preview[data-render='2']").forEach((item: HTMLElement) => {
             processCodeRender(item, vditor);
         });
-
-        if (typeof event !== "string") {
-            // 初始化不 focus
-            vditor.ir.element.focus();
-            IRHighlightToolbar(vditor);
-        }
     } else if (type === "wysiwyg") {
-        hideToolbar(vditor.toolbar.elements, ["format", "both"]);
+        hideToolbar(vditor.toolbar.elements, ["both"]);
         showToolbar(vditor.toolbar.elements, ["outdent", "indent", "outline", "insert-before", "insert-after"]);
-        vditor.wysiwygUndo.resetIcon(vditor);
         vditor.sv.element.style.display = "none";
         vditor.wysiwyg.element.parentElement.style.display = "block";
         vditor.ir.element.parentElement.style.display = "none";
@@ -82,18 +73,15 @@ export const setEditMode = (vditor: IVditor, type: string, event: Event | string
         vditor.currentMode = "wysiwyg";
 
         setPadding(vditor);
-        renderDomByMd(vditor, markdownText, false);
-
-        if (typeof event !== "string") {
-            // 初始化不 focus
-            vditor.wysiwyg.element.focus();
-            highlightToolbar(vditor);
-        }
+        renderDomByMd(vditor, markdownText, {
+            enableAddUndoStack: true,
+            enableHint: false,
+            enableInput: false,
+        });
         vditor.wysiwyg.popover.style.display = "none";
     } else if (type === "sv") {
-        showToolbar(vditor.toolbar.elements, ["format", "both"]);
+        showToolbar(vditor.toolbar.elements, ["both"]);
         hideToolbar(vditor.toolbar.elements, ["outdent", "indent", "outline", "insert-before", "insert-after"]);
-        vditor.undo.resetIcon(vditor);
         vditor.wysiwyg.element.parentElement.style.display = "none";
         vditor.ir.element.parentElement.style.display = "none";
         if (vditor.options.preview.mode === "both") {
@@ -102,17 +90,20 @@ export const setEditMode = (vditor: IVditor, type: string, event: Event | string
             vditor.sv.element.style.display = "block";
         }
         vditor.currentMode = "sv";
-        formatRender(vditor, markdownText, undefined, {
+        vditor.sv.element.innerHTML = vditor.lute.SpinVditorSVDOM(markdownText);
+        processSVAfterRender(vditor, {
             enableAddUndoStack: true,
             enableHint: false,
             enableInput: false,
         });
-        if (typeof event !== "string") {
-            // 初始化不 focus
-            vditor.sv.element.focus();
-        }
         setPadding(vditor);
     }
+    vditor.undo.resetIcon(vditor);
+    if (typeof event !== "string") {
+        // 初始化不 focus
+        vditor[vditor.currentMode].element.focus();
+        highlightToolbar(vditor);
+    }
     if (typeof event === "string") {
         vditor.outline.render(vditor);
     }

+ 17 - 23
src/ts/toolbar/Emoji.ts

@@ -1,5 +1,5 @@
-import {insertText} from "../sv/insertText";
 import {getEventName} from "../util/compatibility";
+import {execAfterRender} from "../util/fixBrowserBehavior";
 import {getEditorRange, insertHTML, setSelectionFocus} from "../util/selection";
 import {MenuItem} from "./MenuItem";
 import {toggleSubMenu} from "./setToolbar";
@@ -43,31 +43,25 @@ data-value=":${key}: " data-key=":${key}:" class="vditor-emojis__icon" src="${em
             element.addEventListener(getEventName(), (event: Event) => {
                 event.preventDefault();
                 const value = element.getAttribute("data-value");
-                if (vditor.currentMode === "sv") {
-                    insertText(vditor, value, "", true);
+                const range = getEditorRange(vditor[vditor.currentMode].element);
+                let html = value;
+                if (vditor.currentMode === "wysiwyg") {
+                    html = vditor.lute.SpinVditorDOM(value);
+                } else if (vditor.currentMode === "ir") {
+                    html = vditor.lute.SpinVditorIRDOM(value);
+                }
+                if (value.indexOf(":") > -1 && vditor.currentMode !== "sv") {
+                    const tempElement = document.createElement("div");
+                    tempElement.innerHTML = html;
+                    html = tempElement.firstElementChild.firstElementChild.outerHTML + " ";
+                    insertHTML(html, vditor);
                 } else {
-                    let range;
-                    let html = "";
-                    if (vditor.currentMode === "wysiwyg") {
-                        range = getEditorRange(vditor.wysiwyg.element);
-                        html = vditor.lute.SpinVditorDOM(value);
-                    } else if (vditor.currentMode === "ir") {
-                        range = getEditorRange(vditor.ir.element);
-                        html = vditor.lute.SpinVditorIRDOM(value);
-                    }
-                    if (value.indexOf(":") > -1) {
-                        const tempElement = document.createElement("div");
-                        tempElement.innerHTML = html;
-                        html = tempElement.firstElementChild.firstElementChild.outerHTML + " ";
-                        insertHTML(html, vditor);
-                    } else {
-                        range.insertNode(document.createTextNode(value));
-                    }
-                    range.collapse(false);
-                    setSelectionFocus(range);
+                    range.insertNode(document.createTextNode(value));
                 }
-
+                range.collapse(false);
+                setSelectionFocus(range);
                 panelElement.style.display = "none";
+                execAfterRender(vditor);
             });
             element.addEventListener("mouseover", (event: Event) => {
                 if ((event.target as HTMLElement).tagName === "BUTTON") {

+ 0 - 16
src/ts/toolbar/Format.ts

@@ -1,16 +0,0 @@
-import {getMarkdown} from "../markdown/getMarkdown";
-import {formatRender} from "../sv/formatRender";
-import {getEventName} from "../util/compatibility";
-import {getSelectPosition} from "../util/selection";
-import {MenuItem} from "./MenuItem";
-
-export class Format extends MenuItem {
-    constructor(vditor: IVditor, menuItem: IMenuItem) {
-        super(vditor, menuItem);
-        this.element.children[0].addEventListener(getEventName(), (event) => {
-            event.preventDefault();
-            formatRender(vditor,  vditor.lute.FormatMd( getMarkdown(vditor)),
-                getSelectPosition(vditor.sv.element, getSelection().getRangeAt(0)));
-        });
-    }
-}

+ 14 - 17
src/ts/toolbar/Headings.ts

@@ -1,6 +1,6 @@
 import {Constants} from "../constants";
 import {processHeading} from "../ir/process";
-import {insertText} from "../sv/insertText";
+import {processHeading as processHeadingSV} from "../sv/process";
 import {getEventName, updateHotkeyTip} from "../util/compatibility";
 import {afterRenderEvent} from "../wysiwyg/afterRenderEvent";
 import {removeHeading, setHeading} from "../wysiwyg/setHeading";
@@ -35,20 +35,19 @@ export class Headings extends MenuItem {
                 return;
             }
             actionBtn.blur();
-            if (vditor.currentMode === "wysiwyg" && actionBtn.classList.contains("vditor-menu--current")) {
-                removeHeading(vditor);
-                afterRenderEvent(vditor);
-                actionBtn.classList.remove("vditor-menu--current");
-            } else if (vditor.currentMode === "ir" && actionBtn.classList.contains("vditor-menu--current")) {
-                processHeading(vditor, "");
-                actionBtn.classList.remove("vditor-menu--current");
-            } else {
-                if (panelElement.style.display === "block") {
-                    panelElement.style.display = "none";
+            if (actionBtn.classList.contains("vditor-menu--current")) {
+                if (vditor.currentMode === "wysiwyg") {
+                    removeHeading(vditor);
+                    afterRenderEvent(vditor);
+                } else if (vditor.currentMode === "ir") {
+                    processHeading(vditor, "");
                 } else {
-                    hidePanel(vditor, ["subToolbar"]);
-                    panelElement.style.display = "block";
+                    processHeadingSV(vditor, "");
                 }
+                actionBtn.classList.remove("vditor-menu--current");
+            } else {
+                hidePanel(vditor, ["subToolbar"]);
+                panelElement.style.display = "block";
             }
         });
 
@@ -58,14 +57,12 @@ export class Headings extends MenuItem {
                 if (vditor.currentMode === "wysiwyg") {
                     setHeading(vditor, (event.target as HTMLElement).getAttribute("data-tag"));
                     afterRenderEvent(vditor);
-                    actionBtn.classList.add("vditor-menu--current");
                 } else if (vditor.currentMode === "ir") {
                     processHeading(vditor, (event.target as HTMLElement).getAttribute("data-value"));
-                    actionBtn.classList.add("vditor-menu--current");
                 } else {
-                    insertText(vditor, (event.target as HTMLElement).getAttribute("data-value"), "",
-                        false, true);
+                    processHeadingSV(vditor, (event.target as HTMLElement).getAttribute("data-value"));
                 }
+                actionBtn.classList.add("vditor-menu--current");
                 panelElement.style.display = "none";
             });
         }

+ 5 - 5
src/ts/toolbar/MenuItem.ts

@@ -1,7 +1,7 @@
 import {Constants} from "../constants";
 import {i18n} from "../i18n/index";
 import {processToolbar} from "../ir/process";
-import {insertText} from "../sv/insertText";
+import {processToolbar as processToolbarSV} from "../sv/process";
 import {getEventName} from "../util/compatibility";
 import {updateHotkeyTip} from "../util/compatibility";
 import {toolbarEvent} from "../wysiwyg/toolbarEvent";
@@ -23,7 +23,7 @@ export class MenuItem {
             menuItem.tip + hotkey : i18n[vditor.options.lang][menuItem.name] + hotkey;
         const tagName = menuItem.name === "upload" ? "div" : "button";
         if (menuItem.level === 2) {
-            this.element.innerHTML = `<${tagName}>${tip}</${tagName}>`;
+            this.element.innerHTML = `<${tagName} data-type="${menuItem.name}">${tip}</${tagName}>`;
         } else {
             this.element.classList.add("vditor-toolbar__item");
             const iconElement = document.createElement(tagName);
@@ -43,13 +43,13 @@ export class MenuItem {
                 return;
             }
             if (vditor.currentMode === "wysiwyg") {
-                toolbarEvent(vditor, this.element.children[0]);
+                toolbarEvent(vditor, this.element.children[0], event);
             } else if (vditor.currentMode === "ir") {
                 processToolbar(vditor, this.element.children[0],
                     menuItem.prefix || "", menuItem.suffix || "");
             } else {
-                insertText(vditor, menuItem.prefix || "", menuItem.suffix || "",
-                    false, true);
+                processToolbarSV(vditor, this.element.children[0],
+                    menuItem.prefix || "", menuItem.suffix || "");
             }
         });
     }

+ 1 - 1
src/ts/toolbar/Preview.ts

@@ -18,7 +18,7 @@ export class Preview extends MenuItem {
                 return;
             }
 
-            const toolbars = Constants.EDIT_TOOLBARS.concat(["both", "format", "edit-mode", "devtools"]);
+            const toolbars = Constants.EDIT_TOOLBARS.concat(["both", "edit-mode", "devtools"]);
             if (btnElement.classList.contains("vditor-menu--current")) {
                 btnElement.classList.remove("vditor-menu--current");
                 if (vditor.currentMode === "sv") {

+ 1 - 1
src/ts/toolbar/Record.ts

@@ -2,8 +2,8 @@ import {Constants} from "../constants";
 import {i18n} from "../i18n/index";
 import {uploadFiles} from "../upload/index";
 import {getEventName} from "../util/compatibility";
+import {RecordMedia} from "../util/RecordMedia";
 import {MenuItem} from "./MenuItem";
-import {RecordMedia} from "./RecordMedia";
 
 export class Record extends MenuItem {
     constructor(vditor: IVditor, menuItem: IMenuItem) {

+ 1 - 7
src/ts/toolbar/Redo.ts

@@ -12,13 +12,7 @@ export class Redo extends MenuItem {
             if (this.element.firstElementChild.classList.contains(Constants.CLASS_MENU_DISABLED)) {
                 return;
             }
-            if (vditor.currentMode === "sv") {
-                vditor.undo.redo(vditor);
-            } else if (vditor.currentMode === "wysiwyg") {
-                vditor.wysiwygUndo.redo(vditor);
-            } else if (vditor.currentMode === "ir") {
-                vditor.irUndo.redo(vditor);
-            }
+            vditor.undo.redo(vditor);
         });
     }
 }

+ 1 - 7
src/ts/toolbar/Undo.ts

@@ -12,13 +12,7 @@ export class Undo extends MenuItem {
             if (this.element.firstElementChild.classList.contains(Constants.CLASS_MENU_DISABLED)) {
                 return;
             }
-            if (vditor.currentMode === "sv") {
-                vditor.undo.undo(vditor);
-            } else if (vditor.currentMode === "wysiwyg") {
-                vditor.wysiwygUndo.undo(vditor);
-            } else if (vditor.currentMode === "ir") {
-                vditor.irUndo.undo(vditor);
-            }
+            vditor.undo.undo(vditor);
         });
     }
 }

+ 6 - 3
src/ts/toolbar/Upload.ts

@@ -6,11 +6,14 @@ import {MenuItem} from "./MenuItem";
 export class Upload extends MenuItem {
     constructor(vditor: IVditor, menuItem: IMenuItem) {
         super(vditor, menuItem);
-        let inputHTML = '<input multiple="multiple" type="file">';
+        let inputHTML = '<input type="file"';
+        if (vditor.options.upload.multiple) {
+            inputHTML += ' multiple="multiple"';
+        }
         if (vditor.options.upload.accept) {
-            inputHTML = `<input multiple="multiple" type="file" accept="${vditor.options.upload.accept}">`;
+            inputHTML += ` accept="${vditor.options.upload.accept}"`;
         }
-        this.element.children[0].innerHTML = `${(menuItem.icon || uploadSVG)}${inputHTML}`;
+        this.element.children[0].innerHTML = `${(menuItem.icon || uploadSVG)}${inputHTML}>`;
         this._bindEvent(vditor);
     }
 

+ 0 - 4
src/ts/toolbar/index.ts

@@ -10,7 +10,6 @@ import {Divider} from "./Divider";
 import {EditMode} from "./EditMode";
 import {Emoji} from "./Emoji";
 import {Export} from "./Export";
-import {Format} from "./Format";
 import {Fullscreen} from "./Fullscreen";
 import {Headings} from "./Headings";
 import {Help} from "./Help";
@@ -127,9 +126,6 @@ export class Toolbar {
             case "info":
                 menuItemObj = new Info(vditor, menuItem);
                 break;
-            case "format":
-                menuItemObj = new Format(vditor, menuItem);
-                break;
             case "edit-mode":
                 menuItemObj = new EditMode(vditor, menuItem);
                 break;

+ 6 - 4
src/ts/ui/initUI.ts

@@ -1,6 +1,6 @@
 import {Constants} from "../constants";
-import {html2md} from "../sv/html2md";
 import {setEditMode} from "../toolbar/EditMode";
+import {accessLocalStorage} from "../util/compatibility";
 import {setContentTheme} from "./setContentTheme";
 import {setTheme} from "./setTheme";
 
@@ -14,7 +14,7 @@ export const initUI = (vditor: IVditor) => {
     vditor.element.innerHTML = "";
     vditor.element.classList.add("vditor");
     setTheme(vditor);
-    setContentTheme(vditor.options.preview.markdown.theme, vditor.options.cdn);
+    setContentTheme(vditor.options.preview.theme.current, vditor.options.preview.theme.path);
     if (typeof vditor.options.height === "number") {
         vditor.element.style.height = vditor.options.height + "px";
     }
@@ -68,6 +68,8 @@ export const initUI = (vditor: IVditor) => {
 
     setEditMode(vditor, vditor.options.mode, afterRender(vditor, contentElement));
 
+    document.execCommand("DefaultParagraphSeparator", false, "p");
+
     if (navigator.userAgent.indexOf("iPhone") > -1 && typeof window.visualViewport !== "undefined") {
         // https://github.com/Vanessa219/vditor/issues/379
         let pendingUpdate = false;
@@ -141,12 +143,12 @@ const afterRender = (vditor: IVditor, contentElement: HTMLElement) => {
     });
 
     // set default value
-    let initValue = localStorage.getItem(vditor.options.cache.id);
+    let initValue = accessLocalStorage() && localStorage.getItem(vditor.options.cache.id);
     if (!vditor.options.cache.enable || !initValue) {
         if (vditor.options.value) {
             initValue = vditor.options.value;
         } else if (vditor.originalInnerHTML) {
-            initValue = html2md(vditor, vditor.originalInnerHTML);
+            initValue = vditor.lute.HTML2Md(vditor.originalInnerHTML);
         } else if (!vditor.options.cache.enable) {
             initValue = "";
         }

+ 2 - 3
src/ts/ui/setCodeTheme.ts

@@ -1,8 +1,7 @@
-import {Constants, VDITOR_VERSION} from "../constants";
+import {Constants} from "../constants";
 import {addStyle} from "../util/addStyle";
 
-export const setCodeTheme = (codeTheme: string,
-                             cdn = `https://cdn.jsdelivr.net/npm/vditor@${VDITOR_VERSION}`) => {
+export const setCodeTheme = (codeTheme: string, cdn = Constants.CDN) => {
     if (!Constants.CODE_THEME.includes(codeTheme)) {
         codeTheme = "github";
     }

+ 5 - 16
src/ts/ui/setContentTheme.ts

@@ -1,23 +1,12 @@
-import {Constants, VDITOR_VERSION} from "../constants";
 import {addStyle} from "../util/addStyle";
 
-export const setContentTheme = (contentTheme: string,
-                                cdn = `https://cdn.jsdelivr.net/npm/vditor@${VDITOR_VERSION}`) => {
-    if (!Constants.CONTENT_THEME.includes(contentTheme)) {
-        return;
-    }
+export const setContentTheme = (contentTheme: string, path: string) => {
     const vditorContentTheme = document.getElementById("vditorContentTheme") as HTMLLinkElement;
-    const href = `${cdn}/dist/css/content-theme/${contentTheme}.css`;
-    if (contentTheme === "light") {
-        if (vditorContentTheme) {
-            vditorContentTheme.remove();
-        }
-        return;
-    }
+    const cssPath = `${path}/${contentTheme}.css`;
     if (!vditorContentTheme) {
-        addStyle(href, "vditorContentTheme");
-    } else if (vditorContentTheme.href !== href) {
+        addStyle(cssPath, "vditorContentTheme");
+    } else if (vditorContentTheme.href !== cssPath) {
         vditorContentTheme.remove();
-        addStyle(href, "vditorContentTheme");
+        addStyle(cssPath, "vditorContentTheme");
     }
 };

+ 0 - 165
src/ts/undo/IRUndo.ts

@@ -1,165 +0,0 @@
-import DiffMatchPatch, {diff_match_patch, patch_obj} from "diff-match-patch";
-import {processAfterRender} from "../ir/process";
-import {disableToolbar, enableToolbar} from "../toolbar/setToolbar";
-import {isFirefox, isSafari} from "../util/compatibility";
-import {scrollCenter} from "../util/editorCommonEvent";
-import {setRangeByWbr, setSelectionFocus} from "../util/selection";
-
-class IRUndo {
-    private undoStack: patch_obj[][];
-    private redoStack: patch_obj[][];
-    private stackSize = 50;
-    private dmp: diff_match_patch;
-    private lastText: string;
-    private hasUndo: boolean;
-
-    constructor() {
-        this.redoStack = [];
-        this.undoStack = [];
-        // @ts-ignore
-        this.dmp = new DiffMatchPatch();
-        this.lastText = "";
-        this.hasUndo = false;
-    }
-
-    public resetIcon(vditor: IVditor) {
-        if (this.undoStack.length > 1) {
-            enableToolbar(vditor.toolbar.elements, ["undo"]);
-        } else {
-            disableToolbar(vditor.toolbar.elements, ["undo"]);
-        }
-
-        if (this.redoStack.length !== 0) {
-            enableToolbar(vditor.toolbar.elements, ["redo"]);
-        } else {
-            disableToolbar(vditor.toolbar.elements, ["redo"]);
-        }
-    }
-
-    public undo(vditor: IVditor) {
-        if (vditor.ir.element.getAttribute("contenteditable") === "false") {
-            return;
-        }
-        if (this.undoStack.length < 2) {
-            return;
-        }
-        const state = this.undoStack.pop();
-        if (!state || !state) {
-            return;
-        }
-        this.redoStack.push(state);
-        this.renderDiff(state, vditor);
-        this.hasUndo = true;
-    }
-
-    public redo(vditor: IVditor) {
-        if (vditor.ir.element.getAttribute("contenteditable") === "false") {
-            return;
-        }
-        const state = this.redoStack.pop();
-        if (!state) {
-            return;
-        }
-        this.undoStack.push(state);
-        this.renderDiff(state, vditor, true);
-    }
-
-    public recordFirstWbr(vditor: IVditor, event: KeyboardEvent) {
-        if (getSelection().rangeCount === 0) {
-            return;
-        }
-        if (this.undoStack.length !== 1 || this.undoStack[0].length === 0) {
-            return;
-        }
-        if (isFirefox() && event.key === "Backspace") {
-            // Firefox 第一次删除无效
-            return;
-        }
-        if (isSafari()) {
-            // Safari keydown 在 input 之后,不需要重复记录历史
-            return;
-        }
-        getSelection().getRangeAt(0).insertNode(document.createElement("wbr"));
-        this.undoStack[0][0].diffs[0][1] = vditor.lute.SpinVditorIRDOM(vditor.ir.element.innerHTML);
-        this.lastText = this.undoStack[0][0].diffs[0][1];
-        if (vditor.ir.element.querySelector("wbr")) {
-            vditor.ir.element.querySelector("wbr").remove();
-        }
-        // 不能添加 setSelectionFocus(cloneRange); 否则 windows chrome 首次输入会烂
-    }
-
-    public addToUndoStack(vditor: IVditor) {
-        let cloneRange: Range;
-        if (getSelection().rangeCount !== 0 && !vditor.ir.element.querySelector("wbr")) {
-            const range = getSelection().getRangeAt(0);
-            cloneRange = range.cloneRange();
-            if (vditor.ir.element.contains(range.startContainer)) {
-                range.insertNode(document.createElement("wbr"));
-            }
-        }
-        const text = vditor.lute.SpinVditorIRDOM(vditor.ir.element.innerHTML);
-        if (vditor.ir.element.querySelector("wbr")) {
-            vditor.ir.element.querySelector("wbr").remove();
-        }
-        if (cloneRange) {
-            setSelectionFocus(cloneRange);
-        }
-        const diff = this.dmp.diff_main(text, this.lastText, true);
-        const patchList = this.dmp.patch_make(text, this.lastText, diff);
-        if (patchList.length === 0 && this.undoStack.length > 0) {
-            return;
-        }
-        this.lastText = text;
-        this.undoStack.push(patchList);
-        if (this.undoStack.length > this.stackSize) {
-            this.undoStack.shift();
-        }
-        if (this.hasUndo) {
-            this.redoStack = [];
-            this.hasUndo = false;
-        }
-
-        if (this.undoStack.length > 1) {
-            enableToolbar(vditor.toolbar.elements, ["undo"]);
-        }
-    }
-
-    private renderDiff(state: patch_obj[], vditor: IVditor, isRedo: boolean = false) {
-        let text;
-        if (isRedo) {
-            const redoPatchList = this.dmp.patch_deepCopy(state).reverse();
-            redoPatchList.forEach((patch) => {
-                patch.diffs.forEach((diff) => {
-                    diff[0] = -diff[0];
-                });
-            });
-            text = this.dmp.patch_apply(redoPatchList, this.lastText)[0];
-        } else {
-            text = this.dmp.patch_apply(state, this.lastText)[0];
-        }
-
-        this.lastText = text;
-        vditor.ir.element.innerHTML = text;
-        setRangeByWbr(vditor.ir.element, vditor.ir.element.ownerDocument.createRange());
-        scrollCenter(vditor);
-        processAfterRender(vditor, {
-            enableAddUndoStack: false,
-            enableHint: false,
-            enableInput: true,
-        });
-
-        if (this.undoStack.length > 1) {
-            enableToolbar(vditor.toolbar.elements, ["undo"]);
-        } else {
-            disableToolbar(vditor.toolbar.elements, ["undo"]);
-        }
-
-        if (this.redoStack.length !== 0) {
-            enableToolbar(vditor.toolbar.elements, ["redo"]);
-        } else {
-            disableToolbar(vditor.toolbar.elements, ["redo"]);
-        }
-    }
-}
-
-export {IRUndo};

+ 0 - 176
src/ts/undo/WysiwygUndo.ts

@@ -1,176 +0,0 @@
-import DiffMatchPatch, {diff_match_patch, patch_obj} from "diff-match-patch";
-import {disableToolbar} from "../toolbar/setToolbar";
-import {enableToolbar} from "../toolbar/setToolbar";
-import {isFirefox, isSafari} from "../util/compatibility";
-import {scrollCenter} from "../util/editorCommonEvent";
-import {processCodeRender} from "../util/processCode";
-import {setRangeByWbr, setSelectionFocus} from "../util/selection";
-import {afterRenderEvent} from "../wysiwyg/afterRenderEvent";
-import {highlightToolbar} from "../wysiwyg/highlightToolbar";
-
-class WysiwygUndo {
-    private undoStack: patch_obj[][];
-    private redoStack: patch_obj[][];
-    private stackSize = 50;
-    private dmp: diff_match_patch;
-    private lastText: string;
-    private hasUndo: boolean;
-
-    constructor() {
-        this.redoStack = [];
-        this.undoStack = [];
-        // @ts-ignore
-        this.dmp = new DiffMatchPatch();
-        this.lastText = "";
-        this.hasUndo = false;
-    }
-
-    public resetIcon(vditor: IVditor) {
-        if (this.undoStack.length > 1) {
-            enableToolbar(vditor.toolbar.elements, ["undo"]);
-        } else {
-            disableToolbar(vditor.toolbar.elements, ["undo"]);
-        }
-
-        if (this.redoStack.length !== 0) {
-            enableToolbar(vditor.toolbar.elements, ["redo"]);
-        } else {
-            disableToolbar(vditor.toolbar.elements, ["redo"]);
-        }
-    }
-
-    public undo(vditor: IVditor) {
-        if (vditor.wysiwyg.element.getAttribute("contenteditable") === "false") {
-            return;
-        }
-        if (this.undoStack.length < 2) {
-            return;
-        }
-        const state = this.undoStack.pop();
-        if (!state || !state) {
-            return;
-        }
-        this.redoStack.push(state);
-        this.renderDiff(state, vditor);
-        this.hasUndo = true;
-    }
-
-    public redo(vditor: IVditor) {
-        if (vditor.wysiwyg.element.getAttribute("contenteditable") === "false") {
-            return;
-        }
-        const state = this.redoStack.pop();
-        if (!state) {
-            return;
-        }
-        this.undoStack.push(state);
-        this.renderDiff(state, vditor, true);
-    }
-
-    public recordFirstWbr(vditor: IVditor, event: KeyboardEvent) {
-        if (getSelection().rangeCount === 0) {
-            return;
-        }
-        if (this.undoStack.length !== 1 || this.undoStack[0].length === 0) {
-            return;
-        }
-        if (isFirefox() && event.key === "Backspace") {
-            // Firefox 第一次删除无效
-            return;
-        }
-        if (isSafari()) {
-            // Safari keydown 在 input 之后,不需要重复记录历史
-            return;
-        }
-        getSelection().getRangeAt(0).insertNode(document.createElement("wbr"));
-        this.undoStack[0][0].diffs[0][1] = vditor.lute.SpinVditorDOM(vditor.wysiwyg.element.innerHTML);
-        this.lastText = this.undoStack[0][0].diffs[0][1];
-        if (vditor.wysiwyg.element.querySelector("wbr")) {
-            vditor.wysiwyg.element.querySelector("wbr").remove();
-        }
-        // 不能添加 setSelectionFocus(cloneRange); 否则 windows chrome 首次输入会烂
-    }
-
-    public addToUndoStack(vditor: IVditor) {
-        // wysiwyg/afterRenderEvent.ts 已经 debounce
-        let cloneRange: Range;
-        if (getSelection().rangeCount !== 0 && !vditor.wysiwyg.element.querySelector("wbr")) {
-            const range = getSelection().getRangeAt(0);
-            if (vditor.wysiwyg.element.contains(range.startContainer)) {
-                cloneRange = range.cloneRange();
-                range.insertNode(document.createElement("wbr"));
-            }
-        }
-        const text = vditor.lute.SpinVditorDOM(vditor.wysiwyg.element.innerHTML);
-        if (vditor.wysiwyg.element.querySelector("wbr")) {
-            vditor.wysiwyg.element.querySelector("wbr").remove();
-        }
-        if (cloneRange) {
-            setSelectionFocus(cloneRange);
-        }
-        const diff = this.dmp.diff_main(text, this.lastText, true);
-        const patchList = this.dmp.patch_make(text, this.lastText, diff);
-        if (patchList.length === 0 && this.undoStack.length > 0) {
-            return;
-        }
-        this.lastText = text;
-        this.undoStack.push(patchList);
-        if (this.undoStack.length > this.stackSize) {
-            this.undoStack.shift();
-        }
-        if (this.hasUndo) {
-            this.redoStack = [];
-            this.hasUndo = false;
-            disableToolbar(vditor.toolbar.elements, ["redo"]);
-        }
-
-        if (this.undoStack.length > 1) {
-            enableToolbar(vditor.toolbar.elements, ["undo"]);
-        }
-    }
-
-    private renderDiff(state: patch_obj[], vditor: IVditor, isRedo: boolean = false) {
-        let text;
-        if (isRedo) {
-            const redoPatchList = this.dmp.patch_deepCopy(state).reverse();
-            redoPatchList.forEach((patch) => {
-                patch.diffs.forEach((diff) => {
-                    diff[0] = -diff[0];
-                });
-            });
-            text = this.dmp.patch_apply(redoPatchList, this.lastText)[0];
-        } else {
-            text = this.dmp.patch_apply(state, this.lastText)[0];
-        }
-
-        this.lastText = text;
-
-        vditor.wysiwyg.element.innerHTML = text;
-        vditor.wysiwyg.element.querySelectorAll(".vditor-wysiwyg__preview[data-render='2']")
-            .forEach((blockElement: HTMLElement) => {
-                processCodeRender(blockElement, vditor);
-            });
-        setRangeByWbr(vditor.wysiwyg.element, vditor.wysiwyg.element.ownerDocument.createRange());
-        scrollCenter(vditor);
-        afterRenderEvent(vditor, {
-            enableAddUndoStack: false,
-            enableHint: false,
-            enableInput: true,
-        });
-        highlightToolbar(vditor);
-
-        if (this.undoStack.length > 1) {
-            enableToolbar(vditor.toolbar.elements, ["undo"]);
-        } else {
-            disableToolbar(vditor.toolbar.elements, ["undo"]);
-        }
-
-        if (this.redoStack.length !== 0) {
-            enableToolbar(vditor.toolbar.elements, ["redo"]);
-        } else {
-            disableToolbar(vditor.toolbar.elements, ["redo"]);
-        }
-    }
-}
-
-export {WysiwygUndo};

+ 177 - 74
src/ts/undo/index.ts

@@ -1,150 +1,253 @@
 import DiffMatchPatch, {diff_match_patch, patch_obj} from "diff-match-patch";
-import {getMarkdown} from "../markdown/getMarkdown";
-import {formatRender} from "../sv/formatRender";
-import {disableToolbar} from "../toolbar/setToolbar";
-import {enableToolbar} from "../toolbar/setToolbar";
+import {disableToolbar, enableToolbar} from "../toolbar/setToolbar";
+import {isFirefox, isSafari} from "../util/compatibility";
 import {scrollCenter} from "../util/editorCommonEvent";
-import {getSelectPosition} from "../util/selection";
+import {execAfterRender} from "../util/fixBrowserBehavior";
+import {highlightToolbar} from "../util/highlightToolbar";
+import {processCodeRender} from "../util/processCode";
+import {setRangeByWbr, setSelectionFocus} from "../util/selection";
+
+interface IUndo {
+    hasUndo: boolean;
+    lastText: string;
+    redoStack: patch_obj[][];
+    undoStack: patch_obj[][];
+}
 
 class Undo {
-    private undoStack: Array<{ patchList: patch_obj[], end: number }>;
-    private redoStack: Array<{ patchList: patch_obj[], end: number }>;
     private stackSize = 50;
     private dmp: diff_match_patch;
-    private lastText: string;
-    private hasUndo: boolean;
-    private timeout: number;
+    private wysiwyg: IUndo;
+    private ir: IUndo;
+    private sv: IUndo;
 
     constructor() {
-        this.redoStack = [];
-        this.undoStack = [];
+        this.resetStack();
         // @ts-ignore
         this.dmp = new DiffMatchPatch();
-        this.lastText = "";
-        this.hasUndo = false;
+    }
+
+    public clearStack(vditor: IVditor) {
+        this.resetStack();
+        this.resetIcon(vditor);
     }
 
     public resetIcon(vditor: IVditor) {
-        if (this.undoStack.length > 1) {
+        if (!vditor.toolbar) {
+            return;
+        }
+
+        if (this[vditor.currentMode].undoStack.length > 1) {
             enableToolbar(vditor.toolbar.elements, ["undo"]);
         } else {
             disableToolbar(vditor.toolbar.elements, ["undo"]);
         }
 
-        if (this.redoStack.length !== 0) {
+        if (this[vditor.currentMode].redoStack.length !== 0) {
             enableToolbar(vditor.toolbar.elements, ["redo"]);
         } else {
             disableToolbar(vditor.toolbar.elements, ["redo"]);
         }
     }
 
-    public recordFirstPosition(vditor: IVditor) {
-        if (this.undoStack.length === 1) {
-            this.undoStack[0].end = getSelectPosition(vditor.sv.element).end;
-        }
-    }
-
     public undo(vditor: IVditor) {
-        if (vditor.sv.element.getAttribute("contenteditable") === "false") {
+        if (vditor[vditor.currentMode].element.getAttribute("contenteditable") === "false") {
             return;
         }
-        if (this.undoStack.length < 2) {
+        if (this[vditor.currentMode].undoStack.length < 2) {
             return;
         }
-        const state = this.undoStack.pop();
-        if (!state || !state.patchList) {
+        const state = this[vditor.currentMode].undoStack.pop();
+        if (!state) {
             return;
         }
-        this.redoStack.push(state);
+        this[vditor.currentMode].redoStack.push(state);
         this.renderDiff(state, vditor);
-        this.hasUndo = true;
+        this[vditor.currentMode].hasUndo = true;
     }
 
     public redo(vditor: IVditor) {
-        if (vditor.sv.element.getAttribute("contenteditable") === "false") {
+        if (vditor[vditor.currentMode].element.getAttribute("contenteditable") === "false") {
             return;
         }
-        const state = this.redoStack.pop();
-        if (!state || !state.patchList) {
+        const state = this[vditor.currentMode].redoStack.pop();
+        if (!state) {
             return;
         }
-        this.undoStack.push(state);
+        this[vditor.currentMode].undoStack.push(state);
         this.renderDiff(state, vditor, true);
     }
 
+    public recordFirstPosition(vditor: IVditor, event: KeyboardEvent) {
+        if (getSelection().rangeCount === 0) {
+            return;
+        }
+        if (this[vditor.currentMode].undoStack.length !== 1 || this[vditor.currentMode].undoStack[0].length === 0) {
+            return;
+        }
+        if (isFirefox() && event.key === "Backspace") {
+            // Firefox 第一次删除无效
+            return;
+        }
+        if (isSafari()) {
+            // Safari keydown 在 input 之后,不需要重复记录历史
+            return;
+        }
+        if (vditor.currentMode === "sv") {
+            const caretElement = document.createElement("span");
+            caretElement.className = "wbr";
+            caretElement.textContent = Lute.Caret;
+            getSelection().getRangeAt(0).insertNode(caretElement);
+        } else {
+            getSelection().getRangeAt(0).insertNode(document.createElement("wbr"));
+        }
+        if (vditor.currentMode === "wysiwyg") {
+            this[vditor.currentMode].undoStack[0][0].diffs[0][1] =
+                vditor.lute.SpinVditorDOM(vditor[vditor.currentMode].element.innerHTML);
+        } else if (vditor.currentMode === "ir") {
+            this[vditor.currentMode].undoStack[0][0].diffs[0][1] =
+                vditor.lute.SpinVditorIRDOM(vditor[vditor.currentMode].element.innerHTML);
+        } else {
+            this[vditor.currentMode].undoStack[0][0].diffs[0][1] =
+                vditor.lute.SpinVditorSVDOM(vditor[vditor.currentMode].element.textContent);
+        }
+        this[vditor.currentMode].lastText = this[vditor.currentMode].undoStack[0][0].diffs[0][1];
+        const wbrElement =
+            vditor[vditor.currentMode].element.querySelector(vditor.currentMode === "sv" ? ".wbr" : "wbr");
+        if (wbrElement) {
+            wbrElement.remove();
+        }
+        // 不能添加 setSelectionFocus(cloneRange); 否则 windows chrome 首次输入会烂
+    }
+
     public addToUndoStack(vditor: IVditor) {
-        clearTimeout(this.timeout);
-        this.timeout = window.setTimeout(() => {
-            const text = getMarkdown(vditor);
-            const diff = this.dmp.diff_main(text, this.lastText, true);
-            const patchList = this.dmp.patch_make(text, this.lastText, diff);
-            if (patchList.length === 0) {
-                return;
-            }
-            this.lastText = text;
-            this.undoStack.push({patchList, end: getSelectPosition(vditor.sv.element).end});
-            if (this.undoStack.length > this.stackSize) {
-                this.undoStack.shift();
-            }
-            if (this.hasUndo) {
-                this.redoStack = [];
-                this.hasUndo = false;
-                disableToolbar(vditor.toolbar.elements, ["redo"]);
+        // afterRenderEvent.ts 已经 debounce
+        let cloneRange: Range;
+        if (getSelection().rangeCount !== 0 && !vditor[vditor.currentMode].element.querySelector("wbr")) {
+            const range = getSelection().getRangeAt(0);
+            if (vditor[vditor.currentMode].element.contains(range.startContainer)) {
+                cloneRange = range.cloneRange();
+                if (vditor.currentMode === "sv") {
+                    const caretElement = document.createElement("span");
+                    caretElement.className = "wbr";
+                    caretElement.textContent = Lute.Caret;
+                    range.insertNode(caretElement);
+                } else {
+                    range.insertNode(document.createElement("wbr"));
+                }
             }
+        }
+        let text;
+        if (vditor.currentMode === "wysiwyg") {
+            text = vditor.lute.SpinVditorDOM(vditor[vditor.currentMode].element.innerHTML);
+        } else if (vditor.currentMode === "ir") {
+            text = vditor.lute.SpinVditorIRDOM(vditor[vditor.currentMode].element.innerHTML);
+        } else {
+            text = vditor.lute.SpinVditorSVDOM(vditor[vditor.currentMode].element.textContent);
+        }
+        const wbrElement =
+            vditor[vditor.currentMode].element.querySelector(vditor.currentMode === "sv" ? ".wbr" : "wbr");
+        if (wbrElement) {
+            wbrElement.remove();
+        }
+        if (cloneRange) {
+            setSelectionFocus(cloneRange);
+        }
+        const diff = this.dmp.diff_main(text, this[vditor.currentMode].lastText, true);
+        const patchList = this.dmp.patch_make(text, this[vditor.currentMode].lastText, diff);
+        if (patchList.length === 0 && this[vditor.currentMode].undoStack.length > 0) {
+            return;
+        }
+        this[vditor.currentMode].lastText = text;
+        this[vditor.currentMode].undoStack.push(patchList);
+        if (this[vditor.currentMode].undoStack.length > this.stackSize) {
+            this[vditor.currentMode].undoStack.shift();
+        }
+        if (this[vditor.currentMode].hasUndo) {
+            this[vditor.currentMode].redoStack = [];
+            this[vditor.currentMode].hasUndo = false;
+            disableToolbar(vditor.toolbar.elements, ["redo"]);
+        }
 
-            if (this.undoStack.length > 1) {
-                enableToolbar(vditor.toolbar.elements, ["undo"]);
-            }
-        }, 500);
+        if (this[vditor.currentMode].undoStack.length > 1) {
+            enableToolbar(vditor.toolbar.elements, ["undo"]);
+        }
     }
 
-    private renderDiff(state: { patchList: patch_obj[], end: number }, vditor: IVditor, isRedo: boolean = false) {
+    private renderDiff(state: patch_obj[], vditor: IVditor, isRedo: boolean = false) {
         let text;
-        let positoin;
         if (isRedo) {
-            const redoPatchList = this.dmp.patch_deepCopy(state.patchList).reverse();
+            const redoPatchList = this.dmp.patch_deepCopy(state).reverse();
             redoPatchList.forEach((patch) => {
                 patch.diffs.forEach((diff) => {
                     diff[0] = -diff[0];
                 });
             });
-            text = this.dmp.patch_apply(redoPatchList, this.lastText)[0];
-            positoin = {
-                end: state.end,
-                start: state.end,
-            };
+            text = this.dmp.patch_apply(redoPatchList, this[vditor.currentMode].lastText)[0];
         } else {
-            text = this.dmp.patch_apply(state.patchList, this.lastText)[0];
-            if (this.undoStack[this.undoStack.length - 1]) {
-                positoin = {
-                    end: this.undoStack[this.undoStack.length - 1].end,
-                    start: this.undoStack[this.undoStack.length - 1].end,
-                };
-            }
+            text = this.dmp.patch_apply(state, this[vditor.currentMode].lastText)[0];
+        }
+
+        this[vditor.currentMode].lastText = text;
+        vditor[vditor.currentMode].element.innerHTML = text;
+        if (vditor.currentMode !== "sv") {
+            vditor[vditor.currentMode].element.querySelectorAll(`.vditor-${vditor.currentMode}__preview[data-render='2']`)
+                .forEach((blockElement: HTMLElement) => {
+                    processCodeRender(blockElement, vditor);
+                });
         }
 
-        this.lastText = text;
+        if (!vditor[vditor.currentMode].element.querySelector("wbr")) {
+            // Safari 第一次输入没有光标,需手动定位到结尾
+            const range = getSelection().getRangeAt(0);
+            range.setEndBefore(vditor[vditor.currentMode].element);
+            range.collapse(false);
+        } else {
+            setRangeByWbr(
+                vditor[vditor.currentMode].element, vditor[vditor.currentMode].element.ownerDocument.createRange());
+            scrollCenter(vditor);
+        }
 
-        formatRender(vditor, text, positoin, {
+        execAfterRender(vditor, {
             enableAddUndoStack: false,
             enableHint: false,
             enableInput: true,
         });
+        highlightToolbar(vditor);
 
-        scrollCenter(vditor);
-
-        if (this.undoStack.length > 1) {
+        if (this[vditor.currentMode].undoStack.length > 1) {
             enableToolbar(vditor.toolbar.elements, ["undo"]);
         } else {
             disableToolbar(vditor.toolbar.elements, ["undo"]);
         }
 
-        if (this.redoStack.length !== 0) {
+        if (this[vditor.currentMode].redoStack.length !== 0) {
             enableToolbar(vditor.toolbar.elements, ["redo"]);
         } else {
             disableToolbar(vditor.toolbar.elements, ["redo"]);
         }
     }
+
+    private resetStack() {
+        this.ir = {
+            hasUndo: false,
+            lastText: "",
+            redoStack: [],
+            undoStack: [],
+        };
+        this.sv = {
+            hasUndo: false,
+            lastText: "",
+            redoStack: [],
+            undoStack: [],
+        };
+        this.wysiwyg = {
+            hasUndo: false,
+            lastText: "",
+            redoStack: [],
+            undoStack: [],
+        };
+    }
 }
 
 export {Undo};

+ 13 - 19
src/ts/upload/index.ts

@@ -1,5 +1,4 @@
 import {i18n} from "../i18n/index";
-import {insertText} from "../sv/insertText";
 import {getEditorRange, setSelectionFocus} from "../util/selection";
 import {getElement} from "./getElement";
 import {setHeaders} from "./setHeaders";
@@ -21,7 +20,7 @@ const validateFile = (vditor: IVditor, files: File[]) => {
     const uploadFileList = [];
     let errorTip = "";
     let uploadingStr = "";
-    const lang: (keyof II18nLang) = vditor.options.lang;
+    const lang: keyof II18n = vditor.options.lang;
 
     for (let iMax = files.length, i = 0; i < iMax; i++) {
         const file = files[i];
@@ -42,18 +41,18 @@ const validateFile = (vditor: IVditor, files: File[]) => {
         const filename = vditor.options.upload.filename(file.name.substr(0, lastIndex)) + fileExt;
 
         if (vditor.options.upload.accept) {
-            let isAccept = false;
-            vditor.options.upload.accept.split(",").forEach((item) => {
+            const isAccept = vditor.options.upload.accept.split(",").some((item) => {
                 const type = item.trim();
                 if (type.indexOf(".") === 0) {
-                    if (fileExt === type) {
-                        isAccept = true;
+                    if (fileExt.toLowerCase() === type.toLowerCase()) {
+                        return true;
                     }
                 } else {
                     if (file.type.split("/")[0] === type.split("/")[0]) {
-                        isAccept = true;
+                        return true;
                     }
                 }
+                return false;
             });
 
             if (!isAccept) {
@@ -135,18 +134,15 @@ const genUploadedLabel = (responseText: string, vditor: IVditor) => {
         }
     });
     setSelectionFocus(vditor.upload.range);
-    if (vditor.currentMode !== "sv") {
-        document.execCommand("insertHTML", false, succFileText);
-    } else {
-        insertText(vditor, succFileText, "", true);
-    }
+    document.execCommand("insertHTML", false, succFileText);
     vditor.upload.range = getSelection().getRangeAt(0).cloneRange();
 };
 
 const uploadFiles = (vditor: IVditor, files: FileList | DataTransferItemList | File[], element?: HTMLInputElement) => {
     // FileList | DataTransferItemList | File[] => File[]
     let fileList = [];
-    for (let iMax = files.length, i = 0; i < iMax; i++) {
+    const filesMax = vditor.options.upload.multiple === true ? files.length : 1;
+    for (let i = 0; i < filesMax; i++) {
         let fileItem = files[i];
         if (fileItem instanceof DataTransferItem) {
             fileItem = fileItem.getAsFile();
@@ -196,7 +192,7 @@ const uploadFiles = (vditor: IVditor, files: FileList | DataTransferItemList | F
 
     const formData = new FormData();
     for (let i = 0, iMax = validateResult.length; i < iMax; i++) {
-        formData.append("file[]", validateResult[i]);
+        formData.append(vditor.options.upload.fieldName, validateResult[i]);
     }
 
     const extraData = vditor.options.upload.extraData;
@@ -215,15 +211,10 @@ const uploadFiles = (vditor: IVditor, files: FileList | DataTransferItemList | F
     setHeaders(vditor, xhr);
     vditor.upload.isUploading = true;
     editorElement.setAttribute("contenteditable", "false");
-
     xhr.onreadystatechange = () => {
         if (xhr.readyState === XMLHttpRequest.DONE) {
             vditor.upload.isUploading = false;
-            if (element) {
-                element.value = "";
-            }
             editorElement.setAttribute("contenteditable", "true");
-
             if (xhr.status === 200) {
                 if (vditor.options.upload.success) {
                     vditor.options.upload.success(editorElement, xhr.responseText);
@@ -241,6 +232,9 @@ const uploadFiles = (vditor: IVditor, files: FileList | DataTransferItemList | F
                     vditor.tip.show(xhr.responseText);
                 }
             }
+            if (element) {
+                element.value = "";
+            }
             vditor.upload.element.style.display = "none";
         }
     };

+ 210 - 279
src/ts/util/Options.ts

@@ -22,7 +22,6 @@ import linkSVG from "../../assets/icons/link.svg";
 import listSVG from "../../assets/icons/list.svg";
 import moreSVG from "../../assets/icons/more.svg";
 import orderedListVG from "../../assets/icons/ordered-list.svg";
-import formatSVG from "../../assets/icons/outdent.svg";
 import outdentSVG from "../../assets/icons/outdent.svg";
 import previewSVG from "../../assets/icons/preview.svg";
 import quoteSVG from "../../assets/icons/quote.svg";
@@ -33,7 +32,8 @@ import tableSVG from "../../assets/icons/table.svg";
 import contentThemeSVG from "../../assets/icons/theme.svg";
 import undoSVG from "../../assets/icons/undo.svg";
 import uploadSVG from "../../assets/icons/upload.svg";
-import {VDITOR_VERSION} from "../constants";
+import {Constants} from "../constants";
+import {merge} from "./merge";
 
 export class Options {
     public options: IOptions;
@@ -42,7 +42,7 @@ export class Options {
         cache: {
             enable: true,
         },
-        cdn: `https://cdn.jsdelivr.net/npm/vditor@${VDITOR_VERSION}`,
+        cdn: Constants.CDN,
         classes: {
             preview: "",
         },
@@ -64,42 +64,21 @@ export class Options {
                 "smile": "😄",
                 "tada": "🎉️",
             },
-            emojiPath: `https://cdn.jsdelivr.net/npm/vditor@${VDITOR_VERSION}/dist/images/emoji`,
-        },
-        keymap: {
-            deleteLine: "⌘-Backspace",
-            duplicate: "⌘-D",
+            emojiPath: `${Constants.CDN}/dist/images/emoji`,
         },
         lang: "zh_CN",
-        mode: "wysiwyg",
+        mode: "ir",
         outline: false,
         placeholder: "",
         preview: {
+            actions: ["desktop", "tablet", "mobile", "mp-wechat", "zhihu"],
             delay: 1000,
-            hljs: {
-                enable: true,
-                lineNumber: false,
-                style: "github",
-            },
-            markdown: {
-                autoSpace: false,
-                chinesePunct: false,
-                codeBlockPreview: true,
-                fixTermTypo: false,
-                footnotes: true,
-                listMarker: false,
-                sanitize: true,
-                setext: false,
-                theme: "light",
-                toc: false,
-            },
-            math: {
-                engine: "KaTeX",
-                inlineDigit: false,
-                macros: {},
-            },
+            hljs: Constants.HLJS_OPTIONS,
+            markdown: Constants.MARKDOWN_OPTIONS,
+            math: Constants.MATH_OPTIONS,
             maxWidth: 800,
             mode: "both",
+            theme: Constants.THEME_OPTIONS,
         },
         resize: {
             enable: false,
@@ -145,7 +124,6 @@ export class Options {
                     "export",
                     "outline",
                     "preview",
-                    "format",
                     "devtools",
                     "info",
                     "help",
@@ -158,9 +136,11 @@ export class Options {
         typewriterMode: false,
         upload: {
             extraData: {},
+            fieldName: "file[]",
             filename: (name: string) => name.replace(/\W/g, ""),
             linkToImgUrl: "",
             max: 10 * 1024 * 1024,
+            multiple: true,
             url: "",
             withCredentials: false,
         },
@@ -168,265 +148,26 @@ export class Options {
         width: "auto",
     };
 
-    private toolbarItem = [{
-        icon: exportSVG,
-        name: "export",
-        tipPosition: "ne",
-    }, {
-        hotkey: "⌘-E",
-        icon: emojiSVG,
-        name: "emoji",
-        tipPosition: "ne",
-    }, {
-        hotkey: "⌘-H",
-        icon: headingsSVG,
-        name: "headings",
-        tipPosition: "ne",
-    }, {
-        hotkey: "⌘-B",
-        icon: boldSVG,
-        name: "bold",
-        prefix: "**",
-        suffix: "**",
-        tipPosition: "ne",
-    }, {
-        hotkey: "⌘-I",
-        icon: italicSVG,
-        name: "italic",
-        prefix: "*",
-        suffix: "*",
-        tipPosition: "ne",
-    }, {
-        hotkey: "⌘-S",
-        icon: strikekSVG,
-        name: "strike",
-        prefix: "~~",
-        suffix: "~~",
-        tipPosition: "ne",
-    }, {
-        hotkey: "⌘-K",
-        icon: linkSVG,
-        name: "link",
-        prefix: "[",
-        suffix: "](https://)",
-        tipPosition: "n",
-    }, {
-        name: "|",
-    }, {
-        hotkey: "⌘-L",
-        icon: listSVG,
-        name: "list",
-        prefix: "* ",
-        tipPosition: "n",
-    }, {
-        hotkey: "⌘-O",
-        icon: orderedListVG,
-        name: "ordered-list",
-        prefix: "1. ",
-        tipPosition: "n",
-    }, {
-        hotkey: "⌘-J",
-        icon: checkSVG,
-        name: "check",
-        prefix: "* [ ] ",
-        tipPosition: "n",
-    }, {
-        hotkey: "⌘-⇧-I",
-        icon: outdentSVG,
-        name: "outdent",
-        tipPosition: "n",
-    }, {
-        hotkey: "⌘-⇧-O",
-        icon: indentSVG,
-        name: "indent",
-        tipPosition: "n",
-    }, {
-        name: "|",
-    }, {
-        hotkey: "⌘-;",
-        icon: quoteSVG,
-        name: "quote",
-        prefix: "> ",
-        tipPosition: "n",
-    }, {
-        hotkey: "⌘-⇧-H",
-        icon: lineSVG,
-        name: "line",
-        prefix: "---",
-        tipPosition: "n",
-    }, {
-        hotkey: "⌘-U",
-        icon: codeSVG,
-        name: "code",
-        prefix: "```\n",
-        suffix: "\n```",
-        tipPosition: "n",
-    }, {
-        hotkey: "⌘-G",
-        icon: inlineCodeSVG,
-        name: "inline-code",
-        prefix: "`",
-        suffix: "`",
-        tipPosition: "n",
-    }, {
-        hotkey: "⌘-⇧-B",
-        icon: insertBeforeSVG,
-        name: "insert-before",
-        tipPosition: "n",
-    }, {
-        hotkey: "⌘-⇧-E",
-        icon: insertAfterSVG,
-        name: "insert-after",
-        tipPosition: "n",
-    }, {
-        name: "|",
-    }, {
-        icon: uploadSVG,
-        name: "upload",
-        tipPosition: "n",
-    }, {
-        icon: recordSVG,
-        name: "record",
-        tipPosition: "n",
-    }, {
-        hotkey: "⌘-M",
-        icon: tableSVG,
-        name: "table",
-        prefix: "| col1",
-        suffix: " | col2 | col3 |\n| --- | --- | --- |\n|  |  |  |\n|  |  |  |",
-        tipPosition: "n",
-    }, {
-        name: "|",
-    }, {
-        hotkey: "⌘-Z",
-        icon: undoSVG,
-        name: "undo",
-        tipPosition: "nw",
-    }, {
-        hotkey: "⌘-Y",
-        icon: redoSVG,
-        name: "redo",
-        tipPosition: "nw",
-    }, {
-        name: "|",
-    }, {
-        icon: moreSVG,
-        name: "more",
-        tipPosition: "e",
-    }, {
-        hotkey: "⌘-'",
-        icon: fullscreenSVG,
-        name: "fullscreen",
-        tipPosition: "nw",
-    }, {
-        icon: editSVG,
-        name: "edit-mode",
-        tipPosition: "nw",
-    }, {
-        hotkey: "⌘-P",
-        icon: bothSVG,
-        name: "both",
-        tipPosition: "nw",
-    }, {
-        icon: previewSVG,
-        name: "preview",
-        tipPosition: "nw",
-    }, {
-        hotkey: "⌘-⇧-F",
-        icon: formatSVG,
-        name: "format",
-        tipPosition: "nw",
-    }, {
-        icon: outlinerSVG,
-        name: "outline",
-        tipPosition: "nw",
-    }, {
-        icon: contentThemeSVG,
-        name: "content-theme",
-        tipPosition: "nw",
-    }, {
-        icon: codeThemeSVG,
-        name: "code-theme",
-        tipPosition: "nw",
-    }, {
-        icon: bugSVG,
-        name: "devtools",
-        tipPosition: "nw",
-    }, {
-        icon: infoSVG,
-        name: "info",
-        tipPosition: "nw",
-    }, {
-        icon: helpSVG,
-        name: "help",
-        tipPosition: "nw",
-    }, {
-        name: "br",
-    }];
-
     constructor(options: IOptions) {
         this.options = options;
     }
 
     public merge(): IOptions {
         if (this.options) {
-            if (this.options.upload) {
-                this.options.upload = Object.assign({}, this.defaultOptions.upload, this.options.upload);
-            }
-
-            if (this.options.cache) {
-                this.options.cache = Object.assign({}, this.defaultOptions.cache, this.options.cache);
-            }
-
-            if (this.options.classes) {
-                this.options.classes = Object.assign({}, this.defaultOptions.classes, this.options.classes);
-            }
-
-            if (this.options.keymap) {
-                this.options.keymap = Object.assign({}, this.defaultOptions.keymap, this.options.keymap);
-            }
-
-            if (this.options.preview) {
-                if (this.options.preview.hljs) {
-                    this.options.preview.hljs =
-                        Object.assign({}, this.defaultOptions.preview.hljs, this.options.preview.hljs);
-                }
-                if (this.options.preview.math) {
-                    this.options.preview.math =
-                        Object.assign({}, this.defaultOptions.preview.math, this.options.preview.math);
-                }
-                if (this.options.preview.markdown) {
-                    this.options.preview.markdown =
-                        Object.assign({}, this.defaultOptions.preview.markdown, this.options.preview.markdown);
-                }
-                this.options.preview = Object.assign({}, this.defaultOptions.preview, this.options.preview);
-            }
-
-            if (this.options.hint) {
-                this.options.hint = Object.assign({}, this.defaultOptions.hint, this.options.hint);
-            }
-
-            if (this.options.resize) {
-                this.options.resize = Object.assign({}, this.defaultOptions.resize, this.options.resize);
-            }
-
-            if (this.options.counter) {
-                this.options.counter = Object.assign({}, this.defaultOptions.counter, this.options.counter);
-            }
-
-            if (this.options.toolbarConfig) {
-                this.options.toolbarConfig =
-                    Object.assign({}, this.defaultOptions.toolbarConfig, this.options.toolbarConfig);
-            }
-
             if (this.options.toolbar) {
                 this.options.toolbar = this.mergeToolbar(this.options.toolbar);
             } else {
                 this.options.toolbar = this.mergeToolbar(this.defaultOptions.toolbar);
             }
+            if (this.options.preview?.theme?.list) {
+                this.defaultOptions.preview.theme.list = this.options.preview.theme.list;
+            }
+            if (this.options.hint?.emoji) {
+                this.defaultOptions.hint.emoji = this.options.hint.emoji;
+            }
         }
 
-        const mergedOptions = Object.assign({}, this.defaultOptions, this.options);
+        const mergedOptions = merge(this.defaultOptions, this.options);
 
         if (mergedOptions.cache.enable && !mergedOptions.cache.id) {
             throw new Error("need options.cache.id, see https://hacpai.com/article/1549638745630#options");
@@ -436,10 +177,200 @@ export class Options {
     }
 
     private mergeToolbar(toolbar: Array<string | IMenuItem>) {
+        const toolbarItem = [{
+            icon: exportSVG,
+            name: "export",
+            tipPosition: "ne",
+        }, {
+            hotkey: "⌘-E",
+            icon: emojiSVG,
+            name: "emoji",
+            tipPosition: "ne",
+        }, {
+            hotkey: "⌘-H",
+            icon: headingsSVG,
+            name: "headings",
+            tipPosition: "ne",
+        }, {
+            hotkey: "⌘-B",
+            icon: boldSVG,
+            name: "bold",
+            prefix: "**",
+            suffix: "**",
+            tipPosition: "ne",
+        }, {
+            hotkey: "⌘-I",
+            icon: italicSVG,
+            name: "italic",
+            prefix: "*",
+            suffix: "*",
+            tipPosition: "ne",
+        }, {
+            hotkey: "⌘-S",
+            icon: strikekSVG,
+            name: "strike",
+            prefix: "~~",
+            suffix: "~~",
+            tipPosition: "ne",
+        }, {
+            hotkey: "⌘-K",
+            icon: linkSVG,
+            name: "link",
+            prefix: "[",
+            suffix: "](https://)",
+            tipPosition: "n",
+        }, {
+            name: "|",
+        }, {
+            hotkey: "⌘-L",
+            icon: listSVG,
+            name: "list",
+            prefix: "* ",
+            tipPosition: "n",
+        }, {
+            hotkey: "⌘-O",
+            icon: orderedListVG,
+            name: "ordered-list",
+            prefix: "1. ",
+            tipPosition: "n",
+        }, {
+            hotkey: "⌘-J",
+            icon: checkSVG,
+            name: "check",
+            prefix: "* [ ] ",
+            tipPosition: "n",
+        }, {
+            hotkey: "⌘-⇧-I",
+            icon: outdentSVG,
+            name: "outdent",
+            tipPosition: "n",
+        }, {
+            hotkey: "⌘-⇧-O",
+            icon: indentSVG,
+            name: "indent",
+            tipPosition: "n",
+        }, {
+            name: "|",
+        }, {
+            hotkey: "⌘-;",
+            icon: quoteSVG,
+            name: "quote",
+            prefix: "> ",
+            tipPosition: "n",
+        }, {
+            hotkey: "⌘-⇧-H",
+            icon: lineSVG,
+            name: "line",
+            prefix: "---",
+            tipPosition: "n",
+        }, {
+            hotkey: "⌘-U",
+            icon: codeSVG,
+            name: "code",
+            prefix: "```",
+            suffix: "\n```",
+            tipPosition: "n",
+        }, {
+            hotkey: "⌘-G",
+            icon: inlineCodeSVG,
+            name: "inline-code",
+            prefix: "`",
+            suffix: "`",
+            tipPosition: "n",
+        }, {
+            hotkey: "⌘-⇧-B",
+            icon: insertBeforeSVG,
+            name: "insert-before",
+            tipPosition: "n",
+        }, {
+            hotkey: "⌘-⇧-E",
+            icon: insertAfterSVG,
+            name: "insert-after",
+            tipPosition: "n",
+        }, {
+            name: "|",
+        }, {
+            icon: uploadSVG,
+            name: "upload",
+            tipPosition: "n",
+        }, {
+            icon: recordSVG,
+            name: "record",
+            tipPosition: "n",
+        }, {
+            hotkey: "⌘-M",
+            icon: tableSVG,
+            name: "table",
+            prefix: "| col1",
+            suffix: " | col2 | col3 |\n| --- | --- | --- |\n|  |  |  |\n|  |  |  |",
+            tipPosition: "n",
+        }, {
+            name: "|",
+        }, {
+            hotkey: "⌘-Z",
+            icon: undoSVG,
+            name: "undo",
+            tipPosition: "nw",
+        }, {
+            hotkey: "⌘-Y",
+            icon: redoSVG,
+            name: "redo",
+            tipPosition: "nw",
+        }, {
+            name: "|",
+        }, {
+            icon: moreSVG,
+            name: "more",
+            tipPosition: "e",
+        }, {
+            hotkey: "⌘-'",
+            icon: fullscreenSVG,
+            name: "fullscreen",
+            tipPosition: "nw",
+        }, {
+            icon: editSVG,
+            name: "edit-mode",
+            tipPosition: "nw",
+        }, {
+            hotkey: "⌘-P",
+            icon: bothSVG,
+            name: "both",
+            tipPosition: "nw",
+        }, {
+            icon: previewSVG,
+            name: "preview",
+            tipPosition: "nw",
+        }, {
+            icon: outlinerSVG,
+            name: "outline",
+            tipPosition: "nw",
+        }, {
+            icon: contentThemeSVG,
+            name: "content-theme",
+            tipPosition: "nw",
+        }, {
+            icon: codeThemeSVG,
+            name: "code-theme",
+            tipPosition: "nw",
+        }, {
+            icon: bugSVG,
+            name: "devtools",
+            tipPosition: "nw",
+        }, {
+            icon: infoSVG,
+            name: "info",
+            tipPosition: "nw",
+        }, {
+            icon: helpSVG,
+            name: "help",
+            tipPosition: "nw",
+        }, {
+            name: "br",
+        }];
         const toolbarResult: IMenuItem[] = [];
         toolbar.forEach((menuItem: IMenuItem) => {
             let currentMenuItem = menuItem;
-            this.toolbarItem.forEach((defaultMenuItem: IMenuItem) => {
+            toolbarItem.forEach((defaultMenuItem: IMenuItem) => {
                 if (typeof menuItem === "string" && defaultMenuItem.name === menuItem) {
                     currentMenuItem = defaultMenuItem;
                 }

+ 0 - 0
src/ts/toolbar/RecordMedia.ts → src/ts/util/RecordMedia.ts


+ 8 - 0
src/ts/util/compatibility.ts

@@ -6,6 +6,14 @@ export const isFirefox = () => {
     return navigator.userAgent.toLowerCase().indexOf("firefox") > -1;
 };
 
+export const accessLocalStorage = () => {
+    try {
+       return typeof localStorage !== "undefined";
+    } catch (e) {
+        return false;
+    }
+};
+
 // 用户 iPhone 点击延迟/需要双击的处理
 export const getEventName = () => {
     if (navigator.userAgent.indexOf("iPhone") > -1) {

+ 30 - 36
src/ts/util/editorCommonEvent.ts

@@ -1,16 +1,17 @@
 import {processHeading} from "../ir/process";
 import {processKeydown as irProcessKeydown} from "../ir/processKeydown";
 import {getMarkdown} from "../markdown/getMarkdown";
-import {getSelectText} from "../sv/getSelectText";
-import {insertText} from "../sv/insertText";
+import {processHeading as processHeadingSV} from "../sv/process";
 import {processKeydown as mdProcessKeydown} from "../sv/processKeydown";
 import {setEditMode} from "../toolbar/EditMode";
 import {hidePanel} from "../toolbar/setToolbar";
+import {uploadFiles} from "../upload";
 import {getCursorPosition} from "../util/selection";
 import {afterRenderEvent} from "../wysiwyg/afterRenderEvent";
 import {processKeydown} from "../wysiwyg/processKeydown";
 import {removeHeading, setHeading} from "../wysiwyg/setHeading";
 import {getEventName, isCtrl} from "./compatibility";
+import {getSelectText} from "./getSelectText";
 import {hasClosestByMatchTag} from "./hasClosest";
 import {matchHotKey} from "./hotKey";
 
@@ -31,6 +32,22 @@ export const blurEvent = (vditor: IVditor, editorElement: HTMLElement) => {
     });
 };
 
+export const dropEvent = (vditor: IVditor, editorElement: HTMLElement) => {
+    if (vditor.options.upload.url || vditor.options.upload.handler) {
+        editorElement.addEventListener("drop",
+            (event: CustomEvent & { dataTransfer?: DataTransfer, target: HTMLElement }) => {
+                if (event.dataTransfer.types[0] !== "Files") {
+                    return;
+                }
+                const files = event.dataTransfer.items;
+                if (files.length > 0) {
+                    uploadFiles(vditor, files);
+                }
+                event.preventDefault();
+            });
+    }
+};
+
 export const scrollCenter = (vditor: IVditor) => {
     if (!vditor.options.typewriterMode) {
         return;
@@ -74,46 +91,25 @@ export const hotkeyEvent = (vditor: IVditor, editorElement: HTMLElement) => {
         }
 
         // undo
-        if (matchHotKey("⌘-Z", event)) {
-            if (vditor.currentMode === "sv" && !vditor.toolbar.elements.undo) {
-                vditor.undo.undo(vditor);
-                event.preventDefault();
-                return;
-            } else if (vditor.currentMode === "wysiwyg" && !vditor.toolbar.elements.undo) {
-                vditor.wysiwygUndo.undo(vditor);
-                event.preventDefault();
-                return;
-            } else if (vditor.currentMode === "ir") {
-                vditor.irUndo.undo(vditor);
-                event.preventDefault();
-                return;
-            }
+        if (matchHotKey("⌘-Z", event) && !vditor.toolbar.elements.undo) {
+            vditor.undo.undo(vditor);
+            event.preventDefault();
+            return;
         }
 
         // redo
-        if (matchHotKey("⌘-Y", event)) {
-            if (vditor.currentMode === "sv" && !vditor.toolbar.elements.redo) {
-                vditor.undo.redo(vditor);
-                event.preventDefault();
-                return;
-            } else if (vditor.currentMode === "wysiwyg" && !vditor.toolbar.elements.redo) {
-                vditor.wysiwygUndo.redo(vditor);
-                event.preventDefault();
-                return;
-            } else if (vditor.currentMode === "ir") {
-                vditor.irUndo.redo(vditor);
-                event.preventDefault();
-                return;
-            }
+        if (matchHotKey("⌘-Y", event) && !vditor.toolbar.elements.redo) {
+            vditor.undo.redo(vditor);
+            event.preventDefault();
+            return;
         }
 
         // esc
         if (event.key === "Escape") {
-            if (vditor.options.esc) {
-                vditor.options.esc(getMarkdown(vditor));
-            }
             if (vditor.hint.element.style.display === "block") {
                 vditor.hint.element.style.display = "none";
+            } else if (vditor.options.esc) {
+                vditor.options.esc(getMarkdown(vditor));
             }
             event.preventDefault();
             return;
@@ -130,9 +126,7 @@ export const hotkeyEvent = (vditor: IVditor, editorElement: HTMLElement) => {
                 }
                 afterRenderEvent(vditor);
             } else if (vditor.currentMode === "sv") {
-                insertText(vditor,
-                    "#".repeat(parseInt(event.code.replace("Digit", ""), 10)) + " ",
-                    "", false, true);
+                processHeadingSV(vditor, "#".repeat(parseInt(event.code.replace("Digit", ""), 10)) + " ");
             } else if (vditor.currentMode === "ir") {
                 processHeading(vditor, "#".repeat(parseInt(event.code.replace("Digit", ""), 10)) + " ");
             }

+ 231 - 142
src/ts/util/fixBrowserBehavior.ts

@@ -1,11 +1,12 @@
 import {Constants} from "../constants";
-import {highlightToolbar as highlightToolbarIR} from "../ir/highlightToolbar";
+import {input as IRInput} from "../ir/input";
 import {processAfterRender} from "../ir/process";
+import {processAfterRender as processSVAfterRender} from "../sv/process";
 import {uploadFiles} from "../upload";
 import {setHeaders} from "../upload/setHeaders";
 import {processCodeRender, processPasteCode} from "../util/processCode";
 import {afterRenderEvent} from "../wysiwyg/afterRenderEvent";
-import {highlightToolbar} from "../wysiwyg/highlightToolbar";
+import {input} from "../wysiwyg/input";
 import {isCtrl, isFirefox} from "./compatibility";
 import {scrollCenter} from "./editorCommonEvent";
 import {
@@ -17,6 +18,7 @@ import {
 } from "./hasClosest";
 import {getLastNode} from "./hasClosest";
 import {hasClosestByHeadings} from "./hasClosestByHeadings";
+import {highlightToolbar} from "./highlightToolbar";
 import {matchHotKey} from "./hotKey";
 import {
     getEditorRange,
@@ -26,14 +28,27 @@ import {
     setSelectionByPosition, setSelectionFocus,
 } from "./selection";
 
+// https://github.com/Vanessa219/vditor/issues/508 软键盘无法删除空块
+export const fixGSKeyBackspace = (event: KeyboardEvent, vditor: IVditor, startContainer: Node) => {
+    if (event.keyCode === 229 && event.code === "" && event.key === "Unidentified" && vditor.currentMode !== "sv") {
+        const blockElement = hasClosestBlock(startContainer);
+        // 移动端的标点符号都显示为 299,因此需限定为空删除的条件
+        if (blockElement && blockElement.textContent.trim() === "") {
+            vditor[vditor.currentMode].composingLock = true;
+            return false;
+        }
+    }
+    return true;
+};
+
 // https://github.com/Vanessa219/vditor/issues/361
-export const fixCJKPosition = (range: Range, event: KeyboardEvent) => {
+export const fixCJKPosition = (range: Range, vditor: IVditor, event: KeyboardEvent) => {
     if (event.key === "Enter" || event.key === "Tab" || event.key === "Backspace" || event.key.indexOf("Arrow") > -1
         || isCtrl(event) || event.key === "Escape" || event.shiftKey || event.altKey) {
         return;
     }
     const pElement = hasClosestByMatchTag(range.startContainer, "P");
-    if (pElement && getSelectPosition(pElement, range).start === 0) {
+    if (pElement && getSelectPosition(pElement, vditor[vditor.currentMode].element, range).start === 0) {
         const zwspNode = document.createTextNode(Constants.ZWSP);
         range.insertNode(zwspNode);
         range.setStartAfter(zwspNode);
@@ -61,11 +76,7 @@ export const insertEmptyBlock = (vditor: IVditor, position: InsertPosition) => {
     if (blockElement) {
         blockElement.insertAdjacentHTML(position, `<p data-block="0">${Constants.ZWSP}<wbr>\n</p>`);
         setRangeByWbr(vditor[vditor.currentMode].element, range);
-        if (vditor.currentMode === "ir") {
-            highlightToolbarIR(vditor);
-        } else {
-            highlightToolbar(vditor);
-        }
+        highlightToolbar(vditor);
         execAfterRender(vditor);
     }
 };
@@ -112,7 +123,7 @@ const goPreviousCell = (cellElement: HTMLElement, range: Range, isSelected = tru
 
 export const insertAfterBlock = (vditor: IVditor, event: KeyboardEvent, range: Range, element: HTMLElement,
                                  blockElement: HTMLElement) => {
-    const position = getSelectPosition(element, range);
+    const position = getSelectPosition(element, vditor[vditor.currentMode].element, range);
     if ((event.key === "ArrowDown" && element.textContent.trimRight().substr(position.start).indexOf("\n") === -1) ||
         (event.key === "ArrowRight" && position.start >= element.textContent.trimRight().length)) {
         const nextElement = blockElement.nextElementSibling;
@@ -134,7 +145,7 @@ export const insertAfterBlock = (vditor: IVditor, event: KeyboardEvent, range: R
 
 export const insertBeforeBlock = (vditor: IVditor, event: KeyboardEvent, range: Range, element: HTMLElement,
                                   blockElement: HTMLElement) => {
-    const position = getSelectPosition(element, range);
+    const position = getSelectPosition(element, vditor[vditor.currentMode].element, range);
     if ((event.key === "ArrowUp" && element.textContent.substr(position.start).indexOf("\n") === -1) ||
         ((event.key === "ArrowLeft" || event.key === "Backspace") && position.start === 0)) {
         const previousElement = blockElement.previousElementSibling;
@@ -257,9 +268,7 @@ export const listIndent = (vditor: IVditor, liElement: HTMLElement, range: Range
                 });
         }
         execAfterRender(vditor);
-        if (vditor.currentMode === "wysiwyg") {
-            highlightToolbar(vditor);
-        }
+        highlightToolbar(vditor);
     } else {
         vditor[vditor.currentMode].element.focus();
     }
@@ -314,9 +323,7 @@ export const listOutdent = (vditor: IVditor, liElement: HTMLElement, range: Rang
                 });
         }
         execAfterRender(vditor);
-        if (vditor.currentMode === "wysiwyg") {
-            highlightToolbar(vditor);
-        }
+        highlightToolbar(vditor);
     } else {
         vditor[vditor.currentMode].element.focus();
     }
@@ -368,7 +375,10 @@ export const isHrMD = (text: string) => {
     return false;
 };
 
-export const isHeadingMD = (text: string) => {
+export const isHeadingMD = (text: string, setext: boolean) => {
+    if (!setext) {
+        return false;
+    }
     // - =
     const textArray = text.trimRight().split("\n");
     text = textArray.pop();
@@ -414,11 +424,17 @@ export const renderToc = (vditor: IVditor) => {
     tocElement.innerHTML = tocHTML || "[ToC]";
 };
 
-export const execAfterRender = (vditor: IVditor) => {
+export const execAfterRender = (vditor: IVditor, options = {
+    enableAddUndoStack: true,
+    enableHint: false,
+    enableInput: true,
+}) => {
     if (vditor.currentMode === "wysiwyg") {
-        afterRenderEvent(vditor);
+        afterRenderEvent(vditor, options);
     } else if (vditor.currentMode === "ir") {
-        processAfterRender(vditor);
+        processAfterRender(vditor, options);
+    } else if (vditor.currentMode === "sv") {
+        processSVAfterRender(vditor, options);
     }
 };
 
@@ -433,7 +449,7 @@ export const fixList = (range: Range, vditor: IVditor, pElement: HTMLElement | f
                 // li 结尾需 \n
                 liElement.insertAdjacentText("beforeend", "\n");
             }
-            range.insertNode(document.createTextNode("\n"));
+            range.insertNode(document.createTextNode("\n\n"));
             range.collapse(false);
             execAfterRender(vditor);
             event.preventDefault();
@@ -442,7 +458,7 @@ export const fixList = (range: Range, vditor: IVditor, pElement: HTMLElement | f
 
         if (!isCtrl(event) && !event.shiftKey && !event.altKey && event.key === "Backspace" &&
             !liElement.previousElementSibling && range.toString() === "" &&
-            getSelectPosition(liElement, range).start === 0) {
+            getSelectPosition(liElement, vditor[vditor.currentMode].element, range).start === 0) {
             // 光标位于点和第一个字符中间时,无法删除 li 元素
             if (liElement.nextElementSibling) {
                 liElement.parentElement.insertAdjacentHTML("beforebegin",
@@ -501,6 +517,7 @@ export const fixTab = (vditor: IVditor, range: Range, event: KeyboardEvent) => {
                 range.collapse(false);
             }
         }
+        setSelectionFocus(range);
         execAfterRender(vditor);
         event.preventDefault();
         return true;
@@ -518,7 +535,7 @@ export const fixMarkdown = (event: KeyboardEvent, vditor: IVditor, pElement: HTM
             // table 自动完成
             let tableHeaderMD = pTextList.map(() => "---").join("|");
             tableHeaderMD =
-                pElement.textContent + tableHeaderMD.substring(3, tableHeaderMD.length - 3) + "\n|<wbr>";
+                pElement.textContent + "\n" + tableHeaderMD.substring(3, tableHeaderMD.length - 3) + "\n|<wbr>";
             pElement.outerHTML = vditor.lute.SpinVditorDOM(tableHeaderMD);
             setRangeByWbr(vditor[vditor.currentMode].element, range);
             execAfterRender(vditor);
@@ -538,7 +555,7 @@ export const fixMarkdown = (event: KeyboardEvent, vditor: IVditor, pElement: HTM
             }
 
             pElement.insertAdjacentHTML("afterend",
-                `${pInnerHTML}<hr data-block="0"><p data-block="0">\n<wbr></p>`);
+                `${pInnerHTML}<hr data-block="0"><p data-block="0"><wbr>\n</p>`);
             pElement.remove();
             setRangeByWbr(vditor[vditor.currentMode].element, range);
             execAfterRender(vditor);
@@ -547,9 +564,9 @@ export const fixMarkdown = (event: KeyboardEvent, vditor: IVditor, pElement: HTM
             return true;
         }
 
-        if (isHeadingMD(pElement.innerHTML)) {
+        if (isHeadingMD(pElement.innerHTML, vditor.options.preview.markdown.setext)) {
             // heading 渲染
-            pElement.outerHTML = vditor.lute.SpinVditorDOM(pElement.innerHTML + '<p data-block="0">\n<wbr></p>');
+            pElement.outerHTML = vditor.lute.SpinVditorDOM(pElement.innerHTML + '<p data-block="0"><wbr>\n</p>');
             setRangeByWbr(vditor[vditor.currentMode].element, range);
             execAfterRender(vditor);
             scrollCenter(vditor);
@@ -561,7 +578,7 @@ export const fixMarkdown = (event: KeyboardEvent, vditor: IVditor, pElement: HTM
     // 软换行会被切割 https://github.com/Vanessa219/vditor/issues/220
     if (pElement.previousElementSibling && event.key === "Backspace" && !isCtrl(event) && !event.altKey &&
         !event.shiftKey && pElement.textContent.trimRight().split("\n").length > 1 &&
-        getSelectPosition(pElement, range).start === 0) {
+        getSelectPosition(pElement, vditor[vditor.currentMode].element, range).start === 0) {
         const lastElement = getLastNode(pElement.previousElementSibling) as HTMLElement;
         if (!lastElement.textContent.endsWith("\n")) {
             lastElement.textContent = lastElement.textContent + "\n";
@@ -574,6 +591,87 @@ export const fixMarkdown = (event: KeyboardEvent, vditor: IVditor, pElement: HTM
     return false;
 };
 
+export const insertRow = (vditor: IVditor, range: Range, cellElement: HTMLElement) => {
+    let rowHTML = "";
+    for (let m = 0; m < cellElement.parentElement.childElementCount; m++) {
+        rowHTML += `<td>${m === 0 ? " <wbr>" : " "}</td>`;
+    }
+    if (cellElement.tagName === "TH") {
+        cellElement.parentElement.parentElement.insertAdjacentHTML("afterend",
+            `<tbody><tr>${rowHTML}</tr></tbody>`);
+    } else {
+        cellElement.parentElement.insertAdjacentHTML("afterend", `<tr>${rowHTML}</tr>`);
+    }
+
+    setRangeByWbr(vditor[vditor.currentMode].element, range);
+    execAfterRender(vditor);
+    scrollCenter(vditor);
+};
+
+export const insertColumn = (vditor: IVditor, tableElement: HTMLTableElement, cellElement: HTMLElement) => {
+    let index = 0;
+    let previousElement = cellElement.previousElementSibling;
+    while (previousElement) {
+        index++;
+        previousElement = previousElement.previousElementSibling;
+    }
+    for (let i = 0; i < tableElement.rows.length; i++) {
+        if (i === 0) {
+            tableElement.rows[i].cells[index].insertAdjacentHTML("afterend", "<th> </th>");
+        } else {
+            tableElement.rows[i].cells[index].insertAdjacentHTML("afterend", "<td> </td>");
+        }
+    }
+
+    execAfterRender(vditor);
+};
+
+export const deleteRow = (vditor: IVditor, range: Range, cellElement: HTMLElement) => {
+    if (cellElement.tagName === "TD") {
+        const tbodyElement = cellElement.parentElement.parentElement;
+        if (cellElement.parentElement.previousElementSibling) {
+            range.selectNodeContents(cellElement.parentElement.previousElementSibling.lastElementChild);
+        } else {
+            range.selectNodeContents(tbodyElement.previousElementSibling.lastElementChild.lastElementChild);
+        }
+
+        if (tbodyElement.childElementCount === 1) {
+            tbodyElement.remove();
+        } else {
+            cellElement.parentElement.remove();
+        }
+
+        range.collapse(false);
+        setSelectionFocus(range);
+        execAfterRender(vditor);
+    }
+};
+
+export const deleteColumn =
+    (vditor: IVditor, range: Range, tableElement: HTMLTableElement, cellElement: HTMLElement) => {
+        let index = 0;
+        let previousElement = cellElement.previousElementSibling;
+        while (previousElement) {
+            index++;
+            previousElement = previousElement.previousElementSibling;
+        }
+        if (cellElement.previousElementSibling || cellElement.nextElementSibling) {
+            range.selectNodeContents(cellElement.previousElementSibling || cellElement.nextElementSibling);
+            range.collapse(true);
+        }
+        for (let i = 0; i < tableElement.rows.length; i++) {
+            const cells = tableElement.rows[i].cells;
+            if (cells.length === 1) {
+                tableElement.remove();
+                highlightToolbar(vditor);
+                break;
+            }
+            cells[index].remove();
+        }
+        setSelectionFocus(range);
+        execAfterRender(vditor);
+    };
+
 export const fixTable = (vditor: IVditor, event: KeyboardEvent, range: Range) => {
     const startContainer = range.startContainer;
     const cellElement = hasClosestByMatchTag(startContainer, "TD") ||
@@ -687,49 +785,6 @@ export const fixTable = (vditor: IVditor, event: KeyboardEvent, range: Range) =>
             return true;
         }
 
-        // 删除当前行
-        if (matchHotKey("⌘--", event)) {
-            if (cellElement.tagName === "TD") {
-                const tbodyElement = cellElement.parentElement.parentElement;
-                if (cellElement.parentElement.previousElementSibling) {
-                    range.selectNodeContents(cellElement.parentElement.previousElementSibling.lastElementChild);
-                } else {
-                    range.selectNodeContents(tbodyElement.previousElementSibling.lastElementChild.lastElementChild);
-                }
-
-                if (tbodyElement.childElementCount === 1) {
-                    tbodyElement.remove();
-                } else {
-                    cellElement.parentElement.remove();
-                }
-
-                range.collapse(false);
-                execAfterRender(vditor);
-            }
-            event.preventDefault();
-            return true;
-        }
-
-        // 下方新添加一行 https://github.com/Vanessa219/vditor/issues/46
-        if (matchHotKey("⌘-=", event)) {
-            let rowHTML = "";
-            for (let m = 0; m < cellElement.parentElement.childElementCount; m++) {
-                rowHTML += `<td>${m === 0 ? " <wbr>" : " "}</td>`;
-            }
-            if (cellElement.tagName === "TH") {
-                cellElement.parentElement.parentElement.insertAdjacentHTML("afterend",
-                    `<tbody><tr>${rowHTML}</tr></tbody>`);
-            } else {
-                cellElement.parentElement.insertAdjacentHTML("afterend", `<tr>${rowHTML}</tr>`);
-            }
-
-            setRangeByWbr(vditor[vditor.currentMode].element, range);
-            execAfterRender(vditor);
-            scrollCenter(vditor);
-            event.preventDefault();
-            return true;
-        }
-
         // focus row input, only wysiwyg
         if (vditor.currentMode === "wysiwyg" &&
             !isCtrl(event) && event.key === "Enter" && !event.shiftKey && event.altKey) {
@@ -745,56 +800,43 @@ export const fixTable = (vditor: IVditor, event: KeyboardEvent, range: Range) =>
             && range.startOffset === 0 && range.toString() === "") {
             const previousCellElement = goPreviousCell(cellElement, range, false);
             if (!previousCellElement && tableElement) {
-                tableElement.outerHTML = `<p data-block="0"><wbr>${tableElement.textContent.trim()}</p>`;
-                setRangeByWbr(vditor[vditor.currentMode].element, range);
+                if (tableElement.textContent.trim() === "") {
+                    tableElement.outerHTML = `<p data-block="0"><wbr>\n</p>`;
+                    setRangeByWbr(vditor[vditor.currentMode].element, range);
+                } else {
+                    range.setStartBefore(tableElement);
+                    range.collapse(true);
+                }
                 execAfterRender(vditor);
             }
             event.preventDefault();
             return true;
         }
 
+        // 下方新添加一行 https://github.com/Vanessa219/vditor/issues/46
+        if (matchHotKey("⌘-=", event)) {
+            insertRow(vditor, range, cellElement);
+            event.preventDefault();
+            return true;
+        }
+
         // 后方新添加一列
         if (matchHotKey("⌘-⇧-=", event)) {
-            let index = 0;
-            let previousElement = cellElement.previousElementSibling;
-            while (previousElement) {
-                index++;
-                previousElement = previousElement.previousElementSibling;
-            }
-            for (let i = 0; i < tableElement.rows.length; i++) {
-                if (i === 0) {
-                    tableElement.rows[i].cells[index].insertAdjacentHTML("afterend", "<th> </th>");
-                } else {
-                    tableElement.rows[i].cells[index].insertAdjacentHTML("afterend", "<td> </td>");
-                }
-            }
+            insertColumn(vditor, tableElement, cellElement);
+            event.preventDefault();
+            return true;
+        }
 
-            execAfterRender(vditor);
+        // 删除当前行
+        if (matchHotKey("⌘--", event)) {
+            deleteRow(vditor, range, cellElement);
             event.preventDefault();
             return true;
         }
 
         // 删除当前列
         if (matchHotKey("⌘-⇧--", event)) {
-            let index = 0;
-            let previousElement = cellElement.previousElementSibling;
-            while (previousElement) {
-                index++;
-                previousElement = previousElement.previousElementSibling;
-            }
-            if (cellElement.previousElementSibling || cellElement.nextElementSibling) {
-                range.selectNodeContents(cellElement.previousElementSibling || cellElement.nextElementSibling);
-                range.collapse(true);
-            }
-            for (let i = 0; i < tableElement.rows.length; i++) {
-                const cells = tableElement.rows[i].cells;
-                if (cells.length === 1) {
-                    tableElement.remove();
-                    break;
-                }
-                cells[index].remove();
-            }
-            execAfterRender(vditor);
+            deleteColumn(vditor, range, tableElement, cellElement);
             event.preventDefault();
             return true;
         }
@@ -872,7 +914,7 @@ export const fixCodeBlock = (vditor: IVditor, event: KeyboardEvent, codeRenderEl
 
     // Backspace: 光标位于第零个字符,仅删除代码块标签
     if (event.key === "Backspace" && !isCtrl(event) && !event.shiftKey && !event.altKey) {
-        const codePosition = getSelectPosition(codeRenderElement, range);
+        const codePosition = getSelectPosition(codeRenderElement, vditor[vditor.currentMode].element, range);
         if ((codePosition.start === 0 ||
             (codePosition.start === 1 && codeRenderElement.innerText === "\n")) // 空代码块,光标在 \n 后
             && range.toString() === "") {
@@ -893,8 +935,17 @@ export const fixCodeBlock = (vditor: IVditor, event: KeyboardEvent, codeRenderEl
         range.insertNode(document.createTextNode("\n"));
         range.collapse(false);
         setSelectionFocus(range);
-        execAfterRender(vditor);
-        scrollCenter(vditor);
+        if (codeRenderElement.firstElementChild.classList.contains("language-mindmap")) {
+            // 脑图换行需要渲染
+            if (vditor.currentMode === "wysiwyg") {
+                input(vditor, range);
+            } else {
+                IRInput(vditor, range);
+            }
+        } else {
+            execAfterRender(vditor);
+            scrollCenter(vditor);
+        }
         event.preventDefault();
         return true;
     }
@@ -906,7 +957,7 @@ export const fixBlockquote = (vditor: IVditor, range: Range, event: KeyboardEven
     const blockquoteElement = hasClosestByMatchTag(startContainer, "BLOCKQUOTE");
     if (blockquoteElement && range.toString() === "") {
         if (event.key === "Backspace" && !isCtrl(event) && !event.shiftKey && !event.altKey &&
-            getSelectPosition(blockquoteElement, range).start === 0) {
+            getSelectPosition(blockquoteElement, vditor[vditor.currentMode].element, range).start === 0) {
             // Backspace: 光标位于引用中的第零个字符,仅删除引用标签
             range.insertNode(document.createElement("wbr"));
             blockquoteElement.outerHTML = blockquoteElement.innerHTML;
@@ -920,12 +971,14 @@ export const fixBlockquote = (vditor: IVditor, range: Range, event: KeyboardEven
             && pElement.parentElement.tagName === "BLOCKQUOTE") {
             // Enter: 空行回车应逐层跳出
             let isEmpty = false;
-            if (pElement.innerHTML.replace(Constants.ZWSP, "") === "\n") {
+            if (pElement.innerHTML.replace(Constants.ZWSP, "") === "\n" ||
+                pElement.innerHTML.replace(Constants.ZWSP, "") === "") {
                 // 空 P
                 isEmpty = true;
                 pElement.remove();
             } else if (pElement.innerHTML.endsWith("\n\n") &&
-                getSelectPosition(pElement, range).start === pElement.textContent.length - 1) {
+                getSelectPosition(pElement, vditor[vditor.currentMode].element, range).start ===
+                pElement.textContent.length - 1) {
                 // 软换行
                 pElement.innerHTML = pElement.innerHTML.substr(0, pElement.innerHTML.length - 2);
                 isEmpty = true;
@@ -1031,11 +1084,13 @@ export const fixTask = (vditor: IVditor, range: Range, event: KeyboardEvent) =>
                             startAttribute = taskItemElement.parentElement.tagName === "UL" ? "" : ` start="1"`;
                             beforeHTML = `<${parentTagName} data-tight="true"${dataMarker} data-block="0">${beforeHTML}</${parentTagName}>`;
                         }
-                        taskItemElement.parentElement.outerHTML = `${beforeHTML}<p data-block="0">\n<wbr></p><${parentTagName}
+                        // <p data-block="0">\n<wbr></p> => <p data-block="0"><wbr>\n</p>
+                        // https://github.com/Vanessa219/vditor/issues/430
+                        taskItemElement.parentElement.outerHTML = `${beforeHTML}<p data-block="0"><wbr>\n</p><${parentTagName}
  data-tight="true"${dataMarker} data-block="0"${startAttribute}>${afterHTML}</${parentTagName}>`;
                     } else {
                         // 任务列表下方无任务列表元素
-                        taskItemElement.parentElement.insertAdjacentHTML("afterend", `<p data-block="0">\n<wbr></p>`);
+                        taskItemElement.parentElement.insertAdjacentHTML("afterend", `<p data-block="0"><wbr>\n</p>`);
                         if (taskItemElement.parentElement.querySelectorAll("li").length === 1) {
                             // 任务列表仅有一项时,使用 p 元素替换
                             taskItemElement.parentElement.remove();
@@ -1079,7 +1134,7 @@ export const fixDelete = (vditor: IVditor, range: Range, event: KeyboardEvent, p
 
     if (pElement) {
         const previousElement = pElement.previousElementSibling;
-        if (previousElement && getSelectPosition(pElement, range).start === 0 &&
+        if (previousElement && getSelectPosition(pElement, vditor[vditor.currentMode].element, range).start === 0 &&
             ((isFirefox() && previousElement.tagName === "HR") || previousElement.tagName === "TABLE")) {
             if (previousElement.tagName === "TABLE") {
                 // table 后删除 https://github.com/Vanessa219/vditor/issues/243
@@ -1113,8 +1168,8 @@ export const fixFirefoxArrowUpTable = (event: KeyboardEvent, blockElement: false
         return false;
     }
     if (event.key === "ArrowUp" && blockElement && blockElement.previousElementSibling?.tagName === "TABLE") {
-        const tableElement = blockElement.previousElementSibling as HTMLTableElement
-        range.selectNodeContents(tableElement.rows[tableElement.rows.length - 1].lastElementChild)
+        const tableElement = blockElement.previousElementSibling as HTMLTableElement;
+        range.selectNodeContents(tableElement.rows[tableElement.rows.length - 1].lastElementChild);
         range.collapse(false);
         event.preventDefault();
         return true;
@@ -1134,17 +1189,19 @@ export const paste = (vditor: IVditor, event: ClipboardEvent & { target: HTMLEle
     event.stopPropagation();
     event.preventDefault();
     let textHTML = event.clipboardData.getData("text/html");
-    const textPlain = event.clipboardData.getData("text/plain");
+    let textPlain = event.clipboardData.getData("text/plain");
     const renderers: {
         HTML2VditorDOM?: ILuteRender,
         HTML2VditorIRDOM?: ILuteRender,
+        HTML2VditorSVDOM?: ILuteRender,
         Md2VditorDOM?: ILuteRender,
         Md2VditorIRDOM?: ILuteRender,
+        Md2VditorSVDOM?: ILuteRender,
     } = {};
     const renderLinkDest: ILuteRenderCallback = (node) => {
         const src = node.TokensStr();
-        if (node.__internal_object__.Parent.Type === 34 && src
-            && src.indexOf("file://") === -1 && vditor.options.upload.linkToImgUrl) {
+        if (node.__internal_object__.Parent.Type === 34 && src && src.indexOf("file://") === -1 &&
+            vditor.options.upload.linkToImgUrl) {
             const xhr = new XMLHttpRequest();
             xhr.open("POST", vditor.options.upload.linkToImgUrl);
             setHeaders(vditor, xhr);
@@ -1157,12 +1214,21 @@ export const paste = (vditor: IVditor, event: ClipboardEvent & { target: HTMLEle
                             return;
                         }
                         const original = responseJSON.data.originalURL;
-                        const imgElement: HTMLImageElement =
-                            vditor[vditor.currentMode].element.querySelector(`img[src="${original}"]`);
-                        imgElement.src = responseJSON.data.url;
-                        if (vditor.currentMode === "ir") {
-                            imgElement.previousElementSibling.previousElementSibling.innerHTML =
-                                responseJSON.data.url;
+                        if (vditor.currentMode === "sv") {
+                            vditor.sv.element.querySelectorAll('[data-type="image"] .vditor-sv__marker--link')
+                                .forEach((item: HTMLElement) => {
+                                    if (item.textContent === original) {
+                                        item.textContent = responseJSON.data.url;
+                                    }
+                                });
+                        } else {
+                            const imgElement: HTMLImageElement =
+                                vditor[vditor.currentMode].element.querySelector(`img[src="${original}"]`);
+                            imgElement.src = responseJSON.data.url;
+                            if (vditor.currentMode === "ir") {
+                                imgElement.previousElementSibling.previousElementSibling.innerHTML =
+                                    responseJSON.data.url;
+                            }
                         }
                         execAfterRender(vditor);
                     } else {
@@ -1174,8 +1240,10 @@ export const paste = (vditor: IVditor, event: ClipboardEvent & { target: HTMLEle
         }
         if (vditor.currentMode === "ir") {
             return [`<span class="vditor-ir__marker vditor-ir__marker--link">${src}</span>`, Lute.WalkStop];
-        } else {
+        } else if (vditor.currentMode === "wysiwyg") {
             return ["", Lute.WalkStop];
+        } else {
+            return [`<span class="vditor-sv__marker--link">${src}</span>`, Lute.WalkStop];
         }
     };
 
@@ -1195,17 +1263,28 @@ export const paste = (vditor: IVditor, event: ClipboardEvent & { target: HTMLEle
 
     // process code
     const code = processPasteCode(textHTML, textPlain, vditor.currentMode);
-    const codeElement = hasClosestByMatchTag(event.target, "CODE");
+    const codeElement = vditor.currentMode === "sv" ?
+        hasClosestByAttribute(event.target, "data-type", "code-block") :
+        hasClosestByMatchTag(event.target, "CODE");
     if (codeElement) {
         // 粘贴在代码位置
-        const position = getSelectPosition(event.target);
-        codeElement.textContent = codeElement.textContent.substring(0, position.start)
-            + textPlain + codeElement.textContent.substring(position.end);
-        setSelectionByPosition(position.start + textPlain.length, position.start + textPlain.length,
-            codeElement.parentElement);
-        if (codeElement.parentElement.nextElementSibling.classList.contains(`vditor-${vditor.currentMode}__preview`)) {
-            codeElement.parentElement.nextElementSibling.innerHTML = codeElement.outerHTML;
-            processCodeRender(codeElement.parentElement.nextElementSibling as HTMLElement, vditor);
+        if (vditor.currentMode === "sv") {
+            document.execCommand("insertHTML", false, textPlain);
+        } else {
+            const position = getSelectPosition(event.target, vditor[vditor.currentMode].element);
+            if (codeElement.parentElement.tagName !== "PRE") {
+                // https://github.com/Vanessa219/vditor/issues/463
+                textPlain += Constants.ZWSP;
+            }
+            codeElement.textContent = codeElement.textContent.substring(0, position.start)
+                + textPlain + codeElement.textContent.substring(position.end);
+            setSelectionByPosition(position.start + textPlain.length, position.start + textPlain.length,
+                codeElement.parentElement);
+            if (codeElement.parentElement?.nextElementSibling.classList
+                .contains(`vditor-${vditor.currentMode}__preview`)) {
+                codeElement.parentElement.nextElementSibling.innerHTML = codeElement.outerHTML;
+                processCodeRender(codeElement.parentElement.nextElementSibling as HTMLElement, vditor);
+            }
         }
     } else if (code) {
         callback.pasteCode(code);
@@ -1223,10 +1302,14 @@ export const paste = (vditor: IVditor, event: ClipboardEvent & { target: HTMLEle
                 renderers.HTML2VditorIRDOM = {renderLinkDest};
                 vditor.lute.SetJSRenderers({renderers});
                 insertHTML(vditor.lute.HTML2VditorIRDOM(tempElement.innerHTML), vditor);
-            } else {
+            } else if (vditor.currentMode === "wysiwyg") {
                 renderers.HTML2VditorDOM = {renderLinkDest};
                 vditor.lute.SetJSRenderers({renderers});
                 insertHTML(vditor.lute.HTML2VditorDOM(tempElement.innerHTML), vditor);
+            } else {
+                renderers.HTML2VditorSVDOM = {renderLinkDest};
+                vditor.lute.SetJSRenderers({renderers});
+                insertHTML(vditor.lute.HTML2VditorSVDOM(tempElement.innerHTML), vditor);
             }
             vditor.outline.render(vditor);
         } else if (event.clipboardData.files.length > 0 && vditor.options.upload.url) {
@@ -1236,17 +1319,23 @@ export const paste = (vditor: IVditor, event: ClipboardEvent & { target: HTMLEle
                 renderers.Md2VditorIRDOM = {renderLinkDest};
                 vditor.lute.SetJSRenderers({renderers});
                 insertHTML(vditor.lute.Md2VditorIRDOM(textPlain), vditor);
-            } else {
+            } else if (vditor.currentMode === "wysiwyg") {
                 renderers.Md2VditorDOM = {renderLinkDest};
                 vditor.lute.SetJSRenderers({renderers});
                 insertHTML(vditor.lute.Md2VditorDOM(textPlain), vditor);
+            } else {
+                renderers.Md2VditorSVDOM = {renderLinkDest};
+                vditor.lute.SetJSRenderers({renderers});
+                insertHTML(vditor.lute.Md2VditorSVDOM(textPlain), vditor);
             }
             vditor.outline.render(vditor);
         }
     }
-    vditor[vditor.currentMode].element.querySelectorAll(`.vditor-${vditor.currentMode}__preview[data-render='2']`)
-        .forEach((item: HTMLElement) => {
-            processCodeRender(item, vditor);
-        });
+    if (vditor.currentMode !== "sv") {
+        vditor[vditor.currentMode].element.querySelectorAll(`.vditor-${vditor.currentMode}__preview[data-render='2']`)
+            .forEach((item: HTMLElement) => {
+                processCodeRender(item, vditor);
+            });
+    }
     execAfterRender(vditor);
 };

+ 1 - 1
src/ts/sv/getSelectText.ts → src/ts/util/getSelectText.ts

@@ -1,4 +1,4 @@
-import {selectIsEditor} from "../util/selection";
+import {selectIsEditor} from "./selection";
 
 export const getSelectText = (editor: HTMLElement, range?: Range) => {
     if (selectIsEditor(editor, range)) {

+ 40 - 37
src/ts/util/hasClosest.ts

@@ -15,6 +15,46 @@ export const hasTopClosestByClassName = (element: Node, className: string) => {
     return closest || false;
 };
 
+export const hasTopClosestByAttribute = (element: Node, attr: string, value: string) => {
+    let closest = hasClosestByAttribute(element, attr, value);
+    let parentClosest: boolean | HTMLElement = false;
+    let findTop = false;
+    while (closest && !closest.classList.contains("vditor-reset") && !findTop) {
+        parentClosest = hasClosestByAttribute(closest.parentElement, attr, value);
+        if (parentClosest) {
+            closest = parentClosest;
+        } else {
+            findTop = true;
+        }
+    }
+    return closest || false;
+};
+
+export const hasTopClosestByTag = (element: Node, nodeName: string) => {
+    let closest = hasClosestByTag(element, nodeName);
+    let parentClosest: boolean | HTMLElement = false;
+    let findTop = false;
+    while (closest && !closest.classList.contains("vditor-reset") && !findTop) {
+        parentClosest = hasClosestByTag(closest.parentElement, nodeName);
+        if (parentClosest) {
+            closest = parentClosest;
+        } else {
+            findTop = true;
+        }
+    }
+    return closest || false;
+};
+
+export const getTopList = (element: Node) => {
+    const topUlElement = hasTopClosestByTag(element, "UL");
+    const topOlElement = hasTopClosestByTag(element, "OL");
+    let topListElement = topUlElement;
+    if (topOlElement && (!topUlElement || (topUlElement && topOlElement.contains(topUlElement)))) {
+        topListElement = topOlElement;
+    }
+    return topListElement;
+};
+
 export const hasClosestByAttribute = (element: Node, attr: string, value: string) => {
     if (!element) {
         return false;
@@ -106,43 +146,6 @@ export const hasClosestByClassName = (element: Node, className: string) => {
     return isClosest && e;
 };
 
-export const hasTopClosestByTag = (element: Node, nodeName: string) => {
-    if (!element) {
-        return false;
-    }
-    if (element.nodeType === 3) {
-        element = element.parentElement;
-    }
-
-    let closest = hasClosestByTag(element, nodeName);
-    let parentClosest: boolean | HTMLElement = false;
-    if (closest) {
-        parentClosest = hasClosestByTag(closest.parentElement, nodeName);
-    }
-    let findTop = false;
-    while (closest && !closest.classList.contains("vditor-reset") && !findTop) {
-        if (parentClosest) {
-            closest = hasClosestByTag(closest.parentElement, nodeName);
-            if (closest) {
-                parentClosest = hasClosestByTag(closest.parentElement, nodeName);
-            }
-        } else {
-            findTop = true;
-        }
-    }
-    return closest || false;
-};
-
-export const getTopList = (element: Node) => {
-    const topUlElement = hasTopClosestByTag(element, "UL");
-    const topOlElement = hasTopClosestByTag(element, "OL");
-    let topListElement = topUlElement;
-    if (topOlElement && (!topUlElement || (topUlElement && topOlElement.contains(topUlElement)))) {
-        topListElement = topOlElement;
-    }
-    return topListElement;
-};
-
 export const getLastNode = (node: Node) => {
     while (node && node.lastChild) {
         node = node.lastChild;

+ 48 - 35
src/ts/ir/highlightToolbar.ts → src/ts/util/highlightToolbar.ts

@@ -1,23 +1,26 @@
 import {Constants} from "../constants";
+import {highlightToolbarIR} from "../ir/highlightToolbarIR";
+import {highlightToolbarSV} from "../sv/highlightToolbarSV";
 import {disableToolbar, enableToolbar, removeCurrentToolbar, setCurrentToolbar} from "../toolbar/setToolbar";
 import {hasClosestByAttribute, hasClosestByMatchTag} from "../util/hasClosest";
 import {hasClosestByHeadings} from "../util/hasClosestByHeadings";
 import {getEditorRange, selectIsEditor} from "../util/selection";
+import {highlightToolbarWYSIWYG} from "../wysiwyg/highlightToolbarWYSIWYG";
 
-export const highlightToolbar = (vditor: IVditor) => {
-    clearTimeout(vditor.ir.hlToolbarTimeoutId);
-    vditor.ir.hlToolbarTimeoutId = window.setTimeout(() => {
-        if (vditor.ir.element.getAttribute("contenteditable") === "false") {
+export const highlightToolbarIRSV = (vditor: IVditor, processLi: (node: HTMLElement) => void) => {
+    clearTimeout(vditor[vditor.currentMode].hlToolbarTimeoutId);
+    vditor[vditor.currentMode].hlToolbarTimeoutId = window.setTimeout(() => {
+        if (vditor[vditor.currentMode].element.getAttribute("contenteditable") === "false") {
             return;
         }
-        if (!selectIsEditor(vditor.ir.element)) {
+        if (!selectIsEditor(vditor[vditor.currentMode].element)) {
             return;
         }
 
         removeCurrentToolbar(vditor.toolbar.elements, Constants.EDIT_TOOLBARS);
         enableToolbar(vditor.toolbar.elements, Constants.EDIT_TOOLBARS);
 
-        const range = getEditorRange(vditor.ir.element);
+        const range = getEditorRange(vditor[vditor.currentMode].element);
         let typeElement = range.startContainer as HTMLElement;
         if (range.startContainer.nodeType === 3) {
             typeElement = range.startContainer.parentElement;
@@ -26,60 +29,70 @@ export const highlightToolbar = (vditor: IVditor) => {
             typeElement = typeElement.childNodes[range.startOffset] as HTMLElement;
         }
 
-        const headingElement = hasClosestByHeadings(typeElement);
+        const headingElement = vditor.currentMode === "sv" ?
+            hasClosestByAttribute(typeElement, "data-type", "heading") : hasClosestByHeadings(typeElement);
         if (headingElement) {
             setCurrentToolbar(vditor.toolbar.elements, ["headings"]);
         }
 
-        const quoteElement = hasClosestByMatchTag(typeElement, "BLOCKQUOTE");
+        const quoteElement =
+            vditor.currentMode === "sv" ? hasClosestByAttribute(typeElement, "data-type", "blockquote") :
+                hasClosestByMatchTag(typeElement, "BLOCKQUOTE");
         if (quoteElement) {
             setCurrentToolbar(vditor.toolbar.elements, ["quote"]);
         }
-        const aElement = hasClosestByAttribute(typeElement, "data-type", "a");
-        if (aElement) {
-            setCurrentToolbar(vditor.toolbar.elements, ["link"]);
+
+        const strongElement = hasClosestByAttribute(typeElement, "data-type", "strong");
+        if (strongElement) {
+            setCurrentToolbar(vditor.toolbar.elements, ["bold"]);
         }
+
         const emElement = hasClosestByAttribute(typeElement, "data-type", "em");
         if (emElement) {
             setCurrentToolbar(vditor.toolbar.elements, ["italic"]);
         }
-        const strongElement = hasClosestByAttribute(typeElement, "data-type", "strong");
-        if (strongElement) {
-            setCurrentToolbar(vditor.toolbar.elements, ["bold"]);
-        }
+
         const sElement = hasClosestByAttribute(typeElement, "data-type", "s");
         if (sElement) {
             setCurrentToolbar(vditor.toolbar.elements, ["strike"]);
         }
-        const codeElement = hasClosestByAttribute(typeElement, "data-type", "code");
-        if (codeElement) {
-            disableToolbar(vditor.toolbar.elements, ["headings", "bold", "italic", "strike", "line", "quote",
-                "list", "ordered-list", "check", "code", "upload", "link", "table", "record"]);
-            setCurrentToolbar(vditor.toolbar.elements, ["inline-code"]);
+
+        const aElement = hasClosestByAttribute(typeElement, "data-type", "a");
+        if (aElement) {
+            setCurrentToolbar(vditor.toolbar.elements, ["link"]);
         }
+
+        processLi(typeElement);
+
         const codeBlockElement = hasClosestByAttribute(typeElement, "data-type", "code-block");
         if (codeBlockElement) {
             disableToolbar(vditor.toolbar.elements, ["headings", "bold", "italic", "strike", "line", "quote",
                 "list", "ordered-list", "check", "code", "inline-code", "upload", "link", "table", "record"]);
             setCurrentToolbar(vditor.toolbar.elements, ["code"]);
         }
-        const tableElement = hasClosestByMatchTag(typeElement, "TABLE") as HTMLTableElement;
-        if (tableElement) {
-            disableToolbar(vditor.toolbar.elements, ["table"]);
+
+        const codeElement = hasClosestByAttribute(typeElement, "data-type", "code");
+        if (codeElement) {
+            disableToolbar(vditor.toolbar.elements, ["headings", "bold", "italic", "strike", "line", "quote",
+                "list", "ordered-list", "check", "code", "upload", "link", "table", "record"]);
+            setCurrentToolbar(vditor.toolbar.elements, ["inline-code"]);
         }
-        const liElement = hasClosestByMatchTag(typeElement, "LI");
-        if (liElement) {
-            if (liElement.classList.contains("vditor-task")) {
-                setCurrentToolbar(vditor.toolbar.elements, ["check"]);
-            } else if (liElement.parentElement.tagName === "OL") {
-                setCurrentToolbar(vditor.toolbar.elements, ["ordered-list"]);
-            } else if (liElement.parentElement.tagName === "UL") {
-                setCurrentToolbar(vditor.toolbar.elements, ["list"]);
-            }
-            enableToolbar(vditor.toolbar.elements, ["outdent", "indent"]);
-        } else {
-            disableToolbar(vditor.toolbar.elements, ["outdent", "indent"]);
+
+        const tableElement = hasClosestByAttribute(typeElement, "data-type", "table");
+        if (tableElement) {
+            disableToolbar(vditor.toolbar.elements, ["headings", "list", "ordered-list", "check", "line",
+                "quote", "code", "table"]);
         }
 
     }, 200);
 };
+
+export const highlightToolbar = (vditor: IVditor) => {
+    if (vditor.currentMode === "wysiwyg") {
+        highlightToolbarWYSIWYG(vditor);
+    } else if (vditor.currentMode === "ir") {
+        highlightToolbarIR(vditor);
+    } else if (vditor.currentMode === "sv") {
+        highlightToolbarSV(vditor);
+    }
+};

+ 18 - 0
src/ts/util/merge.ts

@@ -0,0 +1,18 @@
+export const merge = (...options: any[]) => {
+    const target: any = {};
+    const merger = (obj: any) => {
+        for (const prop in obj) {
+            if (obj.hasOwnProperty(prop)) {
+                if (Object.prototype.toString.call(obj[prop]) === "[object Object]") {
+                    target[prop] = merge(target[prop], obj[prop]);
+                } else {
+                    target[prop] = obj[prop];
+                }
+            }
+        }
+    };
+    for (let i = 0; i < options.length; i++) {
+        merger(options[i]);
+    }
+    return target;
+};

+ 14 - 8
src/ts/util/processCode.ts

@@ -19,7 +19,7 @@ export const processPasteCode = (html: string, text: string, type = "sv") => {
     const pres = tempElement.querySelectorAll("pre");
     if (tempElement.childElementCount === 1 && pres.length === 1
         && pres[0].className !== "vditor-wysiwyg"
-        && pres[0].className !== "vditor-textarea") {
+        && pres[0].className !== "vditor-sv") {
         // IDE
         isCode = true;
     }
@@ -27,6 +27,11 @@ export const processPasteCode = (html: string, text: string, type = "sv") => {
         // Xcode
         isCode = true;
     }
+    if (tempElement.childElementCount === 1 && tempElement.firstElementChild.tagName === "TABLE" &&
+        tempElement.querySelector(".line-number") && tempElement.querySelector(".line-content")) {
+        // 网页源码
+        isCode = true;
+    }
 
     if (isCode) {
         const code = text || html;
@@ -35,10 +40,7 @@ export const processPasteCode = (html: string, text: string, type = "sv") => {
                 return `<div class="vditor-wysiwyg__block" data-block="0" data-type="code-block"><pre><code>${
                     code.replace(/&/g, "&amp;").replace(/</g, "&lt;")}<wbr></code></pre></div>`;
             }
-            if (type === "ir") {
-                return "```\n" + code.replace(/&/g, "&amp;").replace(/</g, "&lt;") + "\n```";
-            }
-            return "```\n" + code + "\n```";
+            return "```\n" + code.replace(/&/g, "&amp;").replace(/</g, "&lt;") + "\n```";
         } else {
             if (type === "wysiwyg") {
                 return `<code>${code.replace(/&/g, "&amp;").replace(/</g, "&lt;")}</code><wbr>`;
@@ -55,6 +57,11 @@ export const processCodeRender = (previewPanel: HTMLElement, vditor: IVditor) =>
     }
     const codeElement = previewPanel.querySelector("code");
     if (!codeElement) {
+        if (previewPanel.parentElement.getAttribute("data-type") === "html-block") {
+            previewPanel.style.backgroundColor = "var(--preview-background-color)";
+            previewPanel.style.padding = "0.2em 0.4em";
+            previewPanel.setAttribute("data-render", "1");
+        }
         return;
     }
     const language = codeElement.className.replace("language-", "");
@@ -64,7 +71,7 @@ export const processCodeRender = (previewPanel: HTMLElement, vditor: IVditor) =>
         mermaidRender(previewPanel, `.vditor-${vditor.currentMode}__preview .language-mermaid`, vditor.options.cdn);
     } else if (language === "echarts") {
         chartRender(previewPanel, vditor.options.cdn);
-    }  else if (language === "mindmap") {
+    } else if (language === "mindmap") {
         mindmapRender(previewPanel, vditor.options.cdn);
     } else if (language === "graphviz") {
         graphvizRender(previewPanel, vditor.options.cdn);
@@ -76,8 +83,7 @@ export const processCodeRender = (previewPanel: HTMLElement, vditor: IVditor) =>
         previewPanel.innerHTML = `<code class="language-math"><${tag} class="vditor-math">${previewPanel.innerHTML}</${tag}></code>`;
         mathRender(previewPanel.parentElement, {cdn: vditor.options.cdn, math: vditor.options.preview.math});
     } else {
-        highlightRender(Object.assign({}, vditor.options.preview.hljs, {enable: true}),
-            previewPanel, vditor.options.cdn);
+        highlightRender(Object.assign({}, vditor.options.preview.hljs), previewPanel, vditor.options.cdn);
         codeRender(previewPanel, vditor.options.lang);
     }
 

+ 51 - 36
src/ts/util/selection.ts

@@ -1,6 +1,6 @@
 import {Constants} from "../constants";
 import {isChrome} from "./compatibility";
-import {hasClosestBlock} from "./hasClosest";
+import {hasClosestBlock, hasClosestByClassName} from "./hasClosest";
 
 export const getEditorRange = (element: HTMLElement) => {
     let range: Range;
@@ -19,7 +19,7 @@ export const getEditorRange = (element: HTMLElement) => {
 
 export const getCursorPosition = (editor: HTMLElement) => {
     const range = window.getSelection().getRangeAt(0);
-    if (!editor.contains(range.startContainer)) {
+    if (!editor.contains(range.startContainer) && !hasClosestByClassName(range.startContainer, "vditor-panel--none")) {
         return {
             left: 0,
             top: 0,
@@ -29,32 +29,42 @@ export const getCursorPosition = (editor: HTMLElement) => {
     let cursorRect;
     if (range.getClientRects().length === 0) {
         if (range.startContainer.nodeType === 3) {
-            return {
-                left: 0,
-                top: 0,
-            };
-        }
-        const children = (range.startContainer as Element).children;
-        if (children[range.startOffset] &&
-            children[range.startOffset].getClientRects().length > 0) {
-            // markdown 模式回车
-            cursorRect = children[range.startOffset].getClientRects()[0];
-        } else if (range.startContainer.childNodes.length > 0) {
-            // in table or code block
-            range.selectNode(range.startContainer.childNodes[Math.max(0, range.startOffset - 1)]);
-            cursorRect = range.getClientRects()[0];
-            range.collapse(false);
+            // 空行时,会出现没有 br 的情况,需要根据父元素 <p> 获取位置信息
+            const parent = range.startContainer.parentElement;
+            if (parent && parent.getClientRects().length > 0) {
+                cursorRect = parent.getClientRects()[0];
+            } else {
+                return {
+                    left: 0,
+                    top: 0,
+                };
+            }
         } else {
-            cursorRect = (range.startContainer as HTMLElement).getClientRects()[0];
-        }
-        if (!cursorRect) {
-            let parentElement = range.startContainer.childNodes[range.startOffset] as HTMLElement;
-            while (!parentElement.getClientRects ||
-            (parentElement.getClientRects && parentElement.getClientRects().length === 0)) {
-                parentElement = parentElement.parentElement;
+            const children = (range.startContainer as Element).children;
+            if (children[range.startOffset] &&
+                children[range.startOffset].getClientRects().length > 0) {
+                // markdown 模式回车
+                cursorRect = children[range.startOffset].getClientRects()[0];
+            } else if (range.startContainer.childNodes.length > 0) {
+                // in table or code block
+                const cloneRange = range.cloneRange();
+                range.selectNode(range.startContainer.childNodes[Math.max(0, range.startOffset - 1)]);
+                cursorRect = range.getClientRects()[0];
+                range.setEnd(cloneRange.endContainer, cloneRange.endOffset);
+                range.setStart(cloneRange.startContainer, cloneRange.startOffset);
+            } else {
+                cursorRect = (range.startContainer as HTMLElement).getClientRects()[0];
+            }
+            if (!cursorRect) {
+                let parentElement = range.startContainer.childNodes[range.startOffset] as HTMLElement;
+                while (!parentElement.getClientRects ||
+                (parentElement.getClientRects && parentElement.getClientRects().length === 0)) {
+                    parentElement = parentElement.parentElement;
+                }
+                cursorRect = parentElement.getClientRects()[0];
             }
-            cursorRect = parentElement.getClientRects()[0];
         }
+
     } else {
         cursorRect = range.getClientRects()[0];
     }
@@ -84,7 +94,7 @@ export const setSelectionFocus = (range: Range) => {
     selection.addRange(range);
 };
 
-export const getSelectPosition = (editorElement: HTMLElement, range?: Range) => {
+export const getSelectPosition = (selectElement: HTMLElement, editorElement: HTMLElement, range?: Range) => {
     const position = {
         end: 0,
         start: 0,
@@ -99,10 +109,10 @@ export const getSelectPosition = (editorElement: HTMLElement, range?: Range) =>
 
     if (selectIsEditor(editorElement, range)) {
         const preSelectionRange = range.cloneRange();
-        if (editorElement.childNodes[0] && editorElement.childNodes[0].childNodes[0]) {
-            preSelectionRange.setStart(editorElement.childNodes[0].childNodes[0], 0);
+        if (selectElement.childNodes[0] && selectElement.childNodes[0].childNodes[0]) {
+            preSelectionRange.setStart(selectElement.childNodes[0].childNodes[0], 0);
         } else {
-            preSelectionRange.selectNodeContents(editorElement);
+            preSelectionRange.selectNodeContents(selectElement);
         }
         preSelectionRange.setEnd(range.startContainer, range.startOffset);
         position.start = preSelectionRange.toString().length;
@@ -225,20 +235,25 @@ export const insertHTML = (html: string, vditor: IVditor) => {
     // 使用 lute 方法会添加 p 元素,只有一个 p 元素的时候进行删除
     const tempElement = document.createElement("div");
     tempElement.innerHTML = html;
-    const pElements = tempElement.querySelectorAll("p");
-    if (pElements.length === 1 && !pElements[0].previousSibling && !pElements[0].nextSibling) {
-        if ((vditor.currentMode === "wysiwyg" && vditor.wysiwyg.element.children.length > 0) ||
-            (vditor.currentMode === "ir" && vditor.ir.element.children.length > 0)) {
-            // empty and past
-            html = pElements[0].innerHTML.trim();
+    const tempBlockElement = vditor.currentMode === "sv" ?
+        tempElement.querySelectorAll('[data-type="p"]') :
+        tempElement.querySelectorAll("p");
+    if (tempBlockElement.length === 1 && !tempBlockElement[0].previousSibling && !tempBlockElement[0].nextSibling &&
+        vditor[vditor.currentMode].element.children.length > 0) {
+        // empty and past
+        if (vditor.currentMode === "sv") {
+            tempBlockElement[0].querySelectorAll('[data-type="newline"]').forEach((item: HTMLElement) => {
+                item.remove();
+            });
         }
+        html = tempBlockElement[0].innerHTML.trim();
     }
 
     const pasteElement = document.createElement("template");
     pasteElement.innerHTML = html;
 
     const range = getEditorRange(vditor[vditor.currentMode].element);
-    if (range.toString() !== "" && vditor.currentMode !== "sv") {
+    if (range.toString() !== "") {
         vditor[vditor.currentMode].preventInput = true;
         document.execCommand("delete", false, "");
     }

+ 4 - 5
src/ts/wysiwyg/afterRenderEvent.ts

@@ -1,5 +1,5 @@
 import {getMarkdown} from "../markdown/getMarkdown";
-import {isSafari} from "../util/compatibility";
+import {accessLocalStorage} from "../util/compatibility";
 
 export const afterRenderEvent = (vditor: IVditor, options = {
     enableAddUndoStack: true,
@@ -11,8 +11,7 @@ export const afterRenderEvent = (vditor: IVditor, options = {
     }
     clearTimeout(vditor.wysiwyg.afterRenderTimeoutId);
     vditor.wysiwyg.afterRenderTimeoutId = window.setTimeout(() => {
-        if (vditor.wysiwyg.composingLock && isSafari()) {
-            // safari 中文输入遇到 addToUndoStack 会影响下一次的中文输入
+        if (vditor.wysiwyg.composingLock) {
             return;
         }
         const text = getMarkdown(vditor);
@@ -24,7 +23,7 @@ export const afterRenderEvent = (vditor: IVditor, options = {
             vditor.counter.render(vditor, text);
         }
 
-        if (vditor.options.cache.enable) {
+        if (vditor.options.cache.enable && accessLocalStorage()) {
             localStorage.setItem(vditor.options.cache.id, text);
             if (vditor.options.cache.after) {
                 vditor.options.cache.after(text);
@@ -36,7 +35,7 @@ export const afterRenderEvent = (vditor: IVditor, options = {
         }
 
         if (options.enableAddUndoStack) {
-            vditor.wysiwygUndo.addToUndoStack(vditor);
+            vditor.undo.addToUndoStack(vditor);
         }
     }, 800);
 };

+ 123 - 15
src/ts/wysiwyg/highlightToolbar.ts → src/ts/wysiwyg/highlightToolbarWYSIWYG.ts

@@ -1,7 +1,11 @@
 import alignCenterSVG from "../../assets/icons/align-center.svg";
-import downSVG from "../../assets/icons/down.svg";
 import alingLeftSVG from "../../assets/icons/align-left.svg";
 import alingRightSVG from "../../assets/icons/align-right.svg";
+import deleteColumnSVG from "../../assets/icons/delete-column.svg";
+import deleteRowSVG from "../../assets/icons/delete-row.svg";
+import downSVG from "../../assets/icons/down.svg";
+import insertColumnSVG from "../../assets/icons/insert-column.svg";
+import insertRowSVG from "../../assets/icons/insert-row.svg";
 import trashcanSVG from "../../assets/icons/trashcan.svg";
 import upSVG from "../../assets/icons/up.svg";
 import {Constants} from "../constants";
@@ -12,7 +16,7 @@ import {removeCurrentToolbar} from "../toolbar/setToolbar";
 import {setCurrentToolbar} from "../toolbar/setToolbar";
 import {isCtrl, updateHotkeyTip} from "../util/compatibility";
 import {scrollCenter} from "../util/editorCommonEvent";
-import {setTableAlign} from "../util/fixBrowserBehavior";
+import {deleteColumn, deleteRow, insertColumn, insertRow, setTableAlign} from "../util/fixBrowserBehavior";
 import {
     hasClosestByAttribute,
     hasClosestByClassName,
@@ -22,8 +26,9 @@ import {hasClosestByHeadings, hasClosestByTag} from "../util/hasClosestByHeading
 import {processCodeRender} from "../util/processCode";
 import {getEditorRange, selectIsEditor, setRangeByWbr, setSelectionFocus} from "../util/selection";
 import {afterRenderEvent} from "./afterRenderEvent";
+import {removeBlockElement} from "./processKeydown";
 
-export const highlightToolbar = (vditor: IVditor) => {
+export const highlightToolbarWYSIWYG = (vditor: IVditor) => {
     clearTimeout(vditor.wysiwyg.hlToolbarTimeoutId);
     vditor.wysiwyg.hlToolbarTimeoutId = window.setTimeout(() => {
         if (vditor.wysiwyg.element.getAttribute("contenteditable") === "false") {
@@ -262,6 +267,66 @@ export const highlightToolbar = (vditor: IVditor) => {
                 setAlign("right");
             };
 
+            const insertRowElement = document.createElement("button");
+            insertRowElement.setAttribute("aria-label", i18n[vditor.options.lang]["insert-row"] +
+                "<" + updateHotkeyTip("⌘-=") + ">");
+            insertRowElement.setAttribute("data-type", "insertRow");
+            insertRowElement.innerHTML = insertRowSVG;
+            insertRowElement.className = "vditor-icon vditor-tooltipped vditor-tooltipped__n";
+            insertRowElement.onclick = () => {
+                const startContainer = getSelection().getRangeAt(0).startContainer;
+                const cellElement = hasClosestByMatchTag(startContainer, "TD") ||
+                    hasClosestByMatchTag(startContainer, "TH");
+                if (cellElement) {
+                    insertRow(vditor, range, cellElement);
+                }
+            };
+
+            const insertColumnElement = document.createElement("button");
+            insertColumnElement.setAttribute("aria-label", i18n[vditor.options.lang]["insert-column"] +
+                "<" + updateHotkeyTip("⌘-⇧-=") + ">");
+            insertColumnElement.setAttribute("data-type", "insertColumn");
+            insertColumnElement.innerHTML = insertColumnSVG;
+            insertColumnElement.className = "vditor-icon vditor-tooltipped vditor-tooltipped__n";
+            insertColumnElement.onclick = () => {
+                const startContainer = getSelection().getRangeAt(0).startContainer;
+                const cellElement = hasClosestByMatchTag(startContainer, "TD") ||
+                    hasClosestByMatchTag(startContainer, "TH");
+                if (cellElement) {
+                    insertColumn(vditor, tableElement, cellElement);
+                }
+            };
+
+            const deleteRowElement = document.createElement("button");
+            deleteRowElement.setAttribute("aria-label", i18n[vditor.options.lang]["delete-row"] +
+                "<" + updateHotkeyTip("⌘--") + ">");
+            deleteRowElement.setAttribute("data-type", "deleteRow");
+            deleteRowElement.innerHTML = deleteRowSVG;
+            deleteRowElement.className = "vditor-icon vditor-tooltipped vditor-tooltipped__n";
+            deleteRowElement.onclick = () => {
+                const startContainer = getSelection().getRangeAt(0).startContainer;
+                const cellElement = hasClosestByMatchTag(startContainer, "TD") ||
+                    hasClosestByMatchTag(startContainer, "TH");
+                if (cellElement) {
+                    deleteRow(vditor, range, cellElement);
+                }
+            };
+
+            const deleteColumnElement = document.createElement("button");
+            deleteColumnElement.setAttribute("aria-label", i18n[vditor.options.lang]["delete-column"] +
+                "<" + updateHotkeyTip("⌘-⇧--") + ">");
+            deleteColumnElement.setAttribute("data-type", "deleteColumn");
+            deleteColumnElement.innerHTML = deleteColumnSVG;
+            deleteColumnElement.className = "vditor-icon vditor-tooltipped vditor-tooltipped__n";
+            deleteColumnElement.onclick = () => {
+                const startContainer = getSelection().getRangeAt(0).startContainer;
+                const cellElement = hasClosestByMatchTag(startContainer, "TD") ||
+                    hasClosestByMatchTag(startContainer, "TH");
+                if (cellElement) {
+                    deleteColumn(vditor, range, tableElement, cellElement);
+                }
+            };
+
             const inputWrap = document.createElement("span");
             inputWrap.setAttribute("aria-label", i18n[vditor.options.lang].row);
             inputWrap.className = "vditor-tooltipped vditor-tooltipped__n";
@@ -287,6 +352,7 @@ export const highlightToolbar = (vditor: IVditor) => {
                     event.preventDefault();
                     return;
                 }
+                removeBlockElement(vditor, event);
             };
 
             const input2Wrap = document.createElement("span");
@@ -314,6 +380,7 @@ export const highlightToolbar = (vditor: IVditor) => {
                     event.preventDefault();
                     return;
                 }
+                removeBlockElement(vditor, event);
             };
 
             genUp(range, tableElement, vditor);
@@ -322,6 +389,10 @@ export const highlightToolbar = (vditor: IVditor) => {
             vditor.wysiwyg.popover.insertAdjacentElement("beforeend", left);
             vditor.wysiwyg.popover.insertAdjacentElement("beforeend", center);
             vditor.wysiwyg.popover.insertAdjacentElement("beforeend", right);
+            vditor.wysiwyg.popover.insertAdjacentElement("beforeend", insertRowElement);
+            vditor.wysiwyg.popover.insertAdjacentElement("beforeend", insertColumnElement);
+            vditor.wysiwyg.popover.insertAdjacentElement("beforeend", deleteRowElement);
+            vditor.wysiwyg.popover.insertAdjacentElement("beforeend", deleteColumnElement);
             vditor.wysiwyg.popover.insertAdjacentElement("beforeend", inputWrap);
             vditor.wysiwyg.popover.insertAdjacentHTML("beforeend", " x ");
             vditor.wysiwyg.popover.insertAdjacentElement("beforeend", input2Wrap);
@@ -356,6 +427,9 @@ export const highlightToolbar = (vditor: IVditor) => {
                 updateLinkRef();
             };
             input.onkeydown = (event) => {
+                if (removeBlockElement(vditor, event)) {
+                    return;
+                }
                 linkHotkey(vditor.wysiwyg.element, linkRefElement, event, input1);
             };
 
@@ -371,6 +445,9 @@ export const highlightToolbar = (vditor: IVditor) => {
                 updateLinkRef();
             };
             input1.onkeydown = (event) => {
+                if (removeBlockElement(vditor, event)) {
+                    return;
+                }
                 linkHotkey(vditor.wysiwyg.element, linkRefElement, event, input);
             };
 
@@ -409,7 +486,9 @@ export const highlightToolbar = (vditor: IVditor) => {
                     range.collapse(false);
                     setSelectionFocus(range);
                     event.preventDefault();
+                    return;
                 }
+                removeBlockElement(vditor, event);
             };
 
             genClose(footnotesRefElement, vditor);
@@ -435,15 +514,17 @@ export const highlightToolbar = (vditor: IVditor) => {
 
                 const codeElement = blockRenderElement.firstElementChild.firstElementChild;
 
-                const updateLanguage = () => {
-                    codeElement.className = `language-${language.value}`;
-                };
                 language.className = "vditor-input";
                 language.setAttribute("placeholder", i18n[vditor.options.lang].language + "<" + updateHotkeyTip("⌥-Enter") + ">");
                 language.value = codeElement.className.indexOf("language-") > -1 ?
-                    codeElement.className.split("-")[1].split(" ")[0] : vditor.hint.recentLanguage;
+                    codeElement.className.split("-")[1].split(" ")[0] : "";
                 language.oninput = () => {
-                    updateLanguage();
+                    if (language.value.trim() !== "") {
+                        codeElement.className = `language-${language.value}`;
+                    } else {
+                        codeElement.className = "";
+                        vditor.hint.recentLanguage = "";
+                    }
                     if (blockRenderElement.lastElementChild.classList.contains("vditor-wysiwyg__preview")) {
                         blockRenderElement.lastElementChild.innerHTML = blockRenderElement.firstElementChild.innerHTML;
                         processCodeRender(blockRenderElement.lastElementChild as HTMLElement, vditor);
@@ -454,6 +535,14 @@ export const highlightToolbar = (vditor: IVditor) => {
                     if (event.isComposing) {
                         return;
                     }
+                    if (removeBlockElement(vditor, event)) {
+                        return;
+                    }
+                    if (event.key === "Escape" && vditor.hint.element.style.display === "block") {
+                        vditor.hint.element.style.display = "none";
+                        event.preventDefault();
+                        return;
+                    }
                     if (!isCtrl(event) && !event.shiftKey && event.altKey && event.key === "Enter") {
                         range.setStart(codeElement.firstChild, 0);
                         range.collapse(true);
@@ -462,7 +551,8 @@ export const highlightToolbar = (vditor: IVditor) => {
                     vditor.hint.select(event, vditor);
                 };
                 language.onkeyup = (event: KeyboardEvent) => {
-                    if (event.isComposing || event.key === "Enter" || event.key === "ArrowUp" || event.key === "ArrowDown") {
+                    if (event.isComposing || event.key === "Enter" || event.key === "ArrowUp" || event.key === "Escape"
+                        || event.key === "ArrowDown") {
                         return;
                     }
                     const matchLangData: IHintData[] = [];
@@ -515,7 +605,9 @@ export const highlightToolbar = (vditor: IVditor) => {
                     range.collapse(false);
                     setSelectionFocus(range);
                     event.preventDefault();
+                    return;
                 }
+                removeBlockElement(vditor, event);
             };
 
             genUp(range, headingElement, vditor);
@@ -588,7 +680,7 @@ const genUp = (range: Range, element: HTMLElement, vditor: IVditor) => {
         previousElement.insertAdjacentElement("beforebegin", element);
         setRangeByWbr(vditor.wysiwyg.element, range);
         afterRenderEvent(vditor);
-        highlightToolbar(vditor);
+        highlightToolbarWYSIWYG(vditor);
         scrollCenter(vditor);
     };
     vditor.wysiwyg.popover.insertAdjacentElement("beforeend", upElement);
@@ -610,16 +702,13 @@ const genDown = (range: Range, element: HTMLElement, vditor: IVditor) => {
         nextElement.insertAdjacentElement("afterend", element);
         setRangeByWbr(vditor.wysiwyg.element, range);
         afterRenderEvent(vditor);
-        highlightToolbar(vditor);
+        highlightToolbarWYSIWYG(vditor);
         scrollCenter(vditor);
     };
     vditor.wysiwyg.popover.insertAdjacentElement("beforeend", downElement);
 };
 
 const genClose = (element: HTMLElement, vditor: IVditor) => {
-    if (vditor.wysiwyg.element.children.length === 1 && vditor.wysiwyg.element.firstElementChild.tagName === "P") {
-        return;
-    }
     const close = document.createElement("button");
     close.setAttribute("data-type", "remove");
     close.setAttribute("aria-label", i18n[vditor.options.lang].remove +
@@ -632,7 +721,7 @@ const genClose = (element: HTMLElement, vditor: IVditor) => {
         setSelectionFocus(range);
         element.remove();
         afterRenderEvent(vditor);
-        highlightToolbar(vditor);
+        highlightToolbarWYSIWYG(vditor);
     };
     vditor.wysiwyg.popover.insertAdjacentElement("beforeend", close);
 };
@@ -686,6 +775,9 @@ export const genAPopover = (vditor: IVditor, aElement: HTMLElement) => {
         updateA();
     };
     input.onkeydown = (event) => {
+        if (removeBlockElement(vditor, event)) {
+            return;
+        }
         linkHotkey(vditor.wysiwyg.element, aElement, event, input1);
     };
 
@@ -701,6 +793,9 @@ export const genAPopover = (vditor: IVditor, aElement: HTMLElement) => {
         updateA();
     };
     input1.onkeydown = (event) => {
+        if (removeBlockElement(vditor, event)) {
+            return;
+        }
         linkHotkey(vditor.wysiwyg.element, aElement, event, input2);
     };
 
@@ -717,6 +812,9 @@ export const genAPopover = (vditor: IVditor, aElement: HTMLElement) => {
         updateA();
     };
     input2.onkeydown = (event) => {
+        if (removeBlockElement(vditor, event)) {
+            return;
+        }
         linkHotkey(vditor.wysiwyg.element, aElement, event, input);
     };
 
@@ -747,6 +845,9 @@ export const genImagePopover = (event: Event, vditor: IVditor) => {
     inputElement.oninput = () => {
         updateImg();
     };
+    inputElement.onkeydown = (elementEvent) => {
+        removeBlockElement(vditor, elementEvent);
+    };
 
     const altWrap = document.createElement("span");
     altWrap.setAttribute("aria-label", i18n[vditor.options.lang].alternateText);
@@ -760,6 +861,9 @@ export const genImagePopover = (event: Event, vditor: IVditor) => {
     alt.oninput = () => {
         updateImg();
     };
+    alt.onkeydown = (elementEvent) => {
+        removeBlockElement(vditor, elementEvent);
+    };
 
     const titleWrap = document.createElement("span");
     titleWrap.setAttribute("aria-label", "Title");
@@ -772,6 +876,10 @@ export const genImagePopover = (event: Event, vditor: IVditor) => {
     title.oninput = () => {
         updateImg();
     };
+    title.onkeydown = (elementEvent) => {
+        removeBlockElement(vditor, elementEvent);
+    };
+    genClose(imgElement, vditor);
     vditor.wysiwyg.popover.insertAdjacentElement("beforeend", inputWrap);
     vditor.wysiwyg.popover.insertAdjacentElement("beforeend", altWrap);
     vditor.wysiwyg.popover.insertAdjacentElement("beforeend", titleWrap);

Some files were not shown because too many files changed in this diff