pax_global_header00006660000000000000000000000064145361201650014515gustar00rootroot0000000000000052 comment=18496bebb174bcc0f818005c4f56090b700287c2 django-js-asset-2.2/000077500000000000000000000000001453612016500143515ustar00rootroot00000000000000django-js-asset-2.2/.editorconfig000066400000000000000000000003101453612016500170200ustar00rootroot00000000000000# top-most EditorConfig file root = true [*] end_of_line = lf insert_final_newline = true charset = utf-8 trim_trailing_whitespace = true indent_style = space indent_size = 2 [*.py] indent_size = 4 django-js-asset-2.2/.github/000077500000000000000000000000001453612016500157115ustar00rootroot00000000000000django-js-asset-2.2/.github/workflows/000077500000000000000000000000001453612016500177465ustar00rootroot00000000000000django-js-asset-2.2/.github/workflows/tests.yml000066400000000000000000000016571453612016500216440ustar00rootroot00000000000000name: Tests on: push: branches: [main] pull_request: branches: [main] schedule: - cron: "37 1 1 * *" jobs: tests: name: Python ${{ matrix.python-version }} runs-on: ubuntu-latest strategy: fail-fast: false matrix: python-version: - "3.8" - "3.9" - "3.10" - "3.11" - "3.12" steps: - uses: actions/checkout@v3 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - name: Install dependencies run: | python -m pip install --upgrade pip wheel setuptools tox - name: Run tox targets for ${{ matrix.python-version }} run: | ENV_PREFIX=$(tr -C -d "0-9" <<< "${{ matrix.python-version }}") TOXENV=$(tox --listenvs | grep "^py$ENV_PREFIX" | tr '\n' ',') python -m tox django-js-asset-2.2/.gitignore000066400000000000000000000002301453612016500163340ustar00rootroot00000000000000*.py? *~ *.sw? \#*# /secrets.py .DS_Store ._* /*.egg-info /MANIFEST /_build /build /dist tests/test.zip /docs/_build /.eggs .coverage htmlcov venv .tox django-js-asset-2.2/.pre-commit-config.yaml000066400000000000000000000023461453612016500206370ustar00rootroot00000000000000exclude: ".yarn/|yarn.lock|\\.min\\.(css|js)$" repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.5.0 hooks: - id: check-added-large-files - id: check-builtin-literals - id: check-executables-have-shebangs - id: check-merge-conflict - id: check-toml - id: check-yaml - id: detect-private-key - id: end-of-file-fixer - id: mixed-line-ending - id: trailing-whitespace - repo: https://github.com/adamchainz/django-upgrade rev: 1.15.0 hooks: - id: django-upgrade args: [--target-version, "3.2"] - repo: https://github.com/astral-sh/ruff-pre-commit rev: "v0.1.7" hooks: - id: ruff - repo: https://github.com/psf/black rev: 23.12.0 hooks: - id: black - repo: https://github.com/pre-commit/mirrors-prettier rev: v4.0.0-alpha.4 hooks: - id: prettier entry: env PRETTIER_LEGACY_CLI=1 prettier args: [--list-different, --no-semi] exclude: "^conf/|.*\\.html$" - repo: https://github.com/tox-dev/pyproject-fmt rev: 1.5.3 hooks: - id: pyproject-fmt - repo: https://github.com/abravalheri/validate-pyproject rev: v0.15 hooks: - id: validate-pyproject django-js-asset-2.2/CHANGELOG.rst000066400000000000000000000060211453612016500163710ustar00rootroot00000000000000 .. _changelog: Change log ========== Next version ~~~~~~~~~~~~ 2.2 (2023-12-12) ~~~~~~~~~~~~~~~~ - Started running the tests periodically to detect breakages early. - Added Django 5.0, Python 3.12. - Fixed building with hatchling 1.19. Thanks Michał Górny! 2.1 (2023-06-28) ~~~~~~~~~~~~~~~~ - Added Django 4.1, 4.2 and Python 3.11 to the CI. - Removed the pytz dependency from the tests. - Dropped Python < 3.8, Django < 3.2 from the CI. - Switched to hatchling and ruff. `2.0`_ (2022-02-10) ~~~~~~~~~~~~~~~~~~~ .. _2.0: https://github.com/matthiask/django-js-asset/compare/1.2...2.0 - Raised the minimum supported versions of Python to 3.6, Django to 2.2. - Added pre-commit. - Replaced the explicit configuration of whether ``static()`` should be used or not with automatic configuration. The ``static`` argument is still accepted but ignored and will be removed at a later time. - Added support for boolean attributes when using Django 4.1 or better. Released as 1.2.1 and 1.2.2: ---------------------------- - Made ``JS()`` objects hashable so that they can be put into sets in preparation for a possible fix for media ordering in Django #30179. - Confirmed support for Django 3.0 and 3.1a1. - Django dropped ``type="text/javascript"`` in 3.1, changed our tests to pass again. - Switched from Travis CI to GitHub actions. - Dropped Django 1.7 from the CI jobs list because it somehow didn't discover our tests. - Renamed the main branch to ``main``. - Added CI testing for Django 3.2. `1.2`_ (2019-02-08) ~~~~~~~~~~~~~~~~~~~ - Reformatted the code using Black. - Added equality of ``JS()`` objects to avoid adding the same script more than once in the same configuration. - Determine the ``static`` callable at module import time, not each time a static path is generated. - Customized the ``repr()`` of ``JS()`` objects. - Added Python 3.7 and Django 2.2 to the test matrix. `1.1`_ (2018-04-19) ~~~~~~~~~~~~~~~~~~~ - Added support for skipping ``static()``, mostly useful when adding external scripts via ``JS()`` (e.g for adding ``defer="defer"``). - Made the attributes dictionary optional. `1.0`_ (2018-01-16) ~~~~~~~~~~~~~~~~~~~ - Added an export of the ``js_asset.static()`` helper (which does the right thing regarding ``django.contrib.staticfiles``) - Fixed the documentation to not mention internal (and removed) API of Django's ``Media()`` class. - Switched to using tox_ for running tests and style checks locally. - Added more versions of Python and Django to the CI matrix. `0.1`_ (2017-04-19) ~~~~~~~~~~~~~~~~~~~ - Initial public release extracted from django-content-editor_. .. _Django: https://www.djangoproject.com/ .. _django-content-editor: https://django-content-editor.readthedocs.io/ .. _tox: https://tox.readthedocs.io/ .. _0.1: https://github.com/matthiask/django-js-asset/commit/e335c79a87 .. _1.0: https://github.com/matthiask/django-js-asset/compare/0.1...1.0 .. _1.1: https://github.com/matthiask/django-js-asset/compare/1.0...1.1 .. _1.2: https://github.com/matthiask/django-js-asset/compare/1.1...1.2 django-js-asset-2.2/LICENSE000066400000000000000000000030161453612016500153560ustar00rootroot00000000000000Copyright (c) 2016, FEINHEIT AG and individual contributors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of FEINHEIT GmbH nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. django-js-asset-2.2/README.rst000066400000000000000000000043621453612016500160450ustar00rootroot00000000000000=============================================================================== django-js-asset -- script tag with additional attributes for django.forms.Media =============================================================================== .. image:: https://github.com/matthiask/django-js-asset/workflows/Tests/badge.svg :target: https://github.com/matthiask/django-js-asset Usage ===== Use this to insert a script tag via ``forms.Media`` containing additional attributes (such as ``id`` and ``data-*`` for CSP-compatible data injection.): .. code-block:: python from js_asset import JS forms.Media(js=[ JS("asset.js", { "id": "asset-script", "data-answer": "42", }), ]) The rendered media tag (via ``{{ media.js }}`` or ``{{ media }}`` will now contain a script tag as follows, without line breaks: .. code-block:: html The attributes are automatically escaped. The data attributes may now be accessed inside ``asset.js``: .. code-block:: javascript var answer = document.querySelector("#asset-script").dataset.answer; Also, because the implementation of ``static`` differs between supported Django versions (older do not take the presence of ``django.contrib.staticfiles`` in ``INSTALLED_APPS`` into account), a ``js_asset.static`` function is provided which does the right thing automatically. When adding external script assets, you should pass ``static=False`` to the ``JS`` object to avoid passing the script URL through ``static()``. In this case, you probably want to add ``defer`` or ``async``, and maybe also ``integrity`` and ``crossorigin`` attributes. Please note that boolean attributes are not properly supported when using Django before 4.1 so specify them as follows: .. code-block:: python JS( "https://cdn.example.com/script.js", {"defer": "defer"}, static=False, ) Compatibility ============= At the time of writing this app is compatible with Django 1.8 and better (up to and including the Django master branch), but have a look at the `tox configuration `_ for definitive answers. django-js-asset-2.2/js_asset/000077500000000000000000000000001453612016500161645ustar00rootroot00000000000000django-js-asset-2.2/js_asset/__init__.py000066400000000000000000000001761453612016500203010ustar00rootroot00000000000000__version__ = "2.2.0" import contextlib with contextlib.suppress(ImportError): from js_asset.js import * # noqa: F403 django-js-asset-2.2/js_asset/js.py000066400000000000000000000052431453612016500171560ustar00rootroot00000000000000import json import warnings from django import VERSION from django.forms.utils import flatatt from django.templatetags.static import static from django.utils.html import format_html, html_safe, mark_safe __all__ = ("JS", "static") _sentinel = object() class JS: """ Use this to insert a script tag via ``forms.Media`` containing additional attributes (such as ``id`` and ``data-*`` for CSP-compatible data injection.):: forms.Media(js=[ JS('asset.js', { 'id': 'asset-script', 'data-answer': '"42"', }), ]) The rendered media tag (via ``{{ media.js }}`` or ``{{ media }}`` will now contain a script tag as follows, without line breaks:: The attributes are automatically escaped. The data attributes may now be accessed inside ``asset.js``:: var answer = document.querySelector('#asset-script').dataset.answer; """ def __init__(self, js, attrs=None, static=_sentinel): self.js = js self.attrs = attrs or {} if static is not _sentinel: warnings.warn( "JS automatically determines whether it received an absolute" " path or not. Stop passing the 'static' argument please.", DeprecationWarning, stacklevel=2, ) def startswith(self, _): # Masquerade as absolute path so that we are returned as-is. return True def __repr__(self): return f"JS({self.js}, {json.dumps(self.attrs, sort_keys=True)})" if VERSION >= (4, 1): def __str__(self): return format_html( '', self.js if self.js.startswith(("http://", "https://", "/")) else static(self.js), mark_safe(flatatt(self.attrs)), ) else: def __html__(self): js = ( self.js if self.js.startswith(("http://", "https://", "/")) else static(self.js) ) return ( format_html('{}"{}', js, mark_safe(flatatt(self.attrs)))[:-1] if self.attrs else js ) def __eq__(self, other): if isinstance(other, JS): return self.js == other.js and self.attrs == other.attrs return self.js == other and not self.attrs def __hash__(self): return hash((self.js, json.dumps(self.attrs, sort_keys=True))) if VERSION >= (4, 1): JS = html_safe(JS) django-js-asset-2.2/pyproject.toml000066400000000000000000000043661453612016500172760ustar00rootroot00000000000000[build-system] build-backend = "hatchling.build" requires = [ "hatchling", ] [project] name = "django-js-asset" description = "script tag with additional attributes for django.forms.Media" readme = "README.rst" license = {text = "BSD-3-Clause"} authors = [ { name = "Matthias Kestenholz", email = "mk@feinheit.ch" }, ] requires-python = ">=3.8" classifiers = [ "Environment :: Web Environment", "Framework :: Django", "Intended Audience :: Developers", "License :: OSI Approved :: BSD License", "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 3 :: Only", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Topic :: Internet :: WWW/HTTP :: Dynamic Content", "Topic :: Software Development", "Topic :: Software Development :: Libraries :: Application Frameworks", ] dynamic = [ "version", ] dependencies = [ "Django>=3.2", ] [project.optional-dependencies] tests = [ "coverage", ] [project.urls] Homepage = "https://github.com/matthiask/django-js-asset/" [tool.hatch.build.targets.wheel] packages = ["js_asset"] [tool.hatch.version] path = "js_asset/__init__.py" [tool.ruff] extend-select = [ # pyflakes, pycodestyle "F", "E", "W", # mmcabe "C90", # isort "I", # pep8-naming "N", # pyupgrade "UP", # flake8-2020 "YTT", # flake8-boolean-trap "FBT", # flake8-bugbear "B", # flake8-comprehensions "C4", # flake8-django "DJ", # flake8-logging-format "G", # flake8-pie "PIE", # flake8-simplify "SIM", # flake8-tidy-imports "TID", # flake8-gettext "INT", # pygrep-hooks "PGH", # pylint "PL", # unused noqa "RUF100", ] extend-ignore = [ # Allow zip() without strict= "B905", # No line length errors "E501", ] fix = true show-fixes = true target-version = "py38" [tool.ruff.isort] combine-as-imports = true lines-after-imports = 2 [tool.ruff.mccabe] max-complexity = 15 [tool.ruff.per-file-ignores] "*/migrat*/*" = [ # Allow using PascalCase model names in migrations "N806", # Ignore the fact that migration files are invalid module names "N999", ] django-js-asset-2.2/tests/000077500000000000000000000000001453612016500155135ustar00rootroot00000000000000django-js-asset-2.2/tests/.gitignore000066400000000000000000000000241453612016500174770ustar00rootroot00000000000000/.coverage /htmlcov django-js-asset-2.2/tests/cov.sh000077500000000000000000000002021453612016500166330ustar00rootroot00000000000000#!/bin/sh venv/bin/coverage run --branch --include="*js_asset/*" --omit="*tests*" ./manage.py test testapp venv/bin/coverage html django-js-asset-2.2/tests/manage.py000077500000000000000000000005351453612016500173230ustar00rootroot00000000000000#!/usr/bin/env python import os import sys from os.path import abspath, dirname if __name__ == "__main__": os.environ.setdefault("DJANGO_SETTINGS_MODULE", "testapp.settings") sys.path.insert(0, dirname(dirname(abspath(__file__)))) from django.core.management import execute_from_command_line execute_from_command_line(sys.argv) django-js-asset-2.2/tests/requirements.txt000066400000000000000000000000201453612016500207670ustar00rootroot00000000000000Django coverage django-js-asset-2.2/tests/testapp/000077500000000000000000000000001453612016500171735ustar00rootroot00000000000000django-js-asset-2.2/tests/testapp/__init__.py000066400000000000000000000000001453612016500212720ustar00rootroot00000000000000django-js-asset-2.2/tests/testapp/settings.py000066400000000000000000000002731453612016500214070ustar00rootroot00000000000000DATABASES = {"default": {"ENGINE": "django.db.backends.sqlite3", "NAME": ":memory:"}} INSTALLED_APPS = ["django.contrib.staticfiles"] STATIC_URL = "/static/" SECRET_KEY = "supersikret" django-js-asset-2.2/tests/testapp/test_js_asset.py000066400000000000000000000053341453612016500224240ustar00rootroot00000000000000from unittest import skipIf import django from django.forms import Media from django.test import TestCase from js_asset.js import JS CSS_TYPE = ' type="text/css"' if django.VERSION < (4, 1) else "" JS_TYPE = ' type="text/javascript"' if django.VERSION < (3, 1) else "" class AssetTest(TestCase): def test_asset(self): media = Media( css={"print": ["app/print.css"]}, js=[ "app/test.js", JS("app/asset.js", {"id": "asset-script", "data-the-answer": 42}), JS("app/asset-without.js", {}), ], ) html = "%s" % media # print(html) self.assertInHTML( f'', html, ) self.assertInHTML( f'', html, ) self.assertInHTML( f'', html, ) self.assertInHTML( f'', html, ) def test_absolute(self): media = Media(js=[JS("https://cdn.example.org/script.js", static=False)]) html = "%s" % media self.assertInHTML( f'', html, ) def test_asset_merging(self): media1 = Media(js=["thing.js", JS("other.js"), "some.js"]) media2 = Media(js=["thing.js", JS("other.js"), "some.js"]) media = media1 + media2 self.assertEqual(len(media._js), 3) self.assertEqual(media._js[0], "thing.js") self.assertEqual(media._js[2], "some.js") def test_repr(self): self.assertEqual( repr( JS("app/asset.js", {"id": "asset-script", "data-the-answer": 42}) ).lstrip("u"), 'JS(app/asset.js, {"data-the-answer": 42, "id": "asset-script"})', ) def test_set(self): media = [ JS("app/asset.js", {"id": "asset-script", "data-the-answer": 42}), JS("app/asset.js", {"id": "asset-script", "data-the-answer": 42}), JS("app/asset.js", {"id": "asset-script", "data-the-answer": 43}), ] self.assertEqual(len(set(media)), 2) @skipIf( django.VERSION < (4, 1), "django-js-asset doesn't support boolean attributes yet", ) def test_boolean_attributes(self): self.assertEqual( str(JS("app/asset.js", {"bool": True, "cool": False})), '', ) django-js-asset-2.2/tox.ini000066400000000000000000000007151453612016500156670ustar00rootroot00000000000000[tox] envlist = py{38,39,310}-dj{32,41,42} py{310,311}-dj{32,41,42,50,main} py{312}-dj{42,50,main} [testenv] usedevelop = true extras = tests commands = python -Wd {envbindir}/coverage run tests/manage.py test -v2 --keepdb {posargs:testapp} coverage report -m deps = dj32: Django>=3.2,<4.0 dj41: Django>=4.1,<4.2 dj42: Django>=4.2,<5.0 dj50: Django>=5.0,<5.1 djmain: https://github.com/django/django/archive/main.tar.gz