diff --git a/lib/assets/Lib/browser/markdown.py b/lib/assets/Lib/browser/markdown.py new file mode 100644 --- /dev/null +++ b/lib/assets/Lib/browser/markdown.py @@ -0,0 +1,423 @@ +# -*- coding: utf-8 -*- + +try: + import _jsre as re +except: + import re + +import random +import time + +letters = 'abcdefghijklmnopqrstuvwxyz' +letters += letters.upper()+'0123456789' + +class URL: + def __init__(self,src): + elts = src.split(maxsplit=1) + self.href = elts[0] + self.alt = '' + if len(elts)==2: + alt = elts[1] + if alt[0]=='"' and alt[-1]=='"':self.alt=alt[1:-1] + elif alt[0]=="'" and alt[-1]=="'":self.alt=alt[1:-1] + elif alt[0]=="(" and alt[-1]==")":self.alt=alt[1:-1] + +class CodeBlock: + def __init__(self,line): + self.lines = [line] + if line.startswith("```") and len(line)>3: + self.info = line[3:] + else: + self.info = None + + def to_html(self): + if self.lines[0].startswith("`"): + self.lines.pop(0) + res = escape('\n'.join(self.lines)) + res = unmark(res) + _class = self.info or "marked" + res = '
%s
\n' %(_class, res) + return res,[] + +class HtmlBlock: + + def __init__(self, src): + self.src = src + + def to_html(self): + return self.src + +class Marked: + def __init__(self, line=''): + self.line = line + self.children = [] + + def to_html(self): + return apply_markdown(self.line) + +# get references +refs = {} +ref_pattern = r"^\[(.*)\]:\s+(.*)" + +def mark(src): + + global refs + t0 = time.time() + refs = {} + # split source in sections + # sections can be : + # - a block-level HTML element (markdown syntax will not be processed) + # - a script + # - a span-level HTML tag (markdown syntax will be processed) + # - a code block + + # normalise line feeds + src = src.replace('\r\n','\n') + + # lines followed by dashes + src = re.sub(r'(.*?)\n=+\n', '\n# \\1\n', src) + src = re.sub(r'(.*?)\n-+\n', '\n## \\1\n', src) + + lines = src.split('\n')+[''] + + i = bq = 0 + ul = ol = 0 + + while i in a blockquote + if lines[i].startswith('>'): + nb = 1 + while nb': + nb += 1 + lines[i] = lines[i][nb:] + if nb>bq: + lines.insert(i,'
'*(nb-bq)) + i += 1 + bq = nb + elif nb'*(bq-nb)) + i += 1 + bq = nb + elif bq>0: + lines.insert(i,'
'*bq) + i += 1 + bq = 0 + + # unordered lists + if lines[i].strip() and lines[i].lstrip()[0] in '-+*' \ + and len(lines[i].lstrip())>1 \ + and lines[i].lstrip()[1]==' ' \ + and (i==0 or ul or not lines[i-1].strip()): + # line indentation indicates nesting level + nb = 1+len(lines[i])-len(lines[i].lstrip()) + lines[i] = '
  • '+lines[i][nb:] + if nb>ul: + lines.insert(i,'
      '*(nb-ul)) + i += 1 + elif nb'*(ul-nb)) + i += 1 + ul = nb + elif ul and not lines[i].strip(): + if i1 and nline[1]==' ': + pass + else: + lines.insert(i,'
    '*ul) + i += 1 + ul = 0 + + # ordered lists + mo = re.search(r'^(\d+\.)',lines[i]) + if mo: + if not ol: + lines.insert(i,'
      ') + i += 1 + lines[i] = '
    1. '+lines[i][len(mo.groups()[0]):] + ol = 1 + elif ol and not lines[i].strip() and i') + i += 1 + ol = 0 + + i += 1 + + if ul: + lines.append(''*ul) + if ol: + lines.append('
    '*ol) + if bq: + lines.append(''*bq) + + t1 = time.time() + #print('part 1', t1-t0) + sections = [] + scripts = [] + section = Marked() + + i = 0 + while i'): + scripts.append('\n'.join(lines[i+1:j])) + for k in range(i,j+1): + lines[k] = '' + break + j += 1 + i = j + continue + + # atext header + elif line.startswith('#'): + level = 1 + line = lines[i] + while level','>') + czone = czone.replace('_','_') + czone = czone.replace('*','*') + return czone + +def s_escape(mo): + # used in re.sub + czone = mo.string[mo.start():mo.end()] + return escape(czone) + +def unmark(code_zone): + # convert _ to _ inside inline code + code_zone = code_zone.replace('_','_') + return code_zone + +def s_unmark(mo): + # convert _ to _ inside inline code + code_zone = mo.string[mo.start():mo.end()] + code_zone = code_zone.replace('_','_') + return code_zone + +def apply_markdown(src): + + scripts = [] + key = None + + t0 = time.time() + i = 0 + while i-1 and src[start_a:end_a].find('\n')==-1: + link = src[start_a:end_a] + rest = src[end_a+1:].lstrip() + if rest and rest[0]=='(': + j = 0 + while True: + end_href = rest.find(')',j) + if end_href == -1: + break + if rest[end_href-1]=='\\': + j = end_href+1 + else: + break + if end_href>-1 and rest[:end_href].find('\n')==-1: + tag = ''+link+'' + src = src[:start_a-1]+tag+rest[end_href+1:] + i = start_a+len(tag) + elif rest and rest[0]=='[': + j = 0 + while True: + end_key = rest.find(']',j) + if end_key == -1: + break + if rest[end_key-1]=='\\': + j = end_key+1 + else: + break + if end_key>-1 and rest[:end_key].find('\n')==-1: + if not key: + key = link + if key.lower() not in refs: + raise KeyError('unknown reference %s' %key) + url = refs[key.lower()] + tag = ''+link+'' + src = src[:start_a-1]+tag+rest[end_key+1:] + i = start_a+len(tag) + + i += 1 + + t1 = time.time() + #print('apply markdown 1', t1-t0) + # before applying the markup with _ and *, isolate HTML tags because + # they can contain these characters + + # We replace them temporarily by a random string + rstr = ''.join(random.choice(letters) for i in range(16)) + + i = 0 + state = None + start = -1 + data = '' + tags = [] + while i' and state is None: + tags.append(src[i:j+1]) + src = src[:i]+rstr+src[j+1:] + i += len(rstr) + break + elif state=='"' or state=="'": + data += src[j] + elif src[j]=='\n': + # if a sign < is not followed by > in the same ligne, it + # is the sign "lesser than" + src = src[:i]+'<'+src[i+1:] + j=i+4 + break + j += 1 + elif src[i]=='`' and i>0 and src[i-1]!='\\': + # ignore the content of inline code + j = i+1 + while j", "&" and "_" in inline code + code_pattern = r'\`(.*?)\`' + src = re.sub(code_pattern,s_escape,src) + + # replace escaped ` _ * by HTML characters + src = src.replace(r'\\`','`') + src = src.replace(r'\_','_') + src = src.replace(r'\*','*') + + # emphasis + strong_patterns = [('STRONG',r'\*\*(.*?)\*\*'),('B',r'__(.*?)__')] + for tag,strong_pattern in strong_patterns: + src = re.sub(strong_pattern,r'<%s>\1' %(tag,tag),src) + + em_patterns = [('EM',r'\*(.*?)\*'),('I',r'\_(.*?)\_')] + for tag,em_pattern in em_patterns: + src = re.sub(em_pattern,r'<%s>\1' %(tag,tag),src) + + # inline code + code_pattern = r'\`(.*?)\`' + src = re.sub(code_pattern,r'\1',src) + + # restore tags + while True: + pos = src.rfind(rstr) + if pos==-1: + break + repl = tags.pop() + src = src[:pos]+repl+src[pos+len(rstr):] + + src = '

    '+src+'

    ' + + t3 = time.time() + #print('apply markdown 3', t3-t2) + + return src,scripts