0001"""Tag Helpers
0002
0003Use these methods to generate XHTML comliant tags programmatically.
0004"""
0005
0006
0007from webhelpers.util import html_escape
0008import re
0009
0010def camelize(name):
0011 """
0012 Camelize a ``name``
0013 """
0014 def upcase(matchobj):
0015 return getattr(matchobj.group(0)[1:], 'upper')()
0016 name = re.sub(r'(_[a-zA-Z])', upcase, name)
0017 name = name[0].upper() + name[1:]
0018 return name
0019
0020def strip_unders(options):
0021 for x, y in options.iteritems():
0022 if x.endswith('_'):
0023 options[x[:-1]] = y
0024 del options[x]
0025
0026def tag(name, open=False, **options):
0027 """
0028 Returns an XHTML compliant tag of type ``name``.
0029
0030 ``open``
0031 Set to True if the tag should remain open
0032
0033 All additional keyword args become attribute/value's for the tag. To pass in Python
0034 reserved words, append _ to the name of the key. For attributes with no value (such as
0035 disabled and readonly), a value of True is permitted.
0036
0037 Examples::
0038
0039 >>> tag("br")
0040 '<br />'
0041 >>> tag("br", True)
0042 '<br>'
0043 >>> tag("input", type="text")
0044 '<input type="text" />'
0045 >>> tag("input", type='text', disabled=True)
0046 '<input disabled="disabled" type="text" />'
0047 """
0048 tag = '<%s%s%s' % (name, (options and tag_options(**options)) or '', (open and '>') or ' />')
0049 return tag
0050
0051def content_tag(name, content, **options):
0052 """
0053 Create a tag with content
0054
0055 Takes the same keyword args as ``tag``
0056
0057 Examples::
0058
0059 >>> content_tag("p", "Hello world!")
0060 '<p>Hello world!</p>'
0061 >>> content_tag("div", content_tag("p", "Hello world!"), class_="strong")
0062 '<div class="strong"><p>Hello world!</p></div>'
0063 """
0064 if content is None:
0065 content = ''
0066 tag = '<%s%s>%s</%s>' % (name, (options and tag_options(**options)) or '', content, name)
0067 return tag
0068
0069def cdata_section(content):
0070 """
0071 Returns a CDATA section with the given ``content``.
0072
0073 CDATA sections are used to escape blocks of text containing characters which would
0074 otherwise be recognized as markup. CDATA sections begin with the string
0075 ``<![CDATA[`` and end with (and may not contain) the string
0076 ``]]>``.
0077 """
0078 if content is None:
0079 content = ''
0080 return "<![CDATA[%s]]>" % content
0081
0082def escape_once(html):
0083 """Escapes a given string without affecting existing escaped entities.
0084
0085 >>> escape_once("1 < 2 & 3")
0086 '1 < 2 & 3'
0087 """
0088 return fix_double_escape(html_escape(html))
0089
0090def fix_double_escape(escaped):
0091 """Fix double-escaped entities, such as &amp;, &#123;, etc"""
0092 return re.sub(r'&([a-z]+|(#\d+));', r'&\1;', escaped)
0093
0094def tag_options(**options):
0095 strip_unders(options)
0096 cleaned_options = convert_booleans(dict([(x, y) for x, y in options.iteritems() if y is not None]))
0097 optionlist = ['%s="%s"' % (x, escape_once(y)) for x, y in cleaned_options.iteritems()]
0098 optionlist.sort()
0099 if optionlist:
0100 return ' ' + ' '.join(optionlist)
0101 else:
0102 return ''
0103
0104def convert_booleans(options):
0105 for attr in ['disabled', 'readonly', 'multiple']:
0106 boolean_attribute(options, attr)
0107 return options
0108
0109def boolean_attribute(options, attribute):
0110 if options.get(attribute):
0111 options[attribute] = attribute
0112 elif options.has_key(attribute):
0113 del options[attribute]
0114
0115__all__ = ['tag', 'content_tag', 'cdata_section', 'camelize', 'escape_once']