You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			154 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			Python
		
	
			
		
		
	
	
			154 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			Python
		
	
from typing import Any, ClassVar, Dict, Optional, Tuple, Literal
 | 
						|
from ..core import BaseRenderer, BlockState
 | 
						|
from ..util import escape as escape_text
 | 
						|
from ..util import safe_entity, striptags
 | 
						|
 | 
						|
 | 
						|
class HTMLRenderer(BaseRenderer):
 | 
						|
    """A renderer for converting Markdown to HTML."""
 | 
						|
 | 
						|
    _escape: bool
 | 
						|
    NAME: ClassVar[Literal["html"]] = "html"
 | 
						|
    HARMFUL_PROTOCOLS: ClassVar[Tuple[str, ...]] = (
 | 
						|
        "javascript:",
 | 
						|
        "vbscript:",
 | 
						|
        "file:",
 | 
						|
        "data:",
 | 
						|
    )
 | 
						|
    GOOD_DATA_PROTOCOLS: ClassVar[Tuple[str, ...]] = (
 | 
						|
        "data:image/gif;",
 | 
						|
        "data:image/png;",
 | 
						|
        "data:image/jpeg;",
 | 
						|
        "data:image/webp;",
 | 
						|
    )
 | 
						|
 | 
						|
    def __init__(self, escape: bool = True, allow_harmful_protocols: Optional[bool] = None) -> None:
 | 
						|
        super(HTMLRenderer, self).__init__()
 | 
						|
        self._allow_harmful_protocols = allow_harmful_protocols
 | 
						|
        self._escape = escape
 | 
						|
 | 
						|
    def render_token(self, token: Dict[str, Any], state: BlockState) -> str:
 | 
						|
        # backward compitable with v2
 | 
						|
        func = self._get_method(token["type"])
 | 
						|
        attrs = token.get("attrs")
 | 
						|
 | 
						|
        if "raw" in token:
 | 
						|
            text = token["raw"]
 | 
						|
        elif "children" in token:
 | 
						|
            text = self.render_tokens(token["children"], state)
 | 
						|
        else:
 | 
						|
            if attrs:
 | 
						|
                return func(**attrs)
 | 
						|
            else:
 | 
						|
                return func()
 | 
						|
        if attrs:
 | 
						|
            return func(text, **attrs)
 | 
						|
        else:
 | 
						|
            return func(text)
 | 
						|
 | 
						|
    def safe_url(self, url: str) -> str:
 | 
						|
        """Ensure the given URL is safe. This method is used for rendering
 | 
						|
        links, images, and etc.
 | 
						|
        """
 | 
						|
        if self._allow_harmful_protocols is True:
 | 
						|
            return escape_text(url)
 | 
						|
 | 
						|
        _url = url.lower()
 | 
						|
        if self._allow_harmful_protocols and _url.startswith(tuple(self._allow_harmful_protocols)):
 | 
						|
            return escape_text(url)
 | 
						|
 | 
						|
        if _url.startswith(self.HARMFUL_PROTOCOLS) and not _url.startswith(self.GOOD_DATA_PROTOCOLS):
 | 
						|
            return "#harmful-link"
 | 
						|
        return escape_text(url)
 | 
						|
 | 
						|
    def text(self, text: str) -> str:
 | 
						|
        if self._escape:
 | 
						|
            return escape_text(text)
 | 
						|
        return safe_entity(text)
 | 
						|
 | 
						|
    def emphasis(self, text: str) -> str:
 | 
						|
        return "<em>" + text + "</em>"
 | 
						|
 | 
						|
    def strong(self, text: str) -> str:
 | 
						|
        return "<strong>" + text + "</strong>"
 | 
						|
 | 
						|
    def link(self, text: str, url: str, title: Optional[str] = None) -> str:
 | 
						|
        s = '<a href="' + self.safe_url(url) + '"'
 | 
						|
        if title:
 | 
						|
            s += ' title="' + safe_entity(title) + '"'
 | 
						|
        return s + ">" + text + "</a>"
 | 
						|
 | 
						|
    def image(self, text: str, url: str, title: Optional[str] = None) -> str:
 | 
						|
        src = self.safe_url(url)
 | 
						|
        alt = escape_text(striptags(text))
 | 
						|
        s = '<img src="' + src + '" alt="' + alt + '"'
 | 
						|
        if title:
 | 
						|
            s += ' title="' + safe_entity(title) + '"'
 | 
						|
        return s + " />"
 | 
						|
 | 
						|
    def codespan(self, text: str) -> str:
 | 
						|
        return "<code>" + escape_text(text) + "</code>"
 | 
						|
 | 
						|
    def linebreak(self) -> str:
 | 
						|
        return "<br />\n"
 | 
						|
 | 
						|
    def softbreak(self) -> str:
 | 
						|
        return "\n"
 | 
						|
 | 
						|
    def inline_html(self, html: str) -> str:
 | 
						|
        if self._escape:
 | 
						|
            return escape_text(html)
 | 
						|
        return html
 | 
						|
 | 
						|
    def paragraph(self, text: str) -> str:
 | 
						|
        return "<p>" + text + "</p>\n"
 | 
						|
 | 
						|
    def heading(self, text: str, level: int, **attrs: Any) -> str:
 | 
						|
        tag = "h" + str(level)
 | 
						|
        html = "<" + tag
 | 
						|
        _id = attrs.get("id")
 | 
						|
        if _id:
 | 
						|
            html += ' id="' + _id + '"'
 | 
						|
        return html + ">" + text + "</" + tag + ">\n"
 | 
						|
 | 
						|
    def blank_line(self) -> str:
 | 
						|
        return ""
 | 
						|
 | 
						|
    def thematic_break(self) -> str:
 | 
						|
        return "<hr />\n"
 | 
						|
 | 
						|
    def block_text(self, text: str) -> str:
 | 
						|
        return text
 | 
						|
 | 
						|
    def block_code(self, code: str, info: Optional[str] = None) -> str:
 | 
						|
        html = "<pre><code"
 | 
						|
        if info is not None:
 | 
						|
            info = safe_entity(info.strip())
 | 
						|
        if info:
 | 
						|
            lang = info.split(None, 1)[0]
 | 
						|
            html += ' class="language-' + lang + '"'
 | 
						|
        return html + ">" + escape_text(code) + "</code></pre>\n"
 | 
						|
 | 
						|
    def block_quote(self, text: str) -> str:
 | 
						|
        return "<blockquote>\n" + text + "</blockquote>\n"
 | 
						|
 | 
						|
    def block_html(self, html: str) -> str:
 | 
						|
        if self._escape:
 | 
						|
            return "<p>" + escape_text(html.strip()) + "</p>\n"
 | 
						|
        return html + "\n"
 | 
						|
 | 
						|
    def block_error(self, text: str) -> str:
 | 
						|
        return '<div class="error"><pre>' + text + "</pre></div>\n"
 | 
						|
 | 
						|
    def list(self, text: str, ordered: bool, **attrs: Any) -> str:
 | 
						|
        if ordered:
 | 
						|
            html = "<ol"
 | 
						|
            start = attrs.get("start")
 | 
						|
            if start is not None:
 | 
						|
                html += ' start="' + str(start) + '"'
 | 
						|
            return html + ">\n" + text + "</ol>\n"
 | 
						|
        return "<ul>\n" + text + "</ul>\n"
 | 
						|
 | 
						|
    def list_item(self, text: str) -> str:
 | 
						|
        return "<li>" + text + "</li>\n"
 |