diff --git a/responsive/middleware.py b/responsive/middleware.py index 88f5c8c..e46053f 100644 --- a/responsive/middleware.py +++ b/responsive/middleware.py @@ -7,6 +7,7 @@ from django.utils.encoding import force_text from .conf import settings +from .utils import Device class ResponsiveMiddleware(object): @@ -20,12 +21,20 @@ def process_request(self, request): return try: - width, height, pixelratio = parts - width, height, pixelratio = int(width), int(height), float(pixelratio) + width, height, pixel_ratio = parts + width, height, pixel_ratio = int(width), int(height), float(pixel_ratio) except ValueError: request.INVALID_RESPONSIVE_COOKIE = True return + device_info = { + 'width': width, + 'height': height, + 'pixel_ratio': pixel_ratio + } + + request.device = Device(**device_info) + def process_response(self, request, response): html_types = ('text/html', 'application/xhtml+xml') content_type = response.get('Content-Type', '').split(';')[0] diff --git a/responsive/utils.py b/responsive/utils.py new file mode 100644 index 0000000..4d39bd8 --- /dev/null +++ b/responsive/utils.py @@ -0,0 +1,79 @@ +import six + +from django.core.cache import cache +from django.core.exceptions import ImproperlyConfigured + +from .conf import settings + + +class Device(object): + + def __init__(self, width=0, height=0, pixel_ratio=1): + self.width = int(width) + self.height = int(height) + self.pixel_ratio = float(pixel_ratio) + self.matched = [] + + cache_key = '%(prefix)s%(width)s_%(height)s_%(pixel_ratio)s' % { + 'prefix': settings.RESPONSIVE_CACHE_PREFIX, + 'width': self.width, + 'height': self.height, + 'pixel_ratio': self.pixel_ratio + } + matches = cache.get(cache_key, None) + if not matches: + matches = self.match_media_queries() + cache.set(cache_key, matches, settings.RESPONSIVE_CACHE_DURATION) + + for name, value in six.iteritems(matches): + setattr(self, 'is_%s' % name, value) + if value is True: + self.matched.append(name) + + def match_media_queries(self): + matches = {} + media_queries = self.get_media_queries() + for alias, config in six.iteritems(media_queries): + yesno = [] + if config['min_width']: + yesno.append(self.width >= config['min_width']) + if config['max_width']: + yesno.append(self.width <= config['max_width']) + if config['min_height']: + yesno.append(self.height >= config['min_height']) + if config['max_height']: + yesno.append(self.height <= config['max_height']) + if config['pixel_ratio']: + yesno.append(self.pixel_ratio == config['pixel_ratio']) + + matches[alias] = all(yesno) + return matches + + @staticmethod + def get_media_queries(): + media_queries = settings.RESPONSIVE_MEDIA_QUERIES + for name, config in six.iteritems(media_queries): + for attr in ('min_width', 'max_width', 'min_height', 'max_height', 'pixel_ratio'): + if attr not in config: + config[attr] = None + elif config[attr] is not None: + try: + config[attr] = int(config[attr]) if attr != 'pixel_ratio' \ + else float(config[attr]) + except ValueError: + raise ImproperlyConfigured( + 'RESPONSIVE_MEDIA_QUERIES - %s for %s breakpoint must ' + 'be a number.' % (attr, name)) + else: + media_queries[name][attr] = config[attr] + return media_queries + + def clear(self): + cache.delete(self.cache_key) + + def __str__(self): + return 'Device :: width=%(width)s height=%(height)s pixel-ratio=%(pixel_ratio)s' % { + 'width': self.width, + 'height': self.height, + 'pixel_ratio': self.pixel_ratio + }