187 lines
4.2 KiB
Python
187 lines
4.2 KiB
Python
'''
|
|
Utility classes for creating dynamic html documents
|
|
'''
|
|
|
|
__license__ = '''
|
|
This file is part of Dominate.
|
|
|
|
Dominate is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU Lesser General Public License as
|
|
published by the Free Software Foundation, either version 3 of
|
|
the License, or (at your option) any later version.
|
|
|
|
Dominate is distributed in the hope that it will be useful, but
|
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General
|
|
Public License along with Dominate. If not, see
|
|
<http://www.gnu.org/licenses/>.
|
|
'''
|
|
|
|
import re
|
|
|
|
from .dom_tag import dom_tag
|
|
|
|
try:
|
|
basestring = basestring
|
|
except NameError:
|
|
basestring = str
|
|
unichr = chr
|
|
|
|
|
|
def include(f):
|
|
'''
|
|
includes the contents of a file on disk.
|
|
takes a filename
|
|
'''
|
|
fl = open(f, 'r')
|
|
data = fl.read()
|
|
fl.close()
|
|
return raw(data)
|
|
|
|
|
|
def system(cmd, data=None):
|
|
'''
|
|
pipes the output of a program
|
|
'''
|
|
import subprocess
|
|
s = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE)
|
|
out, err = s.communicate(data)
|
|
return out.decode('utf8')
|
|
|
|
|
|
def escape(data, quote=True): # stolen from std lib cgi
|
|
'''
|
|
Escapes special characters into their html entities
|
|
Replace special characters "&", "<" and ">" to HTML-safe sequences.
|
|
If the optional flag quote is true, the quotation mark character (")
|
|
is also translated.
|
|
|
|
This is used to escape content that appears in the body of an HTML document
|
|
'''
|
|
data = data.replace("&", "&") # Must be done first!
|
|
data = data.replace("<", "<")
|
|
data = data.replace(">", ">")
|
|
if quote:
|
|
data = data.replace('"', """)
|
|
return data
|
|
|
|
|
|
_unescape = {
|
|
'quot': 34,
|
|
'amp': 38,
|
|
'lt': 60,
|
|
'gt': 62,
|
|
'nbsp': 32,
|
|
# more here
|
|
# http://www.w3.org/TR/html4/sgml/entities.html
|
|
'yuml': 255,
|
|
}
|
|
str_escape = escape
|
|
|
|
|
|
def unescape(data):
|
|
'''
|
|
unescapes html entities. the opposite of escape.
|
|
'''
|
|
cc = re.compile(r'&(?:(?:#(\d+))|([^;]+));')
|
|
|
|
result = []
|
|
m = cc.search(data)
|
|
while m:
|
|
result.append(data[0:m.start()])
|
|
d = m.group(1)
|
|
if d:
|
|
d = int(d)
|
|
result.append(unichr(d))
|
|
else:
|
|
d = _unescape.get(m.group(2), ord('?'))
|
|
result.append(unichr(d))
|
|
|
|
data = data[m.end():]
|
|
m = cc.search(data)
|
|
|
|
result.append(data)
|
|
return ''.join(result)
|
|
|
|
|
|
_reserved = ";/?:@&=+$, "
|
|
_replace_map = dict((c, '%%%2X' % ord(c)) for c in _reserved)
|
|
|
|
|
|
def url_escape(data):
|
|
return ''.join(_replace_map.get(c, c) for c in data)
|
|
|
|
|
|
def url_unescape(data):
|
|
return re.sub('%([0-9a-fA-F]{2})',
|
|
lambda m: unichr(int(m.group(1), 16)), data)
|
|
|
|
|
|
class container(dom_tag):
|
|
'''
|
|
Contains multiple elements, but does not add a level
|
|
'''
|
|
is_inline = True
|
|
def _render(self, sb, indent_level, indent_str, pretty, xhtml):
|
|
inline = self._render_children(sb, indent_level, indent_str, pretty, xhtml)
|
|
if pretty and not inline:
|
|
sb.append('\n')
|
|
sb.append(indent_str * (indent_level - 1))
|
|
return sb
|
|
|
|
|
|
class lazy(dom_tag):
|
|
'''
|
|
delays function execution until rendered
|
|
'''
|
|
def __new__(_cls, *args, **kwargs):
|
|
'''
|
|
Need to reset this special method or else
|
|
dom_tag will think it's being used as a dectorator.
|
|
|
|
This means lazy() can't be used as a dectorator, but
|
|
thinking about when you might want that just confuses me.
|
|
'''
|
|
return object.__new__(_cls)
|
|
|
|
def __init__(self, func, *args, **kwargs):
|
|
super(lazy, self).__init__()
|
|
self.func = func
|
|
self.args = args
|
|
self.kwargs = kwargs
|
|
|
|
|
|
def _render(self, sb, *a, **kw):
|
|
r = self.func(*self.args, **self.kwargs)
|
|
sb.append(str(r))
|
|
|
|
|
|
class text(dom_tag):
|
|
'''
|
|
Just a string. Useful for inside context managers
|
|
'''
|
|
is_pretty = False
|
|
is_inline = True
|
|
|
|
def __init__(self, _text, escape=True):
|
|
super(text, self).__init__()
|
|
self.escape = escape
|
|
if escape:
|
|
self.text = str_escape(_text)
|
|
else:
|
|
self.text = _text
|
|
|
|
def _render(self, sb, *a, **kw):
|
|
sb.append(self.text)
|
|
return sb
|
|
|
|
|
|
def raw(s):
|
|
'''
|
|
Inserts a raw string into the DOM. Unsafe. Alias for text(x, escape=False)
|
|
'''
|
|
return text(s, escape=False)
|