"""
**Features:**
- Validates whether the property_name allows a color property to be set.
- Decodes the following color formats: hexidecimal, rgb, rgba, hsl, hsla.
Exception: English color names are handled separately
| **Either** in the property_alias_dict under the key ``color``,
| **Or** they are passed through to cssutils because they are valid CSS and do not require further processing.
**Note:** The shorthand properties ``background``, ``border``, and ``outline`` are supported (as of November 2015).
**Assumption:** It is assumed that all dashes are removed from the input ``value`` prior to using this parser.
**Example:**
>>> color_parser = ColorParser('border-color', 'h0df48a')
>>> color_parser.property_name_allows_color()
True
"""
# python 2
from __future__ import absolute_import
# builtins
from re import findall
# custom
from blowdrycss.utilities import contains_a_digit
from blowdrycss.datalibrary import property_regex_dict
__author__ = 'chad nelson'
__project__ = 'blowdrycss'
[docs]class ColorParser(object):
""" Extracts plain text, hexidecimal, rgb, rgba, hsl, and hsla color codes from encoded class selectors.
"""
def __init__(self, property_name='', value=''):
self.color_regexes = property_regex_dict['color']
self.property_name = property_name
self.value = value
[docs] def property_name_allows_color(self):
""" Detects if the ``property_name`` allows a color property value.
**Reference:** http://www.w3.org/TR/CSS21/propidx.html
:return: (bool) -- Returns True if the ``property_name`` is allow to contain colors. Otherwise, it returns
False.
**Examples:**
>>> color_parser = ColorParser('border-color', 'h0df48a')
>>> color_parser.property_name_allows_color()
True
>>> color_parser.property_name = 'invalid'
>>> color_parser.property_name_allows_color()
False
"""
color_property_names = {
'color', 'background-color', 'border', 'border-color', 'border-top-color', 'border-right-color',
'border-bottom-color', 'border-left-color', 'outline', 'outline_color',
'background', 'border-top', 'border-right', 'border-bottom', 'border-left',
'text-shadow',
}
for color_property_name in color_property_names:
if self.property_name == color_property_name:
return True
return False
[docs] def find_h_index(self, value=''):
""" Detects if the ``value`` is a valid hexidecimal encoding.
**Note:** Supports shorthand properties.
:type value: str
:param value: Expects a ``value`` of the form: h0ff48f or hfaf i.e. 'h' + a 3 or 6 digit hexidecimal value 0-f.
:return: (int or NoneType) -- Returns the index of the ``h`` to be replaced in the ``value`` if it matches
the hex regex. Otherwise it returns None.
**Examples:**
>>> color_parser = ColorParser()
>>> color_parser.find_h_index(value='h0df48a')
0
>>> color_parser.find_h_index(value='h1invalid')
None
"""
for regex in self.color_regexes:
matches = findall(regex, value)
if len(matches) == 1:
h_index = value.index(matches[0])
return h_index
return None
[docs] def replace_h_with_hash(self, value=''):
""" Replaces the prepended ``h`` prefix with a hash sign ``#`` or octothorpe if you prefer long words.
| Includes an internal check to ensure that the value is a valid hexidecimal encoding.
| Only replaces the ``h`` that matches the regex as other ``h`` characters may be present.
| **Shorthand properties are supported:**
| **border case:** ``1px solid hddd`` becomes ``1px solid #ddd``
:param value: Encoded hexidecimal value of the form ``hf1f`` or ``hc2c2c2``.
:return: (str) -- Returns actually #0ff48f and #faf in the valid case. Returns the input ``value`` unchanged
for the invalid case.
>>> color_parser = ColorParser()
>>> # Valid Cases
>>> color_parser.replace_h_with_hash('h0ff24f')
#0ff24f
>>> color_parser.replace_h_with_hash('hf4f')
#f4f
>>> # Valid multiple 'h' case.
>>> color_parser.replace_h_with_hash('13px dashed hd0d')
13px dashed #d0d
>>> # Invalid Cases
>>> color_parser.replace_h_with_hash('bold')
bold
>>> color_parser.replace_h_with_hash('he2z')
he2z
"""
if self.property_name_allows_color():
h_index = self.find_h_index(value=value) # Only this 'h' will be replaced.
if h_index is not None:
value = value[0:h_index] + '#' + value[h_index + 1:]
return value
[docs] def add_color_parenthetical(self, value=''):
""" Convert parenthetical color values: rbg, rbga, hsl, hsla to valid css format
Assumes that color conversion happens after dashes, decimal point, negative signs, and percentage signs
are converted.
**Note:** Currently not compatible with shorthand properties.
:type value: str
:param value: Space delimited rbg, rbga, hsl, hsla values.
:return: (str) -- Returns the valid css color parenthetical. Returns the input ``value`` unchanged
for the non-matching case.
**Examples:**
>>> color_parser = ColorParser('color', '')
>>> color_parser.add_color_parenthetical('rgb 0 255 0')
rgb(0, 255, 0)
>>> color_parser.add_color_parenthetical('rgba 255 0 0 0.5')
rgba(255, 0, 0, 0.5)
>>> color_parser.add_color_parenthetical('hsl 120 60% 70%')
hsl(120, 60%, 70%)
>>> color_parser.add_color_parenthetical('hsla 120 60% 70% 0.3')
hsla(120, 60%, 70%, 0.3)
>>> # Pass-through case as no conversion is possible.
>>> color_parser.add_color_parenthetical('hsla')
hsla
"""
if self.property_name_allows_color():
if contains_a_digit(string=value):
keywords = {'rgb ', 'rgba ', 'hsl ', 'hsla '}
for key in keywords:
if value.startswith(key):
value = value.replace(key, key.strip() + '(') # Remove key whitespace and add opening '('
value += ')' # Add closing ')'
value = value.replace(' ', ', ') # Add commas ', '
break
return value