Package cssutils :: Package css :: Module cssproperties |
[hide private]
[frames] | no frames]

# Source Code for Module cssutils.css.cssproperties

      1  """CSS2Properties (partly!) implements DOM Level 2 CSS CSS2Properties used
      2  by CSSStyleDeclaration
      3
      4  TODO: CSS2Properties
      5      If an implementation does implement this interface, it is expected to
      6      understand the specific syntax of the shorthand properties, and apply
      7      their semantics; when the margin property is set, for example, the
      8      marginTop, marginRight, marginBottom and marginLeft properties are
      9      actually being set by the underlying implementation.
     10
     11      When dealing with CSS "shorthand" properties, the shorthand properties
     12      should be decomposed into their component longhand properties as
     13      appropriate, and when querying for their value, the form returned
     14      should be the shortest form exactly equivalent to the declarations made
     15      in the ruleset. However, if there is no shorthand declaration that
     16      could be added to the ruleset without changing in any way the rules
     17      already declared in the ruleset (i.e., by adding longhand rules that
     18      were previously not declared in the ruleset), then the empty string
     19      should be returned for the shorthand property.
     20
     21      For example, querying for the font property should not return
     22      "normal normal normal 14pt/normal Arial, sans-serif", when
     23      "14pt Arial, sans-serif" suffices. (The normals are initial values, and
     24      are implied by use of the longhand property.)
     25
     26      If the values for all the longhand properties that compose a particular
     27      string are the initial values, then a string consisting of all the
     28      initial values should be returned (e.g. a border-width value of
     29      "medium" should be returned as such, not as "").
     30
     31      For some shorthand properties that take missing values from other
     32      sides, such as the margin, padding, and border-[width|style|color]
     33      properties, the minimum number of sides possible should be used; i.e.,
     34      "0px 10px" will be returned instead of "0px 10px 0px 10px".
     35
     36      If the value of a shorthand property can not be decomposed into its
     37      component longhand properties, as is the case for the font property
     38      with a value of "menu", querying for the values of the component
     39      longhand properties should return the empty string.
     40
     41  TODO: CSS2Properties DOMImplementation
     42      The interface found within this section are not mandatory. A DOM
     43      application can use the hasFeature method of the DOMImplementation
     44      interface to determine whether it is supported or not. The feature
     45      string for this extended interface listed in this section is "CSS2"
     46      and the version is "2.0".
     47
     48
     49  cssvalues
     50  =========
     51  contributed by Kevin D. Smith, thanks!
     52
     53  "cssvalues" is used as a property validator.
     54  it is an importable object that contains a dictionary of compiled regular
     55  expressions.  The keys of this dictionary are all of the valid CSS property
     56  names.  The values are compiled regular expressions that can be used to
     57  validate the values for that property. (Actually, the values are references
     58  to the 'match' method of a compiled regular expression, so that they are
     59  simply called like functions.)
     60
     61  """
     62  __all__ = ['CSS2Properties', 'cssvalues']
     63  __docformat__ = 'restructuredtext'
     64  __author__ = '$LastChangedBy: cthedot $'
     65  __date__ = '$LastChangedDate: 2007-11-03 22:43:36 +0100 (Sa, 03 Nov 2007) $'
     66  __version__ = '$LastChangedRevision: 632 $'
     67
     68  import re
     69
     70  """
     71  Define some regular expression fragments that will be used as
     72  macros within the CSS property value regular expressions.
     73  """
     74  MACROS = {
     75      'ident': r'[-]?{nmstart}{nmchar}*',
     76      'name': r'{nmchar}+',
     77      'nmstart': r'[_a-z]|{nonascii}|{escape}',
     78      'nonascii': r'[^\0-\177]',
     79      'unicode': r'\\[0-9a-f]{1,6}(\r\n|[ \n\r\t\f])?',
     80      'escape': r'{unicode}|\\[ -~\200-\777]',
     81  #   'escape': r'{unicode}|\\[ -~\200-\4177777]',
     82      'int': r'[-]?\d+',
     83      'nmchar': r'[\w-]|{nonascii}|{escape}',
     84      'num': r'[-]?\d+|[-]?\d*\.\d+',
     85      'number': r'{num}',
     86      'string': r'{string1}|{string2}',
     87      'string1': r'"(\\\"|[^\"])*"',
     88      'string2': r"'(\\\'|[^\'])*'",
     89      'nl': r'\n|\r\n|\r|\f',
     90      'w': r'\s*',
     91
     92      'integer': r'{int}',
     93      'length': r'0|{num}(em|ex|px|in|cm|mm|pt|pc)',
     94      'angle': r'0|{num}(deg|grad|rad)',
     95      'time': r'0|{num}m?s',
     96      'frequency': r'0|{num}k?Hz',
     97      'color': r'(maroon|red|orange|yellow|olive|purple|fuchsia|white|lime|green|navy|blue|aqua|teal|black|silver|gray|ActiveBorder|ActiveCaption|AppWorkspace|Background|ButtonFace|ButtonHighlight|ButtonShadow|ButtonText|CaptionText|GrayText|Highlight|HighlightText|InactiveBorder|InactiveCaption|InactiveCaptionText|InfoBackground|InfoText|Menu|MenuText|Scrollbar|ThreeDDarkShadow|ThreeDFace|ThreeDHighlight|ThreeDLightShadow|ThreeDShadow|Window|WindowFrame|WindowText)|#[0-9a-f]{3}|#[0-9a-f]{6}|rgb\({w}{int}{w},{w}{int}{w},{w}{int}{w}\)|rgb\({w}{num}%{w},{w}{num}%{w},{w}{num}%{w}\)',
     98      'uri': r'url\({w}({string}|(\\\)|[^\)])+){w}\)',
     99      'percentage': r'{num}%',
    100      'border-style': 'none|hidden|dotted|dashed|solid|double|groove|ridge|inset|outset',
    101      'border-color': '{color}',
    102      'border-width': '{length}|thin|medium|thick',
    103
    104      'background-color': r'{color}|transparent|inherit',
    105      'background-image': r'{uri}|none|inherit',
    106      'background-position': r'({percentage}|{length})(\s*({percentage}|{length}))?|((top|center|bottom)\s*(left|center|right))|((left|center|right)\s*(top|center|bottom))|inherit',
    107      'background-repeat': r'repeat|repeat-x|repeat-y|no-repeat|inherit',
    108      'background-attachment': r'scroll|fixed|inherit',
    109
    110      'shape': r'rect\(({w}({length}|auto}){w},){3}{w}({length}|auto){w}\)',
    111      'counter': r'counter\({w}{identifier}{w}(?:,{w}{list-style-type}{w})?\)',
    112      'identifier': r'{ident}',
    113      'family-name': r'{string}|{identifier}',
    114      'generic-family': r'serif|sans-serif|cursive|fantasy|monospace',
    115      'absolute-size': r'(x?x-)?(small|large)|medium',
    116      'relative-size': r'smaller|larger',
    117      'font-family': r'(({family-name}|{generic-family}){w},{w})*({family-name}|{generic-family})|inherit',
    118      'font-size': r'{absolute-size}|{relative-size}|{length}|{percentage}|inherit',
    119      'font-style': r'normal|italic|oblique|inherit',
    120      'font-variant': r'normal|small-caps|inherit',
    121      'font-weight': r'normal|bold|bolder|lighter|[1-9]00|inherit',
    122      'line-height': r'normal|{number}|{length}|{percentage}|inherit',
    123      'list-style-image': r'{uri}|none|inherit',
    124      'list-style-position': r'inside|outside|inherit',
    125      'list-style-type': r'disc|circle|square|decimal|decimal-leading-zero|lower-roman|upper-roman|lower-greek|lower-(latin|alpha)|upper-(latin|alpha)|armenian|georgian|none|inherit',
    126      'margin-width': r'{length}|{percentage}|auto',
    127      'outline-color': r'{color}|invert|inherit',
    128      'outline-style': r'{border-style}|inherit',
    129      'outline-width': r'{border-width}|inherit',
    130      'padding-width': r'{length}|{percentage}',
    131      'specific-voice': r'{identifier}',
    132      'generic-voice': r'male|female|child',
    133      'content': r'{string}|{uri}|{counter}|attr\({w}{identifier}{w}\)|open-quote|close-quote|no-open-quote|no-close-quote',
    134      'border-attrs': r'{border-width}|{border-style}|{border-color}',
    135      'background-attrs': r'{background-color}|{background-image}|{background-repeat}|{background-attachment}|{background-position}',
    136      'list-attrs': r'{list-style-type}|{list-style-position}|{list-style-image}',
    137      'font-attrs': r'{font-style}|{font-variant}|{font-weight}',
    138      'outline-attrs': r'{outline-color}|{outline-style}|{outline-width}',
    139      'text-attrs': r'underline|overline|line-through|blink',
    140  }
    141
    142  """
    143  Define the regular expressions for validation all CSS values
    144  """
    145  cssvalues = {
    146      'azimuth': r'{angle}|(behind\s+)?(left-side|far-left|left|center-left|center|center-right|right|far-right|right-side)(\s+behind)?|behind|leftwards|rightwards|inherit',
    147      'background-attachment': r'{background-attachment}',
    148      'background-color': r'{background-color}',
    149      'background-image': r'{background-image}',
    150      'background-position': r'{background-position}',
    151      'background-repeat': r'{background-repeat}',
    152      # Each piece should only be allowed one time
    153      'background': r'{background-attrs}(\s+{background-attrs})*|inherit',
    154      'border-collapse': r'collapse|separate|inherit',
    155      'border-color': r'({border-color}|transparent)(\s+({border-color}|transparent)){0,3}|inherit',
    156      'border-spacing': r'{length}(\s+{length})?|inherit',
    157      'border-style': r'{border-style}(\s+{border-style}){0,3}|inherit',
    158      'border-top': r'{border-attrs}(\s+{border-attrs})*|inherit',
    159      'border-right': r'{border-attrs}(\s+{border-attrs})*|inherit',
    160      'border-bottom': r'{border-attrs}(\s+{border-attrs})*|inherit',
    161      'border-left': r'{border-attrs}(\s+{border-attrs})*|inherit',
    162      'border-top-color': r'{border-color}|transparent|inherit',
    163      'border-right-color': r'{border-color}|transparent|inherit',
    164      'border-bottom-color': r'{border-color}|transparent|inherit',
    165      'border-left-color': r'{border-color}|transparent|inherit',
    166      'border-top-style': r'{border-style}|inherit',
    167      'border-right-style': r'{border-style}|inherit',
    168      'border-bottom-style': r'{border-style}|inherit',
    169      'border-left-style': r'{border-style}|inherit',
    170      'border-top-width': r'{border-width}|inherit',
    171      'border-right-width': r'{border-width}|inherit',
    172      'border-bottom-width': r'{border-width}|inherit',
    173      'border-right-width': r'{border-width}|inherit',
    174      'border-width': r'{border-width}(\s+{border-width}){0,3}|inherit',
    175      'border': r'{border-attrs}(\s+{border-attrs})*|inherit',
    176      'bottom': r'{length}|{percentage}|auto|inherit',
    177      'caption-side': r'top|bottom|inherit',
    178      'clear': r'none|left|right|both|inherit',
    179      'clip': r'{shape}|auto|inherit',
    180      'color': r'{color}|inherit',
    181      'content': r'normal|{content}(\s+{content})*|inherit',
    182      'counter-increment': r'({identifier}(\s+{integer})?)(\s+({identifier}(\s+{integer})))*|none|inherit',
    183      'counter-reset': r'({identifier}(\s+{integer})?)(\s+({identifier}(\s+{integer})))*|none|inherit',
    184      'cue-after': r'{uri}|none|inherit',
    185      'cue-before': r'{uri}|none|inherit',
    186      'cue': r'({uri}|none|inherit){1,2}|inherit',
    187      'cursor': r'((({uri}{w},{w})*)?(auto|crosshair|default|pointer|move|(e|ne|nw|n|se|sw|s|w)-resize|text|wait|help|progress))|inherit',
    188      'direction': r'ltr|rtl|inherit',
    189      'display': r'inline|block|list-item|run-in|inline-block|table|inline-table|table-row-group|table-header-group|table-footer-group|table-row|table-column-group|table-column|table-cell|table-caption|none|inherit',
    190      'elevation': r'{angle}|below|level|above|higher|lower|inherit',
    191      'empty-cells': r'show|hide|inherit',
    192      'float': r'left|right|none|inherit',
    193      'font-family': r'{font-family}',
    194      'font-size': r'{font-size}',
    195      'font-style': r'{font-style}',
    196      'font-variant': r'{font-variant}',
    197      'font-weight': r'{font-weight}',
    198      'font': r'({font-attrs}\s+)*{font-size}({w}/{w}{line-height})?\s+{font-family}|caption|icon|menu|message-box|small-caption|status-bar|inherit',
    199      'height': r'{length}|{percentage}|auto|inherit',
    200      'left': r'{length}|{percentage}|auto|inherit',
    201      'letter-spacing': r'normal|{length}|inherit',
    202      'line-height': r'{line-height}',
    203      'list-style-image': r'{list-style-image}',
    204      'list-style-position': r'{list-style-position}',
    205      'list-style-type': r'{list-style-type}',
    206      'list-style': r'{list-attrs}(\s+{list-attrs})*|inherit',
    207      'margin-right': r'{margin-width}|inherit',
    208      'margin-left': r'{margin-width}|inherit',
    209      'margin-top': r'{margin-width}|inherit',
    210      'margin-bottom': r'{margin-width}|inherit',
    211      'margin': r'{margin-width}(\s+{margin-width}){0,3}|inherit',
    212      'max-height': r'{length}|{percentage}|none|inherit',
    213      'max-width': r'{length}|{percentage}|none|inherit',
    214      'min-height': r'{length}|{percentage}|none|inherit',
    215      'min-width': r'{length}|{percentage}|none|inherit',
    216      'orphans': r'{integer}|inherit',
    217      'outline-color': r'{outline-color}',
    218      'outline-style': r'{outline-style}',
    219      'outline-width': r'{outline-width}',
    220      'outline': r'{outline-attrs}(\s+{outline-attrs})*|inherit',
    221      'overflow': r'visible|hidden|scroll|auto|inherit',
    222      'padding-top': r'{padding-width}|inherit',
    223      'padding-right': r'{padding-width}|inherit',
    224      'padding-bottom': r'{padding-width}|inherit',
    225      'padding-left': r'{padding-width}|inherit',
    226      'padding': r'{padding-width}(\s+{padding-width}){0,3}|inherit',
    227      'page-break-after': r'auto|always|avoid|left|right|inherit',
    228      'page-break-before': r'auto|always|avoid|left|right|inherit',
    229      'page-break-inside': r'avoid|auto|inherit',
    230      'pause-after': r'{time}|{percentage}|inherit',
    231      'pause-before': r'{time}|{percentage}|inherit',
    232      'pause': r'({time}|{percentage}){1,2}|inherit',
    233      'pitch-range': r'{number}|inherit',
    234      'pitch': r'{frequency}|x-low|low|medium|high|x-high|inherit',
    235      'play-during': r'{uri}(\s+(mix|repeat))*|auto|none|inherit',
    236      'position': r'static|relative|absolute|fixed|inherit',
    237      'quotes': r'({string}\s+{string})(\s+{string}\s+{string})*|none|inherit',
    238      'richness': r'{number}|inherit',
    239      'right': r'{length}|{percentage}|auto|inherit',
    240      'speak-header': r'once|always|inherit',
    241      'speak-numeral': r'digits|continuous|inherit',
    242      'speak-punctuation': r'code|none|inherit',
    243      'speak': r'normal|none|spell-out|inherit',
    244      'speech-rate': r'{number}|x-slow|slow|medium|fast|x-fast|faster|slower|inherit',
    245      'stress': r'{number}|inherit',
    246      'table-layout': r'auto|fixed|inherit',
    247      'text-align': r'left|right|center|justify|inherit',
    248      'text-decoration': r'none|{text-attrs}(\s+{text-attrs})*|inherit',
    249      'text-indent': r'{length}|{percentage}|inherit',
    250      'text-transform': r'capitalize|uppercase|lowercase|none|inherit',
    251      'top': r'{length}|{percentage}|auto|inherit',
    252      'unicode-bidi': r'normal|embed|bidi-override|inherit',
    253      'vertical-align': r'baseline|sub|super|top|text-top|middle|bottom|text-bottom|{percentage}|{length}|inherit',
    254      'visibility': r'visible|hidden|collapse|inherit',
    255      'voice-family': r'({specific-voice}|{generic-voice}{w},{w})*({specific-voice}|{generic-voice})|inherit',
    256      'volume': r'{number}|{percentage}|silent|x-soft|soft|medium|loud|x-loud|inherit',
    257      'white-space': r'normal|pre|nowrap|pre-wrap|pre-line|inherit',
    258      'widows': r'{integer}|inherit',
    259      'width': r'{length}|{percentage}|auto|inherit',
    260      'word-spacing': r'normal|{length}|inherit',
    261      'z-index': r'auto|{integer}|inherit',
    262  }
    263
    264 -def _expand_macros(tokdict):
    265      """ Expand macros in token dictionary """
    266      def macro_value(m):
    267          return '(?:%s)' % MACROS[m.groupdict()['macro']]
    268      for key, value in tokdict.items():
    269          while re.search(r'{[a-z][a-z0-9-]*}', value):
    270              value = re.sub(r'{(?P<macro>[a-z][a-z0-9-]*)}',
    271                             macro_value, value)
    272          tokdict[key] = value
    273      return tokdict
    274
    275 -def _compile_regexes(tokdict):
    276      """ Compile all regular expressions into callable objects """
    277      for key, value in tokdict.items():
    278          tokdict[key] = re.compile('^(?:%s)$' % value, re.I).match
    279      return tokdict
    280
    281  _compile_regexes(_expand_macros(cssvalues))
    282
    283
    284  # functions to convert between CSS and DOM name
    285
    286  _reCSStoDOMname = re.compile('-[a-z]', re.I)
    287 -def _toDOMname(CSSname):
    288      """
    289      returns DOMname for given CSSname e.g. for CSSname 'font-style' returns
    290      'fontStyle'
    291      """
    292      def _doCSStoDOMname2(m): return m.group(0)[1].capitalize()
    293      return _reCSStoDOMname.sub(_doCSStoDOMname2, CSSname)
    294
    295  _reDOMtoCSSname = re.compile('([A-Z])[a-z]+')
    296 -def _toCSSname(DOMname):
    297      """
    298      returns CSSname for given DOMname e.g. for DOMname 'fontStyle' returns
    299      'font-style'
    300      """
    301      def _doDOMtoCSSname2(m): return '-' + m.group(0).lower()
    302      return _reDOMtoCSSname.sub(_doDOMtoCSSname2, DOMname)
    303
    304
    305 -class CSS2Properties(object):
    306      """
    307      The CSS2Properties interface represents a convenience mechanism
    308      for retrieving and setting properties within a CSSStyleDeclaration.
    309      The attributes of this interface correspond to all the properties
    310      specified in CSS2. Getting an attribute of this interface is
    311      equivalent to calling the getPropertyValue method of the
    312      CSSStyleDeclaration interface. Setting an attribute of this
    313      interface is equivalent to calling the setProperty method of the
    314      CSSStyleDeclaration interface.
    315
    316      cssutils actually also allows usage of ``del`` to remove a CSS property
    317      from a CSSStyleDeclaration.
    318
    319      This is an abstract class, the following functions need to be present
    320      in inheriting class:
    321
    322      - ``_getP``
    323      - ``_setP``
    324      - ``_delP``
    325      """
    326      # actual properties are set after the class definition!
    327 -    def _getP(self, CSSname): pass
    328 -    def _setP(self, CSSname, value): pass
    329 -    def _delP(self, CSSname): pass
    330
    331  # add list of DOMname properties to CSS2Properties
    332  # used for CSSStyleDeclaration to check if allowed properties
    333  # but somehow doubled, any better way?
    334  CSS2Properties._properties = [_toDOMname(p) for p in cssvalues.keys()]
    335
    336  # add CSS2Properties to CSSStyleDeclaration:
    337 -def __named_property_def(DOMname):
    338      """
    339      closure to keep name known in each properties accessor function
    340      DOMname is converted to CSSname here, so actual calls use CSSname
    341      """
    342      CSSname = _toCSSname(DOMname)
    343      def _get(self): return self._getP(CSSname)
    344      def _set(self, value): self._setP(CSSname, value)
    345      def _del(self): self._delP(CSSname)
    346      return _get, _set, _del
    347
    348  # add all CSS2Properties to CSSStyleDeclaration
    349  for DOMname in CSS2Properties._properties:
    350      setattr(CSS2Properties, DOMname,
    351          property(*__named_property_def(DOMname)))
    352