"""Various functions for dealing with HTML.

These functions are fairly simple but it is critical that they be used
correctly.  Many security problems are caused by quoting errors (cross site
scripting is one example).


html_quote
----------

Use for quoting data that will occur outside a HTML tag, for example:

    "<title>%s</title>" % html_quote(title)


value_quote
-----------

Use for quoting HTML attribute values, for example:

    '<input type=text value=%s>' % value_quote(name)


url_quote
---------

Use for quoting data to be included as part of a URL, for example:

    '<a href="/search?keyword=%s">' % url_quote(input)

Note that since href is an attribute, its value should usually be quoted
with value_quote.  To see why this is necessary, consider the URL:

    http://example.com/?a=1&copy=0

As a URL this is perfectly valid.  However, when placed in an HTML document
the string "&copy" should be interpreted as the copyright character.

"""

# created 20010821, NAS (from mems.ui.lib.util and mems.ui.lib.html)

__revision__ = "$Id: html.py,v 1.2 2001/09/28 19:22:21 nascheme Exp $"

import cgi
import urllib
from types import UnicodeType


def _quote (value, quoter, fallback):
    if value is None:
        if fallback is None:
            raise ValueError, "value is None and no fallback supplied"
        else:
            return fallback
    if isinstance(value,  UnicodeType):
        value = value.encode('iso-8859-1')
    return quoter(str(value))
 

def html_quote(value, fallback=None):
    """html_quote(value : any [, fallback : string]) -> string

    Quotes 'value' for use in an HTML page; see cgi.escape().  If value is
    None, then the behavior depends on the fallback argument.  If it is not
    supplied then an error is raised.  Otherwise, the fallback value is
    returned unquoted.
    """
    return _quote(value, cgi.escape, fallback)


def url_quote(value, fallback=None):
    """url_quote(value : any [, fallback : string]) -> string

    Quotes 'value' for use in a URL; see urllib.quote().  If value is None,
    then the behavior depends on the fallback argument.  If it is not
    supplied then an error is raised.  Otherwise, the fallback value is
    returned unquoted.
    """
    return _quote(value, urllib.quote, fallback)


def value_quote(value):
    """Quote HTML attribute values.
    
    XHTML 1.0 requires that all values be quoted.  weblint claims
    that some clients don't understand single quotes.  For compatibility
    with HTML, XHTML 1.0 requires that ampersands be encoded.
    """
    assert value is not None, "can't pass None to value_quote"
    value = str(value).replace('&', '&amp;')
    value = value.replace('"', '&quot;')
    return '"%s"' % value


def link (url, text, title=None, name=None):
    return render_tag("a", name=name, href=url, title=title) + str(text) + "</a>"


ValuelessAttr = ["valueless_attr"] # magic singleton object

def render_tag (tag, **attrs):
    r = ""
    r += "<%s" % tag
    for (attr, val) in attrs.items():
        if val is ValuelessAttr:
            r += " %s" % attr
        elif val is not None:
            r += " %s=%s" % (attr, value_quote(val))
    r += ">"
    return r
