edwh-editorjs 2.0.0b4__py3-none-any.whl → 2.0.1__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.
editorjs/__about__.py CHANGED
@@ -1 +1 @@
1
- __version__ = "2.0.0-beta.4"
1
+ __version__ = "2.0.1"
editorjs/blocks.py CHANGED
@@ -6,6 +6,9 @@ import abc
6
6
  import re
7
7
  import typing as t
8
8
  from html.parser import HTMLParser
9
+ from urllib.parse import urlparse
10
+
11
+ import markdown2
9
12
 
10
13
  from .exceptions import TODO
11
14
  from .types import EditorChildData, MDChildNode
@@ -141,6 +144,7 @@ class ParagraphBlock(EditorJSBlock):
141
144
 
142
145
  skip = 0
143
146
  nodes = node.get("children", [])
147
+ any_html = False
144
148
 
145
149
  for idx, child in enumerate(nodes):
146
150
  if skip:
@@ -148,6 +152,7 @@ class ParagraphBlock(EditorJSBlock):
148
152
  continue
149
153
 
150
154
  _type = child.get("type")
155
+ any_html |= _type == "html"
151
156
 
152
157
  # deal with custom types
153
158
  if _type == "html" and child.get("value", "").startswith("<editorjs"):
@@ -160,7 +165,7 @@ class ParagraphBlock(EditorJSBlock):
160
165
  else:
161
166
  # <editorjs>something</editorjs> = 3 children
162
167
  result.extend(
163
- EditorJSCustom.to_json({"children": nodes[idx : idx + 2]})
168
+ EditorJSCustom.to_json({"children": nodes[idx: idx + 2]})
164
169
  )
165
170
 
166
171
  skip = 2
@@ -168,8 +173,9 @@ class ParagraphBlock(EditorJSBlock):
168
173
 
169
174
  elif _type == "image":
170
175
  if current_text:
171
- result.append({"data": {"text": current_text}, "type": "paragraph"})
176
+ result.append({"data": {"text": current_text}, "type": "raw" if any_html else "paragraph"})
172
177
  current_text = ""
178
+ any_html = False # reset
173
179
 
174
180
  result.extend(ImageBlock.to_json(child))
175
181
  else:
@@ -188,7 +194,7 @@ class ParagraphBlock(EditorJSBlock):
188
194
 
189
195
  # final text after image:
190
196
  if current_text:
191
- result.append({"data": {"text": current_text}, "type": "paragraph"})
197
+ result.append({"data": {"text": current_text}, "type": "raw" if any_html else "paragraph"})
192
198
 
193
199
  return result
194
200
 
@@ -217,7 +223,7 @@ class ListBlock(EditorJSBlock):
217
223
 
218
224
  return "\n".join(markdown_items)
219
225
 
220
- return "\n" + parse_items(items) + "\n"
226
+ return "\n" + parse_items(items) + "\n\n"
221
227
 
222
228
  @classmethod
223
229
  def to_json(cls, node: MDChildNode) -> list[dict]:
@@ -311,7 +317,7 @@ class ChecklistBlock(ListBlock):
311
317
  char = "x" if item.get("checked", False) else " "
312
318
  markdown_items.append(f"- [{char}] {text}")
313
319
 
314
- return "\n" + "\n".join(markdown_items) + "\n"
320
+ return "\n" + "\n".join(markdown_items) + "\n\n"
315
321
 
316
322
 
317
323
  @block("thematicBreak", "delimiter")
@@ -419,6 +425,22 @@ class QuoteBlock(EditorJSBlock):
419
425
  return default_to_text(node)
420
426
 
421
427
 
428
+ @block("raw")
429
+ class RawBlock(EditorJSBlock):
430
+
431
+ @classmethod
432
+ def to_markdown(cls, data: EditorChildData) -> str:
433
+ return data.get("html", "")
434
+
435
+ @classmethod
436
+ def to_json(cls, node: MDChildNode) -> list[dict]:
437
+ raise TODO(node)
438
+
439
+ @classmethod
440
+ def to_text(cls, node: MDChildNode) -> str:
441
+ raise TODO(node)
442
+
443
+
422
444
  @block("table")
423
445
  class TableBlock(EditorJSBlock):
424
446
 
@@ -474,8 +496,10 @@ class TableBlock(EditorJSBlock):
474
496
  return [
475
497
  {
476
498
  "type": "table",
477
- "content": table,
478
- "withHeadings": with_headings,
499
+ "data": {
500
+ "content": table,
501
+ "withHeadings": with_headings,
502
+ }
479
503
  }
480
504
  ]
481
505
 
@@ -515,7 +539,66 @@ class LinkBlock(EditorJSBlock):
515
539
 
516
540
  @classmethod
517
541
  def to_text(cls, node: MDChildNode) -> str:
518
- return ""
542
+ url = node.get("href", "")
543
+ image = node.get("image", "")
544
+ title = node.get("title", "")
545
+ body = node.get("body", "")
546
+ domain = urlparse(url).netloc
547
+
548
+ return f"""
549
+ <div class="link-tool">
550
+ <a class="link-tool__content link-tool__content--rendered" target="_blank"
551
+ rel="nofollow noindex noreferrer" href="{url}">
552
+ <div class="link-tool__image"
553
+ style="background-image: url(&quot;{image}&quot;);"></div>
554
+ <div class="link-tool__title">{title}</div>
555
+ <p class="link-tool__description">{body}</p>
556
+ <span class="link-tool__anchor">{domain}</span>
557
+ </a>
558
+ </div>
559
+ """
560
+
561
+
562
+ @block("attaches")
563
+ class AttachmentBlock(EditorJSBlock):
564
+
565
+ @classmethod
566
+ def to_markdown(cls, data: EditorChildData) -> str:
567
+ file = data.get("file", {}).get("url", "")
568
+ title = data.get("title", "")
569
+ return f"""<editorjs type="attaches" file="{file}">{title}</editorjs>"""
570
+
571
+ @classmethod
572
+ def to_json(cls, node: MDChildNode) -> list[dict]:
573
+ return [
574
+ {
575
+ "type": "attaches",
576
+ "data": {
577
+ "file": {"url": node.get("file", "")},
578
+ "title": node.get("body", ""),
579
+ },
580
+ }
581
+ ]
582
+
583
+ @classmethod
584
+ def to_text(cls, node: MDChildNode) -> str:
585
+ return f"""
586
+ <div class="cdx-attaches cdx-attaches--with-file">
587
+ <div class="cdx-attaches__file-icon">
588
+ <div class="cdx-attaches__file-icon-background">
589
+ <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13.3236 8.43554L9.49533 12.1908C9.13119 12.5505 8.93118 13.043 8.9393 13.5598C8.94741 14.0767 9.163 14.5757 9.53862 14.947C9.91424 15.3182 10.4191 15.5314 10.9422 15.5397C11.4653 15.5479 11.9637 15.3504 12.3279 14.9908L16.1562 11.2355C16.8845 10.5161 17.2845 9.53123 17.2682 8.4975C17.252 7.46376 16.8208 6.46583 16.0696 5.72324C15.3184 4.98066 14.3086 4.55425 13.2624 4.53782C12.2162 4.52138 11.2193 4.91627 10.4911 5.63562L6.66277 9.39093C5.57035 10.4699 4.97032 11.9473 4.99467 13.4979C5.01903 15.0485 5.66578 16.5454 6.79264 17.6592C7.9195 18.7731 9.43417 19.4127 11.0034 19.4374C12.5727 19.462 14.068 18.8697 15.1604 17.7907L18.9887 14.0354"></path></svg>
590
+ </div>
591
+ </div>
592
+ <div class="cdx-attaches__file-info">
593
+ <div class="cdx-attaches__title" data-placeholder="File title" data-empty="false">
594
+ {node.get("body", "")}
595
+ </div>
596
+ </div>
597
+ <a class="cdx-attaches__download-button" href="{node.get('file', '')}" target="_blank" rel="nofollow noindex noreferrer">
598
+ <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path stroke="currentColor" stroke-linecap="round" stroke-width="2" d="M7 10L11.8586 14.8586C11.9367 14.9367 12.0633 14.9367 12.1414 14.8586L17 10"></path></svg>
599
+ </a>
600
+ </div>
601
+ """
519
602
 
520
603
 
521
604
  class AttributeParser(HTMLParser):
@@ -532,11 +615,14 @@ class AttributeParser(HTMLParser):
532
615
  self.data = data
533
616
 
534
617
 
535
- class EditorJSCustom(EditorJSBlock):
618
+ class EditorJSCustom(EditorJSBlock, markdown2.Extra):
536
619
  """
537
620
  Special type of block to deal with custom attributes
538
621
  """
539
622
 
623
+ name = "editorjs"
624
+ order = (), (markdown2.Stage.POSTPROCESS,)
625
+
540
626
  @classmethod
541
627
  def parse_html(cls, html: str):
542
628
  parser = AttributeParser()
@@ -563,3 +649,27 @@ class EditorJSCustom(EditorJSBlock):
563
649
  @classmethod
564
650
  def to_text(cls, node: MDChildNode) -> str:
565
651
  raise TODO()
652
+
653
+ # markdown2:
654
+ re_short = re.compile(r"<editorjs.*?/>")
655
+ re_long = re.compile(r"<editorjs.*?>.*?</editorjs>")
656
+
657
+ def run(self, text: str) -> str:
658
+ def replace_html(match):
659
+ attrs, body = self.parse_html(match.group())
660
+ _type = attrs.get("type", "")
661
+ attrs.setdefault("body", body) # only if there is no such attribute yet
662
+
663
+ if not (handler := BLOCKS.get(_type)):
664
+ raise ValueError(f"Unknown custom type {_type}")
665
+
666
+ return handler.to_text(attrs)
667
+
668
+ # Substitute using the replacement functions
669
+ text = self.re_long.sub(replace_html, text)
670
+ text = self.re_short.sub(replace_html, text)
671
+
672
+ return text
673
+
674
+
675
+ EditorJSCustom.register()
editorjs/core.py CHANGED
@@ -1,5 +1,4 @@
1
1
  import json
2
- import textwrap
3
2
  import typing as t
4
3
 
5
4
  import markdown2
@@ -7,7 +6,6 @@ import mdast
7
6
  from typing_extensions import Self
8
7
 
9
8
  from .blocks import BLOCKS
10
- from .exceptions import TODO
11
9
  from .helpers import unix_timestamp
12
10
  from .types import MDRootNode
13
11
 
@@ -21,7 +19,7 @@ class EditorJS:
21
19
  def __init__(
22
20
  self,
23
21
  _mdast: str | dict,
24
- extras: list = ("task_list", "fenced-code-blocks", "tables"),
22
+ extras: list = ("task_list", "fenced-code-blocks", "tables", "editorjs"),
25
23
  ):
26
24
  if not isinstance(_mdast, str | dict):
27
25
  raise TypeError("Only `str` or `dict` is supported!")
@@ -102,7 +100,7 @@ class EditorJS:
102
100
  Export HTML string
103
101
  """
104
102
  md = self.to_markdown()
105
- # todo: deal with custom elements like linktool
103
+ # todo: deal with custom elements like linktool, attaches
106
104
  return self._md.convert(md)
107
105
 
108
106
  def __repr__(self):
@@ -1,11 +1,9 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: edwh-editorjs
3
- Version: 2.0.0b4
3
+ Version: 2.0.1
4
4
  Summary: EditorJS.py
5
5
  Project-URL: Homepage, https://github.com/educationwarehouse/edwh-EditorJS
6
6
  Author-email: SKevo <skevo.cw@gmail.com>, Robin van der Noord <robin.vdn@educationwarehouse.nl>
7
- License: MIT
8
- License-File: LICENSE
9
7
  Keywords: bleach,clean,editor,editor.js,html,javascript,json,parser,wysiwyg
10
8
  Classifier: Development Status :: 4 - Beta
11
9
  Classifier: Intended Audience :: Developers
@@ -0,0 +1,10 @@
1
+ editorjs/__about__.py,sha256=wAxkK8w13vqoF47A8iqWdSlIgRRXmZiQ0R4wePZfzhs,22
2
+ editorjs/__init__.py,sha256=-OHUf7ZXfkbdFB1r85eIjpHRfql-GCNUCKuBEdEt2Rc,58
3
+ editorjs/blocks.py,sha256=QBHiAck6lX6454XhixRuWWzzsYEcMWTUGlxHzG1B8gM,21871
4
+ editorjs/core.py,sha256=CdZnprX-vkjFJATxPfG1acDqGKkHa3mDfbMEeXiR6eI,3331
5
+ editorjs/exceptions.py,sha256=TyfHvk2Z5RbKxTDK7lrjgwAgVgInXIuDW63eO5jzVFw,106
6
+ editorjs/helpers.py,sha256=q861o5liNibMTp-Ozay17taF7CTNsRe901lYhhxdwHg,73
7
+ editorjs/types.py,sha256=W7IZWMWgzJaQulybIt0Gx5N63rVj4mEy73VJWo4VAQA,1029
8
+ edwh_editorjs-2.0.1.dist-info/METADATA,sha256=ojEg0-0U-zubbzWHmU6raAtHdgwSWNWFSvyrNHk2iy8,972
9
+ edwh_editorjs-2.0.1.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
10
+ edwh_editorjs-2.0.1.dist-info/RECORD,,
@@ -1,11 +0,0 @@
1
- editorjs/__about__.py,sha256=nNwsxbhqslw4VX_3DwOL0FdYImal5yO3Y3Co_qiJaQk,29
2
- editorjs/__init__.py,sha256=-OHUf7ZXfkbdFB1r85eIjpHRfql-GCNUCKuBEdEt2Rc,58
3
- editorjs/blocks.py,sha256=g9gP09aIc63gRU7kbI7by1cJyvPFm69JxYn_43xblpM,17230
4
- editorjs/core.py,sha256=RiJYbQk6ladkOuTM7Gj7nrmHO_ud6qQg31dhTxMn6bI,3354
5
- editorjs/exceptions.py,sha256=TyfHvk2Z5RbKxTDK7lrjgwAgVgInXIuDW63eO5jzVFw,106
6
- editorjs/helpers.py,sha256=q861o5liNibMTp-Ozay17taF7CTNsRe901lYhhxdwHg,73
7
- editorjs/types.py,sha256=W7IZWMWgzJaQulybIt0Gx5N63rVj4mEy73VJWo4VAQA,1029
8
- edwh_editorjs-2.0.0b4.dist-info/METADATA,sha256=x9NF5PqOaUK-5YaAMrqABybTV0SmX4LsRAq2rotleOo,1009
9
- edwh_editorjs-2.0.0b4.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
10
- edwh_editorjs-2.0.0b4.dist-info/licenses/LICENSE,sha256=zzllbk0pvnmgzk31iq8Zkg0GkA8vVx_Zc3OHjVlTjxo,1101
11
- edwh_editorjs-2.0.0b4.dist-info/RECORD,,
@@ -1,22 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2021 SKevo
4
- Copyright (c) 2024 Education Warehouse
5
-
6
- Permission is hereby granted, free of charge, to any person obtaining a copy
7
- of this software and associated documentation files (the "Software"), to deal
8
- in the Software without restriction, including without limitation the rights
9
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
- copies of the Software, and to permit persons to whom the Software is
11
- furnished to do so, subject to the following conditions:
12
-
13
- The above copyright notice and this permission notice shall be included in all
14
- copies or substantial portions of the Software.
15
-
16
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
- SOFTWARE.