diff --git a/.gitignore b/.gitignore index 110b7d05..af61cb30 100644 --- a/.gitignore +++ b/.gitignore @@ -120,3 +120,7 @@ stats.json # Karma .karma/ **/.karma/ + +# Bundled/generated JS (video XBlock) +xblocks_contrib/video/public/js/video-xblock.js +xblocks_contrib/video/public/js/video-xblock.js.map diff --git a/requirements/base.in b/requirements/base.in index ed641fdc..5bf28fd6 100644 --- a/requirements/base.in +++ b/requirements/base.in @@ -2,9 +2,12 @@ -c constraints.txt django-statici18n +edx-django-utils edx-i18n-tools edx-opaque-keys +edxval nh3 oauthlib openedx-django-pyfs +wrapt XBlock diff --git a/requirements/base.txt b/requirements/base.txt index ee7068b1..35f2ed8d 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -8,28 +8,93 @@ appdirs==1.4.4 # via fs asgiref==3.11.1 # via django -boto3==1.42.49 +boto3==1.42.53 # via fs-s3fs -botocore==1.42.49 +botocore==1.42.53 # via # boto3 # s3transfer +cachetools==7.0.1 + # via edxval +certifi==2026.1.4 + # via requests +cffi==2.0.0 + # via + # cryptography + # pynacl +chardet==5.2.0 + # via pysrt +charset-normalizer==3.4.4 + # via requests +click==8.3.1 + # via + # code-annotations + # edx-django-utils +code-annotations==2.3.0 + # via edx-toggles +cryptography==46.0.5 + # via pyjwt django==5.2.11 # via # -c https://raw.githubusercontent.com/edx/edx-lint/master/edx_lint/files/common_constraints.txt # django-appconf + # django-crum + # django-model-utils # django-statici18n + # django-storages + # django-waffle + # djangorestframework + # drf-jwt + # edx-django-release-util + # edx-django-utils + # edx-drf-extensions # edx-i18n-tools + # edx-toggles + # edxval # openedx-django-pyfs django-appconf==1.2.0 # via django-statici18n +django-crum==0.7.9 + # via + # edx-django-utils + # edx-toggles +django-model-utils==5.0.0 + # via edxval django-statici18n==2.6.0 # via -r requirements/base.in +django-storages==1.14.6 + # via edxval +django-waffle==5.0.0 + # via + # edx-django-utils + # edx-drf-extensions + # edx-toggles +djangorestframework==3.16.1 + # via + # drf-jwt + # edx-drf-extensions dnspython==2.8.0 # via pymongo +drf-jwt==1.19.2 + # via edx-drf-extensions +edx-django-release-util==1.5.0 + # via edxval +edx-django-utils==8.0.1 + # via + # -r requirements/base.in + # edx-drf-extensions + # edx-toggles +edx-drf-extensions==10.6.0 + # via edxval edx-i18n-tools==1.9.0 # via -r requirements/base.in edx-opaque-keys==3.0.0 + # via + # -r requirements/base.in + # edx-drf-extensions +edx-toggles==5.4.1 + # via edxval +edxval==3.2.0 # via -r requirements/base.in fs==2.4.16 # via @@ -38,6 +103,10 @@ fs==2.4.16 # xblock fs-s3fs==1.1.1 # via openedx-django-pyfs +idna==3.11 + # via requests +jinja2==3.1.6 + # via code-annotations jmespath==1.1.0 # via # boto3 @@ -45,6 +114,7 @@ jmespath==1.1.0 lxml[html-clean]==6.0.2 # via # edx-i18n-tools + # edxval # lxml-html-clean # xblock lxml-html-clean==0.4.3 @@ -53,6 +123,7 @@ mako==1.3.10 # via xblock markupsafe==3.0.3 # via + # jinja2 # mako # xblock nh3==0.3.3 @@ -63,41 +134,73 @@ openedx-django-pyfs==3.8.0 # via -r requirements/base.in path==16.16.0 # via edx-i18n-tools +pillow==12.1.1 + # via edxval polib==1.2.0 # via edx-i18n-tools +psutil==7.2.2 + # via edx-django-utils +pycparser==3.0 + # via cffi +pyjwt[crypto]==2.11.0 + # via + # drf-jwt + # edx-drf-extensions pymongo==4.16.0 # via edx-opaque-keys +pynacl==1.6.2 + # via edx-django-utils +pysrt==1.1.2 + # via edxval python-dateutil==2.9.0.post0 # via # botocore # xblock +python-slugify==8.0.4 + # via code-annotations pytz==2025.2 # via xblock pyyaml==6.0.3 # via + # code-annotations + # edx-django-release-util # edx-i18n-tools # xblock +requests==2.32.5 + # via edx-drf-extensions s3transfer==0.16.0 # via boto3 +semantic-version==2.10.0 + # via edx-drf-extensions simplejson==3.20.2 # via xblock six==1.17.0 # via + # edx-django-release-util # fs # fs-s3fs # python-dateutil sqlparse==0.5.5 # via django stevedore==5.6.0 - # via edx-opaque-keys + # via + # code-annotations + # edx-django-utils + # edx-opaque-keys +text-unidecode==1.3 + # via python-slugify typing-extensions==4.15.0 # via edx-opaque-keys urllib3==2.6.3 - # via botocore + # via + # botocore + # requests web-fragments==3.1.0 # via xblock webob==1.8.9 # via xblock +wrapt==2.1.1 + # via -r requirements/base.in xblock==5.3.0 # via -r requirements/base.in diff --git a/requirements/dev.txt b/requirements/dev.txt index c8cd6815..81012366 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -29,12 +29,12 @@ binaryornot==0.4.4 # -r requirements/quality.txt # -r requirements/test.txt # cookiecutter -boto3==1.42.49 +boto3==1.42.53 # via # -r requirements/quality.txt # -r requirements/test.txt # fs-s3fs -botocore==1.42.49 +botocore==1.42.53 # via # -r requirements/quality.txt # -r requirements/test.txt @@ -48,18 +48,26 @@ cachetools==7.0.1 # via # -r requirements/quality.txt # -r requirements/test.txt + # edxval # tox certifi==2026.1.4 # via # -r requirements/quality.txt # -r requirements/test.txt # requests +cffi==2.0.0 + # via + # -r requirements/quality.txt + # -r requirements/test.txt + # cryptography + # pynacl chardet==5.2.0 # via # -r requirements/quality.txt # -r requirements/test.txt # binaryornot # diff-cover + # pysrt # tox charset-normalizer==3.4.4 # via @@ -74,6 +82,7 @@ click==8.3.1 # click-log # code-annotations # cookiecutter + # edx-django-utils # edx-lint # pip-tools click-log==0.4.0 @@ -85,6 +94,7 @@ code-annotations==2.3.0 # -r requirements/quality.txt # -r requirements/test.txt # edx-lint + # edx-toggles colorama==0.4.6 # via # -r requirements/quality.txt @@ -100,6 +110,11 @@ coverage[toml]==7.13.4 # -r requirements/quality.txt # -r requirements/test.txt # pytest-cov +cryptography==46.0.5 + # via + # -r requirements/quality.txt + # -r requirements/test.txt + # pyjwt ddt==1.7.2 # via # -r requirements/quality.txt @@ -121,8 +136,19 @@ django==5.2.11 # -r requirements/quality.txt # -r requirements/test.txt # django-appconf + # django-crum + # django-model-utils # django-statici18n + # django-storages + # django-waffle + # djangorestframework + # drf-jwt + # edx-django-release-util + # edx-django-utils + # edx-drf-extensions # edx-i18n-tools + # edx-toggles + # edxval # openedx-django-pyfs # xblock-sdk django-appconf==1.2.0 @@ -130,15 +156,65 @@ django-appconf==1.2.0 # -r requirements/quality.txt # -r requirements/test.txt # django-statici18n +django-crum==0.7.9 + # via + # -r requirements/quality.txt + # -r requirements/test.txt + # edx-django-utils + # edx-toggles +django-model-utils==5.0.0 + # via + # -r requirements/quality.txt + # -r requirements/test.txt + # edxval django-statici18n==2.6.0 # via # -r requirements/quality.txt # -r requirements/test.txt +django-storages==1.14.6 + # via + # -r requirements/quality.txt + # -r requirements/test.txt + # edxval +django-waffle==5.0.0 + # via + # -r requirements/quality.txt + # -r requirements/test.txt + # edx-django-utils + # edx-drf-extensions + # edx-toggles +djangorestframework==3.16.1 + # via + # -r requirements/quality.txt + # -r requirements/test.txt + # drf-jwt + # edx-drf-extensions dnspython==2.8.0 # via # -r requirements/quality.txt # -r requirements/test.txt # pymongo +drf-jwt==1.19.2 + # via + # -r requirements/quality.txt + # -r requirements/test.txt + # edx-drf-extensions +edx-django-release-util==1.5.0 + # via + # -r requirements/quality.txt + # -r requirements/test.txt + # edxval +edx-django-utils==8.0.1 + # via + # -r requirements/quality.txt + # -r requirements/test.txt + # edx-drf-extensions + # edx-toggles +edx-drf-extensions==10.6.0 + # via + # -r requirements/quality.txt + # -r requirements/test.txt + # edxval edx-i18n-tools==1.9.0 # via # -r requirements/dev.in @@ -150,7 +226,17 @@ edx-opaque-keys==3.0.0 # via # -r requirements/quality.txt # -r requirements/test.txt -filelock==3.24.1 + # edx-drf-extensions +edx-toggles==5.4.1 + # via + # -r requirements/quality.txt + # -r requirements/test.txt + # edxval +edxval==3.2.0 + # via + # -r requirements/quality.txt + # -r requirements/test.txt +filelock==3.24.3 # via # -r requirements/quality.txt # -r requirements/test.txt @@ -201,6 +287,7 @@ lxml[html-clean]==6.0.2 # -r requirements/quality.txt # -r requirements/test.txt # edx-i18n-tools + # edxval # lxml-html-clean # xblock # xblock-sdk @@ -262,9 +349,14 @@ path==16.16.0 # -r requirements/quality.txt # -r requirements/test.txt # edx-i18n-tools +pillow==12.1.1 + # via + # -r requirements/quality.txt + # -r requirements/test.txt + # edxval pip-tools==7.5.3 # via -r requirements/pip-tools.txt -platformdirs==4.9.1 +platformdirs==4.9.2 # via # -r requirements/quality.txt # -r requirements/test.txt @@ -284,8 +376,18 @@ polib==1.2.0 # -r requirements/quality.txt # -r requirements/test.txt # edx-i18n-tools +psutil==7.2.2 + # via + # -r requirements/quality.txt + # -r requirements/test.txt + # edx-django-utils pycodestyle==2.14.0 # via -r requirements/quality.txt +pycparser==3.0 + # via + # -r requirements/quality.txt + # -r requirements/test.txt + # cffi pydocstyle==6.3.0 # via -r requirements/quality.txt pygments==2.19.2 @@ -295,6 +397,12 @@ pygments==2.19.2 # diff-cover # pytest # rich +pyjwt[crypto]==2.11.0 + # via + # -r requirements/quality.txt + # -r requirements/test.txt + # drf-jwt + # edx-drf-extensions pylint==4.0.4 # via # -r requirements/quality.txt @@ -320,6 +428,11 @@ pymongo==4.16.0 # -r requirements/quality.txt # -r requirements/test.txt # edx-opaque-keys +pynacl==1.6.2 + # via + # -r requirements/quality.txt + # -r requirements/test.txt + # edx-django-utils pypng==0.20220715.0 # via # -r requirements/quality.txt @@ -335,6 +448,11 @@ pyproject-hooks==1.2.0 # -r requirements/pip-tools.txt # build # pip-tools +pysrt==1.1.2 + # via + # -r requirements/quality.txt + # -r requirements/test.txt + # edxval pytest==9.0.2 # via # -r requirements/quality.txt @@ -373,6 +491,7 @@ pyyaml==6.0.3 # -r requirements/test.txt # code-annotations # cookiecutter + # edx-django-release-util # edx-i18n-tools # xblock requests==2.32.5 @@ -380,8 +499,9 @@ requests==2.32.5 # -r requirements/quality.txt # -r requirements/test.txt # cookiecutter + # edx-drf-extensions # xblock-sdk -rich==14.3.2 +rich==14.3.3 # via # -r requirements/quality.txt # -r requirements/test.txt @@ -391,6 +511,11 @@ s3transfer==0.16.0 # -r requirements/quality.txt # -r requirements/test.txt # boto3 +semantic-version==2.10.0 + # via + # -r requirements/quality.txt + # -r requirements/test.txt + # edx-drf-extensions simplejson==3.20.2 # via # -r requirements/quality.txt @@ -401,6 +526,7 @@ six==1.17.0 # via # -r requirements/quality.txt # -r requirements/test.txt + # edx-django-release-util # edx-lint # fs # fs-s3fs @@ -419,6 +545,7 @@ stevedore==5.6.0 # -r requirements/quality.txt # -r requirements/test.txt # code-annotations + # edx-django-utils # edx-opaque-keys text-unidecode==1.3 # via @@ -429,7 +556,7 @@ tomlkit==0.14.0 # via # -r requirements/quality.txt # pylint -tox==4.36.0 +tox==4.41.0 # via # -r requirements/quality.txt # -r requirements/test.txt @@ -449,7 +576,7 @@ urllib3==2.6.3 # -r requirements/test.txt # botocore # requests -virtualenv==20.36.1 +virtualenv==20.38.0 # via # -r requirements/quality.txt # -r requirements/test.txt @@ -470,6 +597,10 @@ wheel==0.46.3 # via # -r requirements/pip-tools.txt # pip-tools +wrapt==2.1.1 + # via + # -r requirements/quality.txt + # -r requirements/test.txt xblock==5.3.0 # via # -r requirements/quality.txt diff --git a/requirements/doc.txt b/requirements/doc.txt index 30ce6cc9..c11f765f 100644 --- a/requirements/doc.txt +++ b/requirements/doc.txt @@ -32,11 +32,11 @@ binaryornot==0.4.4 # via # -r requirements/test.txt # cookiecutter -boto3==1.42.49 +boto3==1.42.53 # via # -r requirements/test.txt # fs-s3fs -botocore==1.42.49 +botocore==1.42.53 # via # -r requirements/test.txt # boto3 @@ -46,17 +46,22 @@ build==1.4.0 cachetools==7.0.1 # via # -r requirements/test.txt + # edxval # tox certifi==2026.1.4 # via # -r requirements/test.txt # requests cffi==2.0.0 - # via cryptography + # via + # -r requirements/test.txt + # cryptography + # pynacl chardet==5.2.0 # via # -r requirements/test.txt # binaryornot + # pysrt # tox charset-normalizer==3.4.4 # via @@ -67,8 +72,11 @@ click==8.3.1 # -r requirements/test.txt # code-annotations # cookiecutter + # edx-django-utils code-annotations==2.3.0 - # via -r requirements/test.txt + # via + # -r requirements/test.txt + # edx-toggles colorama==0.4.6 # via # -r requirements/test.txt @@ -82,7 +90,9 @@ coverage[toml]==7.13.4 # -r requirements/test.txt # pytest-cov cryptography==46.0.5 - # via secretstorage + # via + # -r requirements/test.txt + # pyjwt ddt==1.7.2 # via -r requirements/test.txt distlib==0.4.0 @@ -94,16 +104,51 @@ django==5.2.11 # -c https://raw.githubusercontent.com/edx/edx-lint/master/edx_lint/files/common_constraints.txt # -r requirements/test.txt # django-appconf + # django-crum + # django-model-utils # django-statici18n + # django-storages + # django-waffle + # djangorestframework + # drf-jwt + # edx-django-release-util + # edx-django-utils + # edx-drf-extensions # edx-i18n-tools + # edx-toggles + # edxval # openedx-django-pyfs # xblock-sdk django-appconf==1.2.0 # via # -r requirements/test.txt # django-statici18n +django-crum==0.7.9 + # via + # -r requirements/test.txt + # edx-django-utils + # edx-toggles +django-model-utils==5.0.0 + # via + # -r requirements/test.txt + # edxval django-statici18n==2.6.0 # via -r requirements/test.txt +django-storages==1.14.6 + # via + # -r requirements/test.txt + # edxval +django-waffle==5.0.0 + # via + # -r requirements/test.txt + # edx-django-utils + # edx-drf-extensions + # edx-toggles +djangorestframework==3.16.1 + # via + # -r requirements/test.txt + # drf-jwt + # edx-drf-extensions dnspython==2.8.0 # via # -r requirements/test.txt @@ -117,11 +162,36 @@ docutils==0.21.2 # readme-renderer # restructuredtext-lint # sphinx +drf-jwt==1.19.2 + # via + # -r requirements/test.txt + # edx-drf-extensions +edx-django-release-util==1.5.0 + # via + # -r requirements/test.txt + # edxval +edx-django-utils==8.0.1 + # via + # -r requirements/test.txt + # edx-drf-extensions + # edx-toggles +edx-drf-extensions==10.6.0 + # via + # -r requirements/test.txt + # edxval edx-i18n-tools==1.9.0 # via -r requirements/test.txt edx-opaque-keys==3.0.0 + # via + # -r requirements/test.txt + # edx-drf-extensions +edx-toggles==5.4.1 + # via + # -r requirements/test.txt + # edxval +edxval==3.2.0 # via -r requirements/test.txt -filelock==3.24.1 +filelock==3.24.3 # via # -r requirements/test.txt # tox @@ -157,10 +227,6 @@ jaraco-context==6.1.0 # via keyring jaraco-functools==4.4.0 # via keyring -jeepney==0.9.0 - # via - # keyring - # secretstorage jinja2==3.1.6 # via # -r requirements/test.txt @@ -178,6 +244,7 @@ lxml[html-clean]==6.0.2 # via # -r requirements/test.txt # edx-i18n-tools + # edxval # lxml-html-clean # xblock # xblock-sdk @@ -229,7 +296,11 @@ path==16.16.0 # via # -r requirements/test.txt # edx-i18n-tools -platformdirs==4.9.1 +pillow==12.1.1 + # via + # -r requirements/test.txt + # edxval +platformdirs==4.9.2 # via # -r requirements/test.txt # tox @@ -244,8 +315,14 @@ polib==1.2.0 # via # -r requirements/test.txt # edx-i18n-tools +psutil==7.2.2 + # via + # -r requirements/test.txt + # edx-django-utils pycparser==3.0 - # via cffi + # via + # -r requirements/test.txt + # cffi pydata-sphinx-theme==0.15.4 # via sphinx-book-theme pygments==2.19.2 @@ -258,10 +335,19 @@ pygments==2.19.2 # readme-renderer # rich # sphinx +pyjwt[crypto]==2.11.0 + # via + # -r requirements/test.txt + # drf-jwt + # edx-drf-extensions pymongo==4.16.0 # via # -r requirements/test.txt # edx-opaque-keys +pynacl==1.6.2 + # via + # -r requirements/test.txt + # edx-django-utils pypng==0.20220715.0 # via # -r requirements/test.txt @@ -272,6 +358,10 @@ pyproject-api==1.10.0 # tox pyproject-hooks==1.2.0 # via build +pysrt==1.1.2 + # via + # -r requirements/test.txt + # edxval pytest==9.0.2 # via # -r requirements/test.txt @@ -301,6 +391,7 @@ pyyaml==6.0.3 # -r requirements/test.txt # code-annotations # cookiecutter + # edx-django-release-util # edx-i18n-tools # xblock readme-renderer==44.0 @@ -309,6 +400,7 @@ requests==2.32.5 # via # -r requirements/test.txt # cookiecutter + # edx-drf-extensions # requests-toolbelt # sphinx # twine @@ -319,7 +411,7 @@ restructuredtext-lint==2.0.2 # via doc8 rfc3986==2.0.0 # via twine -rich==14.3.2 +rich==14.3.3 # via # -r requirements/test.txt # cookiecutter @@ -330,8 +422,10 @@ s3transfer==0.16.0 # via # -r requirements/test.txt # boto3 -secretstorage==3.5.0 - # via keyring +semantic-version==2.10.0 + # via + # -r requirements/test.txt + # edx-drf-extensions simplejson==3.20.2 # via # -r requirements/test.txt @@ -340,6 +434,7 @@ simplejson==3.20.2 six==1.17.0 # via # -r requirements/test.txt + # edx-django-release-util # fs # fs-s3fs # python-dateutil @@ -375,12 +470,13 @@ stevedore==5.6.0 # -r requirements/test.txt # code-annotations # doc8 + # edx-django-utils # edx-opaque-keys text-unidecode==1.3 # via # -r requirements/test.txt # python-slugify -tox==4.36.0 +tox==4.41.0 # via -r requirements/test.txt twine==6.2.0 # via -r requirements/doc.in @@ -401,7 +497,7 @@ urllib3==2.6.3 # id # requests # twine -virtualenv==20.36.1 +virtualenv==20.38.0 # via # -r requirements/test.txt # tox @@ -415,6 +511,8 @@ webob==1.8.9 # -r requirements/test.txt # xblock # xblock-sdk +wrapt==2.1.1 + # via -r requirements/test.txt xblock==5.3.0 # via # -r requirements/test.txt diff --git a/requirements/quality.txt b/requirements/quality.txt index 3e934a17..5c3a737a 100644 --- a/requirements/quality.txt +++ b/requirements/quality.txt @@ -24,11 +24,11 @@ binaryornot==0.4.4 # via # -r requirements/test.txt # cookiecutter -boto3==1.42.49 +boto3==1.42.53 # via # -r requirements/test.txt # fs-s3fs -botocore==1.42.49 +botocore==1.42.53 # via # -r requirements/test.txt # boto3 @@ -36,15 +36,22 @@ botocore==1.42.49 cachetools==7.0.1 # via # -r requirements/test.txt + # edxval # tox certifi==2026.1.4 # via # -r requirements/test.txt # requests +cffi==2.0.0 + # via + # -r requirements/test.txt + # cryptography + # pynacl chardet==5.2.0 # via # -r requirements/test.txt # binaryornot + # pysrt # tox charset-normalizer==3.4.4 # via @@ -56,6 +63,7 @@ click==8.3.1 # click-log # code-annotations # cookiecutter + # edx-django-utils # edx-lint click-log==0.4.0 # via edx-lint @@ -63,6 +71,7 @@ code-annotations==2.3.0 # via # -r requirements/test.txt # edx-lint + # edx-toggles colorama==0.4.6 # via # -r requirements/test.txt @@ -75,6 +84,10 @@ coverage[toml]==7.13.4 # via # -r requirements/test.txt # pytest-cov +cryptography==46.0.5 + # via + # -r requirements/test.txt + # pyjwt ddt==1.7.2 # via -r requirements/test.txt dill==0.4.1 @@ -88,27 +101,87 @@ django==5.2.11 # -c https://raw.githubusercontent.com/edx/edx-lint/master/edx_lint/files/common_constraints.txt # -r requirements/test.txt # django-appconf + # django-crum + # django-model-utils # django-statici18n + # django-storages + # django-waffle + # djangorestframework + # drf-jwt + # edx-django-release-util + # edx-django-utils + # edx-drf-extensions # edx-i18n-tools + # edx-toggles + # edxval # openedx-django-pyfs # xblock-sdk django-appconf==1.2.0 # via # -r requirements/test.txt # django-statici18n +django-crum==0.7.9 + # via + # -r requirements/test.txt + # edx-django-utils + # edx-toggles +django-model-utils==5.0.0 + # via + # -r requirements/test.txt + # edxval django-statici18n==2.6.0 # via -r requirements/test.txt +django-storages==1.14.6 + # via + # -r requirements/test.txt + # edxval +django-waffle==5.0.0 + # via + # -r requirements/test.txt + # edx-django-utils + # edx-drf-extensions + # edx-toggles +djangorestframework==3.16.1 + # via + # -r requirements/test.txt + # drf-jwt + # edx-drf-extensions dnspython==2.8.0 # via # -r requirements/test.txt # pymongo +drf-jwt==1.19.2 + # via + # -r requirements/test.txt + # edx-drf-extensions +edx-django-release-util==1.5.0 + # via + # -r requirements/test.txt + # edxval +edx-django-utils==8.0.1 + # via + # -r requirements/test.txt + # edx-drf-extensions + # edx-toggles +edx-drf-extensions==10.6.0 + # via + # -r requirements/test.txt + # edxval edx-i18n-tools==1.9.0 # via -r requirements/test.txt edx-lint==5.6.0 # via -r requirements/quality.in edx-opaque-keys==3.0.0 + # via + # -r requirements/test.txt + # edx-drf-extensions +edx-toggles==5.4.1 + # via + # -r requirements/test.txt + # edxval +edxval==3.2.0 # via -r requirements/test.txt -filelock==3.24.1 +filelock==3.24.3 # via # -r requirements/test.txt # tox @@ -150,6 +223,7 @@ lxml[html-clean]==6.0.2 # via # -r requirements/test.txt # edx-i18n-tools + # edxval # lxml-html-clean # xblock # xblock-sdk @@ -193,7 +267,11 @@ path==16.16.0 # via # -r requirements/test.txt # edx-i18n-tools -platformdirs==4.9.1 +pillow==12.1.1 + # via + # -r requirements/test.txt + # edxval +platformdirs==4.9.2 # via # -r requirements/test.txt # pylint @@ -209,8 +287,16 @@ polib==1.2.0 # via # -r requirements/test.txt # edx-i18n-tools +psutil==7.2.2 + # via + # -r requirements/test.txt + # edx-django-utils pycodestyle==2.14.0 # via -r requirements/quality.in +pycparser==3.0 + # via + # -r requirements/test.txt + # cffi pydocstyle==6.3.0 # via -r requirements/quality.in pygments==2.19.2 @@ -218,6 +304,11 @@ pygments==2.19.2 # -r requirements/test.txt # pytest # rich +pyjwt[crypto]==2.11.0 + # via + # -r requirements/test.txt + # drf-jwt + # edx-drf-extensions pylint==4.0.4 # via # edx-lint @@ -236,6 +327,10 @@ pymongo==4.16.0 # via # -r requirements/test.txt # edx-opaque-keys +pynacl==1.6.2 + # via + # -r requirements/test.txt + # edx-django-utils pypng==0.20220715.0 # via # -r requirements/test.txt @@ -244,6 +339,10 @@ pyproject-api==1.10.0 # via # -r requirements/test.txt # tox +pysrt==1.1.2 + # via + # -r requirements/test.txt + # edxval pytest==9.0.2 # via # -r requirements/test.txt @@ -273,14 +372,16 @@ pyyaml==6.0.3 # -r requirements/test.txt # code-annotations # cookiecutter + # edx-django-release-util # edx-i18n-tools # xblock requests==2.32.5 # via # -r requirements/test.txt # cookiecutter + # edx-drf-extensions # xblock-sdk -rich==14.3.2 +rich==14.3.3 # via # -r requirements/test.txt # cookiecutter @@ -288,6 +389,10 @@ s3transfer==0.16.0 # via # -r requirements/test.txt # boto3 +semantic-version==2.10.0 + # via + # -r requirements/test.txt + # edx-drf-extensions simplejson==3.20.2 # via # -r requirements/test.txt @@ -296,6 +401,7 @@ simplejson==3.20.2 six==1.17.0 # via # -r requirements/test.txt + # edx-django-release-util # edx-lint # fs # fs-s3fs @@ -310,6 +416,7 @@ stevedore==5.6.0 # via # -r requirements/test.txt # code-annotations + # edx-django-utils # edx-opaque-keys text-unidecode==1.3 # via @@ -317,7 +424,7 @@ text-unidecode==1.3 # python-slugify tomlkit==0.14.0 # via pylint -tox==4.36.0 +tox==4.41.0 # via -r requirements/test.txt typing-extensions==4.15.0 # via @@ -332,7 +439,7 @@ urllib3==2.6.3 # -r requirements/test.txt # botocore # requests -virtualenv==20.36.1 +virtualenv==20.38.0 # via # -r requirements/test.txt # tox @@ -346,6 +453,8 @@ webob==1.8.9 # -r requirements/test.txt # xblock # xblock-sdk +wrapt==2.1.1 + # via -r requirements/test.txt xblock==5.3.0 # via # -r requirements/test.txt diff --git a/requirements/test.txt b/requirements/test.txt index bab0e9a3..49311ef1 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -16,37 +16,60 @@ asgiref==3.11.1 # django binaryornot==0.4.4 # via cookiecutter -boto3==1.42.49 +boto3==1.42.53 # via # -r requirements/base.txt # fs-s3fs -botocore==1.42.49 +botocore==1.42.53 # via # -r requirements/base.txt # boto3 # s3transfer cachetools==7.0.1 - # via tox + # via + # -r requirements/base.txt + # edxval + # tox certifi==2026.1.4 - # via requests + # via + # -r requirements/base.txt + # requests +cffi==2.0.0 + # via + # -r requirements/base.txt + # cryptography + # pynacl chardet==5.2.0 # via + # -r requirements/base.txt # binaryornot + # pysrt # tox charset-normalizer==3.4.4 - # via requests + # via + # -r requirements/base.txt + # requests click==8.3.1 # via + # -r requirements/base.txt # code-annotations # cookiecutter + # edx-django-utils code-annotations==2.3.0 - # via -r requirements/test.in + # via + # -r requirements/base.txt + # -r requirements/test.in + # edx-toggles colorama==0.4.6 # via tox cookiecutter==2.6.0 # via xblock-sdk coverage[toml]==7.13.4 # via pytest-cov +cryptography==46.0.5 + # via + # -r requirements/base.txt + # pyjwt ddt==1.7.2 # via -r requirements/test.in distlib==0.4.0 @@ -55,27 +78,86 @@ distlib==0.4.0 # -c https://raw.githubusercontent.com/edx/edx-lint/master/edx_lint/files/common_constraints.txt # -r requirements/base.txt # django-appconf + # django-crum + # django-model-utils # django-statici18n + # django-storages + # django-waffle + # djangorestframework + # drf-jwt + # edx-django-release-util + # edx-django-utils + # edx-drf-extensions # edx-i18n-tools + # edx-toggles + # edxval # openedx-django-pyfs # xblock-sdk django-appconf==1.2.0 # via # -r requirements/base.txt # django-statici18n +django-crum==0.7.9 + # via + # -r requirements/base.txt + # edx-django-utils + # edx-toggles +django-model-utils==5.0.0 + # via + # -r requirements/base.txt + # edxval django-statici18n==2.6.0 # via -r requirements/base.txt +django-storages==1.14.6 + # via + # -r requirements/base.txt + # edxval +django-waffle==5.0.0 + # via + # -r requirements/base.txt + # edx-django-utils + # edx-drf-extensions + # edx-toggles +djangorestframework==3.16.1 + # via + # -r requirements/base.txt + # drf-jwt + # edx-drf-extensions dnspython==2.8.0 # via # -r requirements/base.txt # pymongo +drf-jwt==1.19.2 + # via + # -r requirements/base.txt + # edx-drf-extensions +edx-django-release-util==1.5.0 + # via + # -r requirements/base.txt + # edxval +edx-django-utils==8.0.1 + # via + # -r requirements/base.txt + # edx-drf-extensions + # edx-toggles +edx-drf-extensions==10.6.0 + # via + # -r requirements/base.txt + # edxval edx-i18n-tools==1.9.0 # via -r requirements/base.txt edx-opaque-keys==3.0.0 # via # -r requirements/base.txt # -r requirements/test.in -filelock==3.24.1 + # edx-drf-extensions +edx-toggles==5.4.1 + # via + # -r requirements/base.txt + # edxval +edxval==3.2.0 + # via -r requirements/base.txt +filelock==3.24.3 # via # tox # virtualenv @@ -91,11 +173,14 @@ fs-s3fs==1.1.1 # openedx-django-pyfs # xblock-sdk idna==3.11 - # via requests + # via + # -r requirements/base.txt + # requests iniconfig==2.3.0 # via pytest jinja2==3.1.6 # via + # -r requirements/base.txt # code-annotations # cookiecutter jmespath==1.1.0 @@ -107,6 +192,7 @@ lxml[html-clean]==6.0.2 # via # -r requirements/base.txt # edx-i18n-tools + # edxval # lxml-html-clean # xblock # xblock-sdk @@ -143,7 +229,11 @@ path==16.16.0 # via # -r requirements/base.txt # edx-i18n-tools -platformdirs==4.9.1 +pillow==12.1.1 + # via + # -r requirements/base.txt + # edxval +platformdirs==4.9.2 # via # tox # virtualenv @@ -156,18 +246,39 @@ polib==1.2.0 # via # -r requirements/base.txt # edx-i18n-tools +psutil==7.2.2 + # via + # -r requirements/base.txt + # edx-django-utils +pycparser==3.0 + # via + # -r requirements/base.txt + # cffi pygments==2.19.2 # via # pytest # rich +pyjwt[crypto]==2.11.0 + # via + # -r requirements/base.txt + # drf-jwt + # edx-drf-extensions pymongo==4.16.0 # via # -r requirements/base.txt # edx-opaque-keys +pynacl==1.6.2 + # via + # -r requirements/base.txt + # edx-django-utils pypng==0.20220715.0 # via xblock-sdk pyproject-api==1.10.0 # via tox +pysrt==1.1.2 + # via + # -r requirements/base.txt + # edxval pytest==9.0.2 # via # pytest-cov @@ -184,6 +295,7 @@ python-dateutil==2.9.0.post0 # xblock python-slugify==8.0.4 # via + # -r requirements/base.txt # code-annotations # cookiecutter pytz==2025.2 @@ -195,18 +307,25 @@ pyyaml==6.0.3 # -r requirements/base.txt # code-annotations # cookiecutter + # edx-django-release-util # edx-i18n-tools # xblock requests==2.32.5 # via + # -r requirements/base.txt # cookiecutter + # edx-drf-extensions # xblock-sdk -rich==14.3.2 +rich==14.3.3 # via cookiecutter s3transfer==0.16.0 # via # -r requirements/base.txt # boto3 +semantic-version==2.10.0 + # via + # -r requirements/base.txt + # edx-drf-extensions simplejson==3.20.2 # via # -r requirements/base.txt @@ -215,6 +334,7 @@ simplejson==3.20.2 six==1.17.0 # via # -r requirements/base.txt + # edx-django-release-util # fs # fs-s3fs # python-dateutil @@ -226,10 +346,13 @@ stevedore==5.6.0 # via # -r requirements/base.txt # code-annotations + # edx-django-utils # edx-opaque-keys text-unidecode==1.3 - # via python-slugify -tox==4.36.0 + # via + # -r requirements/base.txt + # python-slugify +tox==4.41.0 # via -r requirements/test.in typing-extensions==4.15.0 # via @@ -242,7 +365,7 @@ urllib3==2.6.3 # -r requirements/base.txt # botocore # requests -virtualenv==20.36.1 +virtualenv==20.38.0 # via tox web-fragments==3.1.0 # via @@ -254,6 +377,8 @@ webob==1.8.9 # -r requirements/base.txt # xblock # xblock-sdk +wrapt==2.1.1 + # via -r requirements/base.txt xblock==5.3.0 # via # -r requirements/base.txt diff --git a/xblocks_contrib/common/utils.py b/xblocks_contrib/common/utils.py new file mode 100644 index 00000000..240d83e9 --- /dev/null +++ b/xblocks_contrib/common/utils.py @@ -0,0 +1,35 @@ +""" +Common utility functions for XBlocks. +""" + +from django.conf import settings + + + +def get_resource_url(xblock, path, package_scope=None): + """ + Return the runtime URL for a static resource in this XBlock's package. + + When the Django pipeline is enabled or REQUIRE_DEBUG is False, uses the + pipeline path (e.g. {package_scope}/public/{path}). Otherwise uses the + dev path (e.g. public/{path}). See platform's xblock_local_resource_url() in: + openedx/core/lib/xblock_utils/__init__.py + + Arguments: + xblock: The XBlock instance (for runtime.local_resource_url). + path (str): Relative path within the package, e.g. "css/video.css". + package_scope (str): Package name prefix, e.g. "video". If None, + both paths are "public/{path}"; otherwise pipeline adds the prefix. + + Returns: + str: URL from xblock.runtime.local_resource_url() for the resource. + """ + pipeline_path = dev_path = f"public/{path}" + if package_scope: + pipeline_path = f"{package_scope}/{pipeline_path}" + pipeline = getattr(settings, 'PIPELINE', {}) + if pipeline.get('PIPELINE_ENABLED', True) or not getattr(settings, 'REQUIRE_DEBUG', False): + resource_path = pipeline_path + else: + resource_path = dev_path + return xblock.runtime.local_resource_url(xblock, resource_path) diff --git a/xblocks_contrib/video/ajax_handler_mixin.py b/xblocks_contrib/video/ajax_handler_mixin.py index a0dd5e9b..11dfd053 100644 --- a/xblocks_contrib/video/ajax_handler_mixin.py +++ b/xblocks_contrib/video/ajax_handler_mixin.py @@ -1,21 +1,24 @@ -# NOTE: Code has been copied from the following source file -# https://github.com/openedx/edx-platform/blob/master/xmodule/x_module.py#L739 +""" Mixin that provides AJAX handling for Video XBlock """ +from webob import Response +from webob.multidict import MultiDict +from xblock.core import XBlock -class XModuleToXBlockMixin: + +class AjaxHandlerMixin: """ - Common code needed by XModule and XBlocks converted from XModules. + Mixin that provides AJAX handling for Video XBlock """ @property def ajax_url(self): """ Returns the URL for the ajax handler. """ - return self.runtime.handler_url(self, 'xmodule_handler', '', '').rstrip('/?') + return self.runtime.handler_url(self, 'ajax_handler', '', '').rstrip('/?') @XBlock.handler - def xmodule_handler(self, request, suffix=None): + def ajax_handler(self, request, suffix=None): """ - XBlock handler that wraps `handle_ajax` + XBlock handler that wraps `ajax_handler` """ class FileObjForWebobFiles: """ diff --git a/xblocks_contrib/video/bumper_utils.py b/xblocks_contrib/video/bumper_utils.py index f9f2f5b1..297e0b7f 100644 --- a/xblocks_contrib/video/bumper_utils.py +++ b/xblocks_contrib/video/bumper_utils.py @@ -1,5 +1,3 @@ -# NOTE: Code has been copied from the following source files -# https://github.com/openedx/edx-platform/blob/master/xmodule/video_block/bumper_utils.py """ Utils for video bumper """ diff --git a/xblocks_contrib/video/cache_utils.py b/xblocks_contrib/video/cache_utils.py index 45ff22b2..77a97c74 100644 --- a/xblocks_contrib/video/cache_utils.py +++ b/xblocks_contrib/video/cache_utils.py @@ -1,15 +1,8 @@ -# NOTE: Code has been copied from the following source files -# https://github.com/openedx/edx-platform/blob/master/openedx/core/lib/cache_utils.py -""" -Utilities related to caching. -""" - - +""" Cache Utils for Video Block """ import itertools import wrapt from django.utils.encoding import force_str - from edx_django_utils.cache import RequestCache diff --git a/xblocks_contrib/video/constants.py b/xblocks_contrib/video/constants.py new file mode 100644 index 00000000..17bf5750 --- /dev/null +++ b/xblocks_contrib/video/constants.py @@ -0,0 +1,17 @@ +""" +Constants used by DjangoXBlockUserService +""" + +# This is the view that will be rendered to display the XBlock in the LMS. +# It will also be used to render the block in "preview" mode in Studio, unless +# the XBlock also implements author_view. +STUDENT_VIEW = 'student_view' + +# This is the view that will be rendered to display the XBlock in the LMS for unenrolled learners. +# Implementations of this view should assume that a user and user data are not available. +PUBLIC_VIEW = 'public_view' + +# The personally identifiable user ID. +ATTR_KEY_USER_ID = 'edx-platform.user_id' +# The country code determined from the user's request IP address. +ATTR_KEY_REQUEST_COUNTRY_CODE = 'edx-platform.request_country_code' diff --git a/xblocks_contrib/video/content.py b/xblocks_contrib/video/content.py index b57e05fa..461e278e 100644 --- a/xblocks_contrib/video/content.py +++ b/xblocks_contrib/video/content.py @@ -1,8 +1,9 @@ -# NOTE: Original code has been copied from the following file: -# https://github.com/openedx/edx-platform/blob/farhan/extract-video-xblock/xmodule/contentstore/content.py#L28 +""" Video Block Static Content, class copied from StaticContent class in edx-platform/xmodule/contentstore/content.py """ +from opaque_keys import InvalidKeyError +from opaque_keys.edx.keys import AssetKey +from opaque_keys.edx.locator import AssetLocator - -class StaticContent: # lint-amnesty, pylint: disable=missing-class-docstring +class VideoBlockStaticContent: # lint-amnesty, pylint: disable=missing-class-docstring def __init__(self, loc, name, content_type, data, last_modified_at=None, thumbnail_location=None, import_path=None, length=None, locked=False, content_digest=None): self.location = loc diff --git a/xblocks_contrib/video/mixin.py b/xblocks_contrib/video/mixin.py index dd212089..b75b466e 100644 --- a/xblocks_contrib/video/mixin.py +++ b/xblocks_contrib/video/mixin.py @@ -1,7 +1,5 @@ -# NOTE: Code has been copied from the following source files -# https://github.com/openedx/edx-platform/blob/master/openedx/core/lib/license/mixin.py """ -License mixin for XBlocks and XModules +License mixin for XBlocks """ from xblock.core import XBlockMixin diff --git a/xblocks_contrib/video/package-lock.json b/xblocks_contrib/video/package-lock.json new file mode 100644 index 00000000..ce7e2d86 --- /dev/null +++ b/xblocks_contrib/video/package-lock.json @@ -0,0 +1,2903 @@ +{ + "name": "video-xblock", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "video-xblock", + "version": "1.0.0", + "dependencies": { + "draggabilly": "^3", + "edx-ui-toolkit": "^1", + "hls.js": "^1", + "underscore": "^1", + "webpack-merge": "^6" + }, + "devDependencies": { + "clean-webpack-plugin": "^4", + "webpack": "^5", + "webpack-cli": "^5", + "webpack-manifest-plugin": "^5" + } + }, + "node_modules/@discoveryjs/json-ext": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", + "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", + "dev": true, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.11", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz", + "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@parcel/watcher": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.6.tgz", + "integrity": "sha512-tmmZ3lQxAe/k/+rNnXQRawJ4NjxO2hqiOLTHvWchtGZULp4RyFeh6aU4XdOYBFe2KE1oShQTv4AblOs2iOrNnQ==", + "hasInstallScript": true, + "optional": true, + "dependencies": { + "detect-libc": "^2.0.3", + "is-glob": "^4.0.3", + "node-addon-api": "^7.0.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "@parcel/watcher-android-arm64": "2.5.6", + "@parcel/watcher-darwin-arm64": "2.5.6", + "@parcel/watcher-darwin-x64": "2.5.6", + "@parcel/watcher-freebsd-x64": "2.5.6", + "@parcel/watcher-linux-arm-glibc": "2.5.6", + "@parcel/watcher-linux-arm-musl": "2.5.6", + "@parcel/watcher-linux-arm64-glibc": "2.5.6", + "@parcel/watcher-linux-arm64-musl": "2.5.6", + "@parcel/watcher-linux-x64-glibc": "2.5.6", + "@parcel/watcher-linux-x64-musl": "2.5.6", + "@parcel/watcher-win32-arm64": "2.5.6", + "@parcel/watcher-win32-ia32": "2.5.6", + "@parcel/watcher-win32-x64": "2.5.6" + } + }, + "node_modules/@parcel/watcher-android-arm64": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.6.tgz", + "integrity": "sha512-YQxSS34tPF/6ZG7r/Ih9xy+kP/WwediEUsqmtf0cuCV5TPPKw/PQHRhueUo6JdeFJaqV3pyjm0GdYjZotbRt/A==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-arm64": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.6.tgz", + "integrity": "sha512-Z2ZdrnwyXvvvdtRHLmM4knydIdU9adO3D4n/0cVipF3rRiwP+3/sfzpAwA/qKFL6i1ModaabkU7IbpeMBgiVEA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-x64": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.6.tgz", + "integrity": "sha512-HgvOf3W9dhithcwOWX9uDZyn1lW9R+7tPZ4sug+NGrGIo4Rk1hAXLEbcH1TQSqxts0NYXXlOWqVpvS1SFS4fRg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-freebsd-x64": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.6.tgz", + "integrity": "sha512-vJVi8yd/qzJxEKHkeemh7w3YAn6RJCtYlE4HPMoVnCpIXEzSrxErBW5SJBgKLbXU3WdIpkjBTeUNtyBVn8TRng==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-glibc": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.6.tgz", + "integrity": "sha512-9JiYfB6h6BgV50CCfasfLf/uvOcJskMSwcdH1PHH9rvS1IrNy8zad6IUVPVUfmXr+u+Km9IxcfMLzgdOudz9EQ==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-musl": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.6.tgz", + "integrity": "sha512-Ve3gUCG57nuUUSyjBq/MAM0CzArtuIOxsBdQ+ftz6ho8n7s1i9E1Nmk/xmP323r2YL0SONs1EuwqBp2u1k5fxg==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-glibc": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.6.tgz", + "integrity": "sha512-f2g/DT3NhGPdBmMWYoxixqYr3v/UXcmLOYy16Bx0TM20Tchduwr4EaCbmxh1321TABqPGDpS8D/ggOTaljijOA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-musl": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.6.tgz", + "integrity": "sha512-qb6naMDGlbCwdhLj6hgoVKJl2odL34z2sqkC7Z6kzir8b5W65WYDpLB6R06KabvZdgoHI/zxke4b3zR0wAbDTA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-glibc": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.6.tgz", + "integrity": "sha512-kbT5wvNQlx7NaGjzPFu8nVIW1rWqV780O7ZtkjuWaPUgpv2NMFpjYERVi0UYj1msZNyCzGlaCWEtzc+exjMGbQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-musl": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.6.tgz", + "integrity": "sha512-1JRFeC+h7RdXwldHzTsmdtYR/Ku8SylLgTU/reMuqdVD7CtLwf0VR1FqeprZ0eHQkO0vqsbvFLXUmYm/uNKJBg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-arm64": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.6.tgz", + "integrity": "sha512-3ukyebjc6eGlw9yRt678DxVF7rjXatWiHvTXqphZLvo7aC5NdEgFufVwjFfY51ijYEWpXbqF5jtrK275z52D4Q==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-ia32": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.6.tgz", + "integrity": "sha512-k35yLp1ZMwwee3Ez/pxBi5cf4AoBKYXj00CZ80jUz5h8prpiaQsiRPKQMxoLstNuqe2vR4RNPEAEcjEFzhEz/g==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-x64": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.6.tgz", + "integrity": "sha512-hbQlYcCq5dlAX9Qx+kFb0FHue6vbjlf0FrNzSKdYK2APUf7tGfGxQCk2ihEREmbR6ZMc0MVAD5RIX/41gpUzTw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@types/eslint": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", + "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/eslint-scope": { + "version": "3.7.7", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", + "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", + "dependencies": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==" + }, + "node_modules/@types/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==", + "dev": true, + "dependencies": { + "@types/minimatch": "*", + "@types/node": "*" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==" + }, + "node_modules/@types/minimatch": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-6.0.0.tgz", + "integrity": "sha512-zmPitbQ8+6zNutpwgcQuLcsEpn/Cj54Kbn7L5pX0Os5kdWplB7xPgEh/g+SWOB/qmows2gpuCaPyduq8ZZRnxA==", + "deprecated": "This is a stub types definition. minimatch provides its own type definitions, so you do not need this installed.", + "dev": true, + "dependencies": { + "minimatch": "*" + } + }, + "node_modules/@types/node": { + "version": "25.3.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.3.0.tgz", + "integrity": "sha512-4K3bqJpXpqfg2XKGK9bpDTc6xO/xoUP/RBWS7AtRMug6zZFaRekiLzjVtAoZMquxoAbzBvy5nxQ7veS5eYzf8A==", + "dependencies": { + "undici-types": "~7.18.0" + } + }, + "node_modules/@webassemblyjs/ast": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", + "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", + "dependencies": { + "@webassemblyjs/helper-numbers": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2" + } + }, + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", + "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==" + }, + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", + "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==" + }, + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", + "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==" + }, + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz", + "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", + "dependencies": { + "@webassemblyjs/floating-point-hex-parser": "1.13.2", + "@webassemblyjs/helper-api-error": "1.13.2", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", + "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==" + }, + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz", + "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/wasm-gen": "1.14.1" + } + }, + "node_modules/@webassemblyjs/ieee754": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", + "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "node_modules/@webassemblyjs/leb128": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", + "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/utf8": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", + "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==" + }, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz", + "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/helper-wasm-section": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-opt": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1", + "@webassemblyjs/wast-printer": "1.14.1" + } + }, + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", + "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" + } + }, + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz", + "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1" + } + }, + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz", + "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-api-error": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" + } + }, + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz", + "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webpack-cli/configtest": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-2.1.1.tgz", + "integrity": "sha512-wy0mglZpDSiSS0XHrVR+BAdId2+yxPSoJW8fsna3ZpYSlufjvxnP4YbKTCBZnNIcGN4r6ZPXV55X4mYExOfLmw==", + "dev": true, + "engines": { + "node": ">=14.15.0" + }, + "peerDependencies": { + "webpack": "5.x.x", + "webpack-cli": "5.x.x" + } + }, + "node_modules/@webpack-cli/info": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-2.0.2.tgz", + "integrity": "sha512-zLHQdI/Qs1UyT5UBdWNqsARasIA+AaF8t+4u2aS2nEpBQh2mWIVb8qAklq0eUENnC5mOItrIB4LiS9xMtph18A==", + "dev": true, + "engines": { + "node": ">=14.15.0" + }, + "peerDependencies": { + "webpack": "5.x.x", + "webpack-cli": "5.x.x" + } + }, + "node_modules/@webpack-cli/serve": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-2.0.5.tgz", + "integrity": "sha512-lqaoKnRYBdo1UgDX8uF24AfGMifWK19TxPmM5FHc2vAGxrJ/qtyUyFBWoY1tISZdelsQ5fBcOusifo5o5wSJxQ==", + "dev": true, + "engines": { + "node": ">=14.15.0" + }, + "peerDependencies": { + "webpack": "5.x.x", + "webpack-cli": "5.x.x" + }, + "peerDependenciesMeta": { + "webpack-dev-server": { + "optional": true + } + } + }, + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==" + }, + "node_modules/@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==" + }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-import-phases": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/acorn-import-phases/-/acorn-import-phases-1.0.4.tgz", + "integrity": "sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==", + "engines": { + "node": ">=10.13.0" + }, + "peerDependencies": { + "acorn": "^8.14.0" + } + }, + "node_modules/ajv": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/ansi-colors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", + "integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==", + "dependencies": { + "ansi-wrap": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ansi-gray": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-gray/-/ansi-gray-0.1.1.tgz", + "integrity": "sha512-HrgGIZUl8h2EHuZaU9hTR/cU5nhKxpVE1V6kdGsQ8e4zirElJ5fvtfc8N7Q1oq1aatO275i8pUFUCpNWCAnVWw==", + "dependencies": { + "ansi-wrap": "0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/ansi-wrap": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/ansi-wrap/-/ansi-wrap-0.1.0.tgz", + "integrity": "sha512-ZyznvL8k/FZeQHr2T6LzcJ/+vBApDnMNZvfVFy3At0knswWd6rJ3/0Hhmpu8oqa6C92npmozs890sX9Dl6q+Qw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==", + "dev": true, + "dependencies": { + "array-uniq": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/backbone": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/backbone/-/backbone-1.6.0.tgz", + "integrity": "sha512-13PUjmsgw/49EowNcQvfG4gmczz1ximTMhUktj0Jfrjth0MVaTxehpU+qYYX4MxnuIuhmvBLC6/ayxuAGnOhbA==", + "dependencies": { + "underscore": ">=1.8.3" + } + }, + "node_modules/backbone.paginator": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/backbone.paginator/-/backbone.paginator-2.0.8.tgz", + "integrity": "sha512-8XS2CTbjnwMbJ/3Traa1te2RPecOGbZ9tc52T89pzo6NXlVEJDFnC++dp7CQLBUZpgk3g0veX8mUbEF4wbD2NQ==", + "dependencies": { + "backbone": "1.1.2 || 1.2.3 || ^1.3.2", + "underscore": "^1.8.0" + } + }, + "node_modules/balanced-match": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.3.tgz", + "integrity": "sha512-1pHv8LX9CpKut1Zp4EXey7Z8OfH11ONNH6Dhi2WDUt31VVZFXZzKwXcysBgqSumFCmR+0dqjMK5v5JiFHzi0+g==", + "dev": true, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/baseline-browser-mapping": { + "version": "2.9.19", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.19.tgz", + "integrity": "sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg==", + "bin": { + "baseline-browser-mapping": "dist/cli.js" + } + }, + "node_modules/brace-expansion": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.2.tgz", + "integrity": "sha512-Pdk8c9poy+YhOgVWw1JNN22/HcivgKWwpxKq04M/jTmHyCZn12WPJebZxdjSa5TmBqISrUSgNYU3eRORljfCCw==", + "dev": true, + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/browserslist": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", + "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "baseline-browser-mapping": "^2.9.0", + "caniuse-lite": "^1.0.30001759", + "electron-to-chromium": "^1.5.263", + "node-releases": "^2.0.27", + "update-browserslist-db": "^1.2.0" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + }, + "node_modules/call-bind": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.0", + "es-define-property": "^1.0.0", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001770", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001770.tgz", + "integrity": "sha512-x/2CLQ1jHENRbHg5PSId2sXq1CIO1CISvwWAj027ltMVG2UNgW+w9oH2+HzgEIRFembL8bUlXtfbBHR1fCg2xw==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] + }, + "node_modules/chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/chrome-trace-event": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", + "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", + "engines": { + "node": ">=6.0" + } + }, + "node_modules/clean-webpack-plugin": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/clean-webpack-plugin/-/clean-webpack-plugin-4.0.0.tgz", + "integrity": "sha512-WuWE1nyTNAyW5T7oNyys2EN0cfP2fdRxhxnIQWiAp0bMabPdHhoGxM8A6YL2GhqwgrPnnaemVE7nv5XJ2Fhh2w==", + "dev": true, + "dependencies": { + "del": "^4.1.1" + }, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "webpack": ">=4.0.0 <6.0.0" + } + }, + "node_modules/clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "dependencies": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "bin": { + "color-support": "bin.js" + } + }, + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "dev": true + }, + "node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/del": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/del/-/del-4.1.1.tgz", + "integrity": "sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ==", + "dev": true, + "dependencies": { + "@types/glob": "^7.1.1", + "globby": "^6.1.0", + "is-path-cwd": "^2.0.0", + "is-path-in-cwd": "^2.0.0", + "p-map": "^2.0.0", + "pify": "^4.0.1", + "rimraf": "^2.6.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/draggabilly": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/draggabilly/-/draggabilly-3.0.0.tgz", + "integrity": "sha512-aEs+B6prbMZQMxc9lgTpCBfyCUhRur/VFucHhIOvlvvdARTj7TcDmX/cdOUtqbjJJUh7+agyJXR5Z6IFe1MxwQ==", + "dependencies": { + "get-size": "^3.0.0", + "unidragger": "^3.0.0" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/edx-ui-toolkit": { + "version": "1.8.7", + "resolved": "https://registry.npmjs.org/edx-ui-toolkit/-/edx-ui-toolkit-1.8.7.tgz", + "integrity": "sha512-2GF463dcfKu9u0wFY1u5fX6CUs5V3VQjcTQKt1tLzFzGoK1oGg/lRp0SYsbdcIAzCT8yQAFyrpvAQh1oGuTkiA==", + "dependencies": { + "backbone": "1.6.0", + "backbone.paginator": "2.0.8", + "gulp-shell": "0.8.0", + "jquery": "~3.7.0", + "mini-css-extract-plugin": "^2.7.2", + "moment": "2.30.1", + "moment-timezone": "0.5.46", + "requirejs": "2.1.22", + "requirejs-text": "2.0.16", + "sass": "^1.58.3", + "sinon": "1.17.7", + "underscore": "~1.8.3", + "urijs": "1.19.11" + } + }, + "node_modules/edx-ui-toolkit/node_modules/underscore": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz", + "integrity": "sha512-5WsVTFcH1ut/kkhAaHf4PVgI8c7++GiVcpCGxPouI6ZVjsqPnSDf8h/8HtVqc0t4fzRXwnMK70EcZeAs3PIddg==" + }, + "node_modules/electron-to-chromium": { + "version": "1.5.286", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.286.tgz", + "integrity": "sha512-9tfDXhJ4RKFNerfjdCcZfufu49vg620741MNs26a9+bhLThdB+plgMeou98CAaHu/WATj2iHOOHTp1hWtABj2A==" + }, + "node_modules/enhanced-resolve": { + "version": "5.19.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.19.0.tgz", + "integrity": "sha512-phv3E1Xl4tQOShqSte26C7Fl84EwUdZsyOuSSk9qtAGyyQs2s3jJzComh+Abf4g187lUUAvH+H26omrqia2aGg==", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.3.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/envinfo": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.21.0.tgz", + "integrity": "sha512-Lw7I8Zp5YKHFCXL7+Dz95g4CcbMEpgvqZNNq3AmlT5XAV6CgAAk6gyAMqn2zjw08K9BHfcNuKrMiCPLByGafow==", + "dev": true, + "bin": { + "envinfo": "dist/cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-module-lexer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.0.0.tgz", + "integrity": "sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==" + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/ev-emitter": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ev-emitter/-/ev-emitter-2.1.2.tgz", + "integrity": "sha512-jQ5Ql18hdCQ4qS+RCrbLfz1n+Pags27q5TwMKvZyhp5hh2UULUYZUy1keqj6k6SYsdqIYjnmz7xyyEY0V67B8Q==" + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", + "dependencies": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fancy-log": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.3.tgz", + "integrity": "sha512-k9oEhlyc0FrVh25qYuSELjr8oxsCoc4/LEZfg2iJJrfEk/tZL9bCoJE47gqAvI2m/AUjluCS4+3I0eTx8n3AEw==", + "dependencies": { + "ansi-gray": "^0.1.1", + "color-support": "^1.1.3", + "parse-node-version": "^1.0.0", + "time-stamp": "^1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "node_modules/fast-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ] + }, + "node_modules/fastest-levenshtein": { + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", + "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", + "dev": true, + "engines": { + "node": ">= 4.9.1" + } + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "bin": { + "flat": "cli.js" + } + }, + "node_modules/for-each": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "dependencies": { + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/formatio": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/formatio/-/formatio-1.1.1.tgz", + "integrity": "sha512-cPh7is6k3d8tIUh+pnXXuAbD/uhSXGgqLPw0UrYpv5lfdJ+MMMSjx40JNpqP7Top9Nt25YomWEiRmkHbOvkCaA==", + "deprecated": "This package is unmaintained. Use @sinonjs/formatio instead", + "dependencies": { + "samsam": "~1.1" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/generator-function": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz", + "integrity": "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-size": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-size/-/get-size-3.0.0.tgz", + "integrity": "sha512-Y8aiXLq4leR7807UY0yuKEwif5s3kbVp1nTv+i4jBeoUzByTLKkLWu/HorS6/pB+7gsB0o7OTogC8AoOOeT0Hw==" + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==" + }, + "node_modules/glob/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/globby": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "integrity": "sha512-KVbFv2TQtbzCoxAnfD6JcHZTYCzyliEaaeM/gH8qQdkKr5s0OP9scEgvdcngyk7AVdY6YVW/TJHd+lQ/Df3Daw==", + "dev": true, + "dependencies": { + "array-union": "^1.0.1", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/globby/node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" + }, + "node_modules/gulp-shell": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/gulp-shell/-/gulp-shell-0.8.0.tgz", + "integrity": "sha512-wHNCgmqbWkk1c6Gc2dOL5SprcoeujQdeepICwfQRo91DIylTE7a794VEE+leq3cE2YDoiS5ulvRfKVIEMazcTQ==", + "dependencies": { + "chalk": "^3.0.0", + "fancy-log": "^1.3.3", + "lodash.template": "^4.5.0", + "plugin-error": "^1.0.1", + "through2": "^3.0.1", + "tslib": "^1.10.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hls.js": { + "version": "1.6.15", + "resolved": "https://registry.npmjs.org/hls.js/-/hls.js-1.6.15.tgz", + "integrity": "sha512-E3a5VwgXimGHwpRGV+WxRTKeSp2DW5DI5MWv34ulL3t5UNmyJWCQ1KmLEHbYzcfThfXG8amBL+fCYPneGHC4VA==" + }, + "node_modules/immutable": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.4.tgz", + "integrity": "sha512-p6u1bG3YSnINT5RQmx/yRZBpenIl30kVxkTLDyHLIMk0gict704Q9n+thfDI7lTRm9vXdDYutVzXhzcThxTnXA==" + }, + "node_modules/import-local": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", + "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", + "dev": true, + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/interpret": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-3.1.1.tgz", + "integrity": "sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==", + "dev": true, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/is-arguments": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.2.0.tgz", + "integrity": "sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==", + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-generator-function": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz", + "integrity": "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==", + "dependencies": { + "call-bound": "^1.0.4", + "generator-function": "^2.0.0", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "optional": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-path-cwd": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", + "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/is-path-in-cwd": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz", + "integrity": "sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ==", + "dev": true, + "dependencies": { + "is-path-inside": "^2.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/is-path-inside": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-2.1.0.tgz", + "integrity": "sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg==", + "dev": true, + "dependencies": { + "path-is-inside": "^1.0.2" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "dependencies": { + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/jquery": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz", + "integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==" + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/loader-runner": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.1.tgz", + "integrity": "sha512-IWqP2SCPhyVFTBtRcgMHdzlf9ul25NwaFx4wCEH/KjAXuuHY4yNjvPXsBokp8jCB936PyWRaPKUNh8NvylLp2Q==", + "engines": { + "node": ">=6.11.5" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lodash._reinterpolate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", + "integrity": "sha512-xYHt68QRoYGjeeM/XOE1uJtvXQAgvszfBhjV4yvsQH0u2i9I6cI6c6/eG4Hh3UAOVn0y/xAXwmTzEay49Q//HA==" + }, + "node_modules/lodash.template": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.5.0.tgz", + "integrity": "sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A==", + "deprecated": "This package is deprecated. Use https://socket.dev/npm/package/eta instead.", + "dependencies": { + "lodash._reinterpolate": "^3.0.0", + "lodash.templatesettings": "^4.0.0" + } + }, + "node_modules/lodash.templatesettings": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz", + "integrity": "sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ==", + "dependencies": { + "lodash._reinterpolate": "^3.0.0" + } + }, + "node_modules/lolex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/lolex/-/lolex-1.3.2.tgz", + "integrity": "sha512-YYp8cqz7/8eruZ15L1mzcPkvLYxipfdsWIDESvNdNmQP9o7TsDitRhNuV2xb7aFu2ofZngao1jiVrVZ842x4BQ==" + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mini-css-extract-plugin": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.10.0.tgz", + "integrity": "sha512-540P2c5dYnJlyJxTaSloliZexv8rji6rY8FhQN+WF/82iHQfA23j/xtJx97L+mXOML27EqksSek/g4eK7jaL3g==", + "dependencies": { + "schema-utils": "^4.0.0", + "tapable": "^2.2.1" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/minimatch": { + "version": "10.2.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.1.tgz", + "integrity": "sha512-MClCe8IL5nRRmawL6ib/eT4oLyeKMGCghibcDWK+J0hh0Q8kqSdia6BvbRMVk6mPa6WqUa5uR2oxt6C5jd533A==", + "dev": true, + "dependencies": { + "brace-expansion": "^5.0.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/moment": { + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", + "engines": { + "node": "*" + } + }, + "node_modules/moment-timezone": { + "version": "0.5.46", + "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.46.tgz", + "integrity": "sha512-ZXm9b36esbe7OmdABqIWJuBBiLLwAjrN7CE+7sYdCCx82Nabt1wHDj8TVseS59QIlfFPbOoiBPm6ca9BioG4hw==", + "dependencies": { + "moment": "^2.29.4" + }, + "engines": { + "node": "*" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" + }, + "node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "optional": true + }, + "node_modules/node-releases": { + "version": "2.0.27", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", + "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==" + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-map": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", + "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-node-version": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz", + "integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==", + "dev": true + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" + }, + "node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "optional": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==", + "dev": true, + "dependencies": { + "pinkie": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/plugin-error": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-1.0.1.tgz", + "integrity": "sha512-L1zP0dk7vGweZME2i+EeakvUNqSrdiI3F91TwEoYiGrAfUXmVv6fJIq4g82PAXxNsWOp0J7ZqQy/3Szz0ajTxA==", + "dependencies": { + "ansi-colors": "^1.0.1", + "arr-diff": "^4.0.0", + "arr-union": "^3.1.0", + "extend-shallow": "^3.0.2" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/rechoir": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz", + "integrity": "sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==", + "dev": true, + "dependencies": { + "resolve": "^1.20.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/requirejs": { + "version": "2.1.22", + "resolved": "https://registry.npmjs.org/requirejs/-/requirejs-2.1.22.tgz", + "integrity": "sha512-AhZqN7UrWV8R2d1LfGfznskMJNF0Vb6yStwrCn52UbktJg6y5V1I7RoGRyJtACe86d50PyQn0Iw0jYDKMvc4iA==", + "bin": { + "r.js": "bin/r.js" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/requirejs-text": { + "version": "2.0.16", + "resolved": "https://registry.npmjs.org/requirejs-text/-/requirejs-text-2.0.16.tgz", + "integrity": "sha512-XrzjeTb1pwzIWmkz8qnUiM20gENgiwB+66IciNuziwlaPAJsYQsQPSYyQ1kD4tGKGZxTisIfDbOHk02DpI/76Q==" + }, + "node_modules/resolve": { + "version": "1.22.11", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", + "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", + "dev": true, + "dependencies": { + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safe-regex-test": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/samsam": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/samsam/-/samsam-1.1.2.tgz", + "integrity": "sha512-iVL7LibpM3tl4rQPweOXXrmjGegxx27flTOjQEZD3PXe4oZNFzuz6Si4mgleK/JWU/hyCvtV01RUovjvBEpDmw==", + "deprecated": "This package has been deprecated in favour of @sinonjs/samsam" + }, + "node_modules/sass": { + "version": "1.97.3", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.97.3.tgz", + "integrity": "sha512-fDz1zJpd5GycprAbu4Q2PV/RprsRtKC/0z82z0JLgdytmcq0+ujJbJ/09bPGDxCLkKY3Np5cRAOcWiVkLXJURg==", + "dependencies": { + "chokidar": "^4.0.0", + "immutable": "^5.0.2", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=14.0.0" + }, + "optionalDependencies": { + "@parcel/watcher": "^2.4.1" + } + }, + "node_modules/schema-utils": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", + "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/serialize-javascript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "dependencies": { + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/sinon": { + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-1.17.7.tgz", + "integrity": "sha512-M9rtyQxKfcTTdB64rpPSRaTzOvunb+HHPv/3PxvNPrEDnFSny95Pi6/3VoD471ody0ay0IHyzT3BErfcLXj6NA==", + "deprecated": "16.1.1", + "dependencies": { + "formatio": "1.1.1", + "lolex": "1.3.2", + "samsam": "1.1.2", + "util": ">=0.10.3 <1" + }, + "engines": { + "node": ">=0.1.103" + } + }, + "node_modules/source-list-map": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", + "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==", + "dev": true + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tapable": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz", + "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==", + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/terser": { + "version": "5.46.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.46.0.tgz", + "integrity": "sha512-jTwoImyr/QbOWFFso3YoU3ik0jBBDJ6JTOQiy/J2YxVJdZCc+5u7skhNwiOR3FQIygFqVUPHl7qbbxtjW2K3Qg==", + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.15.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser-webpack-plugin": { + "version": "5.3.16", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.16.tgz", + "integrity": "sha512-h9oBFCWrq78NyWWVcSwZarJkZ01c2AyGrzs1crmHZO3QUg9D61Wu4NPjBy69n7JqylFF5y+CsUZYmYEIZ3mR+Q==", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.25", + "jest-worker": "^27.4.5", + "schema-utils": "^4.3.0", + "serialize-javascript": "^6.0.2", + "terser": "^5.31.1" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "uglify-js": { + "optional": true + } + } + }, + "node_modules/through2": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz", + "integrity": "sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==", + "dependencies": { + "inherits": "^2.0.4", + "readable-stream": "2 || 3" + } + }, + "node_modules/time-stamp": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/time-stamp/-/time-stamp-1.1.0.tgz", + "integrity": "sha512-gLCeArryy2yNTRzTGKbZbloctj64jkZ57hj5zdraXue6aFgd6PmvVtEyiUU+hvU0v7q08oVv8r8ev0tRo6bvgw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/underscore": { + "version": "1.13.7", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.7.tgz", + "integrity": "sha512-GMXzWtsc57XAtguZgaQViUOzs0KTkk8ojr3/xAxXLITqf/3EMwxC0inyETfDFjH/Krbhuep0HNbbjI9i/q3F3g==" + }, + "node_modules/undici-types": { + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", + "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==" + }, + "node_modules/unidragger": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/unidragger/-/unidragger-3.0.1.tgz", + "integrity": "sha512-RngbGSwBFmqGBWjkaH+yB677uzR95blSQyxq6hYbrQCejH3Mx1nm8DVOuh3M9k2fQyTstWUG5qlgCnNqV/9jVw==", + "dependencies": { + "ev-emitter": "^2.0.0" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/urijs": { + "version": "1.19.11", + "resolved": "https://registry.npmjs.org/urijs/-/urijs-1.19.11.tgz", + "integrity": "sha512-HXgFDgDommxn5/bIv0cnQZsPhHDA90NPHD6+c/v21U5+Sx5hoP8+dP9IZXBU1gIfvdRfhG8cel9QNPeionfcCQ==" + }, + "node_modules/util": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", + "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", + "dependencies": { + "inherits": "^2.0.3", + "is-arguments": "^1.0.4", + "is-generator-function": "^1.0.7", + "is-typed-array": "^1.1.3", + "which-typed-array": "^1.1.2" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "node_modules/watchpack": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.5.1.tgz", + "integrity": "sha512-Zn5uXdcFNIA1+1Ei5McRd+iRzfhENPCe7LeABkJtNulSxjma+l7ltNx55BWZkRlwRnpOgHqxnjyaDgJnNXnqzg==", + "dependencies": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack": { + "version": "5.105.2", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.105.2.tgz", + "integrity": "sha512-dRXm0a2qcHPUBEzVk8uph0xWSjV/xZxenQQbLwnwP7caQCYpqG1qddwlyEkIDkYn0K8tvmcrZ+bOrzoQ3HxCDw==", + "dependencies": { + "@types/eslint-scope": "^3.7.7", + "@types/estree": "^1.0.8", + "@types/json-schema": "^7.0.15", + "@webassemblyjs/ast": "^1.14.1", + "@webassemblyjs/wasm-edit": "^1.14.1", + "@webassemblyjs/wasm-parser": "^1.14.1", + "acorn": "^8.15.0", + "acorn-import-phases": "^1.0.3", + "browserslist": "^4.28.1", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.19.0", + "es-module-lexer": "^2.0.0", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.11", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.3.1", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^4.3.3", + "tapable": "^2.3.0", + "terser-webpack-plugin": "^5.3.16", + "watchpack": "^2.5.1", + "webpack-sources": "^3.3.3" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-cli": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-5.1.4.tgz", + "integrity": "sha512-pIDJHIEI9LR0yxHXQ+Qh95k2EvXpWzZ5l+d+jIo+RdSm9MiHfzazIxwwni/p7+x4eJZuvG1AJwgC4TNQ7NRgsg==", + "dev": true, + "dependencies": { + "@discoveryjs/json-ext": "^0.5.0", + "@webpack-cli/configtest": "^2.1.1", + "@webpack-cli/info": "^2.0.2", + "@webpack-cli/serve": "^2.0.5", + "colorette": "^2.0.14", + "commander": "^10.0.1", + "cross-spawn": "^7.0.3", + "envinfo": "^7.7.3", + "fastest-levenshtein": "^1.0.12", + "import-local": "^3.0.2", + "interpret": "^3.1.1", + "rechoir": "^0.8.0", + "webpack-merge": "^5.7.3" + }, + "bin": { + "webpack-cli": "bin/cli.js" + }, + "engines": { + "node": ">=14.15.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "5.x.x" + }, + "peerDependenciesMeta": { + "@webpack-cli/generators": { + "optional": true + }, + "webpack-bundle-analyzer": { + "optional": true + }, + "webpack-dev-server": { + "optional": true + } + } + }, + "node_modules/webpack-cli/node_modules/commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "dev": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/webpack-cli/node_modules/webpack-merge": { + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.10.0.tgz", + "integrity": "sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA==", + "dev": true, + "dependencies": { + "clone-deep": "^4.0.1", + "flat": "^5.0.2", + "wildcard": "^2.0.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/webpack-manifest-plugin": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/webpack-manifest-plugin/-/webpack-manifest-plugin-5.0.1.tgz", + "integrity": "sha512-xTlX7dC3hrASixA2inuWFMz6qHsNi6MT3Uiqw621sJjRTShtpMjbDYhPPZBwWUKdIYKIjSq9em6+uzWayf38aQ==", + "dev": true, + "dependencies": { + "tapable": "^2.0.0", + "webpack-sources": "^2.2.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "webpack": "^5.75.0" + } + }, + "node_modules/webpack-manifest-plugin/node_modules/webpack-sources": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-2.3.1.tgz", + "integrity": "sha512-y9EI9AO42JjEcrTJFOYmVywVZdKVUfOvDUPsJea5GIr1JOEGFVqwlY2K098fFoIjOkDzHn2AjRvM8dsBZu+gCA==", + "dev": true, + "dependencies": { + "source-list-map": "^2.0.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack-merge": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-6.0.1.tgz", + "integrity": "sha512-hXXvrjtx2PLYx4qruKl+kyRSLc52V+cCvMxRjmKwoA+CBbbF5GfIBtR6kCvl0fYGqTUPKB+1ktVmTHqMOzgCBg==", + "dependencies": { + "clone-deep": "^4.0.1", + "flat": "^5.0.2", + "wildcard": "^2.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/webpack-sources": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.3.4.tgz", + "integrity": "sha512-7tP1PdV4vF+lYPnkMR0jMY5/la2ub5Fc/8VQrrU+lXkiM6C4TjVfGw7iKfyhnTQOsD+6Q/iKw0eFciziRgD58Q==", + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.20", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.20.tgz", + "integrity": "sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/wildcard": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz", + "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==" + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + } + } +} diff --git a/xblocks_contrib/video/package.json b/xblocks_contrib/video/package.json new file mode 100644 index 00000000..4129b09f --- /dev/null +++ b/xblocks_contrib/video/package.json @@ -0,0 +1,26 @@ +{ + "name": "video-xblock", + "version": "1.0.0", + "directories": { + "test": "tests" + }, + "scripts": { + "build": "webpack --config webpack.prod.config.js", + "build-dev": "webpack --config webpack.dev.config.js", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "dependencies": { + "draggabilly": "^3", + "edx-ui-toolkit": "^1", + "hls.js": "^1", + "underscore": "^1", + "webpack-merge": "^6" + }, + "devDependencies": { + "clean-webpack-plugin": "^4", + "webpack": "^5", + "webpack-cli": "^5", + "webpack-manifest-plugin": "^5" + } + } + \ No newline at end of file diff --git a/xblocks_contrib/video/public/css/video.css b/xblocks_contrib/video/public/css/video.css index ca20ba91..e8af61ba 100644 --- a/xblocks_contrib/video/public/css/video.css +++ b/xblocks_contrib/video/public/css/video.css @@ -1,18 +1,17 @@ - +/* CSS for VideoBlock */ @import url("https://fonts.googleapis.com/css?family=Open+Sans:300,400,400i,600,700"); -.xmodule_display.xmodule_VideoBlock { +.xblock { margin-bottom: calc((var(--baseline, 20px) * 1.5)); } -.xmodule_display.xmodule_VideoBlock .is-hidden, -.xmodule_display.xmodule_VideoBlock .video.closed .subtitles { +.is-hidden, +.video.closed .subtitles { display: none; } -.xmodule_display.xmodule_VideoBlock .video { +.video { background: whitesmoke; display: block; margin: 0 -12px; @@ -21,111 +20,111 @@ https://github.com/openedx/edx-platform/blob/master/xmodule/static/css-builtin-b outline: none; } -.xmodule_display.xmodule_VideoBlock .video:after { +.video:after { content: ""; display: table; clear: both; } -.xmodule_display.xmodule_VideoBlock .video:focus, -.xmodule_display.xmodule_VideoBlock .video:active, -.xmodule_display.xmodule_VideoBlock .video:hover { +.video:focus, +.video:active, +.video:hover { border: 0; } -.xmodule_display.xmodule_VideoBlock .video.is-initialized .video-wrapper .spinner { +.video.is-initialized .video-wrapper .spinner { display: none; } -.xmodule_display.xmodule_VideoBlock .video.is-pre-roll .slider { +.video.is-pre-roll .slider { visibility: hidden; } -.xmodule_display.xmodule_VideoBlock .video.is-pre-roll .video-player { +.video.is-pre-roll .video-player { position: relative; } -.xmodule_display.xmodule_VideoBlock .video.is-pre-roll .video-player::before { +.video.is-pre-roll .video-player::before { display: block; content: ""; width: 100%; padding-top: 55%; } -.xmodule_display.xmodule_VideoBlock .video .tc-wrapper { +.video .tc-wrapper { position: relative; } -.xmodule_display.xmodule_VideoBlock .video .tc-wrapper:after { +.video .tc-wrapper:after { content: ""; display: table; clear: both; } -.xmodule_display.xmodule_VideoBlock .video .focus_grabber { +.video .focus_grabber { position: relative; display: inline; width: 0; height: 0; } -.xmodule_display.xmodule_VideoBlock .video .downloads-heading { +.video .downloads-heading { margin: 1em 0 0; } -.xmodule_display.xmodule_VideoBlock .video .wrapper-video-bottom-section { +.video .wrapper-video-bottom-section { display: flex; justify-content: space-between; } -.xmodule_display.xmodule_VideoBlock .video .wrapper-video-bottom-section .wrapper-download-video, -.xmodule_display.xmodule_VideoBlock .video .wrapper-video-bottom-section .wrapper-download-transcripts, -.xmodule_display.xmodule_VideoBlock .video .wrapper-video-bottom-section .wrapper-handouts, -.xmodule_display.xmodule_VideoBlock .video .wrapper-video-bottom-section , -.xmodule_display.xmodule_VideoBlock .video .wrapper-video-bottom-section .wrapper-transcript-feedback { +.video .wrapper-video-bottom-section .wrapper-download-video, +.video .wrapper-video-bottom-section .wrapper-download-transcripts, +.video .wrapper-video-bottom-section .wrapper-handouts, +.video .wrapper-video-bottom-section , +.video .wrapper-video-bottom-section .wrapper-transcript-feedback { margin-top: var(--baseline, 20px); padding-right: var(--baseline, 20px); vertical-align: top; } @media (min-width: 768px) { - .xmodule_display.xmodule_VideoBlock .video .wrapper-downloads { + .video .wrapper-downloads { display: flex; } } -.xmodule_display.xmodule_VideoBlock .video .wrapper-downloads .hd { +.video .wrapper-downloads .hd { margin: 0; } -.xmodule_display.xmodule_VideoBlock .video .wrapper-downloads .wrapper-download-video .video-sources { +.video .wrapper-downloads .wrapper-download-video .video-sources { margin: 0; } -.xmodule_display.xmodule_VideoBlock .video .wrapper-downloads .wrapper-download-transcripts .list-download-transcripts { +.video .wrapper-downloads .wrapper-download-transcripts .list-download-transcripts { margin: 0; padding: 0; list-style: none; } -.xmodule_display.xmodule_VideoBlock .video .wrapper-downloads .wrapper-download-transcripts .list-download-transcripts .transcript-option { +.video .wrapper-downloads .wrapper-download-transcripts .list-download-transcripts .transcript-option { display: flex; align-items: center; margin: 0; } -.xmodule_display.xmodule_VideoBlock .video .wrapper-downloads .wrapper-download-transcripts .list-download-transcripts .transcript-option a.btn, -.xmodule_display.xmodule_VideoBlock .video .wrapper-downloads .wrapper-download-transcripts .list-download-transcripts .transcript-option a.btn-link { +.video .wrapper-downloads .wrapper-download-transcripts .list-download-transcripts .transcript-option a.btn, +.video .wrapper-downloads .wrapper-download-transcripts .list-download-transcripts .transcript-option a.btn-link { font-size: 16px !important; font-weight: unset; padding-left: 4px; } -.xmodule_display.xmodule_VideoBlock .video .wrapper-downloads { +.video .wrapper-downloads { padding-right: 0; } -.xmodule_display.xmodule_VideoBlock .video .wrapper-downloads .host-tag { +.video .wrapper-downloads .host-tag { position: absolute; left: -9999em; display: inline-block; @@ -133,7 +132,7 @@ https://github.com/openedx/edx-platform/blob/master/xmodule/static/css-builtin-b color: var(--body-color, #313131); } -.xmodule_display.xmodule_VideoBlock .video .wrapper-downloads .brand-logo { +.video .wrapper-downloads .brand-logo { display: inline-block; max-width: 100%; max-height: calc((var(--baseline, 20px) * 2)); @@ -141,33 +140,33 @@ https://github.com/openedx/edx-platform/blob/master/xmodule/static/css-builtin-b vertical-align: middle; } -.xmodule_display.xmodule_VideoBlock .video .wrapper-transcript-feedback { +.video .wrapper-transcript-feedback { display: none; } -.xmodule_display.xmodule_VideoBlock .video .wrapper-transcript-feedback .transcript-feedback-buttons { +.video .wrapper-transcript-feedback .transcript-feedback-buttons { display: flex; } -.xmodule_display.xmodule_VideoBlock .video .wrapper-transcript-feedback .transcript-feedback-btn-wrapper { +.video .wrapper-transcript-feedback .transcript-feedback-btn-wrapper { margin-right: 10px; } -.xmodule_display.xmodule_VideoBlock .video .wrapper-transcript-feedback .thumbs-up-btn, -.xmodule_display.xmodule_VideoBlock .video .wrapper-transcript-feedback .thumbs-down-btn { +.video .wrapper-transcript-feedback .thumbs-up-btn, +.video .wrapper-transcript-feedback .thumbs-down-btn { border: none; box-shadow: none; background: transparent; } -.xmodule_display.xmodule_VideoBlock .video .google-disclaimer { +.video .google-disclaimer { display: none; margin-top: var(--baseline, 20px); padding-right: var(--baseline, 20px); vertical-align: top; } -.xmodule_display.xmodule_VideoBlock .video .video-wrapper { +.video .video-wrapper { float: left; margin-right: 2.27273%; width: 65.90909%; @@ -175,21 +174,21 @@ https://github.com/openedx/edx-platform/blob/master/xmodule/static/css-builtin-b position: relative; } -.xmodule_display.xmodule_VideoBlock .video .video-wrapper:hover .btn-play { +.video .video-wrapper:hover .btn-play { color: #0075b4; } -.xmodule_display.xmodule_VideoBlock .video .video-wrapper:hover .btn-play::after { +.video .video-wrapper:hover .btn-play::after { background: #fff; } -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .video-player-pre, -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .video-player-post { +.video .video-wrapper .video-player-pre, +.video .video-wrapper .video-player-post { height: 50px; background-color: #111010; } -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .spinner { +.video .video-wrapper .spinner { transform: translate(-50%, -50%); position: absolute; z-index: 1; @@ -200,7 +199,7 @@ https://github.com/openedx/edx-platform/blob/master/xmodule/static/css-builtin-b border-radius: 25%; } -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .spinner::after { +.video .video-wrapper .spinner::after { animation: rotateCW 3s infinite linear; content: ''; display: block; @@ -212,7 +211,7 @@ https://github.com/openedx/edx-platform/blob/master/xmodule/static/css-builtin-b position: relative; } -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .btn-play { +.video .video-wrapper .btn-play { transform: translate(-50%, -50%); position: absolute; z-index: 1; @@ -223,7 +222,7 @@ https://github.com/openedx/edx-platform/blob/master/xmodule/static/css-builtin-b opacity: 0.1; } -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .btn-play::after { +.video .video-wrapper .btn-play::after { background: var(--white, #fff); position: absolute; width: 50%; @@ -237,7 +236,7 @@ https://github.com/openedx/edx-platform/blob/master/xmodule/static/css-builtin-b z-index: -1; } -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .closed-captions { +.video .video-wrapper .closed-captions { left: 5%; position: absolute; width: 90%; @@ -246,7 +245,7 @@ https://github.com/openedx/edx-platform/blob/master/xmodule/static/css-builtin-b text-align: center; } -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .closed-captions.is-visible { +.video .video-wrapper .closed-captions.is-visible { max-height: calc((var(--baseline, 20px) * 3)); border-radius: calc((var(--baseline, 20px) / 5)); padding: 8px calc((var(--baseline, 20px) / 2)) 8px calc((var(--baseline, 20px) * 1.5)); @@ -254,7 +253,7 @@ https://github.com/openedx/edx-platform/blob/master/xmodule/static/css-builtin-b color: var(--yellow, #e2c01f); } -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .closed-captions.is-visible::before { +.video .video-wrapper .closed-captions.is-visible::before { position: absolute; display: inline-block; top: 50%; @@ -266,76 +265,76 @@ https://github.com/openedx/edx-platform/blob/master/xmodule/static/css-builtin-b opacity: 0.5; } -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .closed-captions.is-visible:hover, -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .closed-captions.is-visible.is-dragging { +.video .video-wrapper .closed-captions.is-visible:hover, +.video .video-wrapper .closed-captions.is-visible.is-dragging { background: black; cursor: move; } -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .closed-captions.is-visible:hover::before, -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .closed-captions.is-visible.is-dragging::before { +.video .video-wrapper .closed-captions.is-visible:hover::before, +.video .video-wrapper .closed-captions.is-visible.is-dragging::before { opacity: 1; } -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .video-player { +.video .video-wrapper .video-player { overflow: hidden; min-height: 158px; } -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .video-player>div { +.video .video-wrapper .video-player>div { height: 100%; } -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .video-player>div.hidden { +.video .video-wrapper .video-player>div.hidden { display: none; } -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .video-player .video-error, -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .video-player .video-hls-error { +.video .video-wrapper .video-player .video-error, +.video .video-wrapper .video-player .video-hls-error { padding: calc((var(--baseline, 20px) / 5)); background: black; color: white !important; } -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .video-player object, -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .video-player iframe, -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .video-player video { +.video .video-wrapper .video-player object, +.video .video-wrapper .video-player iframe, +.video .video-wrapper .video-player video { left: 0; display: block; border: none; width: 100%; } -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .video-player h4 { +.video .video-wrapper .video-player h4 { text-align: center; color: white; } -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .video-player h4.hidden { +.video .video-wrapper .video-player h4.hidden { display: none; } -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .video-controls { +.video .video-wrapper .video-controls { position: relative; border: 0; background: #282c2e; color: #f0f3f5; } -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .video-controls:after { +.video .video-wrapper .video-controls:after { content: ""; display: table; clear: both; } -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .video-controls:hover ul, -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .video-controls:hover div, -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .video-controls:focus ul, -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .video-controls:focus div { +.video .video-wrapper .video-controls:hover ul, +.video .video-wrapper .video-controls:hover div, +.video .video-wrapper .video-controls:focus ul, +.video .video-wrapper .video-controls:focus div { opacity: 1; } -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .video-controls .control { +.video .video-wrapper .video-controls .control { display: inline-block; vertical-align: middle; margin: 0; @@ -348,26 +347,26 @@ https://github.com/openedx/edx-platform/blob/master/xmodule/static/css-builtin-b color: #cfd8dc; } -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .video-controls .control:hover, -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .video-controls .control:focus { +.video .video-wrapper .video-controls .control:hover, +.video .video-wrapper .video-controls .control:focus { background: #171a1b; } -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .video-controls .control:active, -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .video-controls .is-active.control, -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .video-controls .active.control { +.video .video-wrapper .video-controls .control:active, +.video .video-wrapper .video-controls .is-active.control, +.video .video-wrapper .video-controls .active.control { color: #0ea6ec; } -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .video-controls .control .icon { +.video .video-wrapper .video-controls .control .icon { width: 1em; } -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .video-controls .control .icon.icon-hd { +.video .video-wrapper .video-controls .control .icon.icon-hd { width: auto; } -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .video-controls .slider { +.video .video-wrapper .video-controls .slider { transform-origin: bottom left; transition: height 0.7s ease-in-out 0s; box-sizing: border-box; @@ -383,13 +382,13 @@ https://github.com/openedx/edx-platform/blob/master/xmodule/static/css-builtin-b background: #4f595d; } -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .video-controls .slider:after { +.video .video-wrapper .video-controls .slider:after { content: ""; display: table; clear: both; } -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .video-controls .slider .ui-widget-header { +.video .video-wrapper .video-controls .slider .ui-widget-header { background: #8e3e63; border: 1px solid #8e3e63; box-shadow: none; @@ -397,12 +396,12 @@ https://github.com/openedx/edx-platform/blob/master/xmodule/static/css-builtin-b left: -1px; } -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .video-controls .slider .ui-corner-all.slider-range { +.video .video-wrapper .video-controls .slider .ui-corner-all.slider-range { opacity: 0.3; background-color: #1e91d3; } -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .video-controls .slider .ui-slider-handle { +.video .video-wrapper .video-controls .slider .ui-slider-handle { transform-origin: bottom left; transition: all 0.7s ease-in-out 0s; box-sizing: border-box; @@ -417,13 +416,13 @@ https://github.com/openedx/edx-platform/blob/master/xmodule/static/css-builtin-b box-shadow: none; } -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .video-controls .slider .ui-slider-handle:focus, -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .video-controls .slider .ui-slider-handle:hover { +.video .video-wrapper .video-controls .slider .ui-slider-handle:focus, +.video .video-wrapper .video-controls .slider .ui-slider-handle:hover { background-color: #db8baf; border-color: #db8baf; } -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .video-controls .vcr { +.video .video-wrapper .video-controls .vcr { float: left; list-style: none; border-right: 1px solid #282c2e; @@ -431,21 +430,21 @@ https://github.com/openedx/edx-platform/blob/master/xmodule/static/css-builtin-b } @media (max-width: 1120px) { - .xmodule_display.xmodule_VideoBlock .video .video-wrapper .video-controls .vcr { + .video .video-wrapper .video-controls .vcr { margin-right: lh(0.5); font-size: 0.875em; } } -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .video-controls .vcr .video_control:focus { +.video .video-wrapper .video-controls .vcr .video_control:focus { position: relative; } -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .video-controls .vcr .video_control.skip { +.video .video-wrapper .video-controls .vcr .video_control.skip { white-space: nowrap; } -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .video-controls .vcr .vidtime { +.video .video-wrapper .video-controls .vcr .vidtime { padding-left: lh(0.75); display: inline-block; color: #cfd8dc; @@ -453,38 +452,38 @@ https://github.com/openedx/edx-platform/blob/master/xmodule/static/css-builtin-b } @media (max-width: 1120px) { - .xmodule_display.xmodule_VideoBlock .video .video-wrapper .video-controls .vcr .vidtime { + .video .video-wrapper .video-controls .vcr .vidtime { padding-left: lh(0.5); } } -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .video-controls .secondary-controls { +.video .video-wrapper .video-controls .secondary-controls { float: right; border-left: 1px dotted #4f595d; } -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .video-controls .secondary-controls .volume, -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .video-controls .secondary-controls .add-fullscreen, -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .video-controls .secondary-controls .grouped-controls, -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .video-controls .secondary-controls .auto-advance, -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .video-controls .secondary-controls .quality-control { +.video .video-wrapper .video-controls .secondary-controls .volume, +.video .video-wrapper .video-controls .secondary-controls .add-fullscreen, +.video .video-wrapper .video-controls .secondary-controls .grouped-controls, +.video .video-wrapper .video-controls .secondary-controls .auto-advance, +.video .video-wrapper .video-controls .secondary-controls .quality-control { border-left: 1px dotted #4f595d; } -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .video-controls .secondary-controls .speed-button:focus, -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .video-controls .secondary-controls .volume>.control:focus, -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .video-controls .secondary-controls .add-fullscreen:focus, -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .video-controls .secondary-controls .auto-advance:focus, -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .video-controls .secondary-controls .quality-control:focus, -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .video-controls .secondary-controls .toggle-transcript:focus { +.video .video-wrapper .video-controls .secondary-controls .speed-button:focus, +.video .video-wrapper .video-controls .secondary-controls .volume>.control:focus, +.video .video-wrapper .video-controls .secondary-controls .add-fullscreen:focus, +.video .video-wrapper .video-controls .secondary-controls .auto-advance:focus, +.video .video-wrapper .video-controls .secondary-controls .quality-control:focus, +.video .video-wrapper .video-controls .secondary-controls .toggle-transcript:focus { position: relative; } -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .video-controls .secondary-controls .menu-container { +.video .video-wrapper .video-controls .secondary-controls .menu-container { position: relative; } -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .video-controls .secondary-controls .menu-container .menu { +.video .video-wrapper .video-controls .secondary-controls .menu-container .menu { transition: none; position: absolute; display: none; @@ -499,12 +498,12 @@ https://github.com/openedx/edx-platform/blob/master/xmodule/static/css-builtin-b list-style: none; } -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .video-controls .secondary-controls .menu-container .menu li { +.video .video-wrapper .video-controls .secondary-controls .menu-container .menu li { color: #e7ecee; } -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .video-controls .secondary-controls .menu-container .menu li .speed-option, -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .video-controls .secondary-controls .menu-container .menu li .control-lang { +.video .video-wrapper .video-controls .secondary-controls .menu-container .menu li .speed-option, +.video .video-wrapper .video-controls .secondary-controls .menu-container .menu li .control-lang { text-align: left; display: block; width: 100%; @@ -520,84 +519,84 @@ https://github.com/openedx/edx-platform/blob/master/xmodule/static/css-builtin-b white-space: nowrap; } -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .video-controls .secondary-controls .menu-container .menu li .speed-option:hover, -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .video-controls .secondary-controls .menu-container .menu li .speed-option:focus, -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .video-controls .secondary-controls .menu-container .menu li .control-lang:hover, -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .video-controls .secondary-controls .menu-container .menu li .control-lang:focus { +.video .video-wrapper .video-controls .secondary-controls .menu-container .menu li .speed-option:hover, +.video .video-wrapper .video-controls .secondary-controls .menu-container .menu li .speed-option:focus, +.video .video-wrapper .video-controls .secondary-controls .menu-container .menu li .control-lang:hover, +.video .video-wrapper .video-controls .secondary-controls .menu-container .menu li .control-lang:focus { background-color: #4f595d; color: #fcfcfc; } -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .video-controls .secondary-controls .menu-container .menu li.is-active .speed-option, -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .video-controls .secondary-controls .menu-container .menu li.is-active .control-lang { +.video .video-wrapper .video-controls .secondary-controls .menu-container .menu li.is-active .speed-option, +.video .video-wrapper .video-controls .secondary-controls .menu-container .menu li.is-active .control-lang { border-left: calc(var(--baseline, 20px) / 10) solid #90d7f9; font-weight: var(--font-bold, 700); color: #90d7f9; } -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .video-controls .secondary-controls .menu-container.is-opened .menu { +.video .video-wrapper .video-controls .secondary-controls .menu-container.is-opened .menu { display: block; } -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .video-controls .secondary-controls .speeds, -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .video-controls .secondary-controls .lang, -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .video-controls .secondary-controls .grouped-controls { +.video .video-wrapper .video-controls .secondary-controls .speeds, +.video .video-wrapper .video-controls .secondary-controls .lang, +.video .video-wrapper .video-controls .secondary-controls .grouped-controls { display: inline-block; } -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .video-controls .secondary-controls .speeds.is-opened .control .icon { +.video .video-wrapper .video-controls .secondary-controls .speeds.is-opened .control .icon { transform: rotate(-90deg); } -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .video-controls .secondary-controls .speeds .speed-button .label { +.video .video-wrapper .video-controls .secondary-controls .speeds .speed-button .label { padding: 0 calc((var(--baseline, 20px) / 3)) 0 0; font-family: var(--font-family-sans-serif); color: #e7ecee; } @media (max-width: 1120px) { - .xmodule_display.xmodule_VideoBlock .video .video-wrapper .video-controls .secondary-controls .speeds .speed-button .label { + .video .video-wrapper .video-controls .secondary-controls .speeds .speed-button .label { position: absolute; clip: rect(1px, 1px, 1px, 1px); } } -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .video-controls .secondary-controls .speeds .speed-button .value { +.video .video-wrapper .video-controls .secondary-controls .speeds .speed-button .value { padding: 0 lh(0.5) 0 0; color: #e7ecee; font-weight: bold; } @media (max-width: 1120px) { - .xmodule_display.xmodule_VideoBlock .video .video-wrapper .video-controls .secondary-controls .speeds .speed-button .value { + .video .video-wrapper .video-controls .secondary-controls .speeds .speed-button .value { padding: 0 lh(0.5); } } -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .video-controls .secondary-controls .lang .language-menu { +.video .video-wrapper .video-controls .secondary-controls .lang .language-menu { width: var(--baseline, 20px); padding: calc((var(--baseline, 20px) / 2)) 0; } -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .video-controls .secondary-controls .lang.is-opened .control .icon { +.video .video-wrapper .video-controls .secondary-controls .lang.is-opened .control .icon { transform: rotate(90deg); } -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .video-controls .secondary-controls .volume { +.video .video-wrapper .video-controls .secondary-controls .volume { display: inline-block; position: relative; } -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .video-controls .secondary-controls .volume.is-opened .volume-slider-container { +.video .video-wrapper .video-controls .secondary-controls .volume.is-opened .volume-slider-container { display: block; opacity: 1; } -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .video-controls .secondary-controls .volume:not(:first-child)>a { +.video .video-wrapper .video-controls .secondary-controls .volume:not(:first-child)>a { border-left: none; } -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .video-controls .secondary-controls .volume .volume-slider-container { +.video .video-wrapper .video-controls .secondary-controls .volume .volume-slider-container { transition: none; display: none; position: absolute; @@ -608,7 +607,7 @@ https://github.com/openedx/edx-platform/blob/master/xmodule/static/css-builtin-b background-color: #282c2e; } -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .video-controls .secondary-controls .volume .volume-slider-container .volume-slider { +.video .video-wrapper .video-controls .secondary-controls .volume .volume-slider-container .volume-slider { height: 100px; width: calc((var(--baseline, 20px) / 4)); margin: 14px auto; @@ -617,7 +616,7 @@ https://github.com/openedx/edx-platform/blob/master/xmodule/static/css-builtin-b background: #4f595d; } -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .video-controls .secondary-controls .volume .volume-slider-container .volume-slider .ui-slider-handle { +.video .video-wrapper .video-controls .secondary-controls .volume .volume-slider-container .volume-slider .ui-slider-handle { transition: height var(--tmg-s2, 2s) ease-in-out 0s, width var(--tmg-s2, 2s) ease-in-out 0s; left: -5px; box-sizing: border-box; @@ -630,59 +629,59 @@ https://github.com/openedx/edx-platform/blob/master/xmodule/static/css-builtin-b box-shadow: none; } -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .video-controls .secondary-controls .volume .volume-slider-container .volume-slider .ui-slider-handle:hover, -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .video-controls .secondary-controls .volume .volume-slider-container .volume-slider .ui-slider-handle:focus { +.video .video-wrapper .video-controls .secondary-controls .volume .volume-slider-container .volume-slider .ui-slider-handle:hover, +.video .video-wrapper .video-controls .secondary-controls .volume .volume-slider-container .volume-slider .ui-slider-handle:focus { background: #db8baf; border-color: #db8baf; } -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .video-controls .secondary-controls .volume .volume-slider-container .volume-slider .ui-slider-range { +.video .video-wrapper .video-controls .secondary-controls .volume .volume-slider-container .volume-slider .ui-slider-range { background: #8e3e63; border: 1px solid #8e3e63; left: -1px; bottom: -1px; } -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .video-controls .secondary-controls .quality-control { +.video .video-wrapper .video-controls .secondary-controls .quality-control { font-weight: 700; letter-spacing: -1px; } -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .video-controls .secondary-controls .quality-control.active { +.video .video-wrapper .video-controls .secondary-controls .quality-control.active { color: #0ea6ec; } -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .video-controls .secondary-controls .quality-control.is-hidden, -.xmodule_display.xmodule_VideoBlock .video.closed .video-wrapper .video-controls .secondary-controls .quality-control.subtitles { +.video .video-wrapper .video-controls .secondary-controls .quality-control.is-hidden, +.video.closed .video-wrapper .video-controls .secondary-controls .quality-control.subtitles { display: none !important; } -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .video-controls .secondary-controls .toggle-transcript.is-active { +.video .video-wrapper .video-controls .secondary-controls .toggle-transcript.is-active { color: #0ea6ec; } -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .video-controls .secondary-controls .lang>.hide-subtitles { +.video .video-wrapper .video-controls .secondary-controls .lang>.hide-subtitles { transition: none; } -.xmodule_display.xmodule_VideoBlock .video .video-wrapper:hover .video-controls .slider { +.video .video-wrapper:hover .video-controls .slider { height: calc((var(--baseline, 20px) / 1.5)); } -.xmodule_display.xmodule_VideoBlock .video .video-wrapper:hover .video-controls .slider .ui-slider-handle { +.video .video-wrapper:hover .video-controls .slider .ui-slider-handle { height: calc((var(--baseline, 20px) / 1.5)); width: calc((var(--baseline, 20px) / 1.5)); } -.xmodule_display.xmodule_VideoBlock .video.video-fullscreen .closed-captions { +.video.video-fullscreen .closed-captions { width: 65%; } -.xmodule_display.xmodule_VideoBlock .video.video-fullscreen.closed .closed-captions { +.video.video-fullscreen.closed .closed-captions { width: 90%; } -.xmodule_display.xmodule_VideoBlock .video .subtitles { +.video .subtitles { float: left; overflow: auto; max-height: 460px; @@ -692,18 +691,18 @@ https://github.com/openedx/edx-platform/blob/master/xmodule/static/css-builtin-b visibility: visible; } -.xmodule_display.xmodule_VideoBlock .video .subtitles a { +.video .subtitles a { color: #0074b5; } -.xmodule_display.xmodule_VideoBlock .video .subtitles .subtitles-menu { +.video .subtitles .subtitles-menu { height: 100%; margin: 0; padding: 0 3px; list-style: none; } -.xmodule_display.xmodule_VideoBlock .video .subtitles .subtitles-menu li { +.video .subtitles .subtitles-menu li { margin-bottom: 8px; border: 0; padding: 0; @@ -711,48 +710,48 @@ https://github.com/openedx/edx-platform/blob/master/xmodule/static/css-builtin-b line-height: lh(); } -.xmodule_display.xmodule_VideoBlock .video .subtitles .subtitles-menu li:has(> span:empty) { +.video .subtitles .subtitles-menu li:has(> span:empty) { display: none; } -.xmodule_display.xmodule_VideoBlock .video .subtitles .subtitles-menu li span { +.video .subtitles .subtitles-menu li span { display: block; } -.xmodule_display.xmodule_VideoBlock .video .subtitles .subtitles-menu li.current { +.video .subtitles .subtitles-menu li.current { color: #333; font-weight: 700; } -.xmodule_display.xmodule_VideoBlock .video .subtitles .subtitles-menu li.focused { +.video .subtitles .subtitles-menu li.focused { outline: #000 dotted thin; outline-offset: -1px; } -.xmodule_display.xmodule_VideoBlock .video .subtitles .subtitles-menu li:hover, -.xmodule_display.xmodule_VideoBlock .video .subtitles .subtitles-menu li:focus { +.video .subtitles .subtitles-menu li:hover, +.video .subtitles .subtitles-menu li:focus { text-decoration: underline; } -.xmodule_display.xmodule_VideoBlock .video .subtitles .subtitles-menu li:empty { +.video .subtitles .subtitles-menu li:empty { margin-bottom: 0; } -.xmodule_display.xmodule_VideoBlock .video .subtitles .subtitles-menu li.spacing:last-of-type { +.video .subtitles .subtitles-menu li.spacing:last-of-type { position: relative; } -.xmodule_display.xmodule_VideoBlock .video .subtitles .subtitles-menu li.spacing:last-of-type .transcript-end { +.video .subtitles .subtitles-menu li.spacing:last-of-type .transcript-end { position: absolute; bottom: 0; } -.xmodule_display.xmodule_VideoBlock .video.closed .video-wrapper { +.video.closed .video-wrapper { width: 100%; background-color: inherit; } -.xmodule_display.xmodule_VideoBlock .video.closed .video-wrapper .video-controls.html5 { +.video.closed .video-wrapper .video-controls.html5 { bottom: 0; left: 0; right: 0; @@ -760,16 +759,16 @@ https://github.com/openedx/edx-platform/blob/master/xmodule/static/css-builtin-b z-index: 1; } -.xmodule_display.xmodule_VideoBlock .video.closed .video-wrapper .video-player-pre, -.xmodule_display.xmodule_VideoBlock .video.closed .video-wrapper .video-player-post { +.video.closed .video-wrapper .video-player-pre, +.video.closed .video-wrapper .video-player-post { height: 0; } -.xmodule_display.xmodule_VideoBlock .video.closed .video-wrapper .video-player h3 { +.video.closed .video-wrapper .video-player h3 { color: black; } -.xmodule_display.xmodule_VideoBlock .video.closed .subtitles.html5 { +.video.closed .subtitles.html5 { background-color: rgba(243, 243, 243, 0.8); height: 100%; position: absolute; @@ -781,7 +780,7 @@ https://github.com/openedx/edx-platform/blob/master/xmodule/static/css-builtin-b display: none; } -.xmodule_display.xmodule_VideoBlock .video.video-fullscreen { +.video.video-fullscreen { background: rgba(0, 0, 0, 0.95); border: 0; bottom: 0; @@ -796,58 +795,58 @@ https://github.com/openedx/edx-platform/blob/master/xmodule/static/css-builtin-b border-radius: 0; } -.xmodule_display.xmodule_VideoBlock .video.video-fullscreen.closed .tc-wrapper .video-wrapper { +.video.video-fullscreen.closed .tc-wrapper .video-wrapper { width: 100%; } -.xmodule_display.xmodule_VideoBlock .video.video-fullscreen .video-wrapper .video-player-pre, -.xmodule_display.xmodule_VideoBlock .video.video-fullscreen .video-wrapper .video-player-post { +.video.video-fullscreen .video-wrapper .video-player-pre, +.video.video-fullscreen .video-wrapper .video-player-post { height: 0; } -.xmodule_display.xmodule_VideoBlock .video.video-fullscreen .video-wrapper { +.video.video-fullscreen .video-wrapper { position: static; } -.xmodule_display.xmodule_VideoBlock .video.video-fullscreen .video-wrapper .video-player h3 { +.video.video-fullscreen .video-wrapper .video-player h3 { color: white; } -.xmodule_display.xmodule_VideoBlock .video.video-fullscreen .tc-wrapper { +.video.video-fullscreen .tc-wrapper { width: 100%; height: 100%; position: static; } -.xmodule_display.xmodule_VideoBlock .video.video-fullscreen .tc-wrapper:after { +.video.video-fullscreen .tc-wrapper:after { content: ""; display: table; clear: both; } -.xmodule_display.xmodule_VideoBlock .video.video-fullscreen .tc-wrapper .video-wrapper { +.video.video-fullscreen .tc-wrapper .video-wrapper { height: 100%; width: 75%; margin-right: 0; vertical-align: middle; } -.xmodule_display.xmodule_VideoBlock .video.video-fullscreen .tc-wrapper .video-wrapper object, -.xmodule_display.xmodule_VideoBlock .video.video-fullscreen .tc-wrapper .video-wrapper iframe, -.xmodule_display.xmodule_VideoBlock .video.video-fullscreen .tc-wrapper .video-wrapper video { +.video.video-fullscreen .tc-wrapper .video-wrapper object, +.video.video-fullscreen .tc-wrapper .video-wrapper iframe, +.video.video-fullscreen .tc-wrapper .video-wrapper video { position: absolute; width: auto; height: auto; } -.xmodule_display.xmodule_VideoBlock .video.video-fullscreen .tc-wrapper .video-controls { +.video.video-fullscreen .tc-wrapper .video-controls { position: absolute; bottom: 0; left: 0; width: 100%; } -.xmodule_display.xmodule_VideoBlock .video.video-fullscreen .subtitles { +.video.video-fullscreen .subtitles { height: 100%; width: 25%; padding: lh(); @@ -857,22 +856,22 @@ https://github.com/openedx/edx-platform/blob/master/xmodule/static/css-builtin-b visibility: visible; } -.xmodule_display.xmodule_VideoBlock .video.video-fullscreen .subtitles li { +.video.video-fullscreen .subtitles li { color: #aaa; } -.xmodule_display.xmodule_VideoBlock .video.video-fullscreen .subtitles li.current { +.video.video-fullscreen .subtitles li.current { color: var(--white, #fff); } -.xmodule_display.xmodule_VideoBlock .video.is-touch .tc-wrapper .video-wrapper object, -.xmodule_display.xmodule_VideoBlock .video.is-touch .tc-wrapper .video-wrapper iframe, -.xmodule_display.xmodule_VideoBlock .video.is-touch .tc-wrapper .video-wrapper video { +.video.is-touch .tc-wrapper .video-wrapper object, +.video.is-touch .tc-wrapper .video-wrapper iframe, +.video.is-touch .tc-wrapper .video-wrapper video { width: 100%; height: 100%; } -.xmodule_display.xmodule_VideoBlock .video .video-pre-roll { +.video .video-pre-roll { position: absolute; top: 0; left: 0; @@ -884,11 +883,11 @@ https://github.com/openedx/edx-platform/blob/master/xmodule/static/css-builtin-b background-color: var(--black, #000); } -.xmodule_display.xmodule_VideoBlock .video .video-pre-roll.is-html5 { +.video .video-pre-roll.is-html5 { background-size: 15%; } -.xmodule_display.xmodule_VideoBlock .video .video-pre-roll .btn-play.btn-pre-roll { +.video .video-pre-roll .btn-play.btn-pre-roll { padding: var(--baseline, 20px); border: none; border-radius: var(--baseline, 20px); @@ -896,69 +895,69 @@ https://github.com/openedx/edx-platform/blob/master/xmodule/static/css-builtin-b box-shadow: none; } -.xmodule_display.xmodule_VideoBlock .video .video-pre-roll .btn-play.btn-pre-roll::after { +.video .video-pre-roll .btn-play.btn-pre-roll::after { display: none; } -.xmodule_display.xmodule_VideoBlock .video .video-pre-roll .btn-play.btn-pre-roll img { +.video .video-pre-roll .btn-play.btn-pre-roll img { height: calc((var(--baseline, 20px) * 4)); width: calc((var(--baseline, 20px) * 4)); } -.xmodule_display.xmodule_VideoBlock .video .video-pre-roll .btn-play.btn-pre-roll:hover, -.xmodule_display.xmodule_VideoBlock .video .video-pre-roll .btn-play.btn-pre-roll:focus { +.video .video-pre-roll .btn-play.btn-pre-roll:hover, +.video .video-pre-roll .btn-play.btn-pre-roll:focus { background: var(--blue, #0075b4); } -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .video-controls .slider .ui-slider-handle, -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .video-controls .secondary-controls .menu-container .menu li, -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .video-controls .secondary-controls .volume .volume-slider-container .volume-slider .ui-slider-handle, -.xmodule_display.xmodule_VideoBlock .video .subtitles .subtitles-menu li, -.xmodule_display.xmodule_VideoBlock .a11y-menu-container .a11y-menu-list li { +.video .video-wrapper .video-controls .slider .ui-slider-handle, +.video .video-wrapper .video-controls .secondary-controls .menu-container .menu li, +.video .video-wrapper .video-controls .secondary-controls .volume .volume-slider-container .volume-slider .ui-slider-handle, +.video .subtitles .subtitles-menu li, +.a11y-menu-container .a11y-menu-list li { cursor: pointer; } -.xmodule_display.xmodule_VideoBlock .video.closed .subtitles.html5 { +.video.closed .subtitles.html5 { z-index: 0; } -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .video-controls .secondary-controls .menu-container .menu, -.xmodule_display.xmodule_VideoBlock .video .video-wrapper .video-controls .secondary-controls .volume .volume-slider-container { +.video .video-wrapper .video-controls .secondary-controls .menu-container .menu, +.video .video-wrapper .video-controls .secondary-controls .volume .volume-slider-container { z-index: 10; } -.xmodule_display.xmodule_VideoBlock .video .video-pre-roll, -.xmodule_display.xmodule_VideoBlock .a11y-menu-container .a11y-menu-list { +.video .video-pre-roll, +.a11y-menu-container .a11y-menu-list { z-index: 1000; } -.xmodule_display.xmodule_VideoBlock .video.video-fullscreen, -.xmodule_display.xmodule_VideoBlock .video.video-fullscreen .tc-wrapper .video-controls, -.xmodule_display.xmodule_VideoBlock .overlay { +.video.video-fullscreen, +.video.video-fullscreen .tc-wrapper .video-controls, +.overlay { z-index: 10000; } -.xmodule_display.xmodule_VideoBlock .contextmenu, -.xmodule_display.xmodule_VideoBlock .submenu { +.contextmenu, +.submenu { z-index: 100000; } -.xmodule_display.xmodule_VideoBlock .video-tracks .a11y-menu-container>a::after { +.video-tracks .a11y-menu-container>a::after { font-family: FontAwesome; -webkit-font-smoothing: antialiased; display: inline-block; speak: none; } -.xmodule_display.xmodule_VideoBlock .a11y-menu-container { +.a11y-menu-container { position: relative; } -.xmodule_display.xmodule_VideoBlock .a11y-menu-container.open .a11y-menu-list { +.a11y-menu-container.open .a11y-menu-list { display: block; } -.xmodule_display.xmodule_VideoBlock .a11y-menu-container .a11y-menu-list { +.a11y-menu-container .a11y-menu-list { top: 100%; margin: 0; padding: 0; @@ -969,14 +968,14 @@ https://github.com/openedx/edx-platform/blob/master/xmodule/static/css-builtin-b border: 1px solid #eee; } -.xmodule_display.xmodule_VideoBlock .a11y-menu-container .a11y-menu-list li { +.a11y-menu-container .a11y-menu-list li { margin: 0; padding: 0; border-bottom: 1px solid #eee; color: var(--white, #fff); } -.xmodule_display.xmodule_VideoBlock .a11y-menu-container .a11y-menu-list li a { +.a11y-menu-container .a11y-menu-list li a { display: block; overflow: hidden; text-overflow: ellipsis; @@ -986,37 +985,37 @@ https://github.com/openedx/edx-platform/blob/master/xmodule/static/css-builtin-b line-height: 23px; } -.xmodule_display.xmodule_VideoBlock .a11y-menu-container .a11y-menu-list li a:hover, -.xmodule_display.xmodule_VideoBlock .a11y-menu-container .a11y-menu-list li a:focus { +.a11y-menu-container .a11y-menu-list li a:hover, +.a11y-menu-container .a11y-menu-list li a:focus { color: var(--gray-d1, #5e5e5e); } -.xmodule_display.xmodule_VideoBlock .a11y-menu-container .a11y-menu-list li.active a { +.a11y-menu-container .a11y-menu-list li.active a { color: #009fe6; } -.xmodule_display.xmodule_VideoBlock .a11y-menu-container .a11y-menu-list li:last-child { +.a11y-menu-container .a11y-menu-list li:last-child { box-shadow: none; border-bottom: 0; margin-top: 0; } -.xmodule_display.xmodule_VideoBlock .video-tracks .a11y-menu-container { +.video-tracks .a11y-menu-container { display: inline-block; vertical-align: top; border-left: 1px solid #eee; } -.xmodule_display.xmodule_VideoBlock .video-tracks .a11y-menu-container.open>a { +.video-tracks .a11y-menu-container.open>a { background-color: var(--action-primary-active-bg, #0075b4); color: var(--very-light-text, white); } -.xmodule_display.xmodule_VideoBlock .video-tracks .a11y-menu-container.open>a::after { +.video-tracks .a11y-menu-container.open>a::after { color: var(--very-light-text, white); } -.xmodule_display.xmodule_VideoBlock .video-tracks .a11y-menu-container>a { +.video-tracks .a11y-menu-container>a { transition: all var(--tmg-f2, 0.25s) ease-in-out 0s; font-size: 12px; display: block; @@ -1031,7 +1030,7 @@ https://github.com/openedx/edx-platform/blob/master/xmodule/static/css-builtin-b text-overflow: ellipsis; } -.xmodule_display.xmodule_VideoBlock .video-tracks .a11y-menu-container>a::after { +.video-tracks .a11y-menu-container>a::after { content: "\f0d7"; position: absolute; right: calc((var(--baseline, 20px) * 0.5)); @@ -1039,15 +1038,15 @@ https://github.com/openedx/edx-platform/blob/master/xmodule/static/css-builtin-b color: var(--lighter-base-font-color, #646464); } -.xmodule_display.xmodule_VideoBlock .video-tracks .a11y-menu-container .a11y-menu-list { +.video-tracks .a11y-menu-container .a11y-menu-list { right: 0; } -.xmodule_display.xmodule_VideoBlock .video-tracks .a11y-menu-container .a11y-menu-list li { +.video-tracks .a11y-menu-container .a11y-menu-list li { font-size: 0.875em; } -.xmodule_display.xmodule_VideoBlock .video-tracks .a11y-menu-container .a11y-menu-list li a { +.video-tracks .a11y-menu-container .a11y-menu-list li a { border: 0; display: block; padding: 0.70788em; @@ -1056,8 +1055,8 @@ https://github.com/openedx/edx-platform/blob/master/xmodule/static/css-builtin-b white-space: nowrap; } -.xmodule_display.xmodule_VideoBlock .contextmenu, -.xmodule_display.xmodule_VideoBlock .submenu { +.contextmenu, +.submenu { border: 1px solid #333; background: var(--white, #fff); color: #333; @@ -1072,57 +1071,57 @@ https://github.com/openedx/edx-platform/blob/master/xmodule/static/css-builtin-b white-space: nowrap; } -.xmodule_display.xmodule_VideoBlock .contextmenu.is-opened, -.xmodule_display.xmodule_VideoBlock .submenu.is-opened { +.contextmenu.is-opened, +.submenu.is-opened { display: block; } -.xmodule_display.xmodule_VideoBlock .contextmenu .menu-item, -.xmodule_display.xmodule_VideoBlock .contextmenu .submenu-item, -.xmodule_display.xmodule_VideoBlock .submenu .menu-item, -.xmodule_display.xmodule_VideoBlock .submenu .submenu-item { +.contextmenu .menu-item, +.contextmenu .submenu-item, +.submenu .menu-item, +.submenu .submenu-item { border-top: 1px solid var(--gray-l3, #c8c8c8); padding: calc((var(--baseline, 20px) / 4)) calc((var(--baseline, 20px) / 2)); outline: none; } -.xmodule_display.xmodule_VideoBlock .contextmenu .menu-item>span, -.xmodule_display.xmodule_VideoBlock .contextmenu .submenu-item>span, -.xmodule_display.xmodule_VideoBlock .submenu .menu-item>span, -.xmodule_display.xmodule_VideoBlock .submenu .submenu-item>span { +.contextmenu .menu-item>span, +.contextmenu .submenu-item>span, +.submenu .menu-item>span, +.submenu .submenu-item>span { color: #333; } -.xmodule_display.xmodule_VideoBlock .contextmenu .menu-item:first-child, -.xmodule_display.xmodule_VideoBlock .contextmenu .submenu-item:first-child, -.xmodule_display.xmodule_VideoBlock .submenu .menu-item:first-child, -.xmodule_display.xmodule_VideoBlock .submenu .submenu-item:first-child { +.contextmenu .menu-item:first-child, +.contextmenu .submenu-item:first-child, +.submenu .menu-item:first-child, +.submenu .submenu-item:first-child { border-top: none; } -.xmodule_display.xmodule_VideoBlock .contextmenu .menu-item:focus, -.xmodule_display.xmodule_VideoBlock .contextmenu .submenu-item:focus, -.xmodule_display.xmodule_VideoBlock .submenu .menu-item:focus, -.xmodule_display.xmodule_VideoBlock .submenu .submenu-item:focus { +.contextmenu .menu-item:focus, +.contextmenu .submenu-item:focus, +.submenu .menu-item:focus, +.submenu .submenu-item:focus { background: #333; color: var(--white, #fff); } -.xmodule_display.xmodule_VideoBlock .contextmenu .menu-item:focus>span, -.xmodule_display.xmodule_VideoBlock .contextmenu .submenu-item:focus>span, -.xmodule_display.xmodule_VideoBlock .submenu .menu-item:focus>span, -.xmodule_display.xmodule_VideoBlock .submenu .submenu-item:focus>span { +.contextmenu .menu-item:focus>span, +.contextmenu .submenu-item:focus>span, +.submenu .menu-item:focus>span, +.submenu .submenu-item:focus>span { color: var(--white, #fff); } -.xmodule_display.xmodule_VideoBlock .contextmenu .submenu-item, -.xmodule_display.xmodule_VideoBlock .submenu .submenu-item { +.contextmenu .submenu-item, +.submenu .submenu-item { position: relative; padding: calc((var(--baseline, 20px) / 4)) var(--baseline, 20px) calc((var(--baseline, 20px) / 4)) calc((var(--baseline, 20px) / 2)); } -.xmodule_display.xmodule_VideoBlock .contextmenu .submenu-item::after, -.xmodule_display.xmodule_VideoBlock .submenu .submenu-item::after { +.contextmenu .submenu-item::after, +.submenu .submenu-item::after { content: '\25B6'; position: absolute; right: 5px; @@ -1130,39 +1129,39 @@ https://github.com/openedx/edx-platform/blob/master/xmodule/static/css-builtin-b font-size: 10px; } -.xmodule_display.xmodule_VideoBlock .contextmenu .submenu-item .submenu, -.xmodule_display.xmodule_VideoBlock .submenu .submenu-item .submenu { +.contextmenu .submenu-item .submenu, +.submenu .submenu-item .submenu { display: none; } -.xmodule_display.xmodule_VideoBlock .contextmenu .submenu-item.is-opened, -.xmodule_display.xmodule_VideoBlock .submenu .submenu-item.is-opened { +.contextmenu .submenu-item.is-opened, +.submenu .submenu-item.is-opened { background: #333; color: var(--white, #fff); } -.xmodule_display.xmodule_VideoBlock .contextmenu .submenu-item.is-opened>span, -.xmodule_display.xmodule_VideoBlock .submenu .submenu-item.is-opened>span { +.contextmenu .submenu-item.is-opened>span, +.submenu .submenu-item.is-opened>span { color: var(--white, #fff); } -.xmodule_display.xmodule_VideoBlock .contextmenu .submenu-item.is-opened>.submenu, -.xmodule_display.xmodule_VideoBlock .submenu .submenu-item.is-opened>.submenu { +.contextmenu .submenu-item.is-opened>.submenu, +.submenu .submenu-item.is-opened>.submenu { display: block; } -.xmodule_display.xmodule_VideoBlock .contextmenu .submenu-item .is-selected, -.xmodule_display.xmodule_VideoBlock .submenu .submenu-item .is-selected { +.contextmenu .submenu-item .is-selected, +.submenu .submenu-item .is-selected { font-weight: bold; } -.xmodule_display.xmodule_VideoBlock .contextmenu .is-disabled, -.xmodule_display.xmodule_VideoBlock .submenu .is-disabled { +.contextmenu .is-disabled, +.submenu .is-disabled { pointer-events: none; color: var(--gray-l3, #c8c8c8); } -.xmodule_display.xmodule_VideoBlock .overlay { +.overlay { position: fixed; top: 0; left: 0; @@ -1171,7 +1170,7 @@ https://github.com/openedx/edx-platform/blob/master/xmodule/static/css-builtin-b background-color: transparent; } -.xmodule_display.xmodule_VideoBlock .wrapper-social-share .social-toggle-btn { +.wrapper-social-share .social-toggle-btn { background: var(--primary); font-size: 13px; font-weight: 700; @@ -1183,16 +1182,16 @@ https://github.com/openedx/edx-platform/blob/master/xmodule/static/css-builtin-b border: none; } -.xmodule_display.xmodule_VideoBlock .wrapper-social-share .social-toggle-btn:hover, -.xmodule_display.xmodule_VideoBlock .wrapper-social-share .social-toggle-btn:focus { +.wrapper-social-share .social-toggle-btn:hover, +.wrapper-social-share .social-toggle-btn:focus { background: var(--btn-brand-focus-background); } -.xmodule_display.xmodule_VideoBlock .wrapper-social-share .social-toggle-btn .fa { +.wrapper-social-share .social-toggle-btn .fa { margin-right: calc(var(--baseline) * 0.4); } -.xmodule_display.xmodule_VideoBlock .wrapper-social-share .container-social-share { +.wrapper-social-share .container-social-share { padding: calc(var(--baseline) * 0.4); width: 300px; border-radius: 6px; @@ -1200,7 +1199,7 @@ https://github.com/openedx/edx-platform/blob/master/xmodule/static/css-builtin-b box-shadow: rgba(0, 0, 0, 0.15) 0 0.5rem 1rem, rgba(0, 0, 0, 0.15) 0 0.25rem 0.625rem; } -.xmodule_display.xmodule_VideoBlock .wrapper-social-share .container-social-share .close-btn { +.wrapper-social-share .container-social-share .close-btn { float: right; cursor: pointer; vertical-align: top; @@ -1209,7 +1208,7 @@ https://github.com/openedx/edx-platform/blob/master/xmodule/static/css-builtin-b text-decoration: none !important; } -.xmodule_display.xmodule_VideoBlock .wrapper-social-share .container-social-share .social-share-link { +.wrapper-social-share .container-social-share .social-share-link { margin-right: calc(var(--baseline) * 0.2); font-size: 24px; height: 24px; @@ -1218,14 +1217,14 @@ https://github.com/openedx/edx-platform/blob/master/xmodule/static/css-builtin-b display: inline-flex; } -.xmodule_display.xmodule_VideoBlock .wrapper-social-share .container-social-share .social-share-link > span > svg { +.wrapper-social-share .container-social-share .social-share-link > span > svg { width: auto; height: 24px; vertical-align: top; display: inline-flex; } -.xmodule_display.xmodule_VideoBlock .wrapper-social-share .container-social-share .public-video-url-container { +.wrapper-social-share .container-social-share .public-video-url-container { padding: calc(var(--baseline) * 0.4); display: flex; align-items: center; @@ -1233,7 +1232,7 @@ https://github.com/openedx/edx-platform/blob/master/xmodule/static/css-builtin-b background-color: #f2f0ef; } -.xmodule_display.xmodule_VideoBlock .wrapper-social-share .container-social-share .public-video-url-link { +.wrapper-social-share .container-social-share .public-video-url-link { color: var(--black); overflow: hidden; text-overflow: ellipsis; @@ -1241,18 +1240,18 @@ https://github.com/openedx/edx-platform/blob/master/xmodule/static/css-builtin-b white-space: nowrap; } -.xmodule_display.xmodule_VideoBlock .wrapper-social-share .container-social-share .public-video-url-link:hover { +.wrapper-social-share .container-social-share .public-video-url-link:hover { text-decoration: underline; } -.xmodule_display.xmodule_VideoBlock .wrapper-social-share .container-social-share .public-video-copy-btn { +.wrapper-social-share .container-social-share .public-video-copy-btn { margin-left: calc(var(--baseline) * 0.7); flex-shrink: 0; color: var(--primary); cursor: pointer; } -.xmodule_display.xmodule_VideoBlock .wrapper-social-share .container-social-share .public-video-copy-btn:hover { +.wrapper-social-share .container-social-share .public-video-copy-btn:hover { text-decoration: none; color: var(--link-hover-color); -} +} \ No newline at end of file diff --git a/xblocks_contrib/video/studio_metadata_mixin.py b/xblocks_contrib/video/studio_metadata_mixin.py new file mode 100644 index 00000000..1c3808c3 --- /dev/null +++ b/xblocks_contrib/video/studio_metadata_mixin.py @@ -0,0 +1,165 @@ +""" Studio Metadata Mixin""" +from django.conf import settings +from xblock.core import XBlock +from xblock.fields import Dict, Float, Integer, List, Scope, String + +from xblocks_contrib.video.exceptions import TranscriptNotFoundError +from xblocks_contrib.video.video_transcripts_utils import TranscriptExtensions, get_html5_ids +from xblocks_contrib.video.video_xfields import RelativeTime + + +class StudioMetadataMixin: + """ + Mixin providing Studio metadata editing capabilities for XBlocks. + """ + + @property + def non_editable_metadata_fields(self): + """ + Return the list of fields that should not be editable in Studio. + + When overriding, be sure to append to the superclasses' list. + """ + # We are not allowing editing of xblock tag and name fields at this time (for any component). + return [XBlock.tags, XBlock.name] + + def _create_metadata_editor_info(self, field): + """ + Creates the information needed by the metadata editor for a specific field. + """ + + def jsonify_value(field, json_choice): + """ + Convert field value to JSON, if needed. + """ + if isinstance(json_choice, dict): + new_json_choice = dict(json_choice) # make a copy so below doesn't change the original + if "display_name" in json_choice: + new_json_choice["display_name"] = get_text(json_choice["display_name"]) + if "value" in json_choice: + new_json_choice["value"] = field.to_json(json_choice["value"]) + else: + new_json_choice = field.to_json(json_choice) + return new_json_choice + + def get_text(value): + """Localize a text value that might be None.""" + if value is None: + return None + else: + return self.runtime.service(self, "i18n").ugettext(value) + + # gets the 'default_value' and 'explicitly_set' attrs + metadata_field_editor_info = self.runtime.get_field_provenance(self, field) + metadata_field_editor_info["field_name"] = field.name + metadata_field_editor_info["display_name"] = get_text(field.display_name) + metadata_field_editor_info["help"] = get_text(field.help) + metadata_field_editor_info["value"] = field.read_json(self) + + # We support the following editors: + # 1. A select editor for fields with a list of possible values (includes Booleans). + # 2. Number editors for integers and floats. + # 3. A generic string editor for anything else (editing JSON representation of the value). + editor_type = "Generic" + values = field.values + if "values_provider" in field.runtime_options: + values = field.runtime_options["values_provider"](self) + if isinstance(values, (tuple, list)) and len(values) > 0: + editor_type = "Select" + values = [jsonify_value(field, json_choice) for json_choice in values] + elif isinstance(field, Integer): + editor_type = "Integer" + elif isinstance(field, Float): + editor_type = "Float" + elif isinstance(field, List): + editor_type = "List" + elif isinstance(field, Dict): + editor_type = "Dict" + elif isinstance(field, RelativeTime): + editor_type = "RelativeTime" + elif isinstance(field, String) and field.name == "license": + editor_type = "License" + metadata_field_editor_info["type"] = editor_type + metadata_field_editor_info["options"] = [] if values is None else values + + return metadata_field_editor_info + + def _get_editable_metadata_fields(self): + """ + Returns the metadata fields to be edited in Studio. These are fields with scope `Scope.settings`. + + Can be limited by extending `non_editable_metadata_fields`. + """ + metadata_fields = {} + + # Only use the fields from this class, not mixins + fields = getattr(self, "unmixed_class", self.__class__).fields + + for field in fields.values(): + if field in self.non_editable_metadata_fields: + continue + if field.scope not in (Scope.settings, Scope.content): + continue + + metadata_fields[field.name] = self._create_metadata_editor_info(field) + + return metadata_fields + + @property + def editable_metadata_fields(self): + """ + Returns the metadata fields to be edited in Studio. + """ + editable_fields = self._get_editable_metadata_fields() + + settings_service = self.runtime.service(self, 'settings') + if settings_service: + xb_settings = settings_service.get_settings_bucket(self) + if not xb_settings.get("licensing_enabled", False) and "license" in editable_fields: + del editable_fields["license"] + + # Default Timed Transcript a.k.a `sub` has been deprecated and end users shall + # not be able to modify it. + editable_fields.pop('sub') + + languages = [{'label': label, 'code': lang} for lang, label in settings.ALL_LANGUAGES] + languages.sort(key=lambda l: l['label']) + editable_fields['transcripts']['custom'] = True + editable_fields['transcripts']['languages'] = languages + editable_fields['transcripts']['type'] = 'VideoTranslations' + + # We need to send ajax requests to show transcript status + # whenever edx_video_id changes on frontend. Thats why we + # are changing type to `VideoID` so that a specific + # Backbonjs view can handle it. + editable_fields['edx_video_id']['type'] = 'VideoID' + + # `public_access` is a boolean field and by default backbonejs code render it as a dropdown with 2 options + # but in our case we also need to show an input field with dropdown, the input field will show the url to + # be shared with leaners. This is not possible with default rendering logic in backbonjs code, that is why + # we are setting a new type and then do a custom rendering in backbonejs code to render the desired UI. + editable_fields['public_access']['type'] = 'PublicAccess' + editable_fields['public_access']['url'] = self.get_public_video_url() + + # construct transcripts info and also find if `en` subs exist + transcripts_info = self.get_transcripts_info() + possible_sub_ids = [self.sub, self.youtube_id_1_0] + get_html5_ids(self.html5_sources) + video_config_service = self.runtime.service(self, 'video_config') + if video_config_service: + for sub_id in possible_sub_ids: + try: + _, sub_id, _ = video_config_service.get_transcript(self, lang='en', output_format=TranscriptExtensions.TXT) + transcripts_info['transcripts'] = dict(transcripts_info['transcripts'], en=sub_id) + break + except TranscriptNotFoundError: + continue + + editable_fields['transcripts']['value'] = transcripts_info['transcripts'] + editable_fields['transcripts']['urlRoot'] = self.runtime.handler_url( + self, + 'studio_transcript', + 'translation' + ).rstrip('/?') + editable_fields['handout']['type'] = 'FileUploader' + + return editable_fields diff --git a/xblocks_contrib/video/templates/video.html b/xblocks_contrib/video/templates/video.html index 87d3a631..362ae3df 100644 --- a/xblocks_contrib/video/templates/video.html +++ b/xblocks_contrib/video/templates/video.html @@ -1,5 +1,3 @@ - {% load i18n %} {% if display_name is not None and not is_embed %} @@ -16,7 +14,7 @@

{{ display_name|escape }}

data-block-id='{{ block_id|escape }}' data-course-id='{{ course_id|escape }}' tabindex="-1" -> + data-package-name="xblocks-contrib">
diff --git a/xblocks_contrib/video/validation.py b/xblocks_contrib/video/validation.py index 7af278df..2496f54c 100644 --- a/xblocks_contrib/video/validation.py +++ b/xblocks_contrib/video/validation.py @@ -1,5 +1,3 @@ -# NOTE: Code has been copied from the following source files -# https://github.com/openedx/edx-platform/blob/master/xmodule/validation.py """ Extension of XBlock Validation class to include information for presentation in Studio. """ diff --git a/xblocks_contrib/video/video.py b/xblocks_contrib/video/video.py index ffa3ddbd..bcb7f3a9 100644 --- a/xblocks_contrib/video/video.py +++ b/xblocks_contrib/video/video.py @@ -1,4 +1,3 @@ -# NOTE: Code has been copied from the source file https://github.com/openedx/edx-platform/blob/master/xmodule/video_block/video_block.py """Video is ungraded Xmodule for support video content. It's new improved video block, which support additional feature: - Can play non-YouTube video sources via in-browser HTML5 video player. @@ -13,7 +12,6 @@ https://s3.amazonaws.com/edx-course-videos/edx-intro/edX-FA12-cware-1_100.ogv """ - import copy import json import logging @@ -21,48 +19,49 @@ from operator import itemgetter from django.conf import settings +from django.utils.translation import gettext_noop as _ from edx_django_utils.cache import RequestCache from lxml import etree -from opaque_keys.edx.keys import CourseKey +from opaque_keys.edx.keys import CourseKey, UsageKey from opaque_keys.edx.locator import AssetLocator from web_fragments.fragment import Fragment from xblock.completable import XBlockCompletionMode -from xblock.core import XBlock -from xblock.fields import ScopeIds -from xblock.runtime import KvsFieldData -from xblocks_contrib.video import VideoBlock as _ExtractedVideoBlock +from xblock.core import Scope, XBlock +from xblock.fields import ScopeIds, UserScope from xblock.utils.resources import ResourceLoader -from common.djangoapps.xblock_django.constants import ATTR_KEY_REQUEST_COUNTRY_CODE, ATTR_KEY_USER_ID -from openedx.core.lib.cache_utils import request_cached -from openedx.core.lib.license import LicenseMixin -from xmodule.contentstore.content import StaticContent -from xmodule.editing_block import EditingMixin -from xmodule.exceptions import NotFoundError -from xmodule.modulestore.inheritance import InheritanceKeyValueStore -from xmodule.raw_block import EmptyDataRawMixin -from xmodule.util.builtin_assets import add_css_to_fragment, add_webpack_js_to_fragment -from xmodule.validation import StudioValidation, StudioValidationMessage -from xmodule.x_module import ( - PUBLIC_VIEW, STUDENT_VIEW, - ResourceTemplates, - XModuleMixin, XModuleToXBlockMixin, +from xblocks_contrib.common.utils import get_resource_url +from xblocks_contrib.common.xml_utils import ( + EdxJSONEncoder, + LegacyXmlMixin, + deserialize_field, + is_pointer_tag, + name_to_pathname, ) -from xmodule.xml_block import XmlMixin, deserialize_field, is_pointer_tag, name_to_pathname -from .bumper_utils import bumperize -from openedx.core.djangoapps.video_config.transcripts_utils import ( - Transcript, +from xblocks_contrib.video.ajax_handler_mixin import AjaxHandlerMixin +from xblocks_contrib.video.bumper_utils import bumperize +from xblocks_contrib.video.cache_utils import request_cached +from xblocks_contrib.video.constants import ATTR_KEY_REQUEST_COUNTRY_CODE, ATTR_KEY_USER_ID, PUBLIC_VIEW, STUDENT_VIEW +from xblocks_contrib.video.content import VideoBlockStaticContent +from xblocks_contrib.video.exceptions import TranscriptNotFoundError +from xblocks_contrib.video.mixin import LicenseMixin +from xblocks_contrib.video.studio_metadata_mixin import StudioMetadataMixin +from xblocks_contrib.video.validation import StudioValidation, StudioValidationMessage +from xblocks_contrib.video.video_handlers import VideoStudentViewHandlers, VideoStudioViewHandlers +from xblocks_contrib.video.video_transcripts_utils import ( + TranscriptExtensions, VideoTranscriptsMixin, clean_video_id, get_endonym_or_label, - get_html5_ids, - subs_filename + subs_filename, ) -from .video_handlers import VideoStudentViewHandlers, VideoStudioViewHandlers -from .video_utils import create_youtube_string, format_xml_exception_message, get_poster, rewrite_video_url -from .video_xfields import VideoFields - -from xblocks_contrib.video.exceptions import TranscriptNotFoundError +from xblocks_contrib.video.video_utils import ( + create_youtube_string, + format_xml_exception_message, + get_poster, + rewrite_video_url, +) +from xblocks_contrib.video.video_xfields import VideoFields # The following import/except block for edxval is temporary measure until # edxval is a proper XBlock Runtime Service. @@ -96,7 +95,7 @@ edxval_api = None log = logging.getLogger(__name__) -loader = ResourceLoader("lms") +loader = ResourceLoader(__name__) # Make '_' a no-op so we can scrape strings. Using lambda instead of # `django.utils.translation.ugettext_noop` because Django cannot be imported in this file @@ -106,12 +105,13 @@ EXPORT_IMPORT_STATIC_DIR = 'static' -@XBlock.wants('settings', 'completion', 'i18n', 'request_cache', 'video_config') -@XBlock.needs('mako', 'user') -class _BuiltInVideoBlock( - VideoFields, VideoTranscriptsMixin, VideoStudioViewHandlers, VideoStudentViewHandlers, - EmptyDataRawMixin, XmlMixin, EditingMixin, XModuleToXBlockMixin, - ResourceTemplates, XModuleMixin, LicenseMixin): +@XBlock.wants('settings', 'completion', 'request_cache', 'video_config') +@XBlock.needs('i18n', 'user') +class VideoBlock( + VideoFields, VideoTranscriptsMixin, VideoStudioViewHandlers, VideoStudentViewHandlers, + LegacyXmlMixin, XBlock, + AjaxHandlerMixin, StudioMetadataMixin, + LicenseMixin): """ XML source example: """ - is_extracted = False + is_extracted = True has_custom_completion = True completion_mode = XBlockCompletionMode.COMPLETABLE @@ -144,11 +144,56 @@ class _BuiltInVideoBlock( } ] - mako_template = "widgets/tabs-aggregator.html" js_module_name = "TabsEditingDescriptor" uses_xmodule_styles_setup = True + @property + def course_id(self): + return self.location.course_key + + @property + def category(self): + return self.scope_ids.block_type + + @property + def location(self): + return self.scope_ids.usage_id + + @location.setter + def location(self, value): + assert isinstance(value, UsageKey) + self.scope_ids = self.scope_ids._replace( + def_id=value, # Note: assigning a UsageKey as def_id is OK in old mongo / import system but wrong in split + usage_id=value, + ) + + @property + def url_name(self): + return self.location.block_id + + @property + def display_name_with_default(self): + """ + Return a display name for the module: use display_name if defined in + metadata, otherwise convert the url name. + """ + return ( + self.display_name if self.display_name is not None + else self.location.block_id.replace('_', ' ') + ) + + @property + def xblock_kvs(self): + """ + Retrieves the internal KeyValueStore for this XModule. + + Should only be used by the persistence layer. Use with caution. + """ + # if caller wants kvs, caller's assuming it's up to date; so, decache it + self.save() + return self._field_data._kvs # pylint: disable=protected-access + def get_transcripts_for_student(self, transcripts, dest_lang=None): """Return transcript information necessary for rendering the XModule student view. This is more or less a direct extraction from `get_html`. @@ -176,7 +221,7 @@ def get_transcripts_for_student(self, transcripts, dest_lang=None): try: label = get_endonym_or_label(lang_code) languages[lang_code] = label - except NotFoundError: + except TranscriptNotFoundError: continue if not other_lang or (other_lang and sub): @@ -238,14 +283,29 @@ def prioritize_hls(self, youtube_streams, html5_sources): return False - def student_view(self, context): + def studio_view(self, context): # pylint: disable=unused-argument + """ + Returns a fragment having ``data-metadata`` attribute containing + ``editable_metadata_fields`` as JSON. The authoring MFE (frontend-app-authoring) + reads this attribute from the block's HTML to power the video editor UI + (e.g. transcript parsing via ``parseTranscripts``and related thunk actions). + See: https://github.com/openedx/frontend-app-authoring/blob/master/src/editors/data/redux/thunkActions/video.js#L163 + + This matches the contract of the built-in video block: + edx-platform's ``mako_block.studio_view`` (xmodule/mako_block.py) uses + cms/templates/widgets/metadata-edit.html, which also serializes + ``editable_metadata_fields`` to JSON in a ``data-metadata`` attribute. + """ + return Fragment(f'
') + + def student_view(self, context=None): """ Return the student view. """ fragment = Fragment(self.get_html(context=context)) - add_css_to_fragment(fragment, 'VideoBlockDisplay.css') - add_webpack_js_to_fragment(fragment, 'VideoBlockDisplay') - fragment.initialize_js('Video') + fragment.add_css_url(get_resource_url(self, "css/video.css", "video")) + fragment.add_javascript_url(get_resource_url(self, "js/video-xblock.js", "video")) + fragment.initialize_js("Video") return fragment def author_view(self, context): @@ -265,9 +325,9 @@ def public_view(self, context): return self.student_view(context) fragment = Fragment(self.get_html(view=PUBLIC_VIEW, context=context)) - add_css_to_fragment(fragment, 'VideoBlockDisplay.css') - add_webpack_js_to_fragment(fragment, 'VideoBlockMain') - fragment.initialize_js('Video') + fragment.add_css_url(get_resource_url(self, "css/video.css", "video")) + fragment.add_javascript_url(get_resource_url(self, "js/video-xblock.js", "video")) + fragment.initialize_js("Video") return fragment def get_html(self, view=STUDENT_VIEW, context=None): # lint-amnesty, pylint: disable=arguments-differ, too-many-statements @@ -500,7 +560,7 @@ def is_transcript_feedback_enabled(self): video_config_service = self.runtime.service(self, 'video_config') feature_enabled = video_config_service.is_transcript_feedback_enabled( self.context_key) if video_config_service else False - except Exception as err: # pylint: disable=broad-except + except Exception: # pylint: disable=broad-except log.exception(f"Error retrieving course for course ID: {self.context_key}") return False return feature_enabled @@ -572,62 +632,6 @@ def save_with_metadata(self, user_id): self.save() self.runtime.modulestore.update_item(self, user_id) - @property - def editable_metadata_fields(self): - editable_fields = super().editable_metadata_fields - - settings_service = self.runtime.service(self, 'settings') - if settings_service: - xb_settings = settings_service.get_settings_bucket(self) - if not xb_settings.get("licensing_enabled", False) and "license" in editable_fields: - del editable_fields["license"] - - # Default Timed Transcript a.k.a `sub` has been deprecated and end users shall - # not be able to modify it. - editable_fields.pop('sub') - - languages = [{'label': label, 'code': lang} for lang, label in settings.ALL_LANGUAGES] - languages.sort(key=lambda l: l['label']) - editable_fields['transcripts']['custom'] = True - editable_fields['transcripts']['languages'] = languages - editable_fields['transcripts']['type'] = 'VideoTranslations' - - # We need to send ajax requests to show transcript status - # whenever edx_video_id changes on frontend. Thats why we - # are changing type to `VideoID` so that a specific - # Backbonjs view can handle it. - editable_fields['edx_video_id']['type'] = 'VideoID' - - # `public_access` is a boolean field and by default backbonejs code render it as a dropdown with 2 options - # but in our case we also need to show an input field with dropdown, the input field will show the url to - # be shared with leaners. This is not possible with default rendering logic in backbonjs code, that is why - # we are setting a new type and then do a custom rendering in backbonejs code to render the desired UI. - editable_fields['public_access']['type'] = 'PublicAccess' - editable_fields['public_access']['url'] = self.get_public_video_url() - - # construct transcripts info and also find if `en` subs exist - transcripts_info = self.get_transcripts_info() - possible_sub_ids = [self.sub, self.youtube_id_1_0] + get_html5_ids(self.html5_sources) - video_config_service = self.runtime.service(self, 'video_config') - if video_config_service: - for sub_id in possible_sub_ids: - try: - _, sub_id, _ = video_config_service.get_transcript(self, lang='en', output_format=Transcript.TXT) - transcripts_info['transcripts'] = dict(transcripts_info['transcripts'], en=sub_id) - break - except TranscriptNotFoundError: - continue - - editable_fields['transcripts']['value'] = transcripts_info['transcripts'] - editable_fields['transcripts']['urlRoot'] = self.runtime.handler_url( - self, - 'studio_transcript', - 'translation' - ).rstrip('/?') - editable_fields['handout']['type'] = 'FileUploader' - - return editable_fields - @classmethod def parse_xml_new_runtime(cls, node, runtime, keys): """ @@ -660,17 +664,19 @@ def parse_xml(cls, node, runtime, _keys): filepath = cls._format_filepath(node.tag, name_to_pathname(url_name)) node = cls.load_file(filepath, runtime.resources_fs, usage_id) aside_children = runtime.parse_asides(node, definition_id, usage_id, runtime.id_generator) + + scope_ids = ScopeIds(None, block_type, definition_id, usage_id) + video = runtime.construct_xblock_from_class(cls, scope_ids) + field_data = cls.parse_video_xml(node, runtime.id_generator) - kvs = InheritanceKeyValueStore(initial_values=field_data) - field_data = KvsFieldData(kvs) - video = runtime.construct_xblock_from_class( - cls, - # We're loading a descriptor, so student_id is meaningless - # We also don't have separate notions of definition and usage ids yet, - # so we use the location for both - ScopeIds(None, block_type, definition_id, usage_id), - field_data, - ) + + for field_name, value in field_data.items(): + # The 'xml_attributes' field has a special setter logic in its Field class, + # so we must handle it carefully to avoid duplicating data. + if field_name == "xml_attributes": + video.xml_attributes.update(value) + elif field_name in video.fields: + setattr(video, field_name, cls.fields[field_name].from_json(value)) # Update VAL with info extracted from `node` video.edx_video_id = video.import_video_info_into_val( @@ -976,10 +982,10 @@ def parse_video_xml(cls, xml, id_generator=None): course_id = getattr(id_generator, 'target_course_id', None) # Update the handout location with current course_id if 'handout' in field_data and course_id: - handout_location = StaticContent.get_location_from_path(field_data['handout']) + handout_location = VideoBlockStaticContent.get_location_from_path(field_data['handout']) if isinstance(handout_location, AssetLocator): - handout_new_location = StaticContent.compute_location(course_id, handout_location.path) - field_data['handout'] = StaticContent.serialize_asset_key_with_slash(handout_new_location) + handout_new_location = VideoBlockStaticContent.compute_location(course_id, handout_location.path) + field_data['handout'] = VideoBlockStaticContent.serialize_asset_key_with_slash(handout_new_location) # For backwards compatibility: Add `source` if XML doesn't have `download_video` # attribute. @@ -1053,7 +1059,7 @@ def _update_transcript_for_index(language=None): if video_config_service: try: transcript = video_config_service.get_transcript( - self, lang=language, output_format=Transcript.TXT + self, lang=language, output_format=TranscriptExtensions.TXT )[0].replace("\n", " ") transcript_index_name = f"transcript_{language if language else self.transcript_language}" video_body.update({transcript_index_name: transcript}) @@ -1191,10 +1197,95 @@ def _poster(self): ) return None + def bind_for_student(self, user_id, wrappers=None): + """ + Set up this XBlock to act as an XModule instead of an XModuleDescriptor. -VideoBlock = ( - _ExtractedVideoBlock if settings.USE_EXTRACTED_VIDEO_BLOCK - else _BuiltInVideoBlock -) -VideoBlock.__name__ = "VideoBlock" + Arguments: + user_id: The user_id to set in scope_ids + wrappers: These are a list functions that put a wrapper, such as + LmsFieldData or OverrideFieldData, around the field_data. + Note that the functions will be applied in the order in + which they're listed. So [f1, f2] -> f2(f1(field_data)) + """ + + # Skip rebinding if we're already bound a user, and it's this user. + if self.scope_ids.user_id is not None and user_id == self.scope_ids.user_id: + if getattr(self.runtime, "position", None): + self.position = self.runtime.position # update the position of the tab + return + # If we are switching users mid-request, save the data from the old user. + self.save() + + # Update scope_ids to point to the new user. + self.scope_ids = self.scope_ids._replace(user_id=user_id) + + # Clear out any cached instantiated children. + self.clear_child_cache() + + # Clear out any cached field data scoped to the old user. + for field in self.fields.values(): + if field.scope in (Scope.parent, Scope.children): + continue + + if field.scope.user == UserScope.ONE: + field._del_cached_value(self) # pylint: disable=protected-access + # not the most elegant way of doing this, but if we're removing + # a field from the module's field_data_cache, we should also + # remove it from its _dirty_fields + if field in self._dirty_fields: + del self._dirty_fields[field] + + if wrappers: + # Put user-specific wrappers around the field-data service for this block. + # Note that these are different from modulestore.xblock_field_data_wrappers, which are not user-specific. + wrapped_field_data = self.runtime.service(self, "field-data-unbound") + for wrapper in wrappers: + wrapped_field_data = wrapper(wrapped_field_data) + self._bound_field_data = wrapped_field_data + if getattr(self.runtime, "uses_deprecated_field_data", False): + # This approach is deprecated but OldModuleStoreRuntime still requires it. + # For SplitModuleStoreRuntime, don't set ._field_data this way. + self._field_data = wrapped_field_data + + @classmethod + def definition_from_xml(cls, xml_object, system): # lint-amnesty, pylint: disable=unused-argument + if len(xml_object) == 0 and len(list(xml_object.items())) == 0: + return {"data": ""}, [] + return {"data": etree.tostring(xml_object, pretty_print=True, encoding="unicode")}, [] + + def get_explicitly_set_fields_by_scope(self, scope=Scope.content): + """ + Get a dictionary of the fields for the given scope which are set explicitly on this xblock. (Including + any set to None.) + """ + result = {} + for field in self.fields.values(): + if field.scope == scope and field.is_set_on(self): + try: + result[field.name] = field.read_json(self) + except TypeError as exception: + exception_message = f"{exception}, Block-location:{self.location}, Field-name:{field.name}" + raise TypeError(exception_message) from exception + return result + + @staticmethod + def workbench_scenarios(): + """Create canned scenario for display in the workbench.""" + return [ + ( + "VideoBlock", + """<_video_extracted/> + """, + ), + ( + "Multiple VideoBlock", + """ + <_video_extracted/> + <_video_extracted/> + <_video_extracted/> + + """, + ), + ] diff --git a/xblocks_contrib/video/video_handlers.py b/xblocks_contrib/video/video_handlers.py index 738d934e..3d364aee 100644 --- a/xblocks_contrib/video/video_handlers.py +++ b/xblocks_contrib/video/video_handlers.py @@ -1,5 +1,3 @@ -# NOTE: Original code has been copied from the following files: -# https://github.com/openedx/edx-platform/blob/master/xmodule/video_block/video_handlers.py """ Handlers for video block. @@ -18,18 +16,10 @@ from xblock.core import XBlock from xblock.exceptions import JsonHandlerError -from xmodule.exceptions import NotFoundError -from xmodule.fields import RelativeTime - -from openedx.core.djangoapps.video_config.transcripts_utils import ( - Transcript, - clean_video_id, - subs_filename, -) -from xblocks_contrib.video.exceptions import ( - TranscriptsGenerationException, - TranscriptNotFoundError, -) +from xblocks_contrib.video.exceptions import TranscriptNotFoundError, TranscriptsGenerationException +from xblocks_contrib.video.video_transcripts_utils import TranscriptExtensions, clean_video_id, subs_filename +from xblocks_contrib.video.video_utils import load_metadata_from_youtube +from xblocks_contrib.video.video_xfields import RelativeTime log = logging.getLogger(__name__) @@ -123,7 +113,7 @@ def handle_ajax(self, dispatch, data): log.debug(f"GET {data}") log.debug(f"DISPATCH {dispatch}") - raise NotFoundError('Unexpected dispatch type') + raise TranscriptNotFoundError('Unexpected dispatch type') def get_static_transcript(self, request, transcripts): """ @@ -282,7 +272,7 @@ def transcript(self, request, dispatch): content, filename, mimetype = get_transcript( self, lang=self.transcript_language, - output_format=Transcript.SJSON, + output_format=TranscriptExtensions.SJSON, youtube_id=youtube_id, is_bumper=is_bumper ) @@ -362,7 +352,6 @@ def yt_video_metadata(self, request, suffix=''): # lint-amnesty, pylint: disabl This handler is only used in the Learning-Core-based runtime. The old runtime uses a similar REST API that's not an XBlock handler. """ - from lms.djangoapps.courseware.views.views import load_metadata_from_youtube if not self.youtube_id_1_0: # TODO: more informational response to explain that yt_video_metadata not supported for non-youtube videos. return Response('{}', status=400) @@ -432,7 +421,7 @@ def studio_transcript(self, request, dispatch): Return filename from storage. SRT format is sent back on success. Filename should be in GET dict. We raise all exceptions right in Studio: - NotFoundError: + TranscriptNotFoundError: Video or asset was deleted from module/contentstore, but request came later. Seems impossible to be raised. block_render.py catches NotFoundErrors from here. @@ -534,7 +523,7 @@ def _studio_transcript_get(self, request): if not video_config_service: return Response(status=404) transcript_content, transcript_name, mime_type = video_config_service.get_transcript( - self, lang=language, output_format=Transcript.SRT + self, lang=language, output_format=TranscriptExtensions.SRT ) response = Response(transcript_content, headerlist=[ ( diff --git a/xblocks_contrib/video/video_transcripts_utils.py b/xblocks_contrib/video/video_transcripts_utils.py index b0ab330e..0a4b9b6a 100644 --- a/xblocks_contrib/video/video_transcripts_utils.py +++ b/xblocks_contrib/video/video_transcripts_utils.py @@ -1,24 +1,17 @@ -# NOTE: Code has been copied from the following source files -# https://github.com/openedx/edx-platform/blob/farhan/remove-unused-video-code/openedx/core/djangoapps/video_config/transcripts_utils.py """ -Utility functions for transcripts. -++++++++++++++++++++++++++++++++++ +Utility functions for video block transcripts. +++++++++++++++++++++++++++++++++++++++++++++++ """ + import copy -import html import logging -import os -from functools import wraps -import simplejson as json from django.conf import settings from django.utils.translation import get_language_info -from pysrt import SubRipFile, SubRipItem, SubRipTime -from pysrt.srtexc import Error from xblocks_contrib.video.bumper_utils import get_bumper_settings -from xblocks_contrib.video.exceptions import NotFoundError, TranscriptsGenerationException +from xblocks_contrib.video.exceptions import TranscriptNotFoundError try: from edxval import api as edxval_api @@ -31,9 +24,9 @@ NON_EXISTENT_TRANSCRIPT = 'non_existent_dummy_file_name' -class Transcript: +class TranscriptExtensions: """ - Container for transcript methods. + Video block transcript extensions. """ SRT = 'srt' TXT = 'txt' @@ -134,7 +127,6 @@ def get_transcripts_info(self, is_bumper=False): # TODO: This causes a circular import when imported at the top-level. # This import will be removed as part of the VideoBlock extraction. # https://github.com/openedx/edx-platform/issues/36282 - from xmodule.video_block.bumper_utils import get_bumper_settings if is_bumper: transcripts = copy.deepcopy(get_bumper_settings(self).get('transcripts', {})) @@ -238,4 +230,4 @@ def get_endonym_or_label(language_code): return potential_generic_label log.error("A label was requested for language code `%s` but the code is completely unknown", language_code) - raise NotFoundError(f"Unknown language `{language_code}`") + raise TranscriptNotFoundError(f"Unknown language `{language_code}`") diff --git a/xblocks_contrib/video/video_utils.py b/xblocks_contrib/video/video_utils.py index c88e7f1f..b777f0d5 100644 --- a/xblocks_contrib/video/video_utils.py +++ b/xblocks_contrib/video/video_utils.py @@ -1,17 +1,18 @@ -# NOTE: Code has been copied from the following source files -# https://github.com/openedx/edx-platform/blob/master/xmodule/video_block/video_utils.py """ Module contains utils specific for video_block but not for transcripts. """ +import json import logging from collections import OrderedDict from urllib.parse import parse_qs, urlencode, urlparse, urlsplit, urlunsplit +import requests from django.conf import settings from django.core.exceptions import ValidationError from django.core.validators import URLValidator +from requests.exceptions import Timeout log = logging.getLogger(__name__) @@ -124,39 +125,6 @@ def set_query_parameter(url, param_name, param_value): return urlunsplit((scheme, netloc, path, new_query_string, fragment)) - -def deserialize_field(field, value): - """ - Deserialize the string version to the value stored internally. - - Note that this is not the same as the value returned by from_json, as model types typically store - their value internally as JSON. By default, this method will return the result of calling json.loads - on the supplied value, unless json.loads throws a TypeError, or the type of the value returned by json.loads - is not supported for this class (from_json throws an Error). In either of those cases, this method returns - the input value. - """ - try: - deserialized = json.loads(value) - if deserialized is None: - return deserialized - try: - field.from_json(deserialized) - return deserialized - except (ValueError, TypeError): - # Support older serialized version, which was just a string, not result of json.dumps. - # If the deserialized version cannot be converted to the type (via from_json), - # just return the original value. For example, if a string value of '3.4' was - # stored for a String field (before we started storing the result of json.dumps), - # then it would be deserialized as 3.4, but 3.4 is not supported for a String - # field. Therefore field.from_json(3.4) will throw an Error, and we should - # actually return the original value of '3.4'. - return value - - except (ValueError, TypeError): - # Support older serialized version. - return value - - def load_metadata_from_youtube(video_id, request): """ Get metadata about a YouTube video. diff --git a/xblocks_contrib/video/video_xfields.py b/xblocks_contrib/video/video_xfields.py index 5e6f19d9..09d0a40d 100644 --- a/xblocks_contrib/video/video_xfields.py +++ b/xblocks_contrib/video/video_xfields.py @@ -1,9 +1,22 @@ -# NOTE: Code has been copied from the following source files -# https://github.com/openedx/edx-platform/blob/master/xmodule/video_block/video_xfields.py#L17-L222 +""" # lint-amnesty, pylint: disable=cyclic-import +XFields for video block. +""" + + +import datetime + +from xblock.fields import Boolean, DateTime, Dict, Float, List, RelativeTime, Scope, String + +# TODO: Review the following _ function +# Make '_' a no-op so we can scrape strings. Using lambda instead of +# `django.utils.translation.ugettext_noop` because Django cannot be imported in this file +_ = lambda text: text class VideoFields: """Fields for `VideoBlock`.""" + data = String(default="", scope=Scope.content) + display_name = String( help=_("The display name for this component."), display_name=_("Component Display Name"), diff --git a/xblocks_contrib/video/webpack.common.config.js b/xblocks_contrib/video/webpack.common.config.js new file mode 100644 index 00000000..a94bc44c --- /dev/null +++ b/xblocks_contrib/video/webpack.common.config.js @@ -0,0 +1,36 @@ +const path = require('path'); +const webpack = require('webpack'); +const {CleanWebpackPlugin} = require('clean-webpack-plugin'); + +const config = { + entry: { + 'video-xblock': path.resolve(__dirname, 'static/js/src/10_main.js'), + }, + output: { + path: path.resolve(__dirname, 'public/js'), + filename: '[name].js', + clean: true, + publicPath: '/' + }, + resolve: { + alias: { + 'edx-ui-toolkit/js': 'edx-ui-toolkit/src/js', + 'hls': 'hls.js/dist/hls.js' + } + }, + externals: { + $: 'jQuery', + jquery: 'jQuery', + jQuery: 'jQuery' + }, + plugins: [ + new CleanWebpackPlugin(), + new webpack.DefinePlugin({ + 'process.env.JS_ENV_EXTRA_CONFIG': JSON.parse(process.env.JS_ENV_EXTRA_CONFIG || '{}'), + 'CAPTIONS_CONTENT_TO_REPLACE': JSON.stringify(process.env.CAPTIONS_CONTENT_TO_REPLACE || ''), + 'CAPTIONS_CONTENT_REPLACEMENT': JSON.stringify(process.env.CAPTIONS_CONTENT_REPLACEMENT || '') + }) + ] +}; + +module.exports = config diff --git a/xblocks_contrib/video/webpack.dev.config.js b/xblocks_contrib/video/webpack.dev.config.js new file mode 100644 index 00000000..a83b6b2a --- /dev/null +++ b/xblocks_contrib/video/webpack.dev.config.js @@ -0,0 +1,19 @@ +const {merge} = require('webpack-merge'); +const webpack = require('webpack'); +const _ = require('underscore'); +const commonConfig = require('./webpack.common.config.js'); + +module.exports = commonConfig; + +module.exports = merge(commonConfig, { + mode: 'development', + devtool: 'source-map', + plugins: [ + new webpack.DefinePlugin({ + 'process.env.NODE_ENV': JSON.stringify('development'), + 'process.env.JS_ENV_EXTRA_CONFIG': process.env.JS_ENV_EXTRA_CONFIG || '{}', + 'CAPTIONS_CONTENT_TO_REPLACE': JSON.stringify(process.env.CAPTIONS_CONTENT_TO_REPLACE || ''), + 'CAPTIONS_CONTENT_REPLACEMENT': JSON.stringify(process.env.CAPTIONS_CONTENT_REPLACEMENT || '') + }) + ] +}); diff --git a/xblocks_contrib/video/webpack.prod.config.js b/xblocks_contrib/video/webpack.prod.config.js new file mode 100644 index 00000000..5a403669 --- /dev/null +++ b/xblocks_contrib/video/webpack.prod.config.js @@ -0,0 +1,20 @@ +const {merge} = require('webpack-merge'); +const webpack = require('webpack'); +const _ = require('underscore'); +const commonConfig = require('./webpack.common.config.js'); + +module.exports = commonConfig; + +module.exports = merge(commonConfig, { + mode: 'production', + devtool: false, + plugins: [ + new webpack.DefinePlugin({ + 'process.env.NODE_ENV': JSON.stringify('production'), + 'process.env.JS_ENV_EXTRA_CONFIG': process.env.JS_ENV_EXTRA_CONFIG || '{}', + 'CAPTIONS_CONTENT_TO_REPLACE': JSON.stringify(process.env.CAPTIONS_CONTENT_TO_REPLACE || ''), + 'CAPTIONS_CONTENT_REPLACEMENT': JSON.stringify(process.env.CAPTIONS_CONTENT_REPLACEMENT || '') + }) + ] + +});