code:
from django.db import models
from django.views import generic

class Book(models.Model):

    """ Книга пользователя """

    title = models.CharField(max_length=100, verbose_name='Книга')
    description = models.TextField(max_length=920, null=True, blank=True, verbose_name='Описание')
    content = models.TextField(verbose_name='Содержание')
    added = models.DateTimeField(auto_now_add=True, verbose_name='Добавлена')
    is_read = models.BooleanField(default=False, verbose_name='Прочитана')
    is_defer = models.BooleanField(default=False, verbose_name='Отложена')
    is_favorites = models.BooleanField(default=False, verbose_name='Избранная')
    bookmark = models.PositiveIntegerField(default=0, null=True, blank=True, verbose_name='Закладка')
    author = models.ForeignKey('Author', on_delete=models.CASCADE, verbose_name='Автор')
    image = models.ImageField('Обложка', null=True, blank=True, upload_to='%Y/%m/%d')
    volume = models.PositiveIntegerField(null=True, blank=True, verbose_name='Объем книги')
    group = models.ForeignKey('Group', on_delete=models.CASCADE, verbose_name='Группа')
    slug = models.SlugField(verbose_name='URL')
    file = models.FileField(upload_to='list_of_books', verbose_name='Книга')
    background = models.ImageField(null=True, blank=True, upload_to='%Y/%m/%d', verbose_name='Фоновое изображение')
    last_page = models.PositiveIntegerField('Последняя страница', default=0, null=True, blank=True)

    class Meta:
        verbose_name = 'Книга'
        verbose_name_plural = 'Книги'
        ordering = ['-added']

    def __str__(self):
        return self.title

class Quote(models.Model):

    """ Цитата из какой-то книги """

    content = models.TextField(verbose_name='Цитата')
    book = models.ForeignKey(Book, on_delete=models.CASCADE, verbose_name='Книга')

    class Meta:
        verbose_name = 'Цитата'
        verbose_name_plural = 'Цитаты'

    def __str__(self):
        return self.content

class Author(models.Model):

    """ Автор какой-то книги """

    name = models.CharField(max_length=100, verbose_name='Имя')
    description = models.TextField(null=True, blank=True, verbose_name='Описание')
    image = models.ImageField(null=True, blank=True, upload_to='%Y/%m/%d/')
    slug = models.SlugField(verbose_name='URL')

    class Meta:
        verbose_name = 'Автор'
        verbose_name_plural = 'Авторы'

    def __str__(self):
        return self.name

class Group(models.Model):

    """ Группа книг """

    title = models.CharField(max_length=100, verbose_name='Название')
    description = models.TextField(null=True, blank=True, verbose_name='Описание')
    image = models.ImageField(null=True, blank=True, upload_to='%Y/%m/%d/')
    slug = models.SlugField(verbose_name='URL')

    class Meta:
        verbose_name = 'Группа'
        verbose_name_plural = 'Группы'

    def __str__(self):
        return self.title


code:
import pdfplumber
from pytils.translit import slugify
from django.dispatch import receiver
from django.db.models.signals import post_save, pre_save

from main.models import Book

@receiver(post_save, sender=Book)
def add_volume_to_book(instance, **kwargs):
    """ Добавляем количество страниц для книги """

    if not isinstance(instance.volume, int):
        with pdfplumber.open(instance.file.path) as file:
            volume = len(file.pages)
            instance.volume = int(volume)
            instance.save()

@receiver(pre_save, sender=Book)
def add_slug_to_book(instance, **kwargs):
    """ Добавляем slug для книги """

    instance.slug = slugify(instance.title)

license: mit


code:
{% extends 'base.html' %}

{% load static %}
{% load main_filters %}

{% block content %}

Название: {{ book.title }}

Автор: {{ book.author }}

Группа: {{ book.group }}

Избранная: {{ book.is_favorites|convert_boolean }}

Объем книги: {{ book.volume }}

Отложена: {{ book.is_defer|convert_boolean }}

Прочитана: {{ book.is_read|convert_boolean }}

Добавлена: {{ book.added|date:"d-m-Y" }}

Описание: {{ book.description }}

                    {{ book.last_page }}
                    /

                    <form method="post">
                        {% csrf_token %}
                        <button>

                        </button>
                    </form>

{% endblock content %}


code:
{% extends 'base.html' %}
{% block content %}

        {{ author }}
        /
        {{ title }}

            {% if is_favorites %}

            {% else %}

            {% endif %}

                Скрыть меню

                Размер шрифта

                Фон

                <input type="text" placeholder="0" data-page-number>

            {% if previous_page %}
            {{ previous_page }}
            {% endif %}
            {{ page_number }}
            {{ next_page }}
            ...
            {{ total_pages }}

        <form method="post" name="bookmark">
            {% csrf_token %}
            <button type="submit" class="bookmark" data-bookmark="false">

            </button>
        </form>

{{ text }}

{% endblock content %}


code:
{% extends 'base.html' %}

{% block content %}
<div class="library">
    {% include 'includes/list_of_books.html' %}
</div>
{% endblock content %}


code:
from django import template
from django.apps import apps

register = template.Library()

def is_selected_block(current_block, block):
    """ Проверяем выбран ли этот блок """

    if current_block == block:
        return 'selected_block'
    else:
        return ''

def convert_boolean(value):
    """ Преобразовываем Boolean в читаемое значение """

    if value == False:
        return 'Нет'

register.filter('is_selected_block', is_selected_block)
register.filter('convert_boolean', convert_boolean)


code:
from django.test import TestCase

# Create your tests here.


code:
from django.urls import path

from . import views

urlpatterns = [
    path('', views.HomePageDisplay.as_view(), name='home_page'),
    path('library/', views.LibraryDisplay.as_view(), name='library'),
    path('book/<str:slug>/', views.BookDisplay.as_view(), name='book'),
    path('books/', views.BooksDisplay.as_view(), name='books'),
    path('book/<str:book>/<str:action>/<int:page>/', views.BookBookmark.as_view(), name='bookmark'),
    path('book/<str:book>/<int:page_number>/', views.AddingLastPage.as_view(), name='adding_last_page'),
    path('book/<str:book>/<str:action>/', views.BookFavorites.as_view(), name='bookmark'),
]


code:
import re

from main.models import Book
import pdfplumber

def get_book(request, slug):

    """ Получаем книгу """

    def _convert_to_desired_view(text):
        text = text[::-1]
        current_page = ''
        text = re.sub('^\d{,5}?\n', '', text)
        return (text[::-1], current_page)

    def _get_authors_name(book):
        full_name = book.author.name.split()
        name = full_name[0][0] + '. '
        surname = full_name[1]
        return name + surname

    def _get_right_pages(request, book):
        current_page = int(request.GET.get('page'))
        total_pages = book.volume
        previous_page = current_page -1 if (current_page -1) >= 0 else False
        next_page = current_page + 1
        return {'current_page': current_page,
                'total_pages': total_pages,
                'next_page': next_page,
                'previous_page': previous_page}

    def _get_context(request, slug, book):
        page_number = int(request.GET.get('page'))
        pages = _get_right_pages(request, book)
        path_to_book = book.file.path
        authors_name = _get_authors_name(book)
        context = {'page_number': page_number,
                   'author': book.author.name,
                   'title': book.title,
                   'book_slug': book.slug,
                   'is_favorites': book.is_favorites,
                   'bookmark': book.bookmark,
                   **pages}
        with pdfplumber.open(path_to_book) as file:
            page = file.pages[page_number]
            text, current_page = _convert_to_desired_view(page.extract_text())
            context['text'] = text

        return context

    # ВЫШЕ ОПРЕДЕЛЕНИЕ ФУНКЦИЙ

    book = Book.objects.get(slug=slug)

    if request.GET.get('page'):
        template = 'main/contents_of_book.html'
        context = _get_context(request, slug, book)
        return (template, context)

    else:
        template = 'main/book_big.html'
        context = {'book': book}
        return (template, context)

def get_list_of_books():

    """ Получаем список книг в нужном виде """

    def get_title(book):
        modified_book = book[0].split()
        if len(modified_book) > 4: return ' '.join(modified_book[:4]) + '...'
        else: return book[0]

    def get_description(book):
        return ' '.join(book[1].split()[:25]) + '...'

    # ВЫШЕ ОПРЕДЕЛЕНИЕ ФУНКЦИЙ

    result = []
    books = Book.objects.values_list('title', 'description', 'slug', 'image').all()

    for book in books:
        title = get_title(book)
        description = get_description(book)

        result.append({'title': title, 'description': description, 'slug': book[2], 'image': book[3]})

    return result

def add_bookmark(request, book, action, page):

    """ Добавляем в закладку страницу """

    book_orm = Book.objects.get(slug=book)

    if action == 'add':
        book_orm.bookmark = page
        book_orm.save()
    elif action == 'remove':
        book_orm.bookmark = 0
        book_orm.save()

def add_last_page(request, book, page_number):

    book = Book.objects.get(slug=book)
    book.last_page = page_number
    book.save()

def get_information_about_book(request, slug):

    """ Получаем информацию о книге """

    book = Book.objects.get(slug=slug)
    context = {'last_page': book.last_page,
               'volume': book.volume,
               'favorites': book.is_favorites}
    return context

def action_favorites(book, action):

    """ Выполняем какое-то действие с закладкой """

    book = Book.objects.get(slug=book)

    if action == 'remove_favorites':
        book.is_favorites = False
    elif action == 'add_favorites':
        book.is_favorites = True

    book.save()


code:
from django.http import HttpResponse
from django.shortcuts import render
from django.views import generic
from django.http import JsonResponse

from . import utils
from . import models

class HomePageDisplay(generic.View):

    """ Отображение домашней страницы """

    def get(self, request):
        return render(request, 'main/home_page.html')

class LibraryDisplay(generic.View):

    """ Отображение библиотеки """

    def get(self, request):
        books = models.Book.objects.all()
        context = {'block': 'library',
                   'books': books}

        return render(request, 'main/library.html', context=context)

class BookDisplay(generic.View):

    """ Отображение книги """

    def get(self, request, slug):

        template, context = utils.get_book(request, slug)

        if request.GET.get('update'):
            return JsonResponse(context)
        elif request.GET.get('information'):
            context = utils.get_information_about_book(request, slug)
            return JsonResponse(context)
        else:
            return render(request, template, context)

class BooksDisplay(generic.View):

    """ Отображаем список книг """

    def get(self, request):
        result = utils.get_list_of_books()
        context = {'books': result}
        return render(request, 'list_of_books.html', context)

class BookBookmark(generic.View):

    def post(self, request, book, action, page):
        utils.add_bookmark(request, book, action, page)
        return HttpResponse('')

class AddingLastPage(generic.View):

    def get(self, request, book, page_number):
        utils.add_last_page(request, book, page_number)
        return HttpResponse('')

class BookFavorites(generic.View):

    def post(self, request, book, action):
        utils.action_favorites(book, action)
        return HttpResponse('')


code:
#!/usr/bin/env python
"""Django's command-line utility for administrative tasks."""
import os
import sys

def main():
    """Run administrative tasks."""
    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings')
    try:
        from django.core.management import execute_from_command_line
    except ImportError as exc:
        raise ImportError(
            "Couldn't import Django. Are you sure it's installed and "
            "available on your PYTHONPATH environment variable? Did you "
            "forget to activate a virtual environment?"
        ) from exc
    execute_from_command_line(sys.argv)

if __name__ == '__main__':
    main()


hulk: bin/hulk

deprecated: Legacy versions of mkdirp are no longer supported. Please update to mkdirp 1.x. (Note that the API surface has changed to use Promises in 1.x.)


code:
[[package]]
name = "cffi"
version = "1.15.0"
description = "Foreign Function Interface for Python calling C code."
category = "main"
optional = false
python-versions = "*"

[package.dependencies]
pycparser = "*"

[[package]]
name = "charset-normalizer"
version = "2.0.12"
description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
category = "main"
optional = false
python-versions = ">=3.5.0"

[package.extras]
unicode_backport = ["unicodedata2"]

[[package]]
name = "cryptography"
version = "37.0.2"
description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers."
category = "main"
optional = false
python-versions = ">=3.6"

[package.dependencies]
cffi = ">=1.12"

[package.extras]
docs = ["sphinx (>=1.6.5,!=1.8.0,!=3.1.0,!=3.1.1)", "sphinx-rtd-theme"]
docstest = ["pyenchant (>=1.6.11)", "twine (>=1.12.0)", "sphinxcontrib-spelling (>=4.0.1)"]
pep8test = ["black", "flake8", "flake8-import-order", "pep8-naming"]
sdist = ["setuptools_rust (>=0.11.4)"]
ssh = ["bcrypt (>=3.1.5)"]
test = ["pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-subtests", "pytest-xdist", "pretend", "iso8601", "pytz", "hypothesis (>=1.11.4,!=3.79.2)"]

[[package]]
name = "pdfminer.six"
version = "20220524"
description = "PDF parser and analyzer"
category = "main"
optional = false
python-versions = ">=3.6"

[package.dependencies]
charset-normalizer = ">=2.0.0"
cryptography = ">=36.0.0"

[package.extras]
dev = ["black", "mypy (==0.931)", "nox", "pytest"]
docs = ["sphinx", "sphinx-argparse"]
image = ["pillow"]

[[package]]
name = "pdfplumber"
version = "0.7.0"
description = "Plumb a PDF for detailed information about each char, rectangle, and line."
category = "main"
optional = false
python-versions = ">=3.7"

[package.dependencies]
"pdfminer.six" = "20220524"
Pillow = ">=9.1"
Wand = ">=0.6.7"

[[package]]
name = "pillow"
version = "9.1.1"
description = "Python Imaging Library (Fork)"
category = "main"
optional = false
python-versions = ">=3.7"

[package.extras]
docs = ["olefile", "sphinx (>=2.4)", "sphinx-copybutton", "sphinx-issues (>=3.0.1)", "sphinx-removed-in", "sphinx-rtd-theme (>=1.0)", "sphinxext-opengraph"]
tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"]

[[package]]
name = "pycparser"
version = "2.21"
description = "C parser in Python"
category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"

[[package]]
name = "pypdf2"
version = "1.28.4"
description = "A pure-python PDF library capable of splitting, merging, cropping, and transforming PDF files"
category = "main"
optional = false
python-versions = ">=2.7"

[[package]]
name = "pytils"
version = "0.4.1"
description = "Russian-specific string utils"
category = "main"
optional = false
python-versions = "^3.7"

[[package]]
name = "wand"
version = "0.6.7"
description = "Ctypes-based simple MagickWand API binding for Python"
category = "main"
optional = false
python-versions = "*"

[package.extras]
doc = ["Sphinx (>=2.4.1)"]
test = ["pytest (>=5.3.5)"]

[metadata]
lock-version = "1.1"
python-versions = "^3.10"
content-hash = "3bdb559aadbf9bb6dc119592401139bedd9b784202c8fc7fc3de82ad460f0422"

[metadata.files]
cffi = [
    {file = "cffi-1.15.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:c2502a1a03b6312837279c8c1bd3ebedf6c12c4228ddbad40912d671ccc8a962"},
    {file = "cffi-1.15.0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:23cfe892bd5dd8941608f93348c0737e369e51c100d03718f108bf1add7bd6d0"},
    {file = "cffi-1.15.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:41d45de54cd277a7878919867c0f08b0cf817605e4eb94093e7516505d3c8d14"},
    {file = "cffi-1.15.0-cp27-cp27m-win32.whl", hash = "sha256:4a306fa632e8f0928956a41fa8e1d6243c71e7eb59ffbd165fc0b41e316b2474"},
    {file = "cffi-1.15.0-cp27-cp27m-win_amd64.whl", hash = "sha256:e7022a66d9b55e93e1a845d8c9eba2a1bebd4966cd8bfc25d9cd07d515b33fa6"},
    {file = "cffi-1.15.0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:14cd121ea63ecdae71efa69c15c5543a4b5fbcd0bbe2aad864baca0063cecf27"},
    {file = "cffi-1.15.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:d4d692a89c5cf08a8557fdeb329b82e7bf609aadfaed6c0d79f5a449a3c7c023"},
    {file = "cffi-1.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0104fb5ae2391d46a4cb082abdd5c69ea4eab79d8d44eaaf79f1b1fd806ee4c2"},
    {file = "cffi-1.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:91ec59c33514b7c7559a6acda53bbfe1b283949c34fe7440bcf917f96ac0723e"},
    {file = "cffi-1.15.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:f5c7150ad32ba43a07c4479f40241756145a1f03b43480e058cfd862bf5041c7"},
    {file = "cffi-1.15.0-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:00c878c90cb53ccfaae6b8bc18ad05d2036553e6d9d1d9dbcf323bbe83854ca3"},
    {file = "cffi-1.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:abb9a20a72ac4e0fdb50dae135ba5e77880518e742077ced47eb1499e29a443c"},
    {file = "cffi-1.15.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a5263e363c27b653a90078143adb3d076c1a748ec9ecc78ea2fb916f9b861962"},
    {file = "cffi-1.15.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f54a64f8b0c8ff0b64d18aa76675262e1700f3995182267998c31ae974fbc382"},
    {file = "cffi-1.15.0-cp310-cp310-win32.whl", hash = "sha256:c21c9e3896c23007803a875460fb786118f0cdd4434359577ea25eb556e34c55"},
    {file = "cffi-1.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:5e069f72d497312b24fcc02073d70cb989045d1c91cbd53979366077959933e0"},
    {file = "cffi-1.15.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:64d4ec9f448dfe041705426000cc13e34e6e5bb13736e9fd62e34a0b0c41566e"},
    {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2756c88cbb94231c7a147402476be2c4df2f6078099a6f4a480d239a8817ae39"},
    {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b96a311ac60a3f6be21d2572e46ce67f09abcf4d09344c49274eb9e0bf345fc"},
    {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75e4024375654472cc27e91cbe9eaa08567f7fbdf822638be2814ce059f58032"},
    {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:59888172256cac5629e60e72e86598027aca6bf01fa2465bdb676d37636573e8"},
    {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:27c219baf94952ae9d50ec19651a687b826792055353d07648a5695413e0c605"},
    {file = "cffi-1.15.0-cp36-cp36m-win32.whl", hash = "sha256:4958391dbd6249d7ad855b9ca88fae690783a6be9e86df65865058ed81fc860e"},
    {file = "cffi-1.15.0-cp36-cp36m-win_amd64.whl", hash = "sha256:f6f824dc3bce0edab5f427efcfb1d63ee75b6fcb7282900ccaf925be84efb0fc"},
    {file = "cffi-1.15.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:06c48159c1abed75c2e721b1715c379fa3200c7784271b3c46df01383b593636"},
    {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:c2051981a968d7de9dd2d7b87bcb9c939c74a34626a6e2f8181455dd49ed69e4"},
    {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:fd8a250edc26254fe5b33be00402e6d287f562b6a5b2152dec302fa15bb3e997"},
    {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:91d77d2a782be4274da750752bb1650a97bfd8f291022b379bb8e01c66b4e96b"},
    {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:45db3a33139e9c8f7c09234b5784a5e33d31fd6907800b316decad50af323ff2"},
    {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:263cc3d821c4ab2213cbe8cd8b355a7f72a8324577dc865ef98487c1aeee2bc7"},
    {file = "cffi-1.15.0-cp37-cp37m-win32.whl", hash = "sha256:17771976e82e9f94976180f76468546834d22a7cc404b17c22df2a2c81db0c66"},
    {file = "cffi-1.15.0-cp37-cp37m-win_amd64.whl", hash = "sha256:3415c89f9204ee60cd09b235810be700e993e343a408693e80ce7f6a40108029"},
    {file = "cffi-1.15.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:4238e6dab5d6a8ba812de994bbb0a79bddbdf80994e4ce802b6f6f3142fcc880"},
    {file = "cffi-1.15.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0808014eb713677ec1292301ea4c81ad277b6cdf2fdd90fd540af98c0b101d20"},
    {file = "cffi-1.15.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:57e9ac9ccc3101fac9d6014fba037473e4358ef4e89f8e181f8951a2c0162024"},
    {file = "cffi-1.15.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b6c2ea03845c9f501ed1313e78de148cd3f6cad741a75d43a29b43da27f2e1e"},
    {file = "cffi-1.15.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:10dffb601ccfb65262a27233ac273d552ddc4d8ae1bf93b21c94b8511bffe728"},
    {file = "cffi-1.15.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:786902fb9ba7433aae840e0ed609f45c7bcd4e225ebb9c753aa39725bb3e6ad6"},
    {file = "cffi-1.15.0-cp38-cp38-win32.whl", hash = "sha256:da5db4e883f1ce37f55c667e5c0de439df76ac4cb55964655906306918e7363c"},
    {file = "cffi-1.15.0-cp38-cp38-win_amd64.whl", hash = "sha256:181dee03b1170ff1969489acf1c26533710231c58f95534e3edac87fff06c443"},
    {file = "cffi-1.15.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:45e8636704eacc432a206ac7345a5d3d2c62d95a507ec70d62f23cd91770482a"},
    {file = "cffi-1.15.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:31fb708d9d7c3f49a60f04cf5b119aeefe5644daba1cd2a0fe389b674fd1de37"},
    {file = "cffi-1.15.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6dc2737a3674b3e344847c8686cf29e500584ccad76204efea14f451d4cc669a"},
    {file = "cffi-1.15.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:74fdfdbfdc48d3f47148976f49fab3251e550a8720bebc99bf1483f5bfb5db3e"},
    {file = "cffi-1.15.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffaa5c925128e29efbde7301d8ecaf35c8c60ffbcd6a1ffd3a552177c8e5e796"},
    {file = "cffi-1.15.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3f7d084648d77af029acb79a0ff49a0ad7e9d09057a9bf46596dac9514dc07df"},
    {file = "cffi-1.15.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ef1f279350da2c586a69d32fc8733092fd32cc8ac95139a00377841f59a3f8d8"},
    {file = "cffi-1.15.0-cp39-cp39-win32.whl", hash = "sha256:2a23af14f408d53d5e6cd4e3d9a24ff9e05906ad574822a10563efcef137979a"},
    {file = "cffi-1.15.0-cp39-cp39-win_amd64.whl", hash = "sha256:3773c4d81e6e818df2efbc7dd77325ca0dcb688116050fb2b3011218eda36139"},
    {file = "cffi-1.15.0.tar.gz", hash = "sha256:920f0d66a896c2d99f0adbb391f990a84091179542c205fa53ce5787aff87954"},
]
charset-normalizer = [
    {file = "charset-normalizer-2.0.12.tar.gz", hash = "sha256:2857e29ff0d34db842cd7ca3230549d1a697f96ee6d3fb071cfa6c7393832597"},
    {file = "charset_normalizer-2.0.12-py3-none-any.whl", hash = "sha256:6881edbebdb17b39b4eaaa821b438bf6eddffb4468cf344f09f89def34a8b1df"},
]
cryptography = [
    {file = "cryptography-37.0.2-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:ef15c2df7656763b4ff20a9bc4381d8352e6640cfeb95c2972c38ef508e75181"},
    {file = "cryptography-37.0.2-cp36-abi3-macosx_10_10_x86_64.whl", hash = "sha256:3c81599befb4d4f3d7648ed3217e00d21a9341a9a688ecdd615ff72ffbed7336"},
    {file = "cryptography-37.0.2-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:2bd1096476aaac820426239ab534b636c77d71af66c547b9ddcd76eb9c79e004"},
    {file = "cryptography-37.0.2-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:31fe38d14d2e5f787e0aecef831457da6cec68e0bb09a35835b0b44ae8b988fe"},
    {file = "cryptography-37.0.2-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:093cb351031656d3ee2f4fa1be579a8c69c754cf874206be1d4cf3b542042804"},
    {file = "cryptography-37.0.2-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:59b281eab51e1b6b6afa525af2bd93c16d49358404f814fe2c2410058623928c"},
    {file = "cryptography-37.0.2-cp36-abi3-manylinux_2_24_x86_64.whl", hash = "sha256:0cc20f655157d4cfc7bada909dc5cc228211b075ba8407c46467f63597c78178"},
    {file = "cryptography-37.0.2-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:f8ec91983e638a9bcd75b39f1396e5c0dc2330cbd9ce4accefe68717e6779e0a"},
    {file = "cryptography-37.0.2-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:46f4c544f6557a2fefa7ac8ac7d1b17bf9b647bd20b16decc8fbcab7117fbc15"},
    {file = "cryptography-37.0.2-cp36-abi3-win32.whl", hash = "sha256:731c8abd27693323b348518ed0e0705713a36d79fdbd969ad968fbef0979a7e0"},
    {file = "cryptography-37.0.2-cp36-abi3-win_amd64.whl", hash = "sha256:471e0d70201c069f74c837983189949aa0d24bb2d751b57e26e3761f2f782b8d"},
    {file = "cryptography-37.0.2-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a68254dd88021f24a68b613d8c51d5c5e74d735878b9e32cc0adf19d1f10aaf9"},
    {file = "cryptography-37.0.2-pp37-pypy37_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:a7d5137e556cc0ea418dca6186deabe9129cee318618eb1ffecbd35bee55ddc1"},
    {file = "cryptography-37.0.2-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:aeaba7b5e756ea52c8861c133c596afe93dd716cbcacae23b80bc238202dc023"},
    {file = "cryptography-37.0.2-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95e590dd70642eb2079d280420a888190aa040ad20f19ec8c6e097e38aa29e06"},
    {file = "cryptography-37.0.2-pp38-pypy38_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:1b9362d34363f2c71b7853f6251219298124aa4cc2075ae2932e64c91a3e2717"},
    {file = "cryptography-37.0.2-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:e53258e69874a306fcecb88b7534d61820db8a98655662a3dd2ec7f1afd9132f"},
    {file = "cryptography-37.0.2-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:1f3bfbd611db5cb58ca82f3deb35e83af34bb8cf06043fa61500157d50a70982"},
    {file = "cryptography-37.0.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:419c57d7b63f5ec38b1199a9521d77d7d1754eb97827bbb773162073ccd8c8d4"},
    {file = "cryptography-37.0.2-pp39-pypy39_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:dc26bb134452081859aa21d4990474ddb7e863aa39e60d1592800a8865a702de"},
    {file = "cryptography-37.0.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:3b8398b3d0efc420e777c40c16764d6870bcef2eb383df9c6dbb9ffe12c64452"},
    {file = "cryptography-37.0.2.tar.gz", hash = "sha256:f224ad253cc9cea7568f49077007d2263efa57396a2f2f78114066fd54b5c68e"},
]
"pdfminer.six" = [
    {file = "pdfminer.six-20220524-py3-none-any.whl", hash = "sha256:7e19b857ec76bcbd35665909ad8965a481ad48d9bdff6c45f8522ee66f8a7aab"},
    {file = "pdfminer.six-20220524.tar.gz", hash = "sha256:5a64c924410ac48501d6060b21638bf401db69f5b1bd57207df7fbc070ac8ae2"},
]
pdfplumber = [
    {file = "pdfplumber-0.7.0-py3-none-any.whl", hash = "sha256:bf5037fe2d553e85aa072fb18ddc97cb02294f54bfa8bf824290ab8dd9967f58"},
    {file = "pdfplumber-0.7.0.tar.gz", hash = "sha256:152a8a989080e4be6b442c9650145040864eb5baf8a148d2a268095255559be8"},
]
pillow = [
    {file = "Pillow-9.1.1-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:42dfefbef90eb67c10c45a73a9bc1599d4dac920f7dfcbf4ec6b80cb620757fe"},
    {file = "Pillow-9.1.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ffde4c6fabb52891d81606411cbfaf77756e3b561b566efd270b3ed3791fde4e"},
    {file = "Pillow-9.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c857532c719fb30fafabd2371ce9b7031812ff3889d75273827633bca0c4602"},
    {file = "Pillow-9.1.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:59789a7d06c742e9d13b883d5e3569188c16acb02eeed2510fd3bfdbc1bd1530"},
    {file = "Pillow-9.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4d45dbe4b21a9679c3e8b3f7f4f42a45a7d3ddff8a4a16109dff0e1da30a35b2"},
    {file = "Pillow-9.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e9ed59d1b6ee837f4515b9584f3d26cf0388b742a11ecdae0d9237a94505d03a"},
    {file = "Pillow-9.1.1-cp310-cp310-win32.whl", hash = "sha256:b3fe2ff1e1715d4475d7e2c3e8dabd7c025f4410f79513b4ff2de3d51ce0fa9c"},
    {file = "Pillow-9.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:5b650dbbc0969a4e226d98a0b440c2f07a850896aed9266b6fedc0f7e7834108"},
    {file = "Pillow-9.1.1-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:0b4d5ad2cd3a1f0d1df882d926b37dbb2ab6c823ae21d041b46910c8f8cd844b"},
    {file = "Pillow-9.1.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9370d6744d379f2de5d7fa95cdbd3a4d92f0b0ef29609b4b1687f16bc197063d"},
    {file = "Pillow-9.1.1-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b761727ed7d593e49671d1827044b942dd2f4caae6e51bab144d4accf8244a84"},
    {file = "Pillow-9.1.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a66fe50386162df2da701b3722781cbe90ce043e7d53c1fd6bd801bca6b48d4"},
    {file = "Pillow-9.1.1-cp37-cp37m-win32.whl", hash = "sha256:2b291cab8a888658d72b575a03e340509b6b050b62db1f5539dd5cd18fd50578"},
    {file = "Pillow-9.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:1d4331aeb12f6b3791911a6da82de72257a99ad99726ed6b63f481c0184b6fb9"},
    {file = "Pillow-9.1.1-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:8844217cdf66eabe39567118f229e275f0727e9195635a15e0e4b9227458daaf"},
    {file = "Pillow-9.1.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b6617221ff08fbd3b7a811950b5c3f9367f6e941b86259843eab77c8e3d2b56b"},
    {file = "Pillow-9.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20d514c989fa28e73a5adbddd7a171afa5824710d0ab06d4e1234195d2a2e546"},
    {file = "Pillow-9.1.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:088df396b047477dd1bbc7de6e22f58400dae2f21310d9e2ec2933b2ef7dfa4f"},
    {file = "Pillow-9.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:53c27bd452e0f1bc4bfed07ceb235663a1df7c74df08e37fd6b03eb89454946a"},
    {file = "Pillow-9.1.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:3f6c1716c473ebd1649663bf3b42702d0d53e27af8b64642be0dd3598c761fb1"},
    {file = "Pillow-9.1.1-cp38-cp38-win32.whl", hash = "sha256:c67db410508b9de9c4694c57ed754b65a460e4812126e87f5052ecf23a011a54"},
    {file = "Pillow-9.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:f054b020c4d7e9786ae0404278ea318768eb123403b18453e28e47cdb7a0a4bf"},
    {file = "Pillow-9.1.1-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:c17770a62a71718a74b7548098a74cd6880be16bcfff5f937f900ead90ca8e92"},
    {file = "Pillow-9.1.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f3f6a6034140e9e17e9abc175fc7a266a6e63652028e157750bd98e804a8ed9a"},
    {file = "Pillow-9.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f372d0f08eff1475ef426344efe42493f71f377ec52237bf153c5713de987251"},
    {file = "Pillow-9.1.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09e67ef6e430f90caa093528bd758b0616f8165e57ed8d8ce014ae32df6a831d"},
    {file = "Pillow-9.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66daa16952d5bf0c9d5389c5e9df562922a59bd16d77e2a276e575d32e38afd1"},
    {file = "Pillow-9.1.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d78ca526a559fb84faaaf84da2dd4addef5edb109db8b81677c0bb1aad342601"},
    {file = "Pillow-9.1.1-cp39-cp39-win32.whl", hash = "sha256:55e74faf8359ddda43fee01bffbc5bd99d96ea508d8a08c527099e84eb708f45"},
    {file = "Pillow-9.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:7c150dbbb4a94ea4825d1e5f2c5501af7141ea95825fadd7829f9b11c97aaf6c"},
    {file = "Pillow-9.1.1-pp37-pypy37_pp73-macosx_10_10_x86_64.whl", hash = "sha256:769a7f131a2f43752455cc72f9f7a093c3ff3856bf976c5fb53a59d0ccc704f6"},
    {file = "Pillow-9.1.1-pp37-pypy37_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:488f3383cf5159907d48d32957ac6f9ea85ccdcc296c14eca1a4e396ecc32098"},
    {file = "Pillow-9.1.1-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b525a356680022b0af53385944026d3486fc8c013638cf9900eb87c866afb4c"},
    {file = "Pillow-9.1.1-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:6e760cf01259a1c0a50f3c845f9cad1af30577fd8b670339b1659c6d0e7a41dd"},
    {file = "Pillow-9.1.1-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a4165205a13b16a29e1ac57efeee6be2dfd5b5408122d59ef2145bc3239fa340"},
    {file = "Pillow-9.1.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:937a54e5694684f74dcbf6e24cc453bfc5b33940216ddd8f4cd8f0f79167f765"},
    {file = "Pillow-9.1.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:baf3be0b9446a4083cc0c5bb9f9c964034be5374b5bc09757be89f5d2fa247b8"},
    {file = "Pillow-9.1.1.tar.gz", hash = "sha256:7502539939b53d7565f3d11d87c78e7ec900d3c72945d4ee0e2f250d598309a0"},
]
pycparser = [
    {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"},
    {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"},
]
pypdf2 = [
    {file = "PyPDF2-1.28.4-py3-none-any.whl", hash = "sha256:7d80914ebb4cf93348d270f27a7a5eb741b912be63927da65c4c6b292c2f6b10"},
    {file = "PyPDF2-1.28.4.tar.gz", hash = "sha256:04ce42cd05707881fedbca3164744511805fe8c30618ae4cfb2b940e3368d5ad"},
]
pytils = [
    {file = "pytils-0.4.1.tar.gz", hash = "sha256:aa109b1bc0ec4bdb7c9768520110348d8aeb0706600e6cf641272e6850e26eab"},
]
wand = [
    {file = "Wand-0.6.7-py2.py3-none-any.whl", hash = "sha256:5ba497e90741a05ebce4603b04ee843150c566482a753554da54dc57d8503bba"},
    {file = "Wand-0.6.7.tar.gz", hash = "sha256:ebc01bccc25dba68414ab55b482341f9ad2b197d7f49d5e724f339bbf63fb6db"},
]


code:
.book-big {
    display: flex;
    flex-direction: column;
}

.book-big .description-big {
    display: flex;
    margin-bottom: 1em;
    color: #4c4c4c;
}

.book-big img {
    border-radius: 5px;
    margin-right: 5em;
}

.book-big .block-characteristic {
    margin: .1em 0;
    font-size: 1.2em;
    padding: .1em 0;
}

.book-big .block-characteristic:nth-child(even) {
    background-color: #f1f1f1;
}

.book-big .read {
    text-decoration: none;
    color: #4c4c4c;
}

.action-button {
    padding: 0.5em;
    background: white;
    border: 1px solid #e7e7e7;
    border-radius: 5px;
    font-size: 1.1em;
    color: #4c4c4c;
    font-size: 1.1em;
    margin-right: .4em;
    cursor: pointer;
}

.action-button:hover {
    box-shadow: 1px 2px 4px 0px #626262;
    transition: box-shadow .1s ease-in;
}

.action-button i {
    color: #4c4c4c;
}

.action-button i:hover {
    cursor: pointer;
}

.additional-information-book-big {
    height: 3em;
    width: 29.8em;
    display: flex;
    justify-content: space-between;
    align-items: center;
    flex-direction: column;
    padding: 0.5em;
}

.additional-information-book-big .progress {
    width: 100%;
    height: .1em;
    display: flex;
    flex-direction: column;
    align-items: end;
    padding: 0.1em 0;
    border-radius: 1em;
}

.additional-information-book-big .progress .number-of-pages span {
    font-size: .8em;
    font-weight: bold;
}

.additional-information-book-big .progress .number-of-pages .percentages {
    margin-bottom: 0.5em;
    font-size: .8em;
    font-weight: bold;
}

.additional-information-book-big .progress .total {
    background-color: white;
    width: 100%;
    height: 3.5px;
    border-radius: 1em;
    margin-top: .2em;
}

.additional-information-book-big .progress .now {
    height: 3.5px;
    width: 50%;
    background-color: #4c4c4c;
    border-radius: 1em;
}

.additional-information-book-big .actions-book-big {
    display: flex;
    margin-left: 1.5em;
    margin-top: 2.5em;
}

.additional-information-book-big .actions-book-big .favorites button {
    border: none;
    background: none;
    outline: none;
}

.color-inactive-favorites {
    /* color: ; */
    color: #4c4c4c;
    /* transition: color .5s ease-out; */
}


code:
.book-search-window {
    display: none;
    background-color: #f5f5f5;
    position: absolute;
    border: 1px solid #282828;
    border-radius: 5px;
    flex-direction: column;
    color: #282828;
    padding: .3em .5em 1em .3em;
    overflow-y: auto;
    overflow-x: hidden;
    max-height: 22em;
}

.book-search-window .search {
    display: flex;
    align-items: center;
    padding-bottom: .5em;
    border-bottom: 1px solid #bfbfbf;
    margin-bottom: 1em;
}

.book-search-window .books {
    color: black;
    width: 18em;
}

.book-search-window .books .book {
    margin-bottom: 1em;
    display: none;
}

.book-search-window .books .book a {
    display: contents;
}

.book-search-window .books .book:hover {
    box-shadow: 0px 10px 8px 1px #28282885;
    transition: box-shadow .3s ease;
    cursor: pointer;
}

.book-search-window .books .book-title {
    background-color: #282828;
    color: white;
    padding: .2em;
    border-radius: 5px;
}

.book-search-window .books .book-description {
    font-size: .7em;
    color: #282828;
}

.book-search-window .books .book img {
    height: 8em;
    width: 9em;
}

.book-search-window .search i {
    color: white;
    background-color: #282828;
    padding: .35em;
    margin-left: .2em;
    border-radius: 5px;
}

.book-search-window input{
    outline: none;
    padding: .1em;
    font-size: 1em;
    width: 100%;
}

.book-search-window input::placeholder {
    color: #ababab;
    font-family: monospace;
}

.show-element {
    opacity: 1;
}


code:
.menu {
    border-bottom: 1px solid #dfdfdf;
    display: flex;
    justify-content: space-between;
    padding: .5em .5em 1em .5em;
    margin-bottom: 2em;
    font-size: .9em;
    position: relative;
}

.menu .bookmark {
    position: absolute;
    left: 96%;
    top: 100%;
    font-size: 1.3em;
    color: #c9c9c9;
    cursor: pointer;
    border: none;
    outline: none;
}

.menu .appearance {
    display: flex;
}

.menu .pagination {
    display: flex;
    align-items: end;
}

.menu .pagination .page-entry input {
    background-color: #f9f9f9;
    color: #282828;
    padding: .5em;
    border-radius: 5px;
    outline: none;
    width: 4em;
    border-width: thin;
    height: 2em;
    border: 1px solid #282828;
}

.menu .pagination .page-entry .open-page {
    border: none;
    background-color: #ebebeb;
    padding: .5em;
    margin-right: 1em;
    border-radius: 5px;
    cursor: pointer;
    display: inline;
}

.menu .pagination .page-entry button i {
    color: #282828;
}

.menu .pagination .page-entry input:placeholder {
}

.menu .pagination a {
    text-decoration: none;
    margin-right: .4em;
    background-color: #ebebeb;
    color: #282828;
    padding: .5em;
    border-radius: 5px;
}

.menu .pagination a.current-page {
    background-color: #f9f9f9;
}

.menu .appearance>div {
    margin-right: .6em;
    padding: .5em;
    border-radius: 5px;
    display: flex;
    justify-content: space-between;
    align-items: center;
}

.menu .appearance>div:not(.hide-sidebar) i:hover {
    background-color: #ededed;
}

.menu .appearance .hide-sidebar {
    background-color: #ebebeb;
}

.menu .appearance .hide-sidebar:hover {
    background-color: #e3e3e3;
    transition: background-color .2s ease-in;
}

.menu .appearance>div span {
    margin-right: 1em;
}

.menu .appearance div.font-size i {
    background-color: #ebebeb;
    border-radius: 5px;
    padding: 0.3em;
    font-size: .85em;
}

.menu .appearance>div i:nth-child(3) {
    margin: 0 .6em;
}

.menu .appearance>div:hover {
    cursor: pointer;
}

.menu .appearance div.background-color {
}

.menu .appearance div.background-color span:not(:first-child) {
    margin-right: .8em;
    border: 1px solid #282828;
    width: 1em;
    height: 1em;
    border-radius: 50%;
}

.menu .appearance div.background-color span.first-color {
    background-color: #f5f5f5;
}

.menu .appearance div.background-color span.second-color {
    background-color: #fff5d0;
}

.menu .appearance div.background-color span.third-color {
    background-color: #282828;
}

.book-designation {
    color: #c1c1c1;
    padding-left: 1em;
    margin-bottom: 1em;
    font-size: .9em;
}

.book-designation a {
    color: #c1c1c1;
}

.content-of-book {
    display: flex;
    justify-content: space-between;
    align-items: center;
    height: 87%;
}

.content-of-book .move-page {
    width: 3em;
    display: flex;
    border-radius: 5px;
    height: 100%;
    align-items: center;
    justify-content: center;
    width: 4em;
    cursor: pointer;
}

.content-of-book .move-page[data-previous-page]::before {
    content: '';
    height: 8em;
    width: 1px;
    margin-right: 3.9em;
    background-color: #dfdfdf;
    position: absolute;
}

.content-of-book .move-page[data-next-page]::after {
    content: '';
    height: 8em;
    width: 1px;
    margin-right: -3.9em;
    background-color: #dfdfdf;
    position: absolute;
}

.content-of-book .move-page:hover {
    background: #f9f9f9;
    transition: background .3s ease-in-out;
}

.content-of-book i {
    color: #ddd;
}

.text {
    white-space: pre-line;
    text-align: left;
    letter-spacing: .04em;
    word-spacing: 0.2em;
    line-height: 1.2em;
    display: flex;
    justify-content: center;
    font-size: 1.2em;
}

.current-page {
    text-align: end;
    margin-top: 1em;
}

@keyframes hide-menu-to-left {
    0% {
        transform: translateX(0em);
        opacity: 1;
    }

    100% {
        transform: translateX(-18em);
        opacity: 0;
    }
}

@keyframes move-content-to-right {
    0% {
        transform: translateX(0em);
    }

    100% {
        transform: translateX(8em);
    }
}

.color-active-bookmark {
    color: #f999ff;
}

.color-inactive-bookmark {
    color: #c9c9c9;
}

.color-active-favorites {
    font-size: .8em;
    color: #f5dc65;
}

.color-inactive-favorites {
    font-size: .8em;
    color: #c1c1c1;
}


code:
.list-of-books {
    display: flex;
    flex-direction: column;
}

.book {
    width: 100%;
    display: flex;
    margin-bottom: 2em;
    border-radius: 5px;
    background-color: white;
}

.book img.image-middle {
    width: 22%;
    border-radius: 5px;
}

.book .book-content {
    width: 100%;
    margin: 0 0em 0em 1em;
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    padding-bottom: 1.5em;
}

.book .book-content .title {
    font-size: 1.5em;
    background-color: #282828;
    color: white;
    padding: .2em;
    border-radius: 5px;
}

.book .book-content .description {
    padding-right: 2em;
    display: flex;
    flex-direction: column;
    font-size: 1.1em;
}

.book .book-content .description .read {
    background-color: #f5f5f5;
    padding: .5em;
    width: 8.5em;
    margin-top: 1em;
}

.book .book-content .description .read a {
    color: #282828;
    text-decoration: none;
}


code:
* {
    margin: 0;
    padding: 0;
    list-style: none;
    box-sizing: border-box;
}

body {
    background-color: white;
    font-size: 14px;
    color: #262626;
    font-family: 'Roboto', sans-serif;
}

.shadow {
    box-shadow: 3px 3px 8px 1px #7c7c7c;
}

.wrapper {
    width: 100%;
    height: 100%;
    display: flex;
    padding: 1em 2em;
    display: flex;
    justify-content: center;
}

.navigation-menu {
    width: 20%;
    padding: 1em 1em 2em 1.5em;
    border-radius: 5px;
    margin-right: 2em;
    background-color: #282828;
}

.navigation-menu div.logo {
    font-size: 1.5em;
    font-weight: bold;
    margin-bottom: .7em;
    display: flex;
    justify-content: space-between;
    align-items: center;
    color: white;
}

.navigation-menu div.logo a {
    text-decoration: none;
    color: white;
}

.navigation-menu div.logo i {
    font-size: .7em;
}

.navigation-menu .other-information {
    font-size: 1.1em;
    border-radius: 5px;
    display: flex;
    flex-direction: column;
    justify-content: center;
}

.navigation-menu .other-information div.other {
    margin-bottom: .5em;
    padding: .3em;
    border-bottom: 1px solid #f5f5f5;
    width: 70%;
    color: white;
}

.navigation-menu .other-information .open-book {
    position: relative;
}

/* .navigation-menu .other-information div:hover {
}

.navigation-menu .other-information a:hover {
    background-color: white;
    transition: background-color .2s ease-in;
    color: #282828;
    color: #282828;
} */

.navigation-menu .other-information a {
    padding: .4em 0;
    text-decoration: none;
    color: white;
}

.content {
    width: 80%;
    padding: 2em 2em 4em 2em;
    background-color: #f5f5f5;
    border-radius: 5px;
    min-height: 95vh;
}

.selected_block {
    background-color: #f5f5f5;
}

.selected_block a.block {
    color: #282828;
}

.transparent {
    opacity: 0;
    transition: opacity .5s ease;
}


code:
const base = {

}

base.sendAjaxRequest = (url, method, body=null) => {
    return new Promise((resolve, reject) => {

        function setHeaders() {
            // Устанавливаем заголовки

            if (body) {
                xhr.setRequestHeader('Content-Type', 'application/json')
                xhr.setRequestHeader('X-CSRFToken', body.token)
            }
        }

        // ВЫШЕ ОПРЕДЕЛЕНИЕ ФУНКЦИЙ

        const xhr = new XMLHttpRequest();
        xhr.responseType = 'json';
        xhr.open(method, url);
        setHeaders(body);
        xhr.onload = () => {
            resolve(xhr.response)
        }

        if (body) xhr.send(JSON.stringify(body));
        else xhr.send();
    })
}


code:
document.body.addEventListener('click', event => {
    if (event.target.closest('[data-favorites]')) {
        addRemoveFavorites(event);
    }
})

if (document.querySelector('.book-big')) {

    const url = document.location.href + '?information=true/'
    base.sendAjaxRequest(url, 'GET')
        .then(result => {
            showAmountOfReading(result);
            displayFavorites(result);

        })

}

function addRemoveFavorites(event) {
    event.preventDefault();

    const token = document.querySelector('[data-favorites]>form>input[name="csrfmiddlewaretoken"]').value
    const isFavorites = event.target.closest('[data-favorites]').dataset.favorites.toLowerCase();
    const initial_url = document.location.href;

    if (isFavorites == 'true') {
        console.log(1)
        removeFavorites(event, initial_url, token);
    } else if (isFavorites == 'false') {
        console.log(0)
        addFavorites(event, initial_url, token);
    }

    // НИЖЕ ОПРЕДЕЛЕНИЕ ФУНКЦИЙ

    function removeFavorites(event, initial_url, token) {
        const url = initial_url + 'remove_favorites/';
        base.sendAjaxRequest(url, 'POST', {'token': token})
            .then(() => {
                document.querySelector('[data-favorites] i').style.cssText = `
                    color: #4c4c4c;
                    transition: color .3s ease-in;
                `
                event.target.closest('[data-favorites]').dataset.favorites = 'false'
            })
    }

    function addFavorites(event, initial_url, token) {
        const url = initial_url + 'add_favorites/';
        base.sendAjaxRequest(url, 'POST', {'token': token})
            .then(() => {
                document.querySelector('[data-favorites] i').style.cssText = `
                    color: #f5dc65;
                    transition: color .3s ease-in;
                `
                event.target.closest('[data-favorites]').dataset.favorites = 'true'
            })
    }
}

function displayFavorites(result) {
    if (result.favorites) {
        document.querySelector('.fa-star').style.fontWeight = 'bold';
        document.querySelector('.fa-star').style.color = '#f5dc65';
    }
}

function showAmountOfReading(result) {
    // Показываем количество прочитанного

    const readPercentages = Math.trunc(result.last_page / result.volume * 100);
    const value = readPercentages + '%';
    document.querySelector('.number-of-pages>.percentages').innerHTML = value;
    document.querySelector('.now').style.width = value;
}


code:
document.querySelector('[data-book-search]').addEventListener('input', (event) => {
    const initialListOfBooks = getInitialListOfBooks();
    const enteredValue = event.target.value;
    let sortedListOfBooks = initialListOfBooks.sort(book => {
        if (isEnteredValueInName(enteredValue, book)) return -1
    }).slice(0, 2);

    changeContent(initialListOfBooks, sortedListOfBooks)

    for (let book of Array.from(document.querySelector('.books').children)) {
        if (enteredValue && isEnteredValueInName(enteredValue, book)) {
            book.style.cssText = `
                display: flex;
            `
        } else {
            book.style.cssText = `
                display: none;
            `
        }
    }

    setEmptyValue(enteredValue)

    // НИЖЕ ОПРЕДЕЛЕНИЕ ФУНКЦИЙ

    function getInitialListOfBooks() {
        // Получаем начальный список книг

        const initialListOfBooks = Array.from(document.querySelector('.books').children)
            .filter(book => book.classList.contains('book'))
        console.log(initialListOfBooks)
        return initialListOfBooks
    }

    function setEmptyValue(enteredValue) {
        // Устанавливаем пустое значение

        const numberOfBooksFound = Array.from(document.querySelector('.books').children)
            .filter(book => getComputedStyle(book).display == 'flex');

        const text = document.createElement('div');
        text.style.color = '#898989';
        text.style.textAlign = 'center';

        if (numberOfBooksFound.length == 0 && enteredValue) {
            text.textContent = 'Книга не найдена';
            document.querySelector('.books').insertAdjacentElement('afterbegin', text)
        }
    }

    function changeContent(initialListOfBooks, sortedListOfBooks) {
        // Меняям содержимое местами в списке книг

        const finalListOfBooks = [...new Set(initialListOfBooks.concat(sortedListOfBooks))]
        const result = finalListOfBooks.map(book => book.outerHTML)
        document.querySelector('.books').innerHTML = result.join('')

    }

    function isEnteredValueInName(enteredValue, book) {
        // Проверяем соответствует ли введенное значение
        // названию какой-то книги

        const regex = new RegExp(enteredValue, 'gi')
        if (regex.test(book.querySelector('.book-title').textContent)) {
            return true
        } else return false
    }
})


code:
document.addEventListener('click', event => {

    if (event.target.classList.contains('hide-sidebar') ||
        event.target.closest('.hide-sidebar')) {
        hideSidebar(event);
    } else if (event.target.closest('.font-size')) {
        changeFontSize(event);
    } else if (event.target.closest('[data-open-page]') ||
        event.target.closest('[data-next-page]') ||
        event.target.closest('[data-previous-page]')) {
        openPage(event);
    };
})

document.body.addEventListener('input', (event) => {
    if (event.target.querySelector('[data-page-number]')) {
        const totalPageLength = document.querySelector('[data-total-page]').textContent.length;

        if (event.target.value.length > totalPageLength) {
            event.target.value = event.target.value.substring(0, event.target.value.length - 1);
        }

        event.target.value = event.target.value.replace(/\D/gi, '');
    }
})

if (document.forms.bookmark) {
    document.forms.bookmark.onsubmit = (event) => {

        event.preventDefault();

        if (event.target.closest('[name="bookmark"]')){
            addRemoveBookmark(event)
        }
    }
}

function getCurrentPageNumber() {
    // Получаем номер текущей страницы

    return document.querySelector('.current-page').textContent;
}

function addRemoveBookmark(event) {
    // Добавляем/удаляем закладку

    const token = event.target.querySelector('input[name="csrfmiddlewaretoken"]').value;
    const initial_url = document.location.href.replace(/\?.*/ig, '');

    if (event.target.querySelector('button[data-bookmark="false"]')) {
        addBookmark(token, initial_url);
    } else if (event.target.querySelector('button[data-bookmark="true"]')) {
        removeBookmark(token, initial_url)
    }

    // НИЖЕ ОПРЕДЕЛЕНИЕ ФУНКЦИЙ

    function removeBookmark(token, initial_url) {
        const url = initial_url + 'remove/' + '0' + '/';
        base.sendAjaxRequest(url, 'POST', {'token': token})
            .then(() => {
                document.querySelector('.bookmark>i').classList.remove('color-active-bookmark')
                document.querySelector('.bookmark>i').classList.add('color-inactive-bookmark')
                document.querySelector('button[data-bookmark="true"]').dataset.bookmark = 'false'
            })
    }

    function addBookmark(token, initial_url) {
        const currentPageNumber = getCurrentPageNumber();
        const url = initial_url + 'add/' + currentPageNumber + '/';
        base.sendAjaxRequest(url, 'POST', {'token': token})
            .then(() => {
                document.querySelector('.bookmark>i').classList.remove('color-inactive-bookmark')
                document.querySelector('.bookmark>i').classList.add('color-active-bookmark')
                document.querySelector('button[data-bookmark="false"]').dataset.bookmark = 'true'
            })

    }
}

function openPage(event) {
    // Открываем страницу

    function scrollPage() {
        // Прокручиваем страницу вверх

        window.scrollTo(0, 77)
    }

    function displayBookmark(context) {
        // Отображаем закладку

        const currentPageNumber = getCurrentPageNumber();
        if (context.bookmark == +currentPageNumber) {
            document.querySelector('[data-bookmark]>i').classList.add('color-active-bookmark');
        } else {
            document.querySelector('[data-bookmark]>i').classList.remove('color-active-bookmark');
        }

    }

    function changePageContent(context) {
        // Меняем содержимое страницы

        document.querySelector('.text').innerHTML = context.text;
        document.querySelector('.current-page').innerHTML = context.page_number;
        document.querySelector('.previous-page').innerHTML = context.previous_page;
        document.querySelector('.next-page').innerHTML = context.next_page;
        scrollPage();
        displayBookmark(context);
    }

    function openPageByTyping(event) {
        // Открываем страницу по указанному номеру

        const url = document.location.href.replace(/\?.*/ig, '') + `?update=true&page=${pageNumber.value}`
        base.sendAjaxRequest(url, 'GET')
            .then(result => {
                changePageContent(result);
                pageNumber.value = '';
            })
    }

    function openPagePreviousNext(event) {
        // Открываем предыдущую/следующую страницу

        const currentPage = +document.querySelector('.current-page').textContent;

        if (event.target.closest('[data-previous-page]')) {
            const previousPage = currentPage - 1;
            const url = document.location.href.replace(/\?.*/ig, '') + `?update=true&page=${previousPage}`;
            console.log(url)
            base.sendAjaxRequest(url, 'GET')
                .then(result => {
                    changePageContent(result)
                })
        } else if (event.target.closest('[data-next-page')) {
            const nextPage = currentPage + 1;
            const url = document.location.href.replace(/\?.*/ig, '') + `?update=true&page=${nextPage}`;
            console.log(url)
            base.sendAjaxRequest(url, 'GET')
                .then(result => {
                    changePageContent(result)
                })
        }
    }

    function updateLastPage() {
        const currentPage = +getCurrentPageNumber() + 1;
        const url = document.location.href.replace(/\?.*/ig, '') + currentPage + '/';
        base.sendAjaxRequest(url, 'GET')
    }

    // ВЫШЕ ОПРЕДЕЛЕНИЕ ФУНКЦИЙ

    event.preventDefault()
    const pageNumber = document.querySelector('[data-page-number]');

    if (pageNumber.value) {
        openPageByTyping(event)
    } else {
        openPagePreviousNext(event)
    }
    updateLastPage()

}

function changeFontSize(event) {
    // Меняем размер шрифта

    const text = document.querySelector('.text');
    const currentFontSizePx = parseInt(getComputedStyle(text).fontSize);

    if (event.target.closest('.fa-plus') && currentFontSizePx < 22){
        console.log(3)
        text.style.fontSize = currentFontSizePx + 1 + 'px';
    }
    else if (event.target.closest('.fa-minus') && currentFontSizePx > 16) {
        console.log(2)
        text.style.fontSize = currentFontSizePx - .5 + 'px';
    }
    else if (event.target.closest('.fa-arrow-rotate-left')) {
        text.style.fontSize = '19.2px';
    }
}

function hideSidebar(event) {
    // Скрываем боковое меню

    const status = document.querySelector('.hide-sidebar')

    if (status.dataset.statusSidebar == 'false') {
        console.log(1)
        document.querySelector('.fa-xmark').style.display = 'none';
        document.querySelector('.fa-check').style.margin = '0';
        document.querySelector('.fa-check').style.display = 'block';
        document.querySelector('.content').style.animation = 'none';
        document.querySelector('.navigation-menu').style.animation = 'hide-menu-to-left 200ms ease-out 1 forwards';
        setTimeout(() => {
            document.querySelector('.navigation-menu').style.display = 'none';
        }, 200)

        status.dataset.statusSidebar = 'true';
        return
    }

    if (status.dataset.statusSidebar == 'true') {
        console.log(0)
        document.querySelector('.fa-xmark').style.display = 'block';
        document.querySelector('.fa-check').style.display = 'none';
        document.querySelector('.content').style.animation = 'move-content-to-right 200ms ease-out 1';
        document.querySelector('.navigation-menu').style.animation = 'none'
        setTimeout(() => {
            document.querySelector('.navigation-menu').style.display = 'block'
        }, 200)
        status.dataset.statusSidebar = 'false';
        return
    }

}


code:
document.addEventListener('click', event => {

    closeBookSearchWindow(event);
    showBookSearchWindow(event);
    // openBook(event);
})

document.addEventListener('keydown', event => {
    closeBookSearchWindow(event);
})

function openBook(event) {
    // Открываем книгу

    if (event.target.dataset.book ||
        event.target.closest('[data-book]')) {
        const slug = event.target.closest('[data-book]').dataset.book;
        const url = document.location.host + '/book/' + slug + '/';
        base.sendAjaxRequest(url, 'GET')
            .then(result => {
                console.log(result)
            })
        console.log(url)
    }
}

function closeBookSearchWindow(event) {
    // Закрываем окно для поиска книг

    const $bookSearchWindow = document.querySelector('.book-search-window');
    if (!(event.target.dataset.openBook == 'true') &&
        $bookSearchWindow.classList.contains('a-flip-left') &&
        !event.target.closest('.book-search-window') ||
        event.code == 'Escape') {
        $bookSearchWindow.classList.remove('a-flip-left');
        $bookSearchWindow.classList.add('transparent');
        document.querySelector('.books').innerHTML = '';
        setTimeout(() => {
            document.querySelector('[data-book-search]').value = ''
        }, 500)
    }
}

function showBookSearchWindow(event) {
    // Показываем окно для поиска книг

    if (event.target.dataset.openBook == 'true') {
        event.preventDefault();
        console.log('blablblalbalab')
        showWindow();
        addContent(event);
        document.querySelector('[data-book-search]').focus();
    }

    // НИЖЕ ОПРЕДЕЛЕНИ ФУКНЦИЙ

    function addContent(event) {
        // Добавляем содержимое

        base.sendAjaxRequest(url=event.target.href, mehtod='GET')
            .then(result => {
                render(result);
            })

        // НИЖЕ ОПРЕДЕЛЕНИЕ ФУНКЦИЙ

        function render(data) {
            // Объединяем html с данными

            const template = Hogan.compile(getListOfBooks());
            const output = template.render(data);
            document.querySelector('.books').insertAdjacentHTML('beforeend', output);
        }

        function getListOfBooks() {
            // Получаем список книг в виде html

            return `
            {{#books}}

                                {{ title }}

                                {{ description }}

            {{/books}}
            `
        }
    }

    function getCoordinates() {
        // Получаем координаты

        const left = document.querySelector('.open-book').clientWidth + 10 + 'px';
        return [left, 0]
    }

    function showWindow() {
        // Создаем окно

        const $bookSearchWindow = document.querySelector('.book-search-window');
        const [left, top] = getCoordinates();
        $bookSearchWindow.style.cssText = `
            left: ${left};
            top: ${top};
            display: flex;
        `;
        $bookSearchWindow.classList.add('a-flip-left');
        $bookSearchWindow.classList.add('show-hide-window')
    }
}


code:
# abbrev-js

Just like [ruby's Abbrev](http://apidock.com/ruby/Abbrev).

Usage:

    var abbrev = require("abbrev");
    abbrev("foo", "fool", "folding", "flop");

    // returns:
    { fl: 'flop'
    , flo: 'flop'
    , flop: 'flop'
    , fol: 'folding'
    , fold: 'folding'
    , foldi: 'folding'
    , foldin: 'folding'
    , folding: 'folding'
    , foo: 'foo'
    , fool: 'fool'
    }

This is handy for command-line scripts, or other cases where you want to be able to accept shorthands.


code:
module.exports = exports = abbrev.abbrev = abbrev

abbrev.monkeyPatch = monkeyPatch

function monkeyPatch () {
  Object.defineProperty(Array.prototype, 'abbrev', {
    value: function () { return abbrev(this) },
    enumerable: false, configurable: true, writable: true
  })

  Object.defineProperty(Object.prototype, 'abbrev', {
    value: function () { return abbrev(Object.keys(this)) },
    enumerable: false, configurable: true, writable: true
  })
}

function abbrev (list) {
  if (arguments.length !== 1 || !Array.isArray(list)) {
    list = Array.prototype.slice.call(arguments, 0)
  }
  for (var i = 0, l = list.length, args = [] ; i < l ; i ++) {
    args[i] = typeof list[i] === "string" ? list[i] : String(list[i])
  }

  // sort them lexicographically, so that they're next to their nearest kin
  args = args.sort(lexSort)

  // walk through each, seeing how much it has in common with the next and previous
  var abbrevs = {}
    , prev = ""
  for (var i = 0, l = args.length ; i < l ; i ++) {
    var current = args[i]
      , next = args[i + 1] || ""
      , nextMatches = true
      , prevMatches = true
    if (current === next) continue
    for (var j = 0, cl = current.length ; j < cl ; j ++) {
      var curChar = current.charAt(j)
      nextMatches = nextMatches && curChar === next.charAt(j)
      prevMatches = prevMatches && curChar === prev.charAt(j)
      if (!nextMatches && !prevMatches) {
        j ++
        break
      }
    }
    prev = current
    if (j === cl) {
      abbrevs[current] = current
      continue
    }
    for (var a = current.substr(0, j) ; j <= cl ; j ++) {
      abbrevs[a] = current
      a += current.charAt(j)
    }
  }
  return abbrevs
}

function lexSort (a, b) {
  return a === b ? 0 : a > b ? 1 : -1
}


abbrev

description: Like ruby's abbrev module, but in js

author: Isaac Z. Schlueter <i@izs.me>

test: tap test.js --100

preversion: npm test

postversion: npm publish

postpublish: git push origin --all; git push origin --tags

license: ISC


code:
All Animation
=============

All Animation.css is a set of nice and crazy css animations made with the purpose to bring life and interactions to your project. They are cross-browser animations which will give more emphases on your pages likes sliding controls and 3D efects...

[You can view the website here -- We are going to change our layout soon](http://clovisdasilvaneto.github.io/all-animation/)

## V3 is now ready to be used!
It's already on npm, but we're still improving our documentation and api.

New modules will also be released on the future, the first one will be:

- [ ] all-animation/react 🥰🥰

### How to use:

It's easy to use, let's see step by step:

### Step 1, install the library as dependency

It's pretty straight forward:

```sh
npm install all-animation
```
or
```sh
yarn add all-animation
```

### Step 2, link the library into your project:

Currently there's thwo ways of linking all animation into your project:

**Via SCSS imports:**

In your main `scss` file of your project, just include the all-animation module, like:

```scss
// main.scss
@import "../node_modules/all-animation/scss/main"
```
After that you should be ready to use our library.

**Loading only specific animations**

In case you don't want to have the whole all-animations on your final bundle, you can import the specific animation which you want together with core all-animation dependencies:

```scss
// Core all-animation dependencies, like variables and mixins
@import "../node_modules/all-animation/scss/config/_config";

// The specific animation which you want to use
// in this case "a-bomb"
@import "../node_modules/all-animation/scss/modules/bomb/bomb"
```

**Link directly via CSS file**
you can also download the css file and put it before closing the `</head>` tag:

```html
<link rel="stylesheet" type="text/css" href="node_modules/all-animaton/dist/all-animation.css" />
```
> _We also include the source maps of it, so you can have a better debugging visibility during development_

### Step 2, HTML:

```
<div id="animation"></div>

<button class="anny-class">Trigger class, go!</button>

```

### Step 3, jQuery (You can also use plain javascript):

```js
$(".any-class").click(() =>{
 $("#animation").addClass("a-journal");
});
```

### Optional

If you want to add the effect at some specified time, just place a timer:

Example, triggering an animation in a particular element after 2 seconds:

```js
setTimeout(() =>{
 $("#animation").addClass("a-journal");
}, 2000);
```

### Caution:

If you want to add some animation on an element that had another animation, or you want to restart the animation previously triggered, you have to remove the last animation and trigger the new one. Example:

```js
 $("#animation").removeClass("a-journal").addClass("a-four-rock");
```

We have several classes for animations:

### Specials:

a-dance

a-journal

a-pulse

a-pulse-slow

a-jamp

a-four-rock

### Bounce:

a-enter-up-bounce

a-enter-down-bounce

a-enter-right-bounce

a-enter-left-bounce

a-scale-bounce

a-jump-bounce

### Perspective:
For using the perspective animations, please add a parent container with a class `a-perspective`, like:

```html
<div class="a-perspective">
    <!-- Your animated code here, like: -->
    <div class="a-three-flip-up">...</div>
</div>
```

Some classes for

a-three-flip-right

a-three-flip-up

a-three-flip-down

a-flip-left-bounce

a-rotate-flip

a-flip-right-bounce

### Fading Entrances:

a-flip-top

a-flip-left

a-flip-right

a-flip-bottom

### Rotate:

a-rotate-flip-down

a-rotate-down-bounce

a-rotate-out

### Agrecives:

a-flash-bang

a-bomb

### Contributors:
If you want to contribute to our project, read the file: contributing.md 😊

#### Credits:

Clóvis Neto


all-animation

author: Clóvis Neto

type: git

description: All Animation.css is a set of nice and crazy css animations made with the purpose to bring life and interactions to your project. They are cross-browser animations which will give more emphases on your pages likes sliding controls and 3D efects...

build: rimraf css && yarn build-sass && yarn build-autoprefix && yarn build-cleancss

build-sass: node-sass --output-style expanded scss/main.scss ./dist/all-animation.css

build-autoprefix: postcss --use autoprefixer --map --output ./dist/all-animation.css ./dist/all-animation.css

build-cleancss: cleancss -o ./dist/all-animation.min.css ./dist/all-animation.css

lint: stylelint '**/*.scss'

lint:fix: yarn lint --fix

files: scss dist

license: MIT


code:
@import './mixins/mixins';
@import './_variables';


code:
// -------------------
// Variables
// -------------------

// animations timing
// -------------------
$dance-timing: 2s;
$bounce-timing: 0.8s;
$four-rock-timing: 2s;
$scale-bounce-timing: 0.6s;
$jamp-timing: 1s;
$journal-timing: 2s;
$pulse-timing: 0.5s;
$pulse-slow-timing: 1s;
$perspective-bounce-timing: 1s;
$flip-timing: 1s;
$jump-bounce-timing: 1.5s;

// Rotate timing
$rotate-timing: 1s;
$rotate-down-bounce-timing: 2s;
$rotate-out-timing: 1.5s;

// Agreccives timing
$flash-bang-timing: 2s;
$bomb-timing: 1s;

// Agreccives timing
$flash-bang-timing: 2s;
$legend-timing: 0.5s;

// 3D perspective
// --------------------
$perspective: 600;


code:
@mixin animate($content) {
  animation: $content;
}


code:
@mixin hide() {
  opacity: 0;
  visibility: hidden;
}


code:
@mixin keyframes($name) {
  @keyframes #{$name} {
    @content;
  }
}


code:
@import './hide.scss';
@import './show.scss';
@import './animate.scss';
@import './keyframes.scss';


code:
@mixin show() {
  opacity: 1 !important;
  visibility: visible !important;
}


code:
@import './config/_config.scss';
@import './modules/modules.scss';


code:
.a-bomb {
  @include keyframes(bomb) {
    0% {
      transform-origin: -50% 0%;
      transform: rotateZ(0);
      opacity: 1;
      visibility: visible;
    }

    50%,
    100% {
      transform: rotateZ(-210deg);
      transform-origin: -20% 0%;
    }

    47%,
    100% {
      opacity: 0;
      visibility: hidden;
    }
  }

  @include hide();
  @include animate(bomb $bomb-timing ease-out 1);
}


code:
.a-dance {
  @include keyframes(dance) {
    0%,
    10%,
    20% {
      transform: rotateZ(10deg);
    }

    5%,
    15%,
    25% {
      transform: rotateZ(-10deg);
    }

    35%,
    60%,
    100% {
      transform: scale(1) rotateZ(0deg);
      box-shadow: inset 0 0 0 0 transparent;
    }

    40% {
      transform: scale(1.3);
      box-shadow: inset 0 0 0 0;
      box-shadow: inset 0 0 0 2px rgba(0, 0, 0, 0.1);
    }
  }

  @include show();
  @include animate(dance $dance-timing linear 1);
}


code:
.a-enter-down-bounce {
  @include keyframes(enterDownBounce) {
    0% {
      transform: translateY(-120px);
      opacity: 0;
    }

    50% {
      transform: translateY(0);
    }

    70% {
      transform: translateY(-20px);
    }

    100% {
      transform: translateY(0);
      opacity: 1;
    }
  }

  @include show();
  @include animate(enterDownBounce 1 linear $bounce-timing);
}


code:
.a-enter-left-bounce {
  @include keyframes(enterLeftBounce) {
    0% {
      transform: translateX(-120px);
      opacity: 0;
    }

    50% {
      transform: translateX(0);
    }

    70% {
      transform: translateX(-8px);
    }

    100% {
      transform: translateX(0);
      opacity: 1;
    }
  }

  @include show();
  @include animate(enterLeftBounce 1 linear $bounce-timing);
}


code:
.a-enter-right-bounce {
  @include keyframes(enterRightBounce) {
    0% {
      transform: translateX(120px);
      opacity: 0;
    }

    50% {
      transform: translateX(0);
    }

    70% {
      transform: translateX(8px);
    }

    100% {
      transform: translateX(0);
      opacity: 1;
    }
  }

  @include show();
  @include animate(enterRightBounce 1 linear $bounce-timing);
}


code:
.a-enter-up-bounce {
  @include keyframes(enterUpBounce) {
    0% {
      transform: translateY(120px);
      opacity: 0;
    }

    50% {
      transform: translateY(0);
    }

    70% {
      transform: translateY(20px);
    }

    100% {
      transform: translateY(0);
      opacity: 1;
    }
  }

  @include show();
  @include animate(enterUpBounce 1 linear $bounce-timing);
}


code:
.a-flash-bang {
  @include keyframes(flashBang) {
    0%,
    20% {
      opacity: 0;
    }

    10%,
    30%,
    100% {
      opacity: 1;
    }
  }

  @include show();
  @include animate(flashBang $flash-bang-timing linear 1);
}


code:
.a-flip-bottom {
  @include keyframes(flipBottom) {
    0% {
      transform: translateY(100px);
      opacity: 0;
    }

    70% {
      transform: translateY(30px);
      opacity: 1;
    }

    100% {
      transform: translateY(0);
    }
  }

  @include show();
  @include animate(flipBottom $flip-timing ease-out 1);
}


code:
.a-flip-left-bounce {
  @include keyframes(flipLeftBounce) {
    0% {
      opacity: 0;
      transform: rotateY(70deg);
    }

    50% {
      opacity: 1;
      transform: rotateY(-5deg);
    }

    80% {
      transform: rotateY(20deg);
    }

    100% {
      transform: rotateY(0deg);
    }
  }

  @include show();
  @include animate(flipLeftBounce $perspective-bounce-timing ease-in-out 1);
}


code:
.a-flip-left {
  @include keyframes(flipLeft) {
    0% {
      transform: translateX(-100px);
      opacity: 0;
    }

    70% {
      transform: translateX(0);
      opacity: 1;
    }
  }

  @include show();
  @include animate(flipLeft $flip-timing ease-out 1);
}


code:
.a-flip-right-bounce {
  @include keyframes(flipRightBounce) {
    0% {
      opacity: 0;
      transform: rotateY(-70deg);
    }

    50% {
      opacity: 1;
      transform: rotateY(5deg);
    }

    80% {
      transform: rotateY(-20deg);
    }

    100% {
      transform: rotateY(0deg);
    }
  }

  @include show();
  @include animate(flipRightBounce $perspective-bounce-timing ease-in-out 1);
}


code:
.a-flip-right {
  @include keyframes(flipRight) {
    0% {
      transform: translateX(100px);
      opacity: 0;
    }

    70% {
      transform: translateX(0);
      opacity: 1;
    }
  }

  @include show();
  @include animate(flipRight $flip-timing ease-out 1);
}


code:
.a-flip-top {
  @include keyframes(flipTop) {
    0% {
      transform: translateY(-100px);
      opacity: 0;
    }

    70% {
      transform: translateY(0);
      opacity: 1;
    }
  }

  @include show();
  @include animate(flipTop $flip-timing ease-out 1);
}


code:
@include keyframes(fourRock) {
  0% {
    transform-origin: 0 0;
  }

  5% {
    transform: scale(0.5);
  }

  10% {
    transform: scale(0.5);
  }

  15% {
    transform: scale(0.5) translateX(130px);
  }

  20% {
    transform: scale(0.5) translateX(130px);
  }

  25% {
    transform: scale(0.5) translate(130px, 130px);
  }

  30% {
    transform: scale(0.5) translate(0, 130px);
  }

  35% {
    transform: scale(0.5) translate(0, 0);
  }

  40% {
    transform: scale(1);
  }

  100% {
    transform: scale(1);
    transform-origin: 0 0;
  }
}


code:
@import './animation.scss';

.a-four-rock {
  @include show();
  @include animate(fourRock 1 ease-out $four-rock-timing);
}


code:
@include keyframes(jamp) {
  0% {
    transform: translateY(0);
  }

  10% {
    transform: translateY(-50px);
  }

  25% {
    transform: translateY(-50px) scale(1.1);
  }

  35% {
    transform: translateY(-50px) scale(1);
  }

  45% {
    transform: translateY(-50px) scale(1.2);
  }

  55% {
    transform: translateY(-50px) scale(1);
  }

  65% {
    transform: translate(150px, -50px) scale(0.8);
  }

  85% {
    transform: translate(150px, -50px) scale(1.2);
  }

  95% {
    transform: translate(0, 0) scale(1);
  }

  100% {
    transform: translate(0, 0);
  }
}


code:
@import './animation.scss';

.a-jamp {
  @include show();
  @include animate(jamp 1 linear $jamp-timing);
}


code:
.a-journal {
  @include keyframes(journal) {
    0% {
      transform: scale(0) rotate(-360deg);
    }

    80% {
      transform: scale(0.5) rotate(-180deg);
    }

    100% {
      transform: scale(1) rotate(0);
    }
  }

  @include show();
  @include animate(journal $journal-timing ease-in 1);
}


code:
.a-jump-bounce {
  @include keyframes(jumpBounce) {
    10% {
      transform: translateY(-100px);
    }

    20%,
    35%,
    50% {
      transform: translateY(0);
    }

    25% {
      transform: translateY(-40px);
    }

    40% {
      transform: translateY(-10px);
    }
  }

  @include show();
  @include animate(jumpBounce 1 $jump-bounce-timing ease-out);
}


code:
.a-legend-down {
  &::before {
    @include keyframes(legendDown) {
      0% {
        transform: translateY(0) translateX(9px);
        height: 0;
      }

      100% {
        height: 64%;
        transform: translateY(9px) translateX(9px);
      }
    }

    content: '';
    display: block;
    position: absolute;
    width: 90%;
    height: 64%;
    z-index: 1000;
    transform: translateY(9px) translateX(9px);
    background: rgba(0, 0, 0, 0.5);

    @include animate(legendDown $legend-timing ease-out 1);
  }

  @include show();
}


code:
.a-legend-left {
  &::before {
    @include keyframes(legendLeft) {
      0% {
        transform: translateY(9px) translateX(0);
        width: 0;
      }

      100% {
        width: 90%;
        transform: translateY(9px) translateX(9px);
      }
    }

    content: '';
    display: block;
    position: absolute;
    width: 90%;
    height: 64%;
    z-index: 1000;
    left: 0;
    top: 0;
    transform: translateY(9px) translateX(9px);
    background: rgba(0, 0, 0, 0.5);

    @include animate(legendLeft $legend-timing ease-out 1);
  }

  @include show();
}


code:
.a-legend-right {
  &::before {
    @include keyframes(legendRight) {
      0% {
        left: 100%;
        width: 0;
      }

      100% {
        width: 90%;
      }
    }

    content: '';
    display: block;
    position: absolute;
    width: 90%;
    height: 64%;
    z-index: 1000;
    left: 0;
    top: 0;
    transform: translateY(9px) translateX(9px);
    background: rgba(0, 0, 0, 0.5);

    @include animate(legendRight $legend-timing ease-out 1);
  }

  @include show();
}


code:
.a-legend-up {
  &::before {
    @include keyframes(legendUp) {
      0% {
        top: 70%;
        height: 0;
      }

      100% {
        height: 64%;
        top: 0;
      }
    }

    content: '';
    display: block;
    position: absolute;
    width: 90%;
    height: 64%;
    z-index: 1000;
    transform: translateY(9px) translateX(9px);
    background: rgba(0, 0, 0, 0.5);

    @include animate(legendUp $legend-timing ease-out 1);
  }

  @include show();
}


code:
//Specials
@import './dance/dance.scss';
@import './journal/journal.scss';
@import './pulse/pulse.scss';
@import './pulse-slow/pulse-slow.scss';
@import './jamp/jamp.scss';
@import './four-rock/four-rock.scss';

// Bounce
@import './enter-up-bounce/enter-up-bounce.scss';
@import './enter-down-bounce/enter-down-bounce.scss';
@import './enter-left-bounce/enter-left-bounce.scss';
@import './enter-right-bounce/enter-right-bounce.scss';
@import './jump-bounce/jump-bounce.scss';
@import './scale-bounce/scale-bounce.scss';

// Perspective
@import './perspective/perspective.scss';
@import './three-flip-down/three-flip-down.scss';
@import './three-flip-up/three-flip-up.scss';
@import './three-flip-right/three-flip-right.scss';
@import './three-flip-left/three-flip-left.scss';
@import './flip-left-bounce/flip-left-bounce.scss';
@import './flip-right-bounce/flip-right-bounce.scss';
@import './rotate-flip/rotate-flip.scss';

// Fade
@import './flip-top/flip-top.scss';
@import './flip-bottom/flip-bottom.scss';
@import './flip-left/flip-left.scss';
@import './flip-right/flip-right.scss';

// Rotate
@import './rotate-out/rotate-out.scss';
@import './rotate-flip-down/rotate-flip-down.scss';
@import './rotate-down-bounce/rotate-down-bounce.scss';

// Agreccives
@import './flash-bang/flash-bang.scss';
@import './bomb/bomb.scss';

// Legend
@import './legend-left/legend-left.scss';
@import './legend-right/legend-right.scss';
@import './legend-up/legend-up.scss';
@import './legend-down/legend-down.scss';

// Portrait
@import './portrait-before/portrait-before.scss';
@import './portrait-blur/portrait-blur.scss';
@import './scale-before/scale-before.scss';


code:
.a-perspective {
  /* autoprefixer: off */
  -webkit-perspective: $perspective;
  -o-perspective: $perspective;
  -moz-perspective: $perspective;
  perspective: $perspective;
}


code:
.a-portrait-before {
  &::before {
    @include keyframes(portraitBefore) {
      0% {
        box-shadow: inset 0 0 0 0 rgba(0, 0, 0, 0.5);
        width: 100%;
        height: 74%;
        transform: translateY(0) translateX(0);
      }

      100% {
        box-shadow: inset 0 0 0 100px rgba(0, 0, 0, 0.5);
        width: 90%;
        height: 64%;
        transform: translateY(9px) translateX(9px);
      }
    }

    content: '';
    display: block;
    position: absolute;
    width: 90%;
    height: 64%;
    z-index: 1000;
    transform: translateY(9px) translateX(9px);
    box-shadow: inset 0 0 0 100px rgba(0, 0, 0, 0.5);

    @include animate(portraitBefore $legend-timing ease-out 1);
  }

  @include show();
}


code:
.a-portrait-blur {
  &::before {
    @include keyframes(portraitBlur) {
      0% {
        box-shadow: inset 0 0 0 0 rgba(0, 0, 0, 0.5);
        width: 100%;
        height: 74%;
        transform: translateY(0) translateX(0);
      }

      100% {
        box-shadow: inset 0 0 100px 100px rgba(0, 0, 0, 0.5);
        width: 90%;
        height: 64%;
        transform: translateY(9px) translateX(9px);
      }
    }

    content: '';
    display: block;
    position: absolute;
    width: 90%;
    height: 64%;
    z-index: 1000;
    transform: translateY(9px) translateX(9px);
    box-shadow: inset 0 0 100px 100px rgba(0, 0, 0, 0.5);

    @include animate(portraitBlur $legend-timing ease-out 1);
  }

  @include show();
}


code:
.a-pulse-slow {
  @include keyframes(pulseSlow) {
    0% {
      transform: scale(1);
      opacity: 1;
    }

    100% {
      transform: scale(0.85);
      opacity: 0.5;
    }
  }

  @include show();
  @include animate(pulseSlow alternate infinite ease-in-out $pulse-slow-timing);
}


code:
.a-pulse {
  @include keyframes(pulse) {
    0% {
      transform: scale(1);
    }

    100% {
      transform: scale(1.05);
    }
  }

  @include show();
  @include animate(pulse infinite linear $pulse-timing alternate);
}


code:
.a-rotate-down-bounce {
  @include keyframes(rotateDowBounce) {
    0% {
      transform: rotate(0deg);
      opacity: 1;
      visibility: visible;
    }

    20% {
      transform: rotate(25deg);
    }

    40% {
      transform: rotate(30deg);
    }

    10%,
    30%,
    50% {
      transform: rotate(40deg);
      opacity: 1;
      visibility: visible;
    }

    80%,
    100% {
      transform: rotate(30deg) translateY(310px) translateX(230px);
      opacity: 0;
      visibility: hidden;
    }
  }

  @include hide();
  @include animate(rotateDowBounce $rotate-down-bounce-timing ease-in-out 1);

  transform-origin: 0 0;
}


code:
.a-rotate-flip-down {
  @include keyframes(rotateFlipDown) {
    0% {
      transform-origin: 50%;
    }

    40% {
      transform: rotateX(70deg);
      opacity: 0;
    }

    60% {
      transform: rotateX(70deg);
      opacity: 0;
    }

    100% {
      transform: rotateX(0deg);
      opacity: 1;
    }
  }

  @include show();
  @include animate(rotateFlipDown $rotate-timing linear 1);
}


code:
.a-rotate-flip {
  @include keyframes(rotateFlip) {
    0% {
      transform-origin: 50%;
    }

    40% {
      transform: rotateY(70deg);
      opacity: 0;
    }

    60% {
      transform: rotateY(70deg);
      opacity: 0;
    }

    100% {
      transform: rotateY(0deg);
      opacity: 1;
    }
  }

  @include show();
  @include animate(rotateFlip $perspective-bounce-timing linear 1);
}


code:
.a-rotate-out {
  @include keyframes(rotateOut) {
    0% {
      opacity: 1;
      visibility: visible;
    }

    50%,
    100% {
      transform: scale(0) rotateY(189deg);
    }
  }

  @include hide();
  @include animate(rotateOut $rotate-out-timing ease-in 1);
}


code:
.a-scale-before {
  &::before {
    @include keyframes(scaleBefore) {
      0% {
        transform: translateY(9px) translateX(9px) scale(1.5);
      }

      100% {
        transform: translateY(9px) translateX(9px) scale(1);
      }
    }

    content: '';
    display: block;
    position: absolute;
    width: 90%;
    height: 64%;
    z-index: 1000;
    transform: translateY(9px) translateX(9px);
    background: rgba(0, 0, 0, 0.5);

    @include animate(scaleBefore $legend-timing ease-out 1);
  }

  @include show();
}


code:
.a-scale-bounce {
  @include keyframes(scaleBounce) {
    0% {
      transform: scale(1.5);
      opacity: 0;
    }

    50% {
      transform: scale(1);
    }

    70% {
      transform: scale(1.1);
    }

    100% {
      opacity: 1;
      transform: scale(1);
    }
  }

  @include show();
  @include animate(scaleBounce 0.6s ease-in-out);
}


code:
.a-three-flip-down {
  @include keyframes(threeFlipDown) {
    0% {
      transform: rotateX(0);
    }

    100% {
      transform: rotateX(-90deg);
    }
  }

  transform: rotateX(90deg);
  transform-origin: 0 100%;

  @include show();
  @include animate(threeFlipDown $perspective-bounce-timing ease-in-out 1);
}


code:
.a-three-flip-up {
  @include keyframes(threeFlipUp) {
    0% {
      transform: rotateX(0);
    }

    100% {
      transform: rotateX(90deg);
    }
  }

  transform: rotateX(90deg);
  transform-origin: 100% 0;

  @include show();
  @include animate(threeFlipUp $perspective-bounce-timing ease-in-out 1);
}


code:
.a-three-flip-right {
  @include keyframes(threeFlipRight) {
    0% {
      transform: rotateY(0);
    }

    100% {
      transform: rotateY(90deg);
    }
  }

  transform: rotateY(90deg);
  transform-origin: 100% 0;

  @include show();
  @include animate(threeFlipRight $perspective-bounce-timing ease-in-out 1);
}


code:
.a-three-flip-left {
  @include keyframes(threeFlipLeft) {
    0% {
      transform: rotateY(0);
    }

    100% {
      transform: rotateY(-90deg);
    }
  }

  transform: rotateY(-90deg);
  transform-origin: 0 0;

  @include show();
  @include animate(threeFlipLeft $perspective-bounce-timing ease-in-out 1);
}


code:
# Animate.css

[![GitHub Version](https://img.shields.io/github/release/daneden/animate.css.svg?style=for-the-badge)](https://github.com/daneden/animate.css) [![Github Star](https://img.shields.io/github/stars/daneden/animate.css.svg?style=for-the-badge)](https://github.com/daneden/animate.css) [![Github Fork](https://img.shields.io/github/forks/daneden/animate.css.svg?style=for-the-badge)](https://github.com/daneden/animate.css) [![License](https://img.shields.io/github/license/daneden/animate.css.svg?style=for-the-badge)](https://github.com/daneden/animate.css)

> If you need the old docs - v3.x.x and under - you can find it [here](https://github.com/animate-css/animate.css/tree/a8d92e585b1b302f7749809c3308d5e381f9cb17).

## _Just-add-water CSS animation_

## Installation

Install with npm:

```shell
npm install animate.css --save
```

Install with yarn:

```shell
yarn add animate.css
```

## Getting started

You can find the Animate.css documentation on the [website](https://animate.style/).

## Accessibility

Animate.css supports the [`prefers-reduced-motion` media query](https://webkit.org/blog/7551/responsive-design-for-motion/) so that users with motion sensitivity can opt out of animations. On supported platforms (currently all the majors browsers and OS), users can select "reduce motion" on their operating system preferences and it will turn off CSS transitions for them without any further work required.

## Core team

| ![](https://avatars2.githubusercontent.com/u/439365?s=460&u=512b4cc5324938ae40bbb8f3b7769d335953cd3a&v=4) | ![](https://avatars2.githubusercontent.com/u/5007208?s=460&u=418401ee605824272e5dcb955fd64ea24546a857&v=4) | ![](https://avatars1.githubusercontent.com/u/15052701?s=460&u=9e58364978379536d3f26c4ce5cae1a2a449a0e4&v=4) |
| --- | --- | --- |
| [Daniel Eden](https://github.com/daneden) | [Elton Mesquita](https://github.com/eltonmesquita) | [Waren Gonzaga](https://github.com/WarenGonzaga) |
| Animate.css creator | Maintainer | Core contributor |

## License

Animate.css is licensed under the MIT license. <https://opensource.org/licenses/MIT>

## Code of Conduct

This project and everyone participating in it is governed by the [Contributor Covenant Code of Conduct](CODE_OF_CONDUCT.md). By participating, you are expected to uphold this code. Please report unacceptable behavior to [callmeelton@gmail.com](mailto:callmeelton@gmail.com).

## Contributing

Pull requests are the way to go here. We only have two rules for submitting a pull request: match the naming convention (camelCase, categorised [fades, bounces, etc]) and let us see a demo of submitted animations in a [pen](https://codepen.io). That **last one is important**.