Ticket #13978: 0001-Allow-inline-JS-CSS-in-forms.Media-13978.patch

File 0001-Allow-inline-JS-CSS-in-forms.Media-13978.patch, 7.7 KB (added by Derek Payton, 8 years ago)

Updated patch for Django 1.5

  • django/forms/widgets.py

    From 36f0556de03a65f35f7c69e130bc853c1e01e7a5 Mon Sep 17 00:00:00 2001
    From: dmpayton <derek.payton@gmail.com>
    Date: Tue, 20 Nov 2012 15:34:44 -0800
    Subject: [PATCH] Allow inline JS/CSS in forms.Media (#13978)
    
    ---
     django/forms/widgets.py                    |   44 ++++++++++++++++++++--------
     docs/topics/forms/media.txt                |   37 +++++++++++++++++++++++
     tests/regressiontests/forms/tests/media.py |   37 +++++++++++++++++++++++
     3 files changed, 106 insertions(+), 12 deletions(-)
    
    diff --git a/django/forms/widgets.py b/django/forms/widgets.py
    index c761ea8..65e59e2 100644
    a b  
    3434
    3535@python_2_unicode_compatible
    3636class Media(object):
     37    class Inline(object):
     38        def __init__(self, content):
     39            self.content = content
     40
     41        def __eq__(self, other):
     42            return (
     43                type(self) is type(other) and
     44                self.content == other.content
     45            )
     46
    3747    def __init__(self, media=None, **kwargs):
    3848        if media:
    3949            media_attrs = media.__dict__
    def render(self): 
    5767        return mark_safe('\n'.join(chain(*[getattr(self, 'render_' + name)() for name in MEDIA_TYPES])))
    5868
    5969    def render_js(self):
    60         return [format_html('<script type="text/javascript" src="{0}"></script>', self.absolute_path(path)) for path in self._js]
     70        html = []
     71        for script in self._js:
     72            if isinstance(script, self.Inline):
     73                html.append(format_html('<script type="text/javascript">{0}</script>', mark_safe(script.content)))
     74            else:
     75                html.append(format_html('<script type="text/javascript" src="{0}"></script>', self.absolute_path(script)))
     76        return html
    6177
    6278    def render_css(self):
    6379        # To keep rendering order consistent, we can't just iterate over items().
    6480        # We need to sort the keys, and iterate over the sorted list.
     81        html = []
    6582        media = sorted(self._css.keys())
    66         return chain(*[
    67                 [format_html('<link href="{0}" type="text/css" media="{1}" rel="stylesheet" />', self.absolute_path(path), medium)
    68                     for path in self._css[medium]]
    69                 for medium in media])
     83        for medium in media:
     84            for style in self._css[medium]:
     85                if isinstance(style, self.Inline):
     86                    html.append(format_html('<style type="text/css" media="{0}">{1}</style>', medium, mark_safe(style.content)))
     87                else:
     88                    html.append(format_html('<link href="{0}" type="text/css" media="{1}" rel="stylesheet" />', self.absolute_path(style), medium))
     89        return html
    7090
    7191    def absolute_path(self, path, prefix=None):
    7292        if path.startswith(('http://', 'https://', '/')):
    def __getitem__(self, name): 
    87107
    88108    def add_js(self, data):
    89109        if data:
    90             for path in data:
    91                 if path not in self._js:
    92                     self._js.append(path)
     110            for script in data:
     111                if script not in self._js:
     112                    self._js.append(script)
    93113
    94114    def add_css(self, data):
    95115        if data:
    96             for medium, paths in data.items():
    97                 for path in paths:
    98                     if not self._css.get(medium) or path not in self._css[medium]:
    99                         self._css.setdefault(medium, []).append(path)
     116            for medium, styles in data.items():
     117                for style in styles:
     118                    if not self._css.get(medium) or style not in self._css[medium]:
     119                        self._css.setdefault(medium, []).append(style)
    100120
    101121    def __add__(self, other):
    102122        combined = Media()
  • docs/topics/forms/media.txt

    diff --git a/docs/topics/forms/media.txt b/docs/topics/forms/media.txt
    index 98e70e5..34d5f89 100644
    a b If you require even more control over media inheritance, define your media 
    167167using a :ref:`dynamic property <dynamic-property>`. Dynamic properties give
    168168you complete control over which media files are inherited, and which are not.
    169169
     170Specifying inline media
     171-----------------------
     172
     173.. versionadded:: 1.5
     174
     175At some point you may run into a situation that calls for a bit of inline CSS
     176or JavaScript, such as initializing a JavaScript plugin. To do this, simply
     177pass your inline code as a ``forms.Media.Inline`` object in the Media class::
     178
     179    >>> class FancyCalendarWidget(CalendarWidget):
     180    ...     class Media:
     181    ...         css = {
     182    ...             'all': ('fancy.css',)
     183    ...         }
     184    ...         js = (
     185    ...             'whizbang.js',
     186    ...             forms.Media.Inline('init_fancification();'),
     187    ...         )
     188
     189    >>> w = FancyCalendarWidget()
     190    >>> print(w.media)
     191    <link href="/static/fancy.css" type="text/css" media="all" rel="stylesheet" />
     192    <script type="text/javascript" src="/static/whizbang.js"></script>
     193    <script type="text/javascript">init_fancification();</script>
     194
     195Inline CSS works the same way::
     196
     197    >>> class HoneypotWidget(forms.TextInput):
     198    ...     class Media:
     199    ...         css = {
     200    ...             'all': (forms.Media.Inline(".honeypot_row { display: none; }"),)
     201    ...         }
     202
     203    >>> w = HoneypotWidget()
     204    >>> print(w.media)
     205    <style type="text/css" media="all">.honeypot_row { display: none; }</style>
     206
    170207.. _dynamic-property:
    171208
    172209Media as a dynamic property
  • tests/regressiontests/forms/tests/media.py

    diff --git a/tests/regressiontests/forms/tests/media.py b/tests/regressiontests/forms/tests/media.py
    index c492a1e..71e969e 100644
    a b class Media: 
    455455<link href="/path/to/css3" type="text/css" media="all" rel="stylesheet" />
    456456<link href="/some/form/css" type="text/css" media="all" rel="stylesheet" />""")
    457457
     458    def test_inline_media(self):
     459        ## Inline JavaScript and CSS
     460        class MyWidget(TextInput):
     461            class Media:
     462                css = {'all': (Media.Inline('.mywidget { display: none; }'),)}
     463                js = (Media.Inline('init_mywidget();'),)
     464
     465        w = MyWidget()
     466        self.assertEqual('<style type="text/css" media="all">.mywidget { display: none; }</style>', str(w.media['css']))
     467        self.assertEqual('<script type="text/javascript">init_mywidget();</script>', str(w.media['js']))
     468
     469    def test_inline_media_property(self):
     470        ## Inline JavaScript and CSS as a media property
     471        class MyWidget(TextInput):
     472            def _media(self):
     473                return Media(css={'all': (Media.Inline('.mywidget { display: none; }'),)},
     474                             js=(Media.Inline('init_mywidget();'),))
     475            media = property(_media)
     476
     477        w = MyWidget()
     478        self.assertEqual('<style type="text/css" media="all">.mywidget { display: none; }</style>', str(w.media['css']))
     479        self.assertEqual('<script type="text/javascript">init_mywidget();</script>', str(w.media['js']))
     480
     481    def test_inline_media_mutiple(self):
     482        ## Multiple instances of inline media should only be rendered once
     483        class MyWidget(TextInput):
     484            class Media:
     485                css = {'all': (Media.Inline('.mywidget { display: none; }'),)}
     486                js = (Media.Inline('init_mywidget();'),)
     487
     488        class MyForm(Form):
     489            field1 = CharField(widget=MyWidget)
     490            field2 = CharField(widget=MyWidget)
     491
     492        f = MyForm()
     493        self.assertEqual("""<style type="text/css" media="all">.mywidget { display: none; }</style>
     494<script type="text/javascript">init_mywidget();</script>""", str(f.media))
    458495
    459496@override_settings(
    460497    STATIC_URL='http://media.example.com/static/',
Back to Top