mirror of
https://github.com/django/django.git
synced 2025-11-07 07:15:35 +00:00
Fixed #28377 -- Made combining form Media retain relative asset order.
Thanks Florian Apolloner, Mariusz Felisiak, and Tim Graham for reviews.
This commit is contained in:
committed by
Tim Graham
parent
f86b6f351d
commit
c19b56f633
@@ -5,6 +5,7 @@ HTML Widget classes
|
||||
import copy
|
||||
import datetime
|
||||
import re
|
||||
import warnings
|
||||
from contextlib import suppress
|
||||
from itertools import chain
|
||||
|
||||
@@ -33,19 +34,23 @@ __all__ = (
|
||||
MEDIA_TYPES = ('css', 'js')
|
||||
|
||||
|
||||
class MediaOrderConflictWarning(RuntimeWarning):
|
||||
pass
|
||||
|
||||
|
||||
@html_safe
|
||||
class Media:
|
||||
def __init__(self, media=None, **kwargs):
|
||||
if media:
|
||||
media_attrs = media.__dict__
|
||||
def __init__(self, media=None, css=None, js=None):
|
||||
if media is not None:
|
||||
css = getattr(media, 'css', {})
|
||||
js = getattr(media, 'js', [])
|
||||
else:
|
||||
media_attrs = kwargs
|
||||
|
||||
self._css = {}
|
||||
self._js = []
|
||||
|
||||
for name in MEDIA_TYPES:
|
||||
getattr(self, 'add_' + name)(media_attrs.get(name))
|
||||
if css is None:
|
||||
css = {}
|
||||
if js is None:
|
||||
js = []
|
||||
self._css = css
|
||||
self._js = js
|
||||
|
||||
def __str__(self):
|
||||
return self.render()
|
||||
@@ -88,24 +93,48 @@ class Media:
|
||||
return Media(**{str(name): getattr(self, '_' + name)})
|
||||
raise KeyError('Unknown media type "%s"' % name)
|
||||
|
||||
def add_js(self, data):
|
||||
if data:
|
||||
for path in data:
|
||||
if path not in self._js:
|
||||
self._js.append(path)
|
||||
@staticmethod
|
||||
def merge(list_1, list_2):
|
||||
"""
|
||||
Merge two lists while trying to keep the relative order of the elements.
|
||||
Warn if the lists have the same two elements in a different relative
|
||||
order.
|
||||
|
||||
def add_css(self, data):
|
||||
if data:
|
||||
for medium, paths in data.items():
|
||||
for path in paths:
|
||||
if not self._css.get(medium) or path not in self._css[medium]:
|
||||
self._css.setdefault(medium, []).append(path)
|
||||
For static assets it can be important to have them included in the DOM
|
||||
in a certain order. In JavaScript you may not be able to reference a
|
||||
global or in CSS you might want to override a style.
|
||||
"""
|
||||
# Start with a copy of list_1.
|
||||
combined_list = list(list_1)
|
||||
last_insert_index = len(list_1)
|
||||
# Walk list_2 in reverse, inserting each element into combined_list if
|
||||
# it doesn't already exist.
|
||||
for path in reversed(list_2):
|
||||
try:
|
||||
# Does path already exist in the list?
|
||||
index = combined_list.index(path)
|
||||
except ValueError:
|
||||
# Add path to combined_list since it doesn't exist.
|
||||
combined_list.insert(last_insert_index, path)
|
||||
else:
|
||||
if index > last_insert_index:
|
||||
warnings.warn(
|
||||
'Detected duplicate Media files in an opposite order:\n'
|
||||
'%s\n%s' % (combined_list[last_insert_index], combined_list[index]),
|
||||
MediaOrderConflictWarning,
|
||||
)
|
||||
# path already exists in the list. Update last_insert_index so
|
||||
# that the following elements are inserted in front of this one.
|
||||
last_insert_index = index
|
||||
return combined_list
|
||||
|
||||
def __add__(self, other):
|
||||
combined = Media()
|
||||
for name in MEDIA_TYPES:
|
||||
getattr(combined, 'add_' + name)(getattr(self, '_' + name, None))
|
||||
getattr(combined, 'add_' + name)(getattr(other, '_' + name, None))
|
||||
combined._js = self.merge(self._js, other._js)
|
||||
combined._css = {
|
||||
medium: self.merge(self._css.get(medium, []), other._css.get(medium, []))
|
||||
for medium in self._css.keys() | other._css.keys()
|
||||
}
|
||||
return combined
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user