1
0

js.py 2.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. import json
  2. import warnings
  3. from django import VERSION
  4. from django.forms.utils import flatatt
  5. from django.templatetags.static import static
  6. from django.utils.html import format_html, html_safe, mark_safe
  7. __all__ = ("JS", "static")
  8. _sentinel = object()
  9. class JS:
  10. """
  11. Use this to insert a script tag via ``forms.Media`` containing additional
  12. attributes (such as ``id`` and ``data-*`` for CSP-compatible data
  13. injection.)::
  14. forms.Media(js=[
  15. JS('asset.js', {
  16. 'id': 'asset-script',
  17. 'data-answer': '"42"',
  18. }),
  19. ])
  20. The rendered media tag (via ``{{ media.js }}`` or ``{{ media }}`` will
  21. now contain a script tag as follows, without line breaks::
  22. <script type="text/javascript" src="/static/asset.js"
  23. data-answer="&quot;42&quot;" id="asset-script"></script>
  24. The attributes are automatically escaped. The data attributes may now be
  25. accessed inside ``asset.js``::
  26. var answer = document.querySelector('#asset-script').dataset.answer;
  27. """
  28. def __init__(self, js, attrs=None, static=_sentinel):
  29. self.js = js
  30. self.attrs = attrs or {}
  31. if static is not _sentinel:
  32. warnings.warn(
  33. "JS automatically determines whether it received an absolute"
  34. " path or not. Stop passing the 'static' argument please.",
  35. DeprecationWarning,
  36. stacklevel=2,
  37. )
  38. def startswith(self, _):
  39. # Masquerade as absolute path so that we are returned as-is.
  40. return True
  41. def __repr__(self):
  42. return f"JS({self.js}, {json.dumps(self.attrs, sort_keys=True)})"
  43. if VERSION >= (4, 1):
  44. def __str__(self):
  45. return format_html(
  46. '<script src="{}"{}></script>',
  47. self.js
  48. if self.js.startswith(("http://", "https://", "/"))
  49. else static(self.js),
  50. mark_safe(flatatt(self.attrs)),
  51. )
  52. else:
  53. def __html__(self):
  54. js = (
  55. self.js
  56. if self.js.startswith(("http://", "https://", "/"))
  57. else static(self.js)
  58. )
  59. return (
  60. format_html('{}"{}', js, mark_safe(flatatt(self.attrs)))[:-1]
  61. if self.attrs
  62. else js
  63. )
  64. def __eq__(self, other):
  65. if isinstance(other, JS):
  66. return self.js == other.js and self.attrs == other.attrs
  67. return self.js == other and not self.attrs
  68. def __hash__(self):
  69. return hash((self.js, json.dumps(self.attrs, sort_keys=True)))
  70. if VERSION >= (4, 1):
  71. JS = html_safe(JS)