punkweb-bb 0.2.2__py3-none-any.whl → 0.3.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. punkweb_bb/__pycache__/admin.cpython-311.pyc +0 -0
  2. punkweb_bb/__pycache__/admin_forms.cpython-311.pyc +0 -0
  3. punkweb_bb/__pycache__/forms.cpython-311.pyc +0 -0
  4. punkweb_bb/__pycache__/models.cpython-311.pyc +0 -0
  5. punkweb_bb/__pycache__/settings.cpython-311.pyc +0 -0
  6. punkweb_bb/__pycache__/sitemap.cpython-311.pyc +0 -0
  7. punkweb_bb/__pycache__/tests.cpython-311.pyc +0 -0
  8. punkweb_bb/__pycache__/urls.cpython-311.pyc +0 -0
  9. punkweb_bb/__pycache__/utils.cpython-311.pyc +0 -0
  10. punkweb_bb/__pycache__/views.cpython-311.pyc +0 -0
  11. punkweb_bb/__pycache__/widgets.cpython-311.pyc +0 -0
  12. punkweb_bb/admin.py +0 -4
  13. punkweb_bb/admin_forms.py +6 -5
  14. punkweb_bb/forms.py +13 -5
  15. punkweb_bb/migrations/0005_alter_thread_options.py +24 -0
  16. punkweb_bb/migrations/0006_remove_boardprofile__signature_rendered_and_more.py +60 -0
  17. punkweb_bb/migrations/__pycache__/0005_alter_thread_options.cpython-311.pyc +0 -0
  18. punkweb_bb/migrations/__pycache__/0006_remove_boardprofile__signature_rendered_and_more.cpython-311.pyc +0 -0
  19. punkweb_bb/models.py +12 -9
  20. punkweb_bb/settings.py +1 -0
  21. punkweb_bb/static/punkweb_bb/css/thread.css +45 -28
  22. punkweb_bb/static/punkweb_bb/editor/markdown-editor.js +23 -0
  23. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/.eslintrc.json +15 -0
  24. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/.gitignore +108 -0
  25. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/.prettierrc.json +1 -0
  26. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/LICENSE +21 -0
  27. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/README.md +240 -0
  28. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/babel.config.json +14 -0
  29. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/dist/blank.html +18 -0
  30. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/dist/demo.html +126 -0
  31. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/dist/tiny-mde.css +231 -0
  32. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/dist/tiny-mde.js +3086 -0
  33. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/dist/tiny-mde.min.css +1 -0
  34. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/dist/tiny-mde.min.js +1 -0
  35. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/dist/tiny-mde.tiny.js +1 -0
  36. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/docs/_config.yml +1 -0
  37. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/docs/_layouts/default.html +50 -0
  38. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/docs/index.md +174 -0
  39. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/globals.d.ts +172 -0
  40. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/gulpfile.mjs +226 -0
  41. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/jest/block.test.js +696 -0
  42. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/jest/commandbar.test.js +84 -0
  43. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/jest/inline.test.js +486 -0
  44. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/jest/interaction.test.js +31 -0
  45. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/jest/setup.test.js +164 -0
  46. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/jest/util/config.js +2 -0
  47. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/jest/util/server.js +9 -0
  48. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/jest/util/setup.js +1 -0
  49. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/jest/util/test-helpers.js +98 -0
  50. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/jest-puppeteer.config.js +8 -0
  51. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/jest.config.js +13 -0
  52. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/package-lock.json +16295 -0
  53. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/package.json +72 -0
  54. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/src/TinyMDE.js +1926 -0
  55. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/src/TinyMDECommandBar.js +256 -0
  56. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/src/css/commandbar.css +72 -0
  57. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/src/css/editor.css +157 -0
  58. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/src/css/index.css +3 -0
  59. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/src/grammar.js +300 -0
  60. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/src/html/blank.html +18 -0
  61. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/src/html/demo.html +126 -0
  62. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/src/index.js +4 -0
  63. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/src/svg/blockquote.svg +1 -0
  64. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/src/svg/bold.svg +1 -0
  65. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/src/svg/clear_formatting.svg +1 -0
  66. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/src/svg/code.svg +1 -0
  67. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/src/svg/h1.svg +1 -0
  68. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/src/svg/h2.svg +1 -0
  69. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/src/svg/hr.svg +1 -0
  70. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/src/svg/image.svg +1 -0
  71. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/src/svg/italic.svg +1 -0
  72. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/src/svg/link.svg +1 -0
  73. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/src/svg/ol.svg +1 -0
  74. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/src/svg/strikethrough.svg +1 -0
  75. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/src/svg/svg.js +17 -0
  76. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/src/svg/ul.svg +1 -0
  77. punkweb_bb/static/punkweb_bb/vendor/tiny-markdown-editor/src/tiny.js +3 -0
  78. punkweb_bb/templates/punkweb_bb/base_delete_modal.html +13 -0
  79. punkweb_bb/templates/punkweb_bb/index.html +2 -2
  80. punkweb_bb/templates/punkweb_bb/members.html +3 -1
  81. punkweb_bb/templates/punkweb_bb/partials/category_delete.html +4 -8
  82. punkweb_bb/templates/punkweb_bb/partials/post_delete.html +4 -8
  83. punkweb_bb/templates/punkweb_bb/partials/shout_delete.html +4 -8
  84. punkweb_bb/templates/punkweb_bb/partials/subcategory_delete.html +4 -8
  85. punkweb_bb/templates/punkweb_bb/partials/thread_delete.html +4 -8
  86. punkweb_bb/templates/punkweb_bb/partials/thread_move.html +24 -0
  87. punkweb_bb/templates/punkweb_bb/profile.html +8 -4
  88. punkweb_bb/templates/punkweb_bb/shoutbox/shout_list.html +3 -3
  89. punkweb_bb/templates/punkweb_bb/subcategory.html +1 -1
  90. punkweb_bb/templates/punkweb_bb/thread.html +89 -71
  91. punkweb_bb/templates/punkweb_bb/widgets/markdown-editor.html +3 -0
  92. punkweb_bb/templatetags/__pycache__/markdown.cpython-311.pyc +0 -0
  93. punkweb_bb/templatetags/__pycache__/render.cpython-311.pyc +0 -0
  94. punkweb_bb/templatetags/__pycache__/shoutbox_bbcode.cpython-311.pyc +0 -0
  95. punkweb_bb/templatetags/__pycache__/shoutbox_render.cpython-311.pyc +0 -0
  96. punkweb_bb/templatetags/__pycache__/styled_group_name.cpython-311.pyc +0 -0
  97. punkweb_bb/templatetags/render.py +48 -0
  98. punkweb_bb/templatetags/shoutbox_render.py +22 -0
  99. punkweb_bb/templatetags/styled_group_name.py +2 -2
  100. punkweb_bb/tests.py +3 -3
  101. punkweb_bb/urls.py +1 -0
  102. punkweb_bb/utils.py +23 -7
  103. punkweb_bb/views.py +36 -7
  104. punkweb_bb/widgets.py +20 -0
  105. {punkweb_bb-0.2.2.dist-info → punkweb_bb-0.3.0.dist-info}/METADATA +56 -41
  106. {punkweb_bb-0.2.2.dist-info → punkweb_bb-0.3.0.dist-info}/RECORD +109 -41
  107. punkweb_bb/templatetags/shoutbox_bbcode.py +0 -14
  108. {punkweb_bb-0.2.2.dist-info → punkweb_bb-0.3.0.dist-info}/LICENSE +0 -0
  109. {punkweb_bb-0.2.2.dist-info → punkweb_bb-0.3.0.dist-info}/WHEEL +0 -0
  110. {punkweb_bb-0.2.2.dist-info → punkweb_bb-0.3.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,84 @@
1
+ beforeEach(async () => {
2
+ await page.goto(PATH, { waitUntil: 'load'});
3
+ });
4
+
5
+ test('Basic command bar setup works', async () => {
6
+ await page.evaluate(() => {
7
+ const tinyMDE = new TinyMDE.Editor({element: 'tinymde'});
8
+ const commandBar = new TinyMDE.CommandBar({element: 'tinymde_commandbar', editor: tinyMDE});
9
+ });
10
+ expect(await page.$eval('#tinymde_commandbar > :first-child', el => el.className)).toEqual('TMCommandBar');
11
+ });
12
+
13
+ test('Customized command bar setup works: selecting commands', async () => {
14
+ const commands = ['bold', 'italic', '|', 'strikethrough'];
15
+ await page.evaluate((commands) => {
16
+ const tinyMDE = new TinyMDE.Editor({element: 'tinymde'});
17
+ const commandBar = new TinyMDE.CommandBar({element: 'tinymde_commandbar', editor: tinyMDE, commands: commands})
18
+ }, commands);
19
+ expect(await page.$eval('.TMCommandBar > :nth-child(1)', el => el.title)).toMatch(/Bold/);
20
+ expect(await page.$eval('.TMCommandBar > :nth-child(2)', el => el.title)).toMatch(/Italic/);
21
+ expect(await page.$eval('.TMCommandBar > :nth-child(3)', el => el.className)).toMatch(/TMCommandDivider/);
22
+ expect(await page.$eval('.TMCommandBar > :nth-child(4)', el => el.title)).toMatch(/Strikethrough/);
23
+ });
24
+
25
+ test('Customized command bar setup works: referencing commands', async () => {
26
+ const commands = [{name: 'bold', innerHTML: 'X'}, 'italic', '|', 'strikethrough'];
27
+ await page.evaluate((commands) => {
28
+ const tinyMDE = new TinyMDE.Editor({element: 'tinymde'});
29
+ const commandBar = new TinyMDE.CommandBar({element: 'tinymde_commandbar', editor: tinyMDE, commands: commands})
30
+ }, commands);
31
+ expect(await page.$eval('.TMCommandBar > :nth-child(1)', el => el.title)).toMatch(/Bold/);
32
+ expect(await page.$eval('.TMCommandBar > :nth-child(1)', el => el.innerHTML)).toEqual(commands[0].innerHTML);
33
+ });
34
+
35
+ test('Clicking command bar fires change event', async () => {
36
+ await page.evaluate(() => {
37
+ window.listenerCalled = false;
38
+ const listener = () => {
39
+ window.listenerCalled = true;
40
+ }
41
+ document.tinyMDE = new TinyMDE.Editor({element: 'tinymde', content: 'XXXA'});
42
+ document.tinyMDE.addEventListener('change', listener);
43
+ document.commandBar = new TinyMDE.CommandBar({element: 'tinymde_commandbar', editor: document.tinyMDE, commands: ['h1']});
44
+ document.getElementById('tinymde').firstChild.focus();
45
+ });
46
+ await page.mouse.click(12, 12)
47
+
48
+ expect(await page.evaluate(() => { return window.listenerCalled; })).toBe(true);
49
+ });
50
+
51
+ test('Keyboard shortcuts work', async () => {
52
+ await page.evaluate(() => {
53
+ document.tinyMDE = new TinyMDE.Editor({element: 'tinymde', content: 'This is a test'});
54
+ document.commandBar = new TinyMDE.CommandBar({element: 'tinymde_commandbar', editor: document.tinyMDE, commands: [{name: 'bold', hotkey: 'Ctrl-B'}]}); // Manually setting ctrl-B as shortcut so this works on all platforms
55
+ document.getElementById('tinymde').firstChild.focus();
56
+ });
57
+ await select(page, 0, 5, 0, 7); // Select "is"
58
+ await page.keyboard.down('ControlLeft');
59
+ await page.keyboard.press('KeyB');
60
+ await page.keyboard.up('ControlLeft');
61
+ expect(await page.evaluate(() => document.tinyMDE.getContent())).toEqual('This **is** a test');
62
+
63
+ });
64
+
65
+ test('Minimal custom command works', async () => {
66
+ await page.evaluate(() => {
67
+ document.tinyMDE = new TinyMDE.Editor({element: 'tinymde', content: 'This\nis\na\ntest'});
68
+ document.commandBar = new TinyMDE.CommandBar({element: 'tinymde_commandbar', editor: document.tinyMDE, commands: [{name: 'X', innerHTML: 'X', action: editor => editor.setContent('XXXA')}]});
69
+ document.getElementById('tinymde').firstChild.focus();
70
+ });
71
+ await page.mouse.click(12, 12)
72
+ expect(await page.evaluate(() => document.tinyMDE.getContent())).toEqual('XXXA');
73
+
74
+ });
75
+
76
+ test('Custom command title defaults to name', async () => {
77
+ await page.evaluate(() => {
78
+ document.tinyMDE = new TinyMDE.Editor({element: 'tinymde', content: 'This\nis\na\ntest'});
79
+ document.commandBar = new TinyMDE.CommandBar({element: 'tinymde_commandbar', editor: document.tinyMDE, commands: [{name: 'X', innerHTML: 'X', action: editor => editor.setContent('XXXA')}]});
80
+ document.getElementById('tinymde').firstChild.focus();
81
+ });
82
+ expect(await page.$eval('#tinymde_commandbar', (el) => el.firstChild.firstChild.title)).toEqual('X');
83
+
84
+ });
@@ -0,0 +1,486 @@
1
+ const htmlRegExp = (html) => classTagRegExp(html, 'TMHTML');
2
+
3
+ const inlineLinkRegExp = (text, destination = '', title = '') => {
4
+ return new RegExp([
5
+ classTagRegExp(text, 'TMLink').source,
6
+ classTagRegExp(destination, 'TMLinkDestination').source,
7
+ classTagRegExp(title, 'TMLinkTitle').source
8
+ ].join('.*'));
9
+ }
10
+
11
+ const inlineImageRegExp = (text, destination = '', title = '') => {
12
+ return new RegExp([
13
+ classTagRegExp(text, 'TMImage').source,
14
+ classTagRegExp(destination, 'TMImageDestination').source,
15
+ classTagRegExp(title, 'TMImageTitle').source
16
+ ].join('.*'));
17
+ }
18
+
19
+ test('correctly parses * emphasis', async () => {
20
+ const editor = await initTinyMDE('*em*');
21
+ expect(await editor.lineHTML(0)).toMatch(/<em[^>]*>em<\/em>/);
22
+ editor.destroy();
23
+ });
24
+
25
+ test('correctly parses ** strong emphasis', async () => {
26
+ const editor = await initTinyMDE('**strong**');
27
+ expect(await editor.lineHTML(0)).toMatch(/<strong[^>]*>strong<\/strong>/);
28
+ editor.destroy();
29
+ });
30
+
31
+ test('triple emphasis *** becomes <em><strong>', async () => {
32
+ const editor = await initTinyMDE('***text***');
33
+ expect(await editor.lineHTML(0)).toMatch(/<em[^>]*>.*<strong[^>]*>text<\/strong>.*<\/em>/);
34
+ editor.destroy();
35
+ });
36
+
37
+ test('correctly parses _ emphasis', async () => {
38
+ const editor = await initTinyMDE('_XXXA_');
39
+ expect(await editor.lineHTML(0)).toMatch(/<em[^>]*>XXXA<\/em>/);
40
+ editor.destroy();
41
+ });
42
+
43
+ test('correctly parses __ strong emphasis', async () => {
44
+ const editor = await initTinyMDE('__XXXA__');
45
+ expect(await editor.lineHTML(0)).toMatch(/<strong[^>]*>XXXA<\/strong>/);
46
+ editor.destroy();
47
+ });
48
+
49
+ test('triple emphasis ___ becomes <em><strong>', async () => {
50
+ const editor = await initTinyMDE('___XXXA___');
51
+ expect(await editor.lineHTML(0)).toMatch(/<em[^>]*>.*<strong[^>]*>XXXA<\/strong>.*<\/em>/);
52
+ editor.destroy();
53
+ });
54
+
55
+ test('correctly parses ***a* b**', async () => {
56
+ const editor = await initTinyMDE('***XXXA* XXXB**');
57
+ expect(await editor.lineHTML(0)).toMatch(/<strong[^>]*>.*<em[^>]*>XXXA<\/em>.*XXXB<\/strong>/);
58
+ editor.destroy();
59
+ });
60
+
61
+ test('correctly parses ***a** b*', async () => {
62
+ const editor = await initTinyMDE('***XXXA** XXXB*');
63
+ expect(await editor.lineHTML(0)).toMatch(/<em[^>]*>.*<strong[^>]*>XXXA<\/strong>.*XXXB<\/em>/);
64
+ editor.destroy();
65
+ });
66
+
67
+ test('correctly parses *a **b***', async () => {
68
+ const editor = await initTinyMDE('*XXXA **XXXB***');
69
+ expect(await editor.lineHTML(0)).toMatch(/<em[^>]*>.*XXXA.*<strong[^>]*>XXXB<\/strong>.*<\/em>/);
70
+ editor.destroy();
71
+ });
72
+
73
+ test('correctly parses **a *b***', async () => {
74
+ const editor = await initTinyMDE('**XXXA *XXXB***');
75
+ expect(await editor.lineHTML(0)).toMatch(/<strong[^>]*>.*XXXA.*<em[^>]*>XXXB<\/em>.*<\/strong>/);
76
+ editor.destroy();
77
+ });
78
+
79
+ test('asterisk in word can close emphasis: *a*b*', async () => {
80
+ const editor = await initTinyMDE('*XXXA*XXXB*');
81
+ expect(await editor.lineHTML(0)).toMatch(/<em[^>]*>.*XXXA.*<\/em>.*XXXB/);
82
+ editor.destroy();
83
+ });
84
+
85
+ test('underscore in word can NOT close emphasis: _a_b_', async () => {
86
+ const editor = await initTinyMDE('_XXXA_XXXB_');
87
+ expect(await editor.lineHTML(0)).toMatch(/<em[^>]*>.*XXXA_XXXB.*<\/em>/);
88
+ editor.destroy();
89
+ });
90
+
91
+ test('correctly parses opening asterisk without closing: ***a*', async () => {
92
+ const editor = await initTinyMDE('***XXXA*');
93
+ expect(await editor.lineHTML(0)).toMatch(/\*\*.*<em[^>]*>.*XXXA.*<\/em>/);
94
+ editor.destroy();
95
+ });
96
+
97
+ test('correctly parses closing asterisk without opening: *a***', async () => {
98
+ const editor = await initTinyMDE('*XXXA***');
99
+ expect(await editor.lineHTML(0)).toMatch(/<em[^>]*>.*XXXA.*<\/em>.*\*\*/);
100
+ editor.destroy();
101
+ });
102
+
103
+ test('correctly parses opening asterisk without closing: ___a_', async () => {
104
+ const editor = await initTinyMDE('___XXXA_');
105
+ expect(await editor.lineHTML(0)).toMatch(/__.*<em[^>]*>.*XXXA.*<\/em>/);
106
+ editor.destroy();
107
+ });
108
+
109
+ test('correctly parses closing asterisk without opening: _a___', async () => {
110
+ const editor = await initTinyMDE('_XXXA___');
111
+ expect(await editor.lineHTML(0)).toMatch(/<em[^>]*>.*XXXA.*<\/em>.*__/);
112
+ editor.destroy();
113
+ });
114
+
115
+ test('Underscore in between punctuation can open emphasis: foo-_(bar)_', async () => {
116
+ const editor = await initTinyMDE('foo-_(bar)_');
117
+ expect(await editor.lineHTML(0)).toMatch(/foo-.*<em[^>]*>.*\(bar\).*<\/em>.*/);
118
+ editor.destroy();
119
+ });
120
+
121
+ test('Underscore next to punctuation can enclose emphasis: _(bar)_', async () => {
122
+ const editor = await initTinyMDE('_(bar)_');
123
+ expect(await editor.lineHTML(0)).toMatch(/<em[^>]*>.*\(bar\).*<\/em>.*/);
124
+ editor.destroy();
125
+ });
126
+
127
+ test('Emphasis works multiple times on the same line', async () => {
128
+ const editor = await initTinyMDE('Several *emphasized* words *and also* some *phrases* here');
129
+ expect(await editor.lineHTML(0))
130
+ .toMatch(/<em[^>]*>emphasized<\/em>.*<em[^>]*>and also<\/em>.*<em[^>]*>phrases<\/em>/);
131
+ editor.destroy();
132
+ })
133
+
134
+ test('Emphasis delimiters can be mixed and matched', async () => {
135
+ const editor = await initTinyMDE('__*Mixed* and matched__');
136
+ expect(await editor.lineHTML(0)).toMatch(/<strong[^>]*>.*<em[^>]*>Mixed<\/em>.*and matched<\/strong>/);
137
+ editor.destroy();
138
+ })
139
+
140
+ // Strikethrough ----------------------------------------------------
141
+ test('correctly parses ~~ strikethrough', async () => {
142
+ const editor = await initTinyMDE('~~strikethrough~~');
143
+ expect(await editor.lineHTML(0)).toMatch(/<del[^>]*>strikethrough<\/del>/);
144
+ editor.destroy();
145
+ });
146
+
147
+ test('Strikethrough can be nested inside emphasis: *A ~~B~~ C*', async () => {
148
+ const editor = await initTinyMDE('*XXXA ~~XXXB~~ XXXC*');
149
+ const html = await editor.lineHTML(0);
150
+ expect(html).toMatch(classTagRegExp('XXXB', 'TMStrikethrough', 'del'));
151
+ expect(html).toMatch(/<em[^>]*>/); // Ensure the emphasis is processed also
152
+ editor.destroy();
153
+ });
154
+
155
+ test('Strikethrough does not need to be left or right flanking: ~~ A ~~', async () => {
156
+ const editor = await initTinyMDE('~~ XXXA ~~');
157
+ expect(await editor.lineHTML(0)).toMatch(classTagRegExp(' XXXA ', 'TMStrikethrough', 'del'));
158
+ editor.destroy();
159
+ });
160
+
161
+ test('Emphasis and strikethrough bind left to right: *A ~~B* C~~', async () => {
162
+ const editor = await initTinyMDE('*XXXA ~~XXXB* XXXC~~\n~~XXXA *XXXB~~ XXXC*');
163
+ expect(await editor.lineHTML(0)).toMatch(/<em.*>/);
164
+ expect(await editor.lineHTML(0)).not.toMatch(/<del.*>/);
165
+ expect(await editor.lineHTML(1)).not.toMatch(/<em.*>/);
166
+ expect(await editor.lineHTML(1)).toMatch(/<del.*>/);
167
+ editor.destroy();
168
+ });
169
+
170
+
171
+ // Escape ----------------------------------------------------
172
+
173
+ test('ASCII punctuation can be backslash-escaped', async () => {
174
+ let punctuation = ['!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/', ':', ';', '<', '=', '>', '?', '@', '[', '\\', ']', '^', '_', '`', '{', '|', '}', '~'];
175
+ const editor = await initTinyMDE(punctuation.map(string => `\\${string}`).join('\n'));
176
+ for (let l = 0; l < punctuation.length; l++) {
177
+ expect(await editor.lineHTML(l)).toMatch(classTagRegExp('\\', 'TMMark_TMEscape'));
178
+ }
179
+ editor.destroy();
180
+ });
181
+
182
+ test('Non-ASCII-punctuation can NOT be backslash-escaped', async () => {
183
+ let nonPunctuation = ['→', 'A', 'a', ' ', '3', 'φ', '«'];
184
+ for (let p of nonPunctuation) {
185
+ const editor = await initTinyMDE(`\\${p}`);
186
+ expect(await editor.lineHTML(0)).not.toMatch(classTagRegExp('\\', 'TMMark_TMEscape'));
187
+ editor.destroy();
188
+ }
189
+ });
190
+
191
+ test('Single backtick code parsed correctly', async () => {
192
+ const editor = await initTinyMDE('Some `backtickcode` here');
193
+ expect(await editor.lineHTML(0)).toMatch(/Some.*<code[^>]*>backtickcode<\/code>.*here/);
194
+ editor.destroy();
195
+ });
196
+
197
+ test('Backtick escapes NOT processed in code', async () => {
198
+ const editor = await initTinyMDE('`\\!`');
199
+ expect(await editor.lineHTML(0)).toMatch(/<code[^>]*>\\!<\/code>/);
200
+ editor.destroy();
201
+ })
202
+
203
+ test('Backslash backtick ends code block: `code\\`', async () => {
204
+ const editor = await initTinyMDE('`XXXA\\`');
205
+ expect(await editor.lineHTML(0)).toMatch(/<code[^>]*>XXXA\\<\/code>/);
206
+ editor.destroy();
207
+ });
208
+
209
+ test('Double backtick code can contain single backtick: ``a`b``', async () => {
210
+ const editor = await initTinyMDE('``XXXA`XXXB``');
211
+ expect(await editor.lineHTML(0)).toMatch(/<code[^>]*>XXXA`XXXB<\/code>/);
212
+ editor.destroy();
213
+ });
214
+
215
+ test('Escaped backtick doesn\'t start code span', async () => {
216
+ const editor = await initTinyMDE('\\`XXXA`');
217
+ expect(await editor.lineHTML(0)).not.toMatch(/<code[^>]*>/);
218
+ editor.destroy();
219
+ });
220
+
221
+ // TODO Make this test pass
222
+ // test('Single space is stripped from both sides of code block: `` `a` ``', async () => {
223
+ // const editor = await initTinyMDE('`` `XXXA` ``');
224
+ // expect(await editor.lineHTML(0)).toMatch(/<code[^>]*>`XXXA`<\/code>/);
225
+ // });
226
+
227
+ test('Autolink binds more strongly than inline link: [this <https://]()>', async () => {
228
+ const editor = await initTinyMDE('[XXXA <XXXB://]()>');
229
+ const result = await editor.lineHTML(0);
230
+ expect(result).not.toMatch(/<span[^>]*class\s*=\s*["']?[^"'>]*TMLink[^>]*>XXXA/);
231
+ expect(result).toMatch(/<span[^>]*class\s*=\s*["']?[^"'>]*TMAutolink[^>]*>XXXB/);
232
+ editor.destroy();
233
+ });
234
+
235
+ test('HTML binds more strongly than inline link: [this <tag a="]()">', async () => {
236
+ const editor = await initTinyMDE('[XXXA <XXXB XXXC="]()">');
237
+ const result = await editor.lineHTML(0);
238
+ expect(result).not.toMatch(/<span[^>]*class\s*=\s*["']?[^"'>]*TMLink[^>]*>XXXA/);
239
+ expect(result).toMatch(classTagRegExp(`<XXXB XXXC="]()">`, 'TMHTML'));
240
+ editor.destroy();
241
+ });
242
+
243
+ test('Code span binds more strongly than inline link: [this `code]()>`', async () => {
244
+ const editor = await initTinyMDE('[XXXA `XXXB]()`');
245
+ const result = await editor.lineHTML(0);
246
+ expect(result).not.toMatch(/<span[^>]*class\s*=\s*["']?[^"'>]*TMLink[^>]*>XXXA/);
247
+ expect(result).toMatch(/<code[^>]*>XXXB\]\(\)<\/code>/);
248
+ editor.destroy();
249
+ });
250
+
251
+ test(`HTML open tag recognized: <a foo="bar" bam = 'baz <em>"</em>' _boolean zoop:33=zoop:33 />`, async () => {
252
+ const html = `<a foo="bar" bam = 'baz <em>"</em>' _boolean zoop:33=zoop:33 />`;
253
+ const editor = await initTinyMDE(`XXXA ${html}`);
254
+ expect(await editor.lineHTML(0)).toMatch(htmlRegExp(html));
255
+ editor.destroy();
256
+ })
257
+
258
+ test(`Invalid HTML open tags NOT recognized: <__> <33> <a h*ref="hi"> <a href="hi'> <a href=hi'> < a> <a/ > <foo bar=baz bim!bop /> <a href='a'title=title>`, async () => {
259
+ let html = `XXXA <__> <33> <a h*ref="hi"> <a href="hi'> <a href=hi'> < a> <a/ > <foo bar=baz bim!bop /> <a href='a'title=title>`;
260
+ const editor = await initTinyMDE(html);
261
+ expect(await editor.lineHTML(0)).not.toMatch('TMHTML');
262
+ editor.destroy();
263
+ })
264
+
265
+ test(`HTML close tag recognized: </html>`, async () => {
266
+ const editor = await initTinyMDE('XXXA </XXXA>');
267
+ expect(await editor.lineHTML(0)).toMatch(htmlRegExp('</XXXA>'));
268
+ editor.destroy();
269
+ });
270
+
271
+ test('HTML close tag can\'t have attributes: </tag a="b">', async () => {
272
+ const editor = await initTinyMDE('XXXA </tag a="b">');
273
+ expect(await editor.lineHTML(0)).not.toMatch('TMHTML');
274
+ editor.destroy();
275
+ });
276
+
277
+ test(`HTML comments recognized: <!--comment-->`, async () => {
278
+ const editor = await initTinyMDE('XXXA <!--XXXA-->');
279
+ expect(await editor.lineHTML(0)).toMatch(htmlRegExp('<!--XXXA-->'));
280
+ editor.destroy();
281
+ });
282
+
283
+ test('Invalid HTML comments NOT recognized: <!-- not -- valid -->, <!---->', async () => {
284
+ const editor = await initTinyMDE('XXXA <!-- not -- valid --> <!---->');
285
+ expect(await editor.lineHTML(0)).not.toMatch('TMHTML');
286
+ editor.destroy();
287
+ });
288
+
289
+ test('HTML processing instructions recognized: <? instruction ?>', async () => {
290
+ const editor = await initTinyMDE('XXXA <?XXXA?>');
291
+ expect(await editor.lineHTML(0)).toMatch(htmlRegExp('<?XXXA?>'));
292
+ editor.destroy();
293
+ });
294
+
295
+ test(`HTML declarations recognized: <!DOCTYPE html>, <!DECLARE >, <!DO the OK@#( fwekof'230-2= πππ>`, async () => {
296
+ let tests = [`<!DOCTYPE html>`, `<!DECLARE >`, `<!DO the OK@#( fwekof'230-2= πππ>`];
297
+ for (let test of tests) {
298
+ const editor = await initTinyMDE(`XXXA ${test}`);
299
+ expect(await editor.lineHTML(0)).toMatch(htmlRegExp(test));
300
+ editor.destroy();
301
+ }
302
+ });
303
+
304
+ test(`Invalid HTML declaration NOT recognized: <!DOCTYPE>`, async () => {
305
+ const editor = await initTinyMDE('XXXA <!DOCTYPE>');
306
+ expect(await editor.lineHTML(0)).not.toMatch('TMHTML');
307
+ editor.destroy();
308
+ });
309
+
310
+ test(`HTML CDATA section recognized: <![CDATA[A]]B]]>`, async () => {
311
+ const editor = await initTinyMDE('XXXA <![CDATA[A]]B]]>');
312
+ expect(await editor.lineHTML(0)).toMatch(htmlRegExp('<![CDATA[A]]B]]>'));
313
+ editor.destroy();
314
+ });
315
+
316
+ test(`Email autolinks recognized: <abc@def.gh>`, async () => {
317
+ const editor = await initTinyMDE('<abc@def.gh>');
318
+ expect(await editor.lineHTML(0)).toMatch(classTagRegExp('abc@def.gh', 'TMAutolink'));
319
+ editor.destroy();
320
+ });
321
+
322
+ test(`URI autolinks recognized: <http://foo.bar.baz/test?q=hello&id=22&boolean>`, async () => {
323
+ let link = `http://foo.bar.baz/test?q=hello&id=22&boolean`;
324
+ const editor = await initTinyMDE(`<${link}>`);
325
+ expect(await editor.lineHTML(0)).toMatch(classTagRegExp(link, 'TMAutolink'));
326
+ editor.destroy();
327
+ });
328
+
329
+ test(`Spaces not allowed in URI autolinks`, async () => {
330
+ const editor = await initTinyMDE('<http://foo.bar/baz bim>');
331
+ expect(await editor.lineHTML(0)).not.toMatch('TMAutolink');
332
+ editor.destroy();
333
+ });
334
+
335
+ test(`Simple inline link parsed correctly: [text](destination)`, async () => {
336
+ const editor = await initTinyMDE(`[XXXA](XXXB)`);
337
+ expect(await editor.lineHTML(0)).toMatch(inlineLinkRegExp('XXXA', 'XXXB'));
338
+ editor.destroy();
339
+ });
340
+
341
+ test(`Inline link destination can be in angle brackets: [text](<desti nation>)`, async () => {
342
+ const editor = await initTinyMDE(`[XXXA](<XXXB XXXC>)`);
343
+ expect(await editor.lineHTML(0)).toMatch(inlineLinkRegExp('XXXA', 'XXXB XXXC'));
344
+ editor.destroy();
345
+ });
346
+
347
+ test(`Inline link destination can't include spaces: [link](desti nation)`, async () => {
348
+ const editor = await initTinyMDE(`[XXXA](XXXB XXXC)`);
349
+ expect(await editor.lineHTML(0)).not.toMatch('TMLink');
350
+ editor.destroy();
351
+ })
352
+
353
+ test(`Inline link with unbalanced parenthesis in destination is invalid: [text]( ( )`, async () => {
354
+ const editor = await initTinyMDE(`[XXXA]( ( )`);
355
+ expect(await editor.lineHTML(0)).not.toMatch('TMLink');
356
+ editor.destroy();
357
+ });
358
+
359
+ test(`All link destination (none, <>) and title (", ', ()) delimiters work`, async () => {
360
+ const destDelim = [['', ''], ['<', '>']];
361
+ const titleDelim = [['"', '"'], [`'`, `'`], [`(`, `)`]];
362
+ for (let dd of destDelim) for (let td of titleDelim) {
363
+ let link = `[XXXA](${dd[0]}XXXB${dd[1]} ${td[0]}XXXC XXXD${td[1]})`;
364
+ const editor = await initTinyMDE(link);
365
+ expect(await editor.lineHTML(0)).toMatch(inlineLinkRegExp('XXXA', 'XXXB', 'XXXC XXXD'));
366
+ editor.destroy();
367
+ }
368
+ });
369
+
370
+ test(`Empty inline link works: []()`, async () => {
371
+ const editor = await initTinyMDE('[]()');
372
+ expect(await editor.lineHTML(0)).toMatch(inlineLinkRegExp('','',''));
373
+ editor.destroy();
374
+ })
375
+
376
+ test(`Formatting in link text works: [*em*](destination)`, async () => {
377
+ const editor = await initTinyMDE('[*XXXA*](XXXB)');
378
+ expect(await editor.lineHTML(0)).toMatch(/<span[^>]*class\s*=\s*["']?[^"'>]*TMLink[^>]*>.*<em[^>]*>XXXA<\/em>/);
379
+ editor.destroy();
380
+ });
381
+
382
+ test(`Links and emphasis bind left-to-right: [*em](destination)*`, async () => {
383
+ const editor = await initTinyMDE('[*XXXA](XXXB)*');
384
+ const output = await editor.lineHTML(0);
385
+ expect(output).not.toMatch(/<em[^>]*>/);
386
+ expect(output).toMatch(inlineLinkRegExp('*XXXA', 'XXXB'));
387
+ editor.destroy();
388
+ });
389
+
390
+ test(`Links can't be nested, inner link binds more strongly: [a [b](c) d](e)`, async () => {
391
+ const editor = await initTinyMDE('[XXXA [XXXB](XXXC) XXXD](XXXE)');
392
+ expect(await editor.lineHTML(0)).toMatch(inlineLinkRegExp('XXXB', 'XXXC'));
393
+ editor.destroy();
394
+ })
395
+
396
+ test(`Link text can contain images: [a ![b](c) d](e)`, async () => {
397
+ const editor = await initTinyMDE('[XXXA ![XXXB](XXXC) XXXD](XXXE)');
398
+ const result = await editor.lineHTML(0);
399
+ expect(result).toMatch(inlineImageRegExp('XXXB', 'XXXC'));
400
+ expect(result).toMatch(/<span[^>]*class\s*=\s*["']?[^>"']*TMLink[^>]*>XXXA.*<span[^>]*class\s*=\s*["']?[^>"']*TMImage[^>]*>XXXB/)
401
+ editor.destroy();
402
+ })
403
+
404
+
405
+ test(`Basic image works: ![](/url)`, async () => {
406
+ const editor = await initTinyMDE('![](/XXXA)');
407
+ expect(await editor.lineHTML(0)).toMatch(inlineImageRegExp('','/XXXA'));
408
+ editor.destroy();
409
+ })
410
+
411
+ test(`All image destination (none, <>) and title (", ', ()) delimiters work`, async () => {
412
+ const destDelim = [['', ''], ['<', '>']];
413
+ const titleDelim = [['"', '"'], [`'`, `'`], [`(`, `)`]];
414
+ for (let dd of destDelim) for (let td of titleDelim) {
415
+ const link = `![XXXA XXXY](${dd[0]}XXXB${dd[1]} ${td[0]}XXXC XXXD${td[1]})`;
416
+ const editor = await initTinyMDE(link);
417
+ expect(await editor.lineHTML(0)).toMatch(inlineImageRegExp('XXXA XXXY', 'XXXB', 'XXXC XXXD'));
418
+ editor.destroy();
419
+ }
420
+ });
421
+
422
+ test(`Formatting in image text works: ![*em*](destination)`, async () => {
423
+ const editor = await initTinyMDE('![*XXXA*](XXXB)');
424
+ expect(await editor.lineHTML(0)).toMatch(/<span[^>]*class\s*=\s*["']?[^"'>]*TMImage[^>]*>.*<em[^>]*>XXXA<\/em>/);
425
+ editor.destroy();
426
+ });
427
+
428
+ test(`Image description can contain links: ![a [b](c) d](e)`, async () => {
429
+ const text = '![XXXA [XXXB](XXXC) XXXD](XXXE)';
430
+ const editor = await initTinyMDE(text);
431
+ const result = await editor.lineHTML(0);
432
+ expect(result).toMatch(inlineLinkRegExp('XXXB', 'XXXC')); // Just check the link is there...
433
+ expect(result).toMatch(/<span[^>]*class\s*=\s*["']?[^>"']*TMImage[^>]*>XXXA.*<span[^>]*class\s*=\s*["']?[^>"']*TMLink[^>]*>XXXB/)
434
+ editor.destroy();
435
+ });
436
+
437
+ test(`Image description can contain images: ![a ![b](c) d](e)`, async () => {
438
+ const text = '![XXXA ![XXXB](XXXC) XXXD](XXXE)';
439
+ const editor = await initTinyMDE(text);
440
+ const result = await editor.lineHTML(0);
441
+ expect(result).toMatch(inlineImageRegExp('XXXB', 'XXXC')); // Just check the link is there...
442
+ expect(result).toMatch(/<span[^>]*class\s*=\s*["']?[^>"']*TMImage[^>]*>XXXA.*<span[^>]*class\s*=\s*["']?[^>"']*TMImage[^>]*>XXXB/)
443
+ editor.destroy();
444
+ });
445
+
446
+ test(`Simple ref link works: [text][ref]`, async () => {
447
+ const text = '[XXXB]: https://abc.de\n[XXXA][XXXB]';
448
+ const editor = await initTinyMDE(text);
449
+ expect(await editor.lineHTML(1)).toMatch(/<span[^>]*class\s*=\s*["']?[^"'>]*TMLink[^>]*>XXXA.*<span[^>]*class\s*=\s*["']?[^"'>]*TMLinkLabel_Valid[^>]*>XXXB/);
450
+ editor.destroy();
451
+ });
452
+
453
+ test(`Spaces in link label get ignored: [text][ ref ]`, async () => {
454
+ const text = '[ XXXB ]: https://abc.de\n[XXXA][ XXXB ]';
455
+ const editor = await initTinyMDE(text);
456
+ expect(await editor.lineHTML(1)).toMatch(/<span[^>]*class\s*=\s*["']?[^"'>]*TMLink[^>]*>XXXA.*<span[^>]*class\s*=\s*["']?[^"'>]*TMLinkLabel_Valid[^>]*>XXXB/);
457
+ editor.destroy();
458
+ });
459
+
460
+ test(`Collapsed ref link works: [ref][]`, async () => {
461
+ const text = '[XXXA]: https://abc.de\n[XXXA][]';
462
+ const editor = await initTinyMDE(text);
463
+ expect(await editor.lineHTML(1)).toMatch(/<span[^>]*class\s*=\s*["']?[^"'>]*TMLinkLabel_Valid[^>]*>XXXA/);
464
+ editor.destroy();
465
+ });
466
+
467
+ test(`Shortcut ref link works: [ref]`, async () => {
468
+ const text = '[XXXA]: https://abc.de\n[XXXA]';
469
+ const editor = await initTinyMDE(text);
470
+ expect(await editor.lineHTML(1)).toMatch(/<span[^>]*class\s*=\s*["']?[^"'>]*TMLinkLabel_Valid[^>]*>XXXA/);
471
+ editor.destroy();
472
+ });
473
+
474
+ test(`Valid and invalid link references correctly identified`, async () => {
475
+ const editor = await initTinyMDE('[XXXA]: https://abc.de\n[XXXA]\n[XXXB]');
476
+ expect (await editor.lineHTML(1)).toMatch(/<span[^>]*class\s*=\s*["']?[^"'>]*TMLinkLabel_Valid[^>]*>XXXA/);
477
+ expect (await editor.lineHTML(2)).toMatch(/<span[^>]*class\s*=\s*["']?[^"'>]*TMLinkLabel_Invalid[^>]*>XXXB/);
478
+ editor.destroy();
479
+ });
480
+
481
+ test(`Simple ref image works: ![text][ref]`, async () => {
482
+ const text = '[XXXB]: https://abc.de\n![XXXA][XXXB]';
483
+ const editor = await initTinyMDE(text);
484
+ expect(await editor.lineHTML(1)).toMatch(/<span[^>]*class\s*=\s*["']?[^"'>]*TMImage[^>]*>XXXA.*<span[^>]*class\s*=\s*["']?[^"'>]*TMLinkLabel_Valid[^>]*>XXXB/);
485
+ editor.destroy();
486
+ });
@@ -0,0 +1,31 @@
1
+ beforeEach(async () => {
2
+ await page.goto(PATH, { waitUntil: 'load'});
3
+ });
4
+
5
+ test('Bulleted list is continued when pressing enter', async () => {
6
+ await page.evaluate(() => {
7
+ document.tinyMDE = new TinyMDE.Editor({element: 'tinymde', content: '- Line 1\n- Line 2'});
8
+ document.getElementById('tinymde').firstChild.focus();
9
+ });
10
+ await select(page, 1, 8);
11
+ await page.keyboard.press('Enter');
12
+ expect(await page.evaluate(() => document.tinyMDE.getContent())).toEqual('- Line 1\n- Line 2\n- ');
13
+ });
14
+
15
+ test('Numbered list is continued when pressing enter', async () => {
16
+ await page.evaluate(() => {
17
+ document.tinyMDE = new TinyMDE.Editor({element: 'tinymde', content: '1) Line 1\n2) Line 2'});
18
+ document.getElementById('tinymde').firstChild.focus();
19
+ });
20
+ await select(page, 1, 9);
21
+ await page.keyboard.press('Enter');
22
+ expect(await page.evaluate(() => document.tinyMDE.getContent())).toEqual('1) Line 1\n2) Line 2\n3) ');
23
+ });
24
+
25
+ test('Pasting works without a focus', async () => {
26
+ await page.evaluate(() => {
27
+ document.tinyMDE = new TinyMDE.Editor({element: 'tinymde', content: 'This is\na test'});
28
+ document.tinyMDE.paste(' for\npasting');
29
+ });
30
+ expect(await page.evaluate(() => document.tinyMDE.getContent())).toEqual('This is\na test for\npasting');
31
+ })