Showing preview only (1,034K chars total). Download the full file or copy to clipboard to get everything.
Repository: richardbarran/django-photologue
Branch: master
Commit: 82bdbe37eb70
Files: 164
Total size: 966.7 KB
Directory structure:
gitextract_l0cgokhk/
├── .coveragerc
├── .github/
│ └── workflows/
│ └── ci.yml
├── .gitignore
├── .isort.cfg
├── .readthedocs.yml
├── .tx/
│ └── config
├── CHANGELOG.txt
├── CONTRIBUTORS.txt
├── LICENSE.txt
├── MANIFEST.in
├── README.rst
├── SECURITY.md
├── docs/
│ ├── .gitignore
│ ├── Makefile
│ ├── conf.py
│ ├── index.rst
│ ├── make.bat
│ ├── pages/
│ │ ├── changelog_page.rst
│ │ ├── contributing.rst
│ │ ├── customising/
│ │ │ ├── admin.rst
│ │ │ ├── models.rst
│ │ │ ├── settings.rst
│ │ │ ├── templates.rst
│ │ │ └── views.rst
│ │ ├── installation.rst
│ │ └── usage.rst
│ └── requirements.txt
├── example_project/
│ ├── README.rst
│ ├── example_project/
│ │ ├── __init__.py
│ │ ├── example_storages/
│ │ │ ├── README.txt
│ │ │ ├── __init__.py
│ │ │ ├── s3_requirements.txt
│ │ │ ├── s3utils.py
│ │ │ └── settings_s3boto.py
│ │ ├── fixtures/
│ │ │ └── .gitdirectory
│ │ ├── settings.py
│ │ ├── static/
│ │ │ ├── css/
│ │ │ │ └── styles.css
│ │ │ └── js/
│ │ │ └── jquery-3.5.1.js
│ │ ├── templates/
│ │ │ ├── base.html
│ │ │ └── homepage.html
│ │ ├── urls.py
│ │ └── wsgi.py
│ ├── manage.py
│ ├── public/
│ │ ├── .gitdirectory
│ │ ├── media/
│ │ │ └── .gitignore
│ │ └── static/
│ │ └── .gitdirectory
│ └── requirements.txt
├── photologue/
│ ├── __init__.py
│ ├── admin.py
│ ├── apps.py
│ ├── forms.py
│ ├── locale/
│ │ ├── ca/
│ │ │ └── LC_MESSAGES/
│ │ │ ├── django.mo
│ │ │ └── django.po
│ │ ├── cs/
│ │ │ └── LC_MESSAGES/
│ │ │ ├── django.mo
│ │ │ └── django.po
│ │ ├── da/
│ │ │ └── LC_MESSAGES/
│ │ │ ├── django.mo
│ │ │ └── django.po
│ │ ├── de/
│ │ │ └── LC_MESSAGES/
│ │ │ ├── django.mo
│ │ │ └── django.po
│ │ ├── en/
│ │ │ └── LC_MESSAGES/
│ │ │ ├── django.mo
│ │ │ └── django.po
│ │ ├── en_US/
│ │ │ └── LC_MESSAGES/
│ │ │ ├── django.mo
│ │ │ └── django.po
│ │ ├── es_ES/
│ │ │ └── LC_MESSAGES/
│ │ │ ├── django.mo
│ │ │ └── django.po
│ │ ├── eu/
│ │ │ └── LC_MESSAGES/
│ │ │ ├── django.mo
│ │ │ └── django.po
│ │ ├── fr/
│ │ │ └── LC_MESSAGES/
│ │ │ ├── django.mo
│ │ │ └── django.po
│ │ ├── hu/
│ │ │ └── LC_MESSAGES/
│ │ │ ├── django.mo
│ │ │ └── django.po
│ │ ├── it/
│ │ │ └── LC_MESSAGES/
│ │ │ ├── django.mo
│ │ │ └── django.po
│ │ ├── nl/
│ │ │ └── LC_MESSAGES/
│ │ │ ├── django.mo
│ │ │ └── django.po
│ │ ├── no/
│ │ │ └── LC_MESSAGES/
│ │ │ ├── django.mo
│ │ │ └── django.po
│ │ ├── pl/
│ │ │ └── LC_MESSAGES/
│ │ │ ├── django.mo
│ │ │ └── django.po
│ │ ├── pt/
│ │ │ └── LC_MESSAGES/
│ │ │ ├── django.mo
│ │ │ └── django.po
│ │ ├── pt_BR/
│ │ │ └── LC_MESSAGES/
│ │ │ ├── django.mo
│ │ │ └── django.po
│ │ ├── ru/
│ │ │ └── LC_MESSAGES/
│ │ │ ├── django.mo
│ │ │ └── django.po
│ │ ├── sk/
│ │ │ └── LC_MESSAGES/
│ │ │ ├── django.mo
│ │ │ └── django.po
│ │ ├── tr/
│ │ │ └── LC_MESSAGES/
│ │ │ ├── django.mo
│ │ │ └── django.po
│ │ ├── tr_TR/
│ │ │ └── LC_MESSAGES/
│ │ │ ├── django.mo
│ │ │ └── django.po
│ │ ├── uk/
│ │ │ └── LC_MESSAGES/
│ │ │ ├── django.mo
│ │ │ └── django.po
│ │ └── zh_Hans/
│ │ └── LC_MESSAGES/
│ │ ├── django.mo
│ │ └── django.po
│ ├── management/
│ │ ├── __init__.py
│ │ └── commands/
│ │ ├── __init__.py
│ │ ├── plcache.py
│ │ ├── plcreatesize.py
│ │ └── plflush.py
│ ├── managers.py
│ ├── migrations/
│ │ ├── 0001_initial.py
│ │ ├── 0002_photosize_data.py
│ │ ├── 0003_auto_20140822_1716.py
│ │ ├── 0004_auto_20140915_1259.py
│ │ ├── 0005_auto_20141027_1552.py
│ │ ├── 0006_auto_20141028_2005.py
│ │ ├── 0007_auto_20150404_1737.py
│ │ ├── 0008_auto_20150509_1557.py
│ │ ├── 0009_auto_20160102_0904.py
│ │ ├── 0010_auto_20160105_1307.py
│ │ ├── 0011_auto_20190223_2138.py
│ │ ├── 0012_alter_photo_effect.py
│ │ ├── 0013_alter_watermark_image.py
│ │ └── __init__.py
│ ├── models.py
│ ├── sitemaps.py
│ ├── templates/
│ │ ├── admin/
│ │ │ └── photologue/
│ │ │ └── photo/
│ │ │ ├── change_list.html
│ │ │ └── upload_zip.html
│ │ └── photologue/
│ │ ├── gallery_archive.html
│ │ ├── gallery_archive_day.html
│ │ ├── gallery_archive_month.html
│ │ ├── gallery_archive_year.html
│ │ ├── gallery_detail.html
│ │ ├── gallery_list.html
│ │ ├── includes/
│ │ │ ├── gallery_sample.html
│ │ │ └── paginator.html
│ │ ├── photo_archive.html
│ │ ├── photo_archive_day.html
│ │ ├── photo_archive_month.html
│ │ ├── photo_archive_year.html
│ │ ├── photo_detail.html
│ │ ├── photo_list.html
│ │ ├── root.html
│ │ └── tags/
│ │ ├── next_in_gallery.html
│ │ └── prev_in_gallery.html
│ ├── templatetags/
│ │ ├── __init__.py
│ │ └── photologue_tags.py
│ ├── tests/
│ │ ├── __init__.py
│ │ ├── factories.py
│ │ ├── helpers.py
│ │ ├── templates/
│ │ │ └── base.html
│ │ ├── test_effect.py
│ │ ├── test_gallery.py
│ │ ├── test_photo.py
│ │ ├── test_photosize.py
│ │ ├── test_resize.py
│ │ ├── test_sitemap.py
│ │ ├── test_sites.py
│ │ ├── test_urls.py
│ │ ├── test_views_gallery.py
│ │ ├── test_views_photo.py
│ │ └── test_zipupload.py
│ ├── urls.py
│ ├── utils/
│ │ ├── __init__.py
│ │ ├── reflection.py
│ │ └── watermark.py
│ └── views.py
├── requirements.txt
├── scripts/
│ ├── __init__.py
│ └── releaser_hooks.py
├── setup.cfg
├── setup.py
└── tox.ini
================================================
FILE CONTENTS
================================================
================================================
FILE: .coveragerc
================================================
[run]
source = photologue
omit = *migrations*, *wsgi*, */tests/*
================================================
FILE: .github/workflows/ci.yml
================================================
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions
name: CI
on: [ push, pull_request ]
jobs:
build:
runs-on: ubuntu-latest
strategy:
fail-fast: false # If one test fails, complete the other jobs.
matrix:
python: [ 3.8, 3.9, '3.10', '3.11', '3.12', '3.13' ]
steps:
- uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install tox tox-gh-actions
working-directory: ./example_project
- name: Run tests
run: tox
- name: Flake8
if: ${{ matrix.python == '3.12' }}
run: |
pip install flake8
flake8 ./
- name: Isort
if: ${{ matrix.python == '3.12' }}
uses: jamescurtin/isort-action@master
- name: Coverage
if: ${{ matrix.python == '3.12' }}
run: |
pip install coverage[toml] django
coverage run ./example_project/manage.py test photologue
- name: Upload coverage
if: ${{ matrix.python == '3.12' }}
uses: codecov/codecov-action@v1
with:
name: Python ${{ matrix.python }}
================================================
FILE: .gitignore
================================================
.DS_Store
*__pycache__*
django_photologue.egg-info
build
.idea
example_project/db.sqlite3
htmlcov
.coverage
# Tox workfiles
.tox/*
================================================
FILE: .isort.cfg
================================================
[settings]
extend_skip_glob = photologue/migrations
line_length = 119
================================================
FILE: .readthedocs.yml
================================================
# .readthedocs.yml
# Read the Docs configuration file
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
# Required
version: 2
# Build documentation in the docs/ directory with Sphinx
sphinx:
configuration: docs/conf.py
# Optionally build your docs in additional formats such as PDF and ePub
formats:
- htmlzip
# Optionally set the version of Python and requirements required to build your docs
python:
version: 3.7
install:
- requirements: docs/requirements.txt
================================================
FILE: .tx/config
================================================
[main]
host = https://www.transifex.com
lang_map = sr@latin:sr_Latn, zh-Hans:zh_Hans
[django-photologue.core]
file_filter = photologue/locale/<lang>/LC_MESSAGES/django.po
source_file = photologue/locale/en_US/LC_MESSAGES/django.po
source_lang = en_US
type = PO
================================================
FILE: CHANGELOG.txt
================================================
Changelog
=========
3.19 (unreleased)
-----------------
- Nothing changed yet.
3.18 (2025-06-01)
-----------------
- Checked compatibility with Django 5.1 and 5.2.
- Dropped Django 3.2 and 4.1.
3.17 (2023-10-25)
-----------------
- Fixed Python 3.11 bug (#226) (contributed by emirisman).
3.16 (2023-07-28)
-----------------
- Split out zip upload functionality into a separate function (#222) (contributed by lausek).
- Do not allow JS injection into the Photo caption field (#223) (bug detected by Domiee13).
- Fixed deprecation warnings from Pillow. Should fix #225.
- Handle when PHOTOLOGUE_DIR is not a string. Should fix #224.
- Checked compatibility with Django 4.1 and 4.2.
- Dropped Django 2.2 and 4.0. Dropped Python 3.7.
- Note: not testing against Python 3.11 as I do not have it installed.
3.15.1 (2022-02-23)
-------------------
- Django 4.0 adds a no-op migration (#221) (reported by rhbvkleef).
3.15 (2022-02-05)
-----------------
- Made compatible with Django 4.0 (#220) (contributed by Martijn Verkleij).
- Updated French translation (#219) (contributed by Alexandre Iooss).
3.14 (2021-10-03)
-----------------
- Updated the sample templates to use Bootstrap 5 (previously used Bootstrap 3).
- Checked compatibility with Django 3.2.
- Use isort to tidy the Python imports.
3.13 (2020-09-03)
-----------------
- Checked compatibility with Django 3.1.
- Apply crop/effect changes to existing images (#210).
- Encoding objects before hashing error (#205).
3.12 (2020-07-30)
-----------------
- Drop alpha channel only on jpeg save (contributed by drazen)
- Added zh_Hans translation (contributed by Lessica)
- improved Dutch translations (contributed by andreas.milants)
3.11 (2019-12-13)
-----------------
- Added support for Django 3.
- Dropped support for Python 2, python 3.4 and Django 2.1.
3.10 (2019-08-29)
-----------------
- Compatibility with Django 2.2.
3.9 (2019-04-21)
----------------
- Fixes when file doesn't exist in the file system but still is in S3.
- Doc tweaks - and added a page on how to actually use Photologue!
- Make setup compatible with latest version of pip.
- Checked compatibility with Django 2.1 and Python 3.7.
- Updated translations for Catalan and Basque.
- Missed a Django migration (issue #194).
- Test Tox; integrate with Travis and Coveralls.
- Removed old code (old-style demo templates that have been deprecated since 2014).
- Removed old code (old views that have been deprecated since 2014).
- Removed all references to PIL (which hasn't been updated since 2009). I think that by now
there are no servers left anywhere in the world that still use it :-)
3.8.1 (2017-12-03)
------------------
- Admin thumbnails were not displaying correctly.
3.8 (2017-12-03)
----------------
- Added support for Django 2.0.
- Dropped support for Django 1.8 and 1.10.
- Did not really work with Django 1.11 - sortedm2m library was broken. Upgraded sortedm2m and it now works with 1.11.
- New translation for Ukrainian; updated translation for Spanish.
- Fixed template tag that was broken in Django 1.11.
3.7 (2017-05-10)
----------------
- Now works with Django 1.11. Deprecated support for Django 1.9.
- Fixed the management commands to work in the latest versions of Django.
- Fixed an issue with some photo sizes not being created (see #170).
- Updated translations for French and Basque (provided by matthieu.payet and urtzai).
3.6 (2016-10-05)
----------------
- Now works with Django 1.10 (to be precise: Photologue worked, but the unit tests did not).
- Updated urlpatterns in docs, tests and example project for Django 1.8+
- Enhance Python 2.7 EXIF info.
- Updated docs (contributed by lizwalsh).
- Fixed command plcreatesize (contributed by Mikel Larreategi).
- Fixed deprecated template settings (contributed by Justin Dugger).
- Updated translations for German and Russian.
3.5.1 (2016-01-13)
------------------
- Photologue 3.5 failed to install under Python 2.7. Looks like distutils does not like files
with non-ascii filenames (reported in #149).
- Fix for issue #149 - bug with projects that extend ImageModel.
3.5 (2016-01-09)
----------------
- Increased length of 'title' fields to 250 chars in order to store longer title.
- Rotate image before resize, to comply with height/width constraints (see #145).
- Added forgotten migration (#148).
- Changing "Photo" image leaves extra files on server (#147).
- Normalize filenames to ASCII so they work across all filesystems (#109).
- Updated Hungarian translation.
3.4.1 (2015-12-23)
------------------
- Django 1.9 requires latest version of django-sortedm2m.
3.4 (2015-12-23)
----------------
Upgrade notes:
- The EXIF property of an Image is now a method instead.
- Dropped support for Django 1.7.
- Fixed a few minor issues with the unit tests.
- Adding a watermark was crashing (fix suggested by hambro).
- Added/updated translations: Danish, Slovak (contributed by Rasmus Klett, saboter).
- Fixed Django 1.9 Deprecation warnings (contributed by jlemaes).
- Processing of EXIF data was broken (and very broken in Python 3) - updated library and bug fixes.
3.3.2 (2015-07-20)
------------------
- Release Photologue as a universal wheel.
3.3.1 (2015-07-20)
------------------
- Upload of 3.3 to Pypi had failed.
3.3 (2015-07-20)
----------------
- In the initial data setup, the 'thumbnail' photosizes should not increment the
view count (issue #133).
- Fix typo in admin text (issue reported by Transifex user ciastko).
- Updated translations: Hungarian, Czech, Dutch.
- Zip upload used gallery title instead of "Title" field for photos (#139).
- Zip upload: an uploaded photo is not a duplicate of an existing photo simply because they share the same slug.
- Updated django-sortedm2m version - this should help admin performance for galleries with lots of photos.
3.2 (2015-05-11)
----------------
- Dropped support for Django 1.6.
- Rotation of photos based upon EXIF data if available, so they get displayed correctly (#122).
- Misc doc tweaks.
- Only clear scale cache if image has changed.
- Pagination is now hard-coded to 20 items per page - it's a convenience to have it available as
soon as the app is run, but having settings to tweak this value is not needed as it's so
easy to override in a Django project.
- PHOTOLOGUE_GALLERY_PAGINATE_BY and PHOTOLOGUE_PHOTO_PAGINATE_BY were previously deprecated
and have now been removed.
- Tagging has been removed from Photologue.
- All references to 'title_slug' field have been removed.
- Django can now natively chain custom manager filters - so the dependency on django-model-utils
is removed.
- Updated German translation.
- Improved setup file.
3.1.1 (2014-11-13)
------------------
- The 'zip upload' functionality did not work (the required html templates
were not included into the released package).
- Updated French translation.
3.1 (2014-11-03)
----------------
- The 'zip upload' functionality has been moved to a custom admin page.
- Refactor `add_accessor_methods` to be lazily applied (see #110).
- Updated German translation.
- Several improvements to the sample Bootstrap templates.
- Support CACHEDIR.TAG spec issue #89
- Fix issue #99 by adding 10 extra char to photo title(max gallery size up to 999999999 images)
- Sitemap.xml was not aware of Sites (#104).
- In python 3, gallery upload would crash if uploaded file was not a zip file (#106).
3.0.2 (2014-09-23)
------------------
- Updated django-sortedm2m to an official release.
- Updated Spanish translation.
- Updated Bootstrap version used in example project.
3.0.1 (2014-09-16)
------------------
- Missed out some templates from the released package.
3.0 (2014-09-15)
----------------
Upgrade notes:
WARNING: IF YOU'RE USING POSTGRESQL AS A DATABASE & DJANGO 1.7, THE LATEST RELEASE OF
DJANGO-SORTEDM2M HAS A BUG. INSTEAD, YOU'LL HAVE TO MANUALLY INSTALL:
pip install -e git://github.com/richardbarran/django-sortedm2m.git@9a609a1c6b790a40a016e4ceadedbb6dd6b92010#egg=sortedm2m
THE FOLLOWING CHANGES BREAK BACKWARDS COMPATIBILITY!
- Django 1.7 comes with a new migrations framework which replaces South -
if you continue to use Django 1.6, you'll need to add new settings. Please
refer in the docs to the installation instructions.
If you're upgrading to Django 1.7 - upgrade Photologue first, THEN upgrade
Django.
- The Twitter-Bootstrap templates - previously in 'contrib' - become the default; the
previous templates are moved to 'contrib'.
- The django-tagging library is no longer maintained by its author. As a consequence,
it has been disabled - see the docs for more information (page
https://django-photologue.readthedocs.org/en/latest/pages/customising/settings.html#photologue-enable-tags)
- Support for Django 1.4 and 1.5 has been dropped (Photologue depends on django-sortedm2m,
which has dropped support for 1.4; and Django 1.5 is no longer supported).
- PHOTOLOGUE_USE_CKEDITOR has been removed.
- Many urls have been renamed; photologue urls now go into their own namespace. See the urls.py
file for all the changes.
Other changes:
- Support for Amazon S3 to store images (thank you Celia Oakley!).
- List views have changed urls: instead of /page/<n>/, we now have a /?page=<n> pattern.
This is a more common style, and allows us to simplify template code e.g. paginators!
- date_taken field not correctly handled during single photo upload (#80).
- Removed deprecated PhotologueSitemap.
- Gallery zip uploads would fail if the title contained unicode characters.
- Gallery-uploads: Do not require title for uploading to existing gallery (#98).
- The Photologue urls used to use names for months; this has been changed to using
numbers, which is better for non-English websites (#101).
2.8.3 (2014-08-28)
------------------
- Updated Spanish translation.
2.8.2 (2014-07-26)
------------------
- The latest release of django-sortedm2m is not compatible with older
versions of Django, so don't use it (issue #92).
2.8.1 (2014-07-26)
------------------
- Fixed issue #94 (problem with i18n plural forms).
- Updated Slovak translation.
2.8 (2014-05-04)
----------------
Upgrade notes:
1. Photologue now depends on django-sortedm2m and django-model-utils - please
refer to installation instructions. These dependencies should be added
automatically.
2. Run South migrations.
List of changes:
- Photo and Gallery models now support Django's sites framework.
- Photologue now uses django-sortedm2m to sort photos in a gallery.
- Major rewrite of zip archive uploader: warn users of files that could not be
processed, get code to work with Python 3 (issue #71), add extra error
handling.
- Renamed field title_slug to slug - this allows us to simplify views.py a
bit.
- PHOTOLOGUE_USE_CKEDITOR, PHOTOLOGUE_GALLERY_PAGINATE_BY and
PHOTOLOGUE_PHOTO_PAGINATE_BY are deprecated.
- Fixed pagination controls for photo list template.
- Tightened naming rules for Photosize names.
- Fixed a couple of unicode-related bugs.
- Added to the documentation pages describing how to customise the admin and
the views.
- Refactored slightly views.py.
- Started work on chainable querysets.
2.7 (2013-10-27)
----------------
Upgrade notes:
1. All settings are now prefixed with ``PHOTOLOGUE_``. Please check that you are
not affected by this.
List of changes:
- Fixed issue #56, Gallery pagination is broken.
- Photologue now works with Python 3.
- Added a set of templates that work well with Twitter-Bootstrap 3, and used
them for the 'example_project'.
- Fixed issue #64 (allow installation without installing Pillow).
- Optional use of CKEditor.
- Updated/new translations for Polish, Slovak and German.
- Bugfix: allow viewing latest galleries/latest photos pages even if they
are empty.
- Started using factory-boy - makes unit tests a bit easier to read.
- Added settings to customise pagination count on list pages.
- Documented all settings.
- All settings are now prefixed with ``PHOTOLOGUE_``.
2.6.1 (2013-05-19)
------------------
List of changes:
- Fixed broken packaging in release 2.6.
2.6 (2013-05-19)
----------------
Upgrade notes:
1. Photologue now relies on Pillow instead of PIL. The easiest way to upgrade
is to remove PIL completely, then install the new version of Photologue.
2. Photologue, in line with Django itself, has dropped support for Django 1.3.
List of changes:
- Switched from PIL to Pillow - hopefully this should make installation
easier.
- Initial setup of data: removed plinit and replaced it with a South data
migration.
- Added feature to allow extending the built-in templates (and documented
it!).
- Allow editing of Photo added date (temp way of sorting photos).
- Added an example project to help people wanting to contribute to the
project.
- Fixed buggy Travis CI script.
- fixed issue #52, transactions in migration
- fixed issue #51, uniqueness collisions in migration
- Accessing the root url (usually /photologue/ will now redirect you to the
gallery list view.
- Photologue requires min. Django 1.4.
- Tidied a data validator on PhotoSizes.
2.5 (2012-12-13)
----------------
- added a sitemap.xml.
- added some templatetags.
- started using Sphinx for managing documentation.
- started using Transifex for managing translations.
- started using Travis CI.
- added 12 new translations and improved some of the existing translations.
- fixed issue #29 (quote URL of resized image properly).
- misc improvements to clarity of unit tests.
- added Django 1.4 timezone support.
2.4 (2012-08-13)
----------------
Upgrade notes:
1. Starting with this version, Photologue uses South to manage the database
schema. If you are upgrading an existing Photologue installation, please
follow the South instructions at:
http://south.readthedocs.org/en/latest/convertinganapp.html#converting-other-installations-and-servers
2. Photologue has dropped support for Django 1.2.
List of changes:
- use South to manage schema changes.
- updated installation instructions.
- fixed issue #9 (In Django 1.3, FileField no longer deletes files).
- switched from function-based generic views to class-based views.
- fixed PendingDeprecationWarnings seen when running Django 1.3 - this will
make the move to Django 1.5 easier.
- added unit tests.
- fixed bug where GALLERY_SAMPLE_SIZE setting was not being used.
- fixed issue #11 (GalleryUpload with len(title) > 50 causes a crash).
- fixed issue #10 (Increase the size of the name field for photosize).
================================================
FILE: CONTRIBUTORS.txt
================================================
Photologue is made possible by all the people who have contributed to it. A non-exhaustive list follows:
Justin Driscoll
Richard Barran
Marcos Daniel Petry
Jannis
martijnverkleij
hedleyroos
Celia Oakley
Jonas Haag
drazen
Jasper Maes
Krilivye
Jakub Dorňák
Andrew Gerard
Karol Majta
Urtzi Odriozola
Alexey Vasilyev
kdeebee
lausek
Alexandre Iooss
Alasdair Nicol
lizwalsh
Steffen Goertz
Baptiste Mispelon
FilippoIOVINE
ferhat elmas
Benjamin Liles
Emir Isman
Adam Piskorek
r0668522
i_82
Steve Wills
Timothy Hobbs
Chris Frisina
nickledave
cblignaut
Justin Dugger
Mikel Larreategi
wf94
Guilhem Saurel
saboter
Mathieu Hinderyckx
Thomas Güttler
Tomas Babej
Martin Sabo
python-consulting
jscott1971
Vivek Agarwal
Ben Spaulding
Reavis Sutphin-Gray
Herman Schaaf
Antti Kaihola
Federico Maggi
cubells
AduchiMergen
abc Def
================================================
FILE: LICENSE.txt
================================================
Copyright (c) 2007-2025, Justin C. Driscoll and all the people named in CONTRIBUTORS.txt.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of django-photologue nor the names of its contributors may be used
to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
================================================
FILE: MANIFEST.in
================================================
include CHANGELOG.txt
include CONTRIBUTORS.txt
include LICENSE.txt
include requirements.txt
recursive-include photologue/locale *
recursive-include photologue/res *.jpg
recursive-include photologue/res/zips *.zip
recursive-include photologue/templates *.html
recursive-include photologue/contrib *
recursive-exclude * *.py[co]
recursive-exclude * __pycache__
exclude photologue/res/test_unicode*.jpg
exclude example_project
recursive-exclude example_project *
exclude tox.ini
================================================
FILE: README.rst
================================================
Django-photologue
=================
.. image:: https://img.shields.io/pypi/v/django-photologue.svg
:target: https://pypi.python.org/pypi/django-photologue/
:alt: Latest PyPI version
.. image:: https://github.com/richardbarran/django-photologue/workflows/CI/badge.svg?branch=master
:target: https://github.com/richardbarran/django-photologue/actions?workflow=CI
:alt: CI Status
.. image:: https://codecov.io/github/richardbarran/django-photologue/coverage.svg?branch=master
:target: https://codecov.io/github/richardbarran/django-photologue?branch=master
A powerful image management and gallery application for the Django web framework. Upload photos, group them into
galleries, apply effects such as watermarks.
Project status
--------------
Message from the current maintainer: I no longer need django-photologue for my own projects, so I've stopped working on it. I will occasionally
update it to run with current Django releases, but that's all. Any help in running this project is welcome.
Take a closer look
------------------
- We have a `demo website <http://django-photologue.arbee.design/>`_.
- We also have some `examples of sites using Photologue
<https://github.com/richardbarran/django-photologue/wiki/Examples-and-forks>`_ on the wiki.
Documentation
-------------
Please head over to our `online documentation at ReadTheDocs <https://django-photologue.readthedocs.io/en/stable/>`_
for instructions on installing and configuring this application.
Amazon S3
---------
S3 also works with the standard BOTO package.
Support
-------
If you have any questions or need help with any aspect of Photologue then please `join our mailing list
<http://groups.google.com/group/django-photologue>`_.
================================================
FILE: SECURITY.md
================================================
# Security Policy
## Supported Versions
Please note that in the case of a security issue being reported, only the latest release will be fixed. Users of earlier releases of Photologue can of course patch the version that they use and open an MR - it will be reviewed and a micro-release published.
## Reporting a Vulnerability
Please report vulnerabilities via the contact form at the personal website of the main project contributor: https://arbee.design/en-gb/contact/
================================================
FILE: docs/.gitignore
================================================
_build
================================================
FILE: docs/Makefile
================================================
# Makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
PAPER =
BUILDDIR = _build
# Internal variables.
PAPEROPT_a4 = -D latex_paper_size=a4
PAPEROPT_letter = -D latex_paper_size=letter
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
# the i18n builder cannot share the environment and doctrees with the others
I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
help:
@echo "Please use \`make <target>' where <target> is one of"
@echo " html to make standalone HTML files"
@echo " dirhtml to make HTML files named index.html in directories"
@echo " singlehtml to make a single large HTML file"
@echo " pickle to make pickle files"
@echo " json to make JSON files"
@echo " htmlhelp to make HTML files and a HTML help project"
@echo " qthelp to make HTML files and a qthelp project"
@echo " devhelp to make HTML files and a Devhelp project"
@echo " epub to make an epub"
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
@echo " latexpdf to make LaTeX files and run them through pdflatex"
@echo " text to make text files"
@echo " man to make manual pages"
@echo " texinfo to make Texinfo files"
@echo " info to make Texinfo files and run them through makeinfo"
@echo " gettext to make PO message catalogs"
@echo " changes to make an overview of all changed/added/deprecated items"
@echo " linkcheck to check all external links for integrity"
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
clean:
-rm -rf $(BUILDDIR)/*
html:
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
dirhtml:
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
singlehtml:
$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
@echo
@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
pickle:
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
@echo
@echo "Build finished; now you can process the pickle files."
json:
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
@echo
@echo "Build finished; now you can process the JSON files."
htmlhelp:
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
@echo
@echo "Build finished; now you can run HTML Help Workshop with the" \
".hhp project file in $(BUILDDIR)/htmlhelp."
qthelp:
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
@echo
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/django-photologue.qhcp"
@echo "To view the help file:"
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/django-photologue.qhc"
devhelp:
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
@echo
@echo "Build finished."
@echo "To view the help file:"
@echo "# mkdir -p $$HOME/.local/share/devhelp/django-photologue"
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/django-photologue"
@echo "# devhelp"
epub:
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
@echo
@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
latex:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
@echo "Run \`make' in that directory to run these through (pdf)latex" \
"(use \`make latexpdf' here to do that automatically)."
latexpdf:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo "Running LaTeX files through pdflatex..."
$(MAKE) -C $(BUILDDIR)/latex all-pdf
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
text:
$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
@echo
@echo "Build finished. The text files are in $(BUILDDIR)/text."
man:
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
@echo
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
texinfo:
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
@echo
@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
@echo "Run \`make' in that directory to run these through makeinfo" \
"(use \`make info' here to do that automatically)."
info:
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
@echo "Running Texinfo files through makeinfo..."
make -C $(BUILDDIR)/texinfo info
@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
gettext:
$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
@echo
@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
changes:
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
@echo
@echo "The overview file is in $(BUILDDIR)/changes."
linkcheck:
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
@echo
@echo "Link check complete; look for any errors in the above output " \
"or in $(BUILDDIR)/linkcheck/output.txt."
doctest:
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
@echo "Testing of doctests in the sources finished, look at the " \
"results in $(BUILDDIR)/doctest/output.txt."
================================================
FILE: docs/conf.py
================================================
#
# django-photologue documentation build configuration file, created by
# sphinx-quickstart on Mon Sep 3 16:31:44 2012.
#
# This file is execfile()d with the current directory set to its containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.
import datetime
import os
import sys
import django
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
sys.path.insert(0, os.path.abspath('..'))
sys.path.append('../example_project/')
os.environ['DJANGO_SETTINGS_MODULE'] = 'example_project.settings'
django.setup()
# -- General configuration -----------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
#needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest']
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# The suffix of source filenames.
source_suffix = '.rst'
# The encoding of source files.
#source_encoding = 'utf-8-sig'
# The master toctree document.
master_doc = 'index'
# General information about the project.
project = 'django-photologue'
copyright = f'{datetime.datetime.now().year}, Justin Driscoll/Richard Barran'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# Dynamically get the version number from the photologue package.
parent_dir = os.path.join(os.path.dirname(__file__), '..')
sys.path.append(parent_dir)
import photologue
# The short X.Y version.
version = '.'.join(photologue.__version__.split('.')[:1])
# The full version, including alpha/beta/rc tags.
release = photologue.__version__
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#language = None
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
#today = ''
# Else, today_fmt is used as the format for a strftime call.
#today_fmt = '%B %d, %Y'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = ['_build']
# The reST default role (used for this markup: `text`) to use for all documents.
#default_role = None
# If true, '()' will be appended to :func: etc. cross-reference text.
#add_function_parentheses = True
# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
#add_module_names = True
# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
#show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# A list of ignored prefixes for module index sorting.
#modindex_common_prefix = []
# -- Options for HTML output ---------------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
html_theme = 'sphinx_rtd_theme'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#html_theme_options = {}
# Add any paths that contain custom themes here, relative to this directory.
#html_theme_path = []
# The name for this set of Sphinx documents. If None, it defaults to
# "<project> v<release> documentation".
#html_title = None
# A shorter title for the navigation bar. Default is the same as html_title.
#html_short_title = None
# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
#html_logo = None
# The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
#html_favicon = None
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
#html_static_path = ['_static']
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
#html_last_updated_fmt = '%b %d, %Y'
# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
#html_use_smartypants = True
# Custom sidebar templates, maps document names to template names.
#html_sidebars = {}
# Additional templates that should be rendered to pages, maps page names to
# template names.
#html_additional_pages = {}
# If false, no module index is generated.
#html_domain_indices = True
# If false, no index is generated.
#html_use_index = True
# If true, the index is split into individual pages for each letter.
#html_split_index = False
# If true, links to the reST sources are added to the pages.
#html_show_sourcelink = True
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
#html_show_sphinx = True
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
#html_show_copyright = True
# If true, an OpenSearch description file will be output, and all pages will
# contain a <link> tag referring to it. The value of this option must be the
# base URL from which the finished HTML is served.
#html_use_opensearch = ''
# This is the file name suffix for HTML files (e.g. ".xhtml").
#html_file_suffix = None
# Output file base name for HTML help builder.
htmlhelp_basename = 'django-photologuedoc'
# -- Options for LaTeX output --------------------------------------------------
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#'preamble': '',
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [
('index', 'django-photologue.tex', 'django-photologue Documentation',
'Justin Driscoll/Richard Barran', 'manual'),
]
# The name of an image file (relative to this directory) to place at the top of
# the title page.
#latex_logo = None
# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
#latex_use_parts = False
# If true, show page references after internal links.
#latex_show_pagerefs = False
# If true, show URL addresses after external links.
#latex_show_urls = False
# Documents to append as an appendix to all manuals.
#latex_appendices = []
# If false, no module index is generated.
#latex_domain_indices = True
# -- Options for manual page output --------------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
('index', 'django-photologue', 'django-photologue Documentation',
['Justin Driscoll/Richard Barran'], 1)
]
# If true, show URL addresses after external links.
#man_show_urls = False
# -- Options for Texinfo output ------------------------------------------------
# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
('index', 'django-photologue', 'django-photologue Documentation',
'Justin Driscoll/Richard Barran', 'django-photologue', 'One line description of project.',
'Miscellaneous'),
]
# Documents to append as an appendix to all manuals.
#texinfo_appendices = []
# If false, no module index is generated.
#texinfo_domain_indices = True
# How to display URL addresses: 'footnote', 'no', or 'inline'.
#texinfo_show_urls = 'footnote'
================================================
FILE: docs/index.rst
================================================
.. django-photologue documentation master file, created by
sphinx-quickstart on Mon Sep 3 16:31:44 2012.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
Welcome to django-photologue's documentation!
=============================================
.. include:: ../README.rst
:start-line: 0
:end-line: 25
.. include:: ../README.rst
:start-line: 31
:end-line: 46
Contents:
=========
.. toctree::
:maxdepth: 2
pages/installation
pages/usage
pages/customising/templates
pages/customising/settings
pages/customising/admin
pages/customising/views
pages/customising/models
pages/contributing
pages/changelog_page
Indices and tables
==================
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`
================================================
FILE: docs/make.bat
================================================
@ECHO OFF
REM Command file for Sphinx documentation
if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=sphinx-build
)
set BUILDDIR=_build
set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
set I18NSPHINXOPTS=%SPHINXOPTS% .
if NOT "%PAPER%" == "" (
set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS%
)
if "%1" == "" goto help
if "%1" == "help" (
:help
echo.Please use `make ^<target^>` where ^<target^> is one of
echo. html to make standalone HTML files
echo. dirhtml to make HTML files named index.html in directories
echo. singlehtml to make a single large HTML file
echo. pickle to make pickle files
echo. json to make JSON files
echo. htmlhelp to make HTML files and a HTML help project
echo. qthelp to make HTML files and a qthelp project
echo. devhelp to make HTML files and a Devhelp project
echo. epub to make an epub
echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
echo. text to make text files
echo. man to make manual pages
echo. texinfo to make Texinfo files
echo. gettext to make PO message catalogs
echo. changes to make an overview over all changed/added/deprecated items
echo. linkcheck to check all external links for integrity
echo. doctest to run all doctests embedded in the documentation if enabled
goto end
)
if "%1" == "clean" (
for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
del /q /s %BUILDDIR%\*
goto end
)
if "%1" == "html" (
%SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The HTML pages are in %BUILDDIR%/html.
goto end
)
if "%1" == "dirhtml" (
%SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
goto end
)
if "%1" == "singlehtml" (
%SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
goto end
)
if "%1" == "pickle" (
%SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
if errorlevel 1 exit /b 1
echo.
echo.Build finished; now you can process the pickle files.
goto end
)
if "%1" == "json" (
%SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
if errorlevel 1 exit /b 1
echo.
echo.Build finished; now you can process the JSON files.
goto end
)
if "%1" == "htmlhelp" (
%SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
if errorlevel 1 exit /b 1
echo.
echo.Build finished; now you can run HTML Help Workshop with the ^
.hhp project file in %BUILDDIR%/htmlhelp.
goto end
)
if "%1" == "qthelp" (
%SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
if errorlevel 1 exit /b 1
echo.
echo.Build finished; now you can run "qcollectiongenerator" with the ^
.qhcp project file in %BUILDDIR%/qthelp, like this:
echo.^> qcollectiongenerator %BUILDDIR%\qthelp\django-photologue.qhcp
echo.To view the help file:
echo.^> assistant -collectionFile %BUILDDIR%\qthelp\django-photologue.ghc
goto end
)
if "%1" == "devhelp" (
%SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
if errorlevel 1 exit /b 1
echo.
echo.Build finished.
goto end
)
if "%1" == "epub" (
%SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The epub file is in %BUILDDIR%/epub.
goto end
)
if "%1" == "latex" (
%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
if errorlevel 1 exit /b 1
echo.
echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
goto end
)
if "%1" == "text" (
%SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The text files are in %BUILDDIR%/text.
goto end
)
if "%1" == "man" (
%SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The manual pages are in %BUILDDIR%/man.
goto end
)
if "%1" == "texinfo" (
%SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo.
goto end
)
if "%1" == "gettext" (
%SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The message catalogs are in %BUILDDIR%/locale.
goto end
)
if "%1" == "changes" (
%SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
if errorlevel 1 exit /b 1
echo.
echo.The overview file is in %BUILDDIR%/changes.
goto end
)
if "%1" == "linkcheck" (
%SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
if errorlevel 1 exit /b 1
echo.
echo.Link check complete; look for any errors in the above output ^
or in %BUILDDIR%/linkcheck/output.txt.
goto end
)
if "%1" == "doctest" (
%SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
if errorlevel 1 exit /b 1
echo.
echo.Testing of doctests in the sources finished, look at the ^
results in %BUILDDIR%/doctest/output.txt.
goto end
)
:end
================================================
FILE: docs/pages/changelog_page.rst
================================================
.. We want the CHANGELOG to also appear in the docs but we don't want to break
the DRY principle. So we simply include the CHANGELOG from the base directory.
.. include:: ../../CHANGELOG.txt
================================================
FILE: docs/pages/contributing.rst
================================================
##########################
Contributing to Photologue
##########################
Contributions are always very welcome. Even if you have never contributed to an
open-source project before - please do not hesitate to offer help. Fixes for typos in the
documentation, extra unit tests, etc... are welcome. And look in the issues
list for anything tagged "easy_win".
Example project
---------------
Photologue includes an example project under ``/example_project/`` to get you quickly ready for
contributing to the project - do not hesitate to use it! Please refer to ``/example_project/README.rst``
for installation instructions.
You'll probably also want to manually install
`Sphinx <http://sphinx.pocoo.org/>`_ if you're going to update the documentation.
Workflow
--------
Photologue is hosted on Github, so if you have not already done so, read the excellent
`Github help pages <https://help.github.com/articles/fork-a-repo>`_. We try to keep the workflow
as simple as possible; most pull requests are merged straight into the master branch. Please
ensure your pull requests are on separate branches, and please try to only include one new
feature per pull request!
Features that will take a while to develop might warrant a separate branch in the project;
at present only the ImageKit integration project is run on a separate branch.
Coding style
------------
No surprises here - just try to `follow the conventions used by Django itself
<https://docs.djangoproject.com/en/dev/internals/contributing/writing-code/>`_.
Unit tests
----------
Including unit tests with your contributions will earn you bonus points, maybe even a beer. So write
plenty of tests, and run them from the ``/example_project/`` with a
``python manage.py test photologue``.
There's also a `Tox <https://tox.readthedocs.io/en/latest/index.html>`_ configuration file - so if
you have tox installed, run ``tox`` from the ``/example_project/`` folder, and it will run the entire
test suite against all versions of Python and Django that are supported.
Documentation
-------------
Keeping the documentation up-to-date is very important - so if your code changes
how Photologue works, or adds a new feature, please check that the documentation is still accurate, and
update it if required.
We use `Sphinx <http://sphinx.pocoo.org/>`_ to prepare the documentation; please refer to the excellent docs
on that site for help.
.. note::
The CHANGELOG is part of the documentation, so if your patch needs the
end user to do something - e.g. run a database migration - don't forget to update
it!
Translations
------------
`Photologue manages string translations with Transifex
<https://www.transifex.com/projects/p/django-photologue/>`_. The easiest way to help is
to add new/updated translations there.
Once you've added translations, give the maintainer a wave and he will pull the updated
translations into the master branch, so that you can install Photologue directly from the
Github repository (see :ref:`installing-photologue-label`) and use your translations straight away. Or you can do nothing - just before a release
any new/updated translations get pulled from Transifex and added to the Photologue project.
New features
------------
In the wiki there is a `wishlist of new features already planned
for Photologue <https://github.com/richardbarran/django-photologue/wiki/Photologue-3.X-wishlist>`_ - you are welcome to suggest other useful improvements.
If you’re interested in developing a new feature, it is recommended that you first
discuss it on the `mailing list <http://groups.google.com/group/django-photologue>`_
or open a new ticket in Github, in order to avoid working on a feature that will
not get accepted as it is judged to not fit in with the goals of Photologue.
A bit of history
~~~~~~~~~~~~~~~~
Photologue was started by Justin Driscoll in 2007. He quickly built it into a powerful
photo gallery and image processing application, and it became successful.
Justin then moved onto other projects, and no longer had the time required to maintain
Photologue - there was only one commit between August 2009 and August 2012, and
approximately 70 open tickets on the Google Code project page.
At this point Richard Barran took over as maintainer of the project. First priority
was to improve the infrastructure of the project: moving to Github, adding South,
Sphinx for documentation, Transifex for translations, Travis for continuous integration,
zest.releaser.
The codebase has not changed much so far - and it needs quite a bit of TLC
(Tender Loving Care), and new features are waiting to be added. This is where you step in...
And finally...
--------------
Please remember that the maintainer looks after Photologue in his spare time -
so it might be a few weeks before your pull request gets looked at... and the pull
requests that are nicely formatted, with code, tests and docs included, will
always get reviewed first ;-)
================================================
FILE: docs/pages/customising/admin.rst
================================================
.. _customisation-admin-label:
####################
Customisation: Admin
####################
The Photologue admin can easily be customised to your project's requirements. The technique described on this page
is not specific to Photologue - it can be applied to any 3rd party library.
Create a customisation application
----------------------------------
For clarity, it's best to put our customisation code in a new application; let's call it
``photologue_custom``; create the application and add it to your ``INSTALLED_APPS`` setting.
Changing the admin
------------------
In the new ``photologue_custom`` application, create a new empty ``admin.py`` file. In this file we
can replace the admin configuration supplied by Photologue, with a configuration specific to your project.
For example:
.. code-block:: python
from django import forms
from django.contrib import admin
from photologue.admin import GalleryAdmin as GalleryAdminDefault
from photologue.models import Gallery
class GalleryAdminForm(forms.ModelForm):
"""Users never need to enter a description on a gallery."""
class Meta:
model = Gallery
exclude = ['description']
class GalleryAdmin(GalleryAdminDefault):
form = GalleryAdminForm
admin.site.unregister(Gallery)
admin.site.register(Gallery, GalleryAdmin)
This snippet will define a new Gallery admin class based on Photologue's own. The only change we make
is to exclude the ``description`` field from the change form.
We then unregister the default admin for the Gallery model and replace it with our new class.
Possible uses
-------------
The technique outlined above can be used to make many changes to the admin; here are a couple of suggestions.
Custom rich text editors
~~~~~~~~~~~~~~~~~~~~~~~~
The description field on the Gallery model (and the caption field on the Photo model) are plain text fields.
With the above technique, it's easy to use a rich text editor to manage these fields in the admin. For example,
if you have `django-ckeditor <https://github.com/shaunsephton/django-ckeditor>`_ installed:
.. code-block:: python
from django import forms
from django.contrib import admin
from ckeditor.widgets import CKEditorWidget
from photologue.admin import GalleryAdmin as GalleryAdminDefault
from photologue.models import Gallery
class GalleryAdminForm(forms.ModelForm):
"""Replace the default description field, with one that uses a custom widget."""
description = forms.CharField(widget=CKEditorWidget())
class Meta:
model = Gallery
exclude = ['']
class GalleryAdmin(GalleryAdminDefault):
form = GalleryAdminForm
admin.site.unregister(Gallery)
admin.site.register(Gallery, GalleryAdmin)
================================================
FILE: docs/pages/customising/models.rst
================================================
.. _customising-models-label:
#####################
Customisation: Models
#####################
The photologue models can be extended to better suit your project. The technique described on this page
is not specific to Photologue - it can be applied to any 3rd party library.
The models within Photologue cannot be directly modified (unlike, for example, Django's own User model).
There are a number of reasons behind this decision, including:
- If code within a project modifies directly the Photologue models' fields, it leaves the Photologue
schema migrations in an ambiguous state.
- Likewise, model methods can no longer be trusted to behave as intended (as fields on which they
depend may have been overridden).
However, it's easy to create new models linked by one-to-one relationships to Photologue's own
``Gallery`` and ``Photo`` models.
On this page we will show how you can add tags to the ``Gallery`` model. For this we will use
the popular 3rd party application `django-taggit <https://github.com/alex/django-taggit>`_.
.. note::
The ``Gallery`` and ``Photo`` models currently have tag fields, however these are based on the
abandonware `django-tagging <https://github.com/brosner/django-tagging>`_ application. Instead,
tagging is being entirely removed from Photologue, as it is a non-core functionality of a
gallery application, and is easy to add back in - as this page shows!
Create a customisation application
----------------------------------
For clarity, it's best to put our customisation code in a new application; let's call it
``photologue_custom``; create the application and add it to your ``INSTALLED_APPS`` setting.
Extending
---------
Within the ``photologue_custom`` application, we will edit 2 files:
Models.py
~~~~~~~~~
.. code-block:: python
from django.db import models
from taggit.managers import TaggableManager
from photologue.models import Gallery
class GalleryExtended(models.Model):
# Link back to Photologue's Gallery model.
gallery = models.OneToOneField(Gallery, related_name='extended')
# This is the important bit - where we add in the tags.
tags = TaggableManager(blank=True)
# Boilerplate code to make a prettier display in the admin interface.
class Meta:
verbose_name = u'Extra fields'
verbose_name_plural = u'Extra fields'
def __str__(self):
return self.gallery.title
Admin.py
~~~~~~~~
.. code-block:: python
from django.contrib import admin
from photologue.admin import GalleryAdmin as GalleryAdminDefault
from photologue.models import Gallery
from .models import GalleryExtended
class GalleryExtendedInline(admin.StackedInline):
model = GalleryExtended
can_delete = False
class GalleryAdmin(GalleryAdminDefault):
"""Define our new one-to-one model as an inline of Photologue's Gallery model."""
inlines = [GalleryExtendedInline, ]
admin.site.unregister(Gallery)
admin.site.register(Gallery, GalleryAdmin)
The above code is enough to start entering tags in the admin interface. To use/display them in the front
end, you will also need to override Photologue's own templates - as the templates are likely to be
heavily customised for your specific project, an example is not included here.
================================================
FILE: docs/pages/customising/settings.rst
================================================
#######################
Customisation: Settings
#######################
Photologue has several settings to customise behaviour.
PHOTOLOGUE_GALLERY_LATEST_LIMIT
-------------------------------
Default: ``None``
Default limit for gallery.latest
PHOTOLOGUE_GALLERY_SAMPLE_SIZE
------------------------------
Default: ``5``
Number of random images from the gallery to display.
PHOTOLOGUE_IMAGE_FIELD_MAX_LENGTH
---------------------------------
Default: ``100``
max_length setting for the ImageModel ImageField
PHOTOLOGUE_SAMPLE_IMAGE_PATH
----------------------------
Default: ``os.path.join(os.path.dirname(__file__), 'res', 'sample.jpg'))``
Path to sample image
PHOTOLOGUE_MAXBLOCK
-------------------
Default: ``256 * 2 ** 10``
Modify image file buffer size.
PHOTOLOGUE_DIR
--------------
Default: ``'photologue'``
The relative path from your ``MEDIA_ROOT`` setting where Photologue will save image files. If your ``MEDIA_ROOT`` is set to "/home/user/media", photologue will upload your images to "/home/user/media/photologue"
PHOTOLOGUE_PATH
---------------
Default: ``None``
Look for user function to define file paths. Specifies a "callable" that takes a model instance and the original uploaded filename and returns a relative path from your ``MEDIA_ROOT`` that the file will be saved. This function can be set directly.
For example you could use the following code in a util module::
# myapp/utils.py:
import os
def get_image_path(instance, filename):
return os.path.join('path', 'to', 'my', 'files', filename)
Then set in settings::
# settings.py:
from utils import get_image_path
PHOTOLOGUE_PATH = get_image_path
Or instead, pass a string path::
# settings.py:
PHOTOLOGUE_PATH = 'myapp.utils.get_image_path'
.. _settings-photologue-multisite-label:
PHOTOLOGUE_MULTISITE
--------------------
Default: ``False``
Photologue can integrate galleries and photos with `Django's site framework`_.
The default is for this feature to be switched off, and new galleries and photos to be automatically
linked to the current site (``SITE_ID = 1``). The Sites many-to-many field is hidden is the admin, as there is no
need for a user to see it.
If the setting is ``True``, the admin interface is slightly changed:
* The Sites many-to-many field is displayed on Gallery and Photos models.
* The Gallery Upload allows you to associate one more sites to the uploaded photos (and gallery).
.. note:: Gallery Uploads (zip archives) are always associated with the current site. Pull requests to
fix this would be welcome!
.. _Django's site framework: http://django.readthedocs.org/en/latest/ref/contrib/sites.html
================================================
FILE: docs/pages/customising/templates.rst
================================================
##################################
Customisation: extending templates
##################################
Photologue comes with a set of basic templates to get you started quickly - you
can of course replace them with your own. That said, it is possible to extend the basic templates in
your own project and override various blocks, for example to add css classes.
Often this will be enough.
The trick to extending the templates is not special to Photologue, it's used
in other projects such as `Oscar <http://django-oscar.readthedocs.io/en/latest/howto/how_to_customise_templates.html>`_.
First, set up your template configuration as so:
.. code-block:: python
# for Django versions < 1.8
TEMPLATE_LOADERS = (
'django.template.loaders.filesystem.Loader',
'django.template.loaders.app_directories.Loader',
)
from photologue import PHOTOLOGUE_APP_DIR
TEMPLATE_DIRS = (
...other template folders...,
PHOTOLOGUE_APP_DIR
)
# for Django versions >= 1.8
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates')],
# note: if you set APP_DIRS to True, you won't need to add 'loaders' under OPTIONS
# proceeding as if APP_DIRS is False
'APP_DIRS': False,
'OPTIONS': {
'context_processors': [
... context processors ...,
],
# start - please add only if APP_DIRS is False
'loaders': [
'django.template.loaders.filesystem.Loader',
'django.template.loaders.app_directories.Loader',
],
# end - please add only if APP_DIRS is False
},
},
]
The ``PHOTOLOGUE_APP_DIR`` points to the directory above Photologue's normal
templates directory. This means that ``path/to/photologue/template.html`` can also
be reached via ``templates/path/to/photologue/template.html``.
For example, to customise ``photologue/gallery_list.html``, you can have an implementation like:
.. code-block:: html+django
# Create your own photologue/gallery_list.html
{% extends "templates/photologue/gallery_list.html" %}
... we are now extending the built-in gallery_list.html and we can override
the content blocks that we want to customise ...
================================================
FILE: docs/pages/customising/views.rst
================================================
.. _customisation-views-label:
#############################
Customisation: Views and Urls
#############################
The photologue views and urls can be tweaked to better suit your project. The technique described on this page
is not specific to Photologue - it can be applied to any 3rd party library.
Create a customisation application
----------------------------------
For clarity, it's best to put our customisation code in a new application; let's call it
``photologue_custom``; create the application and add it to your ``INSTALLED_APPS`` setting.
We will also want to customise urls:
1. Create a urls.py that will contain our customised urls:
.. code-block:: python
from django.conf.urls import *
urlpatterns = [
# Nothing to see here... for now.
]
2. These custom urls will override the main Photologue urls, so place them just before Photologue
in the project's main urls.py file:
.. code-block:: python
... other code
(r'^photologue/', include('photologue_custom.urls')),
url(r'^photologue/', include('photologue.urls', namespace='photologue')),
... other code
Now we're ready to make some changes.
Changing pagination from our new urls.py
----------------------------------------
The list pages of Photologue (both Gallery and Photo) display 20 objects per page. Let's change this value.
Edit our new urls.py file, and add:
.. code-block:: python
from django.conf.urls import *
from photologue.views import GalleryListView
urlpatterns = [
url(r'^gallerylist/$',
GalleryListView.as_view(paginate_by=5),
name='photologue_custom-gallery-list'),
]
We've copied the urlpattern for
`the gallery list view from Photologue itself <https://github.com/richardbarran/django-photologue/blob/master/photologue/urls.py>`_,
and changed it slightly by passing in ``paginate_by=5``.
And that's it - now when that page is requested, our customised urls.py will be called first, with pagination
set to 5 items.
Values that can be overridden from urls.py
------------------------------------------
GalleryListView
~~~~~~~~~~~~~~~
* paginate_by: number of items to display per page.
PhotoListView
~~~~~~~~~~~~~
* paginate_by: number of items to display per page.
Changing views.py to create a RESTful api
-----------------------------------------
More substantial customisation can be carried out by writing custom views. For example,
it's easy to change a Photologue view to return JSON objects rather than html webpages. For this
quick demo, we'll use the
`django-braces library <http://django-braces.readthedocs.org/en/latest/index.html>`_
to override the view returning a list of all photos.
Add the following code to views.py in ``photologue_custom``:
.. code-block:: python
from photologue.views import PhotoListView
from braces.views import JSONResponseMixin
class PhotoJSONListView(JSONResponseMixin, PhotoListView):
def render_to_response(self, context, **response_kwargs):
return self.render_json_object_response(context['object_list'],
**response_kwargs)
And call this new view from urls.py; here we are replacing the standard Photo list page provided by Photologue:
.. code-block:: python
from .views import PhotoJSONListView
urlpatterns = [
# Other urls...
url(r'^photolist/$',
PhotoJSONListView.as_view(),
name='photologue_custom-photo-json-list'),
# Other urls as required...
]
And that's it! Of course, this is simply a demo and a real RESTful api would be rather more complex.
================================================
FILE: docs/pages/installation.rst
================================================
############################
Installation & configuration
############################
.. _installing-photologue-label:
Installation
------------
The easiest way to install Photologue is with `pip <https://pip.pypa.io/en/latest/>`_; this will give you the latest
version available on `PyPi <https://pypi.python.org/pypi>`_::
pip install django-photologue
You can also take risks and install the latest code directly from the
Github repository::
pip install -e git+https://github.com/richardbarran/django-photologue.git#egg=django-photologue
This code should work ok - like `Django <https://www.djangoproject.com/>`_
itself, we try to keep the master branch bug-free. However, we strongly recommend that you
stick with a release from the PyPi repository, unless if you're confident in your abilities
to fix any potential bugs on your own!
Python 3
~~~~~~~~
Photologue is compatible with Python 3 (3.3 or later).
Dependencies
------------
3 apps that will be installed automatically if required.
* `Django <https://www.djangoproject.com/>`_.
* `Pillow <https://pillow.readthedocs.io/>`_.
* `Django-sortedm2m <https://pypi.python.org/pypi/django-sortedm2m>`_.
And 1 dependency that you will have to manage yourself:
* `Pytz <https://pypi.python.org/pypi/pytz>`_. See the Django release notes `for more information
<https://docs.djangoproject.com/en/1.6/releases/1.6/#time-zone-aware-day-month-and-week-day-lookups>`_.
.. note::
Photologue tries to support the same Django version as are supported by the Django
project itself.
That troublesome Pillow...
~~~~~~~~~~~~~~~~~~~~~~~~~~
Pillow can be tricky to install; sometimes it will install smoothly
out of the box, sometimes you can spend hours figuring it out - installation
issues vary from platform to platform, and from one OS release to the next, so listing
them all here would not be realistic. Google
is your friend!
#. Pillow is a fork of PIL; you should not have installed both - this can cause strange bugs.
#. Sometimes Pillow will install... but is not actually installed. This 'undocumented feature' has been
reported by a user on Windows. If you can't get Photologue to display any images, check
that you can actually import Pillow::
$ python manage.py shell
Python 3.3.1 (default, Sep 25 2013, 19:29:01)
[GCC 4.7.3] on linux
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from PIL import Image
>>>
Configure Your Django Settings file
-----------------------------------
Follow these 4 steps:
#. Add to your ``INSTALLED_APPS`` setting::
INSTALLED_APPS = (
# ...other installed applications...
'photologue',
'sortedm2m',
)
#. Confirm that your `MEDIA_ROOT <https://docs.djangoproject.com/en/dev/ref/settings/#media-root>`_ and
`MEDIA_URL <https://docs.djangoproject.com/en/dev/ref/settings/#std:setting-MEDIA_URL>`_ settings
are correct (Photologue will store uploaded files in a folder called 'photologue' under your ``MEDIA_ROOT``).
#. `Enable the admin app <https://docs.djangoproject.com/en/dev/ref/contrib/admin/>`_ if you have not already done so.
#. `Enable the Django sites framework <https://docs.djangoproject.com/en/dev/ref/contrib/sites/#enabling-the-sites-framework>`_
if you have not already done so. This is not enabled by default in Django, but is required by Photologue.
Add the urls
------------
Add photologue to your projects urls.py file::
urlpatterns += [
...
url(r'^photologue/', include('photologue.urls', namespace='photologue')),
]
Sync Your Database
------------------
You can now sync your database::
python manage.py migrate photologue
If you are installing Photologue for the first time, this will set up some
default PhotoSizes to get you started - you are free to change them of course!
Instant templates
-----------------
Photologue comes with basic templates for galleries and photos, which are designed
to work well with `Twitter-Bootstrap <http://twitter.github.io/bootstrap/index.html>`_.
You can of course use them, or override them, or completely replace them. Note that all
Photologue templates inherit from ``photologue/root.html``, which itself expects your site's
base template to be called ``base.html`` - you can change this to use a different base template.
Sitemap
-------
.. automodule:: photologue.sitemaps
Sites
-----
Photologue supports `Django's site framework`_ since version 2.8. That means
that each Gallery and each Photo can be displayed on one or more sites.
Please bear in mind that photos don't necessarily have to be assigned to the
same sites as the gallery they're belonging to: each gallery will only display
the photos that are on its site. When a gallery does not belong the current site
but a single photo is, that photo is only accessible directly as the gallery
won't be shown in the index.
.. note:: If you're upgrading from a version earlier than 2.8 you don't need to
worry about the assignment of already existing objects to a site because a
datamigration will assign all your objects to the current site automatically.
.. note:: This feature is switched off by default. :ref:`See here to enable it
<settings-photologue-multisite-label>` and for more information.
.. _Django's site framework: http://django.readthedocs.org/en/latest/ref/contrib/sites.html
Amazon S3
---------
Photologue can use a custom file storage system, for example
`Amazon's S3 <http://aws.amazon.com/s3/>`_.
You will need to configure your Django project to use Amazon S3 for storing files; a full discussion of
how to do this is outside the scope of this page.
However, there is a quick demo of using Photologue with S3 in the ``example_project`` directory; if you look
at these files:
* ``example_project/example_project/settings.py``
* ``example_project/requirements.txt``
At the end of each file you will commented-out lines for configuring S3 functionality. These point to extra files
stored under ``example_project/example_storages/``. Uncomment these lines, run the example
project, then study these files for inspiration! After that, setting up S3 will consist of
(at minimum) the following steps:
#. Signup for Amazon AWS S3 at http://aws.amazon.com/s3/.
#. Create a Bucket on S3 to store your media and static files.
#. Set the environment variables:
* ``AWS_ACCESS_KEY_ID`` - issued to your account by S3.
* ``AWS_SECRET_ACCESS_KEY`` - issued to your account by S3.
* ``AWS_STORAGE_BUCKET_NAME`` - name of your bucket on S3.
#. To copy your static files into your S3 Bucket, type ``python manage.py collectstatic`` in the ``example_project`` directory.
.. note:: This simple setup does not handle S3 regions.
================================================
FILE: docs/pages/usage.rst
================================================
#####
Usage
#####
Now that you've installed Photologue, here are a few suggestions on how to use it:
Upload some photos in the admin
-------------------------------
The ``Photo`` model in the admin allows you to add new photos to Photologue. You can add photos one by one - and
it the top-right corner there is a 'Upload a Zip archive' button that will allow you to upload many photos at once.
Define some Photosizes
----------------------
Photologue will create thumbnails of the photos that you upload, and the thumbnails are what is displayed in the
public website. By default Photologue comes with a few Photosizes to get you started - feel free to tweak them, or
to create new ones.
Just note that the ``admin_thumbnail`` size is used by the admin pages, so it's not a good idea to delete it!
Built-in pages and templates
----------------------------
If you've followed all the instructions in the installation page, you will have included Photologue's
urls at ``/photologue/`` - you can use these, tweak them, or discard them if they do not fit in with your website's
requirements.
Custom usage
------------
The base of Photologue is the ``Photo`` model. When an instance is created, we automatically add methods to retrieve
photos at various photosizes. E.g. if you have an instance of ``Photo`` called ``photo``, then the
following methods will have been added automatically::
photo.get_thumbnail_url()
photo.get_display_url()
photo.get_admin_thumbnail_url()
These can be used in a custom template to display a thumbnail, e.g.::
<a href="{{ photo.image.url }}">
<img src="{{ photo.get_display_url }}" alt="{{ photo.title }}">
</a>
This will display an image, sized to the dimensions specified in the Photosize ``display``,
and provide a clickable link to the raw image. Please refer to the example templates for ideas on how to use
``Photo`` and ``Gallery`` instances!
Data integrity
--------------
Photologue will store 'as-is' any data stored for galleries and photos.
You may want to enforce some data integrity rules - e.g. to sanitise
any javascript injected into a ``Photo`` ``caption`` field. An easy way to do this
would be to add extra processing on a ``post-save`` signal.
Photologue does not sanitise data itself as you may legitimately want to store html and
javascript in a caption field e.g. if you use a rich-text editor.
================================================
FILE: docs/requirements.txt
================================================
django
-r ../requirements.txt
================================================
FILE: example_project/README.rst
================================================
#######################
Photologue Demo Project
#######################
About
=====
This project serves 3 purposes:
- It's a quick demo of django-photologue for people who wish to try it out.
- It's an easy way for contributors to the project to have both django-photologue,
and a project that uses it.
- It's used for Travis CI testing of django-photologue.
It uses the Bootstrap-friendly templates that come with Photologue.
The rest of the README will assume that you want to set up the test project in
order to work on django-photologue itself.
Prerequisites
=============
- python 3.
- virtualenvwrapper makes it easy to manage your virtualenvs. Strongly recommended!
Installation
============
**Note**: the project is configured so that it can run immediately with zero configuration
(especially of settings files).
Create a virtual python environment for the project. The use of virtualenvwrapper
is strongly recommended::
mkproject --no-site-packages django-photologue
or for more sophisticated setups:
mkvirtualenv --no-site-packages django-photologue
Clone this code into your project folder::
(cd to the new virtualenv)
git clone https://github.com/richardbarran/django-photologue.git .
**Note**: if you plan to contribute code back to django-photologue, then you'll
probably want instead to fork the project on Github, and clone your fork instead.
Install requirements::
cd example_project
pip install -r requirements.txt
**Note**: this will install Pillow, which is not always straightforward; sometimes it
will install smoothly out of the box, sometimes you can spend hours figuring it out - installation
issues vary from platform to platform, and from one OS release to the next. Google
is your friend here, and it's worth noting that Pillow is a fork of PIL,
so googling 'PIL installation <your platform>' can also help.
The project is set up to run SQLite in dev so that it can be quickly started
with no configuration required (you can of course specify another database in
the settings file). To setup the database::
./manage.py migrate
Follow the instructions to configure photologue here: `Photologue Docs <http://django-photologue.readthedocs.org/en/latest/pages/installation.html>`_
And finally run the project (it defaults to a safe set of settings for a dev
environment)::
./manage.py runserver
Open browser to http://127.0.0.1:8000
Thank you
=========
This example project is based on the earlier `photologue_demo project <https://github.com/richardbarran/photologue_demo>`_.
This project included contributions and input from: crainbf, tomkingston, bmcorser.
..
Note: this README is formatted as reStructuredText so that it's in the same
format as the Sphinx docs.
================================================
FILE: example_project/example_project/__init__.py
================================================
================================================
FILE: example_project/example_project/example_storages/README.txt
================================================
In this folder we keep configuration files for non-default media stores, e.g. Amazon S3.
================================================
FILE: example_project/example_project/example_storages/__init__.py
================================================
================================================
FILE: example_project/example_project/example_storages/s3_requirements.txt
================================================
boto>=2.29.1
django-storages>=1.1.8
================================================
FILE: example_project/example_project/example_storages/s3utils.py
================================================
from storages.backends.s3boto import S3BotoStorage
StaticS3BotoStorage = lambda: S3BotoStorage(location='static')
MediaS3BotoStorage = lambda: S3BotoStorage(location='media')
================================================
FILE: example_project/example_project/example_storages/settings_s3boto.py
================================================
# S3Boto storage settings for photologue example project.
import os
DEFAULT_FILE_STORAGE = 'example_project.example_storages.s3utils.MediaS3BotoStorage'
STATICFILES_STORAGE = 'example_project.example_storages.s3utils.StaticS3BotoStorage'
try:
# If you want to test the example_project with S3, you'll have to configure the
# environment variables as specified below.
# (Secret keys are stored in environment variables for security - you don't want to
# accidentally commit and push them to a public repository).
AWS_ACCESS_KEY_ID = os.environ['AWS_ACCESS_KEY_ID']
AWS_SECRET_ACCESS_KEY = os.environ['AWS_SECRET_ACCESS_KEY']
AWS_STORAGE_BUCKET_NAME = os.environ['AWS_STORAGE_BUCKET_NAME']
except KeyError:
raise KeyError('Need to define AWS environment variables: '
'AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, and AWS_STORAGE_BUCKET_NAME')
# Default Django Storage API behavior - don't overwrite files with same name
AWS_S3_FILE_OVERWRITE = False
MEDIA_ROOT = '/media/'
MEDIA_URL = 'http://%s.s3.amazonaws.com/media/' % AWS_STORAGE_BUCKET_NAME
STATIC_ROOT = '/static/'
STATIC_URL = 'http://%s.s3.amazonaws.com/static/' % AWS_STORAGE_BUCKET_NAME
ADMIN_MEDIA_PREFIX = STATIC_URL + 'admin/'
================================================
FILE: example_project/example_project/fixtures/.gitdirectory
================================================
Placeholder so that this directory will be added to the git repository.
================================================
FILE: example_project/example_project/settings.py
================================================
# Global settings for photologue example project.
import os
import sys
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
SECRET_KEY = '=_v6sfp8u2uuhdncdz9t1_nu8(#8q4=40$f$4rorj4q3)f-nlc'
DEBUG = True
ALLOWED_HOSTS = ['*']
INSTALLED_APPS = (
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.sites',
'django.contrib.messages',
'django.contrib.staticfiles',
# Note: added sitemaps to the INSTALLED_APPS just so that unit tests run,
# but not actually added a sitemap in urls.py.
'django.contrib.sitemaps',
'photologue',
'sortedm2m',
'example_project',
)
MIDDLEWARE = (
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
)
ROOT_URLCONF = 'example_project.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'example_project/templates'), ],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
'django.template.context_processors.i18n',
'django.template.context_processors.media',
'django.template.context_processors.static',
],
'debug': True,
},
},
]
WSGI_APPLICATION = 'example_project.wsgi.application'
# Database
# https://docs.djangoproject.com/en/1.8/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}
# Internationalization
# https://docs.djangoproject.com/en/1.8/topics/i18n/
LANGUAGE_CODE = 'en-gb'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.8/howto/static-files/
STATIC_ROOT = os.path.join(BASE_DIR, 'public', 'static')
STATIC_URL = '/static/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'public', 'media')
MEDIA_URL = '/media/'
STATICFILES_DIRS = (
os.path.join(BASE_DIR, 'example_project/static'),
)
SITE_ID = 1
# LOGGING CONFIGURATION
# A logging configuration that writes log messages to the console.
LOGGING = {
'version': 1,
'disable_existing_loggers': True,
# Formatting of messages.
'formatters': {
# Don't need to show the time when logging to console.
'console': {
'format': '%(levelname)s %(name)s.%(funcName)s (%(lineno)d) %(message)s'
}
},
# The handlers decide what we should do with a logging message - do we email
# it, ditch it, or write it to a file?
'handlers': {
# Writing to console. Use only in dev.
'console': {
'level': 'DEBUG',
'class': 'logging.StreamHandler',
'formatter': 'console'
},
# Send logs to /dev/null.
'null': {
'level': 'DEBUG',
'class': 'logging.NullHandler',
},
},
# Loggers decide what is logged.
'loggers': {
'': {
# Default (suitable for dev) is to log to console.
'handlers': ['console'],
'level': 'INFO',
'propagate': False,
},
'photologue': {
# Default (suitable for dev) is to log to console.
'handlers': ['console'],
'level': 'DEBUG',
'propagate': False,
},
# logging of SQL statements. Default is to ditch them (send them to
# null). Note that this logger only works if DEBUG = True.
'django.db.backends': {
'handlers': ['null'],
'level': 'DEBUG',
'propagate': False,
},
}
}
# Don't display logging messages to console during unit test runs.
if len(sys.argv) > 1 and sys.argv[1] == 'test':
LOGGING['loggers']['']['handlers'] = ['null']
LOGGING['loggers']['photologue']['handlers'] = ['null']
# Uncomment this for Amazon S3 file storage
# from example_storages.settings_s3boto import *
================================================
FILE: example_project/example_project/static/css/styles.css
================================================
/* Some customisation for the Photologue demo site. */
/* Page structure */
h1 {
margin-top: 1rem;
margin-bottom: 2rem;
border-bottom: 1px solid lightgray;
padding-bottom: 0.25rem;
}
/* Photo galleries */
.gallery-sample {
margin-bottom: 2rem;
}
a.btn {
margin-top: 2rem;
}
td {
width: 100px;
}
.gallery-sample a {
text-decoration: none;
}
.gallery-list a {
text-decoration: none;
}
.photo-list a {
text-decoration: none;
}
ul.pagination {
margin-top: 2rem;
}
.img-thumbnail {
margin-bottom: 0.25rem;
}
================================================
FILE: example_project/example_project/static/js/jquery-3.5.1.js
================================================
/*!
* jQuery JavaScript Library v3.5.1
* https://jquery.com/
*
* Includes Sizzle.js
* https://sizzlejs.com/
*
* Copyright JS Foundation and other contributors
* Released under the MIT license
* https://jquery.org/license
*
* Date: 2020-05-04T22:49Z
*/
( function( global, factory ) {
"use strict";
if ( typeof module === "object" && typeof module.exports === "object" ) {
// For CommonJS and CommonJS-like environments where a proper `window`
// is present, execute the factory and get jQuery.
// For environments that do not have a `window` with a `document`
// (such as Node.js), expose a factory as module.exports.
// This accentuates the need for the creation of a real `window`.
// e.g. var jQuery = require("jquery")(window);
// See ticket #14549 for more info.
module.exports = global.document ?
factory( global, true ) :
function( w ) {
if ( !w.document ) {
throw new Error( "jQuery requires a window with a document" );
}
return factory( w );
};
} else {
factory( global );
}
// Pass this if window is not defined yet
} )( typeof window !== "undefined" ? window : this, function( window, noGlobal ) {
// Edge <= 12 - 13+, Firefox <=18 - 45+, IE 10 - 11, Safari 5.1 - 9+, iOS 6 - 9.1
// throw exceptions when non-strict code (e.g., ASP.NET 4.5) accesses strict mode
// arguments.callee.caller (trac-13335). But as of jQuery 3.0 (2016), strict mode should be common
// enough that all such attempts are guarded in a try block.
"use strict";
var arr = [];
var getProto = Object.getPrototypeOf;
var slice = arr.slice;
var flat = arr.flat ? function( array ) {
return arr.flat.call( array );
} : function( array ) {
return arr.concat.apply( [], array );
};
var push = arr.push;
var indexOf = arr.indexOf;
var class2type = {};
var toString = class2type.toString;
var hasOwn = class2type.hasOwnProperty;
var fnToString = hasOwn.toString;
var ObjectFunctionString = fnToString.call( Object );
var support = {};
var isFunction = function isFunction( obj ) {
// Support: Chrome <=57, Firefox <=52
// In some browsers, typeof returns "function" for HTML <object> elements
// (i.e., `typeof document.createElement( "object" ) === "function"`).
// We don't want to classify *any* DOM node as a function.
return typeof obj === "function" && typeof obj.nodeType !== "number";
};
var isWindow = function isWindow( obj ) {
return obj != null && obj === obj.window;
};
var document = window.document;
var preservedScriptAttributes = {
type: true,
src: true,
nonce: true,
noModule: true
};
function DOMEval( code, node, doc ) {
doc = doc || document;
var i, val,
script = doc.createElement( "script" );
script.text = code;
if ( node ) {
for ( i in preservedScriptAttributes ) {
// Support: Firefox 64+, Edge 18+
// Some browsers don't support the "nonce" property on scripts.
// On the other hand, just using `getAttribute` is not enough as
// the `nonce` attribute is reset to an empty string whenever it
// becomes browsing-context connected.
// See https://github.com/whatwg/html/issues/2369
// See https://html.spec.whatwg.org/#nonce-attributes
// The `node.getAttribute` check was added for the sake of
// `jQuery.globalEval` so that it can fake a nonce-containing node
// via an object.
val = node[ i ] || node.getAttribute && node.getAttribute( i );
if ( val ) {
script.setAttribute( i, val );
}
}
}
doc.head.appendChild( script ).parentNode.removeChild( script );
}
function toType( obj ) {
if ( obj == null ) {
return obj + "";
}
// Support: Android <=2.3 only (functionish RegExp)
return typeof obj === "object" || typeof obj === "function" ?
class2type[ toString.call( obj ) ] || "object" :
typeof obj;
}
/* global Symbol */
// Defining this global in .eslintrc.json would create a danger of using the global
// unguarded in another place, it seems safer to define global only for this module
var
version = "3.5.1",
// Define a local copy of jQuery
jQuery = function( selector, context ) {
// The jQuery object is actually just the init constructor 'enhanced'
// Need init if jQuery is called (just allow error to be thrown if not included)
return new jQuery.fn.init( selector, context );
};
jQuery.fn = jQuery.prototype = {
// The current version of jQuery being used
jquery: version,
constructor: jQuery,
// The default length of a jQuery object is 0
length: 0,
toArray: function() {
return slice.call( this );
},
// Get the Nth element in the matched element set OR
// Get the whole matched element set as a clean array
get: function( num ) {
// Return all the elements in a clean array
if ( num == null ) {
return slice.call( this );
}
// Return just the one element from the set
return num < 0 ? this[ num + this.length ] : this[ num ];
},
// Take an array of elements and push it onto the stack
// (returning the new matched element set)
pushStack: function( elems ) {
// Build a new jQuery matched element set
var ret = jQuery.merge( this.constructor(), elems );
// Add the old object onto the stack (as a reference)
ret.prevObject = this;
// Return the newly-formed element set
return ret;
},
// Execute a callback for every element in the matched set.
each: function( callback ) {
return jQuery.each( this, callback );
},
map: function( callback ) {
return this.pushStack( jQuery.map( this, function( elem, i ) {
return callback.call( elem, i, elem );
} ) );
},
slice: function() {
return this.pushStack( slice.apply( this, arguments ) );
},
first: function() {
return this.eq( 0 );
},
last: function() {
return this.eq( -1 );
},
even: function() {
return this.pushStack( jQuery.grep( this, function( _elem, i ) {
return ( i + 1 ) % 2;
} ) );
},
odd: function() {
return this.pushStack( jQuery.grep( this, function( _elem, i ) {
return i % 2;
} ) );
},
eq: function( i ) {
var len = this.length,
j = +i + ( i < 0 ? len : 0 );
return this.pushStack( j >= 0 && j < len ? [ this[ j ] ] : [] );
},
end: function() {
return this.prevObject || this.constructor();
},
// For internal use only.
// Behaves like an Array's method, not like a jQuery method.
push: push,
sort: arr.sort,
splice: arr.splice
};
jQuery.extend = jQuery.fn.extend = function() {
var options, name, src, copy, copyIsArray, clone,
target = arguments[ 0 ] || {},
i = 1,
length = arguments.length,
deep = false;
// Handle a deep copy situation
if ( typeof target === "boolean" ) {
deep = target;
// Skip the boolean and the target
target = arguments[ i ] || {};
i++;
}
// Handle case when target is a string or something (possible in deep copy)
if ( typeof target !== "object" && !isFunction( target ) ) {
target = {};
}
// Extend jQuery itself if only one argument is passed
if ( i === length ) {
target = this;
i--;
}
for ( ; i < length; i++ ) {
// Only deal with non-null/undefined values
if ( ( options = arguments[ i ] ) != null ) {
// Extend the base object
for ( name in options ) {
copy = options[ name ];
// Prevent Object.prototype pollution
// Prevent never-ending loop
if ( name === "__proto__" || target === copy ) {
continue;
}
// Recurse if we're merging plain objects or arrays
if ( deep && copy && ( jQuery.isPlainObject( copy ) ||
( copyIsArray = Array.isArray( copy ) ) ) ) {
src = target[ name ];
// Ensure proper type for the source value
if ( copyIsArray && !Array.isArray( src ) ) {
clone = [];
} else if ( !copyIsArray && !jQuery.isPlainObject( src ) ) {
clone = {};
} else {
clone = src;
}
copyIsArray = false;
// Never move original objects, clone them
target[ name ] = jQuery.extend( deep, clone, copy );
// Don't bring in undefined values
} else if ( copy !== undefined ) {
target[ name ] = copy;
}
}
}
}
// Return the modified object
return target;
};
jQuery.extend( {
// Unique for each copy of jQuery on the page
expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ),
// Assume jQuery is ready without the ready module
isReady: true,
error: function( msg ) {
throw new Error( msg );
},
noop: function() {},
isPlainObject: function( obj ) {
var proto, Ctor;
// Detect obvious negatives
// Use toString instead of jQuery.type to catch host objects
if ( !obj || toString.call( obj ) !== "[object Object]" ) {
return false;
}
proto = getProto( obj );
// Objects with no prototype (e.g., `Object.create( null )`) are plain
if ( !proto ) {
return true;
}
// Objects with prototype are plain iff they were constructed by a global Object function
Ctor = hasOwn.call( proto, "constructor" ) && proto.constructor;
return typeof Ctor === "function" && fnToString.call( Ctor ) === ObjectFunctionString;
},
isEmptyObject: function( obj ) {
var name;
for ( name in obj ) {
return false;
}
return true;
},
// Evaluates a script in a provided context; falls back to the global one
// if not specified.
globalEval: function( code, options, doc ) {
DOMEval( code, { nonce: options && options.nonce }, doc );
},
each: function( obj, callback ) {
var length, i = 0;
if ( isArrayLike( obj ) ) {
length = obj.length;
for ( ; i < length; i++ ) {
if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) {
break;
}
}
} else {
for ( i in obj ) {
if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) {
break;
}
}
}
return obj;
},
// results is for internal usage only
makeArray: function( arr, results ) {
var ret = results || [];
if ( arr != null ) {
if ( isArrayLike( Object( arr ) ) ) {
jQuery.merge( ret,
typeof arr === "string" ?
[ arr ] : arr
);
} else {
push.call( ret, arr );
}
}
return ret;
},
inArray: function( elem, arr, i ) {
return arr == null ? -1 : indexOf.call( arr, elem, i );
},
// Support: Android <=4.0 only, PhantomJS 1 only
// push.apply(_, arraylike) throws on ancient WebKit
merge: function( first, second ) {
var len = +second.length,
j = 0,
i = first.length;
for ( ; j < len; j++ ) {
first[ i++ ] = second[ j ];
}
first.length = i;
return first;
},
grep: function( elems, callback, invert ) {
var callbackInverse,
matches = [],
i = 0,
length = elems.length,
callbackExpect = !invert;
// Go through the array, only saving the items
// that pass the validator function
for ( ; i < length; i++ ) {
callbackInverse = !callback( elems[ i ], i );
if ( callbackInverse !== callbackExpect ) {
matches.push( elems[ i ] );
}
}
return matches;
},
// arg is for internal usage only
map: function( elems, callback, arg ) {
var length, value,
i = 0,
ret = [];
// Go through the array, translating each of the items to their new values
if ( isArrayLike( elems ) ) {
length = elems.length;
for ( ; i < length; i++ ) {
value = callback( elems[ i ], i, arg );
if ( value != null ) {
ret.push( value );
}
}
// Go through every key on the object,
} else {
for ( i in elems ) {
value = callback( elems[ i ], i, arg );
if ( value != null ) {
ret.push( value );
}
}
}
// Flatten any nested arrays
return flat( ret );
},
// A global GUID counter for objects
guid: 1,
// jQuery.support is not used in Core but other projects attach their
// properties to it so it needs to exist.
support: support
} );
if ( typeof Symbol === "function" ) {
jQuery.fn[ Symbol.iterator ] = arr[ Symbol.iterator ];
}
// Populate the class2type map
jQuery.each( "Boolean Number String Function Array Date RegExp Object Error Symbol".split( " " ),
function( _i, name ) {
class2type[ "[object " + name + "]" ] = name.toLowerCase();
} );
function isArrayLike( obj ) {
// Support: real iOS 8.2 only (not reproducible in simulator)
// `in` check used to prevent JIT error (gh-2145)
// hasOwn isn't used here due to false negatives
// regarding Nodelist length in IE
var length = !!obj && "length" in obj && obj.length,
type = toType( obj );
if ( isFunction( obj ) || isWindow( obj ) ) {
return false;
}
return type === "array" || length === 0 ||
typeof length === "number" && length > 0 && ( length - 1 ) in obj;
}
var Sizzle =
/*!
* Sizzle CSS Selector Engine v2.3.5
* https://sizzlejs.com/
*
* Copyright JS Foundation and other contributors
* Released under the MIT license
* https://js.foundation/
*
* Date: 2020-03-14
*/
( function( window ) {
var i,
support,
Expr,
getText,
isXML,
tokenize,
compile,
select,
outermostContext,
sortInput,
hasDuplicate,
// Local document vars
setDocument,
document,
docElem,
documentIsHTML,
rbuggyQSA,
rbuggyMatches,
matches,
contains,
// Instance-specific data
expando = "sizzle" + 1 * new Date(),
preferredDoc = window.document,
dirruns = 0,
done = 0,
classCache = createCache(),
tokenCache = createCache(),
compilerCache = createCache(),
nonnativeSelectorCache = createCache(),
sortOrder = function( a, b ) {
if ( a === b ) {
hasDuplicate = true;
}
return 0;
},
// Instance methods
hasOwn = ( {} ).hasOwnProperty,
arr = [],
pop = arr.pop,
pushNative = arr.push,
push = arr.push,
slice = arr.slice,
// Use a stripped-down indexOf as it's faster than native
// https://jsperf.com/thor-indexof-vs-for/5
indexOf = function( list, elem ) {
var i = 0,
len = list.length;
for ( ; i < len; i++ ) {
if ( list[ i ] === elem ) {
return i;
}
}
return -1;
},
booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|" +
"ismap|loop|multiple|open|readonly|required|scoped",
// Regular expressions
// http://www.w3.org/TR/css3-selectors/#whitespace
whitespace = "[\\x20\\t\\r\\n\\f]",
// https://www.w3.org/TR/css-syntax-3/#ident-token-diagram
identifier = "(?:\\\\[\\da-fA-F]{1,6}" + whitespace +
"?|\\\\[^\\r\\n\\f]|[\\w-]|[^\0-\\x7f])+",
// Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors
attributes = "\\[" + whitespace + "*(" + identifier + ")(?:" + whitespace +
// Operator (capture 2)
"*([*^$|!~]?=)" + whitespace +
// "Attribute values must be CSS identifiers [capture 5]
// or strings [capture 3 or capture 4]"
"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" +
whitespace + "*\\]",
pseudos = ":(" + identifier + ")(?:\\((" +
// To reduce the number of selectors needing tokenize in the preFilter, prefer arguments:
// 1. quoted (capture 3; capture 4 or capture 5)
"('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" +
// 2. simple (capture 6)
"((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" +
// 3. anything else (capture 2)
".*" +
")\\)|)",
// Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter
rwhitespace = new RegExp( whitespace + "+", "g" ),
rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" +
whitespace + "+$", "g" ),
rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ),
rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace +
"*" ),
rdescend = new RegExp( whitespace + "|>" ),
rpseudo = new RegExp( pseudos ),
ridentifier = new RegExp( "^" + identifier + "$" ),
matchExpr = {
"ID": new RegExp( "^#(" + identifier + ")" ),
"CLASS": new RegExp( "^\\.(" + identifier + ")" ),
"TAG": new RegExp( "^(" + identifier + "|[*])" ),
"ATTR": new RegExp( "^" + attributes ),
"PSEUDO": new RegExp( "^" + pseudos ),
"CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" +
whitespace + "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" +
whitespace + "*(\\d+)|))" + whitespace + "*\\)|)", "i" ),
"bool": new RegExp( "^(?:" + booleans + ")$", "i" ),
// For use in libraries implementing .is()
// We use this for POS matching in `select`
"needsContext": new RegExp( "^" + whitespace +
"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + whitespace +
"*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" )
},
rhtml = /HTML$/i,
rinputs = /^(?:input|select|textarea|button)$/i,
rheader = /^h\d$/i,
rnative = /^[^{]+\{\s*\[native \w/,
// Easily-parseable/retrievable ID or TAG or CLASS selectors
rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,
rsibling = /[+~]/,
// CSS escapes
// http://www.w3.org/TR/CSS21/syndata.html#escaped-characters
runescape = new RegExp( "\\\\[\\da-fA-F]{1,6}" + whitespace + "?|\\\\([^\\r\\n\\f])", "g" ),
funescape = function( escape, nonHex ) {
var high = "0x" + escape.slice( 1 ) - 0x10000;
return nonHex ?
// Strip the backslash prefix from a non-hex escape sequence
nonHex :
// Replace a hexadecimal escape sequence with the encoded Unicode code point
// Support: IE <=11+
// For values outside the Basic Multilingual Plane (BMP), manually construct a
// surrogate pair
high < 0 ?
String.fromCharCode( high + 0x10000 ) :
String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 );
},
// CSS string/identifier serialization
// https://drafts.csswg.org/cssom/#common-serializing-idioms
rcssescape = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,
fcssescape = function( ch, asCodePoint ) {
if ( asCodePoint ) {
// U+0000 NULL becomes U+FFFD REPLACEMENT CHARACTER
if ( ch === "\0" ) {
return "\uFFFD";
}
// Control characters and (dependent upon position) numbers get escaped as code points
return ch.slice( 0, -1 ) + "\\" +
ch.charCodeAt( ch.length - 1 ).toString( 16 ) + " ";
}
// Other potentially-special ASCII characters get backslash-escaped
return "\\" + ch;
},
// Used for iframes
// See setDocument()
// Removing the function wrapper causes a "Permission Denied"
// error in IE
unloadHandler = function() {
setDocument();
},
inDisabledFieldset = addCombinator(
function( elem ) {
return elem.disabled === true && elem.nodeName.toLowerCase() === "fieldset";
},
{ dir: "parentNode", next: "legend" }
);
// Optimize for push.apply( _, NodeList )
try {
push.apply(
( arr = slice.call( preferredDoc.childNodes ) ),
preferredDoc.childNodes
);
// Support: Android<4.0
// Detect silently failing push.apply
// eslint-disable-next-line no-unused-expressions
arr[ preferredDoc.childNodes.length ].nodeType;
} catch ( e ) {
push = { apply: arr.length ?
// Leverage slice if possible
function( target, els ) {
pushNative.apply( target, slice.call( els ) );
} :
// Support: IE<9
// Otherwise append directly
function( target, els ) {
var j = target.length,
i = 0;
// Can't trust NodeList.length
while ( ( target[ j++ ] = els[ i++ ] ) ) {}
target.length = j - 1;
}
};
}
function Sizzle( selector, context, results, seed ) {
var m, i, elem, nid, match, groups, newSelector,
newContext = context && context.ownerDocument,
// nodeType defaults to 9, since context defaults to document
nodeType = context ? context.nodeType : 9;
results = results || [];
// Return early from calls with invalid selector or context
if ( typeof selector !== "string" || !selector ||
nodeType !== 1 && nodeType !== 9 && nodeType !== 11 ) {
return results;
}
// Try to shortcut find operations (as opposed to filters) in HTML documents
if ( !seed ) {
setDocument( context );
context = context || document;
if ( documentIsHTML ) {
// If the selector is sufficiently simple, try using a "get*By*" DOM method
// (excepting DocumentFragment context, where the methods don't exist)
if ( nodeType !== 11 && ( match = rquickExpr.exec( selector ) ) ) {
// ID selector
if ( ( m = match[ 1 ] ) ) {
// Document context
if ( nodeType === 9 ) {
if ( ( elem = context.getElementById( m ) ) ) {
// Support: IE, Opera, Webkit
// TODO: identify versions
// getElementById can match elements by name instead of ID
if ( elem.id === m ) {
results.push( elem );
return results;
}
} else {
return results;
}
// Element context
} else {
// Support: IE, Opera, Webkit
// TODO: identify versions
// getElementById can match elements by name instead of ID
if ( newContext && ( elem = newContext.getElementById( m ) ) &&
contains( context, elem ) &&
elem.id === m ) {
results.push( elem );
return results;
}
}
// Type selector
} else if ( match[ 2 ] ) {
push.apply( results, context.getElementsByTagName( selector ) );
return results;
// Class selector
} else if ( ( m = match[ 3 ] ) && support.getElementsByClassName &&
context.getElementsByClassName ) {
push.apply( results, context.getElementsByClassName( m ) );
return results;
}
}
// Take advantage of querySelectorAll
if ( support.qsa &&
!nonnativeSelectorCache[ selector + " " ] &&
( !rbuggyQSA || !rbuggyQSA.test( selector ) ) &&
// Support: IE 8 only
// Exclude object elements
( nodeType !== 1 || context.nodeName.toLowerCase() !== "object" ) ) {
newSelector = selector;
newContext = context;
// qSA considers elements outside a scoping root when evaluating child or
// descendant combinators, which is not what we want.
// In such cases, we work around the behavior by prefixing every selector in the
// list with an ID selector referencing the scope context.
// The technique has to be used as well when a leading combinator is used
// as such selectors are not recognized by querySelectorAll.
// Thanks to Andrew Dupont for this technique.
if ( nodeType === 1 &&
( rdescend.test( selector ) || rcombinators.test( selector ) ) ) {
// Expand context for sibling selectors
newContext = rsibling.test( selector ) && testContext( context.parentNode ) ||
context;
// We can use :scope instead of the ID hack if the browser
// supports it & if we're not changing the context.
if ( newContext !== context || !support.scope ) {
// Capture the context ID, setting it first if necessary
if ( ( nid = context.getAttribute( "id" ) ) ) {
nid = nid.replace( rcssescape, fcssescape );
} else {
context.setAttribute( "id", ( nid = expando ) );
}
}
// Prefix every selector in the list
groups = tokenize( selector );
i = groups.length;
while ( i-- ) {
groups[ i ] = ( nid ? "#" + nid : ":scope" ) + " " +
toSelector( groups[ i ] );
}
newSelector = groups.join( "," );
}
try {
push.apply( results,
newContext.querySelectorAll( newSelector )
);
return results;
} catch ( qsaError ) {
nonnativeSelectorCache( selector, true );
} finally {
if ( nid === expando ) {
context.removeAttribute( "id" );
}
}
}
}
}
// All others
return select( selector.replace( rtrim, "$1" ), context, results, seed );
}
/**
* Create key-value caches of limited size
* @returns {function(string, object)} Returns the Object data after storing it on itself with
* property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength)
* deleting the oldest entry
*/
function createCache() {
var keys = [];
function cache( key, value ) {
// Use (key + " ") to avoid collision with native prototype properties (see Issue #157)
if ( keys.push( key + " " ) > Expr.cacheLength ) {
// Only keep the most recent entries
delete cache[ keys.shift() ];
}
return ( cache[ key + " " ] = value );
}
return cache;
}
/**
* Mark a function for special use by Sizzle
* @param {Function} fn The function to mark
*/
function markFunction( fn ) {
fn[ expando ] = true;
return fn;
}
/**
* Support testing using an element
* @param {Function} fn Passed the created element and returns a boolean result
*/
function assert( fn ) {
var el = document.createElement( "fieldset" );
try {
return !!fn( el );
} catch ( e ) {
return false;
} finally {
// Remove from its parent by default
if ( el.parentNode ) {
el.parentNode.removeChild( el );
}
// release memory in IE
el = null;
}
}
/**
* Adds the same handler for all of the specified attrs
* @param {String} attrs Pipe-separated list of attributes
* @param {Function} handler The method that will be applied
*/
function addHandle( attrs, handler ) {
var arr = attrs.split( "|" ),
i = arr.length;
while ( i-- ) {
Expr.attrHandle[ arr[ i ] ] = handler;
}
}
/**
* Checks document order of two siblings
* @param {Element} a
* @param {Element} b
* @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b
*/
function siblingCheck( a, b ) {
var cur = b && a,
diff = cur && a.nodeType === 1 && b.nodeType === 1 &&
a.sourceIndex - b.sourceIndex;
// Use IE sourceIndex if available on both nodes
if ( diff ) {
return diff;
}
// Check if b follows a
if ( cur ) {
while ( ( cur = cur.nextSibling ) ) {
if ( cur === b ) {
return -1;
}
}
}
return a ? 1 : -1;
}
/**
* Returns a function to use in pseudos for input types
* @param {String} type
*/
function createInputPseudo( type ) {
return function( elem ) {
var name = elem.nodeName.toLowerCase();
return name === "input" && elem.type === type;
};
}
/**
* Returns a function to use in pseudos for buttons
* @param {String} type
*/
function createButtonPseudo( type ) {
return function( elem ) {
var name = elem.nodeName.toLowerCase();
return ( name === "input" || name === "button" ) && elem.type === type;
};
}
/**
* Returns a function to use in pseudos for :enabled/:disabled
* @param {Boolean} disabled true for :disabled; false for :enabled
*/
function createDisabledPseudo( disabled ) {
// Known :disabled false positives: fieldset[disabled] > legend:nth-of-type(n+2) :can-disable
return function( elem ) {
// Only certain elements can match :enabled or :disabled
// https://html.spec.whatwg.org/multipage/scripting.html#selector-enabled
// https://html.spec.whatwg.org/multipage/scripting.html#selector-disabled
if ( "form" in elem ) {
// Check for inherited disabledness on relevant non-disabled elements:
// * listed form-associated elements in a disabled fieldset
// https://html.spec.whatwg.org/multipage/forms.html#category-listed
// https://html.spec.whatwg.org/multipage/forms.html#concept-fe-disabled
// * option elements in a disabled optgroup
// https://html.spec.whatwg.org/multipage/forms.html#concept-option-disabled
// All such elements have a "form" property.
if ( elem.parentNode && elem.disabled === false ) {
// Option elements defer to a parent optgroup if present
if ( "label" in elem ) {
if ( "label" in elem.parentNode ) {
return elem.parentNode.disabled === disabled;
} else {
return elem.disabled === disabled;
}
}
// Support: IE 6 - 11
// Use the isDisabled shortcut property to check for disabled fieldset ancestors
return elem.isDisabled === disabled ||
// Where there is no isDisabled, check manually
/* jshint -W018 */
elem.isDisabled !== !disabled &&
inDisabledFieldset( elem ) === disabled;
}
return elem.disabled === disabled;
// Try to winnow out elements that can't be disabled before trusting the disabled property.
// Some victims get caught in our net (label, legend, menu, track), but it shouldn't
// even exist on them, let alone have a boolean value.
} else if ( "label" in elem ) {
return elem.disabled === disabled;
}
// Remaining elements are neither :enabled nor :disabled
return false;
};
}
/**
* Returns a function to use in pseudos for positionals
* @param {Function} fn
*/
function createPositionalPseudo( fn ) {
return markFunction( function( argument ) {
argument = +argument;
return markFunction( function( seed, matches ) {
var j,
matchIndexes = fn( [], seed.length, argument ),
i = matchIndexes.length;
// Match elements found at the specified indexes
while ( i-- ) {
if ( seed[ ( j = matchIndexes[ i ] ) ] ) {
seed[ j ] = !( matches[ j ] = seed[ j ] );
}
}
} );
} );
}
/**
* Checks a node for validity as a Sizzle context
* @param {Element|Object=} context
* @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value
*/
function testContext( context ) {
return context && typeof context.getElementsByTagName !== "undefined" && context;
}
// Expose support vars for convenience
support = Sizzle.support = {};
/**
* Detects XML nodes
* @param {Element|Object} elem An element or a document
* @returns {Boolean} True iff elem is a non-HTML XML node
*/
isXML = Sizzle.isXML = function( elem ) {
var namespace = elem.namespaceURI,
docElem = ( elem.ownerDocument || elem ).documentElement;
// Support: IE <=8
// Assume HTML when documentElement doesn't yet exist, such as inside loading iframes
// https://bugs.jquery.com/ticket/4833
return !rhtml.test( namespace || docElem && docElem.nodeName || "HTML" );
};
/**
* Sets document-related variables once based on the current document
* @param {Element|Object} [doc] An element or document object to use to set the document
* @returns {Object} Returns the current document
*/
setDocument = Sizzle.setDocument = function( node ) {
var hasCompare, subWindow,
doc = node ? node.ownerDocument || node : preferredDoc;
// Return early if doc is invalid or already selected
// Support: IE 11+, Edge 17 - 18+
// IE/Edge sometimes throw a "Permission denied" error when strict-comparing
// two documents; shallow comparisons work.
// eslint-disable-next-line eqeqeq
if ( doc == document || doc.nodeType !== 9 || !doc.documentElement ) {
return document;
}
// Update global variables
document = doc;
docElem = document.documentElement;
documentIsHTML = !isXML( document );
// Support: IE 9 - 11+, Edge 12 - 18+
// Accessing iframe documents after unload throws "permission denied" errors (jQuery #13936)
// Support: IE 11+, Edge 17 - 18+
// IE/Edge sometimes throw a "Permission denied" error when strict-comparing
// two documents; shallow comparisons work.
// eslint-disable-next-line eqeqeq
if ( preferredDoc != document &&
( subWindow = document.defaultView ) && subWindow.top !== subWindow ) {
// Support: IE 11, Edge
if ( subWindow.addEventListener ) {
subWindow.addEventListener( "unload", unloadHandler, false );
// Support: IE 9 - 10 only
} else if ( subWindow.attachEvent ) {
subWindow.attachEvent( "onunload", unloadHandler );
}
}
// Support: IE 8 - 11+, Edge 12 - 18+, Chrome <=16 - 25 only, Firefox <=3.6 - 31 only,
// Safari 4 - 5 only, Opera <=11.6 - 12.x only
// IE/Edge & older browsers don't support the :scope pseudo-class.
// Support: Safari 6.0 only
// Safari 6.0 supports :scope but it's an alias of :root there.
support.scope = assert( function( el ) {
docElem.appendChild( el ).appendChild( document.createElement( "div" ) );
return typeof el.querySelectorAll !== "undefined" &&
!el.querySelectorAll( ":scope fieldset div" ).length;
} );
/* Attributes
---------------------------------------------------------------------- */
// Support: IE<8
// Verify that getAttribute really returns attributes and not properties
// (excepting IE8 booleans)
support.attributes = assert( function( el ) {
el.className = "i";
return !el.getAttribute( "className" );
} );
/* getElement(s)By*
---------------------------------------------------------------------- */
// Check if getElementsByTagName("*") returns only elements
support.getElementsByTagName = assert( function( el ) {
el.appendChild( document.createComment( "" ) );
return !el.getElementsByTagName( "*" ).length;
} );
// Support: IE<9
support.getElementsByClassName = rnative.test( document.getElementsByClassName );
// Support: IE<10
// Check if getElementById returns elements by name
// The broken getElementById methods don't pick up programmatically-set names,
// so use a roundabout getElementsByName test
support.getById = assert( function( el ) {
docElem.appendChild( el ).id = expando;
return !document.getElementsByName || !document.getElementsByName( expando ).length;
} );
// ID filter and find
if ( support.getById ) {
Expr.filter[ "ID" ] = function( id ) {
var attrId = id.replace( runescape, funescape );
return function( elem ) {
return elem.getAttribute( "id" ) === attrId;
};
};
Expr.find[ "ID" ] = function( id, context ) {
if ( typeof context.getElementById !== "undefined" && documentIsHTML ) {
var elem = context.getElementById( id );
return elem ? [ elem ] : [];
}
};
} else {
Expr.filter[ "ID" ] = function( id ) {
var attrId = id.replace( runescape, funescape );
return function( elem ) {
var node = typeof elem.getAttributeNode !== "undefined" &&
elem.getAttributeNode( "id" );
return node && node.value === attrId;
};
};
// Support: IE 6 - 7 only
// getElementById is not reliable as a find shortcut
Expr.find[ "ID" ] = function( id, context ) {
if ( typeof context.getElementById !== "undefined" && documentIsHTML ) {
var node, i, elems,
elem = context.getElementById( id );
if ( elem ) {
// Verify the id attribute
node = elem.getAttributeNode( "id" );
if ( node && node.value === id ) {
return [ elem ];
}
// Fall back on getElementsByName
elems = context.getElementsByName( id );
i = 0;
while ( ( elem = elems[ i++ ] ) ) {
node = elem.getAttributeNode( "id" );
if ( node && node.value === id ) {
return [ elem ];
}
}
}
return [];
}
};
}
// Tag
Expr.find[ "TAG" ] = support.getElementsByTagName ?
function( tag, context ) {
if ( typeof context.getElementsByTagName !== "undefined" ) {
return context.getElementsByTagName( tag );
// DocumentFragment nodes don't have gEBTN
} else if ( support.qsa ) {
return context.querySelectorAll( tag );
}
} :
function( tag, context ) {
var elem,
tmp = [],
i = 0,
// By happy coincidence, a (broken) gEBTN appears on DocumentFragment nodes too
results = context.getElementsByTagName( tag );
// Filter out possible comments
if ( tag === "*" ) {
while ( ( elem = results[ i++ ] ) ) {
if ( elem.nodeType === 1 ) {
tmp.push( elem );
}
}
return tmp;
}
return results;
};
// Class
Expr.find[ "CLASS" ] = support.getElementsByClassName && function( className, context ) {
if ( typeof context.getElementsByClassName !== "undefined" && documentIsHTML ) {
return context.getElementsByClassName( className );
}
};
/* QSA/matchesSelector
---------------------------------------------------------------------- */
// QSA and matchesSelector support
// matchesSelector(:active) reports false when true (IE9/Opera 11.5)
rbuggyMatches = [];
// qSa(:focus) reports false when true (Chrome 21)
// We allow this because of a bug in IE8/9 that throws an error
// whenever `document.activeElement` is accessed on an iframe
// So, we allow :focus to pass through QSA all the time to avoid the IE error
// See https://bugs.jquery.com/ticket/13378
rbuggyQSA = [];
if ( ( support.qsa = rnative.test( document.querySelectorAll ) ) ) {
// Build QSA regex
// Regex strategy adopted from Diego Perini
assert( function( el ) {
var input;
// Select is set to empty string on purpose
// This is to test IE's treatment of not explicitly
// setting a boolean content attribute,
// since its presence should be enough
// https://bugs.jquery.com/ticket/12359
docElem.appendChild( el ).innerHTML = "<a id='" + expando + "'></a>" +
"<select id='" + expando + "-\r\\' msallowcapture=''>" +
"<option selected=''></option></select>";
// Support: IE8, Opera 11-12.16
// Nothing should be selected when empty strings follow ^= or $= or *=
// The test attribute must be unknown in Opera but "safe" for WinRT
// https://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section
if ( el.querySelectorAll( "[msallowcapture^='']" ).length ) {
rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" );
}
// Support: IE8
// Boolean attributes and "value" are not treated correctly
if ( !el.querySelectorAll( "[selected]" ).length ) {
rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" );
}
// Support: Chrome<29, Android<4.4, Safari<7.0+, iOS<7.0+, PhantomJS<1.9.8+
if ( !el.querySelectorAll( "[id~=" + expando + "-]" ).length ) {
rbuggyQSA.push( "~=" );
}
// Support: IE 11+, Edge 15 - 18+
// IE 11/Edge don't find elements on a `[name='']` query in some cases.
// Adding a temporary attribute to the document before the selection works
// around the issue.
// Interestingly, IE 10 & older don't seem to have the issue.
input = document.createElement( "input" );
input.setAttribute( "name", "" );
el.appendChild( input );
if ( !el.querySelectorAll( "[name='']" ).length ) {
rbuggyQSA.push( "\\[" + whitespace + "*name" + whitespace + "*=" +
whitespace + "*(?:''|\"\")" );
}
// Webkit/Opera - :checked should return selected option elements
// http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
// IE8 throws error here and will not see later tests
if ( !el.querySelectorAll( ":checked" ).length ) {
rbuggyQSA.push( ":checked" );
}
// Support: Safari 8+, iOS 8+
// https://bugs.webkit.org/show_bug.cgi?id=136851
// In-page `selector#id sibling-combinator selector` fails
if ( !el.querySelectorAll( "a#" + expando + "+*" ).length ) {
rbuggyQSA.push( ".#.+[+~]" );
}
// Support: Firefox <=3.6 - 5 only
// Old Firefox doesn't throw on a badly-escaped identifier.
el.querySelectorAll( "\\\f" );
rbuggyQSA.push( "[\\r\\n\\f]" );
} );
assert( function( el ) {
el.innerHTML = "<a href='' disabled='disabled'></a>" +
"<select disabled='disabled'><option/></select>";
// Support: Windows 8 Native Apps
// The type and name attributes are restricted during .innerHTML assignment
var input = document.createElement( "input" );
input.setAttribute( "type", "hidden" );
el.appendChild( input ).setAttribute( "name", "D" );
// Support: IE8
// Enforce case-sensitivity of name attribute
if ( el.querySelectorAll( "[name=d]" ).length ) {
rbuggyQSA.push( "name" + whitespace + "*[*^$|!~]?=" );
}
// FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled)
// IE8 throws error here and will not see later tests
if ( el.querySelectorAll( ":enabled" ).length !== 2 ) {
rbuggyQSA.push( ":enabled", ":disabled" );
}
// Support: IE9-11+
// IE's :disabled selector does not pick up the children of disabled fieldsets
docElem.appendChild( el ).disabled = true;
if ( el.querySelectorAll( ":disabled" ).length !== 2 ) {
rbuggyQSA.push( ":enabled", ":disabled" );
}
// Support: Opera 10 - 11 only
// Opera 10-11 does not throw on post-comma invalid pseudos
el.querySelectorAll( "*,:x" );
rbuggyQSA.push( ",.*:" );
} );
}
if ( ( support.matchesSelector = rnative.test( ( matches = docElem.matches ||
docElem.webkitMatchesSelector ||
docElem.mozMatchesSelector ||
docElem.oMatchesSelector ||
docElem.msMatchesSelector ) ) ) ) {
assert( function( el ) {
// Check to see if it's possible to do matchesSelector
// on a disconnected node (IE 9)
support.disconnectedMatch = matches.call( el, "*" );
// This should fail with an exception
// Gecko does not error, returns false instead
matches.call( el, "[s!='']:x" );
rbuggyMatches.push( "!=", pseudos );
} );
}
rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join( "|" ) );
rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join( "|" ) );
/* Contains
---------------------------------------------------------------------- */
hasCompare = rnative.test( docElem.compareDocumentPosition );
// Element contains another
// Purposefully self-exclusive
// As in, an element does not contain itself
contains = hasCompare || rnative.test( docElem.contains ) ?
function( a, b ) {
var adown = a.nodeType === 9 ? a.documentElement : a,
bup = b && b.parentNode;
return a === bup || !!( bup && bup.nodeType === 1 && (
adown.contains ?
adown.contains( bup ) :
a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16
) );
} :
function( a, b ) {
if ( b ) {
while ( ( b = b.parentNode ) ) {
if ( b === a ) {
return true;
}
}
}
return false;
};
/* Sorting
---------------------------------------------------------------------- */
// Document order sorting
sortOrder = hasCompare ?
function( a, b ) {
// Flag for duplicate removal
if ( a === b ) {
hasDuplicate = true;
return 0;
}
// Sort on method existence if only one input has compareDocumentPosition
var compare = !a.compareDocumentPosition - !b.compareDocumentPosition;
if ( compare ) {
return compare;
}
// Calculate position if both inputs belong to the same document
// Support: IE 11+, Edge 17 - 18+
// IE/Edge sometimes throw a "Permission denied" error when strict-comparing
// two documents; shallow comparisons work.
// eslint-disable-next-line eqeqeq
compare = ( a.ownerDocument || a ) == ( b.ownerDocument || b ) ?
a.compareDocumentPosition( b ) :
// Otherwise we know they are disconnected
1;
// Disconnected nodes
if ( compare & 1 ||
( !support.sortDetached && b.compareDocumentPosition( a ) === compare ) ) {
// Choose the first element that is related to our preferred document
// Support: IE 11+, Edge 17 - 18+
// IE/Edge sometimes throw a "Permission denied" error when strict-comparing
// two documents; shallow comparisons work.
// eslint-disable-next-line eqeqeq
if ( a == document || a.ownerDocument == preferredDoc &&
contains( preferredDoc, a ) ) {
return -1;
}
// Support: IE 11+, Edge 17 - 18+
// IE/Edge sometimes throw a "Permission denied" error when strict-comparing
// two documents; shallow comparisons work.
// eslint-disable-next-line eqeqeq
if ( b == document || b.ownerDocument == preferredDoc &&
contains( preferredDoc, b ) ) {
return 1;
}
// Maintain original order
return sortInput ?
( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) :
0;
}
return compare & 4 ? -1 : 1;
} :
function( a, b ) {
// Exit early if the nodes are identical
if ( a === b ) {
hasDuplicate = true;
return 0;
}
var cur,
i = 0,
aup = a.parentNode,
bup = b.parentNode,
ap = [ a ],
bp = [ b ];
// Parentless nodes are either documents or disconnected
if ( !aup || !bup ) {
// Support: IE 11+, Edge 17 - 18+
// IE/Edge sometimes throw a "Permission denied" error when strict-comparing
// two documents; shallow comparisons work.
/* eslint-disable eqeqeq */
return a == document ? -1 :
b == document ? 1 :
/* eslint-enable eqeqeq */
aup ? -1 :
bup ? 1 :
sortInput ?
( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) :
0;
// If the nodes are siblings, we can do a quick check
} else if ( aup === bup ) {
return siblingCheck( a, b );
}
// Otherwise we need full lists of their ancestors for comparison
cur = a;
while ( ( cur = cur.parentNode ) ) {
ap.unshift( cur );
}
cur = b;
while ( ( cur = cur.parentNode ) ) {
bp.unshift( cur );
}
// Walk down the tree looking for a discrepancy
while ( ap[ i ] === bp[ i ] ) {
i++;
}
return i ?
// Do a sibling check if the nodes have a common ancestor
siblingCheck( ap[ i ], bp[ i ] ) :
// Otherwise nodes in our document sort first
// Support: IE 11+, Edge 17 - 18+
// IE/Edge sometimes throw a "Permission denied" error when strict-comparing
// two documents; shallow comparisons work.
/* eslint-disable eqeqeq */
ap[ i ] == preferredDoc ? -1 :
bp[ i ] == preferredDoc ? 1 :
/* eslint-enable eqeqeq */
0;
};
return document;
};
Sizzle.matches = function( expr, elements ) {
return Sizzle( expr, null, null, elements );
};
Sizzle.matchesSelector = function( elem, expr ) {
setDocument( elem );
if ( support.matchesSelector && documentIsHTML &&
!nonnativeSelectorCache[ expr + " " ] &&
( !rbuggyMatches || !rbuggyMatches.test( expr ) ) &&
( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) {
try {
var ret = matches.call( elem, expr );
// IE 9's matchesSelector returns false on disconnected nodes
if ( ret || support.disconnectedMatch ||
// As well, disconnected nodes are said to be in a document
// fragment in IE 9
elem.document && elem.document.nodeType !== 11 ) {
return ret;
}
} catch ( e ) {
nonnativeSelectorCache( expr, true );
}
}
return Sizzle( expr, document, null, [ elem ] ).length > 0;
};
Sizzle.contains = function( context, elem ) {
// Set document vars if needed
// Support: IE 11+, Edge 17 - 18+
// IE/Edge sometimes throw a "Permission denied" error when strict-comparing
// two documents; shallow comparisons work.
// eslint-disable-next-line eqeqeq
if ( ( context.ownerDocument || context ) != document ) {
setDocument( context );
}
return contains( context, elem );
};
Sizzle.attr = function( elem, name ) {
// Set document vars if needed
// Support: IE 11+, Edge 17 - 18+
// IE/Edge sometimes throw a "Permission denied" error when strict-comparing
// two documents; shallow comparisons work.
// eslint-disable-next-line eqeqeq
if ( ( elem.ownerDocument || elem ) != document ) {
setDocument( elem );
}
var fn = Expr.attrHandle[ name.toLowerCase() ],
// Don't get fooled by Object.prototype properties (jQuery #13807)
val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ?
fn( elem, name, !documentIsHTML ) :
undefined;
return val !== undefined ?
val :
support.attributes || !documentIsHTML ?
elem.getAttribute( name ) :
( val = elem.getAttributeNode( name ) ) && val.specified ?
val.value :
null;
};
Sizzle.escape = function( sel ) {
return ( sel + "" ).replace( rcssescape, fcssescape );
};
Sizzle.error = function( msg ) {
throw new Error( "Syntax error, unrecognized expression: " + msg );
};
/**
* Document sorting and removing duplicates
* @param {ArrayLike} results
*/
Sizzle.uniqueSort = function( results ) {
var elem,
duplicates = [],
j = 0,
i = 0;
// Unless we *know* we can detect duplicates, assume their presence
hasDuplicate = !support.detectDuplicates;
sortInput = !support.sortStable && results.slice( 0 );
results.sort( sortOrder );
if ( hasDuplicate ) {
while ( ( elem = results[ i++ ] ) ) {
if ( elem === results[ i ] ) {
j = duplicates.push( i );
}
}
while ( j-- ) {
results.splice( duplicates[ j ], 1 );
}
}
// Clear input after sorting to release objects
// See https://github.com/jquery/sizzle/pull/225
sortInput = null;
return results;
};
/**
* Utility function for retrieving the text value of an array of DOM nodes
* @param {Array|Element} elem
*/
getText = Sizzle.getText = function( elem ) {
var node,
ret = "",
i = 0,
nodeType = elem.nodeType;
if ( !nodeType ) {
// If no nodeType, this is expected to be an array
while ( ( node = elem[ i++ ] ) ) {
// Do not traverse comment nodes
ret += getText( node );
}
} else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) {
// Use textContent for elements
// innerText usage removed for consistency of new lines (jQuery #11153)
if ( typeof elem.textContent === "string" ) {
return elem.textContent;
} else {
// Traverse its children
for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
ret += getText( elem );
}
}
} else if ( nodeType === 3 || nodeType === 4 ) {
return elem.nodeValue;
}
// Do not include comment or processing instruction nodes
return ret;
};
Expr = Sizzle.selectors = {
// Can be adjusted by the user
cacheLength: 50,
createPseudo: markFunction,
match: matchExpr,
attrHandle: {},
find: {},
relative: {
">": { dir: "parentNode", first: true },
" ": { dir: "parentNode" },
"+": { dir: "previousSibling", first: true },
"~": { dir: "previousSibling" }
},
preFilter: {
"ATTR": function( match ) {
match[ 1 ] = match[ 1 ].replace( runescape, funescape );
// Move the given value to match[3] whether quoted or unquoted
match[ 3 ] = ( match[ 3 ] || match[ 4 ] ||
match[ 5 ] || "" ).replace( runescape, funescape );
if ( match[ 2 ] === "~=" ) {
match[ 3 ] = " " + match[ 3 ] + " ";
}
return match.slice( 0, 4 );
},
"CHILD": function( match ) {
/* matches from matchExpr["CHILD"]
1 type (only|nth|...)
2 what (child|of-type)
3 argument (even|odd|\d*|\d*n([+-]\d+)?|...)
4 xn-component of xn+y argument ([+-]?\d*n|)
5 sign of xn-component
6 x of xn-component
7 sign of y-component
8 y of y-component
*/
match[ 1 ] = match[ 1 ].toLowerCase();
if ( match[ 1 ].slice( 0, 3 ) === "nth" ) {
// nth-* requires argument
if ( !match[ 3 ] ) {
Sizzle.error( match[ 0 ] );
}
// numeric x and y parameters for Expr.filter.CHILD
// remember that false/true cast respectively to 0/1
match[ 4 ] = +( match[ 4 ] ?
match[ 5 ] + ( match[ 6 ] || 1 ) :
2 * ( match[ 3 ] === "even" || match[ 3 ] === "odd" ) );
match[ 5 ] = +( ( match[ 7 ] + match[ 8 ] ) || match[ 3 ] === "odd" );
// other types prohibit arguments
} else if ( match[ 3 ] ) {
Sizzle.error( match[ 0 ] );
}
return match;
},
"PSEUDO": function( match ) {
var excess,
unquoted = !match[ 6 ] && match[ 2 ];
if ( matchExpr[ "CHILD" ].test( match[ 0 ] ) ) {
return null;
}
// Accept quoted arguments as-is
if ( match[ 3 ] ) {
match[ 2 ] = match[ 4 ] || match[ 5 ] || "";
// Strip excess characters from unquoted arguments
} else if ( unquoted && rpseudo.test( unquoted ) &&
// Get excess from tokenize (recursively)
( excess = tokenize( unquoted, true ) ) &&
// advance to the next closing parenthesis
( excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length ) ) {
// excess is a negative index
match[ 0 ] = match[ 0 ].slice( 0, excess );
match[ 2 ] = unquoted.slice( 0, excess );
}
// Return only captures needed by the pseudo filter method (type and argument)
return match.slice( 0, 3 );
}
},
filter: {
"TAG": function( nodeNameSelector ) {
var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase();
return nodeNameSelector === "*" ?
function() {
return true;
} :
function( elem ) {
return elem.nodeName && elem.nodeName.toLowerCase() === nodeName;
};
},
"CLASS": function( className ) {
var pattern = classCache[ className + " " ];
return pattern ||
( pattern = new RegExp( "(^|" + whitespace +
")" + className + "(" + whitespace + "|$)" ) ) && classCache(
className, function( elem ) {
return pattern.test(
typeof elem.className === "string" && elem.className ||
typeof elem.getAttribute !== "undefined" &&
elem.getAttribute( "class" ) ||
""
);
} );
},
"ATTR": function( name, operator, check ) {
return function( elem ) {
var result = Sizzle.attr( elem, name );
if ( result == null ) {
return operator === "!=";
}
if ( !operator ) {
return true;
}
result += "";
/* eslint-disable max-len */
return operator === "=" ? result === check :
operator === "!=" ? result !== check :
operator === "^=" ? check && result.indexOf( check ) === 0 :
operator === "*=" ? check && result.indexOf( check ) > -1 :
operator === "$=" ? check && result.slice( -check.length ) === check :
operator === "~=" ? ( " " + result.replace( rwhitespace, " " ) + " " ).indexOf( check ) > -1 :
operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" :
false;
/* eslint-enable max-len */
};
},
"CHILD": function( type, what, _argument, first, last ) {
var simple = type.slice( 0, 3 ) !== "nth",
forward = type.slice( -4 ) !== "last",
ofType = what === "of-type";
return first === 1 && last === 0 ?
// Shortcut for :nth-*(n)
function( elem ) {
return !!elem.parentNode;
} :
function( elem, _context, xml ) {
var cache, uniqueCache, outerCache, node, nodeIndex, start,
dir = simple !== forward ? "nextSibling" : "previousSibling",
parent = elem.parentNode,
name = ofType && elem.nodeName.toLowerCase(),
useCache = !xml && !ofType,
diff = false;
if ( parent ) {
// :(first|last|only)-(child|of-type)
if ( simple ) {
while ( dir ) {
node = elem;
while ( ( node = node[ dir ] ) ) {
if ( ofType ?
node.nodeName.toLowerCase() === name :
node.nodeType === 1 ) {
return false;
}
}
// Reverse direction for :only-* (if we haven't yet done so)
start = dir = type === "only" && !start && "nextSibling";
}
return true;
}
start = [ forward ? parent.firstChild : parent.lastChild ];
// non-xml :nth-child(...) stores cache data on `parent`
if ( forward && useCache ) {
// Seek `elem` from a previously-cached index
// ...in a gzip-friendly way
node = parent;
outerCache = node[ expando ] || ( node[ expando ] = {} );
// Support: IE <9 only
// Defend against cloned attroperties (jQuery gh-1709)
uniqueCache = outerCache[ node.uniqueID ] ||
( outerCache[ node.uniqueID ] = {} );
cache = uniqueCache[ type ] || [];
nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ];
diff = nodeIndex && cache[ 2 ];
node = nodeIndex && parent.childNodes[ nodeIndex ];
while ( ( node = ++nodeIndex && node && node[ dir ] ||
// Fallback to seeking `elem` from the start
( diff = nodeIndex = 0 ) || start.pop() ) ) {
// When found, cache indexes on `parent` and break
if ( node.nodeType === 1 && ++diff && node === elem ) {
uniqueCache[ type ] = [ dirruns, nodeIndex, diff ];
break;
}
}
} else {
// Use previously-cached element index if available
if ( useCache ) {
// ...in a gzip-friendly way
node = elem;
outerCache = node[ expando ] || ( node[ expando ] = {} );
// Support: IE <9 only
// Defend against cloned attroperties (jQuery gh-1709)
uniqueCache = outerCache[ node.uniqueID ] ||
( outerCache[ node.uniqueID ] = {} );
cache = uniqueCache[ type ] || [];
nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ];
diff = nodeIndex;
}
// xml :nth-child(...)
// or :nth-last-child(...) or :nth(-last)?-of-type(...)
if ( diff === false ) {
// Use the same loop as above to seek `elem` from the start
while ( ( node = ++nodeIndex && node && node[ dir ] ||
( diff = nodeIndex = 0 ) || start.pop() ) ) {
if ( ( ofType ?
node.nodeName.toLowerCase() === name :
node.nodeType === 1 ) &&
++diff ) {
// Cache the index of each encountered element
if ( useCache ) {
outerCache = node[ expando ] ||
( node[ expando ] = {} );
// Support: IE <9 only
// Defend against cloned attroperties (jQuery gh-1709)
uniqueCache = outerCache[ node.uniqueID ] ||
( outerCache[ node.uniqueID ] = {} );
uniqueCache[ type ] = [ dirruns, diff ];
}
if ( node === elem ) {
break;
}
}
}
}
}
// Incorporate the offset, then check against cycle size
diff -= last;
return diff === first || ( diff % first === 0 && diff / first >= 0 );
}
};
},
"PSEUDO": function( pseudo, argument ) {
// pseudo-class names are case-insensitive
// http://www.w3.org/TR/selectors/#pseudo-classes
// Prioritize by case sensitivity in case custom pseudos are added with uppercase letters
// Remember that setFilters inherits from pseudos
var args,
fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] ||
Sizzle.error( "unsupported pseudo: " + pseudo );
// The user may use createPseudo to indicate that
// arguments are needed to create the filter function
// just as Sizzle does
if ( fn[ expando ] ) {
return fn( argument );
}
// But maintain support for old signatures
if ( fn.length > 1 ) {
args = [ pseudo, pseudo, "", argument ];
return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ?
markFunction( function( seed, matches ) {
var idx,
matched = fn( seed, argument ),
i = matched.length;
while ( i-- ) {
idx = indexOf( seed, matched[ i ] );
seed[ idx ] = !( matches[ idx ] = matched[ i ] );
}
} ) :
function( elem ) {
return fn( elem, 0, args );
};
}
return fn;
}
},
pseudos: {
// Potentially complex pseudos
"not": markFunction( function( selector ) {
// Trim the selector passed to compile
// to avoid treating leading and trailing
// spaces as combinators
var input = [],
results = [],
matcher = compile( selector.replace( rtrim, "$1" ) );
return matcher[ expando ] ?
markFunction( function( seed, matches, _context, xml ) {
var elem,
unmatched = matcher( seed, null, xml, [] ),
i = seed.length;
// Match elements unmatched by `matcher`
while ( i-- ) {
if ( ( elem = unmatched[ i ] ) ) {
seed[ i ] = !( matches[ i ] = elem );
}
}
} ) :
function( elem, _context, xml ) {
input[ 0 ] = elem;
matcher( input, null, xml, results );
// Don't keep the element (issue #299)
input[ 0 ] = null;
return !results.pop();
};
} ),
"has": markFunction( function( selector ) {
return function( elem ) {
return Sizzle( selector, elem ).length > 0;
};
} ),
"contains": markFunction( function( text ) {
text = text.replace( runescape, funescape );
return function( elem ) {
return ( elem.textContent || getText( elem ) ).indexOf( text ) > -1;
};
} ),
// "Whether an element is represented by a :lang() selector
// is based solely on the element's language value
// being equal to the identifier C,
// or beginning with the identifier C immediately followed by "-".
// The matching of C against the element's language value is performed case-insensitively.
// The identifier C does not have to be a valid language name."
// http://www.w3.org/TR/selectors/#lang-pseudo
"lang": markFunction( function( lang ) {
// lang value must be a valid identifier
if ( !ridentifier.test( lang || "" ) ) {
Sizzle.error( "unsupported lang: " + lang );
}
lang = lang.replace( runescape, funescape ).toLowerCase();
return function( elem ) {
var elemLang;
do {
if ( ( elemLang = documentIsHTML ?
elem.lang :
elem.getAttribute( "xml:lang" ) || elem.getAttribute( "lang" ) ) ) {
elemLang = elemLang.toLowerCase();
return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0;
}
} while ( ( elem = elem.parentNode ) && elem.nodeType === 1 );
return false;
};
} ),
// Miscellaneous
"target": function( elem ) {
var hash = window.location && window.location.hash;
return hash && hash.slice( 1 ) === elem.id;
},
"root": function( elem ) {
return elem === docElem;
},
"focus": function( elem ) {
return elem === document.activeElement &&
( !document.hasFocus || document.hasFocus() ) &&
!!( elem.type || elem.href || ~elem.tabIndex );
},
// Boolean properties
"enabled": createDisabledPseudo( false ),
"disabled": createDisabledPseudo( true ),
"checked": function( elem ) {
// In CSS3, :checked should return both checked and selected elements
// http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
var nodeName = elem.nodeName.toLowerCase();
return ( nodeName === "input" && !!elem.checked ) ||
( nodeName === "option" && !!elem.selected );
},
"selected": function( elem ) {
// Accessing this property makes selected-by-default
// options in Safari work properly
if ( elem.parentNode ) {
// eslint-disable-next-line no-unused-expressions
elem.parentNode.selectedIndex;
}
return elem.selected === true;
},
// Contents
"empty": function( elem ) {
// http://www.w3.org/TR/selectors/#empty-pseudo
// :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5),
// but not by others (comment: 8; processing instruction: 7; etc.)
// nodeType < 6 works because attributes (2) do not appear as children
for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
if ( elem.nodeType < 6 ) {
return false;
}
}
return true;
},
"parent": function( elem ) {
return !Expr.pseudos[ "empty" ]( elem );
},
// Element/input types
"header": function( elem ) {
return rheader.test( elem.nodeName );
},
"input": function( elem ) {
return rinputs.test( elem.nodeName );
},
"button": function( elem ) {
var name = elem.nodeName.toLowerCase();
return name === "input" && elem.type === "button" || name === "button";
},
"text": function( elem ) {
var attr;
return elem.nodeName.toLowerCase() === "input" &&
elem.type === "text" &&
// Support: IE<8
// New HTML5 attribute values (e.g., "search") appear with elem.type === "text"
( ( attr = elem.getAttribute( "type" ) ) == null ||
attr.toLowerCase() === "text" );
},
// Position-in-collection
"first": createPositionalPseudo( function() {
return [ 0 ];
} ),
"last": createPositionalPseudo( function( _matchIndexes, length ) {
return [ length - 1 ];
} ),
"eq": createPositionalPseudo( function( _matchIndexes, length, argument ) {
return [ argument < 0 ? argument + length : argument ];
} ),
"even": createPositionalPseudo( function( matchIndexes, length ) {
var i = 0;
for ( ; i < length; i += 2 ) {
matchIndexes.push( i );
}
return matchIndexes;
} ),
"odd": createPositionalPseudo( function( matchIndexes, length ) {
var i = 1;
for ( ; i < length; i += 2 ) {
matchIndexes.push( i );
}
return matchIndexes;
} ),
"lt": createPositionalPseudo( function( matchIndexes, length, argument ) {
var i = argument < 0 ?
argument + length :
argument > length ?
length :
argument;
for ( ; --i >= 0; ) {
matchIndexes.push( i );
}
return matchIndexes;
} ),
"gt": createPositionalPseudo( function( matchIndexes, length, argument ) {
var i = argument < 0 ? argument + length : argument;
for ( ; ++i < length; ) {
matchIndexes.push( i );
}
return matchIndexes;
} )
}
};
Expr.pseudos[ "nth" ] = Expr.pseudos[ "eq" ];
// Add button/input type pseudos
for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) {
Expr.pseudos[ i ] = createInputPseudo( i );
}
for ( i in { submit: true, reset: true } ) {
Expr.pseudos[ i ] = createButtonPseudo( i );
}
// Easy API for creating new setFilters
function setFilters() {}
setFilters.prototype = Expr.filters = Expr.pseudos;
Expr.setFilters = new setFilters();
tokenize = Sizzle.tokenize = function( selector, parseOnly ) {
var matched, match, tokens, type,
soFar, groups, preFilters,
cached = tokenCache[ selector + " " ];
if ( cached ) {
return parseOnly ? 0 : cached.slice( 0 );
}
soFar = selector;
groups = [];
preFilters = Expr.preFilter;
while ( soFar ) {
// Comma and first run
if ( !matched || ( match = rcomma.exec( soFar ) ) ) {
if ( match ) {
// Don't consume trailing commas as valid
soFar = soFar.slice( match[ 0 ].length ) || soFar;
}
groups.push( ( tokens = [] ) );
}
matched = false;
// Combinators
if ( ( match = rcombinators.exec( soFar ) ) ) {
matched = match.shift();
tokens.push( {
value: matched,
// Cast descendant combinators to space
type: match[ 0 ].replace( rtrim, " " )
} );
soFar = soFar.slice( matched.length );
}
// Filters
for ( type in Expr.filter ) {
if ( ( match = matchExpr[ type ].exec( soFar ) ) && ( !preFilters[ type ] ||
( match = preFilters[ type ]( match ) ) ) ) {
matched = match.shift();
tokens.push( {
value: matched,
type: type,
matches: match
} );
soFar = soFar.slice( matched.length );
}
}
if ( !matched ) {
break;
}
}
// Return the length of the invalid excess
// if we're just parsing
// Otherwise, throw an error or return tokens
return parseOnly ?
soFar.length :
soFar ?
Sizzle.error( selector ) :
// Cache the tokens
tokenCache( selector, groups ).slice( 0 );
};
function toSelector( tokens ) {
var i = 0,
len = tokens.length,
selector = "";
for ( ; i < len; i++ ) {
selector += tokens[ i ].value;
}
return selector;
}
function addCombinator( matcher, combinator, base ) {
var dir = combinator.dir,
skip = combinator.next,
key = skip || dir,
checkNonElements = base && key === "parentNode",
doneName = done++;
return combinator.first ?
// Check against closest ancestor/preceding element
function( elem, context, xml ) {
while ( ( elem = elem[ dir ] ) ) {
if ( elem.nodeType === 1 || checkNonElements ) {
return matcher( elem, context, xml );
}
}
return false;
} :
// Check against all ancestor/preceding elements
function( elem, context, xml ) {
var oldCache, uniqueCache, outerCache,
newCache = [ dirruns, doneName ];
// We can't set arbitrary data on XML nodes, so they don't benefit from combinator caching
if ( xml ) {
while ( ( elem = elem[ dir ] ) ) {
if ( elem.nodeType === 1 || checkNonElements ) {
if ( matcher( elem, context, xml ) ) {
return true;
}
}
}
} else {
while ( ( elem = elem[ dir ] ) ) {
if ( elem.nodeType === 1 || checkNonElements ) {
outerCache = elem[ expando ] || ( elem[ expando ] = {} );
// Support: IE <9 only
// Defend against cloned attroperties (jQuery gh-1709)
uniqueCache = outerCache[ elem.uniqueID ] ||
( outerCache[ elem.uniqueID ] = {} );
if ( skip && skip === elem.nodeName.toLowerCase() ) {
elem = elem[ dir ] || elem;
} else if ( ( oldCache = uniqueCache[ key ] ) &&
oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) {
// Assign to newCache so results back-propagate to previous elements
return ( newCache[ 2 ] = oldCache[ 2 ] );
} else {
// Reuse newcache so results back-propagate to previous elements
uniqueCache[ key ] = newCache;
// A match means we're done; a fail means we have to keep checking
if ( ( newCache[ 2 ] = matcher( elem, context, xml ) ) ) {
return true;
}
}
}
}
}
return false;
};
}
function elementMatcher( matchers ) {
return matchers.length > 1 ?
function( elem, context, xml ) {
var i = matchers.length;
while ( i-- ) {
if ( !matchers[ i ]( elem, context, xml ) ) {
return false;
}
}
return true;
} :
matchers[ 0 ];
}
function multipleContexts( selector, contexts, results ) {
var i = 0,
len = contexts.length;
for ( ; i < len; i++ ) {
Sizzle( selector, contexts[ i ], results );
}
return results;
}
function condense( unmatched, map, filter, context, xml ) {
var elem,
newUnmatched = [],
i = 0,
len = unmatched.length,
mapped = map != null;
for ( ; i < len; i++ ) {
if ( ( elem = unmatched[ i ] ) ) {
if ( !filter || filter( elem, context, xml ) ) {
newUnmatched.push( elem );
if ( mapped ) {
map.push( i );
}
}
}
}
return newUnmatched;
}
function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) {
if ( postFilter && !postFilter[ expando ] ) {
postFilter = setMatcher( postFilter );
}
if ( postFinder && !postFinder[ expando ] ) {
postFinder = setMatcher( postFinder, postSelector );
}
return markFunction( function( seed, results, context, xml ) {
var temp, i, elem,
preMap = [],
postMap = [],
preexisting = results.length,
// Get initial elements from seed or context
elems = seed || multipleContexts(
selector || "*",
context.nodeType ? [ context ] : context,
[]
),
// Prefilter to get matcher input, preserving a map for seed-results synchronization
matcherIn = preFilter && ( seed || !selector ) ?
condense( elems, preMap, preFilter, context, xml ) :
elems,
matcherOut = matcher ?
// If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results,
postFinder || ( seed ? preFilter : preexisting || postFilter ) ?
// ...intermediate processing is necessary
[] :
// ...otherwise use results directly
results :
matcherIn;
// Find primary matches
if ( matcher ) {
matcher( matcherIn, matcherOut, context, xml );
}
// Apply postFilter
if ( postFilter ) {
temp = condense( matcherOut, postMap );
postFilter( temp, [], context, xml );
// Un-match failing elements by moving them back to matcherIn
i = temp.length;
while ( i-- ) {
if ( ( elem = temp[ i ] ) ) {
matcherOut[ postMap[ i ] ] = !( matcherIn[ postMap[ i ] ] = elem );
}
}
}
if ( seed ) {
if ( postFinder || preFilter ) {
if ( postFinder ) {
// Get the final matcherOut by condensing this intermediate into postFinder contexts
temp = [];
i = matcherOut.length;
while ( i-- ) {
if ( ( elem = matcherOut[ i ] ) ) {
// Restore matcherIn since elem is not yet a final match
temp.push( ( matcherIn[ i ] = elem ) );
}
}
postFinder( null, ( matcherOut = [] ), temp, xml );
}
// Move matched elements from seed to results to keep them synchronized
i = matcherOut.length;
while ( i-- ) {
if ( ( elem = matcherOut[ i ] ) &&
( temp = postFinder ? indexOf( seed, elem ) : preMap[ i ] ) > -1 ) {
seed[ temp ] = !( results[ temp ] = elem );
}
}
}
// Add elements to results, through postFinder if defined
} else {
matcherOut = condense(
matcherOut === results ?
matcherOut.splice( preexisting, matcherOut.length ) :
matcherOut
);
if ( postFinder ) {
postFinder( null, results, matcherOut, xml );
} else {
push.apply( results, matcherOut );
}
}
} );
}
function matcherFromTokens( tokens ) {
var checkContext, matcher, j,
len = tokens.length,
leadingRelative = Expr.relative[ tokens[ 0 ].type ],
implicitRelative = leadingRelative || Expr.relative[ " " ],
i = leadingRelative ? 1 : 0,
// The foundational matcher ensures that elements are reachable from top-level context(s)
matchContext = addCombinator( function( elem ) {
return elem === checkContext;
}, implicitRelative, true ),
matchAnyContext = addCombinator( function( elem ) {
return indexOf( checkContext, elem ) > -1;
}, implicitRelative, true ),
matchers = [ function( elem, context, xml ) {
var ret = ( !leadingRelative && ( xml || context !== outermostContext ) ) || (
( checkContext = context ).nodeType ?
matchContext( elem, context, xml ) :
matchAnyContext( elem, context, xml ) );
// Avoid hanging onto element (issue #299)
checkContext = null;
return ret;
} ];
for ( ; i < len; i++ ) {
if ( ( matcher = Expr.relative[ tokens[ i ].type ] ) ) {
matchers = [ addCombinator( elementMatcher( matchers ), matcher ) ];
} else {
matcher = Expr.filter[ tokens[ i ].type ].apply( null, tokens[ i ].matches );
// Return special upon seeing a positional matcher
if ( matcher[ expando ] ) {
// Find the next relative operator (if any) for proper handling
j = ++i;
for ( ; j < len; j++ ) {
if ( Expr.relative[ tokens[ j ].type ] ) {
break;
}
}
return setMatcher(
i > 1 && elementMatcher( matchers ),
i > 1 && toSelector(
// If the preceding token was a descendant combinator, insert an implicit any-element `*`
tokens
.slice( 0, i - 1 )
.concat( { value: tokens[ i - 2 ].type === " " ? "*" : "" } )
).replace( rtrim, "$1" ),
matcher,
i < j && matcherFromTokens( tokens.slice( i, j ) ),
j < len && matcherFromTokens( ( tokens = tokens.slice( j ) ) ),
j < len && toSelector( tokens )
);
}
matchers.push( matcher );
}
}
return elementMatcher( matchers );
}
function matcherFromGroupMatchers( elementMatchers, setMatchers ) {
var bySet = setMatchers.length > 0,
byElement = elementMatchers.length > 0,
superMatcher = function( seed, context, xml, results, outermost ) {
var elem, j, matcher,
matchedCount = 0,
i = "0",
unmatched = seed && [],
setMatched = [],
contextBackup = outermostContext,
// We must always have either seed elements or outermost context
elems = seed || byElement && Expr.find[ "TAG" ]( "*", outermost ),
// Use integer dirruns iff this is the outermost matcher
dirrunsUnique = ( dirruns += contextBackup == null ? 1 : Math.random() || 0.1 ),
len = elems.length;
if ( outermost ) {
// Support: IE 11+, Edge 17 - 18+
// IE/Edge sometimes throw a "Permission denied" error when strict-comparing
// two documents; shallow comparisons work.
// eslint-disable-next-line eqeqeq
outermostContext = context == document || context || outermost;
}
// Add elements passing elementMatchers directly to results
// Support: IE<9, Safari
// Tolerate NodeList properties (IE: "length"; Safari: <number>) matching elements by id
for ( ; i !== len && ( elem = elems[ i ] ) != null; i++ ) {
if ( byElement && elem ) {
j = 0;
// Support: IE 11+, Edge 17 - 18+
// IE/Edge sometimes throw a "Permission denied" error when strict-comparing
// two documents; shallow comparisons work.
// eslint-disable-next-line eqeqeq
if ( !context && elem.ownerDocument != document ) {
setDocument( elem );
xml = !documentIsHTML;
}
while ( ( matcher = elementMatchers[ j++ ] ) ) {
if ( matcher( elem, context || document, xml ) ) {
results.push( elem );
break;
}
}
if ( outermost ) {
dirruns = dirrunsUnique;
}
}
// Track unmatched elements for set filters
if ( bySet ) {
// They will have gone through all possible matchers
if ( ( elem = !matcher && elem ) ) {
matchedCount--;
}
// Lengthen the array for every element, matched or not
if ( seed ) {
unmatched.push( elem );
}
}
}
// `i` is now the count of elements visited above, and adding it to `matchedCount`
// makes the latter nonnegative.
matchedCount += i;
// Apply set filters to unmatched elements
// NOTE: This can be skipped if there are no unmatched elements (i.e., `matchedCount`
// equals `i`), unless we didn't visit _any_ elements in the above loop because we have
// no element matchers and no seed.
// Incrementing an initially-string "0" `i` allows `i` to remain a string only in that
// case, which will result in a "00" `matchedCount` that differs from `i` but is also
// numerically zero.
if ( bySet && i !== matchedCount ) {
j = 0;
while ( ( matcher = setMatchers[ j++ ] ) ) {
matcher( unmatched, setMatched, context, xml );
}
if ( seed ) {
// Reintegrate element matches to eliminate the need for sorting
if ( matchedCount > 0 ) {
while ( i-- ) {
if ( !( unmatched[ i ] || setMatched[ i ] ) ) {
setMatched[ i ] = pop.call( results );
}
}
}
// Discard index placeholder values to get only actual matches
setMatched = condense( setMatched );
}
// Add matches to results
push.apply( results, setMatched );
// Seedless set matches succeeding multiple successful matchers stipulate sorting
if ( outermost && !seed && setMatched.length > 0 &&
( matchedCount + setMatchers.length ) > 1 ) {
Sizzle.uniqueSort( results );
}
}
// Override manipulation of globals by nested matchers
if ( outermost ) {
dirruns = dirrunsUnique;
outermostContext = contextBackup;
}
return unmatched;
};
return bySet ?
markFunction( superMatcher ) :
superMatcher;
}
compile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) {
var i,
setMatchers = [],
elementMatchers = [],
cached = compilerCache[ selector + " " ];
if ( !cached ) {
// Generate a function of recursive functions that can be used to check each element
if ( !match ) {
match = tokenize( selector );
}
i = match.length;
while ( i-- ) {
cached = matcherFromTokens( match[ i ] );
if ( cached[ expando ] ) {
setMatchers.push( cached );
} else {
elementMatchers.push( cached );
}
}
// Cache the compiled function
cached = compilerCache(
selector,
matcherFromGroupMatchers( elementMatchers, setMatchers )
);
// Save selector and tokenization
cached.selector = selector;
}
return cached;
};
/**
* A low-level selection function that works with Sizzle's compiled
* selector functions
* @param {String|Function} selector A selector or a pre-compiled
* selector function built with Sizzle.compile
* @param {Element} context
* @param {Array} [results]
* @param {Array} [seed] A set of elements to match against
*/
select = Sizzle.select = function( selector, context, results, seed ) {
var i, tokens, token, type, find,
compiled = typeof selector === "function" && selector,
match = !seed && tokenize( ( selector = compiled.selector || selector ) );
results = results || [];
// Try to minimize operations if there is only one selector in the list and no seed
// (the latter of which guarantees us context)
if ( match.length === 1 ) {
// Reduce context if the leading compound selector is an ID
tokens = match[ 0 ] = match[ 0 ].slice( 0 );
if ( tokens.length > 2 && ( token = tokens[ 0 ] ).type === "ID" &&
context.nodeType === 9 && documentIsHTML && Expr.relative[ tokens[ 1 ].type ] ) {
context = ( Expr.find[ "ID" ]( token.matches[ 0 ]
.replace( runescape, funescape ), context ) || [] )[ 0 ];
if ( !context ) {
return results;
// Precompiled matchers will still verify ancestry, so step up a level
} else if ( compiled ) {
context = context.parentNode;
}
selector = selector.slice( tokens.shift().value.length );
}
// Fetch a seed set for right-to-left matching
i = matchExpr[ "needsContext" ].test( selector ) ? 0 : tokens.length;
while ( i-- ) {
token = tokens[ i ];
// Abort if we hit a combinator
if ( Expr.relative[ ( type = token.type ) ] ) {
break;
}
if ( ( find = Expr.find[ type ] ) ) {
// Search, expanding context for leading sibling combinators
if ( ( seed = find(
token.matches[ 0 ].replace( runescape, funescape ),
rsibling.test( tokens[ 0 ].type ) && testContext( context.parentNode ) ||
context
) ) ) {
// If seed is empty or no tokens remain, we can return early
tokens.splice( i, 1 );
selector = seed.length && toSelector( tokens );
if ( !selector ) {
push.apply( results, seed );
return results;
}
break;
}
}
}
}
// Compile and execute a filtering function if one is not provided
// Provide `match` to avoid retokenization if we modified the selector above
( compiled || compile( selector, match ) )(
seed,
context,
!documentIsHTML,
results,
!context || rsibling.test( selector ) && testContext( context.parentNode ) || context
);
return results;
};
// One-time assignments
// Sort stability
support.sortStable = expando.split( "" ).sort( sortOrder ).join( "" ) === expando;
// Support: Chrome 14-35+
// Always assume duplicates if they aren't passed to the comparison function
support.detectDuplicates = !!hasDuplicate;
// Initialize against the default document
setDocument();
// Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27)
// Detached nodes confoundingly follow *each other*
support.sortDetached = assert( function( el ) {
// Should return 1, but returns 4 (following)
return el.compareDocumentPosition( document.createElement( "fieldset" ) ) & 1;
} );
// Support: IE<8
// Prevent attribute/property "interpolation"
// https://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx
if ( !assert( function( el ) {
el.innerHTML = "<a href='#'></a>";
return el.firstChild.getAttribute( "href" ) === "#";
} ) ) {
addHandle( "type|href|height|width", function( elem, name, isXML ) {
if ( !isXML ) {
return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 );
}
} );
}
// Support: IE<9
// Use defaultValue in place of getAttribute("value")
if ( !support.attributes || !assert( function( el ) {
el.innerHTML = "<input/>";
el.firstChild.setAttribute( "value", "" );
return el.firstChild.getAttribute( "value" ) === "";
} ) ) {
addHandle( "value", function( elem, _name, isXML ) {
if ( !isXML && elem.nodeName.toLowerCase() === "input" ) {
return elem.defaultValue;
}
} );
}
// Support: IE<9
// Use getAttributeNode to fetch booleans when getAttribute lies
if ( !assert( function( el ) {
return el.getAttribute( "disabled" ) == null;
} ) ) {
addHandle( booleans, function( elem, name, isXML ) {
var val;
if ( !isXML ) {
return elem[ name ] === true ? name.toLowerCase() :
( val = elem.getAttributeNode( name ) ) && val.specified ?
val.value :
null;
}
} );
}
return Sizzle;
} )( window );
jQuery.find = Sizzle;
jQuery.expr = Sizzle.selectors;
// Deprecated
jQuery.expr[ ":" ] = jQuery.expr.pseudos;
jQuery.uniqueSort = jQuery.unique = Sizzle.uniqueSort;
jQuery.text = Sizzle.getText;
jQuery.isXMLDoc = Sizzle.isXML;
jQuery.contains = Sizzle.contains;
jQuery.escapeSelector = Sizzle.escape;
var dir = function( elem, dir, until ) {
var matched = [],
truncate = until !== undefined;
while ( ( elem = elem[ dir ] ) && elem.nodeType !== 9 ) {
if ( elem.nodeType === 1 ) {
if ( truncate && jQuery( elem ).is( until ) ) {
break;
}
matched.push( elem );
}
}
return matched;
};
var siblings = function( n, elem ) {
var matched = [];
for ( ; n; n = n.nextSibling ) {
if ( n.nodeType === 1 && n !== elem ) {
matched.push( n );
}
}
return matched;
};
var rneedsContext = jQuery.expr.match.needsContext;
function nodeName( elem, name ) {
return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase();
};
var rsingleTag = ( /^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i );
// Implement the identical functionality for filter and not
function winnow( elements, qualifier, not ) {
if ( isFunction( qualifier ) ) {
return jQuery.grep( elements, function( elem, i ) {
return !!qualifier.call( elem, i, elem ) !== not;
} );
}
// Single element
if ( qualifier.nodeType ) {
return jQuery.grep( elements, function( elem ) {
return ( elem === qualifier ) !== not;
} );
}
// Arraylike of elements (jQuery, arguments, Array)
if ( typeof qualifier !== "string" ) {
return jQuery.grep( elements, function( elem ) {
return ( indexOf.call( qualifier, elem ) > -1 ) !== not;
} );
}
// Filtered directly for both simple and complex selectors
return jQuery.filter( qualifier, elements, not );
}
jQuery.filter = function( expr, elems, not ) {
var elem = elems[ 0 ];
if ( not ) {
expr = ":not(" + expr + ")";
}
if ( elems.length === 1 && elem.nodeType === 1 ) {
return jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : [];
}
return jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) {
return elem.nodeType === 1;
} ) );
};
jQuery.fn.extend( {
find: function( selector ) {
var i, ret,
len = this.length,
self = this;
if ( typeof selector !== "string" ) {
return this.pushStack( jQuery( selector ).filter( function() {
for ( i = 0; i < len; i++ ) {
if ( jQuery.contains( self[ i ], this ) ) {
return true;
}
}
} ) );
}
ret = this.pushStack( [] );
for ( i = 0; i < len; i++ ) {
jQuery.find( selector, self[ i ], ret );
}
return len > 1 ? jQuery.uniqueSort( ret ) : ret;
},
filter: function( selector ) {
return this.pushStack( winnow( this, selector || [], false ) );
},
not: function( selector ) {
return this.pushStack( winnow( this, selector || [], true ) );
},
is: function( selector ) {
return !!winnow(
this,
// If this is a positional/relative selector, check membership in the returned set
// so $("p:first").is("p:last") won't return true for a doc with two "p".
typeof selector === "string" && rneedsContext.test( selector ) ?
jQuery( selector ) :
selector || [],
false
).length;
}
} );
// Initialize a jQuery object
// A central reference to the root jQuery(document)
var rootjQuery,
// A simple way to check for HTML strings
// Prioritize #id over <tag> to avoid XSS via location.hash (#9521)
// Strict HTML recognition (#11290: must start with <)
// Shortcut simple #id case for speed
rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/,
init = jQuery.fn.init = function( selector, context, root ) {
var match, elem;
// HANDLE: $(""), $(null), $(undefined), $(false)
if ( !selector ) {
return this;
}
// Method init() accepts an alternate rootjQuery
// so migrate can support jQuery.sub (gh-2101)
root = root || rootjQuery;
// Handle HTML strings
if ( typeof selector === "string" ) {
if ( selector[ 0 ] === "<" &&
selector[ selector.length - 1 ] === ">" &&
selector.length >= 3 ) {
// Assume that strings that start and end with <> are HTML and skip the regex check
match = [ null, selector, null ];
} else {
match = rquickExpr.exec( selector );
}
// Match html or make sure no context is specified for #id
if ( match && ( match[ 1 ] || !context ) ) {
// HANDLE: $(html) -> $(array)
if ( match[ 1 ] ) {
context = context instanceof jQuery ? context[ 0 ] : context;
// Option to run scripts is true for back-compat
// Intentionally let the error be thrown if parseHTML is not present
jQuery.merge( this, jQuery.parseHTML(
match[ 1 ],
context && context.nodeType ? context.ownerDocument || context : document,
true
) );
// HANDLE: $(html, props)
if ( rsingleTag.test( match[ 1 ] ) && jQuery.isPlainObject( context ) ) {
for ( match in context ) {
// Properties of context are called as methods if possible
if ( isFunction( this[ match ] ) ) {
this[ match ]( context[ match ] );
// ...and otherwise set as attributes
} else {
this.attr( match, context[ match ] );
}
}
}
return this;
// HANDLE: $(#id)
} else {
elem = document.getElementById( match[ 2 ] );
if ( elem ) {
// Inject the element directly into the jQuery object
this[ 0 ] = elem;
this.length = 1;
}
return this;
}
// HANDLE: $(expr, $(...))
} else if ( !context || context.jquery ) {
return ( context || root ).find( selector );
// HANDLE: $(expr, context)
// (which is just equivalent to: $(context).find(expr)
} else {
return this.constructor( context ).find( selector );
}
// HANDLE: $(DOMElement)
} else if ( selector.nodeType ) {
this[ 0 ] = selector;
this.length = 1;
return this;
// HANDLE: $(function)
// Shortcut for document ready
} else if ( isFunction( selector ) ) {
return root.ready !== undefined ?
root.ready( selector ) :
// Execute immediately if ready is not present
selector( jQuery );
}
return jQuery.makeArray( selector, this );
};
// Give the init function the jQuery prototype for later instantiation
init.prototype = jQuery.fn;
// Initialize central reference
rootjQuery = jQuery( document );
var rparentsprev = /^(?:parents|prev(?:Until|All))/,
// Methods guaranteed to produce a unique set when starting from a unique set
guaranteedUnique = {
children: true,
contents: true,
next: true,
prev: true
};
jQuery.fn.extend( {
has: function( target ) {
var targets = jQuery( target, this ),
l = targets.length;
return this.filter( function() {
var i = 0;
for ( ; i < l; i++ ) {
if ( jQuery.contains( this, targets[ i ] ) ) {
return true;
}
}
} );
},
closest: function( selectors, context ) {
var cur,
i = 0,
l = this.length,
matched = [],
targets = typeof selectors !== "string" && jQuery( selectors );
// Positional selectors never match, since there's no _selection_ context
if ( !rneedsContext.test( selectors ) ) {
for ( ; i < l; i++ ) {
for ( cur = this[ i ]; cur && cur !== context; cur = cur.parentNode ) {
// Always skip document fragments
if ( cur.nodeType < 11 && ( targets ?
targets.index( cur ) > -1 :
// Don't pass non-elements to Sizzle
cur.nodeType === 1 &&
jQuery.find.matchesSelector( cur, selectors ) ) ) {
matched.push( cur );
break;
}
}
}
}
return this.pushStack( matched.length > 1 ? jQuery.uniqueSort( matched ) : matched );
},
// Determine the position of an element within the set
index: function( elem ) {
// No argument, return index in parent
if ( !elem ) {
return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1;
}
// Index in selector
if ( typeof elem === "string" ) {
return indexOf.call( jQuery( elem ), this[ 0 ] );
}
// Locate the position of the desired element
return indexOf.call( this,
// If it receives a jQuery object, the first element is used
elem.jquery ? elem[ 0 ] : elem
);
},
add: function( selector, context ) {
return this.pushStack(
jQuery.uniqueSort(
jQuery.merge( this.get(), jQuery( selector, context ) )
)
);
},
addBack: function( selector ) {
return this.add( selector == null ?
this.prevObject : this.prevObject.filter( selector )
);
}
} );
function sibling( cur, dir ) {
while ( ( cur = cur[ dir ] ) && cur.nodeType !== 1 ) {}
return cur;
}
jQuery.each( {
parent: function( elem ) {
var parent = elem.parentNode;
return parent && parent.nodeType !== 11 ? parent : null;
},
parents: function( elem ) {
return dir( elem, "parentNode" );
},
parentsUntil: function( elem, _i, until ) {
return dir( elem, "parentNode", until );
},
next: function( elem ) {
return sibling( elem, "nextSibling" );
},
prev: function( elem ) {
return sibling( elem, "previousSibling" );
},
nextAll: function( elem ) {
return dir( elem, "nextSibling" );
},
prevAll: function( elem ) {
return dir( elem, "previousSibling" );
},
nextUntil: function( elem, _i, until ) {
return dir( elem, "nextSibling", until );
},
prevUntil: function( elem, _i, until ) {
return dir( elem, "previousSibling", until );
},
siblings: function( elem ) {
return siblings( ( elem.parentNode || {} ).firstChild, elem );
},
children: function( elem ) {
return siblings( elem.firstChild );
},
contents: function( elem ) {
if ( elem.contentDocument != null &&
// Support: IE 11+
// <object> elements with no `data` attribute has an object
// `contentDocument` with a `null` prototype.
getProto( elem.contentDocument ) ) {
return elem.contentDocument;
}
// Support: IE 9 - 11 only, iOS 7 only, Android Browser <=4.3 only
// Treat the template element as a regular one in browsers that
// don't support it.
if ( nodeName( elem, "template" ) ) {
elem = elem.content || elem;
}
return jQuery.merge( [], elem.childNodes );
}
}, function( name, fn ) {
jQuery.fn[ name ] = function( until, selector ) {
var matched = jQuery.map( this, fn, until );
if ( name.slice( -5 ) !== "Until" ) {
selector = until;
}
if ( selector && typeof selector === "string" ) {
matched = jQuery.filter( selector, matched );
}
if ( this.length > 1 ) {
// Remove duplicates
if ( !guaranteedUnique[ name ] ) {
jQuery.uniqueSort( matched );
}
// Reverse order for parents* and prev-derivatives
if ( rparentsprev.test( name ) ) {
matched.reverse();
}
}
return this.pushStack( matched );
};
} );
var rnothtmlwhite = ( /[^\x20\t\r\n\f]+/g );
// Convert String-formatted options into Object-formatted ones
function createOptions( options ) {
var object = {};
jQuery.each( options.match( rnothtmlwhite ) || [], function( _, flag ) {
object[ flag ] = true;
} );
return object;
}
/*
* Create a callback list using the following parameters:
*
* options: an optional list of space-separated options that will change how
* the callback list behaves or a more traditional option object
*
* By default a callback list will act like an event callback list and can be
* "fired" multiple times.
*
* Possible options:
*
* once: will ensure the callback list can only be fired once (like a Deferred)
*
* memory: will keep track of previous values and will call any callback added
* after the list has been fired right away with the latest "memorized"
* values (like a Deferred)
*
* unique: will ensure a callback can only be added once (no duplicate in the list)
*
* stopOnFalse: interrupt callings when a callback returns false
*
*/
jQuery.Callbacks = function( options ) {
// Convert options from String-formatted to Object-formatted if needed
// (we check in cache first)
options = typeof options === "string" ?
createOptions( options ) :
jQuery.extend( {}, options );
var // Flag to know if list is currently firing
firing,
// Last fire value for non-forgettable lists
memory,
// Flag to know if list was already fired
fired,
// Flag to prevent firing
locked,
// Actual callback list
list = [],
// Queue of execution data for repeatable lists
queue = [],
// Index of currently firing callback (modified by add/remove as needed)
firingIndex = -1,
// Fire callbacks
fire = function() {
// Enforce single-firing
locked = locked || options.once;
// Execute callbacks for all pending executions,
// respecting firingIndex overrides and runtime changes
fired = firing = true;
for ( ; queue.length; firingIndex = -1 ) {
memory = queue.shift();
while ( ++firingIndex < list.length ) {
// Run callback and check for early termination
if ( list[ firingIndex ].apply( memory[ 0 ], memory[ 1 ] ) === false &&
options.stopOnFalse ) {
// Jump to end and forget the data so .add doesn't re-fire
firingIndex = list.length;
memory = false;
}
}
}
// Forget the data if we're done with it
if ( !options.memory ) {
memory = false;
}
firing = false;
// Clean up if we're done firing for good
if ( locked ) {
// Keep an empty list if we have data for future add calls
if ( memory ) {
list = [];
// Otherwise, this object is spent
} else {
list = "";
}
}
},
// Actual Callbacks object
self = {
// Add a callback or a collection of callbacks to the list
add: function() {
if ( list ) {
// If we have memory from a past run, we should fire after adding
if ( memory && !firing ) {
firingIndex = list.length - 1;
queue.push( memory );
}
( function add( args ) {
jQuery.each( args, function( _, arg ) {
if ( isFunction( arg ) ) {
if ( !options.unique || !self.has( arg ) ) {
list.push( arg );
}
} else if ( arg && arg.length && toType( arg ) !== "string" ) {
// Inspect recursively
add( arg );
}
} );
} )( arguments );
if ( memory && !firing ) {
fire();
}
}
return this;
},
// Remove a callback from the list
remove: function() {
jQuery.each( arguments, function( _, arg ) {
var index;
while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {
list.splice( index, 1 );
// Handle firing indexes
if ( index <= firingIndex ) {
firingIndex--;
}
}
} );
return this;
},
// Check if a given callback is in the list.
// If no argument is given, return whether or not list has callbacks attached.
has: function( fn ) {
return fn ?
jQuery.inArray( fn, list ) > -1 :
list.length > 0;
},
// Remove all callbacks from the list
empty: function() {
if ( list ) {
list = [];
}
return this;
},
// Disable .fire and .add
// Abort any current/pending executions
// Clear all callbacks and values
disable: function() {
locked = queue = [];
list = memory = "";
return this;
},
disabled: function() {
return !list;
},
// Disable .fire
// Also disable .add unless we have memory (since it would have no effect)
// Abort any pending executions
lock: function() {
locked = queue = [];
if ( !memory && !firing ) {
list = memory = "";
}
return this;
},
locked: function() {
return !!locked;
},
// Call all callbacks with the given context and arguments
fireWith: function( context, args ) {
if ( !locked ) {
args = args || [];
args = [ context, args.slice ? args.slice() : args ];
queue.push( args );
if ( !firing ) {
fire();
}
}
return this;
},
// Call all the callbacks with the given arguments
fire: function() {
self.fireWith( this, arguments );
return this;
},
// To know if the callbacks have already been called at least once
fired: function() {
return !!fired;
}
};
return self;
};
function Identity( v ) {
return v;
}
function Thrower( ex ) {
throw ex;
}
function adoptValue( value, resolve, reject, noValue ) {
var method;
try {
// Check for promise aspect first to privilege synchronous behavior
if ( value && isFunction( ( method = value.promise ) ) ) {
method.call( value ).done( resolve ).fail( reject );
// Other thenables
} else if ( value && isFunction( ( method = value.then ) ) ) {
method.call( value, resolve, reject );
// Other non-thenables
} else {
// Control `resolve` arguments by letting Array#slice cast boolean `noValue` to integer:
// * false: [ value ].slice( 0 ) => resolve( value )
// * true: [ value ].slice( 1 ) => resolve()
resolve.apply( undefined, [ value ].slice( noValue ) );
}
// For Promises/A+, convert exceptions into rejections
// Since jQuery.when doesn't unwrap thenables, we can skip the extra checks appearing in
// Deferred#then to conditionally suppress rejection.
} catch ( value ) {
// Support: Android 4.0 only
// Strict mode functions invoked without .call/.apply get global-object context
reject.apply( undefined, [ value ] );
}
}
jQuery.extend( {
Deferred: function( func ) {
var tuples = [
// action, add listener, callbacks,
// ... .then handlers, argument index, [final state]
[ "notify", "progress", jQuery.Callbacks( "memory" ),
jQuery.Callbacks( "memory" ), 2 ],
[ "resolve", "done", jQuery.Callbacks( "once memory" ),
jQuery.Callbacks( "once memory" ), 0, "resolved" ],
[ "reject", "fail", jQuery.Callbacks( "once memory" ),
jQuery.Callbacks( "once memory" ), 1, "rejected" ]
],
state = "pending",
promise = {
state: function() {
return state;
},
always: function() {
deferred.done( arguments ).fail( arguments );
return this;
},
"catch": function( fn ) {
return promise.then( null, fn );
},
// Keep pipe for back-compat
pipe: function( /* fnDone, fnFail, fnProgress */ ) {
var fns = arguments;
return jQuery.Deferred( function( newDefer ) {
jQuery.each( tuples, function( _i, tuple ) {
// Map tuples (progress, done, fail) to arguments (done, fail, progress)
var fn = isFunction( fns[ tuple[ 4 ] ] ) && fns[ tuple[ 4 ] ];
// deferred.progress(function() { bind to newDefer or newDefer.notify })
// deferred.done(function() { bind to newDefer or newDefer.resolve })
// deferred.fail(function() { bind to newDefer or newDefer.reject })
deferred[ tuple[ 1 ] ]( function() {
var returned = fn && fn.apply( this, arguments );
if ( returned && isFunction( returned.promise ) ) {
returned.promise()
.progress( newDefer.notify )
.done( newDefer.resolve )
.fail( newDefer.reject );
} else {
newDefer[ tuple[ 0 ] + "With" ](
this,
fn ? [ returned ] : arguments
);
}
} );
} );
fns = null;
} ).promise();
},
then: function( onFulfilled, onRejected, onProgress ) {
var maxDepth = 0;
function resolve( depth, deferred, handler, special ) {
return function() {
var that = this,
args = arguments,
mightThrow = function() {
var returned, then;
// Support: Promises/A+ section 2.3.3.3.3
// https://promisesaplus.com/#point-59
// Ignore double-resolution attempts
if ( depth < maxDepth ) {
return;
}
returned = handler.apply( that, args );
// Support: Promises/A+ section 2.3.1
// https://promisesaplus.com/#point-48
if ( returned === deferred.promise() ) {
throw new TypeError( "Thenable self-resolution" );
}
// Support: Promises/A+ sections 2.3.3.1, 3.5
// https://promisesaplus.com/#point-54
// https://promisesaplus.com/#point-75
// Retrieve `then` only once
then = returned &&
// Support: Promises/A+ section 2.3.4
// https://promisesaplus.com/#point-64
// Only check objects and functions for thenability
( typeof returned === "object" ||
typeof returned === "function" ) &&
returned.then;
// Handle a returned thenable
if ( isFunction( then ) ) {
// Special processors (notify) just wait for resolution
if ( special ) {
then.call(
returned,
resolve( maxDepth, deferred, Identity, special ),
resolve( maxDepth, deferred, Thrower, special )
);
// Normal processors (resolve) also hook into progress
} else {
// ...and disregard older resolution values
maxDepth++;
then.call(
returned,
resolve( maxDepth, deferred, Identity, special ),
resolve( maxDepth, deferred, Thrower, special ),
resolve( maxDepth, deferred, Identity,
deferred.notifyWith )
);
}
// Handle all other returned values
} else {
// Only substitute handlers pass on context
// and multiple values (non-spec behavior)
if ( handler !== Identity ) {
that = undefined;
args = [ returned ];
}
// Process the value(s)
// Default process is resolve
( special || deferred.resolveWith )( that, args );
}
},
// Only normal processors (resolve) catch and reject exceptions
process = special ?
mightThrow :
function() {
try {
mightThrow();
} catch ( e ) {
if ( jQuery.Deferred.exceptionHook ) {
jQuery.Deferred.exceptionHook( e,
process.stackTrace );
}
// Support: Promises/A+ section 2.3.3.3.4.1
// https://promisesaplus.com/#point-61
// Ignore post-resolution exceptions
if ( depth + 1 >= maxDepth ) {
// Only substitute handlers pass on context
// and multiple values (non-spec behavior)
if ( handler !== Thrower ) {
that = undefined;
args = [ e ];
}
deferred.rejectWith( that, args );
}
}
};
// Support: Promises/A+ section 2.3.3.3.1
// https://promisesaplus.com/#point-57
// Re-resolve promises immediately to dodge false rejection from
// subsequent errors
if ( depth ) {
process();
} else {
// Call an optional hook to record the stack, in case of exception
// since it's otherwise lost when execution goes async
if ( jQuery.Deferred.getStackHook ) {
process.stackTrace = jQuery.Deferred.getStackHook();
}
window.setTimeout( process );
}
};
}
return jQuery.Deferred( function( newDefer ) {
// progress_handlers.add( ... )
tuples[ 0 ][ 3 ].add(
resolve(
0,
newDefer,
isFunction( onProgress ) ?
onProgress :
Identity,
newDefer.notifyWith
)
);
// fulfilled_handlers.add( ... )
tuples[ 1 ][ 3 ].add(
resolve(
0,
newDefer,
isFunction( onFulfilled ) ?
onFulfilled :
Identity
)
);
// rejected_handlers.add( ... )
tuples[ 2 ][ 3 ].add(
resolve(
0,
newDefer,
isFunction( onRejected ) ?
onRejected :
Thrower
)
);
} ).promise();
},
// Get a promise for this deferred
// If obj is provided, the promise aspect is added to the object
promise: function( obj ) {
return obj != null ? jQuery.extend( obj, promise ) : promise;
}
},
deferred = {};
// Add list-specific methods
jQuery.each( tuples, function( i, tuple ) {
var list = tuple[ 2 ],
stateString = tuple[ 5 ];
// promise.progress = list.add
// promise.done = list.add
// promise.fail = list.add
promise[ tuple[ 1 ] ] = list.add;
// Handle state
if ( stateString ) {
list.add(
function() {
// state = "resolved" (i.e., fulfilled)
// state = "rejected"
state = stateString;
},
// rejected_callbacks.disable
// fulfilled_callbacks.disable
tuples[ 3 - i ][ 2 ].disable,
// rejected_handlers.disable
// fulfilled_handlers.disable
tuples[ 3 - i ][ 3 ].disable,
// progress_callbacks.lock
tuples[ 0 ][ 2 ].lock,
// progress_handlers.lock
tuples[ 0 ][ 3 ].lock
);
}
// progress_handlers.fire
// fulfilled_handlers.fire
// rejected_handlers.fire
list.add( tuple[ 3 ].fire );
// deferred.notify = function() { deferred.notifyWith(...) }
// deferred.resolve = function() { deferred.resolveWith(...) }
// deferred.reject = function() { deferred.rejectWith(...) }
deferred[ tuple[ 0 ] ] = function() {
deferred[ tuple[ 0 ] + "With" ]( this === deferred ? undefined : this, arguments );
return this;
};
// deferred.notifyWith = list.fireWith
// deferred.resolveWith = list.fireWith
// deferred.rejectWith = list.fireWith
deferred[ tuple[ 0 ] + "With" ] = list.fireWith;
} );
// Make the deferred a promise
promise.promise( deferred );
// Call given func if any
if ( func ) {
func.call( deferred, deferred );
}
// All done!
return deferred;
},
// Deferred helper
when: function( singleValue ) {
var
// count of uncompleted subordinates
remaining = arguments.length,
// count of unprocessed arguments
i = remaining,
// subordinate fulfillment data
resolveContexts = Array( i ),
resolveValues = slice.call( arguments ),
// the master Deferred
master = jQuery.Deferred(),
// subordinate callback factory
updateFunc = function( i ) {
return function( value ) {
resolveContexts[ i ] = this;
resolveValues[ i ] = arguments.length > 1 ? slice.call( arguments ) : value;
if ( !( --remaining ) ) {
master.resolveWith( resolveContexts, resolveValues );
}
};
};
// Single- and empty arguments are adopted like Promise.resolve
if ( remaining <= 1 ) {
adoptValue( singleValue, master.done( updateFunc( i ) ).resolve, master.reject,
!remaining );
// Use .then() to unwrap secondary thenables (cf. gh-3000)
if ( master.state() === "pending" ||
isFunction( resolveValues[ i ] && resolveValues[ i ].then ) ) {
return master.then();
}
}
// Multiple arguments are aggregated like Promise.all array elements
while ( i-- ) {
adoptValue( resolveValues[ i ], updateFunc( i ), master.reject );
}
return master.promise();
}
} );
// These usually indicate a programmer mistake during development,
// warn about them ASAP rather than swallowing them by default.
var rerrorNames = /^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;
jQuery.Deferred.exceptionHook = function( error, stack ) {
// Support: IE 8 - 9 only
// Console exists when dev tools are open, which can happen at any time
if ( window.console && window.console.warn && error && rerrorNames.test( error.name ) ) {
window.console.warn( "jQuery.Deferred exception: " + error.message, error.stack, stack );
}
};
jQuery.readyException = function( error ) {
window.setTimeout( function() {
throw error;
} );
};
// The deferred used on DOM ready
var readyList = jQuery.Deferred();
jQuery.fn.ready = function( fn ) {
readyList
.then( fn )
// Wrap jQuery.readyException in a function so that the lookup
// happens at the time of error handling instead of callback
// registration.
.catch( function( error ) {
jQuery.readyException( error );
} );
return this;
};
jQuery.extend( {
// Is the DOM ready to be used? Set to true once it occurs.
isReady: false,
// A counter to track how many items to wait for before
// the ready event fires. See #6781
readyWait: 1,
// Handle when the DOM is ready
ready: function( wait ) {
// Abort if there are pending holds or we're already ready
if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) {
return;
}
// Remember that the DOM is ready
jQuery.isReady = true;
// If a normal DOM Ready event fired, decrement, and wait if need be
if ( wait !== true && --jQuery.readyWait > 0 ) {
return;
}
// If there are functions bound, to execute
readyList.resolveWith( document, [ jQuery ] );
}
} );
jQuery.ready.then = readyList.then;
// The ready event handler and self cleanup method
function completed() {
document.removeEventListener( "DOMContentLoaded", completed );
window.removeEventListener( "load", completed );
jQuery.ready();
}
// Catch cases where $(document).ready() is called
// after the browser event has already occurred.
// Support: IE <=9 - 10 only
// Older IE sometimes signals "interactive" too soon
if ( document.readyState === "complete" ||
( document.readyState !== "loading" && !document.documentElement.doScroll ) ) {
// Handle it asynchronously to allow scripts the opportunity to delay ready
window.setTimeout( jQuery.ready );
} else {
// Use the handy event callback
document.addEventListener( "DOMContentLoaded", completed );
// A fallback to window.onload, that will always work
window.addEventListener( "load", completed );
}
// Multifunctional method to get and set values of a collection
// The value/s can optionally be executed if it's a function
var access = function( elems, fn, key, value, chainable, emptyGet, raw ) {
var i = 0,
len = elems.length,
bulk = key == null;
// Sets many values
if ( toType( key ) === "object" ) {
chainable = true;
for ( i in key ) {
access( elems, fn, i, key[ i ], true, emptyGet, raw );
}
// Sets one value
} else if ( value !== undefined ) {
chainable = true;
if ( !isFunction( value ) ) {
raw = true;
}
if ( bulk ) {
// Bulk operations run against the entire set
if ( raw ) {
fn.call( elems, value );
fn = null;
// ...except when executing function values
} else {
bulk = fn;
fn = function( elem, _key, value ) {
return bulk.call( jQuery( elem ), value );
};
}
}
if ( fn ) {
for ( ; i < len; i++ ) {
fn(
elems[ i ], key, raw ?
value :
value.call( elems[ i ], i, fn( elems[ i ], key ) )
);
}
}
}
if ( chainable ) {
return elems;
}
// Gets
if ( bulk ) {
return fn.call( elems );
}
return len ? fn( elems[ 0 ], key ) : emptyGet;
};
// Matches dashed string for camelizing
var rmsPrefix = /^-ms-/,
rdashAlpha = /-([a-z])/g;
// Used by camelCase as callback to replace()
function fcamelCase( _all, letter ) {
return letter.toUpperCase();
}
// Convert dashed to camelCase; used by the css and data modules
// Support: IE <=9 - 11, Edge 12 - 15
// Microsoft forgot to hump their vendor prefix (#9572)
function camelCase( string ) {
return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase );
}
var acceptData = function( owner ) {
// Accepts only:
// - Node
// - Node.ELEMENT_NODE
// - Node.DOCUMENT_NODE
// - Object
// - Any
return owner.nodeType === 1 || o
gitextract_l0cgokhk/ ├── .coveragerc ├── .github/ │ └── workflows/ │ └── ci.yml ├── .gitignore ├── .isort.cfg ├── .readthedocs.yml ├── .tx/ │ └── config ├── CHANGELOG.txt ├── CONTRIBUTORS.txt ├── LICENSE.txt ├── MANIFEST.in ├── README.rst ├── SECURITY.md ├── docs/ │ ├── .gitignore │ ├── Makefile │ ├── conf.py │ ├── index.rst │ ├── make.bat │ ├── pages/ │ │ ├── changelog_page.rst │ │ ├── contributing.rst │ │ ├── customising/ │ │ │ ├── admin.rst │ │ │ ├── models.rst │ │ │ ├── settings.rst │ │ │ ├── templates.rst │ │ │ └── views.rst │ │ ├── installation.rst │ │ └── usage.rst │ └── requirements.txt ├── example_project/ │ ├── README.rst │ ├── example_project/ │ │ ├── __init__.py │ │ ├── example_storages/ │ │ │ ├── README.txt │ │ │ ├── __init__.py │ │ │ ├── s3_requirements.txt │ │ │ ├── s3utils.py │ │ │ └── settings_s3boto.py │ │ ├── fixtures/ │ │ │ └── .gitdirectory │ │ ├── settings.py │ │ ├── static/ │ │ │ ├── css/ │ │ │ │ └── styles.css │ │ │ └── js/ │ │ │ └── jquery-3.5.1.js │ │ ├── templates/ │ │ │ ├── base.html │ │ │ └── homepage.html │ │ ├── urls.py │ │ └── wsgi.py │ ├── manage.py │ ├── public/ │ │ ├── .gitdirectory │ │ ├── media/ │ │ │ └── .gitignore │ │ └── static/ │ │ └── .gitdirectory │ └── requirements.txt ├── photologue/ │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── forms.py │ ├── locale/ │ │ ├── ca/ │ │ │ └── LC_MESSAGES/ │ │ │ ├── django.mo │ │ │ └── django.po │ │ ├── cs/ │ │ │ └── LC_MESSAGES/ │ │ │ ├── django.mo │ │ │ └── django.po │ │ ├── da/ │ │ │ └── LC_MESSAGES/ │ │ │ ├── django.mo │ │ │ └── django.po │ │ ├── de/ │ │ │ └── LC_MESSAGES/ │ │ │ ├── django.mo │ │ │ └── django.po │ │ ├── en/ │ │ │ └── LC_MESSAGES/ │ │ │ ├── django.mo │ │ │ └── django.po │ │ ├── en_US/ │ │ │ └── LC_MESSAGES/ │ │ │ ├── django.mo │ │ │ └── django.po │ │ ├── es_ES/ │ │ │ └── LC_MESSAGES/ │ │ │ ├── django.mo │ │ │ └── django.po │ │ ├── eu/ │ │ │ └── LC_MESSAGES/ │ │ │ ├── django.mo │ │ │ └── django.po │ │ ├── fr/ │ │ │ └── LC_MESSAGES/ │ │ │ ├── django.mo │ │ │ └── django.po │ │ ├── hu/ │ │ │ └── LC_MESSAGES/ │ │ │ ├── django.mo │ │ │ └── django.po │ │ ├── it/ │ │ │ └── LC_MESSAGES/ │ │ │ ├── django.mo │ │ │ └── django.po │ │ ├── nl/ │ │ │ └── LC_MESSAGES/ │ │ │ ├── django.mo │ │ │ └── django.po │ │ ├── no/ │ │ │ └── LC_MESSAGES/ │ │ │ ├── django.mo │ │ │ └── django.po │ │ ├── pl/ │ │ │ └── LC_MESSAGES/ │ │ │ ├── django.mo │ │ │ └── django.po │ │ ├── pt/ │ │ │ └── LC_MESSAGES/ │ │ │ ├── django.mo │ │ │ └── django.po │ │ ├── pt_BR/ │ │ │ └── LC_MESSAGES/ │ │ │ ├── django.mo │ │ │ └── django.po │ │ ├── ru/ │ │ │ └── LC_MESSAGES/ │ │ │ ├── django.mo │ │ │ └── django.po │ │ ├── sk/ │ │ │ └── LC_MESSAGES/ │ │ │ ├── django.mo │ │ │ └── django.po │ │ ├── tr/ │ │ │ └── LC_MESSAGES/ │ │ │ ├── django.mo │ │ │ └── django.po │ │ ├── tr_TR/ │ │ │ └── LC_MESSAGES/ │ │ │ ├── django.mo │ │ │ └── django.po │ │ ├── uk/ │ │ │ └── LC_MESSAGES/ │ │ │ ├── django.mo │ │ │ └── django.po │ │ └── zh_Hans/ │ │ └── LC_MESSAGES/ │ │ ├── django.mo │ │ └── django.po │ ├── management/ │ │ ├── __init__.py │ │ └── commands/ │ │ ├── __init__.py │ │ ├── plcache.py │ │ ├── plcreatesize.py │ │ └── plflush.py │ ├── managers.py │ ├── migrations/ │ │ ├── 0001_initial.py │ │ ├── 0002_photosize_data.py │ │ ├── 0003_auto_20140822_1716.py │ │ ├── 0004_auto_20140915_1259.py │ │ ├── 0005_auto_20141027_1552.py │ │ ├── 0006_auto_20141028_2005.py │ │ ├── 0007_auto_20150404_1737.py │ │ ├── 0008_auto_20150509_1557.py │ │ ├── 0009_auto_20160102_0904.py │ │ ├── 0010_auto_20160105_1307.py │ │ ├── 0011_auto_20190223_2138.py │ │ ├── 0012_alter_photo_effect.py │ │ ├── 0013_alter_watermark_image.py │ │ └── __init__.py │ ├── models.py │ ├── sitemaps.py │ ├── templates/ │ │ ├── admin/ │ │ │ └── photologue/ │ │ │ └── photo/ │ │ │ ├── change_list.html │ │ │ └── upload_zip.html │ │ └── photologue/ │ │ ├── gallery_archive.html │ │ ├── gallery_archive_day.html │ │ ├── gallery_archive_month.html │ │ ├── gallery_archive_year.html │ │ ├── gallery_detail.html │ │ ├── gallery_list.html │ │ ├── includes/ │ │ │ ├── gallery_sample.html │ │ │ └── paginator.html │ │ ├── photo_archive.html │ │ ├── photo_archive_day.html │ │ ├── photo_archive_month.html │ │ ├── photo_archive_year.html │ │ ├── photo_detail.html │ │ ├── photo_list.html │ │ ├── root.html │ │ └── tags/ │ │ ├── next_in_gallery.html │ │ └── prev_in_gallery.html │ ├── templatetags/ │ │ ├── __init__.py │ │ └── photologue_tags.py │ ├── tests/ │ │ ├── __init__.py │ │ ├── factories.py │ │ ├── helpers.py │ │ ├── templates/ │ │ │ └── base.html │ │ ├── test_effect.py │ │ ├── test_gallery.py │ │ ├── test_photo.py │ │ ├── test_photosize.py │ │ ├── test_resize.py │ │ ├── test_sitemap.py │ │ ├── test_sites.py │ │ ├── test_urls.py │ │ ├── test_views_gallery.py │ │ ├── test_views_photo.py │ │ └── test_zipupload.py │ ├── urls.py │ ├── utils/ │ │ ├── __init__.py │ │ ├── reflection.py │ │ └── watermark.py │ └── views.py ├── requirements.txt ├── scripts/ │ ├── __init__.py │ └── releaser_hooks.py ├── setup.cfg ├── setup.py └── tox.ini
SYMBOL INDEX (402 symbols across 42 files)
FILE: example_project/example_project/static/js/jquery-3.5.1.js
function DOMEval (line 103) | function DOMEval( code, node, doc ) {
function toType (line 133) | function toType( obj ) {
function isArrayLike (line 503) | function isArrayLike( obj ) {
function Sizzle (line 755) | function Sizzle( selector, context, results, seed ) {
function createCache (line 903) | function createCache() {
function markFunction (line 923) | function markFunction( fn ) {
function assert (line 932) | function assert( fn ) {
function addHandle (line 956) | function addHandle( attrs, handler ) {
function siblingCheck (line 971) | function siblingCheck( a, b ) {
function createInputPseudo (line 997) | function createInputPseudo( type ) {
function createButtonPseudo (line 1008) | function createButtonPseudo( type ) {
function createDisabledPseudo (line 1019) | function createDisabledPseudo( disabled ) {
function createPositionalPseudo (line 1075) | function createPositionalPseudo( fn ) {
function testContext (line 1098) | function testContext( context ) {
function setFilters (line 2309) | function setFilters() {}
function toSelector (line 2383) | function toSelector( tokens ) {
function addCombinator (line 2393) | function addCombinator( matcher, combinator, base ) {
function elementMatcher (line 2460) | function elementMatcher( matchers ) {
function multipleContexts (line 2474) | function multipleContexts( selector, contexts, results ) {
function condense (line 2483) | function condense( unmatched, map, filter, context, xml ) {
function setMatcher (line 2504) | function setMatcher( preFilter, selector, matcher, postFilter, postFinde...
function matcherFromTokens (line 2604) | function matcherFromTokens( tokens ) {
function matcherFromGroupMatchers (line 2667) | function matcherFromGroupMatchers( elementMatchers, setMatchers ) {
function nodeName (line 3025) | function nodeName( elem, name ) {
function winnow (line 3035) | function winnow( elements, qualifier, not ) {
function sibling (line 3330) | function sibling( cur, dir ) {
function createOptions (line 3423) | function createOptions( options ) {
function Identity (line 3648) | function Identity( v ) {
function Thrower (line 3651) | function Thrower( ex ) {
function adoptValue (line 3655) | function adoptValue( value, resolve, reject, noValue ) {
function resolve (line 3748) | function resolve( depth, deferred, handler, special ) {
function completed (line 4113) | function completed() {
function fcamelCase (line 4208) | function fcamelCase( _all, letter ) {
function camelCase (line 4215) | function camelCase( string ) {
function Data (line 4232) | function Data() {
function getData (line 4401) | function getData( data ) {
function dataAttr (line 4426) | function dataAttr( elem, key, data ) {
function adjustCSS (line 4738) | function adjustCSS( elem, prop, valueParts, tween ) {
function getDefaultDisplay (line 4806) | function getDefaultDisplay( elem ) {
function showHide (line 4829) | function showHide( elements, show ) {
function getAll (line 4961) | function getAll( context, tag ) {
function setGlobalEval (line 4986) | function setGlobalEval( elems, refElements ) {
function buildFragment (line 5002) | function buildFragment( elems, context, scripts, selection, ignored ) {
function returnTrue (line 5097) | function returnTrue() {
function returnFalse (line 5101) | function returnFalse() {
function expectSync (line 5111) | function expectSync( elem, type ) {
function safeActiveElement (line 5118) | function safeActiveElement() {
function on (line 5124) | function on( elem, types, selector, data, fn, one ) {
function leverageNative (line 5612) | function leverageNative( el, type, expectSync ) {
function manipulationTarget (line 5976) | function manipulationTarget( elem, content ) {
function disableScript (line 5987) | function disableScript( elem ) {
function restoreScript (line 5991) | function restoreScript( elem ) {
function cloneCopyEvent (line 6001) | function cloneCopyEvent( src, dest ) {
function fixInput (line 6034) | function fixInput( src, dest ) {
function domManip (line 6047) | function domManip( collection, args, callback, ignored ) {
function remove (line 6139) | function remove( elem, selector, keepData ) {
function computeStyleTests (line 6453) | function computeStyleTests() {
function roundPixelMeasures (line 6497) | function roundPixelMeasures( measure ) {
function curCSS (line 6571) | function curCSS( elem, name, computed ) {
function addGetHookIf (line 6624) | function addGetHookIf( conditionFn, hookFn ) {
function vendorPropName (line 6649) | function vendorPropName( name ) {
function finalPropName (line 6664) | function finalPropName( name ) {
function setPositiveNumber (line 6690) | function setPositiveNumber( _elem, value, subtract ) {
function boxModelAdjustment (line 6702) | function boxModelAdjustment( elem, dimension, box, isBorderBox, styles, ...
function getWidthOrHeight (line 6770) | function getWidthOrHeight( elem, dimension, extra ) {
function Tween (line 7146) | function Tween( elem, options, prop, end, easing ) {
function schedule (line 7269) | function schedule() {
function createFxNow (line 7282) | function createFxNow() {
function genFx (line 7290) | function genFx( type, includeWidth ) {
function createTween (line 7310) | function createTween( value, prop, animation ) {
function defaultPrefilter (line 7324) | function defaultPrefilter( elem, props, opts ) {
function propFilter (line 7496) | function propFilter( props, specialEasing ) {
function Animation (line 7533) | function Animation( elem, properties, options ) {
function stripAndCollapse (line 8248) | function stripAndCollapse( value ) {
function getClass (line 8254) | function getClass( elem ) {
function classesToArray (line 8258) | function classesToArray( value ) {
function buildParams (line 8885) | function buildParams( prefix, obj, traditional, add ) {
function addToPrefiltersOrTransports (line 9039) | function addToPrefiltersOrTransports( structure ) {
function inspectPrefiltersOrTransports (line 9073) | function inspectPrefiltersOrTransports( structure, options, originalOpti...
function ajaxExtend (line 9102) | function ajaxExtend( target, src ) {
function ajaxHandleResponses (line 9122) | function ajaxHandleResponses( s, jqXHR, responses ) {
function ajaxConvert (line 9180) | function ajaxConvert( s, response, jqXHR, isSuccess ) {
function done (line 9696) | function done( status, nativeStatusText, responses, headers ) {
FILE: photologue/admin.py
class GalleryAdminForm (line 18) | class GalleryAdminForm(forms.ModelForm):
class Meta (line 19) | class Meta:
class GalleryAdmin (line 27) | class GalleryAdmin(admin.ModelAdmin):
method formfield_for_manytomany (line 45) | def formfield_for_manytomany(self, db_field, request, **kwargs):
method save_related (line 51) | def save_related(self, request, form, *args, **kwargs):
method add_to_current_site (line 68) | def add_to_current_site(modeladmin, request, queryset):
method remove_from_current_site (line 81) | def remove_from_current_site(modeladmin, request, queryset):
method add_photos_to_current_site (line 94) | def add_photos_to_current_site(modeladmin, request, queryset):
method remove_photos_from_current_site (line 109) | def remove_photos_from_current_site(modeladmin, request, queryset):
class PhotoAdminForm (line 128) | class PhotoAdminForm(forms.ModelForm):
class Meta (line 129) | class Meta:
class PhotoAdmin (line 137) | class PhotoAdmin(admin.ModelAdmin):
method formfield_for_manytomany (line 153) | def formfield_for_manytomany(self, db_field, request, **kwargs):
method add_photos_to_current_site (line 159) | def add_photos_to_current_site(modeladmin, request, queryset):
method remove_photos_from_current_site (line 172) | def remove_photos_from_current_site(modeladmin, request, queryset):
method get_urls (line 185) | def get_urls(self):
method upload_zip (line 194) | def upload_zip(self, request):
class PhotoEffectAdmin (line 221) | class PhotoEffectAdmin(admin.ModelAdmin):
class PhotoSizeAdmin (line 246) | class PhotoSizeAdmin(admin.ModelAdmin):
class WatermarkAdmin (line 264) | class WatermarkAdmin(admin.ModelAdmin):
FILE: photologue/apps.py
class PhotologueConfig (line 4) | class PhotologueConfig(AppConfig):
FILE: photologue/forms.py
class PhotoDefaults (line 27) | class PhotoDefaults:
method __init__ (line 32) | def __init__(self, title: str, caption: str, is_public: bool) -> "Phot...
class UploadMessage (line 38) | class UploadMessage:
method __init__ (line 42) | def __init__(self, severity: MessageSeverity, content: MessageContent)...
method success (line 46) | def success(content: MessageContent):
method warning (line 49) | def warning(content: MessageContent):
class UploadZipForm (line 53) | class UploadZipForm(forms.Form):
method clean_zip_file (line 81) | def clean_zip_file(self):
method clean_title (line 98) | def clean_title(self):
method clean (line 104) | def clean(self):
method save (line 114) | def save(self, request=None, zip_file=None):
method _reuse_or_create_gallery_in_site (line 132) | def _reuse_or_create_gallery_in_site(self, current_site):
function create_gallery_in_site (line 147) | def create_gallery_in_site(site: Site, title: str, description: str = ""...
function upload_photos_to_site (line 156) | def upload_photos_to_site(site: Site, zip: zipfile.ZipFile, gallery: Gal...
FILE: photologue/management/commands/__init__.py
function get_response (line 4) | def get_response(msg, func=int, default=None):
function create_photosize (line 15) | def create_photosize(name, width=0, height=0, crop=False, pre_cache=Fals...
FILE: photologue/management/commands/plcache.py
class Command (line 6) | class Command(BaseCommand):
method add_arguments (line 10) | def add_arguments(self, parser):
method handle (line 21) | def handle(self, *args, **options):
FILE: photologue/management/commands/plcreatesize.py
class Command (line 6) | class Command(BaseCommand):
method add_arguments (line 11) | def add_arguments(self, parser):
method handle (line 16) | def handle(self, *args, **options):
FILE: photologue/management/commands/plflush.py
class Command (line 6) | class Command(BaseCommand):
method add_arguments (line 9) | def add_arguments(self, parser):
method handle (line 15) | def handle(self, *args, **options):
FILE: photologue/managers.py
class SharedQueries (line 5) | class SharedQueries:
method is_public (line 9) | def is_public(self):
method on_site (line 13) | def on_site(self):
class GalleryQuerySet (line 18) | class GalleryQuerySet(SharedQueries, QuerySet):
class PhotoQuerySet (line 22) | class PhotoQuerySet(SharedQueries, QuerySet):
FILE: photologue/migrations/0001_initial.py
class Migration (line 8) | class Migration(migrations.Migration):
FILE: photologue/migrations/0002_photosize_data.py
function initial_photosizes (line 4) | def initial_photosizes(apps, schema_editor):
class Migration (line 31) | class Migration(migrations.Migration):
FILE: photologue/migrations/0003_auto_20140822_1716.py
class Migration (line 4) | class Migration(migrations.Migration):
FILE: photologue/migrations/0004_auto_20140915_1259.py
class Migration (line 5) | class Migration(migrations.Migration):
FILE: photologue/migrations/0005_auto_20141027_1552.py
class Migration (line 4) | class Migration(migrations.Migration):
FILE: photologue/migrations/0006_auto_20141028_2005.py
class Migration (line 4) | class Migration(migrations.Migration):
FILE: photologue/migrations/0007_auto_20150404_1737.py
class Migration (line 5) | class Migration(migrations.Migration):
FILE: photologue/migrations/0008_auto_20150509_1557.py
class Migration (line 4) | class Migration(migrations.Migration):
FILE: photologue/migrations/0009_auto_20160102_0904.py
class Migration (line 6) | class Migration(migrations.Migration):
FILE: photologue/migrations/0010_auto_20160105_1307.py
class Migration (line 6) | class Migration(migrations.Migration):
FILE: photologue/migrations/0011_auto_20190223_2138.py
class Migration (line 6) | class Migration(migrations.Migration):
FILE: photologue/migrations/0012_alter_photo_effect.py
class Migration (line 7) | class Migration(migrations.Migration):
FILE: photologue/migrations/0013_alter_watermark_image.py
class Migration (line 7) | class Migration(migrations.Migration):
FILE: photologue/models.py
function get_storage_path (line 66) | def get_storage_path(instance, filename):
class TagField (line 144) | class TagField(models.CharField):
method __init__ (line 149) | def __init__(self, **kwargs):
method get_internal_type (line 154) | def get_internal_type(self):
class Gallery (line 158) | class Gallery(models.Model):
class Meta (line 183) | class Meta:
method __str__ (line 189) | def __str__(self):
method get_absolute_url (line 192) | def get_absolute_url(self):
method latest (line 195) | def latest(self, limit=LATEST_LIMIT, public=True):
method sample (line 203) | def sample(self, count=None, public=True):
method photo_count (line 218) | def photo_count(self, public=True):
method public (line 227) | def public(self):
method orphaned_photos (line 231) | def orphaned_photos(self):
class ImageModel (line 240) | class ImageModel(models.Model):
class Meta (line 263) | class Meta:
method EXIF (line 266) | def EXIF(self, file=None):
method admin_thumbnail (line 277) | def admin_thumbnail(self):
method cache_path (line 290) | def cache_path(self):
method cache_url (line 293) | def cache_url(self):
method image_filename (line 296) | def image_filename(self):
method _get_filename_for_size (line 299) | def _get_filename_for_size(self, size):
method _get_SIZE_photosize (line 304) | def _get_SIZE_photosize(self, size):
method _get_SIZE_size (line 307) | def _get_SIZE_size(self, size):
method _get_SIZE_url (line 317) | def _get_SIZE_url(self, size):
method _get_SIZE_filename (line 327) | def _get_SIZE_filename(self, size):
method increment_count (line 332) | def increment_count(self):
method __getattr__ (line 336) | def __getattr__(self, name):
method size_exists (line 348) | def size_exists(self, photosize):
method resize_image (line 355) | def resize_image(self, im, photosize):
method create_size (line 397) | def create_size(self, photosize, recreate=False):
method remove_size (line 446) | def remove_size(self, photosize, remove_dirs=True):
method clear_cache (line 453) | def clear_cache(self):
method pre_cache (line 458) | def pre_cache(self, recreate=False):
method __init__ (line 466) | def __init__(self, *args, **kwargs):
method save (line 470) | def save(self, *args, **kwargs):
method delete (line 499) | def delete(self):
class Photo (line 513) | class Photo(ImageModel):
class Meta (line 533) | class Meta:
method __str__ (line 539) | def __str__(self):
method save (line 542) | def save(self, *args, **kwargs):
method get_absolute_url (line 553) | def get_absolute_url(self):
method public_galleries (line 556) | def public_galleries(self):
method get_previous_in_gallery (line 560) | def get_previous_in_gallery(self, gallery):
method get_next_in_gallery (line 575) | def get_next_in_gallery(self, gallery):
class BaseEffect (line 593) | class BaseEffect(models.Model):
class Meta (line 600) | class Meta:
method sample_dir (line 603) | def sample_dir(self):
method sample_url (line 606) | def sample_url(self):
method sample_filename (line 610) | def sample_filename(self):
method create_sample (line 613) | def create_sample(self):
method admin_sample (line 628) | def admin_sample(self):
method pre_process (line 634) | def pre_process(self, im):
method post_process (line 637) | def post_process(self, im):
method process (line 640) | def process(self, im):
method __str__ (line 645) | def __str__(self):
method save (line 648) | def save(self, *args, **kwargs):
method delete (line 663) | def delete(self):
class PhotoEffect (line 671) | class PhotoEffect(BaseEffect):
class Meta (line 711) | class Meta:
method pre_process (line 715) | def pre_process(self, im):
method post_process (line 734) | def post_process(self, im):
class Watermark (line 741) | class Watermark(BaseEffect):
class Meta (line 752) | class Meta:
method delete (line 756) | def delete(self):
method post_process (line 762) | def post_process(self, im):
class PhotoSize (line 767) | class PhotoSize(models.Model):
class Meta (line 825) | class Meta:
method __str__ (line 830) | def __str__(self):
method clear_cache (line 833) | def clear_cache(self):
method clean (line 841) | def clean(self):
method save (line 847) | def save(self, *args, **kwargs):
method delete (line 852) | def delete(self):
method _get_size (line 858) | def _get_size(self):
method _set_size (line 861) | def _set_size(self, value):
class PhotoSizeCache (line 867) | class PhotoSizeCache:
method __init__ (line 870) | def __init__(self):
method reset (line 877) | def reset(self):
function init_size_method_map (line 883) | def init_size_method_map():
function add_default_site (line 896) | def add_default_site(instance, created, **kwargs):
FILE: photologue/sitemaps.py
class GallerySitemap (line 36) | class GallerySitemap(Sitemap):
method items (line 38) | def items(self):
method lastmod (line 43) | def lastmod(self, obj):
class PhotoSitemap (line 47) | class PhotoSitemap(Sitemap):
method items (line 49) | def items(self):
method lastmod (line 54) | def lastmod(self, obj):
FILE: photologue/templatetags/photologue_tags.py
function next_in_gallery (line 11) | def next_in_gallery(photo, gallery):
function previous_in_gallery (line 16) | def previous_in_gallery(photo, gallery):
function cycle_lite_gallery (line 21) | def cycle_lite_gallery(gallery_title, height, width):
function get_photo (line 34) | def get_photo(parser, token):
class PhotoNode (line 52) | class PhotoNode(template.Node):
method __init__ (line 54) | def __init__(self, photo, photosize, css_class):
method render (line 59) | def render(self, context):
function get_rotating_photo (line 82) | def get_rotating_photo(parser, token):
class PhotoGalleryNode (line 100) | class PhotoGalleryNode(template.Node):
method __init__ (line 102) | def __init__(self, gallery, photosize, css_class):
method render (line 107) | def render(self, context):
FILE: photologue/tests/factories.py
class GalleryFactory (line 27) | class GalleryFactory(factory.django.DjangoModelFactory):
class Meta (line 29) | class Meta:
method date_added (line 36) | def date_added(n):
method sites (line 46) | def sites(self, create, extracted, **kwargs):
class ImageModelFactory (line 61) | class ImageModelFactory(factory.django.DjangoModelFactory):
class Meta (line 63) | class Meta:
class PhotoFactory (line 68) | class PhotoFactory(ImageModelFactory):
class Meta (line 74) | class Meta:
method date_added (line 82) | def date_added(n):
method sites (line 92) | def sites(self, create, extracted, **kwargs):
class PhotoSizeFactory (line 107) | class PhotoSizeFactory(factory.django.DjangoModelFactory):
class Meta (line 109) | class Meta:
class PhotoEffectFactory (line 115) | class PhotoEffectFactory(factory.django.DjangoModelFactory):
class Meta (line 117) | class Meta:
FILE: photologue/tests/helpers.py
class PhotologueBaseTest (line 8) | class PhotologueBaseTest(TestCase):
method setUp (line 10) | def setUp(self):
method tearDown (line 23) | def tearDown(self):
FILE: photologue/tests/test_effect.py
class PhotoEffectTest (line 5) | class PhotoEffectTest(PhotologueBaseTest):
method test (line 7) | def test(self):
FILE: photologue/tests/test_gallery.py
class GalleryTest (line 6) | class GalleryTest(PhotologueBaseTest):
method setUp (line 8) | def setUp(self):
method tearDown (line 16) | def tearDown(self):
method test_public (line 20) | def test_public(self):
method test_photo_count (line 27) | def test_photo_count(self):
method test_sample (line 38) | def test_sample(self):
FILE: photologue/tests/test_photo.py
class PhotoTest (line 17) | class PhotoTest(PhotologueBaseTest):
method tearDown (line 19) | def tearDown(self):
method test_new_photo (line 27) | def test_new_photo(self):
method test_paths (line 36) | def test_paths(self):
method test_cachedir_tag (line 44) | def test_cachedir_tag(self):
method test_count (line 50) | def test_count(self):
method test_precache (line 60) | def test_precache(self):
method test_accessor_methods (line 74) | def test_accessor_methods(self):
method test_quoted_url (line 85) | def test_quoted_url(self):
method test_unicode (line 101) | def test_unicode(self):
method test_update_crop_applied (line 108) | def test_update_crop_applied(self, mock_resize_image):
method test_update_effect_applied (line 118) | def test_update_effect_applied(self, mock_post_process, mock_pre_proce...
class PhotoManagerTest (line 128) | class PhotoManagerTest(PhotologueBaseTest):
method setUp (line 131) | def setUp(self):
method tearDown (line 136) | def tearDown(self):
method test_public (line 140) | def test_public(self):
class PreviousNextTest (line 148) | class PreviousNextTest(PhotologueBaseTest):
method setUp (line 151) | def setUp(self):
method tearDown (line 162) | def tearDown(self):
method test_previous_simple (line 168) | def test_previous_simple(self):
method test_previous_public (line 177) | def test_previous_public(self):
method test_previous_gallery_mismatch (line 191) | def test_previous_gallery_mismatch(self):
method test_next_simple (line 202) | def test_next_simple(self):
method test_next_public (line 211) | def test_next_public(self):
method test_next_gallery_mismatch (line 225) | def test_next_gallery_mismatch(self):
class ImageModelTest (line 237) | class ImageModelTest(PhotologueBaseTest):
method setUp (line 239) | def setUp(self):
method tearDown (line 254) | def tearDown(self):
method test_create_size (line 262) | def test_create_size(self):
function raw_image (line 267) | def raw_image(mode='RGB', fmt='JPEG'):
class ImageTransparencyTest (line 276) | class ImageTransparencyTest(PhotologueBaseTest):
method setUp (line 278) | def setUp(self):
method tearDown (line 284) | def tearDown(self):
method test_create_size_png_keep_alpha_channel (line 289) | def test_create_size_png_keep_alpha_channel(self):
FILE: photologue/tests/test_photosize.py
class PhotoSizeNameTest (line 7) | class PhotoSizeNameTest(PhotologueBaseTest):
method test_valid_name (line 9) | def test_valid_name(self):
FILE: photologue/tests/test_resize.py
class PhotoSizeTest (line 10) | class PhotoSizeTest(unittest.TestCase):
method test_clean_wont_allow_zero_dimension_and_crop (line 12) | def test_clean_wont_allow_zero_dimension_and_crop(self):
class ImageResizeTest (line 19) | class ImageResizeTest(PhotologueBaseTest):
method setUp (line 21) | def setUp(self):
method tearDown (line 28) | def tearDown(self):
method test_resize_to_fit (line 33) | def test_resize_to_fit(self):
method test_resize_to_fit_width (line 38) | def test_resize_to_fit_width(self):
method test_resize_to_fit_width_enlarge (line 45) | def test_resize_to_fit_width_enlarge(self):
method test_resize_to_fit_height (line 53) | def test_resize_to_fit_height(self):
method test_resize_to_fit_height_enlarge (line 60) | def test_resize_to_fit_height_enlarge(self):
method test_resize_and_crop (line 68) | def test_resize_and_crop(self):
method test_resize_rounding_to_fit (line 75) | def test_resize_rounding_to_fit(self):
method test_resize_rounding_cropped (line 82) | def test_resize_rounding_cropped(self):
method test_resize_one_dimension_width (line 90) | def test_resize_one_dimension_width(self):
method test_resize_one_dimension_height (line 95) | def test_resize_one_dimension_height(self):
method test_resize_no_upscale (line 100) | def test_resize_no_upscale(self):
method test_resize_no_upscale_mixed_height (line 105) | def test_resize_no_upscale_mixed_height(self):
method test_resize_no_upscale_mixed_width (line 110) | def test_resize_no_upscale_mixed_width(self):
method test_resize_no_upscale_crop (line 115) | def test_resize_no_upscale_crop(self):
method test_resize_upscale (line 121) | def test_resize_upscale(self):
class PhotoSizeCacheTest (line 130) | class PhotoSizeCacheTest(PhotologueBaseTest):
method test (line 132) | def test(self):
FILE: photologue/tests/test_sitemap.py
class SitemapTest (line 13) | class SitemapTest(PhotologueBaseTest):
method test_get_photo (line 15) | def test_get_photo(self):
method test_get_gallery (line 22) | def test_get_gallery(self):
FILE: photologue/tests/test_sites.py
class SitesTest (line 11) | class SitesTest(TestCase):
method setUp (line 13) | def setUp(self):
method tearDown (line 38) | def tearDown(self):
method test_basics (line 45) | def test_basics(self):
method test_auto_add_sites (line 50) | def test_auto_add_sites(self):
method test_gallery_list (line 72) | def test_gallery_list(self):
method test_gallery_detail (line 76) | def test_gallery_detail(self):
method test_photo_list (line 83) | def test_photo_list(self):
method test_photo_detail (line 87) | def test_photo_detail(self):
method test_photo_archive (line 94) | def test_photo_archive(self):
method test_photos_in_gallery (line 98) | def test_photos_in_gallery(self):
method test_sitemap (line 108) | def test_sitemap(self):
method test_orphaned_photos (line 128) | def test_orphaned_photos(self):
FILE: photologue/tests/test_views_gallery.py
class RequestGalleryTest (line 7) | class RequestGalleryTest(TestCase):
method setUp (line 9) | def setUp(self):
method test_archive_gallery_url_works (line 13) | def test_archive_gallery_url_works(self):
method test_archive_gallery_empty (line 17) | def test_archive_gallery_empty(self):
method test_paginated_gallery_url_works (line 30) | def test_paginated_gallery_url_works(self):
method test_gallery_works (line 34) | def test_gallery_works(self):
method test_archive_year_gallery_works (line 38) | def test_archive_year_gallery_works(self):
method test_archive_month_gallery_works (line 42) | def test_archive_month_gallery_works(self):
method test_archive_day_gallery_works (line 46) | def test_archive_day_gallery_works(self):
method test_detail_gallery_works (line 50) | def test_detail_gallery_works(self):
method test_redirect_to_list (line 54) | def test_redirect_to_list(self):
FILE: photologue/tests/test_views_photo.py
class RequestPhotoTest (line 8) | class RequestPhotoTest(TestCase):
method setUp (line 10) | def setUp(self):
method tearDown (line 14) | def tearDown(self):
method test_archive_photo_url_works (line 18) | def test_archive_photo_url_works(self):
method test_archive_photo_empty (line 22) | def test_archive_photo_empty(self):
method test_paginated_photo_url_works (line 34) | def test_paginated_photo_url_works(self):
method test_photo_works (line 38) | def test_photo_works(self):
method test_archive_year_photo_works (line 42) | def test_archive_year_photo_works(self):
method test_archive_month_photo_works (line 46) | def test_archive_month_photo_works(self):
method test_archive_day_photo_works (line 50) | def test_archive_day_photo_works(self):
method test_detail_photo_works (line 54) | def test_detail_photo_works(self):
method test_detail_photo_xss (line 58) | def test_detail_photo_xss(self):
FILE: photologue/tests/test_zipupload.py
class GalleryUploadTest (line 12) | class GalleryUploadTest(TestCase):
method setUp (line 16) | def setUp(self):
method tearDown (line 30) | def tearDown(self):
method test_get (line 36) | def test_get(self):
method test_breadcrumbs (line 45) | def test_breadcrumbs(self):
method test_missing_fields (line 53) | def test_missing_fields(self):
method test_good_data (line 62) | def test_good_data(self):
method test_duplicate_gallery (line 90) | def test_duplicate_gallery(self):
method test_title_or_gallery (line 100) | def test_title_or_gallery(self):
method test_not_image (line 109) | def test_not_image(self):
method test_ignored (line 126) | def test_ignored(self):
method test_existing_gallery (line 145) | def test_existing_gallery(self):
method test_existing_gallery_custom_title (line 168) | def test_existing_gallery_custom_title(self):
method test_duplicate_slug (line 184) | def test_duplicate_slug(self):
method test_bad_zip (line 203) | def test_bad_zip(self):
FILE: photologue/utils/reflection.py
function add_reflection (line 43) | def add_reflection(im, bgcolor="#00000", amount=0.4, opacity=0.6):
FILE: photologue/utils/watermark.py
function reduce_opacity (line 18) | def reduce_opacity(im, opacity):
function apply_watermark (line 31) | def apply_watermark(im, mark, position, opacity=1):
FILE: photologue/views.py
class GalleryListView (line 11) | class GalleryListView(ListView):
class GalleryDetailView (line 16) | class GalleryDetailView(DetailView):
class GalleryDateView (line 20) | class GalleryDateView:
class GalleryDateDetailView (line 26) | class GalleryDateDetailView(GalleryDateView, DateDetailView):
class GalleryArchiveIndexView (line 30) | class GalleryArchiveIndexView(GalleryDateView, ArchiveIndexView):
class GalleryDayArchiveView (line 34) | class GalleryDayArchiveView(GalleryDateView, DayArchiveView):
class GalleryMonthArchiveView (line 38) | class GalleryMonthArchiveView(GalleryDateView, MonthArchiveView):
class GalleryYearArchiveView (line 42) | class GalleryYearArchiveView(GalleryDateView, YearArchiveView):
class PhotoListView (line 48) | class PhotoListView(ListView):
class PhotoDetailView (line 53) | class PhotoDetailView(DetailView):
class PhotoDateView (line 57) | class PhotoDateView:
class PhotoDateDetailView (line 63) | class PhotoDateDetailView(PhotoDateView, DateDetailView):
class PhotoArchiveIndexView (line 67) | class PhotoArchiveIndexView(PhotoDateView, ArchiveIndexView):
class PhotoDayArchiveView (line 71) | class PhotoDayArchiveView(PhotoDateView, DayArchiveView):
class PhotoMonthArchiveView (line 75) | class PhotoMonthArchiveView(PhotoDateView, MonthArchiveView):
class PhotoYearArchiveView (line 79) | class PhotoYearArchiveView(PhotoDateView, YearArchiveView):
FILE: scripts/releaser_hooks.py
function prereleaser_before (line 13) | def prereleaser_before(data):
FILE: setup.py
function get_requirements (line 8) | def get_requirements(source):
Condensed preview — 164 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,099K chars).
[
{
"path": ".coveragerc",
"chars": 66,
"preview": "[run]\nsource = photologue\nomit = *migrations*, *wsgi*, */tests/*\n\n"
},
{
"path": ".github/workflows/ci.yml",
"chars": 1428,
"preview": "# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actio"
},
{
"path": ".gitignore",
"chars": 132,
"preview": ".DS_Store\n*__pycache__*\ndjango_photologue.egg-info\nbuild\n.idea\nexample_project/db.sqlite3\nhtmlcov\n.coverage\n\n# Tox workf"
},
{
"path": ".isort.cfg",
"chars": 70,
"preview": "[settings]\nextend_skip_glob = photologue/migrations\nline_length = 119\n"
},
{
"path": ".readthedocs.yml",
"chars": 504,
"preview": "# .readthedocs.yml\n# Read the Docs configuration file\n# See https://docs.readthedocs.io/en/stable/config-file/v2.html fo"
},
{
"path": ".tx/config",
"chars": 262,
"preview": "[main]\nhost = https://www.transifex.com\nlang_map = sr@latin:sr_Latn, zh-Hans:zh_Hans\n\n[django-photologue.core]\nfile_filt"
},
{
"path": "CHANGELOG.txt",
"chars": 14519,
"preview": "Changelog\n=========\n\n3.19 (unreleased)\n-----------------\n\n- Nothing changed yet.\n\n\n3.18 (2025-06-01)\n-----------------\n\n"
},
{
"path": "CONTRIBUTORS.txt",
"chars": 810,
"preview": "Photologue is made possible by all the people who have contributed to it. A non-exhaustive list follows:\n\nJustin Driscol"
},
{
"path": "LICENSE.txt",
"chars": 1583,
"preview": "Copyright (c) 2007-2025, Justin C. Driscoll and all the people named in CONTRIBUTORS.txt.\nAll rights reserved.\n\nRedistri"
},
{
"path": "MANIFEST.in",
"chars": 476,
"preview": "include CHANGELOG.txt\ninclude CONTRIBUTORS.txt\ninclude LICENSE.txt\ninclude requirements.txt\nrecursive-include photologue"
},
{
"path": "README.rst",
"chars": 1735,
"preview": "Django-photologue\n=================\n.. image:: https://img.shields.io/pypi/v/django-photologue.svg\n :target: https://"
},
{
"path": "SECURITY.md",
"chars": 475,
"preview": "# Security Policy\n\n## Supported Versions\n\nPlease note that in the case of a security issue being reported, only the late"
},
{
"path": "docs/.gitignore",
"chars": 7,
"preview": "_build\n"
},
{
"path": "docs/Makefile",
"chars": 5608,
"preview": "# Makefile for Sphinx documentation\n#\n\n# You can set these variables from the command line.\nSPHINXOPTS =\nSPHINXBUILD "
},
{
"path": "docs/conf.py",
"chars": 8364,
"preview": "#\n# django-photologue documentation build configuration file, created by\n# sphinx-quickstart on Mon Sep 3 16:31:44 2012"
},
{
"path": "docs/index.rst",
"chars": 828,
"preview": ".. django-photologue documentation master file, created by\n sphinx-quickstart on Mon Sep 3 16:31:44 2012.\n You can "
},
{
"path": "docs/make.bat",
"chars": 5118,
"preview": "@ECHO OFF\n\nREM Command file for Sphinx documentation\n\nif \"%SPHINXBUILD%\" == \"\" (\n\tset SPHINXBUILD=sphinx-build\n)\nset BUI"
},
{
"path": "docs/pages/changelog_page.rst",
"chars": 195,
"preview": ".. We want the CHANGELOG to also appear in the docs but we don't want to break\n the DRY principle. So we simply includ"
},
{
"path": "docs/pages/contributing.rst",
"chars": 4950,
"preview": "##########################\nContributing to Photologue\n##########################\n\nContributions are always very welcome."
},
{
"path": "docs/pages/customising/admin.rst",
"chars": 2823,
"preview": ".. _customisation-admin-label:\n\n####################\nCustomisation: Admin\n####################\n\nThe Photologue admin can"
},
{
"path": "docs/pages/customising/models.rst",
"chars": 3372,
"preview": ".. _customising-models-label:\n\n#####################\nCustomisation: Models\n#####################\n\nThe photologue models "
},
{
"path": "docs/pages/customising/settings.rst",
"chars": 2724,
"preview": "#######################\nCustomisation: Settings\n#######################\n\n\nPhotologue has several settings to customise b"
},
{
"path": "docs/pages/customising/templates.rst",
"chars": 2429,
"preview": "##################################\nCustomisation: extending templates\n##################################\n\nPhotologue com"
},
{
"path": "docs/pages/customising/views.rst",
"chars": 3667,
"preview": ".. _customisation-views-label:\n\n#############################\nCustomisation: Views and Urls\n############################"
},
{
"path": "docs/pages/installation.rst",
"chars": 6783,
"preview": "############################\nInstallation & configuration\n############################\n\n\n.. _installing-photologue-label"
},
{
"path": "docs/pages/usage.rst",
"chars": 2387,
"preview": "#####\nUsage\n#####\n\nNow that you've installed Photologue, here are a few suggestions on how to use it:\n\nUpload some photo"
},
{
"path": "docs/requirements.txt",
"chars": 30,
"preview": "django\n-r ../requirements.txt\n"
},
{
"path": "example_project/README.rst",
"chars": 2730,
"preview": "#######################\nPhotologue Demo Project\n#######################\n\nAbout\n=====\nThis project serves 3 purposes:\n\n- "
},
{
"path": "example_project/example_project/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "example_project/example_project/example_storages/README.txt",
"chars": 88,
"preview": "In this folder we keep configuration files for non-default media stores, e.g. Amazon S3."
},
{
"path": "example_project/example_project/example_storages/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "example_project/example_project/example_storages/s3_requirements.txt",
"chars": 36,
"preview": "boto>=2.29.1\ndjango-storages>=1.1.8\n"
},
{
"path": "example_project/example_project/example_storages/s3utils.py",
"chars": 176,
"preview": "from storages.backends.s3boto import S3BotoStorage\n\nStaticS3BotoStorage = lambda: S3BotoStorage(location='static')\nMedia"
},
{
"path": "example_project/example_project/example_storages/settings_s3boto.py",
"chars": 1244,
"preview": "# S3Boto storage settings for photologue example project.\n\nimport os\n\nDEFAULT_FILE_STORAGE = 'example_project.example_st"
},
{
"path": "example_project/example_project/fixtures/.gitdirectory",
"chars": 72,
"preview": "Placeholder so that this directory will be added to the git repository.\n"
},
{
"path": "example_project/example_project/settings.py",
"chars": 4550,
"preview": "# Global settings for photologue example project.\n\nimport os\nimport sys\n\nBASE_DIR = os.path.dirname(os.path.dirname(os.p"
},
{
"path": "example_project/example_project/static/css/styles.css",
"chars": 562,
"preview": "/* Some customisation for the Photologue demo site. */\n\n/* Page structure */\n\nh1 {\n margin-top: 1rem;\n margin-bott"
},
{
"path": "example_project/example_project/static/js/jquery-3.5.1.js",
"chars": 287630,
"preview": "/*!\n * jQuery JavaScript Library v3.5.1\n * https://jquery.com/\n *\n * Includes Sizzle.js\n * https://sizzlejs.com/\n *\n * C"
},
{
"path": "example_project/example_project/templates/base.html",
"chars": 2712,
"preview": "{% load static %}\n<!DOCTYPE html>\n<html lang=\"en\">\n <head>\n <meta charset=\"utf-8\">\n <title>Photologue Demo - {% b"
},
{
"path": "example_project/example_project/templates/homepage.html",
"chars": 588,
"preview": "{% extends 'base.html' %}\n\n{% block title %}Photologue example project{% endblock %}\n\n{% block content %}\n\t<div class=\"r"
},
{
"path": "example_project/example_project/urls.py",
"chars": 482,
"preview": "from django.conf import settings\nfrom django.conf.urls.static import static\nfrom django.contrib import admin\nfrom django"
},
{
"path": "example_project/example_project/wsgi.py",
"chars": 176,
"preview": "import os\n\nfrom django.core.wsgi import get_wsgi_application\n\nos.environ.setdefault(\"DJANGO_SETTINGS_MODULE\", \"example_p"
},
{
"path": "example_project/manage.py",
"chars": 448,
"preview": "#!/usr/bin/env python\nimport os\nimport sys\n\nif __name__ == '__main__':\n os.environ.setdefault('DJANGO_SETTINGS_MODULE"
},
{
"path": "example_project/public/.gitdirectory",
"chars": 72,
"preview": "Placeholder so that this directory will be added to the git repository.\n"
},
{
"path": "example_project/public/media/.gitignore",
"chars": 71,
"preview": "# Ignore everything in this directory\n*\n# Except this file\n!.gitignore\n"
},
{
"path": "example_project/public/static/.gitdirectory",
"chars": 72,
"preview": "Placeholder so that this directory will be added to the git repository.\n"
},
{
"path": "example_project/requirements.txt",
"chars": 279,
"preview": "-r ../requirements.txt\n# The following is only required if developing for Photologue.\nfactory-boy>=3.3.2 # Note: version"
},
{
"path": "photologue/__init__.py",
"chars": 102,
"preview": "import os\n\n__version__ = '3.19.dev0'\n\nPHOTOLOGUE_APP_DIR = os.path.dirname(os.path.abspath(__file__))\n"
},
{
"path": "photologue/admin.py",
"chars": 10184,
"preview": "from django import forms\r\nfrom django.conf import settings\r\nfrom django.contrib import admin, messages\r\nfrom django.cont"
},
{
"path": "photologue/apps.py",
"chars": 149,
"preview": "from django.apps import AppConfig\n\n\nclass PhotologueConfig(AppConfig):\n default_auto_field = 'django.db.models.AutoFi"
},
{
"path": "photologue/forms.py",
"chars": 9157,
"preview": "import logging\nimport os\nimport zipfile\nfrom io import BytesIO\nfrom typing import List\nfrom zipfile import BadZipFile\n\nf"
},
{
"path": "photologue/locale/ca/LC_MESSAGES/django.po",
"chars": 22476,
"preview": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER\n# This file is distributed under the same "
},
{
"path": "photologue/locale/cs/LC_MESSAGES/django.po",
"chars": 19794,
"preview": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER\n# This file is distributed under the same "
},
{
"path": "photologue/locale/da/LC_MESSAGES/django.po",
"chars": 21519,
"preview": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER\n# This file is distributed under the same "
},
{
"path": "photologue/locale/de/LC_MESSAGES/django.po",
"chars": 22613,
"preview": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER\n# This file is distributed under the same "
},
{
"path": "photologue/locale/en/LC_MESSAGES/django.po",
"chars": 16144,
"preview": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER\n# This file is distributed under the same "
},
{
"path": "photologue/locale/en_US/LC_MESSAGES/django.po",
"chars": 19516,
"preview": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER\n# This file is distributed under the same "
},
{
"path": "photologue/locale/es_ES/LC_MESSAGES/django.po",
"chars": 22120,
"preview": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER\n# This file is distributed under the same "
},
{
"path": "photologue/locale/eu/LC_MESSAGES/django.po",
"chars": 19790,
"preview": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER\n# This file is distributed under the same "
},
{
"path": "photologue/locale/fr/LC_MESSAGES/django.po",
"chars": 22903,
"preview": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER\n# This file is distributed under the same "
},
{
"path": "photologue/locale/hu/LC_MESSAGES/django.po",
"chars": 22327,
"preview": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER\n# This file is distributed under the same "
},
{
"path": "photologue/locale/it/LC_MESSAGES/django.po",
"chars": 19184,
"preview": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER\n# This file is distributed under the same "
},
{
"path": "photologue/locale/nl/LC_MESSAGES/django.po",
"chars": 22637,
"preview": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER\n# This file is distributed under the same "
},
{
"path": "photologue/locale/no/LC_MESSAGES/django.po",
"chars": 18285,
"preview": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER\n# This file is distributed under the same "
},
{
"path": "photologue/locale/pl/LC_MESSAGES/django.po",
"chars": 18761,
"preview": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER\n# This file is distributed under the same "
},
{
"path": "photologue/locale/pt/LC_MESSAGES/django.po",
"chars": 19038,
"preview": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER\n# This file is distributed under the same "
},
{
"path": "photologue/locale/pt_BR/LC_MESSAGES/django.po",
"chars": 19052,
"preview": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER\n# This file is distributed under the same "
},
{
"path": "photologue/locale/ru/LC_MESSAGES/django.po",
"chars": 21423,
"preview": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER\n# This file is distributed under the same "
},
{
"path": "photologue/locale/sk/LC_MESSAGES/django.po",
"chars": 22610,
"preview": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER\n# This file is distributed under the same "
},
{
"path": "photologue/locale/tr/LC_MESSAGES/django.po",
"chars": 20067,
"preview": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER\n# This file is distributed under the same "
},
{
"path": "photologue/locale/tr_TR/LC_MESSAGES/django.po",
"chars": 16157,
"preview": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER\n# This file is distributed under the same "
},
{
"path": "photologue/locale/uk/LC_MESSAGES/django.po",
"chars": 23386,
"preview": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER\n# This file is distributed under the same "
},
{
"path": "photologue/locale/zh_Hans/LC_MESSAGES/django.po",
"chars": 17930,
"preview": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER\n# This file is distributed under the same "
},
{
"path": "photologue/management/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "photologue/management/commands/__init__.py",
"chars": 1437,
"preview": "from photologue.models import PhotoSize\n\n\ndef get_response(msg, func=int, default=None):\n while True:\n resp = "
},
{
"path": "photologue/management/commands/plcache.py",
"chars": 1413,
"preview": "from django.core.management.base import BaseCommand, CommandError\n\nfrom photologue.models import ImageModel, PhotoSize\n\n"
},
{
"path": "photologue/management/commands/plcreatesize.py",
"chars": 552,
"preview": "from django.core.management.base import BaseCommand\r\n\r\nfrom photologue.management.commands import create_photosize\r\n\r\n\r\n"
},
{
"path": "photologue/management/commands/plflush.py",
"chars": 1027,
"preview": "from django.core.management.base import BaseCommand, CommandError\n\nfrom photologue.models import ImageModel, PhotoSize\n\n"
},
{
"path": "photologue/managers.py",
"chars": 574,
"preview": "from django.conf import settings\nfrom django.db.models.query import QuerySet\n\n\nclass SharedQueries:\n\n \"\"\"Some queries"
},
{
"path": "photologue/migrations/0001_initial.py",
"chars": 12215,
"preview": "from django.db import models, migrations\nimport photologue.models\nimport django.utils.timezone\nimport django.core.valida"
},
{
"path": "photologue/migrations/0002_photosize_data.py",
"chars": 1346,
"preview": "from django.db import models, migrations\n\n\ndef initial_photosizes(apps, schema_editor):\n\n PhotoSize = apps.get_model("
},
{
"path": "photologue/migrations/0003_auto_20140822_1716.py",
"chars": 487,
"preview": "from django.db import models, migrations\n\n\nclass Migration(migrations.Migration):\n\n dependencies = [\n ('photol"
},
{
"path": "photologue/migrations/0004_auto_20140915_1259.py",
"chars": 1310,
"preview": "from django.db import models, migrations\nimport sortedm2m.fields\n\n\nclass Migration(migrations.Migration):\n\n dependenc"
},
{
"path": "photologue/migrations/0005_auto_20141027_1552.py",
"chars": 408,
"preview": "from django.db import models, migrations\n\n\nclass Migration(migrations.Migration):\n\n dependencies = [\n ('photol"
},
{
"path": "photologue/migrations/0006_auto_20141028_2005.py",
"chars": 375,
"preview": "from django.db import models, migrations\n\n\nclass Migration(migrations.Migration):\n\n dependencies = [\n ('photol"
},
{
"path": "photologue/migrations/0007_auto_20150404_1737.py",
"chars": 860,
"preview": "from django.db import models, migrations\nimport sortedm2m.fields\n\n\nclass Migration(migrations.Migration):\n\n dependenc"
},
{
"path": "photologue/migrations/0008_auto_20150509_1557.py",
"chars": 389,
"preview": "from django.db import models, migrations\n\n\nclass Migration(migrations.Migration):\n\n dependencies = [\n ('photol"
},
{
"path": "photologue/migrations/0009_auto_20160102_0904.py",
"chars": 502,
"preview": "# Generated by Django 1.9 on 2016-01-02 09:04\n\nfrom django.db import migrations, models\n\n\nclass Migration(migrations.Mig"
},
{
"path": "photologue/migrations/0010_auto_20160105_1307.py",
"chars": 1124,
"preview": "# Generated by Django 1.9 on 2016-01-05 13:07\n\nfrom django.db import migrations, models\n\n\nclass Migration(migrations.Mig"
},
{
"path": "photologue/migrations/0011_auto_20190223_2138.py",
"chars": 727,
"preview": "# Generated by Django 2.1.7 on 2019-02-23 21:38\n\nfrom django.db import migrations, models\n\n\nclass Migration(migrations.M"
},
{
"path": "photologue/migrations/0012_alter_photo_effect.py",
"chars": 561,
"preview": "# Generated by Django 4.0.2 on 2022-02-23 09:50\n\nfrom django.db import migrations, models\nimport django.db.models.deleti"
},
{
"path": "photologue/migrations/0013_alter_watermark_image.py",
"chars": 472,
"preview": "# Generated by Django 4.2.3 on 2023-07-28 18:39\n\nfrom django.db import migrations, models\nimport pathlib\n\n\nclass Migrati"
},
{
"path": "photologue/migrations/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "photologue/models.py",
"chars": 37147,
"preview": "import logging\nimport os\nimport pathlib\nimport random\nimport unicodedata\nfrom datetime import datetime\nfrom functools im"
},
{
"path": "photologue/sitemaps.py",
"chars": 1810,
"preview": "\"\"\"\nThe `Sitemaps protocol <http://en.wikipedia.org/wiki/Sitemaps>`_ allows a webmaster\nto inform search engines about U"
},
{
"path": "photologue/templates/admin/photologue/photo/change_list.html",
"chars": 260,
"preview": "{% extends \"admin/change_list.html\" %}\n{% load i18n %}\n\n\n{% block object-tools-items %}\n {{ block.super }}\n <li>\n <"
},
{
"path": "photologue/templates/admin/photologue/photo/upload_zip.html",
"chars": 2094,
"preview": "{% extends \"admin/base_site.html\" %}\n{% load i18n admin_urls static %}\n\n{# Admin styling code largely taken from http://"
},
{
"path": "photologue/templates/photologue/gallery_archive.html",
"chars": 882,
"preview": "{% extends \"photologue/root.html\" %}\n{% load i18n %}\n\n{% block title %}{% trans \"Latest photo galleries\" %}{% endblock %"
},
{
"path": "photologue/templates/photologue/gallery_archive_day.html",
"chars": 971,
"preview": "{% extends \"photologue/root.html\" %}\r\n{% load i18n %}\r\n\r\n{% block title %}{% blocktrans with show_day=day|date:\"d F Y\" %"
},
{
"path": "photologue/templates/photologue/gallery_archive_month.html",
"chars": 1363,
"preview": "{% extends \"photologue/root.html\" %}\r\n{% load i18n %}\r\n\r\n{% block title %}{% blocktrans with show_month=month|date:\"F Y\""
},
{
"path": "photologue/templates/photologue/gallery_archive_year.html",
"chars": 1150,
"preview": "{% extends \"photologue/root.html\" %}\r\n{% load i18n %}\r\n\r\n{% block title %}{% blocktrans with show_year=year|date:\"Y\" %}G"
},
{
"path": "photologue/templates/photologue/gallery_detail.html",
"chars": 889,
"preview": "{% extends \"photologue/root.html\" %}\r\n{% load i18n %}\r\n\r\n{% block title %}{{ gallery.title }}{% endblock %}\r\n\r\n{% block "
},
{
"path": "photologue/templates/photologue/gallery_list.html",
"chars": 658,
"preview": "{% extends \"photologue/root.html\" %}\r\n{% load i18n %}\r\n\r\n{% block title %}{% trans \"All galleries\" %}{% endblock %}\r\n\r\n{"
},
{
"path": "photologue/templates/photologue/includes/gallery_sample.html",
"chars": 575,
"preview": "{% load i18n %}\n\n{# Display a randomnly-selected set of photos from a given gallery #}\n\n<div class=\"gallery-sample\">\n\n "
},
{
"path": "photologue/templates/photologue/includes/paginator.html",
"chars": 1330,
"preview": "{% load i18n %}\n{% if is_paginated %}\n<div class=\"row\">\n <div class=\"col-lg-12\">\n <nav aria-label=\"Page naviga"
},
{
"path": "photologue/templates/photologue/photo_archive.html",
"chars": 1012,
"preview": "{% extends \"photologue/root.html\" %}\r\n{% load i18n %}\r\n\r\n{% block title %}{% trans \"Latest photos\" %}{% endblock %}\r\n\r\n{"
},
{
"path": "photologue/templates/photologue/photo_archive_day.html",
"chars": 978,
"preview": "{% extends \"photologue/root.html\" %}\r\n{% load i18n %}\r\n\r\n{% block title %}{% blocktrans with show_day=day|date:\"d F Y\" %"
},
{
"path": "photologue/templates/photologue/photo_archive_month.html",
"chars": 1531,
"preview": "{% extends \"photologue/root.html\" %}\r\n{% load i18n %}\r\n\r\n{% block title %}{% blocktrans with show_month=month|date:\"F Y\""
},
{
"path": "photologue/templates/photologue/photo_archive_year.html",
"chars": 1330,
"preview": "{% extends \"photologue/root.html\" %}\r\n{% load i18n %}\r\n\r\n{% block title %}{% blocktrans with show_year=year|date:\"Y\" %}P"
},
{
"path": "photologue/templates/photologue/photo_detail.html",
"chars": 1237,
"preview": "{% extends \"photologue/root.html\" %}\r\n{% load photologue_tags i18n %}\r\n\r\n{% block title %}{{ object.title }}{% endblock "
},
{
"path": "photologue/templates/photologue/photo_list.html",
"chars": 734,
"preview": "{% extends \"photologue/root.html\" %}\r\n{% load i18n %}\r\n\r\n{% block title %}{% trans \"All photos\" %}{% endblock %}\r\n\r\n{% b"
},
{
"path": "photologue/templates/photologue/root.html",
"chars": 26,
"preview": "{% extends \"base.html\" %}\n"
},
{
"path": "photologue/templates/photologue/tags/next_in_gallery.html",
"chars": 191,
"preview": "{% if photo %}\n<a title=\"{{ photo.title }}\" href=\"{{ photo.get_absolute_url }}\">\n <img src=\"{{ photo.get_thumbnail_ur"
},
{
"path": "photologue/templates/photologue/tags/prev_in_gallery.html",
"chars": 191,
"preview": "{% if photo %}\n<a title=\"{{ photo.title }}\" href=\"{{ photo.get_absolute_url }}\">\n <img src=\"{{ photo.get_thumbnail_ur"
},
{
"path": "photologue/templatetags/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "photologue/templatetags/photologue_tags.py",
"chars": 4343,
"preview": "import random\n\nfrom django import template\n\nfrom ..models import Gallery, Photo\n\nregister = template.Library()\n\n\n@regist"
},
{
"path": "photologue/tests/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "photologue/tests/factories.py",
"chars": 4096,
"preview": "import datetime\nimport os\n\nfrom django.conf import settings\nfrom django.utils.text import slugify\n\ntry:\n import facto"
},
{
"path": "photologue/tests/helpers.py",
"chars": 785,
"preview": "import warnings\n\nfrom django.test import TestCase\n\nfrom .factories import PhotoFactory, PhotoSizeFactory\n\n\nclass Photolo"
},
{
"path": "photologue/tests/templates/base.html",
"chars": 0,
"preview": ""
},
{
"path": "photologue/tests/test_effect.py",
"chars": 458,
"preview": "from ..models import Image, PhotoEffect\nfrom .helpers import PhotologueBaseTest\n\n\nclass PhotoEffectTest(PhotologueBaseTe"
},
{
"path": "photologue/tests/test_gallery.py",
"chars": 2185,
"preview": "from .. import models\nfrom .factories import GalleryFactory, PhotoFactory\nfrom .helpers import PhotologueBaseTest\n\n\nclas"
},
{
"path": "photologue/tests/test_photo.py",
"chars": 11259,
"preview": "import os\nimport unittest\nfrom io import BytesIO\nfrom unittest.mock import patch\n\nfrom django import VERSION\nfrom django"
},
{
"path": "photologue/tests/test_photosize.py",
"chars": 1084,
"preview": "from django.core.exceptions import ValidationError\n\nfrom .factories import PhotoSizeFactory\nfrom .helpers import Photolo"
},
{
"path": "photologue/tests/test_resize.py",
"chars": 5015,
"preview": "import unittest\n\nfrom django.core.exceptions import ValidationError\n\nfrom ..models import PhotoSize, PhotoSizeCache\nfrom"
},
{
"path": "photologue/tests/test_sitemap.py",
"chars": 1447,
"preview": "import unittest\n\nfrom django.conf import settings\nfrom django.test import override_settings\n\nfrom .factories import Gall"
},
{
"path": "photologue/tests/test_sites.py",
"chars": 5743,
"preview": "import unittest\n\nfrom django.conf import settings\nfrom django.contrib.sites.models import Site\nfrom django.test import T"
},
{
"path": "photologue/tests/test_urls.py",
"chars": 425,
"preview": "from django.contrib.sitemaps.views import sitemap\nfrom django.urls import include, path\n\nfrom ..sitemaps import GalleryS"
},
{
"path": "photologue/tests/test_views_gallery.py",
"chars": 2123,
"preview": "from django.test import TestCase, override_settings\n\nfrom .factories import GalleryFactory\n\n\n@override_settings(ROOT_URL"
},
{
"path": "photologue/tests/test_views_photo.py",
"chars": 2547,
"preview": "from django.test import TestCase, override_settings\n\nfrom ..models import Photo\nfrom .factories import PhotoFactory\n\n\n@o"
},
{
"path": "photologue/tests/test_zipupload.py",
"chars": 9133,
"preview": "import copy\n\nfrom django import VERSION\nfrom django.contrib.auth.models import User\nfrom django.test import TestCase\n\nfr"
},
{
"path": "photologue/urls.py",
"chars": 3065,
"preview": "from django.urls import path, re_path, reverse_lazy\nfrom django.views.generic import RedirectView\n\nfrom .views import (G"
},
{
"path": "photologue/utils/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "photologue/utils/reflection.py",
"chars": 3775,
"preview": "\"\"\" Function for generating web 2.0 style image reflection effects.\n\nCopyright (c) 2007, Justin C. Driscoll\nAll rights r"
},
{
"path": "photologue/utils/watermark.py",
"chars": 1747,
"preview": "\"\"\" Function for applying watermarks to images.\n\nOriginal found here:\nhttp://aspn.activestate.com/ASPN/Cookbook/Python/R"
},
{
"path": "photologue/views.py",
"chars": 1753,
"preview": "from django.views.generic.dates import (ArchiveIndexView, DateDetailView, DayArchiveView, MonthArchiveView,\n "
},
{
"path": "requirements.txt",
"chars": 332,
"preview": "# Note: Specifying django here crashes tox; it's autoinstalled with other packages it\n# so can be removed from this file"
},
{
"path": "scripts/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "scripts/releaser_hooks.py",
"chars": 3204,
"preview": "import os\nimport subprocess\n\ntry:\n import polib\nexcept ImportError:\n print('Msg to the package releaser: prereleas"
},
{
"path": "setup.cfg",
"chars": 367,
"preview": "[zest.releaser]\npython-file-with-version = photologue/__init__.py\nprereleaser.before = scripts.releaser_hooks.prerelease"
},
{
"path": "setup.py",
"chars": 1479,
"preview": "# /usr/bin/env python\nfrom pkg_resources import parse_requirements\nfrom setuptools import find_packages, setup\n\nimport p"
},
{
"path": "tox.ini",
"chars": 734,
"preview": "# tox (https://tox.readthedocs.io/) is a tool for running tests\n# in multiple virtualenvs. This configuration file will "
}
]
// ... and 22 more files (download for full content)
About this extraction
This page contains the full source code of the richardbarran/django-photologue GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 164 files (966.7 KB), approximately 271.2k tokens, and a symbol index with 402 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.