spoiler.py 2.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677
  1. import re
  2. from xml.etree.ElementTree import SubElement
  3. import markdown
  4. from django.utils.crypto import get_random_string
  5. from markdown.blockprocessors import BlockProcessor
  6. from markdown.preprocessors import Preprocessor
  7. SPOILER_START = get_random_string(32)
  8. SPOILER_END = get_random_string(32)
  9. class SpoilerExtension(markdown.Extension):
  10. def extendMarkdown(self, md):
  11. md.registerExtension(self)
  12. md.preprocessors.register(SpoilerPreprocessor(md), "misago_bbcode_spoiler", 200)
  13. md.parser.blockprocessors.register(
  14. SpoilerBlockProcessor(md.parser), "misago_bbcode_spoiler", 85
  15. )
  16. class SpoilerPreprocessor(Preprocessor):
  17. SPOILER_BLOCK_RE = re.compile(
  18. r"""
  19. \[spoiler\](?P<text>.*?)\[/spoiler\]
  20. """.strip(),
  21. re.IGNORECASE | re.MULTILINE | re.DOTALL,
  22. )
  23. def run(self, lines):
  24. text = "\n".join(lines)
  25. while self.SPOILER_BLOCK_RE.search(text):
  26. text = self.SPOILER_BLOCK_RE.sub(self.replace, text)
  27. return text.split("\n")
  28. def replace(self, matchobj):
  29. text = matchobj.group("text")
  30. return "\n\n%s\n\n%s\n\n%s\n\n" % (SPOILER_START, text, SPOILER_END)
  31. class SpoilerBlockProcessor(BlockProcessor):
  32. def __init__(self, *args, **kwargs):
  33. super().__init__(*args, **kwargs)
  34. self._spoiler = 0
  35. self._children = []
  36. def test(self, parent, block):
  37. return block.strip().startswith(SPOILER_START) or self._spoiler
  38. def run(self, parent, blocks):
  39. block = blocks.pop(0)
  40. if block.strip().startswith(SPOILER_START):
  41. self._spoiler += 1
  42. self._children.append(block)
  43. if block.strip() == SPOILER_END:
  44. self._spoiler -= 1
  45. if not self._spoiler:
  46. children, self._children = self._children[1:-1], []
  47. aside = SubElement(parent, "aside")
  48. aside.set("class", "spoiler-block")
  49. blockquote = SubElement(aside, "blockquote")
  50. blockquote.set("class", "spoiler-body")
  51. overlay = SubElement(aside, "div")
  52. overlay.set("class", "spoiler-overlay")
  53. reveal_btn = SubElement(overlay, "button")
  54. reveal_btn.set("class", "spoiler-reveal")
  55. reveal_btn.set("type", "button")
  56. self.parser.parseBlocks(blockquote, children)