Пример #1
0
    def _is_scaling(self):
        """ Return False if ``self.property_name`` does not have default units of ``'px'``.
        Test if ``self.css_class`` contains the scaling flag ``-s``. Returns True if ``-s`` is found and
        False otherwise.

        **Rules:**

        - The ``self.property_name`` must possess units of pixels ``'px'``.
        - If no property priority is set the encoded ``css_class`` must end with ``-s``.
        - If priority is set the encoded ``css_class`` must end with ``-s-i``.

        :return: (*bool*) -- Returns True if ``-s`` is found and False otherwise.

        **Examples**

        >>> scaling_parser = ScalingParser(css_class='font-weight-24-s')
        >>> scaling_parser._is_scaling()
        True
        >>> scaling_parser.css_class = 'font-weight-24-s-i'
        >>> scaling_parser._is_scaling()
        True
        >>> scaling_parser.css_class = 'font-weight-24'
        >>> scaling_parser._is_scaling()
        False

        """
        unit_parser = UnitParser(property_name=self.css_property.name)
        if unit_parser.default_units() != 'px':
            return False
        else:
            return self.css_class.endswith(self.scaling_flag) or self.css_class.endswith(self.scaling_flag + '-i')
Пример #2
0
    def test_add_units_margin_top_conversion_True(self):
        property_name = 'margin-top'
        property_values = ['1', '-20.0', '15px', '60rem']
        expected_values = ['0.0625em', '-1.25em', '0.9375em', '60rem']
        unit_parser = UnitParser(property_name=property_name)

        for i, value in enumerate(property_values):
            new_value = unit_parser.add_units(property_value=value)
            self.assertEqual(new_value, expected_values[i], msg=i)
Пример #3
0
    def test_add_units_multi_value_conversion_True_invalid_units(self):
        # Expect the values to pass through unchanged.
        property_name = 'padding'
        property_values = ['1um', '-20.0no', '15txt', '60st']
        unit_parser = UnitParser(property_name=property_name)

        for value in property_values:
            new_value = unit_parser.add_units(property_value=value)
            self.assertEqual(new_value, value, msg=value)
Пример #4
0
    def test_add_units_UnitLess_Property_conversion_False(self):
        property_name = 'font-weight'
        property_values = ['bold', '200', '800', 'lighter']
        expected_values = ['bold', '200', '800', 'lighter']
        unit_parser = UnitParser(property_name=property_name)

        for i, value in enumerate(property_values):
            new_value = unit_parser.add_units(property_value=value)
            self.assertEqual(new_value, expected_values[i], msg=i)
Пример #5
0
    def test_add_units_multi_value_no_conversion_invalid_units(self):
        # Expect the values to pass through unchanged.
        property_name = 'padding'
        property_values = ['12aou', '-35oeu 15ou', '1ou 2oeu 1ou 2ou', '20i 20u*', '5e 6m 5e 6m']
        unit_parser = UnitParser(property_name=property_name)

        for value in property_values:
            new_value = unit_parser.add_units(property_value=value)
            self.assertEqual(new_value, value, msg=value)
Пример #6
0
    def test_default_units(self):
        property_names = [
            'font-size', 'padding', 'margin', 'pitch', 'text-indent', 'volume', 'width', 'font-weight', 'color',
            'invalid', '', 'none',
        ]
        expected_units = ['px', 'px', 'px', 'Hz', 'px', '%', 'px', '', '', '', '', '']

        for i, property_name in enumerate(property_names):
            unit_parser = UnitParser(property_name=property_name)
            self.assertEqual(unit_parser.default_units(), expected_units[i])
Пример #7
0
    def test_add_units_multi_value_conversion_True_invalid_pass_through(self):
        # Handles cases input like: '1a2', '-35mx 15mx', '1px 2 m11 2', '22.5px 10 22.5px 10'
        # Outputs: '1a2', '-35mx 15mx', '0.0625em 0.125em m11 0.125em', '22.5px 0.625em 22.5px 0.625em'
        property_name = 'padding'
        property_values = ['1a2', '-35mx 15mx', '1px 2 m11 2', '22.5px 10 22.5px 10']
        expected_values = ['1a2', '-35mx 15mx', '0.0625em 0.125em m11 0.125em', '1.4062em 0.625em 1.4062em 0.625em']
        unit_parser = UnitParser(property_name=property_name)

        for i, value in enumerate(property_values):
            new_value = unit_parser.add_units(property_value=value)
            self.assertEqual(new_value, expected_values[i], msg=new_value + ' vs ' + expected_values[i])
Пример #8
0
    def test_add_units_margin_top_no_conversion(self):
        property_name = 'margin-top'
        property_values = ['1', '-20.0', '15px', '60rem']
        expected_values = ['1px', '-20.0px', '15px', '60rem']
        settings.use_em = False
        unit_parser = UnitParser(property_name=property_name)

        for i, value in enumerate(property_values):
            new_value = unit_parser.add_units(property_value=value)
            self.assertEqual(new_value, expected_values[i], msg=i)

        settings.use_em = True
Пример #9
0
    def test_add_units_multi_value_conversion_True_strange_input(self):
        # Note that the cm, rem, and em cases are not handled intuitively causing the units to be mixed.
        # This still produces valid CSS.
        property_name = 'padding'
        property_values = ['-35rem 15', '3 4px 3px 5', '5em 6 5em 6', '1em 100 4cm 9rem']
        expected_values = [
            '-35rem 0.9375em', '0.1875em 0.25em 0.1875em 0.3125em', '5em 0.375em 5em 0.375em', '1em 6.25em 4cm 9rem'
        ]
        unit_parser = UnitParser(property_name=property_name)

        for i, value in enumerate(property_values):
            new_value = unit_parser.add_units(property_value=value)
            self.assertEqual(new_value, expected_values[i], msg=i)
Пример #10
0
    def test_add_units_multi_value_conversion_True(self):
        # Handles cases input like: '12.5', '-35 15', '1 2 1 2', '20% 20%', '5em 6em 5em 6em'
        # Outputs: '0.75em', '-35px 15px', '1px 2px 1px 2px', '20% 20%', '5em 6em 5em 6em'
        property_name = 'padding'
        property_values = ['12', '-35 15', '1 2 1 2', '20% 20%', '5em 6em 5em 6em']
        expected_values = [
            '0.75em', '-2.1875em 0.9375em', '0.0625em 0.125em 0.0625em 0.125em', '20% 20%', '5em 6em 5em 6em'
        ]
        unit_parser = UnitParser(property_name=property_name)

        for i, value in enumerate(property_values):
            new_value = unit_parser.add_units(property_value=value)
            self.assertEqual(new_value, expected_values[i], msg=new_value)
Пример #11
0
    def test_add_units_multi_value_no_conversion_strange_input(self):
        # Note that the cm, rem and em cases are not handled intuitively causing the units to be mixed.
        # This still produces valid CSS.
        property_name = 'padding'
        property_values = ['-35rem 15', '3 4px 3px 5', '5em 6 5em 6', '1em 100 4cm 9rem']
        expected_values = ['-35rem 15px', '3px 4px 3px 5px', '5em 6px 5em 6px', '1em 100px 4cm 9rem']
        settings.use_em = False
        unit_parser = UnitParser(property_name=property_name)

        for i, value in enumerate(property_values):
            new_value = unit_parser.add_units(property_value=value)
            self.assertEqual(new_value, expected_values[i], msg=i)

        settings.use_em = True
Пример #12
0
    def test_add_units_multi_value_no_conversion(self):
        # Handles cases input like: '12', '-35 15', '1 2 1 2'
        # Outputs: '12px', '-35px 15px', '1px 2px 1px 2px'
        property_name = 'padding'
        property_values = ['12', '-35 15', '1 2 1 2', '20% 20%', '5em 6em 5em 6em']
        expected_values = ['12px', '-35px 15px', '1px 2px 1px 2px', '20% 20%', '5em 6em 5em 6em']
        settings.use_em = False
        unit_parser = UnitParser(property_name=property_name)

        for i, value in enumerate(property_values):
            new_value = unit_parser.add_units(property_value=value)
            self.assertEqual(new_value, expected_values[i], msg=i)

        settings.use_em = True
Пример #13
0
 def __init__(self, property_name=''):
     self.property_name = property_name
     self.color_parser = ColorParser(property_name=property_name)
     self.unit_parser = UnitParser(property_name=property_name)
Пример #14
0
class CSSPropertyValueParser(object):
    """
    Accepts a ``property_name`` and ``use_em`` unit conversion flag.

    Contains multiple parsers and methods that decodes the CSS property_value.

    :type property_name: str

    :param property_name: A CSS property name.
    :return: None

    **Attributes:**

    **property_name** (*str*) -- A CSS property name. Not allowed to be ``''`` or None.

    **color_parser** (*ColorParser*) -- Parses encoded color values.

    **unit_parser** (*UnitParser*) -- Parses encoded unit values, and handles unit conversion.

    **Important note about methods:**

    These methods are intended to be called in the order they are defined inside the class.

    """

    def __init__(self, property_name=''):
        self.property_name = property_name
        self.color_parser = ColorParser(property_name=property_name)
        self.unit_parser = UnitParser(property_name=property_name)

    def is_built_in(self, value=''):
        """ Checks if the encoded ``value`` identically matches a value built-in to the CSS standard.
        Returns True if ``value`` matches a CSS built-in valued and False if it does not.

        Examples include: 'bold', 'italic', 'w-resize', 'arial', etc.

        :type value: str

        :param value: Encoded CSS property value.
        :return: (*bool*)

            - Returns ``True`` if ``value`` matches a CSS built-in valued and ``False`` if it does not.
            - The values 'bold', 'italic', 'w-resize', 'arial' all return ``True``.
            - The values '-bold', 'fw-', 'color-' all return ``False``.
            - Invalid ``self.property_name`` also returns ``False`` (KeyError Case).

        **Examples:**

        >>> value_parser = CSSPropertyValueParser(
        >>>     property_name='font-weight', use_em=True
        >>> )
        >>> value_parser.is_built_in('bold')
        True
        >>> value_parser = CSSPropertyValueParser(
        >>>     property_name='padding', use_em=True
        >>> )
        >>> value_parser.is_built_in('7-4-7-4')
        False
        >>> value_parser = CSSPropertyValueParser(
        >>>     property_name='InvalidCSSPropertyName', use_em=True
        >>> )
        >>> value_parser.is_built_in('italic')
        False

        """
        if value.startswith('-') or value.endswith('-'):
            return False
        try:
            aliases = property_alias_dict[self.property_name]
            return True if value in aliases else False
        except KeyError:
            return False

    @staticmethod
    def replace_dashes(value=''):
        """ Remove leading and trailing dashes. Replace internal dashes with spaces. Return the modified value.

        ``-`` becomes either ``''`` or ``' '``.

        :type value: str

        :param value: Encoded CSS property value.
        :return: (*str*) -- Return the value with dashes removed if necessary.

        >>> # Delete leading dash '-bold' --> 'bold'
        >>> value_parser = CSSPropertyValueParser(
        >>>     property_name='font-weight', use_em=True
        >>> )
        >>> value_parser.replace_dashes('-bold')
        'bold'
        >>> #
        >>> # Delete trailing 'white-' --> 'white'
        >>> value_parser = CSSPropertyValueParser(
        >>>     property_name='color', use_em=True
        >>> )
        >>> value_parser.replace_dashes('white-')
        'white'
        >>> #
        >>> # Replace internal '1-5-1-5' --> '1 5 1 5'
        >>> value_parser = CSSPropertyValueParser(
        >>>     property_name='padding', use_em=True
        >>> )
        >>> value_parser.replace_dashes('1-5-1-5')
        '1 5 1 5'

        """
        value = value[1:] if value.startswith('-') else value
        value = value[:-1] if value.endswith('-') else value
        return value.replace('-', ' ')

    @staticmethod
    def replace_underscore_with_decimal(value=''):
        """ Replace underscore with decimal point. Underscores are used to encode a decimal point

        ``'_'`` becomes ``'.'``

        :type value: str

        :param value: Encoded CSS property value.
        :return: (*str*) -- Return the value with decimal points added if necessary.

        **Example**

        >>> value_parser = CSSPropertyValueParser(
        >>>     property_name='padding', use_em=True
        >>> )
        >>> value_parser.replace_underscore_with_decimal('1_32rem')
        '1.32rem'

        """
        if contains_a_digit(string=value):
            value = value.replace('_', '.')
        return value

    @staticmethod
    def replace_p_with_percent(value=''):
        """ Replace ``'p'`` suffix with ``'%'`` if found at the end of any substring containing digits.

        ``'p '`` becomes ``'%'``

        Mind the space

        :type value: str

        :param value: Encoded CSS property value.
        :return: (*str*) -- Return the value with percent signs added if necessary.

        **Example:**

        >>> # Multi-value: '1p 10p 3p 1p' --> '1% 10% 3% 1%'
        >>> value_parser = CSSPropertyValueParser(
        >>>     property_name='padding', use_em=True
        >>> )
        >>> value_parser.replace_p_with_percent(value='1p 10p 3p 1p')
        '1% 10% 3% 1%'
        >>> #
        >>> # Single value ' 1p' --> ' 1%'
        >>> value_parser = CSSPropertyValueParser(
        >>>     property_name='padding', use_em=True
        >>> )
        >>> value_parser.replace_p_with_percent(value=' 1p')
        ' 1%'

        """
        if contains_a_digit(string=value):
            value = value.replace('p ', '% ')
            if value.endswith('p'):
                value = value[:-1] + '%'    # chop last character and add percentage sign
        return value

    @staticmethod
    def replace_n_with_minus(value=''):
        """ If a space plus the letter ``' n'`` is immediately followed by a digit replace it with ``' -'``.
        If ``n`` is the first letter of the string and followed by digits replace it with ``-``.
        The letter ``n`` is an encoding for a negative sign. Leaves other ``n's`` unmodified.

        | ``' n2'`` becomes ``' -2'``  Mind the space.
        | ``'n5'`` becomes ``'-5'``

        :type value: str

        :param value: Encoded CSS property value.
        :return: (*str*) -- Return the value with minus signs added if necessary.

        **Example:**

        >>> # Multi-value: 'n5cm n6cm' --> '-5cm -6cm'
        >>> value_parser = CSSPropertyValueParser(
        >>>     property_name='padding', use_em=True
        >>> )
        >>> value_parser.replace_n_with_minus('n5cm n6cm')
        '-5cm -6cm'
        >>> #
        >>> # 'n9in' --> '-9in' (note that the 'n' at the end is not touched)
        >>> value_parser.replace_n_with_minus('n9in')
        '-9in'

        """
        if contains_a_digit(string=value):
            value = value.replace(' n', ' -')
            if value.startswith('n'):
                value = '-' + value[1:]     # add minus sign and chop first character
        return value

    # Put everything together.
    def decode_property_value(self, value=''):
        """
        Decode the encoded property ``value`` input e.g. 'bold', '1-5-1-5', '1_32rem', '1p-10p-3p-1p', 'n12px',
        'n5_25cm-n6_1cm'. Returns parsed, but non-validated CSS property value.

        :type value: str

        :param value: An encoded CSS property value.
        :return: (*str*) -- Returns the decoded, but non-validated CSS property value.

        **Examples:**

        >>> value_parser = CSSPropertyValueParser(
        >>>     property_name='padding', use_em=True
        >>> )
        >>> value_parser.decode_property_value(value='1-5-1-5')
        '0.0625em 0.3125em 0.0625em 0.3125em'
        >>> value_parser.unit_parser.use_em = False
        >>> value_parser.decode_property_value(value='1-5-1-5')
        '1px 5px 1px 5px'

        """

        # Skip values that are built-in to the CSS standard, and represent the literal property value.
        if not self.is_built_in(value=value):
            # Apply to all non-built-in values.
            value = self.replace_dashes(value=value)

            # These only apply if value contains a digit.
            value = self.replace_underscore_with_decimal(value=value)
            value = self.replace_p_with_percent(value=value)
            value = self.replace_n_with_minus(value=value)

            # Parse color and units
            value = self.color_parser.replace_h_with_hash(value=value)
            value = self.color_parser.add_color_parenthetical(value=value)
            value = self.unit_parser.add_units(property_value=value)
        else:
            # Generate web safe font-family fallback strings.
            if self.property_name == 'font-family':
                font_parser = FontParser(font_value=value)
                value = font_parser.generate_fallback_fonts()
        return value

    @staticmethod
    def property_is_valid(name='', value='', priority=''):
        """ Detects if a given property name, value, and priority combination is valid. Returns True if the
        combination is valid, and false otherwise.

        Validation occurs after the property value is decoded.

        :param name: CSS property name
        :param value: Decoded CSS property value
        :param priority: CSS priority designator
        :return: (*bool*) -- Returns True if the CSS property name, value, and priority
            combination is valid, and false otherwise.

        **Examples:**

        >>> value_parser = CSSPropertyValueParser()
        >>> value_parser.property_is_valid(
        >>>     name='padding', value='1px', priority=''
        >>> )
        True
        >>> value_parser.property_is_valid(
        >>>     name='padding', value='invalid', priority=''
        >>> )
        False

        """
        try:
            css_property = Property(name=name, value=value, priority=priority)
            is_valid = css_property.valid
            return is_valid
        except SyntaxErr:
            return False