Source code for mediaquerybuilder

# python 2
from __future__ import absolute_import, print_function, unicode_literals

# builtins
import logging

# plugins
from cssutils.css import Property
from xml.dom import SyntaxErr

# custom
from blowdrycss.classpropertyparser import ClassPropertyParser
from blowdrycss.breakpointparser import BreakpointParser
from blowdrycss.scalingparser import ScalingParser

__author__ = 'chad nelson'
__project__ = 'blowdrycss'


[docs]class MediaQueryBuilder(object): """ Builds a set of CSS media queries from valid classes found in ``ClassPropertyParser.class_set``. | Takes a set of classes that may or may not contain media query flags. | Mixing breakpoint and scaling syntax is not allowed. Classes that contain mixed syntax like the following: | ``small-down-s`` or ``font-size-28-medium-only-s`` are invalidated. :type property_parser: ClassPropertyParser :param property_parser: ClassPropertyParser object containing ``class_set``. :return: None **Example Usage:** >>> import blowdrycss_settings as settings >>> from classpropertyparser import ClassPropertyParser >>> class_set = {'bold', 'large-down', 'font-size-25-s'} >>> # Filter class names. Only keep classes matching the defined class encoding. >>> property_parser = ClassPropertyParser(class_set=class_set) >>> class_set = property_parser.class_set.copy() >>> # Build Media Queries >>> if settings.media_queries_enabled: >>> unassigned_class_set = class_set.difference(property_parser.class_set) >>> # Only use unassigned classes >>> property_parser.class_set = unassigned_class_set >>> property_parser.removed_class_set = set() >>> media_query_builder = MediaQueryBuilder(property_parser=property_parser) >>> css_text = bytes(media_query_builder.get_css_text(), 'utf-8') >>> print(media_query_builder.property_parser.class_set) {'large-down', 'font-size-25-s'} """ def __init__(self, property_parser=ClassPropertyParser()): message = 'MediaQueryBuilder Running...' print(message) logging.debug(msg=message) self.property_parser = property_parser self.css_media_queries = set() self.media_query_text = '' not_media_classes = dict() for css_class in self.property_parser.class_set: name = self.property_parser.get_property_name(css_class=css_class) priority = self.property_parser.get_property_priority(css_class=css_class) clean_css_class = '' # Prevents css_class from being modified. if name: # value='inherit' since we do not know if the class is valid yet. inherit_property = Property(name=name, value='inherit', priority=priority) scaling_parser = ScalingParser(css_class=css_class, css_property=inherit_property) is_scaling = scaling_parser.is_scaling if is_scaling: clean_css_class = scaling_parser.strip_scaling_flag() breakpoint_parser = BreakpointParser(css_class=css_class, css_property=inherit_property) is_breakpoint = breakpoint_parser.is_breakpoint if is_breakpoint: clean_css_class = breakpoint_parser.strip_breakpoint_limit() if is_breakpoint and is_scaling: # Mixed syntax not_media_classes[css_class] = ' (Breakpoint and scaling media query syntax cannot be combined.)' continue if not is_breakpoint and not is_scaling: # Missing syntax not_media_classes[css_class] = ' is not a media query css_class selector.' continue else: not_media_classes[css_class] = ' is not a media query css_class selector.' continue if clean_css_class and property_parser.is_important(css_class=clean_css_class): clean_css_class = property_parser.strip_priority_designator(css_class=clean_css_class) # Set property value. # Handles case where css_class equals 'small-down', 'large-only', 'medium-up', etc. # Specifically handle the 'display' case. if clean_css_class and clean_css_class != 'display': # Can return an empty string '' if css_class does not match any patterns in the property_alias_dict. try: encoded_property_value = self.property_parser.get_encoded_property_value( property_name=name, css_class=clean_css_class ) value = self.property_parser.get_property_value( property_name=name, encoded_property_value=encoded_property_value ) except ValueError: # Impossible to get here if get_property_name() is working properly. not_media_classes[css_class] = ' property_name not found in property_alias_dict.' continue else: value = 'none' # Breakpoint Parser Case -> display: none; # Build CSS Property AND Add to css_media_queries OR Remove invalid css_class from class_set. try: css_property = Property(name=name, value=value, priority=priority) if css_property.valid: if is_breakpoint and breakpoint_parser: breakpoint_parser.css_property = css_property media_query = breakpoint_parser.build_media_query() self.css_media_queries.add(media_query) if is_scaling: scaling_parser.css_property = css_property media_query = scaling_parser.build_media_query() self.css_media_queries.add(media_query) else: not_media_classes[css_class] = ' (cssutils invalid property value: ' + value + ')' continue # This exception can't be tested as clean_class_set() and get_property_value() prevent it.(Triple Redundant) except SyntaxErr: # Special Case - Not Tested not_media_classes[css_class] = ' (cssutils SyntaxErr invalid property value: ' + value + ')' continue # Clean out invalid CSS Classes. for invalid_css_class, reason in not_media_classes.items(): self.property_parser.class_set.remove(invalid_css_class) self.property_parser.removed_class_set.add(invalid_css_class + reason)
[docs] def get_css_text(self): """ Joins ``css_media_queries`` together with an empty separator string ``''``. :return: str -- Returns all media queries as CSS text. **Example** >>> from classpropertyparser import ClassPropertyParser >>> class_set = {'bold', 'large-down', 'font-size-24-s'} >>> # Filter class names. Only keep classes matching the defined class encoding. >>> property_parser = ClassPropertyParser(class_set=class_set) >>> media_query_builder = MediaQueryBuilder(property_parser=property_parser) >>> print(media_query_builder.get_css_text()) @media only screen and (min-width: 64.0em) { .large-down { display: none; } } .font-size-24-s { font-size: 24px; @media only screen and (max-width: 45.0em) { font-size: 21.3px; } @media only screen and (max-width: 30.0em) { font-size: 19.2px; } } """ return str.join(str(''), self.css_media_queries)