# This file contains the base class for Field and Choice classes for FormKit
#   In essence, it's just a collection of parameters and methods that are common
#   to both. It is not useful on its own.

from types import DictionaryType, ListType
import string

try:
    from WebUtils.Funcs import htmlEncode
except ImportError:
    from Webware.WebUtils.Funcs import htmlEncode


# HTMLElement is an abstract class used to define common methods for Choice and Field
# it is not for general use
class HTMLElement:
    def __init__(self, name='', label='', attributes={}, languageStandard='XHTML'):
        '''The constructor basically just sets up a bunch of containers
        the label is a string that is used by some classes, but not others, mainly for things
        like button labels; attributes is a dictionary of parameters that will be added to
        the HTML tag; languageStandard is whether you want HTML or XHTML tags returned'''

        # storage
        self._name = name
        self._label = label
        if not self._label:
            self._label = name
        self._attributes = {}
        self._idAttribute = ''              # auto-generated on demand unless specifically set with setID method
        self._languageStandard = languageStandard
        self._value = None                  # If this is a Field, then this bucket holds the validated value
                                            #   of the field, if any; if this is a Choice, then this bucket
                                            #   holds the value of the choice, whether it be selected or not
        self._style = { }                   # dictionary of inline style statements
        self._styleClass = []               # list of strings that are for CSS style declarations

        # set up attribues right away
        self.addAttributes(attributes)

    ## Parameterization ##

    def addAttributes(self, attributes):
        '''Public method for adding tag attributes; merges in a dictionary of tag attributes'''
        # ... and makes sure all attribute names are lower case
        assert type(attributes) == DictionaryType, "You must pass in attributes as a dictionary."
        for key in attributes.keys():
            self._attributes[key.lower()] = attributes[key]


    def id(self):
        '''returns the dom id attribute of this htmlElement'''
        # we want to make two behaviors available...
        # 1. if the user (servlet programmer) explicitly sets an id, we should use that
        # 2. if they DON'T, come up with a reasonable default id, which will need to be
        #    defined in the subclasses (either choice or field)
        if self._idAttribute:
            return self._idAttribute
        else:
            # use the _generateID method to determine... make this in your subclass
            return self._generateID()

    def setID(self,string):
        '''Public utility method to surgically override the id tag attribute'''
        self._idAttribute = string


    def setTabIndex(self,num):
        '''Public method for setting the tab index of a field... pass an integer'''
        self.addAttributes( { 'tabindex': num } )


    def setAccessKey(self,key):
        '''Public method for setting the tab index of a field... pass a single character string (a letter)'''
        self.addAttributes( { 'accesskey': key } )


    def addStyle(self, dict):
        '''Public method for adding a style attribute to a tag; takes a dictionary;
        the key is the directive, the value is the parameter.
        Duplicate key names override their predecessor.

        Input like this:
        { 'background-color': 'rgb(255,255,255)',
          'font-size: 11px' }
        ( trailing semicolon can be used, or not )

        set a key to None or empty string to remove it from the set 
        '''
        assert type(dict) == DictionaryType,  "You must pass in styles as a dictionary."
        # make sure that we've got trailing semicolons on the values
        for key,value in dict.items():
            if value[-1] != ";":
                dict[key] = value+";"
        # update the storage cache
        self._style.update(dict)

    def addClass(self,klass):
        '''Public method for appending a CSS class name to this tag; expects a string'''
        # It IS CASE SENSITIVE for some browsers!
        self._styleClass.append(klass)

    def clearClasses(self):
        '''removes all CSS classes from the tag'''
        self._styleClass = []

    def _closeTag(self):
        '''Private method that returns the " /" to close up a tag, if we're in XHTML land'''
        if self._languageStandard.upper() == 'XHTML':
            return " /"
        else:
            return ""

    def _tagAttributes(self):
        '''Private method that converts the attribute dictionary into a string of tag
        attributes. E.g.,  size="12" '''
        atts = ''
        for attribute in self.attributes().keys():
            # handle special attributes (CHECKED, SELECTED, disabled, readonly)
            attribute = attribute.lower()
            if attribute in ['checked','selected','disabled','readonly']:
                if self.languageStandard() == 'XHTML':
                    # syntax is supposed to be 'disabled="disabled"'
                    atts += self._makeTagAttribute( attribute, attribute )
                else:
                    # syntax is supposed to be just 'disabled'
                    atts += self._makeTagAttribute( attribute, 1, 0 )
            # handle everything else
            else:
                atts += self._makeTagAttribute( attribute, self.attributes()[attribute] )

        # get any style declarations, and stack them up in a string
        styleBits = ""
        for key,value in self._style.items():
            if value:   # only output the key if there is a non-False value
                styleBits += "%s:%s" % ( key,value )
        # add anything that's built up to atts
        if styleBits:
            atts += self._makeTagAttribute ( 'style',styleBits )

        # get any class declarations, and stack them up in a string
        # classes are separated by spaces, and are interpreted left to right...
        # later ones take precedence, so take that into account when you stack up classes
        if self._styleClass:
            # should be a list, so just make them into a string and send them off to be made into an att
            atts += self._makeTagAttribute ( 'class', string.join(self._styleClass) )

        # now, pre-pend a space character, for ease-of substitution
        if atts and atts[0] != ' ':
            atts = " %s" % atts
        return atts

    def _makeTagAttribute(self, attribute, value, termDef=1):
        '''Private method that formats dictionary pair data as tag attributes'''
        if not value:
            # value is bogus, so do nothing
            return ''
        elif termDef:
            # if flag is set, render as a term definition pair
            return ' %s="%s"' % (attribute.lower(), htmlEncode(str(value)))
        else:
            # must be a case like "CHECKED" or "SELECTED" that is just a single word (non XHTML)
            return ' ' + attribute.lower()

    ## Access ##

    def name(self):
        '''Generic name parameter access method'''
        return self._name

    def label(self):
        '''Generic label parameter access method'''
        return self._label
    
    def value(self):
        '''Generic field value access method'''
        return self._value

    def languageStandard(self):
        '''Generic parameter access method'''
        return self._languageStandard

    def attributes(self):
        '''Returns a dictionary of field element attributes'''
        return self._attributes

    ## Output ##

    def dump(self):
        '''Quick and dirty method that returns the label and tag in that order'''
        # you will probably not want to use this very often
        return "%s : %s" % ( self.label(), self.tag() )
    
    def tag(self):
        ''' '''
        raise "Abstract class HTMLElement does not define tag"