diff --git a/.gitignore b/.gitignore index c656b3b8f..e59540379 100644 --- a/.gitignore +++ b/.gitignore @@ -54,7 +54,7 @@ coverage.xml # Django stuff: *.log - +sapl.log.* *.swp # Sphinx documentation diff --git a/Dockerfile b/Dockerfile index 4a1f7159c..618264253 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,8 +4,8 @@ FROM python:3.7-slim ENV BUILD_PACKAGES apt-file libpq-dev graphviz-dev graphviz build-essential git pkg-config \ python3-dev libxml2-dev libjpeg-dev libssl-dev libffi-dev libxslt1-dev pgadmin3 \ python3-lxml python3-magic postgresql-contrib postgresql-client \ - python3-psycopg2 poppler-utils antiword curl jq vim openssh-client bash \ - software-properties-common python3-setuptools python3-venv nginx tzdata nodejs + python3-psycopg2 poppler-utils vim curl jq vim openssh-client bash \ + software-properties-common python3-setuptools python3-venv nginx tzdata nodejs \ ENV DEBIAN_FRONTEND noninteractive @@ -76,9 +76,11 @@ RUN rm -rf /var/interlegis/sapl/sapl/.env && \ rm -rf /var/interlegis/sapl/sapl.db RUN chmod +x /var/interlegis/sapl/start.sh && \ + chmod +x /var/interlegis/sapl/check_solr.sh && \ ln -sf /dev/stdout /var/log/nginx/access.log && \ ln -sf /dev/stderr /var/log/nginx/error.log && \ - mkdir /var/log/sapl/ + mkdir /var/log/sapl/ && touch /var/interlegis/sapl/sapl.log && \ + ln -s /var/interlegis/sapl/sapl.log /var/log/sapl/sapl.log # Debian não possui usuário 'nginx' necessário para o Debian RUN useradd --no-create-home nginx diff --git a/MANIFEST.in b/MANIFEST.in index 3fe372d5d..7c730520d 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,5 +1,6 @@ include README.rst LICENSE.txt -recursive-include sapl *.html *.yaml +include sapl/webpack-stats.json +recursive-include sapl *.html *.yaml recursive-include sapl/static * recursive-include sapl/relatorios/templates *.py recursive-include sapl/compilacao *.sql diff --git a/README.rst b/README.rst index dd21aecf0..c2d47d643 100644 --- a/README.rst +++ b/README.rst @@ -1,4 +1,4 @@ -.. image:: https://travis-ci.org/interlegis/sapl.svg?branch=master +.. image:: https://travis-ci.org/interlegis/sapl.svg?branch=3.1.x :target: https://travis-ci.org/interlegis/sapl @@ -17,17 +17,17 @@ atual do sistema (2.5), visite a página do `projeto na Interlegis wiki `_ + `Instalação do Ambiente de Desenvolvimento `_ Instalação do Solr ====================== - `Instalação e configuração do Solr `_ + `Instalação e configuração do Solr `_ Instruções para Deploy ====================== - `Deploy SAPL com Nginx + Gunicorn `_ + `Deploy SAPL com Nginx + Gunicorn `_ Instruções para Importação da base mysql 2.5 @@ -37,19 +37,19 @@ Instruções para Importação da base mysql 2.5 Instruções para Tradução ======================== - `Instruções para Tradução `_ + `Instruções para Tradução `_ Orientações gerais de implementação =================================== - `Instruções para Implementação `_ + `Instruções para Implementação `_ Orientações gerais sobre o GitHub =================================== - `Instruções para GitHub `_ + `Instruções para GitHub `_ diff --git a/check_solr.sh b/check_solr.sh index b3c4760c4..c600466bb 100644 --- a/check_solr.sh +++ b/check_solr.sh @@ -4,15 +4,22 @@ SOLR_URL=$1 +RETRY_COUNT=1 +RETRY_LIMIT=4 + echo "Waiting for solr connection at $SOLR_URL ..." -while true; do +while [[ $RETRY_COUNT < $RETRY_LIMIT ]]; do + echo "Attempt to connect to solr: $RETRY_COUNT of $RETRY_LIMIT" + let RETRY_COUNT=RETRY_COUNT+1; echo "$SOLR_URL/solr/admin/collections?action=LIST" RESULT=$(curl -s -o /dev/null -I "$SOLR_URL/solr/admin/collections?action=LIST" -w '%{http_code}') echo $RESULT - if [ "$RESULT" -eq '200' ]; then + if [ $RESULT == 200 ]; then echo "Solr server is up!" - break + exit 1 else sleep 3 fi done +echo "Solr connection failed." +exit 2 \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index bc03463fb..1aadcd3a7 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -11,8 +11,7 @@ sapldb: ports: - "5433:5432" sapl: - # image: interlegis/sapl:3.1.143 - build: . + image: interlegis/sapl:3.1.155 restart: always environment: ADMIN_PASSWORD: interlegis @@ -24,11 +23,27 @@ sapl: EMAIL_HOST_USER: usuariosmtp EMAIL_SEND_USER: usuariosmtp EMAIL_HOST_PASSWORD: senhasmtp +# USE_SOLR: 'True' +# SOLR_COLLECTION: sapl +# SOLR_URL: http://saplsolr:8983 TZ: America/Sao_Paulo volumes: - sapl_data:/var/interlegis/sapl/data - sapl_media:/var/interlegis/sapl/media links: - sapldb +# - saplsolr ports: - "80:80" + +#saplsolr: +# image: solr:7.4-alpine +# restart: always +# command: bin/solr start -c -f +# volumes: +# - solr_data:/opt/solr/server/solr +# - solr_configsets:/opt/solr/server/solr/configsets +# ports: +# - "8983:8983" + + diff --git a/docs/deploy.rst b/docs/deploy.rst index 7e5e1cc5e..1a0fcd23e 100644 --- a/docs/deploy.rst +++ b/docs/deploy.rst @@ -24,11 +24,8 @@ Entrar no ambiente virtual:: Arquivos Estáticos ------------------ -Com o ambiente em produção, os arquivos estáticos devem ser servidos pelo web service, em nosso caso o `NGINX`, logo para ter acesso aos arquivos primeiro devemos rodar o seguinte comando:: - - ./manage.py compilescss - -para que os arquivos SASS/SCSS sejam compilados em arquivos .css em ambiente de produção, e em seguida rode:: +Com o ambiente em produção, os arquivos estáticos devem ser servidos pelo web service, em nosso caso o `NGINX`, +em ambiente de produção, para tanto, rode:: ./manage.py collectstatic --no-input --clear diff --git a/docs/instalacao31.rst b/docs/instalacao31.rst index a9752990f..4a0e3fd68 100644 --- a/docs/instalacao31.rst +++ b/docs/instalacao31.rst @@ -39,14 +39,14 @@ Instalar o virtualenv usando python 3 para o projeto. sudo mkdir -p /var/interlegis/.virtualenvs -* Ajustar as permissões - onde ``sapl31`` trocar por usuario:: +* Ajustar as permissões:: - sudo chown -R sapl31:sapl31 /var/interlegis/ + sudo chown -R $USER:$USER /var/interlegis/ * Edite o arquivo ``.bashrc`` e adicione ao seu final as configurações abaixo para o virtualenvwrapper:: - nano /home/sapl31/.bashrc + nano /home/$USER/.bashrc export VIRTUALENVWRAPPER_PYTHON=/usr/bin/python3 export WORKON_HOME=/var/interlegis/.virtualenvs @@ -56,7 +56,7 @@ Instalar o virtualenv usando python 3 para o projeto. * Carregue as configurações do virtualenvwrapper:: - source /home/sapl31/.bashrc + source /home/$USER/.bashrc @@ -116,7 +116,7 @@ Instalação e configuração das dependências do projeto * (caso você já possua uma instalação do postrgresql anterior ao processo de instalação do ambiente de desenvolvimento do SAPL em sua máquina e sábia como fazer, esteja livre para proceder como desejar, porém, ao configurar o arquivo ``.env`` no próximo passo, as mesmas definições deverão ser usadas) -* **Ajustar as permissões - onde $USER trocar por usuario**:: +* **Ajustar as permissões - onde $USER trocar por usuário**:: eval $(echo "sudo chown -R $USER:$USER /var/interlegis/") @@ -225,6 +225,7 @@ Preparação do ambiente:: * **Instalação do NodeJs LTS 10.15.x**:: + sudo apt-get install curl curl -sL https://deb.nodesource.com/setup_10.x | sudo -E bash - sudo apt-get install -y nodejs @@ -281,4 +282,15 @@ Feito isso, e você ativando a variável de ambiente FRONTEND_CUSTOM=True (vide **Deste ponto em diante, é exigido o conhecimento que você pode adquirir em https://cli.vuejs.org/guide/ e em https://vuejs.org/v2/guide/ para colaborar com sapl-frontend** -**OBS: após a separação do sapl para o sapl-frontend, o conteúdo da pasta static é compilado e minificado. É gerado pelo build do sapl-frontend e não deve-se tentar customizar ou criar elementos manipulando diretamente informações na pasta static.** +Sobre a pasta static +-------------------- +Após a separação do sapl em sapl e sapl-frontend, o conteúdo da pasta sapl/static/sapl/frontend é compilado e minificado. É gerado pelo build do sapl-frontend e não deve-se tentar customizar ou criar elementos manipulando diretamente informações na pasta sapl/static/sapl/frontend. + +Para aplicar css e javascript sem sapl-frontend: + +1) Não altere diretamente o conteúdo da pasta sapl/static/sapl/frontend. Isso deve ser feito no projeto sapl-frontend. Você perderá qualquer manipulação dentro desta pasta. + +2) Caso venha a criar algum código css/js diretamente no django, crie seus arquivos na pasta sapl/static/sapl. + +3) Não crie nenhum novo conteúdo na pasta sapl/static. Projetos Django podem ser usados como app de outro projeto. É o que ocorre com o Sapl, que é usado como uma app em outros projetos. Qualquer conteúdo colocado dentro sapl/static e não em sapl/static/sapl, pode estar causando erro no uso do Sapl como app em outro projeto. + diff --git a/docs/solr.rst b/docs/solr.rst index 8bc225535..13bdbf02c 100644 --- a/docs/solr.rst +++ b/docs/solr.rst @@ -2,24 +2,87 @@ Instruções para instalar o Solr ================================ -Solr é a ferramenta utilizada pelo SAPL 3.1 para indexar documentos para que possa ser feita -a Pesquisa Textual. +**O servidor do Solr NÃO DEVE SER EXPOSTO NA INTERNET. Assim como o servidor de bancos de dados Postgres ele deve estar acessível pelo SAPL na rede interna (atrás de NATs/firewalls/proxies/etc).** -Adicione ao arquivo ``.env`` o seguinte atributo: +Solr é uma plataforma open source de indexação e busca textual utilizada pelo SAPL 3.1 para indexar documentos (normas jurídicas, matérias legislativas e documentos acessórios). -``SOLR_URL = 'http://127.0.0.1:8983/solr'`` +Observação: Se a execução do SAPL for mediante containers Docker então use o arquivo *docker-compose.yml* disponível em +*https://github.com/interlegis/sapl/blob/3.1.x/solr/docker-compose.yml* (verifique os mapeamentos de volume estão corretos, a verso do SAPL referenciada no arquivo docker-compose.yml, e realize o backup de seu BD **antes** de qualquer tentativa de substituição do arquivo *docker-compose.yml* em uso corrente); -Dentro do diretório principal siga os seguintes passos:: +1) Faça o download da distribuição *binária* do Apache Solr do site oficial do projeto **http://lucene.apache.org/solr** - curl -LO https://archive.apache.org/dist/lucene/solr/4.10.2/solr-4.10.2.tgz - tar xvzf solr-4.10.2.tgz - cd solr-4.10.2 - cd example - java -jar start.jar - ./manage.py build_solr_schema --filename solr-4.10.2/example/solr/collection1/conf/schema.xml + As instalações Solr suportadas até o momento vão da 7.4 à 8; -Após isso, deve-se parar o servidor do Solr e restartar com ``java -jar start.jar`` +2) Descompacte o arquivo em uma pasta do diretório (referenciada neste tutorial como $SOLR_HOME) -**OBS: Toda vez que o código da pesquisa textual for modificado, os comandos de build_solr_schema e start.jar devem ser rodados, nessa mesma ordem.** \ No newline at end of file + +3) Inicie o Solr com o comando: + + **$SOLR_HOME/bin/solr start -c** + + +4) Por meio do browser, acesse a URL **http://localhost:8983** (ou informe o endereço da máquina onde o Solr foi instalado) + +5) Pare o servidor do SAPL; + +6) Edite o arquivo .env adicionando as seguintes linhas: + + USE_SOLR = True + + + SOLR_COLLECTION = sapl + + + SOLR_URL = http://localhost:8983 + + + (o valor do campo SOLR_URL deve corresponder à URL acessada no item 3) + +7) Entre no diretório raiz do SAPL e digite o comando: **python3 solr_api.py -c sapl -u http://localhost:8983`** + + (a URL informada acima deve ser a mesma dos itens 3 e 6) + +8) Enquanto o Solr realiza a indexação da base de dados do SAPL, inicie em uma outra tela o SAPL; + +9) Após realizados os passos com sucesso, nas telas de busca de Matéria Legislativa e Normas deverá aparecer um botão +de 'Pesquisa Textual' na tela de busca tradicional. + +**Observações:** + +* Para parar o Solr execute o comando **$SOLR_HOME/bin/solr stop** + + +* Comandos de manutenção da base textual do Solr: + +1. **python3 manage.py rebuild_index** : Apaga os dados da coleção `sapl` no Solr e reindexa tudo do início; + +2. **python3 manage.py clear_index** : Apaga todos os dados da coleção `sapl` do Solr. **Este comando não irá apagar os dados do BD Postgres, somente os dados do Solr serão apagados.** + +3. **python3 manage.py update_index** : atualiza os dados do Solr: + +3.1. **python3 manage.py update_index --remove** : remove objetos do Solr que não mais existem no BD Postgres (no caso do Postgres e Solr derem dessincronizados). + +3.2. **python3 manage.py update_index --age ** : reindexa os documentos inseridos/alterados nas últimas horas; + +3.3. **python3 manage.py update_index -s YYYY-MM-DDTHH:MM:SS -e YYYY-MM-DDTHH:MM:SS** : reindexa os documentos que foram inseridos/atualizados entre a data inicial (-s) e a data final (-e). Ambos os argumentos de início e fim são opcionais. + + +### FAQ + +1. Uma dúvida quanto a indexação do Solr, pelo que entendi de tempos e tempos tenho que rodar o comando para poder indexar novos arquivos certo? + + Errado. Cada novo documento inserido, atualizado, ou removido do SAPL dispara uma nova indexação somente daquele documento no Solr automaticamente. + +2. O comando **python3 solr_api.py -c sapl -u http://localhost:8983** indexa os novos arquivos? + + Não. Este comando é para construir a coleção do Solr a primeira vez e, por acaso, faz a indexação inicial. Não deve ser usado se a coleção já foi criada. + +3. Ou teria que reindexar do zero com *rebuild_index*? + + Pode acontecer do Postgres e o Solr se dessincronizarem (ex: o Solr ficou fora do ar por um dia e foram inseridos registros no SAPL). Ou por algum motivo se deseja refazer o índice do Solr. Neste caso pode-se refazer a indexação no Solr com o comando : **python3 manage.py rebuild_index** (direto na linha de comando, a partir da pasta raiz do SAPL). Mas existem maneiras de atualizar somente os documentos inseridos/alterados a partir de uma determinada data ao invés de atualizar tudo do zero de novo. + +4. Pergunto isso pois estou querendo criar um script para crontab para indexar esses novos arquivos + +Desnecessário. diff --git a/release.sh b/release.sh index d8301aafe..3470bc633 100755 --- a/release.sh +++ b/release.sh @@ -14,14 +14,16 @@ function bump_version { sed -e s/$VERSION/$NEXT_VERSION/g setup.py > tmp2 mv tmp2 setup.py - sed -e s/$VERSION/$NEXT_VERSION/g sapl/templates/base.html > tmp3 mv tmp3 sapl/templates/base.html + + sed -e s/$VERSION/$NEXT_VERSION/g sapl/settings.py > tmp4 + mv tmp4 sapl/settings.py } function commit_and_push { echo "committing..." - git add docker-compose.yml setup.py sapl/templates/base.html + git add docker-compose.yml setup.py sapl/settings.py sapl/templates/base.html git commit -m "Release: $NEXT_VERSION" git tag $NEXT_VERSION diff --git a/requirements/requirements.txt b/requirements/requirements.txt index 5edc99d50..7e6189373 100644 --- a/requirements/requirements.txt +++ b/requirements/requirements.txt @@ -24,11 +24,12 @@ rtyaml==0.0.5 python-magic==0.4.15 unipath==1.1 WeasyPrint==44 +Pillow==5.1.0 gunicorn==19.9.0 -textract==1.5.0 pysolr==3.6.0 -whoosh==2.7.4 + +pyoai==2.5.0 git+git://github.com/interlegis/trml2pdf.git git+git://github.com/interlegis/django-admin-bootstrapped diff --git a/sapl/api/deprecated.py b/sapl/api/deprecated.py index e38ed3065..1b5fb84e5 100644 --- a/sapl/api/deprecated.py +++ b/sapl/api/deprecated.py @@ -1,11 +1,20 @@ +import logging import logging from django.contrib.contenttypes.models import ContentType from django.db.models import Q +from django.db.models import Q +from django.forms.fields import CharField, MultiValueField +from django.forms.widgets import MultiWidget, TextInput from django.http import Http404 +from django.utils import timezone +from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _ +from django_filters.filters import CharFilter, ModelChoiceFilter, DateFilter from django_filters.rest_framework.backends import DjangoFilterBackend +from django_filters.rest_framework.filterset import FilterSet +from rest_framework import serializers from rest_framework import serializers from rest_framework.generics import ListAPIView from rest_framework.mixins import ListModelMixin, RetrieveModelMixin @@ -13,14 +22,231 @@ from rest_framework.permissions import (IsAuthenticated, IsAuthenticatedOrReadOnly, AllowAny) from rest_framework.viewsets import GenericViewSet -from sapl.api.forms import (AutorChoiceFilterSet, AutoresPossiveisFilterSet, - AutorSearchForFieldFilterSet) from sapl.api.serializers import ModelChoiceSerializer, AutorSerializer,\ ChoiceSerializer from sapl.base.models import TipoAutor, Autor, CasaLegislativa from sapl.materia.models import MateriaLegislativa +from sapl.parlamentares.models import Legislatura from sapl.sessao.models import SessaoPlenaria, OrdemDia from sapl.utils import SaplGenericRelation +from sapl.utils import generic_relations_for_model + + +class SaplGenericRelationSearchFilterSet(FilterSet): + q = CharFilter(method='filter_q') + + def filter_q(self, queryset, name, value): + + query = value.split(' ') + if query: + q = Q() + for qtext in query: + if not qtext: + continue + q_fs = Q(nome__icontains=qtext) + + order_by = [] + + for gr in generic_relations_for_model(self._meta.model): + sgr = gr[1] + for item in sgr: + if item.related_model != self._meta.model: + + continue + flag_order_by = True + for field in item.fields_search: + if flag_order_by: + flag_order_by = False + order_by.append('%s__%s' % ( + item.related_query_name(), + field[0]) + ) + # if len(field) == 3 and field[2](qtext) is not + # None: + q_fs = q_fs | Q(**{'%s__%s%s' % ( + item.related_query_name(), + field[0], + field[1]): qtext if len(field) == 2 + else field[2](qtext)}) + + q = q & q_fs + + if q: + queryset = queryset.filter(q).order_by(*order_by) + + return queryset + + +class SearchForFieldWidget(MultiWidget): + + def decompress(self, value): + if value is None: + return [None, None] + return value + + def __init__(self, attrs=None): + widgets = (TextInput, TextInput) + MultiWidget.__init__(self, widgets, attrs) + + +class SearchForFieldField(MultiValueField): + widget = SearchForFieldWidget + + def __init__(self, *args, **kwargs): + fields = ( + CharField(), + CharField()) + super(SearchForFieldField, self).__init__(fields, *args, **kwargs) + + def compress(self, parameters): + if parameters: + return parameters + return None + + +class SearchForFieldFilter(CharFilter): + field_class = SearchForFieldField + + +class AutorChoiceFilterSet(SaplGenericRelationSearchFilterSet): + q = CharFilter(method='filter_q') + tipo = ModelChoiceFilter(queryset=TipoAutor.objects.all()) + + class Meta: + model = Autor + fields = ['q', + 'tipo', + 'nome', ] + + def filter_q(self, queryset, name, value): + return super().filter_q( + queryset, name, value).distinct('nome').order_by('nome') + + +class AutorSearchForFieldFilterSet(AutorChoiceFilterSet): + q = SearchForFieldFilter(method='filter_q') + + class Meta(AutorChoiceFilterSet.Meta): + pass + + def filter_q(self, queryset, name, value): + + value[0] = value[0].split(',') + value[1] = value[1].split(',') + + params = {} + for key, v in list(zip(value[0], value[1])): + if v in ['True', 'False']: + v = '1' if v == 'True' else '0' + params[key] = v + return queryset.filter(**params).distinct('nome').order_by('nome') + + +class AutoresPossiveisFilterSet(FilterSet): + logger = logging.getLogger(__name__) + data_relativa = DateFilter(method='filter_data_relativa') + tipo = CharFilter(method='filter_tipo') + + class Meta: + model = Autor + fields = ['data_relativa', 'tipo', ] + + def filter_data_relativa(self, queryset, name, value): + return queryset + + def filter_tipo(self, queryset, name, value): + + try: + self.logger.debug( + "Tentando obter TipoAutor correspondente à pk {}.".format(value)) + tipo = TipoAutor.objects.get(pk=value) + except: + self.logger.error("TipoAutor(pk={}) inexistente.".format(value)) + raise serializers.ValidationError(_('Tipo de Autor inexistente.')) + + qs = queryset.filter(tipo=tipo) + + return qs + + @property + def qs(self): + qs = super().qs + + data_relativa = self.form.cleaned_data['data_relativa'] \ + if 'data_relativa' in self.form.cleaned_data else None + + tipo = self.form.cleaned_data['tipo'] \ + if 'tipo' in self.form.cleaned_data else None + + if not tipo: + return qs + + tipo = TipoAutor.objects.get(pk=tipo) + if not tipo.content_type: + return qs + + filter_for_model = 'filter_%s' % tipo.content_type.model + + if not hasattr(self, filter_for_model): + return qs + + if not data_relativa: + data_relativa = timezone.now() + + return getattr(self, filter_for_model)(qs, data_relativa).distinct() + + def filter_parlamentar(self, queryset, data_relativa): + # não leva em conta afastamentos + legislatura_relativa = Legislatura.objects.filter( + data_inicio__lte=data_relativa, + data_fim__gte=data_relativa).first() + + q = Q( + parlamentar_set__mandato__data_inicio_mandato__lte=data_relativa, + parlamentar_set__mandato__data_fim_mandato__isnull=True) | Q( + parlamentar_set__mandato__data_inicio_mandato__lte=data_relativa, + parlamentar_set__mandato__data_fim_mandato__gte=data_relativa) + + if legislatura_relativa.atual(): + q = q & Q(parlamentar_set__ativo=True) + + return queryset.filter(q) + + def filter_comissao(self, queryset, data_relativa): + return queryset.filter( + Q(comissao_set__data_extincao__isnull=True, + comissao_set__data_fim_comissao__isnull=True) | + Q(comissao_set__data_extincao__gte=data_relativa, + comissao_set__data_fim_comissao__isnull=True) | + Q(comissao_set__data_extincao__gte=data_relativa, + comissao_set__data_fim_comissao__isnull=True) | + Q(comissao_set__data_extincao__isnull=True, + comissao_set__data_fim_comissao__gte=data_relativa) | + Q(comissao_set__data_extincao__gte=data_relativa, + comissao_set__data_fim_comissao__gte=data_relativa), + comissao_set__data_criacao__lte=data_relativa) + + def filter_frente(self, queryset, data_relativa): + return queryset.filter( + Q(frente_set__data_extincao__isnull=True) | + Q(frente_set__data_extincao__gte=data_relativa), + frente_set__data_criacao__lte=data_relativa) + + def filter_bancada(self, queryset, data_relativa): + return queryset.filter( + Q(bancada_set__data_extincao__isnull=True) | + Q(bancada_set__data_extincao__gte=data_relativa), + bancada_set__data_criacao__lte=data_relativa) + + def filter_bloco(self, queryset, data_relativa): + return queryset.filter( + Q(bloco_set__data_extincao__isnull=True) | + Q(bloco_set__data_extincao__gte=data_relativa), + bloco_set__data_criacao__lte=data_relativa) + + def filter_orgao(self, queryset, data_relativa): + # na implementação, não havia regras a implementar para orgao + return queryset class AutorChoiceSerializer(ModelChoiceSerializer): diff --git a/sapl/api/forms.py b/sapl/api/forms.py index 0c8a1889f..7cb249ff3 100644 --- a/sapl/api/forms.py +++ b/sapl/api/forms.py @@ -1,231 +1,65 @@ -import logging - -from django.db.models import Q -from django.forms.fields import CharField, MultiValueField -from django.forms.widgets import MultiWidget, TextInput -from django.utils import timezone -from django.utils.translation import ugettext_lazy as _ -from django_filters.filters import CharFilter, ModelChoiceFilter, DateFilter +from django.db.models.fields.files import FileField +from django.template.defaultfilters import capfirst +import django_filters +from django_filters.filters import CharFilter, NumberFilter from django_filters.rest_framework.filterset import FilterSet -from rest_framework import serializers - -from sapl.base.models import Autor, TipoAutor -from sapl.parlamentares.models import Legislatura -from sapl.utils import generic_relations_for_model - - -class SaplGenericRelationSearchFilterSet(FilterSet): - q = CharFilter(method='filter_q') - - def filter_q(self, queryset, name, value): - - query = value.split(' ') - if query: - q = Q() - for qtext in query: - if not qtext: - continue - q_fs = Q(nome__icontains=qtext) - - order_by = [] - - for gr in generic_relations_for_model(self._meta.model): - sgr = gr[1] - for item in sgr: - if item.related_model != self._meta.model: - - continue - flag_order_by = True - for field in item.fields_search: - if flag_order_by: - flag_order_by = False - order_by.append('%s__%s' % ( - item.related_query_name(), - field[0]) - ) - # if len(field) == 3 and field[2](qtext) is not - # None: - q_fs = q_fs | Q(**{'%s__%s%s' % ( - item.related_query_name(), - field[0], - field[1]): qtext if len(field) == 2 - else field[2](qtext)}) - - q = q & q_fs - - if q: - queryset = queryset.filter(q).order_by(*order_by) - - return queryset - - -class SearchForFieldWidget(MultiWidget): +from django_filters.utils import resolve_field +from sapl.sessao.models import SessaoPlenaria - def decompress(self, value): - if value is None: - return [None, None] - return value - def __init__(self, attrs=None): - widgets = (TextInput, TextInput) - MultiWidget.__init__(self, widgets, attrs) +class SaplFilterSetMixin(FilterSet): - -class SearchForFieldField(MultiValueField): - widget = SearchForFieldWidget - - def __init__(self, *args, **kwargs): - fields = ( - CharField(), - CharField()) - super(SearchForFieldField, self).__init__(fields, *args, **kwargs) - - def compress(self, parameters): - if parameters: - return parameters - return None - - -class SearchForFieldFilter(CharFilter): - field_class = SearchForFieldField - - -class AutorChoiceFilterSet(SaplGenericRelationSearchFilterSet): - q = CharFilter(method='filter_q') - tipo = ModelChoiceFilter(queryset=TipoAutor.objects.all()) + o = CharFilter(method='filter_o') class Meta: - model = Autor - fields = ['q', - 'tipo', - 'nome', ] - - def filter_q(self, queryset, name, value): - return super().filter_q( - queryset, name, value).distinct('nome').order_by('nome') - - -class AutorSearchForFieldFilterSet(AutorChoiceFilterSet): - q = SearchForFieldFilter(method='filter_q') - - class Meta(AutorChoiceFilterSet.Meta): - pass - - def filter_q(self, queryset, name, value): - - value[0] = value[0].split(',') - value[1] = value[1].split(',') - - params = {} - for key, v in list(zip(value[0], value[1])): - if v in ['True', 'False']: - v = '1' if v == 'True' else '0' - params[key] = v - return queryset.filter(**params).distinct('nome').order_by('nome') - - -class AutoresPossiveisFilterSet(FilterSet): - logger = logging.getLogger(__name__) - data_relativa = DateFilter(method='filter_data_relativa') - tipo = CharFilter(method='filter_tipo') - - class Meta: - model = Autor - fields = ['data_relativa', 'tipo', ] - - def filter_data_relativa(self, queryset, name, value): - return queryset - - def filter_tipo(self, queryset, name, value): - + fields = '__all__' + filter_overrides = { + FileField: { + 'filter_class': django_filters.CharFilter, + 'extra': lambda f: { + 'lookup_expr': 'exact', + }, + }, + } + + def filter_o(self, queryset, name, value): try: - self.logger.debug( - "Tentando obter TipoAutor correspondente à pk {}.".format(value)) - tipo = TipoAutor.objects.get(pk=value) + return queryset.order_by( + *map(str.strip, value.split(','))) except: - self.logger.error("TipoAutor(pk={}) inexistente.".format(value)) - raise serializers.ValidationError(_('Tipo de Autor inexistente.')) - - qs = queryset.filter(tipo=tipo) - - return qs - - @property - def qs(self): - qs = super().qs - - data_relativa = self.form.cleaned_data['data_relativa'] \ - if 'data_relativa' in self.form.cleaned_data else None - - tipo = self.form.cleaned_data['tipo'] \ - if 'tipo' in self.form.cleaned_data else None - - if not tipo: - return qs - - tipo = TipoAutor.objects.get(pk=tipo) - if not tipo.content_type: - return qs - - filter_for_model = 'filter_%s' % tipo.content_type.model - - if not hasattr(self, filter_for_model): - return qs - - if not data_relativa: - data_relativa = timezone.now() - - return getattr(self, filter_for_model)(qs, data_relativa).distinct() - - def filter_parlamentar(self, queryset, data_relativa): - # não leva em conta afastamentos - legislatura_relativa = Legislatura.objects.filter( - data_inicio__lte=data_relativa, - data_fim__gte=data_relativa).first() - - q = Q( - parlamentar_set__mandato__data_inicio_mandato__lte=data_relativa, - parlamentar_set__mandato__data_fim_mandato__isnull=True) | Q( - parlamentar_set__mandato__data_inicio_mandato__lte=data_relativa, - parlamentar_set__mandato__data_fim_mandato__gte=data_relativa) - - if legislatura_relativa.atual(): - q = q & Q(parlamentar_set__ativo=True) - - return queryset.filter(q) + return queryset + + @classmethod + def filter_for_field(cls, f, name, lookup_expr='exact'): + # Redefine método estático para ignorar filtro para + # fields que não possuam lookup_expr informado + f, lookup_type = resolve_field(f, lookup_expr) + + default = { + 'field_name': name, + 'label': capfirst(f.verbose_name), + 'lookup_expr': lookup_expr + } + + filter_class, params = cls.filter_for_lookup( + f, lookup_type) + default.update(params) + if filter_class is not None: + return filter_class(**default) + return None - def filter_comissao(self, queryset, data_relativa): - return queryset.filter( - Q(comissao_set__data_extincao__isnull=True, - comissao_set__data_fim_comissao__isnull=True) | - Q(comissao_set__data_extincao__gte=data_relativa, - comissao_set__data_fim_comissao__isnull=True) | - Q(comissao_set__data_extincao__gte=data_relativa, - comissao_set__data_fim_comissao__isnull=True) | - Q(comissao_set__data_extincao__isnull=True, - comissao_set__data_fim_comissao__gte=data_relativa) | - Q(comissao_set__data_extincao__gte=data_relativa, - comissao_set__data_fim_comissao__gte=data_relativa), - comissao_set__data_criacao__lte=data_relativa) - def filter_frente(self, queryset, data_relativa): - return queryset.filter( - Q(frente_set__data_extincao__isnull=True) | - Q(frente_set__data_extincao__gte=data_relativa), - frente_set__data_criacao__lte=data_relativa) +class SessaoPlenariaFilterSet(SaplFilterSetMixin): + year = NumberFilter(method='filter_year') + month = NumberFilter(method='filter_month') - def filter_bancada(self, queryset, data_relativa): - return queryset.filter( - Q(bancada_set__data_extincao__isnull=True) | - Q(bancada_set__data_extincao__gte=data_relativa), - bancada_set__data_criacao__lte=data_relativa) + class Meta(SaplFilterSetMixin.Meta): + model = SessaoPlenaria - def filter_bloco(self, queryset, data_relativa): - return queryset.filter( - Q(bloco_set__data_extincao__isnull=True) | - Q(bloco_set__data_extincao__gte=data_relativa), - bloco_set__data_criacao__lte=data_relativa) + def filter_year(self, queryset, name, value): + qs = queryset.filter(data_inicio__year=value) + return qs - def filter_orgao(self, queryset, data_relativa): - # na implementação, não havia regras a implementar para orgao - return queryset + def filter_month(self, queryset, name, value): + qs = queryset.filter(data_inicio__month=value) + return qs diff --git a/sapl/api/serializers.py b/sapl/api/serializers.py index f416517c5..a9ce737c1 100644 --- a/sapl/api/serializers.py +++ b/sapl/api/serializers.py @@ -1,6 +1,13 @@ +from django.conf import settings from rest_framework import serializers +from rest_framework.relations import StringRelatedField -from sapl.base.models import Autor +from sapl.base.models import Autor, CasaLegislativa + + +class IntRelatedField(StringRelatedField): + def to_representation(self, value): + return int(value) class ChoiceSerializer(serializers.Serializer): @@ -38,3 +45,14 @@ class AutorSerializer(serializers.ModelSerializer): class Meta: model = Autor fields = '__all__' + + +class CasaLegislativaSerializer(serializers.ModelSerializer): + version = serializers.SerializerMethodField() + + def get_version(self, obj): + return settings.SAPL_VERSION + + class Meta: + model = CasaLegislativa + fields = '__all__' diff --git a/sapl/api/urls.py b/sapl/api/urls.py index bdcf0e372..27196146d 100644 --- a/sapl/api/urls.py +++ b/sapl/api/urls.py @@ -8,7 +8,7 @@ from rest_framework.routers import DefaultRouter from sapl.api.deprecated import MateriaLegislativaViewSet, SessaoPlenariaViewSet,\ AutoresProvaveisListView, AutoresPossiveisListView, AutorListView,\ ModelChoiceView -from sapl.api.views import SaplSetViews +from sapl.api.views import SaplApiViewSetConstrutor from .apps import AppConfig @@ -21,9 +21,10 @@ router.register(r'materia$', MateriaLegislativaViewSet) router.register(r'sessao-plenaria', SessaoPlenariaViewSet) -for app, built_sets in SaplSetViews.items(): +for app, built_sets in SaplApiViewSetConstrutor._built_sets.items(): for view_prefix, viewset in built_sets.items(): - router.register(app + '/' + view_prefix, viewset) + router.register(app.label + '/' + + view_prefix._meta.model_name, viewset) urlpatterns_router = router.urls @@ -40,7 +41,7 @@ schema_view = get_schema_view( permission_classes=(permissions.AllowAny,), ) -urlpatterns_api = [ +urlpatterns_api_doc = [ url(r'^docs/swagger(?P\.json|\.yaml)$', schema_view.without_ui(cache_timeout=0), name='schema-json'), url(r'^docs/swagger/$', @@ -60,13 +61,16 @@ deprecated_urlpatterns_api = [ url(r'^model/(?P\d+)/(?P\d*)$', ModelChoiceView.as_view(), name='model_list'), + + ] urlpatterns = [ url(r'^api/', include(deprecated_urlpatterns_api)), - url(r'^api/', include(urlpatterns_api)), + url(r'^api/', include(urlpatterns_api_doc)), url(r'^api/', include(urlpatterns_router)), + # implementar caminho para autenticação # https://www.django-rest-framework.org/tutorial/4-authentication-and-permissions/ # url(r'^api/auth/', include('rest_framework.urls', namespace='rest_framework')), diff --git a/sapl/api/views.py b/sapl/api/views.py index 4e88d76ca..13045d6bb 100644 --- a/sapl/api/views.py +++ b/sapl/api/views.py @@ -9,6 +9,7 @@ from django.utils.decorators import classonlymethod from django.utils.text import capfirst from django.utils.translation import ugettext_lazy as _ import django_filters +from django_filters.filters import CharFilter from django_filters.rest_framework.backends import DjangoFilterBackend from django_filters.rest_framework.filterset import FilterSet from django_filters.utils import resolve_field @@ -17,17 +18,42 @@ from rest_framework.decorators import action from rest_framework.response import Response from rest_framework.viewsets import ModelViewSet +from sapl.api.forms import SaplFilterSetMixin from sapl.api.permissions import SaplModelPermissions +from sapl.api.serializers import ChoiceSerializer from sapl.base.models import Autor, AppConfig, DOC_ADM_OSTENSIVO -from sapl.materia.models import Proposicao +from sapl.materia.models import Proposicao, TipoMateriaLegislativa,\ + MateriaLegislativa, Tramitacao from sapl.parlamentares.models import Parlamentar -from sapl.utils import models_with_gr_for_model +from sapl.protocoloadm.models import DocumentoAdministrativo,\ + DocumentoAcessorioAdministrativo, TramitacaoAdministrativo, Anexado +from sapl.sessao.models import SessaoPlenaria, ExpedienteSessao +from sapl.utils import models_with_gr_for_model, choice_anos_com_sessaoplenaria -class SaplApiViewSetConstrutor(ModelViewSet): +class BusinessRulesNotImplementedMixin: + def create(self, request, *args, **kwargs): + raise Exception(_("POST Create não implementado")) + + def update(self, request, *args, **kwargs): + raise Exception(_("PUT and PATCH não implementado")) + + def delete(self, request, *args, **kwargs): + raise Exception(_("DELETE Delete não implementado")) + +class SaplApiViewSet(ModelViewSet): filter_backends = (DjangoFilterBackend,) + +class SaplApiViewSetConstrutor(): + + _built_sets = {} + + @classonlymethod + def get_class_for_model(cls, model): + return cls._built_sets[model._meta.app_config][model] + @classonlymethod def build_class(cls): import inspect @@ -73,40 +99,12 @@ class SaplApiViewSetConstrutor(ModelViewSet): # Define uma classe padrão para filtro caso não tenha sido # criada a classe sapl.api.forms.{model}FilterSet - class SaplFilterSet(FilterSet): - class Meta: + class SaplFilterSet(SaplFilterSetMixin): + class Meta(SaplFilterSetMixin.Meta): model = _model - fields = '__all__' - filter_overrides = { - FileField: { - 'filter_class': django_filters.CharFilter, - 'extra': lambda f: { - 'lookup_expr': 'exact', - }, - }, - } - - @classmethod - def filter_for_field(cls, f, name, lookup_expr='exact'): - # Redefine método estático para ignorar filtro para - # fields que não possuam lookup_expr informado - f, lookup_type = resolve_field(f, lookup_expr) - - default = { - 'field_name': name, - 'label': capfirst(f.verbose_name), - 'lookup_expr': lookup_expr - } - - filter_class, params = cls.filter_for_lookup( - f, lookup_type) - default.update(params) - if filter_class is not None: - return filter_class(**default) - return None # Define uma classe padrão ModelViewSet de DRF - class ModelSaplViewSet(cls): + class ModelSaplViewSet(SaplApiViewSet): queryset = _model.objects.all() # Utiliza o filtro customizado pela classe @@ -130,12 +128,12 @@ class SaplApiViewSetConstrutor(ModelViewSet): apps_sapl = [apps.apps.get_app_config( n[5:]) for n in settings.SAPL_APPS] for app in apps_sapl: - built_sets[app.label] = {} + cls._built_sets[app] = {} for model in app.get_models(): - built_sets[app.label][model._meta.model_name] = build(model) + cls._built_sets[app][model] = build(model) - return built_sets +SaplApiViewSetConstrutor.build_class() """ 1. Constroi uma rest_framework.viewsets.ModelViewSet para @@ -198,15 +196,39 @@ class SaplApiViewSetConstrutor(ModelViewSet): } """ -SaplSetViews = SaplApiViewSetConstrutor.build_class() - # Toda Classe construida acima, pode ser redefinida e aplicado quaisquer # das possibilidades para uma classe normal criada a partir de # rest_framework.viewsets.ModelViewSet conforme exemplo para a classe autor +# decorator para recuperar e transformar o default + + +class customize(object): + def __init__(self, model): + self.model = model + + def __call__(self, cls): + + class _SaplApiViewSet( + cls, + SaplApiViewSetConstrutor._built_sets[ + self.model._meta.app_config][self.model] + ): + pass + + if hasattr(_SaplApiViewSet, 'build'): + _SaplApiViewSet = _SaplApiViewSet.build() + + SaplApiViewSetConstrutor._built_sets[ + self.model._meta.app_config][self.model] = _SaplApiViewSet + return _SaplApiViewSet + # Customização para AutorViewSet com implementação de actions específicas -class _AutorViewSet(SaplSetViews['base']['autor']): + + +@customize(Autor) +class _AutorViewSet: """ Neste exemplo de customização do que foi criado em SaplApiViewSetConstrutor além do ofertado por @@ -251,7 +273,7 @@ class _AutorViewSet(SaplSetViews['base']['autor']): return Response(serializer.data) @classonlymethod - def build_class_with_actions(cls): + def build(cls): models_with_gr_for_autor = models_with_gr_for_model(Autor) @@ -274,7 +296,8 @@ class _AutorViewSet(SaplSetViews['base']['autor']): return cls -class _ParlamentarViewSet(SaplSetViews['parlamentares']['parlamentar']): +@customize(Parlamentar) +class _ParlamentarViewSet: @action(detail=True) def proposicoes(self, request, *args, **kwargs): """ @@ -299,15 +322,16 @@ class _ParlamentarViewSet(SaplSetViews['parlamentares']['parlamentar']): page = self.paginate_queryset(qs) if page is not None: - serializer = SaplSetViews[ - 'materia']['proposicao'].serializer_class(page, many=True) + serializer = SaplApiViewSetConstrutor.get_class_for_model( + Proposicao).serializer_class(page, many=True) return self.get_paginated_response(serializer.data) serializer = self.get_serializer(page, many=True) return Response(serializer.data) -class _ProposicaoViewSet(SaplSetViews['materia']['proposicao']): +@customize(Proposicao) +class _ProposicaoViewSet(): """ list: Retorna lista de Proposições @@ -360,7 +384,49 @@ class _ProposicaoViewSet(SaplSetViews['materia']['proposicao']): return qs -class _DocumentoAdministrativoViewSet(SaplSetViews['protocoloadm']['documentoadministrativo']): +@customize(MateriaLegislativa) +class _MateriaLegislativaViewSet: + + @action(detail=True, methods=['GET']) + def ultima_tramitacao(self, request, *args, **kwargs): + + materia = self.get_object() + if not materia.tramitacao_set.exists(): + return Response({}) + + ultima_tramitacao = materia.tramitacao_set.last() + + serializer_class = SaplApiViewSetConstrutor.get_class_for_model( + Tramitacao).serializer_class(ultima_tramitacao) + + return Response(serializer_class.data) + + @action(detail=True, methods=['GET']) + def anexadas(self, request, *args, **kwargs): + self.queryset = self.get_object().anexadas.all() + return self.list(request, *args, **kwargs) + + +@customize(TipoMateriaLegislativa) +class _TipoMateriaLegislativaViewSet: + + @action(detail=True, methods=['POST']) + def change_position(self, request, *args, **kwargs): + result = { + 'status': 200, + 'message': 'OK' + } + d = request.data + if 'pos_ini' in d and 'pos_fim' in d: + if d['pos_ini'] != d['pos_fim']: + pk = kwargs['pk'] + TipoMateriaLegislativa.objects.reposicione(pk, d['pos_fim']) + + return Response(result) + + +@customize(DocumentoAdministrativo) +class _DocumentoAdministrativoViewSet: class DocumentoAdministrativoPermission(SaplModelPermissions): def has_permission(self, request, view): @@ -394,8 +460,8 @@ class _DocumentoAdministrativoViewSet(SaplSetViews['protocoloadm']['documentoadm return qs -class _DocumentoAcessorioAdministrativoViewSet( - SaplSetViews['protocoloadm']['documentoacessorioadministrativo']): +@customize(DocumentoAcessorioAdministrativo) +class _DocumentoAcessorioAdministrativoViewSet: permission_classes = ( _DocumentoAdministrativoViewSet.DocumentoAdministrativoPermission, ) @@ -408,8 +474,8 @@ class _DocumentoAcessorioAdministrativoViewSet( return qs -class _TramitacaoAdministrativoViewSet( - SaplSetViews['protocoloadm']['tramitacaoadministrativo']): +@customize(TramitacaoAdministrativo) +class _TramitacaoAdministrativoViewSet(BusinessRulesNotImplementedMixin): # TODO: Implementar regras de manutenção das tramitações de docs adms permission_classes = ( @@ -422,25 +488,41 @@ class _TramitacaoAdministrativoViewSet( qs = qs.exclude(documento__restrito=True) return qs - def create(self, request, *args, **kwargs): - raise Exception(_("POST Create não implementado")) - def put(self, request, *args, **kwargs): - raise Exception(_("PUT Update não implementado")) +@customize(Anexado) +class _AnexadoViewSet(BusinessRulesNotImplementedMixin): - def patch(self, request, *args, **kwargs): - raise Exception(_("PATCH Partial Update não implementado")) + permission_classes = ( + _DocumentoAdministrativoViewSet.DocumentoAdministrativoPermission, ) - def delete(self, request, *args, **kwargs): - raise Exception(_("DELETE Delete não implementado")) + def get_queryset(self): + qs = super().get_queryset() + + if self.request.user.is_anonymous(): + qs = qs.exclude(documento__restrito=True) + return qs + + +@customize(SessaoPlenaria) +class _SessaoPlenariaViewSet: + @action(detail=False) + def years(self, request, *args, **kwargs): + years = choice_anos_com_sessaoplenaria() -SaplSetViews['base']['autor'] = _AutorViewSet.build_class_with_actions() + serializer = ChoiceSerializer(years, many=True) + return Response(serializer.data) -SaplSetViews['materia']['proposicao'] = _ProposicaoViewSet + @action(detail=True) + def expedientes(self, request, *args, **kwargs): -SaplSetViews['parlamentares']['parlamentar'] = _ParlamentarViewSet + sessao = self.get_object() -SaplSetViews['protocoloadm']['documentoadministrativo'] = _DocumentoAdministrativoViewSet -SaplSetViews['protocoloadm']['documentoacessorioadministrativo'] = _DocumentoAcessorioAdministrativoViewSet -SaplSetViews['protocoloadm']['tramitacaoadministrativo'] = _TramitacaoAdministrativoViewSet + page = self.paginate_queryset(sessao.expedientesessao_set.all()) + if page is not None: + serializer = SaplApiViewSetConstrutor.get_class_for_model( + ExpedienteSessao).serializer_class(page, many=True) + return self.get_paginated_response(serializer.data) + + serializer = self.get_serializer(page, many=True) + return Response(serializer.data) diff --git a/sapl/audiencia/forms.py b/sapl/audiencia/forms.py index ab2f2a6e7..56f4d1285 100755 --- a/sapl/audiencia/forms.py +++ b/sapl/audiencia/forms.py @@ -7,12 +7,12 @@ from django.utils.translation import ugettext_lazy as _ from sapl.audiencia.models import AudienciaPublica, TipoAudienciaPublica, AnexoAudienciaPublica from crispy_forms.layout import HTML, Button, Column, Fieldset, Layout -from crispy_forms.helper import FormHelper +from sapl.crispy_layout_mixin import SaplFormHelper from sapl.crispy_layout_mixin import SaplFormLayout, form_actions, to_row from sapl.materia.models import MateriaLegislativa, TipoMateriaLegislativa -from sapl.utils import timezone +from sapl.utils import timezone, FileFieldCheckMixin -class AudienciaForm(forms.ModelForm): +class AudienciaForm(FileFieldCheckMixin, forms.ModelForm): logger = logging.getLogger(__name__) data_atual = timezone.now() @@ -134,7 +134,7 @@ class AnexoAudienciaPublicaForm(forms.ModelForm): row2 = to_row( [('assunto', 12)]) - self.helper = FormHelper() + self.helper = SaplFormHelper() self.helper.layout = SaplFormLayout( Fieldset(_('Identificação Básica'), row1, row2)) diff --git a/sapl/audiencia/migrations/0010_auto_20190219_1511.py b/sapl/audiencia/migrations/0010_auto_20190219_1511.py new file mode 100644 index 000000000..e3f9a046b --- /dev/null +++ b/sapl/audiencia/migrations/0010_auto_20190219_1511.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.20 on 2019-02-19 18:11 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.utils.timezone +import sapl.utils + + +class Migration(migrations.Migration): + + dependencies = [ + ('audiencia', '0009_remove_anexoaudienciapublica_indexacao'), + ] + + operations = [ + migrations.AlterField( + model_name='anexoaudienciapublica', + name='arquivo', + field=models.FileField(default='Assunto não existente.', upload_to=sapl.utils.texto_upload_path, verbose_name='Arquivo'), + preserve_default=False, + ), + migrations.AlterField( + model_name='anexoaudienciapublica', + name='assunto', + field=models.TextField(verbose_name='Assunto'), + ), + migrations.AlterField( + model_name='anexoaudienciapublica', + name='data', + field=models.DateField(auto_now=True, default=django.utils.timezone.now), + preserve_default=False, + ), + ] diff --git a/sapl/audiencia/models.py b/sapl/audiencia/models.py index 684f27625..76c36c6c0 100755 --- a/sapl/audiencia/models.py +++ b/sapl/audiencia/models.py @@ -155,13 +155,12 @@ class AnexoAudienciaPublica(models.Model): audiencia = models.ForeignKey(AudienciaPublica, on_delete=models.PROTECT) arquivo = models.FileField( - blank=True, - null=True, upload_to=texto_upload_path, verbose_name=_('Arquivo')) - data = models.DateField(auto_now=timezone.now,blank=True, null=True) + data = models.DateField( + auto_now=timezone.now) assunto = models.TextField( - blank=True, verbose_name=_('Assunto')) + verbose_name=_('Assunto')) class Meta: verbose_name = _('Anexo de Documento Acessório') @@ -174,22 +173,19 @@ class AnexoAudienciaPublica(models.Model): if self.arquivo: self.arquivo.delete() - return models.Model.delete( - self, using=using, keep_parents=keep_parents) - - def save(self, force_insert=False, force_update=False, using=None, - update_fields=None): + return models.Model.delete(self, using=using, keep_parents=keep_parents) + def save(self, force_insert=False, force_update=False, using=None, update_fields=None): if not self.pk and self.arquivo: arquivo = self.arquivo self.arquivo = None - models.Model.save(self, force_insert=force_insert, - force_update=force_update, - using=using, - update_fields=update_fields) + models.Model.save( + self, + force_insert=force_insert, + force_update=force_update, + using=using, + update_fields=update_fields) self.arquivo = arquivo - return models.Model.save(self, force_insert=force_insert, - force_update=force_update, - using=using, - update_fields=update_fields) \ No newline at end of file + return models.Model.save(self, force_insert=force_insert, force_update=force_update, using=using, + update_fields=update_fields) diff --git a/sapl/audiencia/urls.py b/sapl/audiencia/urls.py index bdf02beca..b268fd56f 100755 --- a/sapl/audiencia/urls.py +++ b/sapl/audiencia/urls.py @@ -1,11 +1,10 @@ from django.conf.urls import include, url -from sapl.audiencia.views import (index, AudienciaCrud,AnexoAudienciaPublicaCrud) +from sapl.audiencia.views import (index, AudienciaCrud, AnexoAudienciaPublicaCrud) from .apps import AppConfig app_name = AppConfig.name urlpatterns = [ - url(r'^audiencia/', include(AudienciaCrud.get_urls() + - AnexoAudienciaPublicaCrud.get_urls())), + url(r'^audiencia/', include(AudienciaCrud.get_urls() + AnexoAudienciaPublicaCrud.get_urls())), ] \ No newline at end of file diff --git a/sapl/audiencia/views.py b/sapl/audiencia/views.py index 93d214683..82e921469 100755 --- a/sapl/audiencia/views.py +++ b/sapl/audiencia/views.py @@ -86,6 +86,7 @@ class AnexoAudienciaPublicaCrud(MasterDetailCrud): model = AnexoAudienciaPublica parent_field = 'audiencia' help_topic = 'numeracao_docsacess' + public = [RP_LIST, RP_DETAIL, ] class BaseMixin(MasterDetailCrud.BaseMixin): list_field_names = ['assunto'] @@ -104,7 +105,5 @@ class AnexoAudienciaPublicaCrud(MasterDetailCrud): kwargs = {self.crud.parent_field: self.kwargs['pk']} return qs.filter(**kwargs).order_by('-data', '-id') - class DetailView(AudienciaPublicaMixin, - MasterDetailCrud.DetailView): + class DetailView(AudienciaPublicaMixin, MasterDetailCrud.DetailView): pass - \ No newline at end of file diff --git a/sapl/base/email_utils.py b/sapl/base/email_utils.py index b41c68402..7c23dd2da 100644 --- a/sapl/base/email_utils.py +++ b/sapl/base/email_utils.py @@ -11,6 +11,7 @@ from sapl.materia.models import AcompanhamentoMateria from sapl.protocoloadm.models import AcompanhamentoDocumento from sapl.settings import EMAIL_SEND_USER from sapl.utils import mail_service_configured +from django.utils.translation import ugettext_lazy as _ def load_email_templates(templates, context={}): @@ -208,8 +209,8 @@ def do_envia_email_tramitacao(base_url, tipo, doc_mat, status, unidade_destino): # Envia email de tramitacao para usuarios cadastrados # + logger = logging.getLogger(__name__) if not mail_service_configured(): - logger = logging.getLogger(__name__) logger.warning(_('Servidor de email não configurado.')) return @@ -220,6 +221,10 @@ def do_envia_email_tramitacao(base_url, tipo, doc_mat, status, unidade_destino): destinatarios = AcompanhamentoDocumento.objects.filter(documento=doc_mat, confirmado=True) + if not destinatarios: + logger.debug(_('Não existem destinatários cadastrados para essa matéria.')) + return + casa = CasaLegislativa.objects.first() sender = EMAIL_SEND_USER diff --git a/sapl/base/forms.py b/sapl/base/forms.py index d97ed1804..b04fddbd6 100644 --- a/sapl/base/forms.py +++ b/sapl/base/forms.py @@ -1,7 +1,8 @@ import logging +import os from crispy_forms.bootstrap import FieldWithButtons, InlineRadios, StrictButton -from crispy_forms.helper import FormHelper +from sapl.crispy_layout_mixin import SaplFormHelper from crispy_forms.layout import HTML, Button, Div, Field, Fieldset, Layout, Row from django import forms from django.conf import settings @@ -28,7 +29,7 @@ from sapl.crispy_layout_mixin import (SaplFormLayout, form_actions, to_column, from sapl.materia.models import ( MateriaLegislativa, UnidadeTramitacao, StatusTramitacao) from sapl.norma.models import (NormaJuridica, NormaEstatisticas) -from sapl.parlamentares.models import SessaoLegislativa +from sapl.parlamentares.models import SessaoLegislativa, Partido from sapl.sessao.models import SessaoPlenaria from sapl.settings import MAX_IMAGE_UPLOAD_SIZE from sapl.utils import (RANGE_ANOS, YES_NO_CHOICES, @@ -36,8 +37,7 @@ from sapl.utils import (RANGE_ANOS, YES_NO_CHOICES, RangeWidgetOverride, autor_label, autor_modal, models_with_gr_for_model, qs_override_django_filter, choice_anos_com_normas, choice_anos_com_materias, - FilterOverridesMetaMixin) - + FilterOverridesMetaMixin, FileFieldCheckMixin) from .models import AppConfig, CasaLegislativa @@ -64,19 +64,46 @@ def get_roles(): class UsuarioCreateForm(ModelForm): logger = logging.getLogger(__name__) - username = forms.CharField(required=True, label="Nome de usuário", - max_length=30) - firstname = forms.CharField(required=True, label="Nome", max_length=30) - lastname = forms.CharField(required=True, label="Sobrenome", max_length=30) - password1 = forms.CharField(required=True, widget=forms.PasswordInput, - label='Senha', max_length=128) - password2 = forms.CharField(required=True, widget=forms.PasswordInput, - label='Confirmar senha', max_length=128) - user_active = forms.ChoiceField(required=False, choices=YES_NO_CHOICES, - label="Usuário ativo?", initial='True') - + username = forms.CharField( + required=True, + label="Nome de usuário", + max_length=30 + ) + firstname = forms.CharField( + required=True, + label="Nome", + max_length=30 + ) + lastname = forms.CharField( + required=True, + label="Sobrenome", + max_length=30 + ) + password1 = forms.CharField( + required=True, + widget=forms.PasswordInput, + label='Senha', + min_length=6, + max_length=128 + ) + password2 = forms.CharField( + required=True, + widget=forms.PasswordInput, + label='Confirmar senha', + min_length=6, + max_length=128 + ) + user_active = forms.ChoiceField( + required=True, + choices=YES_NO_CHOICES, + label="Usuário ativo?", + initial='True' + ) roles = forms.MultipleChoiceField( - required=True, widget=forms.CheckboxSelectMultiple(), choices=get_roles) + required=True, + widget=forms.CheckboxSelectMultiple(), + choices=get_roles + ) class Meta: model = get_user_model() @@ -84,7 +111,7 @@ class UsuarioCreateForm(ModelForm): 'password1', 'password2', 'user_active', 'roles'] def clean(self): - super(UsuarioCreateForm, self).clean() + super().clean() if not self.is_valid(): return self.cleaned_data @@ -99,7 +126,7 @@ class UsuarioCreateForm(ModelForm): def __init__(self, *args, **kwargs): - super(UsuarioCreateForm, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) row0 = to_row([('username', 12)]) @@ -112,16 +139,38 @@ class UsuarioCreateForm(ModelForm): [('password1', 6), ('password2', 6)]) - row4 = to_row([(form_actions(label='Confirmar'), 6)]) - - self.helper = FormHelper() + self.helper = SaplFormHelper() self.helper.layout = Layout( row0, row1, row3, row2, 'roles', - row4) + form_actions(label='Confirmar')) + + +class UsuarioFilterSet(django_filters.FilterSet): + + username = django_filters.CharFilter( + label=_('Nome de Usuário'), + lookup_expr='icontains') + + class Meta: + model = User + fields = ['username'] + + def __init__(self, *args, **kwargs): + super(UsuarioFilterSet, self).__init__(*args, **kwargs) + + row0 = to_row([('username', 12)]) + + self.form.helper = SaplFormHelper() + self.form.helper.form_method = 'GET' + self.form.helper.layout = Layout( + Fieldset(_('Pesquisa de Usuário'), + row0, + form_actions(label='Pesquisar')) + ) class UsuarioEditForm(ModelForm): @@ -154,12 +203,12 @@ class UsuarioEditForm(ModelForm): row3 = to_row([(form_actions(label='Salvar Alterações'), 6)]) - self.helper = FormHelper() + self.helper = SaplFormHelper() self.helper.layout = Layout( row1, row2, 'roles', - row3) + form_actions(label='Salvar Alterações')) def clean(self): super(UsuarioEditForm, self).clean() @@ -176,7 +225,7 @@ class UsuarioEditForm(ModelForm): return data -class SessaoLegislativaForm(ModelForm): +class SessaoLegislativaForm(FileFieldCheckMixin, ModelForm): logger = logging.getLogger(__name__) class Meta: @@ -432,7 +481,7 @@ class AutorForm(ModelForm): controle_acesso = Fieldset(_('Controle de Acesso do Autor'), *controle_acesso) - self.helper = FormHelper() + self.helper = SaplFormHelper() self.helper.layout = SaplFormLayout(autor_select, controle_acesso) super(AutorForm, self).__init__(*args, **kwargs) @@ -697,7 +746,7 @@ class RelatorioAtasFilterSet(django_filters.FilterSet): row1 = to_row([('data_inicio', 12)]) - self.form.helper = FormHelper() + self.form.helper = SaplFormHelper() self.form.helper.form_method = 'GET' self.form.helper.layout = Layout( Fieldset(_('Atas das Sessões Plenárias'), @@ -733,7 +782,7 @@ class RelatorioNormasMesFilterSet(django_filters.FilterSet): row1 = to_row([('ano', 12)]) - self.form.helper = FormHelper() + self.form.helper = SaplFormHelper() self.form.helper.form_method = 'GET' self.form.helper.layout = Layout( Fieldset(_('Normas por mês do ano.'), @@ -762,7 +811,7 @@ class EstatisticasAcessoNormasForm(Form): row1 = to_row([('ano', 12)]) - self.helper = FormHelper() + self.helper = SaplFormHelper() self.helper.form_method = 'GET' self.helper.layout = Layout( Fieldset(_('Normas por acessos nos meses do ano.'), @@ -800,7 +849,7 @@ class RelatorioNormasVigenciaFilterSet(django_filters.FilterSet): row1 = to_row([('ano', 12)]) row2 = to_row([('vigencia', 12)]) - self.form.helper = FormHelper() + self.form.helper = SaplFormHelper() self.form.helper.form_method = 'GET' self.form.helper.layout = Layout( Fieldset(_('Normas por vigência.'), @@ -828,7 +877,7 @@ class RelatorioPresencaSessaoFilterSet(django_filters.FilterSet): row1 = to_row([('data_inicio', 12)]) - self.form.helper = FormHelper() + self.form.helper = SaplFormHelper() self.form.helper.form_method = 'GET' self.form.helper.layout = Layout( Fieldset(_('Presença dos parlamentares nas sessões plenárias'), @@ -859,7 +908,7 @@ class RelatorioHistoricoTramitacaoFilterSet(django_filters.FilterSet): self.filters['tipo'].label = 'Tipo de Matéria' self.filters['tramitacao__unidade_tramitacao_local' - ].label = _('Unidade Local (Último Local)') + ].label = _('Unidade Local') self.filters['tramitacao__status'].label = _('Status') row1 = to_row([('tramitacao__data_tramitacao', 12)]) @@ -868,7 +917,7 @@ class RelatorioHistoricoTramitacaoFilterSet(django_filters.FilterSet): ('tramitacao__unidade_tramitacao_local', 4), ('tramitacao__status', 4)]) - self.form.helper = FormHelper() + self.form.helper = SaplFormHelper() self.form.helper.form_method = 'GET' self.form.helper.layout = Layout( Fieldset(_('Histórico de Tramitação'), @@ -894,14 +943,16 @@ class RelatorioDataFimPrazoTramitacaoFilterSet(django_filters.FilterSet): *args, **kwargs) self.filters['tipo'].label = 'Tipo de Matéria' - + self.filters['tramitacao__unidade_tramitacao_local'].label = 'Unidade de tramitação local' + self.filters['tramitacao__status'].label = 'Status de tramitação' + row1 = to_row([('tramitacao__data_fim_prazo', 12)]) row2 = to_row( [('tipo', 4), ('tramitacao__unidade_tramitacao_local', 4), ('tramitacao__status', 4)]) - self.form.helper = FormHelper() + self.form.helper = SaplFormHelper() self.form.helper.form_method = 'GET' self.form.helper.layout = Layout( Fieldset(_('Tramitações por fim de prazo'), @@ -932,7 +983,7 @@ class RelatorioReuniaoFilterSet(django_filters.FilterSet): ('nome', 4), ('tema', 4)]) - self.form.helper = FormHelper() + self.form.helper = SaplFormHelper() self.form.helper.form_method = 'GET' self.form.helper.layout = Layout( Fieldset(_('Reunião de Comissão'), @@ -962,7 +1013,7 @@ class RelatorioAudienciaFilterSet(django_filters.FilterSet): [('tipo', 4), ('nome', 4)]) - self.form.helper = FormHelper() + self.form.helper = SaplFormHelper() self.form.helper.form_method = 'GET' self.form.helper.layout = Layout( Fieldset(_('Audiência Pública'), @@ -1006,7 +1057,7 @@ class RelatorioMateriasTramitacaoilterSet(django_filters.FilterSet): row3 = to_row([('tramitacao__unidade_tramitacao_destino', 12)]) row4 = to_row([('tramitacao__status', 12)]) - self.form.helper = FormHelper() + self.form.helper = SaplFormHelper() self.form.helper.form_method = 'GET' self.form.helper.layout = Layout( Fieldset(_('Pesquisa de Matéria em Tramitação'), @@ -1032,7 +1083,7 @@ class RelatorioMateriasPorAnoAutorTipoFilterSet(django_filters.FilterSet): row1 = to_row( [('ano', 12)]) - self.form.helper = FormHelper() + self.form.helper = SaplFormHelper() self.form.helper.form_method = 'GET' self.form.helper.layout = Layout( Fieldset(_('Pesquisar'), @@ -1074,7 +1125,7 @@ class RelatorioMateriasPorAutorFilterSet(django_filters.FilterSet): 'limpar Autor', css_class='btn btn-primary btn-sm'), 10)]) - self.form.helper = FormHelper() + self.form.helper = SaplFormHelper() self.form.helper.form_method = 'GET' self.form.helper.layout = Layout( Fieldset(_('Pesquisar'), @@ -1086,7 +1137,7 @@ class RelatorioMateriasPorAutorFilterSet(django_filters.FilterSet): ) -class CasaLegislativaForm(ModelForm): +class CasaLegislativaForm(FileFieldCheckMixin, ModelForm): class Meta: @@ -1116,7 +1167,11 @@ class CasaLegislativaForm(ModelForm): } def clean_logotipo(self): - logotipo = self.cleaned_data.get('logotipo', False) + # chama __clean de FileFieldCheckMixin + # por estar em clean de campo + super(CasaLegislativaForm, self)._check() + + logotipo = self.cleaned_data.get('logotipo') if logotipo: if logotipo.size > MAX_IMAGE_UPLOAD_SIZE: raise ValidationError("Imagem muito grande. ( > 2MB )") @@ -1148,13 +1203,15 @@ class ConfiguracoesAppForm(ModelForm): class Meta: model = AppConfig fields = ['documentos_administrativos', - 'sequencia_numeracao', + 'sequencia_numeracao_protocolo', + 'sequencia_numeracao_proposicao', 'esfera_federacao', # 'painel_aberto', # TODO: a ser implementado na versão 3.2 'texto_articulado_proposicao', 'texto_articulado_materia', 'texto_articulado_norma', 'proposicao_incorporacao_obrigatoria', + 'protocolo_manual', 'cronometro_discurso', 'cronometro_aparte', 'cronometro_ordem', @@ -1162,7 +1219,8 @@ class ConfiguracoesAppForm(ModelForm): 'mostrar_brasao_painel', 'receber_recibo_proposicao', 'assinatura_ata', - 'estatisticas_acesso_normas'] + 'estatisticas_acesso_normas', + 'escolher_numero_materia_proposicao'] def __init__(self, *args, **kwargs): super(ConfiguracoesAppForm, self).__init__(*args, **kwargs) @@ -1180,7 +1238,7 @@ class ConfiguracoesAppForm(ModelForm): self.logger.error('Não há casa legislativa relacionada.') raise ValidationError("Não há casa legislativa relacionada.") - if (not bool(casa.logotipo) and mostrar_brasao_painel): + if not casa.logotipo and mostrar_brasao_painel: self.logger.error('Não há logitipo configurado para esta ' 'CasaLegislativa ({}).'.format(casa)) raise ValidationError("Não há logitipo configurado para esta " @@ -1196,7 +1254,7 @@ class RecuperarSenhaForm(PasswordResetForm): def __init__(self, *args, **kwargs): row1 = to_row( [('email', 12)]) - self.helper = FormHelper() + self.helper = SaplFormHelper() self.helper.layout = Layout( Fieldset(_('Insira o e-mail cadastrado com a sua conta'), row1, @@ -1233,7 +1291,7 @@ class NovaSenhaForm(SetPasswordForm): [('new_password1', 6), ('new_password2', 6)]) - self.helper = FormHelper() + self.helper = SaplFormHelper() self.helper.layout = Layout( row1, form_actions(label='Enviar')) @@ -1266,7 +1324,7 @@ class AlterarSenhaForm(Form): [('new_password1', 6), ('new_password2', 6)]) - self.helper = FormHelper() + self.helper = SaplFormHelper() self.helper.layout = Layout( row1, row2, @@ -1322,3 +1380,46 @@ class AlterarSenhaForm(Form): "Nova senha não pode ser igual à senha anterior") return self.cleaned_data + + +class PartidoForm(FileFieldCheckMixin, ModelForm): + + class Meta: + model = Partido + exclude = [] + + def __init__(self, *args, **kwargs): + + super(PartidoForm, self).__init__(*args, **kwargs) + + # TODO Utilizar esses campos na issue #2161 de alteração de nomes de partidos + # if self.instance: + # if self.instance.nome: + # self.fields['nome'].widget.attrs['readonly'] = True + # self.fields['sigla'].widget.attrs['readonly'] = True + + row1 = to_row( + [('sigla', 2), + ('nome', 6), + ('data_criacao', 2), + ('data_extincao', 2),]) + row2 = to_row([('observacao', 12)]) + row3 = to_row([('logo_partido', 12)]) + + self.helper = SaplFormHelper() + self.helper.layout = Layout( + row1, row2, row3, + form_actions(label='Salvar')) + + def clean(self): + + cleaned_data = super(PartidoForm, self).clean() + + if not self.is_valid(): + return cleaned_data + + if cleaned_data['data_criacao'] and cleaned_data['data_extincao']: + if cleaned_data['data_criacao'] > cleaned_data['data_extincao']: + raise ValidationError("Certifique-se de que a data de criação seja anterior à data de extinção.") + + return cleaned_data \ No newline at end of file diff --git a/sapl/base/migrations/0030_appconfig_escolher_numero_materia_proposicao.py b/sapl/base/migrations/0030_appconfig_escolher_numero_materia_proposicao.py new file mode 100644 index 000000000..69bba70bc --- /dev/null +++ b/sapl/base/migrations/0030_appconfig_escolher_numero_materia_proposicao.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.20 on 2019-02-19 11:14 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('base', '0029_remove_appconfig_relatorios_atos'), + ] + + operations = [ + migrations.AddField( + model_name='appconfig', + name='escolher_numero_materia_proposicao', + field=models.BooleanField(choices=[(True, 'Sim'), (False, 'Não')], default=False, verbose_name='Indicar número da matéria a ser gerada na proposição?'), + ), + ] diff --git a/sapl/base/migrations/0030_appconfig_protocolo_manual.py b/sapl/base/migrations/0030_appconfig_protocolo_manual.py new file mode 100644 index 000000000..7782f974d --- /dev/null +++ b/sapl/base/migrations/0030_appconfig_protocolo_manual.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.20 on 2019-02-15 18:25 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('base', '0029_remove_appconfig_relatorios_atos'), + ] + + operations = [ + migrations.AddField( + model_name='appconfig', + name='protocolo_manual', + field=models.BooleanField(choices=[(True, 'Sim'), (False, 'Não')], default=False, verbose_name='Protocolar proposição somente com recibo?'), + ), + ] diff --git a/sapl/base/migrations/0031_auto_20190218_1109.py b/sapl/base/migrations/0031_auto_20190218_1109.py new file mode 100644 index 000000000..a87922a83 --- /dev/null +++ b/sapl/base/migrations/0031_auto_20190218_1109.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.20 on 2019-02-18 14:09 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('base', '0030_appconfig_protocolo_manual'), + ] + + operations = [ + migrations.AlterField( + model_name='appconfig', + name='protocolo_manual', + field=models.BooleanField(choices=[(True, 'Sim'), (False, 'Não')], default=False, verbose_name='Informar data e hora de protocolo?'), + ), + ] diff --git a/sapl/base/migrations/0032_merge_20190219_0941.py b/sapl/base/migrations/0032_merge_20190219_0941.py new file mode 100644 index 000000000..0f30001e6 --- /dev/null +++ b/sapl/base/migrations/0032_merge_20190219_0941.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.20 on 2019-02-19 12:41 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('base', '0031_auto_20190218_1109'), + ('base', '0030_appconfig_escolher_numero_materia_proposicao'), + ] + + operations = [ + ] diff --git a/sapl/base/migrations/0033_auto_20190415_1050.py b/sapl/base/migrations/0033_auto_20190415_1050.py new file mode 100644 index 000000000..8c21764f3 --- /dev/null +++ b/sapl/base/migrations/0033_auto_20190415_1050.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.20 on 2019-04-15 13:50 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('base', '0032_merge_20190219_0941'), + ] + + operations = [ + migrations.AlterField( + model_name='appconfig', + name='sequencia_numeracao', + field=models.CharField(choices=[('A', 'Sequencial por ano para cada autor'), ('B', 'Sequencial por ano indepententemente do autor'), ('L', 'Sequencial por legislatura'), ('U', 'Sequencial único')], default='A', max_length=1, verbose_name='Sequência de numeração'), + ), + ] diff --git a/sapl/base/migrations/0034_auto_20190417_0941.py b/sapl/base/migrations/0034_auto_20190417_0941.py new file mode 100644 index 000000000..01d7347b4 --- /dev/null +++ b/sapl/base/migrations/0034_auto_20190417_0941.py @@ -0,0 +1,29 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.20 on 2019-04-17 12:41 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('base', '0033_auto_20190415_1050'), + ] + + operations = [ + migrations.RemoveField( + model_name='appconfig', + name='sequencia_numeracao', + ), + migrations.AddField( + model_name='appconfig', + name='sequencia_numeracao_proposicao', + field=models.CharField(choices=[('A', 'Sequencial por ano para cada autor'), ('B', 'Sequencial por ano indepententemente do autor'), ('L', 'Sequencial por legislatura'), ('U', 'Sequencial único')], default='A', max_length=1, verbose_name='Sequência de numeração de proposições'), + ), + migrations.AddField( + model_name='appconfig', + name='sequencia_numeracao_protocolo', + field=models.CharField(choices=[('A', 'Sequencial por ano para cada autor'), ('L', 'Sequencial por legislatura'), ('U', 'Sequencial único')], default='A', max_length=1, verbose_name='Sequência de numeração de protocolos'), + ), + ] diff --git a/sapl/base/migrations/0035_auto_20190417_1009.py b/sapl/base/migrations/0035_auto_20190417_1009.py new file mode 100644 index 000000000..ba76ca4d0 --- /dev/null +++ b/sapl/base/migrations/0035_auto_20190417_1009.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.20 on 2019-04-17 13:09 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('base', '0034_auto_20190417_0941'), + ] + + operations = [ + migrations.AlterField( + model_name='appconfig', + name='sequencia_numeracao_proposicao', + field=models.CharField(choices=[('A', 'Sequencial por ano para cada autor'), ('B', 'Sequencial por ano indepententemente do autor')], default='A', max_length=1, verbose_name='Sequência de numeração de proposições'), + ), + ] diff --git a/sapl/base/migrations/0036_auto_20190417_1432.py b/sapl/base/migrations/0036_auto_20190417_1432.py new file mode 100644 index 000000000..47720668a --- /dev/null +++ b/sapl/base/migrations/0036_auto_20190417_1432.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.20 on 2019-04-17 17:32 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('base', '0035_auto_20190417_1009'), + ] + + operations = [ + migrations.AlterField( + model_name='appconfig', + name='sequencia_numeracao_protocolo', + field=models.CharField(choices=[('A', 'Sequencial por ano'), ('L', 'Sequencial por legislatura'), ('U', 'Sequencial único')], default='A', max_length=1, verbose_name='Sequência de numeração de protocolos'), + ), + ] diff --git a/sapl/base/models.py b/sapl/base/models.py index 55792b147..eea9241d9 100644 --- a/sapl/base/models.py +++ b/sapl/base/models.py @@ -18,10 +18,13 @@ TIPO_DOCUMENTO_ADMINISTRATIVO = ((DOC_ADM_OSTENSIVO, _('Ostensiva')), RELATORIO_ATOS_ACESSADOS = (('S', _('Sim')), ('N', _('Não'))) -SEQUENCIA_NUMERACAO = (('A', _('Sequencial por ano')), +SEQUENCIA_NUMERACAO_PROTOCOLO = (('A', _('Sequencial por ano')), ('L', _('Sequencial por legislatura')), ('U', _('Sequencial único'))) +SEQUENCIA_NUMERACAO_PROPOSICAO = (('A', _('Sequencial por ano para cada autor')), + ('B', _('Sequencial por ano indepententemente do autor'))) + ESFERA_FEDERACAO_CHOICES = (('M', _('Municipal')), ('E', _('Estadual')), ('F', _('Federal')), @@ -95,10 +98,15 @@ class AppConfig(models.Model): verbose_name=_('Estatísticas de acesso a normas'), choices=RELATORIO_ATOS_ACESSADOS, default='N') - sequencia_numeracao = models.CharField( + sequencia_numeracao_proposicao = models.CharField( + max_length=1, + verbose_name=_('Sequência de numeração de proposições'), + choices=SEQUENCIA_NUMERACAO_PROPOSICAO, default='A') + + sequencia_numeracao_protocolo = models.CharField( max_length=1, - verbose_name=_('Sequência de numeração'), - choices=SEQUENCIA_NUMERACAO, default='A') + verbose_name=_('Sequência de numeração de protocolos'), + choices=SEQUENCIA_NUMERACAO_PROTOCOLO, default='A') esfera_federacao = models.CharField( max_length=1, @@ -160,6 +168,14 @@ class AppConfig(models.Model): verbose_name=_('Protocolar proposição somente com recibo?'), choices=YES_NO_CHOICES, default=True) + protocolo_manual = models.BooleanField( + verbose_name=_('Informar data e hora de protocolo?'), + choices=YES_NO_CHOICES, default=False) + + escolher_numero_materia_proposicao = models.BooleanField( + verbose_name=_('Indicar número da matéria a ser gerada na proposição?'), + choices=YES_NO_CHOICES, default=False) + class Meta: verbose_name = _('Configurações da Aplicação') verbose_name_plural = _('Configurações da Aplicação') diff --git a/sapl/base/search_indexes.py b/sapl/base/search_indexes.py index 0e0283ba8..359fbd44b 100644 --- a/sapl/base/search_indexes.py +++ b/sapl/base/search_indexes.py @@ -1,5 +1,4 @@ import os.path -import textract import logging from django.db.models import F, Q, Value @@ -11,7 +10,6 @@ from haystack.constants import Indexable from haystack.fields import CharField from haystack.indexes import SearchIndex from haystack.utils import get_model_ct_tuple -from textract.exceptions import ExtensionNotSupported from sapl.compilacao.models import (STATUS_TA_IMMUTABLE_PUBLIC, STATUS_TA_PUBLIC, Dispositivo) @@ -49,19 +47,6 @@ class TextExtractField(CharField): data = '' return data - def whoosh_extraction(self, arquivo): - - if arquivo.path.endswith('html') or arquivo.path.endswith('xml'): - with open(arquivo.path, 'r', encoding="utf8", errors='ignore') as f: - content = ' '.join(f.read()) - return RemoveTag(content) - - else: - return textract.process( - arquivo.path, - language='pt-br').decode('utf-8').replace('\n', ' ').replace( - '\t', ' ') - def print_error(self, arquivo, error): msg = 'Erro inesperado processando arquivo %s erro: %s' % ( arquivo.path, error) @@ -80,20 +65,6 @@ class TextExtractField(CharField): except Exception as err: print(str(err)) self.print_error(arquivo, err) - - # Em ambiente de DEV utiliza-se o Whoosh - # Como ele não possui extração, faz-se uso do textract - else: - try: - self.logger.debug("Tentando whoosh_extraction no arquivo {}".format(arquivo.path)) - return self.whoosh_extraction(arquivo) - self.print_error(arquivo) - except ExtensionNotSupported as err: - print(str(err)) - self.logger.error(str(err)) - except Exception as err: - print(str(err)) - self.print_error(arquivo, str(err)) return '' def ta_extractor(self, value): diff --git a/sapl/base/templatetags/common_tags.py b/sapl/base/templatetags/common_tags.py index 3c72b1654..26fd54f4d 100644 --- a/sapl/base/templatetags/common_tags.py +++ b/sapl/base/templatetags/common_tags.py @@ -1,3 +1,6 @@ +from _functools import reduce +import re + from django import template from django.template.defaultfilters import stringfilter from django.utils.safestring import mark_safe @@ -9,7 +12,6 @@ from sapl.norma.models import NormaJuridica from sapl.parlamentares.models import Filiacao from sapl.utils import filiacao_data, SEPARADOR_HASH_PROPOSICAO - register = template.Library() @@ -286,3 +288,12 @@ def render_chunk_vendors(extension=None): return mark_safe('\n'.join(tags)) except: return '' + + +@register.filter(is_safe=True) +@stringfilter +def dont_break_out(value): + _safe = '
{}
'.format(value) + _safe = mark_safe(_safe) + return _safe + diff --git a/sapl/base/tests/test_login.py b/sapl/base/tests/test_login.py index 7fe7672e5..6c6a75cb8 100755 --- a/sapl/base/tests/test_login.py +++ b/sapl/base/tests/test_login.py @@ -13,7 +13,7 @@ def user(): def test_login_aparece_na_barra_para_usuario_nao_logado(client): response = client.get('/') - assert '' in str( + assert '' in str( response.content) diff --git a/sapl/base/urls.py b/sapl/base/urls.py index ae4add258..5d12b3586 100644 --- a/sapl/base/urls.py +++ b/sapl/base/urls.py @@ -8,31 +8,43 @@ from django.contrib.auth.views import (password_reset, password_reset_complete, password_reset_done) from django.views.generic.base import RedirectView, TemplateView -from sapl.base.views import AutorCrud, ConfirmarEmailView, TipoAutorCrud +from sapl.base.views import AutorCrud, ConfirmarEmailView, TipoAutorCrud, get_estatistica from sapl.settings import EMAIL_SEND_USER, MEDIA_URL from .apps import AppConfig from .forms import LoginForm, NovaSenhaForm, RecuperarSenhaForm from .views import (AlterarSenha, AppConfigCrud, CasaLegislativaCrud, CreateUsuarioView, DeleteUsuarioView, EditUsuarioView, - HelpTopicView, ListarUsuarioView, LogotipoView, - RelatorioAtasView, RelatorioAudienciaView, + HelpTopicView, PesquisarUsuarioView, LogotipoView, + RelatorioAtasView, RelatorioAudienciaView, RelatorioDataFimPrazoTramitacaoView, RelatorioHistoricoTramitacaoView, RelatorioMateriasPorAnoAutorTipoView, RelatorioMateriasPorAutorView, RelatorioMateriasTramitacaoView, - RelatorioPresencaSessaoView, + RelatorioPresencaSessaoView, RelatorioReuniaoView, SaplSearchView, RelatorioNormasPublicadasMesView, RelatorioNormasVigenciaView, EstatisticasAcessoNormas, - RelatoriosListView) + RelatoriosListView, + ListarInconsistenciasView, ListarProtocolosDuplicadosView, + ListarProtocolosComMateriasView, + ListarMatProtocoloInexistenteView, + ListarParlamentaresDuplicadosView, + ListarFiliacoesSemDataFiliacaoView, + ListarMandatoSemDataInicioView, + ListarParlMandatosIntersecaoView, + ListarParlFiliacoesIntersecaoView, + ListarAutoresDuplicadosView, + ListarBancadaComissaoAutorExternoView, + ListarLegislaturaInfindavelView) + app_name = AppConfig.name admin_user = [ - url(r'^sistema/usuario/$', ListarUsuarioView.as_view(), name='user_list'), + url(r'^sistema/usuario/$', PesquisarUsuarioView.as_view(), name='usuario'), url(r'^sistema/usuario/create$', CreateUsuarioView.as_view(), name='user_create'), url(r'^sistema/usuario/(?P\d+)/edit$', EditUsuarioView.as_view(), name='user_edit'), url(r'^sistema/usuario/(?P\d+)/delete$', DeleteUsuarioView.as_view(), name='user_delete') @@ -127,6 +139,44 @@ urlpatterns = [ '(?P[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})$', ConfirmarEmailView.as_view(), name='confirmar_email'), + url(r'^sistema/inconsistencias/$', + ListarInconsistenciasView.as_view(), + name='lista_inconsistencias'), + url(r'^sistema/inconsistencias/protocolos_duplicados$', + ListarProtocolosDuplicadosView.as_view(), + name='lista_protocolos_duplicados'), + url(r'^sistema/inconsistencias/protocolos_com_materias$', + ListarProtocolosComMateriasView.as_view(), + name='lista_protocolos_com_materias'), + url(r'^sistema/inconsistencias/materias_protocolo_inexistente$', + ListarMatProtocoloInexistenteView.as_view(), + name='lista_materias_protocolo_inexistente'), + url(r'^sistema/inconsistencias/filiacoes_sem_data_filiacao$', + ListarFiliacoesSemDataFiliacaoView.as_view(), + name='lista_filiacoes_sem_data_filiacao'), + url(r'^sistema/inconsistencias/mandato_sem_data_inicio', + ListarMandatoSemDataInicioView.as_view(), + name='lista_mandato_sem_data_inicio'), + url(r'^sistema/inconsistencias/parlamentares_duplicados$', + ListarParlamentaresDuplicadosView.as_view(), + name='lista_parlamentares_duplicados'), + url(r'^sistema/inconsistencias/parlamentares_mandatos_intersecao$', + ListarParlMandatosIntersecaoView.as_view(), + name='lista_parlamentares_mandatos_intersecao'), + url(r'^sistema/inconsistencias/parlamentares_filiacoes_intersecao$', + ListarParlFiliacoesIntersecaoView.as_view(), + name='lista_parlamentares_filiacoes_intersecao'), + url(r'^sistema/inconsistencias/autores_duplicados$', + ListarAutoresDuplicadosView.as_view(), + name='lista_autores_duplicados'), + url(r'^sistema/inconsistencias/bancada_comissao_autor_externo$', + ListarBancadaComissaoAutorExternoView.as_view(), + name='lista_bancada_comissao_autor_externo'), + url(r'^sistema/inconsistencias/legislatura_infindavel$', + ListarLegislaturaInfindavelView.as_view(), + name='lista_legislatura_infindavel'), + + url(r'^sistema/estatisticas', get_estatistica), # todos os sublinks de sistema devem vir acima deste url(r'^sistema/$', permission_required('base.view_tabelas_auxiliares') diff --git a/sapl/base/views.py b/sapl/base/views.py index 278ee4f61..3226b4926 100644 --- a/sapl/base/views.py +++ b/sapl/base/views.py @@ -1,18 +1,20 @@ import collections +import itertools import datetime import logging import os +from django.contrib import messages from django.contrib.auth import get_user_model from django.contrib.auth.mixins import PermissionRequiredMixin -from django.contrib.auth.models import Group +from django.contrib.auth.models import Group, User from django.contrib.auth.tokens import default_token_generator -from django.core.exceptions import ObjectDoesNotExist, PermissionDenied +from django.core.exceptions import ObjectDoesNotExist, PermissionDenied, ValidationError from django.core.mail import send_mail -from django.core.urlresolvers import reverse +from django.core.urlresolvers import reverse, reverse_lazy from django.db import connection -from django.db.models import Count, Q -from django.http import Http404, HttpResponseRedirect +from django.db.models import Count, Q, ProtectedError +from django.http import Http404, HttpResponseRedirect, JsonResponse from django.template import TemplateDoesNotExist from django.template.loader import get_template from django.utils import timezone @@ -35,10 +37,13 @@ from sapl.crud.base import CrudAux, make_pagination from sapl.materia.models import (Autoria, MateriaLegislativa, Proposicao, TipoMateriaLegislativa, StatusTramitacao, UnidadeTramitacao) from sapl.norma.models import (NormaJuridica, NormaEstatisticas) +from sapl.parlamentares.models import Parlamentar, Legislatura, Mandato, Filiacao +from sapl.protocoloadm.models import Protocolo from sapl.sessao.models import (PresencaOrdemDia, SessaoPlenaria, - SessaoPlenariaPresenca) + SessaoPlenariaPresenca, Bancada) from sapl.utils import (parlamentares_ativos, gerar_hash_arquivo, SEPARADOR_HASH_PROPOSICAO, - show_results_filter_set, mail_service_configured) + show_results_filter_set, mail_service_configured, + intervalos_tem_intersecao,) from .forms import (AlterarSenhaForm, CasaLegislativaForm, ConfiguracoesAppForm, RelatorioAtasFilterSet, @@ -52,7 +57,7 @@ from .forms import (AlterarSenhaForm, CasaLegislativaForm, RelatorioReuniaoFilterSet, UsuarioCreateForm, UsuarioEditForm, RelatorioNormasMesFilterSet, RelatorioNormasVigenciaFilterSet, - EstatisticasAcessoNormasForm) + EstatisticasAcessoNormasForm, UsuarioFilterSet) from .models import AppConfig, CasaLegislativa @@ -606,12 +611,13 @@ class RelatorioMateriasTramitacaoView(FilterView): qs = filtra_url_materias_em_tramitacao( qr, qs, 'tramitacao__status', 'status') - context['object_list'] = qs + li = [li1 for li1 in qs if li1.tramitacao_set.last() and li1.tramitacao_set.last().status.indicador != 'F'] + context['object_list'] = li qtdes = {} for tipo in TipoMateriaLegislativa.objects.all(): - qs = context['object_list'] - qtde = len(qs.filter(tipo_id=tipo.id)) + li = context['object_list'] + qtde = sum(1 for i in li if i.tipo_id==tipo.id) if qtde > 0: qtdes[tipo] = qtde context['qtdes'] = qtdes @@ -918,42 +924,575 @@ class EstatisticasAcessoNormas(TemplateView): return self.render_to_response(context) -class ListarUsuarioView(PermissionRequiredMixin, ListView): +class ListarInconsistenciasView(PermissionRequiredMixin, ListView): + model = get_user_model() + template_name = 'base/lista_inconsistencias.html' + context_object_name = 'tabela_inconsistencias' + permission_required = ('base.list_appconfig',) + + def get_queryset(self): + tabela = [] + tabela.append( + ('protocolos_duplicados', + 'Protocolos duplicados', + len(protocolos_duplicados()) + ) + ) + tabela.append( + ('protocolos_com_materias', + 'Protocolos que excedem o limite de matérias vinculadas', + len(protocolos_com_materias()) + ) + ) + tabela.append( + ('materias_protocolo_inexistente', + 'Matérias Legislativas com protocolo inexistente', + len(materias_protocolo_inexistente()) + ) + ) + tabela.append( + ('filiacoes_sem_data_filiacao', + 'Filiações sem data filiação', + len(filiacoes_sem_data_filiacao()) + ) + ) + tabela.append( + ('mandato_sem_data_inicio', + 'Mandatos sem data inicial', + len(mandato_sem_data_inicio()) + ) + ) + tabela.append( + ('parlamentares_duplicados', + 'Parlamentares duplicados', + len(parlamentares_duplicados()) + ) + ) + tabela.append( + ('parlamentares_mandatos_intersecao', + 'Parlamentares com mandatos em interseção', + len(parlamentares_mandatos_intersecao()) + ) + ) + tabela.append( + ('parlamentares_filiacoes_intersecao', + 'Parlamentares com filiações em interseção', + len(parlamentares_filiacoes_intersecao()) + ) + ) + tabela.append( + ('autores_duplicados', + 'Autores duplicados', + len(autores_duplicados()) + ) + ) + tabela.append( + ('bancada_comissao_autor_externo', + 'Bancadas e Comissões com autor externo', + len(bancada_comissao_autor_externo()) + ) + ) + tabela.append( + ('legislatura_infindavel', + 'Legislaturas sem data fim', + len(legislatura_infindavel()) + ) + ) + + return tabela + + +def legislatura_infindavel(): + return Legislatura.objects.filter(data_fim__isnull=True).order_by('-numero') + + +class ListarLegislaturaInfindavelView(PermissionRequiredMixin, ListView): + model = get_user_model() + template_name = 'base/legislatura_infindavel.html' + context_object_name = 'legislatura_infindavel' + permission_required = ('base.list_appconfig',) + paginate_by = 10 + + def get_queryset(self): + return legislatura_infindavel() + + def get_context_data(self, **kwargs): + context = super( + ListarLegislaturaInfindavelView, self + ).get_context_data(**kwargs) + paginator = context['paginator'] + page_obj = context['page_obj'] + context['page_range'] = make_pagination( + page_obj.number, paginator.num_pages) + context[ + 'NO_ENTRIES_MSG' + ] = 'Nenhuma encontrada.' + return context + + +def bancada_comissao_autor_externo(): + tipo_autor_externo = TipoAutor.objects.filter(descricao='Externo') + + lista_bancada_autor_externo = [] + for bancada in Bancada.objects.all().order_by('nome'): + autor_externo = bancada.autor.filter(tipo=tipo_autor_externo) + + if autor_externo: + q_autor_externo = bancada.autor.get(tipo=tipo_autor_externo) + lista_bancada_autor_externo.append( + (q_autor_externo, bancada, 'Bancada', 'sistema/bancada') + ) + + lista_comissao_autor_externo = [] + for comissao in Comissao.objects.all().order_by('nome'): + autor_externo = comissao.autor.filter(tipo=tipo_autor_externo) + + if autor_externo: + q_autor_externo = comissao.autor.get(tipo=tipo_autor_externo) + lista_comissao_autor_externo.append( + (q_autor_externo, comissao, 'Comissão', 'comissao') + ) + + return lista_bancada_autor_externo + lista_comissao_autor_externo + + +class ListarBancadaComissaoAutorExternoView(PermissionRequiredMixin, ListView): + model = get_user_model() + template_name = 'base/bancada_comissao_autor_externo.html' + context_object_name = 'bancada_comissao_autor_externo' + permission_required = ('base.list_appconfig',) + paginate_by = 10 + + def get_queryset(self): + return bancada_comissao_autor_externo() + + def get_context_data(self, **kwargs): + context = super( + ListarBancadaComissaoAutorExternoView, self + ).get_context_data(**kwargs) + paginator = context['paginator'] + page_obj = context['page_obj'] + context['page_range'] = make_pagination( + page_obj.number, paginator.num_pages) + context[ + 'NO_ENTRIES_MSG' + ] = 'Nenhuma encontrada.' + return context + + +def autores_duplicados(): + return [autor for autor in Autor.objects.values('nome').annotate(count=Count('nome')).filter(count__gt=1)] + + +class ListarAutoresDuplicadosView(PermissionRequiredMixin, ListView): + model = get_user_model() + template_name = 'base/autores_duplicados.html' + context_object_name = 'autores_duplicados' + permission_required = ('base.list_appconfig',) + paginate_by = 10 + + def get_queryset(self): + return autores_duplicados() + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + paginator = context['paginator'] + page_obj = context['page_obj'] + context['page_range'] = make_pagination( + page_obj.number, paginator.num_pages) + context[ + 'NO_ENTRIES_MSG' + ] = 'Nenhum encontrado.' + return context + + +def parlamentares_filiacoes_intersecao(): + intersecoes = [] + + for parlamentar in Parlamentar.objects.all().order_by('nome_parlamentar'): + filiacoes = parlamentar.filiacao_set.all() + combinacoes = itertools.combinations(filiacoes, 2) + + for c in combinacoes: + data_filiacao1 = c[0].data + data_desfiliacao1 = c[0].data_desfiliacao if c[0].data_desfiliacao else timezone.now().date() + + data_filiacao2 = c[1].data + data_desfiliacao2 = c[1].data_desfiliacao if c[1].data_desfiliacao else timezone.now().date() + + if data_filiacao1 and data_filiacao2: + exists = intervalos_tem_intersecao( + data_filiacao1, data_desfiliacao1, + data_filiacao2, data_desfiliacao2) + if exists: + intersecoes.append((parlamentar, c[0], c[1])) + return intersecoes + + +class ListarParlFiliacoesIntersecaoView(PermissionRequiredMixin, ListView): + model = get_user_model() + template_name = 'base/parlamentares_filiacoes_intersecao.html' + context_object_name = 'parlamentares_filiacoes_intersecao' + permission_required = ('base.list_appconfig',) + paginate_by = 10 + + def get_queryset(self): + return parlamentares_filiacoes_intersecao() + + def get_context_data(self, **kwargs): + context = super( + ListarParlFiliacoesIntersecaoView, self).get_context_data(**kwargs) + paginator = context['paginator'] + page_obj = context['page_obj'] + context['page_range'] = make_pagination( + page_obj.number, paginator.num_pages) + context[ + 'NO_ENTRIES_MSG' + ] = 'Nenhum encontrado.' + return context + + +def parlamentares_mandatos_intersecao(): + intersecoes = [] + + for parlamentar in Parlamentar.objects.all().order_by('nome_parlamentar'): + mandatos = parlamentar.mandato_set.all() + combinacoes = itertools.combinations(mandatos, 2) + + for c in combinacoes: + data_inicio_mandato1 = c[0].data_inicio_mandato + data_fim_mandato1 = c[0].data_fim_mandato if c[0].data_fim_mandato else timezone.now().date() + + data_inicio_mandato2 = c[1].data_inicio_mandato + data_fim_mandato2 = c[1].data_fim_mandato if c[1].data_fim_mandato else timezone.now().date() + + if data_inicio_mandato1 and data_inicio_mandato2: + exists = intervalos_tem_intersecao( + data_inicio_mandato1, data_fim_mandato1, + data_inicio_mandato2, data_fim_mandato2) + if exists: + intersecoes.append((parlamentar, c[0], c[1])) + + return intersecoes + + +class ListarParlMandatosIntersecaoView(PermissionRequiredMixin, ListView): + model = get_user_model() + template_name = 'base/parlamentares_mandatos_intersecao.html' + context_object_name = 'parlamentares_mandatos_intersecao' + permission_required = ('base.list_appconfig',) + paginate_by = 10 + + def get_queryset(self): + return parlamentares_mandatos_intersecao() + + def get_context_data(self, **kwargs): + context = super( + ListarParlMandatosIntersecaoView, self).get_context_data(**kwargs) + paginator = context['paginator'] + page_obj = context['page_obj'] + context['page_range'] = make_pagination( + page_obj.number, paginator.num_pages) + context[ + 'NO_ENTRIES_MSG' + ] = 'Nenhum encontrado.' + return context + + +def parlamentares_duplicados(): + return [parlamentar.values() for parlamentar in Parlamentar.objects.values( + 'nome_parlamentar').order_by('nome_parlamentar').annotate(count=Count( + 'nome_parlamentar')).filter(count__gt=1)] + + +class ListarParlamentaresDuplicadosView(PermissionRequiredMixin, ListView): + model = get_user_model() + template_name = 'base/parlamentares_duplicados.html' + context_object_name = 'parlamentares_duplicados' + permission_required = ('base.list_appconfig',) + paginate_by = 10 + + def get_queryset(self): + return parlamentares_duplicados() + + def get_context_data(self, **kwargs): + context = super( + ListarParlamentaresDuplicadosView, self).get_context_data(**kwargs) + paginator = context['paginator'] + page_obj = context['page_obj'] + context['page_range'] = make_pagination( + page_obj.number, paginator.num_pages) + context[ + 'NO_ENTRIES_MSG' + ] = 'Nenhum encontrado.' + return context + + +def mandato_sem_data_inicio(): + return Mandato.objects.filter(data_inicio_mandato__isnull=True).order_by('parlamentar') + + +def get_estatistica(request): + + json_dict = {} + + datas = [MateriaLegislativa.objects.all(). + order_by('-data_ultima_atualizacao'). + values_list('data_ultima_atualizacao', flat=True). + first(), + NormaJuridica.objects.all(). + order_by('-data_ultima_atualizacao'). + values_list('data_ultima_atualizacao', flat=True). + first()] # Retorna [None, None] se inexistem registros + + max_data = '' + + if datas[0] and datas[1]: + max_data = max(datas) + else: + max_data = next(iter([i for i in datas if i is not None]), '') + + json_dict["data_ultima_atualizacao"] = max_data + json_dict["num_materias_legislativas"] = MateriaLegislativa.objects.all().count() + json_dict["num_normas_juridicas "] = NormaJuridica.objects.all().count() + json_dict["num_parlamentares"] = Parlamentar.objects.all().count() + json_dict["num_sessoes_plenarias"] = SessaoPlenaria.objects.all().count() + + return JsonResponse(json_dict) + + +class ListarMandatoSemDataInicioView(PermissionRequiredMixin, ListView): model = get_user_model() - template_name = 'auth/user_list.html' - context_object_name = 'user_list' + template_name = 'base/mandato_sem_data_inicio.html' + context_object_name = 'mandato_sem_data_inicio' permission_required = ('base.list_appconfig',) paginate_by = 10 def get_queryset(self): - qs = super(ListarUsuarioView, self).get_queryset() - return qs.order_by('username') + return mandato_sem_data_inicio() def get_context_data(self, **kwargs): - context = super(ListarUsuarioView, self).get_context_data(**kwargs) + context = super( + ListarMandatoSemDataInicioView, self + ).get_context_data(**kwargs) paginator = context['paginator'] page_obj = context['page_obj'] context['page_range'] = make_pagination( page_obj.number, paginator.num_pages) - context['NO_ENTRIES_MSG'] = 'Nenhum usuário cadastrado.' + context[ + 'NO_ENTRIES_MSG' + ] = 'Nenhum encontrado.' return context +def filiacoes_sem_data_filiacao(): + return Filiacao.objects.filter(data__isnull=True).order_by('parlamentar') + + +class ListarFiliacoesSemDataFiliacaoView(PermissionRequiredMixin, ListView): + model = get_user_model() + template_name = 'base/filiacoes_sem_data_filiacao.html' + context_object_name = 'filiacoes_sem_data_filiacao' + permission_required = ('base.list_appconfig',) + paginate_by = 10 + + def get_queryset(self): + return filiacoes_sem_data_filiacao() + + def get_context_data(self, **kwargs): + context = super( + ListarFiliacoesSemDataFiliacaoView, self + ).get_context_data(**kwargs) + paginator = context['paginator'] + page_obj = context['page_obj'] + context['page_range'] = make_pagination( + page_obj.number, paginator.num_pages) + context[ + 'NO_ENTRIES_MSG' + ] = 'Nenhuma encontrada.' + return context + + +def materias_protocolo_inexistente(): + materias = [] + for materia in MateriaLegislativa.objects.filter(numero_protocolo__isnull=False).order_by('-ano', 'numero'): + exists = Protocolo.objects.filter( + ano=materia.ano, numero=materia.numero_protocolo).exists() + if not exists: + materias.append( + (materia, materia.ano, materia.numero_protocolo)) + return materias + + +class ListarMatProtocoloInexistenteView(PermissionRequiredMixin, ListView): + model = get_user_model() + template_name = 'base/materias_protocolo_inexistente.html' + context_object_name = 'materias_protocolo_inexistente' + permission_required = ('base.list_appconfig',) + paginate_by = 10 + + def get_queryset(self): + return materias_protocolo_inexistente() + + def get_context_data(self, **kwargs): + context = super( + ListarMatProtocoloInexistenteView, self + ).get_context_data(**kwargs) + paginator = context['paginator'] + page_obj = context['page_obj'] + context['page_range'] = make_pagination( + page_obj.number, paginator.num_pages) + context[ + 'NO_ENTRIES_MSG' + ] = 'Nenhuma encontrada.' + return context + + +def protocolos_com_materias(): + protocolos = {} + + for m in MateriaLegislativa.objects.filter(numero_protocolo__isnull=False).order_by('-ano', 'numero_protocolo'): + if Protocolo.objects.filter(numero=m.numero_protocolo, ano=m.ano).exists(): + key = "{}/{}".format(m.numero_protocolo, m.ano) + val = protocolos.get(key, list()) + val.append(m) + protocolos[key] = val + + return [(v[0], len(v)) for (k, v) in protocolos.items() if len(v) > 1] + + +class ListarProtocolosComMateriasView(PermissionRequiredMixin, ListView): + model = get_user_model() + template_name = 'base/protocolos_com_materias.html' + context_object_name = 'protocolos_com_materias' + permission_required = ('base.list_appconfig',) + paginate_by = 10 + + def get_queryset(self): + return protocolos_com_materias() + + def get_context_data(self, **kwargs): + context = super( + ListarProtocolosComMateriasView, self).get_context_data(**kwargs) + paginator = context['paginator'] + page_obj = context['page_obj'] + context['page_range'] = make_pagination( + page_obj.number, paginator.num_pages) + context[ + 'NO_ENTRIES_MSG' + ] = 'Nenhum encontrado.' + return context + + +def protocolos_duplicados(): + protocolos = {} + for p in Protocolo.objects.order_by('-ano', 'numero'): + key = "{}/{}".format(p.numero, p.ano) + val = protocolos.get(key, list()) + val.append(p) + protocolos[key] = val + + return [(v[0], len(v)) for (k, v) in protocolos.items() if len(v) > 1] + + +class ListarProtocolosDuplicadosView(PermissionRequiredMixin, ListView): + model = get_user_model() + template_name = 'base/protocolos_duplicados.html' + context_object_name = 'protocolos_duplicados' + permission_required = ('base.list_appconfig',) + paginate_by = 10 + + def get_queryset(self): + return protocolos_duplicados() + + def get_context_data(self, **kwargs): + context = super( + ListarProtocolosDuplicadosView, self).get_context_data(**kwargs) + paginator = context['paginator'] + page_obj = context['page_obj'] + context['page_range'] = make_pagination( + page_obj.number, paginator.num_pages) + context[ + 'NO_ENTRIES_MSG' + ] = 'Nenhum encontrado.' + return context + + +class PesquisarUsuarioView(PermissionRequiredMixin, FilterView): + model = User + filterset_class = UsuarioFilterSet + permission_required = ('base.list_appconfig',) + paginate_by = 10 + + def get_filterset_kwargs(self, filterset_class): + super(PesquisarUsuarioView, + self).get_filterset_kwargs(filterset_class) + + kwargs = {'data': self.request.GET or None} + + qs = self.get_queryset().order_by('username').distinct() + + kwargs.update({ + 'queryset': qs, + }) + return kwargs + + def get_context_data(self, **kwargs): + context = super(PesquisarUsuarioView, + self).get_context_data(**kwargs) + + paginator = context['paginator'] + page_obj = context['page_obj'] + + context['page_range'] = make_pagination( + page_obj.number, paginator.num_pages) + + context['NO_ENTRIES_MSG'] = 'Nenhum usuário encontrado!' + + context['title'] = _('Usuários') + + return context + + def get(self, request, *args, **kwargs): + super(PesquisarUsuarioView, self).get(request) + + data = self.filterset.data + url = '' + if data: + url = "&" + str(self.request.META['QUERY_STRING']) + if url.startswith("&page"): + ponto_comeco = url.find('username=') - 1 + url = url[ponto_comeco:] + + context = self.get_context_data(filter=self.filterset, + object_list=self.object_list, + filter_url=url, + numero_res=len(self.object_list) + ) + + context['show_results'] = show_results_filter_set( + self.request.GET.copy()) + + return self.render_to_response(context) + + class CreateUsuarioView(PermissionRequiredMixin, CreateView): model = get_user_model() form_class = UsuarioCreateForm - success_message = 'Usuário criado com sucesso' + success_message = 'Usuário criado com sucesso!' + fail_message = 'Usuário não criado!' permission_required = ('base.add_appconfig',) def get_success_url(self): - return reverse('sapl.base:user_list') + return reverse('sapl.base:usuario') def form_valid(self, form): - data = form.cleaned_data new_user = get_user_model().objects.create( - username=data['username'], email=data['email']) + username=data['username'], + email=data['email'] + ) new_user.first_name = data['firstname'] new_user.last_name = data['lastname'] new_user.set_password(data['password1']) @@ -965,33 +1504,53 @@ class CreateUsuarioView(PermissionRequiredMixin, CreateView): for g in groups: g.user_set.add(new_user) + messages.success(self.request, self.success_message) return HttpResponseRedirect(self.get_success_url()) + def form_invalid(self, form): + messages.error(self.request, self.fail_message) + return super().form_invalid(form) -class DeleteUsuarioView(PermissionRequiredMixin, DeleteView): +class DeleteUsuarioView(PermissionRequiredMixin, DeleteView): model = get_user_model() + template_name = "crud/confirm_delete.html" permission_required = ('base.delete_appconfig',) + success_url = reverse_lazy('sapl.base:usuario') + success_message = "Usuário removido com sucesso!" - def get_success_url(self): - return reverse('sapl.base:user_list') + def delete(self, request, *args, **kwargs): + try: + super(DeleteUsuarioView, self).delete(request, *args, **kwargs) + except ProtectedError as exception: + error_url = reverse_lazy('sapl.base:user_delete', kwargs={'pk': self.kwargs['pk']}) + error_message = "O usuário não pode ser removido, pois é referenciado por:
    " - def get(self, request, *args, **kwargs): - return self.post(request, *args, **kwargs) + for e in exception.protected_objects: + error_message += '
  • {} - {}
  • '.format( + e._meta.verbose_name, e + ) + error_message += '
' + messages.error(self.request, error_message) + return HttpResponseRedirect(error_url) - def get_queryset(self): - qs = super(DeleteUsuarioView, self).get_queryset() - return qs.filter(id=self.kwargs['pk']) + messages.success(self.request, self.success_message) + return HttpResponseRedirect(self.success_url) + + @property + def cancel_url(self): + return reverse('sapl.base:user_edit', + kwargs={'pk': self.kwargs['pk']}) class EditUsuarioView(PermissionRequiredMixin, UpdateView): model = get_user_model() form_class = UsuarioEditForm - success_message = 'Usuário editado com sucesso' + success_message = 'Usuário editado com sucesso!' permission_required = ('base.change_appconfig',) def get_success_url(self): - return reverse('sapl.base:user_list') + return reverse('sapl.base:usuario') def get_initial(self): initial = super(EditUsuarioView, self).get_initial() @@ -1028,6 +1587,7 @@ class EditUsuarioView(PermissionRequiredMixin, UpdateView): for g in groups: g.user_set.add(user) + messages.success(self.request, self.success_message) return super(EditUsuarioView, self).form_valid(form) @@ -1096,8 +1656,11 @@ class AppConfigCrud(CrudAux): def gerar_hash(self, inst): inst.save() if inst.texto_original: - inst.hash_code = gerar_hash_arquivo( - inst.texto_original.path, str(inst.pk)) + try: + inst.hash_code = gerar_hash_arquivo( + inst.texto_original.path, str(inst.pk)) + except IOError: + raise ValidationError("Existem proposicoes com arquivos inexistentes.") elif inst.texto_articulado.exists(): ta = inst.texto_articulado.first() inst.hash_code = 'P' + ta.hash() + SEPARADOR_HASH_PROPOSICAO + str(inst.pk) diff --git a/sapl/comissoes/forms.py b/sapl/comissoes/forms.py index cef967102..9b0e69d72 100644 --- a/sapl/comissoes/forms.py +++ b/sapl/comissoes/forms.py @@ -12,6 +12,7 @@ from sapl.base.models import Autor, TipoAutor from sapl.comissoes.models import (Comissao, Composicao, DocumentoAcessorio, Participacao, Reuniao, Periodo) from sapl.parlamentares.models import Legislatura, Mandato, Parlamentar +from sapl.utils import FileFieldCheckMixin class ComposicaoForm(forms.ModelForm): @@ -382,7 +383,7 @@ class ReuniaoForm(ModelForm): return self.cleaned_data -class DocumentoAcessorioCreateForm(forms.ModelForm): +class DocumentoAcessorioCreateForm(FileFieldCheckMixin, forms.ModelForm): parent_pk = forms.CharField(required=False) # widget=forms.HiddenInput()) @@ -404,7 +405,7 @@ class DocumentoAcessorioCreateForm(forms.ModelForm): reuniao = Reuniao.objects.get(id=self.initial['parent_pk']) -class DocumentoAcessorioEditForm(forms.ModelForm): +class DocumentoAcessorioEditForm(FileFieldCheckMixin, forms.ModelForm): parent_pk = forms.CharField(required=False) # widget=forms.HiddenInput()) diff --git a/sapl/comissoes/urls.py b/sapl/comissoes/urls.py index 72886f1f1..f22f32e1d 100644 --- a/sapl/comissoes/urls.py +++ b/sapl/comissoes/urls.py @@ -1,7 +1,7 @@ from django.conf.urls import include, url from sapl.comissoes.views import (CargoCrud, ComissaoCrud, ComposicaoCrud, DocumentoAcessorioCrud, MateriasTramitacaoListView, ParticipacaoCrud, - PeriodoComposicaoCrud, ReuniaoCrud, TipoComissaoCrud) + PeriodoComposicaoCrud, ReuniaoCrud, TipoComissaoCrud, get_participacoes_comissao) from .apps import AppConfig @@ -21,4 +21,5 @@ urlpatterns = [ url(r'^sistema/comissao/periodo-composicao/', include(PeriodoComposicaoCrud.get_urls())), url(r'^sistema/comissao/tipo/', include(TipoComissaoCrud.get_urls())), + url(r'^sistema/comissao/recupera-participacoes', get_participacoes_comissao), ] diff --git a/sapl/comissoes/views.py b/sapl/comissoes/views.py index 743eaa58f..1db8d23df 100644 --- a/sapl/comissoes/views.py +++ b/sapl/comissoes/views.py @@ -2,7 +2,7 @@ import logging from django.core.urlresolvers import reverse from django.db.models import F -from django.http.response import HttpResponseRedirect +from django.http.response import HttpResponseRedirect, JsonResponse from django.views.decorators.clickjacking import xframe_options_exempt from django.views.generic import ListView from django.views.generic.base import RedirectView @@ -108,7 +108,7 @@ class ComposicaoCrud(MasterDetailCrud): paginate_by = None def take_composicao_pk(self): - + username = self.request.user.username try: self.logger.debug('user=' + username + '. Tentando obter pk da composição.') @@ -186,6 +186,7 @@ class MateriasTramitacaoListView(ListView): context = super( MateriasTramitacaoListView, self).get_context_data(**kwargs) context['object'] = Comissao.objects.get(id=self.kwargs['pk']) + context['qtde'] = self.object_list.count() return context @@ -196,7 +197,8 @@ class ReuniaoCrud(MasterDetailCrud): public = [RP_LIST, RP_DETAIL, ] class BaseMixin(MasterDetailCrud.BaseMixin): - list_field_names = ['data', 'nome', 'tema'] + list_field_names = ['data', 'nome', 'tema', 'upload_ata'] + ordering = '-data' class ListView(MasterDetailCrud.ListView): logger = logging.getLogger(__name__) @@ -277,3 +279,15 @@ class DocumentoAcessorioCrud(MasterDetailCrud): return HttpResponseRedirect( reverse('sapl.comissoes:reuniao_detail', kwargs={'pk': obj.reuniao.pk})) + + +def get_participacoes_comissao(request): + parlamentares = [] + + composicao_id = request.GET.get('composicao_id') + if composicao_id: + parlamentares = [{'nome': p.parlamentar.nome_parlamentar, 'id': p.parlamentar.id} for p in + Participacao.objects.filter(composicao_id=composicao_id).order_by( + 'parlamentar__nome_parlamentar')] + + return JsonResponse(parlamentares, safe=False) diff --git a/sapl/compilacao/forms.py b/sapl/compilacao/forms.py index 0e4f933d7..e82d58f63 100644 --- a/sapl/compilacao/forms.py +++ b/sapl/compilacao/forms.py @@ -3,7 +3,6 @@ from datetime import timedelta from crispy_forms.bootstrap import (Alert, FieldWithButtons, FormActions, InlineCheckboxes, InlineRadios, StrictButton) -from crispy_forms.helper import FormHelper from crispy_forms.layout import (HTML, Button, Column, Div, Field, Fieldset, Layout, Row, Submit) from django import forms @@ -23,10 +22,12 @@ from sapl.compilacao.models import (NOTAS_PUBLICIDADE_CHOICES, TipoTextoArticulado, TipoVide, VeiculoPublicacao, Vide) from sapl.compilacao.utils import DISPOSITIVO_SELECT_RELATED +from sapl.crispy_layout_mixin import SaplFormHelper from sapl.crispy_layout_mixin import SaplFormLayout, to_column, to_row,\ form_actions from sapl.utils import YES_NO_CHOICES + error_messages = { 'required': _('Este campo é obrigatório'), 'invalid': _('URL inválida.') @@ -59,6 +60,13 @@ class TipoTaForm(ModelForm): widget=forms.RadioSelect(), required=True) + rodape_global = forms.CharField( + label=TipoTextoArticulado._meta.get_field( + 'rodape_global').verbose_name, + widget=forms.Textarea(attrs={'id': 'texto-rico'}), + required=False + ) + class Meta: model = TipoTextoArticulado fields = ['sigla', @@ -66,10 +74,12 @@ class TipoTaForm(ModelForm): 'content_type', 'participacao_social', 'publicacao_func', - 'perfis' + 'perfis', + 'rodape_global' ] - widgets = {'perfis': widgets.CheckboxSelectMultiple()} + widgets = {'perfis': widgets.CheckboxSelectMultiple(), + 'rodape_global': forms.Textarea} def __init__(self, *args, **kwargs): @@ -84,12 +94,18 @@ class TipoTaForm(ModelForm): ('perfis', 12), ]) - self.helper = FormHelper() + row3 = to_row([ + ('rodape_global', 12), + ]) + + self.helper = SaplFormHelper() self.helper.layout = SaplFormLayout( Fieldset(_('Identificação Básica'), row1, css_class="col-md-12"), Fieldset(_('Funcionalidades'), - row2, css_class="col-md-12")) + row2, css_class="col-md-12"), + Fieldset(_('Nota de Rodapé Global'), + row3, css_class="col-md-12")) super(TipoTaForm, self).__init__(*args, **kwargs) @@ -153,7 +169,7 @@ class TaForm(ModelForm): ('participacao_social', 3), ]) - self.helper = FormHelper() + self.helper = SaplFormHelper() self.helper.layout = SaplFormLayout( Fieldset(_('Identificação Básica'), row1, css_class="col-md-12"), Fieldset( @@ -204,7 +220,7 @@ class NotaForm(ModelForm): publicacao = forms.DateField( label=Nota._meta.get_field('publicacao').verbose_name, - input_formats=['%d/%m/%Y'], + input_formats=['%d/%m/%Y', '%d%m%Y'], required=True, widget=forms.DateInput( format='%d/%m/%Y'), @@ -212,7 +228,7 @@ class NotaForm(ModelForm): ) efetividade = forms.DateField( label=Nota._meta.get_field('efetividade').verbose_name, - input_formats=['%d/%m/%Y'], + input_formats=['%d/%m/%Y', '%d%m%Y'], required=True, widget=forms.DateInput( format='%d/%m/%Y'), @@ -268,7 +284,7 @@ class NotaForm(ModelForm): css_class='form-group row justify-content-between mr-1 ml-1' ) - self.helper = FormHelper() + self.helper = SaplFormHelper() self.helper.layout = Layout( Div( @@ -363,7 +379,7 @@ class VideForm(ModelForm): 'texto', placeholder=_('Texto Adicional ao Vide')), 12))))) - self.helper = FormHelper() + self.helper = SaplFormHelper() self.helper.layout = Layout( Div( Div(HTML(_('Vides')), css_class='card-header bg-light'), @@ -471,7 +487,7 @@ class PublicacaoForm(ModelForm): ('url_externa', 8), ]) - self.helper = FormHelper() + self.helper = SaplFormHelper() self.helper.layout = SaplFormLayout( Fieldset(Publicacao._meta.verbose_name, row1, row2, row3, css_class="col-md-12")) @@ -659,7 +675,7 @@ class DispositivoEdicaoBasicaForm(ModelForm): for f in fields: self.base_fields.update({f: getattr(self, f)}) - self.helper = FormHelper() + self.helper = SaplFormHelper() if not editor_type: cancel_label = _('Ir para o Editor Sequencial') @@ -790,7 +806,7 @@ class DispositivoSearchModalForm(Form): ) ) - self.helper = FormHelper() + self.helper = SaplFormHelper() self.helper.layout = Layout( fields_search, Row(to_column((Div(css_class='result-busca-dispositivo'), 12)))) @@ -903,7 +919,7 @@ class DispositivoEdicaoVigenciaForm(ModelForm): row_vigencia, css_class="col-md-12")) - self.helper = FormHelper() + self.helper = SaplFormHelper() self.helper.layout = SaplFormLayout( *layout, cancel_label=_('Ir para o Editor Sequencial')) @@ -1023,7 +1039,7 @@ class DispositivoDefinidorVigenciaForm(Form): row_vigencia, css_class="col-md-12")) - self.helper = FormHelper() + self.helper = SaplFormHelper() self.helper.layout = SaplFormLayout( *layout, cancel_label=_('Ir para o Editor Sequencial')) @@ -1162,7 +1178,7 @@ class DispositivoEdicaoAlteracaoForm(ModelForm): if hasattr(self, f): self.base_fields.update({f: getattr(self, f)}) - self.helper = FormHelper() + self.helper = SaplFormHelper() self.helper.layout = SaplFormLayout( *layout, cancel_label=_('Ir para o Editor Sequencial')) @@ -1328,7 +1344,7 @@ class TextNotificacoesForm(Form): (Submit('submit-form', _('Filtrar'), css_class='btn btn-primary float-right'), 2)]) - self.helper = FormHelper() + self.helper = SaplFormHelper() self.helper.layout = Layout(field_type_notificacoes) super(TextNotificacoesForm, self).__init__(*args, **kwargs) @@ -1374,7 +1390,7 @@ class DispositivoRegistroAlteracaoForm(Form): _fields = [Div(*layout, css_class="row")] + \ [to_row([(buttons, 12)])] - self.helper = FormHelper() + self.helper = SaplFormHelper() self.helper.layout = Layout(*_fields) super(DispositivoRegistroAlteracaoForm, self).__init__(*args, **kwargs) @@ -1431,7 +1447,7 @@ class DispositivoRegistroRevogacaoForm(Form): _fields = [Div(*layout, css_class="row")] + \ [to_row([(buttons, 12)])] - self.helper = FormHelper() + self.helper = SaplFormHelper() self.helper.layout = Layout(*_fields) super(DispositivoRegistroRevogacaoForm, self).__init__(*args, **kwargs) @@ -1481,7 +1497,7 @@ class DispositivoRegistroInclusaoForm(Form): _fields = [Div(*layout, css_class="row")] + \ [to_row([(buttons, 12)])] - self.helper = FormHelper() + self.helper = SaplFormHelper() self.helper.layout = Layout(*_fields) super(DispositivoRegistroInclusaoForm, self).__init__(*args, **kwargs) diff --git a/sapl/compilacao/migrations/0011_tipotextoarticulado_rodape_global.py b/sapl/compilacao/migrations/0011_tipotextoarticulado_rodape_global.py new file mode 100644 index 000000000..f3b0e323b --- /dev/null +++ b/sapl/compilacao/migrations/0011_tipotextoarticulado_rodape_global.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.20 on 2019-03-26 18:59 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('compilacao', '0010_auto_20181004_1939'), + ] + + operations = [ + migrations.AddField( + model_name='tipotextoarticulado', + name='rodape_global', + field=models.TextField(default='', help_text='A cada Tipo de Texto Articulado pode ser adicionado uma nota global de rodapé!', verbose_name='Rodapé Global'), + ), + ] diff --git a/sapl/compilacao/migrations/0012_bug_auto_inserido.py b/sapl/compilacao/migrations/0012_bug_auto_inserido.py new file mode 100644 index 000000000..ac07c0a44 --- /dev/null +++ b/sapl/compilacao/migrations/0012_bug_auto_inserido.py @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.13 on 2018-03-19 13:41 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +def adjust_bug_auto_inserido(apps, schema_editor): + Dispositivo = apps.get_model('compilacao', 'Dispositivo') + + Dispositivo.objects.filter( + tipo_dispositivo__class_css__startswith='caput', + dispositivo_pai__tipo_dispositivo__class_css__startswith='artigo', + auto_inserido=False + ).update(auto_inserido=True) + + +class Migration(migrations.Migration): + + dependencies = [ + ('compilacao', '0011_tipotextoarticulado_rodape_global'), + ] + + operations = [ + migrations.RunPython(adjust_bug_auto_inserido), + ] diff --git a/sapl/compilacao/models.py b/sapl/compilacao/models.py index f36e406f9..f64285aee 100644 --- a/sapl/compilacao/models.py +++ b/sapl/compilacao/models.py @@ -149,6 +149,13 @@ class TipoTextoArticulado(models.Model): em edição. """)) + rodape_global = models.TextField( + verbose_name=_('Rodapé Global'), + help_text=_('A cada Tipo de Texto Articulado pode ser adicionado ' + 'uma nota global de rodapé!'), + default='' + ) + class Meta: verbose_name = _('Tipo de Texto Articulado') verbose_name_plural = _('Tipos de Texto Articulados') diff --git a/sapl/compilacao/views.py b/sapl/compilacao/views.py index f09ed8a4f..f3f2294ee 100644 --- a/sapl/compilacao/views.py +++ b/sapl/compilacao/views.py @@ -1545,7 +1545,7 @@ class ActionDeleteDispositivoMixin(ActionsCommonsMixin): if not anterior: self.logger.error("user=" + username + ". Não é possível excluir este Dispositivo (id={}) sem" - " excluir toda a sua estrutura!!!".format(base.ta_id)) + " excluir toda a sua estrutura!!!".format(base.id)) raise Exception( _('Não é possível excluir este Dispositivo sem' ' excluir toda a sua estrutura!!!')) @@ -1566,8 +1566,8 @@ class ActionDeleteDispositivoMixin(ActionsCommonsMixin): for candidato in parents: if candidato == base: self.logger.error("user=" + username + ". Não é possível excluir este " - "Dispositivo ({}) sem " - "excluir toda a sua estrutura!!!".format(candidato)) + "Dispositivo (id={}) sem " + "excluir toda a sua estrutura!!!".format(candidato.id)) raise Exception( _('Não é possível excluir este ' 'Dispositivo sem ' @@ -1604,8 +1604,8 @@ class ActionDeleteDispositivoMixin(ActionsCommonsMixin): break else: self.logger.error("user=" + username + ". Não é possível excluir este " - "Dispositivo ({}) sem excluir toda " - "a sua estrutura!!!".format(candidato)) + "Dispositivo (id={}) sem excluir toda " + "a sua estrutura!!!".format(candidato.id)) raise Exception( _('Não é possível excluir este ' 'Dispositivo sem ' @@ -1643,6 +1643,7 @@ class ActionDeleteDispositivoMixin(ActionsCommonsMixin): # excluir e renumerar irmaos profundidade_base = base.get_profundidade() + auto_inserido_base = base.auto_inserido base.delete() for irmao in irmaos_posteriores: @@ -1666,6 +1667,11 @@ class ActionDeleteDispositivoMixin(ActionsCommonsMixin): i.set_numero_completo([0, 0, 0, 0, 0, 0, ]) i.rotulo = i.rotulo_padrao(local_insert=1) i.save() + + if not irmaos.exists() and \ + auto_inserido_base and \ + pai_base.nivel: + self.remover_dispositivo(pai_base, False) else: # Renumerar Dispostivos de Contagem Contínua # de dentro da base se pai @@ -2224,9 +2230,15 @@ class ActionDispositivoCreateMixin(ActionsCommonsMixin): dispositivo_pai=dp.dispositivo_pai).count() if qtd_existente >= pp[0].quantidade_permitida: - data = {'pk': base.pk, - 'pai': [base.dispositivo_pai.pk, ]} - self.set_message(data, 'warning', + data = {'pk': None + if base.dispositivo_pai else + base.pk, + 'pai': [ + base.dispositivo_pai.pk if + base.dispositivo_pai else + base.pk, + ]} + self.set_message(data, 'danger', _('Limite de inserções de ' 'dispositivos deste tipo ' 'foi excedido.'), time=6000) @@ -2512,7 +2524,7 @@ class ActionsEditMixin(ActionDragAndMoveDispositivoAlteradoMixin, local_add=local_add, create_auto_inserts=True) - if data: + if data and data['pk']: ndp = Dispositivo.objects.get(pk=data['pk']) @@ -2539,6 +2551,9 @@ class ActionsEditMixin(ActionDragAndMoveDispositivoAlteradoMixin, data.update({'pk': ndp.pk, 'pai': [bloco_alteracao.pk, ]}) + else: + data.update({'pk': bloco_alteracao.pk, + 'pai': [bloco_alteracao.pk, ]}) return data @@ -2933,6 +2948,7 @@ class DispositivoDinamicEditView( class DispositivoSearchFragmentFormView(ListView): template_name = 'compilacao/dispositivo_form_search_fragment.html' + logger = logging.getLogger(__name__) def get(self, request, *args, **kwargs): @@ -2955,14 +2971,20 @@ class DispositivoSearchFragmentFormView(ListView): messages.info( request, _('Não foram encontrados resultados ' 'com seus critérios de busca!')) + username = self.request.user.username + self.logger.error("user=" + username + ". Não foram encontrados " + "resultados com esses critérios de busca. " + "id_tipo_ta=".format(request.GET['tipo_ta'])) try: r = response.render() return response except Exception as e: - messages.error(request, "Erro - %s" % e) + messages.error(request, "Erro - %s" % str(e)) context = {} self.template_name = 'compilacao/messages.html' + username = self.request.user.username + self.logger.error("user=" + username + ". " + str(e)) return self.render_to_response(context) def get_queryset(self): @@ -3134,7 +3156,8 @@ class DispositivoSearchFragmentFormView(ListView): return r except Exception as e: - print(e) + username = self.request.user.username + self.logger.error("user=" + username + ". " + str(e)) class DispositivoSearchModalView(FormView): diff --git a/sapl/crispy_layout_mixin.py b/sapl/crispy_layout_mixin.py index 245be6b82..b6af79427 100644 --- a/sapl/crispy_layout_mixin.py +++ b/sapl/crispy_layout_mixin.py @@ -52,6 +52,35 @@ def form_actions(more=[Div(css_class='clearfix')], ) +class SaplFormHelper(FormHelper): + render_hidden_fields = True # default = False + """ + até a release 1.6.1 do django-crispy-forms, os fields em Meta.Fields eram + renderizados mesmo se não mencionados no helper. + Com esta mudança (https://github.com/django-crispy-forms/django-crispy-forms/commit/6b93e8a362422db8fe54aa731319c7cbc39990ba) + render_hidden_fields foi adicionado uma condição em que a cada + instância do Helper, fosse decidido se os fields não mencionados serião ou + não renderizados... + O Sapl até este commit: https://github.com/interlegis/sapl/commit/22b87f36ebc8659a6ecaf8831ab0f425206b0993 + utilizou o django-crispy-forms na versão 1.6.1, ou seja, + sem a condição render_hidden_fields o que fazia o FormHelper, na 1.6.1 + set comportar como se, agora, na 1.7.2 o default fosse True. + Como todos os Forms do Sapl foram construídos assumindo que fields + não incluídos explicitamente no Helper, o helper o incluiria implicitamente, + e assim o era, de acordo com commit acima do django-crispy-forms, então + cria-se essa classe: + + class SaplFormHelper(FormHelper): + render_hidden_fields = True + + onde torna o default, antes False, agora = True, o esperado pelos forms do sapl, + e substituí-se todos os FormHelper por SaplFormHelper dentro do projeto Sapl + + + esta explicação ficará aqui dentro do código, via commit, e na issue #2456. + """ + + class SaplFormLayout(Layout): def __init__(self, *fields, cancel_label=_('Cancelar'), @@ -137,6 +166,7 @@ def get_field_display(obj, fieldname): value) elif 'TextField' in str_type_from_field: display = value.replace('\n', '
') + display = '
{}
'.format(display) else: display = str(value) return verbose_name, display @@ -188,8 +218,11 @@ class CrispyLayoutFormMixin: pass else: if self.layout_key: - form.helper = FormHelper() - form.helper.layout = SaplFormLayout(*self.get_layout()) + form.helper = SaplFormHelper() + layout = self.get_layout() + + form.helper.layout = SaplFormLayout(*layout) + return form @property diff --git a/sapl/crud/base.py b/sapl/crud/base.py index ca5469bf0..98af2a3bb 100644 --- a/sapl/crud/base.py +++ b/sapl/crud/base.py @@ -2,7 +2,6 @@ import logging from braces.views import FormMessagesMixin from crispy_forms.bootstrap import FieldWithButtons, StrictButton -from crispy_forms.helper import FormHelper from crispy_forms.layout import Field, Layout from django import forms from django.conf.urls import url @@ -25,6 +24,7 @@ from django.views.generic.base import ContextMixin from django.views.generic.list import MultipleObjectMixin from sapl.crispy_layout_mixin import CrispyLayoutFormMixin, get_field_display +from sapl.crispy_layout_mixin import SaplFormHelper from sapl.rules.map_rules import (RP_ADD, RP_CHANGE, RP_DELETE, RP_DETAIL, RP_LIST) from sapl.settings import BASE_DIR @@ -150,7 +150,7 @@ class ListWithSearchForm(forms.Form): def __init__(self, *args, **kwargs): super(ListWithSearchForm, self).__init__(*args, **kwargs) - self.helper = FormHelper() + self.helper = SaplFormHelper() self.form_class = 'form-inline' self.helper.form_method = 'GET' self.helper.layout = Layout( @@ -449,18 +449,28 @@ class CrudListView(PermissionRequiredContainerCrudMixin, ListView): if not n: s += '
' continue + m = obj n = n.split('__') for f in n[:-1]: m = getattr(m, f) if not m: break + + ss = '' if m: ss = get_field_display(m, n[-1])[1] ss = ( ('
' if '
    ' in ss else ' - ') + ss)\ if ss and j != 0 and s else ss + + hook = 'hook_{}'.format(''.join(n)) + if hasattr(self, hook): + hs, url = getattr(self, hook)(obj, ss, url) + s += str(hs) + else: s += ss + r.append((s, url)) return r @@ -571,6 +581,8 @@ class CrudListView(PermissionRequiredContainerCrudMixin, ListView): rmo = rmo[0] if not isinstance(rmo, str): rmo = rmo[0] + if rmo.startswith('-'): + rmo = rmo[1:] fo = '%s__%s' % (fo, rmo) fo = desc + fo diff --git a/sapl/legacy/test_renames.py b/sapl/legacy/test_renames.py index b92c5d0ac..9a5c4ebaf 100644 --- a/sapl/legacy/test_renames.py +++ b/sapl/legacy/test_renames.py @@ -13,9 +13,9 @@ from sapl.materia.models import (AcompanhamentoMateria, DocumentoAcessorio, from sapl.norma.models import (AnexoNormaJuridica, NormaJuridica, NormaRelacionada, TipoVinculoNormaJuridica) from sapl.parlamentares.models import (Frente, Mandato, Parlamentar, Partido, - TipoAfastamento, Votante) + TipoAfastamento, Votante, Bloco) from sapl.protocoloadm.models import DocumentoAdministrativo -from sapl.sessao.models import (Bancada, Bloco, CargoBancada, +from sapl.sessao.models import (Bancada, CargoBancada, ExpedienteMateria, Orador, OradorExpediente, OrdemDia, RegistroVotacao, ResumoOrdenacao, SessaoPlenaria, TipoResultadoVotacao, diff --git a/sapl/lexml/OAIServer.py b/sapl/lexml/OAIServer.py new file mode 100644 index 000000000..9783f2fa6 --- /dev/null +++ b/sapl/lexml/OAIServer.py @@ -0,0 +1,315 @@ +import unicodedata +from datetime import datetime + +import oaipmh +import oaipmh.error +import oaipmh.metadata +import oaipmh.server +from django.urls import reverse +from lxml import etree +from lxml.builder import ElementMaker + +from sapl.base.models import AppConfig, CasaLegislativa +from sapl.lexml.models import LexmlPublicador, LexmlProvedor +from sapl.norma.models import NormaJuridica +from sapl.utils import LISTA_DE_UFS + + +class OAILEXML: + """ + Padrao OAI do LeXML + Esta registrado sobre o nome 'oai_lexml' + """ + + def __init__(self, prefix): + self.prefix = prefix + self.ns = {'oai_lexml': 'http://www.lexml.gov.br/oai_lexml', } + self.schemas = {'oai_lexml': 'http://projeto.lexml.gov.br/esquemas/oai_lexml.xsd'} + + def __call__(self, element, metadata): + data = metadata.record + if data.get('metadata'): + value = etree.XML(data['metadata']) + element.append(value) + + +class OAIServer: + """ + An OAI-2.0 compliant oai server. + Underlying code is based on pyoai's oaipmh.server' + """ + + XSI_NS = 'http://www.w3.org/2001/XMLSchema-instance' + ns = {'lexml': 'http://www.lexml.gov.br/oai_lexml'} + schema = {'oai_lexml': 'http://projeto.lexml.gov.br/esquemas/oai_lexml.xsd'} + + def __init__(self, config={}): + self.config = config + + def identify(self): + result = oaipmh.common.Identify( + repositoryName=self.config['titulo'], + baseURL=self.config['base_url'], + protocolVersion='2.0', + adminEmails=self.config['email'], + earliestDatestamp=datetime(2001, 1, 1, 10, 00), + deletedRecord='transient', + granularity='YYYY-MM-DDThh:mm:ssZ', + compression=['identity'], + toolkit_description=False) + if self.config.get('descricao'): + result.add_description(self.config['descricao']) + return result + + def create_header_and_metadata(self, record): + header = self.create_header(record) + metadata = oaipmh.common.Metadata(None, record['metadata']) + metadata.record = record + return header, metadata + + def list_query(self, from_=None, until=None, offset=0, batch_size=10, identifier=None): + if identifier: + identifier = int(identifier.split('/')[-1]) # Get internal id + else: + identifier = '' + until = datetime.now() if not until or until > datetime.now() else until + return self.oai_query(offset=offset, batch_size=batch_size, from_=from_, until=until, + identifier=identifier) + + def check_metadata_prefix(self, metadata_prefix): + if not metadata_prefix in self.config['metadata_prefixes']: + raise oaipmh.error.CannotDisseminateFormatError + + def listRecords(self, metadataPrefix, from_=None, until=None, cursor=0, batch_size=10): + self.check_metadata_prefix(metadataPrefix) + for record in self.list_query(from_, until, cursor, batch_size): + header, metadata = self.create_header_and_metadata(record) + yield header, metadata, None # None? + + def get_oai_id(self, internal_id): + return "oai:{}".format(internal_id) + + def create_header(self, record): + oai_id = self.get_oai_id(record['record']['id']) + timestamp = record['record']['when_modified'] if record['record']['when_modified'] else datetime.now() + timestamp = timestamp.replace(tzinfo=None) + sets = [] + deleted = record['record']['deleted'] + return oaipmh.common.Header(None, oai_id, timestamp, sets, deleted) + + def get_esfera_federacao(self): + appconfig = AppConfig.objects.first() + return appconfig.esfera_federacao + + def recupera_norma(self, offset, batch_size, from_, until, identifier, esfera): + kwargs = {'data__lte': until} + if from_: + kwargs['data__gte'] = from_ + if identifier: + kwargs['numero'] = identifier + if esfera: + kwargs['esfera_federacao'] = esfera + return NormaJuridica.objects.select_related('tipo').filter(**kwargs)[offset:offset + batch_size] + + def monta_id(self, norma): + if norma: + num = len(casa.endereco_web.split('.')) + dominio = '.'.join(casa.endereco_web.split('.')[1:num]) + prefixo_oai = '{}.{}:sapl/'.format(casa.sigla.lower(), dominio) + numero_interno = norma.numero + tipo_norma = norma.tipo.equivalente_lexml + ano_norma = norma.ano + identificador = '{}{};{};{}'.format(prefixo_oai, tipo_norma, ano_norma, numero_interno) + return identificador + else: + return None + + @staticmethod + def remove_acentos(linha): + res = unicodedata.normalize('NFKD', linha).encode('ASCII', 'ignore') + res = res.decode("UTF-8") + remove_list = ["\'", "\"", "-"] + for i in remove_list: + res = res.replace(i, "") + return res + + def monta_urn(self, norma, esfera): + if norma: + urn = 'urn:lex:br;' + esferas = {'M': 'municipal', 'E': 'estadual'} + municipio = self.remove_acentos(casa.municipio.lower()) + uf_map = dict(LISTA_DE_UFS) + uf_desc = uf_map.get(casa.uf.upper(), '').lower() + uf_desc = self.remove_acentos(uf_desc) + for x in [' ', '.de.', '.da.', '.das.', '.do.', '.dos.']: + municipio = municipio.replace(x, '.') + uf_desc = uf_desc.replace(x, '.') + if esfera == 'M': + urn += '{};{}:'.format(uf_desc, municipio) + if norma.tipo.equivalente_lexml == 'regimento.interno' or norma.tipo.equivalente_lexml == 'resolucao': + urn += 'camara.' + urn += esferas[esfera] + ':' + elif esfera == 'E': + urn += '{}:{}:'.format(uf_desc, esferas[esfera]) + else: + urn += ':' + if norma.tipo.equivalente_lexml: + urn += '{}:{};'.format(norma.tipo.equivalente_lexml, norma.data.isoformat()) + else: + urn += '{};'.format(norma.data.isoformat()) + if norma.tipo.equivalente_lexml == 'lei.organica' or norma.tipo.equivalente_lexml == 'constituicao': + urn += str(norma.ano) + else: + urn += str(norma.numero) + if norma.data_vigencia and norma.data_publicacao: + urn += '@{};publicacao;{}'.format(norma.data_vigencia.isoformat(), norma.data_publicacao.isoformat()) + elif norma.data_publicacao: + urn += '@inicio.vigencia;publicacao;{}'.format(norma.data_publicacao.isoformat()) + return urn + else: + return None + + def data_por_extenso(self, data): + data = data.strftime('%d-%m-%Y') + if data != '': + meses = {1: 'Janeiro', 2: 'Fevereiro', 3: 'Março', 4: 'Abril', 5: 'Maio', 6: 'Junho', 7: 'Julho', + 8: 'Agosto', 9: 'Setembro', 10: 'Outubro', 11: 'Novembro', 12: 'Dezembro'} + return '{} de {} de {}'.format(data[0:2], meses[int(data[3:5])], data[6:]) + else: + return '' + + def monta_xml(self, urn, norma): + BASE_URL_SAPL = self.config['base_url'] + BASE_URL_SAPL = BASE_URL_SAPL[:BASE_URL_SAPL.find('/', 8)] + + publicador = LexmlPublicador.objects.first() + if norma and publicador: + LEXML = ElementMaker(namespace=self.ns['lexml'], nsmap=self.ns) + oai_lexml = LEXML.LexML() + oai_lexml.attrib['{{{pre}}}schemaLocation'.format(pre=self.XSI_NS)] = '{} {}'.format( + 'http://www.lexml.gov.br/oai_lexml', 'http://projeto.lexml.gov.br/esquemas/oai_lexml.xsd') + texto_integral = norma.texto_integral + mime_types = {'doc': 'application/msword', + 'docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + 'odt': 'application/vnd.oasis.opendocument.text', + 'pdf': 'application/pdf', + 'rtf': 'application/rtf'} + + if texto_integral: + url_conteudo = BASE_URL_SAPL + texto_integral.url + extensao = texto_integral.url.split('.')[-1] + formato = mime_types.get(extensao, 'application/octet-stream') + else: + formato = 'text/html' + url_conteudo = BASE_URL_SAPL + reverse('sapl.norma:normajuridica_detail', + kwargs={'pk': norma.pk}) + element_maker = ElementMaker() + id_publicador = str(publicador.id_publicador) + item_conteudo = element_maker.Item(url_conteudo, formato=formato, idPublicador=id_publicador, + tipo='conteudo') + oai_lexml.append(item_conteudo) + url = BASE_URL_SAPL + reverse('sapl.norma:normajuridica_detail', kwargs={'pk': norma.pk}) + item_metadado = element_maker.Item(url, formato='text/html', idPublicador=id_publicador, tipo='metadado') + oai_lexml.append(item_metadado) + documento_individual = element_maker.DocumentoIndividual(urn) + oai_lexml.append(documento_individual) + if norma.tipo.equivalente_lexml == 'lei.organica': + epigrafe = '{} de {} - {}, de {}'.format(norma.tipo.descricao, casa.municipio, + casa.uf, norma.ano) + elif norma.tipo.equivalente_lexml == 'constituicao': + epigrafe = '{} do Estado de {}, de {}'.format(norma.tipo.descricao, casa.municipio, + norma.ano) + else: + epigrafe = '{} n° {}, de {}'.format(norma.tipo.descricao, norma.numero, + self.data_por_extenso(norma.data)) + oai_lexml.append(element_maker.Epigrafe(epigrafe)) + oai_lexml.append(element_maker.Ementa(norma.ementa)) + indexacao = norma.indexacao + if indexacao: + oai_lexml.append(element_maker.Indexacao(indexacao)) + return etree.tostring(oai_lexml) + else: + return None + + def oai_query(self, offset=0, batch_size=10, from_=None, until=None, identifier=None): + esfera = self.get_esfera_federacao() + offset = 0 if offset < 0 else offset + batch_size = 10 if batch_size < 0 else batch_size + until = datetime.now() if not until or until > datetime.now() else until + normas = self.recupera_norma(offset, batch_size, from_, until, identifier, esfera) + for norma in normas: + resultado = {} + identificador = self.monta_id(norma) + urn = self.monta_urn(norma, esfera) + xml_lexml = self.monta_xml(urn, norma) + resultado['tx_metadado_xml'] = xml_lexml + resultado['cd_status'] = 'N' + resultado['id'] = identificador + resultado['when_modified'] = norma.timestamp + resultado['deleted'] = 0 + yield {'record': resultado, + 'metadata': resultado['tx_metadado_xml']} + + +def OAIServerFactory(config={}): + """ + Create a new OAI batching OAI Server given a config and a database + """ + for prefix in config['metadata_prefixes']: + metadata_registry = oaipmh.metadata.MetadataRegistry() + metadata_registry.registerWriter(prefix, OAILEXML(prefix)) + return oaipmh.server.BatchingServer( + OAIServer(config), + metadata_registry=metadata_registry, + resumption_batch_size=config['batch_size'] + ) + + +casa = None + + +def casa_legislativa(): + global casa + if not casa: + casa = CasaLegislativa.objects.first() + return casa if casa else CasaLegislativa() # retorna objeto dummy + + +def get_xml_provedor(): + """ antigo get_descricao_casa() """ + descricao = '' + provedor = LexmlProvedor.objects.first() + if provedor: + descricao = provedor.xml + if descricao: + descricao = descricao.encode('utf-8') + return descricao + + +def get_config(url, batch_size): + config = {'content_type': None, + 'delay': 0, + 'base_asset_path': None, + 'metadata_prefixes': ['oai_lexml'], + 'titulo': casa_legislativa().nome, # Inicializa variável global casa + 'email': [casa.email], # lista de e-mails, antigo `def get_email()` + 'base_url': url[:url.find('/', 8)] + reverse('sapl.lexml:lexml_endpoint')[:-4], # remove '/oai' suffix + 'descricao': get_xml_provedor(), + 'batch_size': batch_size + } + return config + + +if __name__ == '__main__': + """ + Para executar localmente (estando no diretório raiz): + + $ ./manage.py shell_plus + + Executar comando + %run sapl/lexml/OAIServer.py + """ + oai_server = OAIServerFactory(get_config('http://127.0.0.1:8000/', 10)) + r = oai_server.handleRequest({'verb': 'ListRecords', + 'metadataPrefix': 'oai_lexml'}) + print(r.decode('UTF-8')) diff --git a/sapl/lexml/models.py b/sapl/lexml/models.py index f6315d399..eeab71f73 100644 --- a/sapl/lexml/models.py +++ b/sapl/lexml/models.py @@ -23,6 +23,12 @@ class LexmlProvedor(models.Model): # LexmlRegistroProvedor blank=True, verbose_name=_('XML fornecido pela equipe do LexML:')) + @property + def pretty_xml(self): + import html + safe_xml = html.escape(self.xml) + return safe_xml.replace('\n', '
    ').replace(' ', ' ') + class Meta: verbose_name = _('Provedor Lexml') verbose_name_plural = _('Provedores Lexml') diff --git a/sapl/lexml/urls.py b/sapl/lexml/urls.py index e7d582030..980ade7c4 100644 --- a/sapl/lexml/urls.py +++ b/sapl/lexml/urls.py @@ -1,6 +1,6 @@ from django.conf.urls import include, url -from sapl.lexml.views import LexmlProvedorCrud, LexmlPublicadorCrud +from sapl.lexml.views import LexmlProvedorCrud, LexmlPublicadorCrud, lexml_request, request_search from .apps import AppConfig @@ -11,4 +11,7 @@ urlpatterns = [ include(LexmlProvedorCrud.get_urls())), url(r'^sistema/lexml/publicador/', include(LexmlPublicadorCrud.get_urls())), + url(r'^sistema/lexml/request_search/(?P[\w\-]+)/', request_search, name='lexml_search'), + url(r'^sistema/lexml/oai', lexml_request, name='lexml_endpoint'), + ] diff --git a/sapl/lexml/views.py b/sapl/lexml/views.py index 3627d5829..92fee8310 100644 --- a/sapl/lexml/views.py +++ b/sapl/lexml/views.py @@ -1,6 +1,35 @@ -from sapl.crud.base import CrudAux +from django.http import HttpResponse +from django.shortcuts import render + +from sapl.crud.base import CrudAux, Crud +from sapl.lexml.OAIServer import OAIServerFactory, get_config +from sapl.rules import RP_DETAIL, RP_LIST from .models import LexmlProvedor, LexmlPublicador -LexmlProvedorCrud = CrudAux.build(LexmlProvedor, 'lexml_provedor') LexmlPublicadorCrud = CrudAux.build(LexmlPublicador, 'lexml_publicador') + + +class LexmlProvedorCrud(Crud): + model = LexmlProvedor + help_topic = 'lexml_provedor' + public = [RP_LIST, RP_DETAIL] + + class DetailView(Crud.DetailView): + layout_key = 'LexmlProvedorDetail' + + +def lexml_request(request): + request_dict = request.GET.copy() + if request_dict.get('batch_size'): + del request_dict['batch_size'] + + config = get_config(request.get_raw_uri(), int(request.GET.get('batch_size', '10'))) + oai_server = OAIServerFactory(config) + r = oai_server.handleRequest(request_dict) + response = r.decode('UTF-8') + return HttpResponse(response, content_type='text/xml') + + +def request_search(request, keyword): + return render(request, "lexml/resultado-pesquisa.html", {"keyword": keyword}) diff --git a/sapl/materia/forms.py b/sapl/materia/forms.py index 2a95a47ba..0b8a0e451 100644 --- a/sapl/materia/forms.py +++ b/sapl/materia/forms.py @@ -3,7 +3,6 @@ import logging import os from crispy_forms.bootstrap import Alert, InlineRadios -from crispy_forms.helper import FormHelper from crispy_forms.layout import (HTML, Button, Column, Div, Field, Fieldset, Layout, Row) from django import forms @@ -26,26 +25,28 @@ import django_filters import sapl from sapl.base.models import AppConfig, Autor, TipoAutor -from sapl.comissoes.models import Comissao +from sapl.comissoes.models import Comissao, Participacao, Composicao from sapl.compilacao.models import (STATUS_TA_IMMUTABLE_PUBLIC, STATUS_TA_PRIVATE) from sapl.crispy_layout_mixin import (SaplFormLayout, form_actions, to_column, to_row) +from sapl.crispy_layout_mixin import SaplFormHelper from sapl.materia.models import (AssuntoMateria, Autoria, MateriaAssunto, MateriaLegislativa, Orgao, RegimeTramitacao, TipoDocumento, TipoProposicao, StatusTramitacao, UnidadeTramitacao) from sapl.norma.models import (LegislacaoCitada, NormaJuridica, TipoNormaJuridica) -from sapl.parlamentares.models import Legislatura, Partido -from sapl.protocoloadm.models import Protocolo, DocumentoAdministrativo +from sapl.parlamentares.models import Legislatura, Partido, Parlamentar +from sapl.protocoloadm.models import Protocolo, DocumentoAdministrativo, Anexado from sapl.settings import MAX_DOC_UPLOAD_SIZE from sapl.utils import (YES_NO_CHOICES, SEPARADOR_HASH_PROPOSICAO, ChoiceWithoutValidationField, MateriaPesquisaOrderingFilter, RangeWidgetOverride, autor_label, autor_modal, gerar_hash_arquivo, models_with_gr_for_model, qs_override_django_filter, - choice_anos_com_materias, FilterOverridesMetaMixin) + choice_anos_com_materias, FilterOverridesMetaMixin, FileFieldCheckMixin, + lista_anexados) from .models import (AcompanhamentoMateria, Anexada, Autoria, DespachoInicial, DocumentoAcessorio, Numeracao, Proposicao, Relatoria, @@ -76,7 +77,7 @@ class AdicionarVariasAutoriasFilterSet(django_filters.FilterSet): row1 = to_row([('nome', 12)]) - self.form.helper = FormHelper() + self.form.helper = SaplFormHelper() self.form.helper.form_method = 'GET' self.form.helper.layout = Layout( Fieldset(_('Filtrar Autores'), @@ -112,7 +113,7 @@ class ReceberProposicaoForm(Form): def __init__(self, *args, **kwargs): row1 = to_row([('cod_hash', 12)]) - self.helper = FormHelper() + self.helper = SaplFormHelper() self.helper.layout = Layout( Fieldset( _('Incorporar Proposição'), row1, @@ -122,7 +123,7 @@ class ReceberProposicaoForm(Form): super(ReceberProposicaoForm, self).__init__(*args, **kwargs) -class MateriaSimplificadaForm(ModelForm): +class MateriaSimplificadaForm(FileFieldCheckMixin, ModelForm): logger = logging.getLogger(__name__) @@ -145,7 +146,7 @@ class MateriaSimplificadaForm(ModelForm): row4 = to_row([('ementa', 12)]) row5 = to_row([('texto_original', 12)]) - self.helper = FormHelper() + self.helper = SaplFormHelper() self.helper.layout = Layout( Fieldset( _('Formulário Simplificado'), @@ -175,7 +176,7 @@ class MateriaSimplificadaForm(ModelForm): return cleaned_data -class MateriaLegislativaForm(ModelForm): +class MateriaLegislativaForm(FileFieldCheckMixin, ModelForm): logger = logging.getLogger(__name__) @@ -204,7 +205,7 @@ class MateriaLegislativaForm(ModelForm): widget=forms.HiddenInput()) self.fields['autor'] = forms.CharField(required=False, widget=forms.HiddenInput()) - if kwargs['instance'].numero_protocolo: + if kwargs['instance'].numero_protocolo and Protocolo.objects.filter(numero=kwargs['instance'].numero_protocolo, ano=kwargs['instance'].ano).exists(): self.fields['numero_protocolo'].widget.attrs['readonly'] = True def clean(self): @@ -340,22 +341,20 @@ class AcompanhamentoMateriaForm(ModelForm): def __init__(self, *args, **kwargs): - row1 = to_row([('email', 10)]) + row1 = to_row([('email', 12)]) - row1.append( - Column(form_actions(label='Cadastrar'), css_class='col-md-2') - ) - - self.helper = FormHelper() + self.helper = SaplFormHelper() self.helper.layout = Layout( Fieldset( - _('Acompanhamento de Matéria por e-mail'), row1 + _('Acompanhamento de Matéria por e-mail'), + row1, + form_actions(label='Cadastrar') ) ) super(AcompanhamentoMateriaForm, self).__init__(*args, **kwargs) -class DocumentoAcessorioForm(ModelForm): +class DocumentoAcessorioForm(FileFieldCheckMixin, ModelForm): data = forms.DateField(required=True) class Meta: @@ -364,39 +363,83 @@ class DocumentoAcessorioForm(ModelForm): class RelatoriaForm(ModelForm): - logger = logging.getLogger(__name__) + composicao = forms.ModelChoiceField( + required=True, + empty_label='---------', + queryset=Composicao.objects.all(), + label=_('Composição') + ) + class Meta: model = Relatoria - fields = ['data_designacao_relator', 'comissao', 'parlamentar', - 'data_destituicao_relator', 'tipo_fim_relatoria'] + fields = [ + 'comissao', + 'data_designacao_relator', + 'data_destituicao_relator', + 'tipo_fim_relatoria', + 'composicao', + 'parlamentar' + ] widgets = {'comissao': forms.Select(attrs={'disabled': 'disabled'})} def __init__(self, *args, **kwargs): - super(RelatoriaForm, self).__init__(*args, **kwargs) + row1 = to_row([('comissao', 12)]) + row2 = to_row([('data_designacao_relator', 4), + ('data_destituicao_relator', 4), + ('tipo_fim_relatoria', 4)]) + row3 = to_row([('composicao', 4), + ('parlamentar', 8)]) - def clean(self): - super(RelatoriaForm, self).clean() + self.helper = SaplFormHelper() + self.helper.layout = SaplFormLayout( + Fieldset(_('Relatoria'), row1, row2, row3)) - if not self.is_valid(): - return self.cleaned_data + super().__init__(*args, **kwargs) + comissao_pk = kwargs['initial']['comissao'] + composicoes = Composicao.objects.filter(comissao_id=comissao_pk) + self.fields['composicao'].choices = [('', '---------')] + \ + [(c.pk, c) for c in composicoes] + + # UPDATE + if self.initial.get('composicao') and self.initial.get('parlamentar'): + parlamentares = [(p.parlamentar.id, p.parlamentar) for p in + Participacao.objects.filter(composicao__comissao_id=comissao_pk, + composicao_id=self.initial['composicao'])] + + self.fields['parlamentar'].choices = [ + ('', '---------')] + parlamentares + # INSERT + else: + self.fields['parlamentar'].choices = [('', '---------')] + + def clean(self): + super().clean() cleaned_data = self.cleaned_data + if not self.is_valid(): + return cleaned_data + try: self.logger.debug("Tentando obter objeto Comissao.") comissao = Comissao.objects.get(id=self.initial['comissao']) except ObjectDoesNotExist as e: - self.logger.error("Objeto Comissao não encontrado com id={} " - ".A localização atual deve ser uma comissão. " - .format(self.initial['comissao']) + str(e)) + self.logger.error( + "Objeto Comissao não encontrado com id={}. A localização atual deve ser uma comissão. ".format( + self.initial['comissao']) + str(e)) msg = _('A localização atual deve ser uma comissão.') raise ValidationError(msg) else: cleaned_data['comissao'] = comissao + if cleaned_data['data_designacao_relator'] < cleaned_data['composicao'].periodo.data_inicio \ + or cleaned_data['data_designacao_relator'] > cleaned_data['composicao'].periodo.data_fim: + raise ValidationError( + _('Data de designação deve estar dentro do período da composição.')) + return cleaned_data @@ -419,11 +462,23 @@ class TramitacaoForm(ModelForm): 'unidade_tramitacao_destino', 'data_encaminhamento', 'data_fim_prazo', - 'texto'] + 'texto', + 'user', + 'ip'] + widgets = {'user': forms.HiddenInput(), + 'ip': forms.HiddenInput()} def __init__(self, *args, **kwargs): super(TramitacaoForm, self).__init__(*args, **kwargs) self.fields['data_tramitacao'].initial = timezone.now().date() + ust = UnidadeTramitacao.objects.select_related().all() + unidade_tramitacao_destino = [('', '---------')] + [(ut.pk, ut) + for ut in ust if ut.comissao and ut.comissao.ativa] + unidade_tramitacao_destino.extend( + [(ut.pk, ut) for ut in ust if ut.orgao]) + unidade_tramitacao_destino.extend( + [(ut.pk, ut) for ut in ust if ut.parlamentar]) + self.fields['unidade_tramitacao_destino'].choices = unidade_tramitacao_destino def clean(self): super(TramitacaoForm, self).clean() @@ -495,6 +550,49 @@ class TramitacaoForm(ModelForm): return cleaned_data + @transaction.atomic + def save(self, commit=True): + tramitacao = super(TramitacaoForm, self).save(commit) + materia = tramitacao.materia + materia.em_tramitacao = False if tramitacao.status.indicador == "F" else True + materia.save() + + lista_tramitacao = [] + lista_anexadas = lista_anexados(materia) + for ma in lista_anexadas: + if not ma.tramitacao_set.all() \ + or ma.tramitacao_set.last().unidade_tramitacao_destino == tramitacao.unidade_tramitacao_local: + ma.em_tramitacao = False if tramitacao.status.indicador == "F" else True + ma.save() + lista_tramitacao.append(Tramitacao( + status=tramitacao.status, + materia=ma, + data_tramitacao=tramitacao.data_tramitacao, + unidade_tramitacao_local=tramitacao.unidade_tramitacao_local, + data_encaminhamento=tramitacao.data_encaminhamento, + unidade_tramitacao_destino=tramitacao.unidade_tramitacao_destino, + urgente=tramitacao.urgente, + turno=tramitacao.turno, + texto=tramitacao.texto, + data_fim_prazo=tramitacao.data_fim_prazo, + user=tramitacao.user, + ip=tramitacao.ip + )) + Tramitacao.objects.bulk_create(lista_tramitacao) + + return tramitacao + + +# Compara se os campos de duas tramitações são iguais, +# exceto os campos id, documento_id e timestamp +def compara_tramitacoes_mat(tramitacao1, tramitacao2): + if not tramitacao1 or not tramitacao2: + return False + + lst_items = ['id', 'materia_id', 'timestamp'] + values = [(k,v) for k,v in tramitacao1.__dict__.items() if ((k not in lst_items) and (k[0] != '_'))] + other_values = [(k,v) for k,v in tramitacao2.__dict__.items() if (k not in lst_items and k[0] != '_')] + return values == other_values class TramitacaoUpdateForm(TramitacaoForm): unidade_tramitacao_local = forms.ModelChoiceField( @@ -516,11 +614,15 @@ class TramitacaoUpdateForm(TramitacaoForm): 'data_encaminhamento', 'data_fim_prazo', 'texto', + 'user', + 'ip' ] widgets = { 'data_encaminhamento': forms.DateInput(format='%d/%m/%Y'), 'data_fim_prazo': forms.DateInput(format='%d/%m/%Y'), + 'user': forms.HiddenInput(), + 'ip': forms.HiddenInput() } def clean(self): @@ -529,33 +631,73 @@ class TramitacaoUpdateForm(TramitacaoForm): if not self.is_valid(): return self.cleaned_data + cd = self.cleaned_data + obj = self.instance + ultima_tramitacao = Tramitacao.objects.filter( - materia_id=self.instance.materia_id).order_by( + materia_id=obj.materia_id).order_by( '-data_tramitacao', '-id').first() # Se a Tramitação que está sendo editada não for a mais recente, # ela não pode ter seu destino alterado. - if ultima_tramitacao != self.instance: - if self.cleaned_data['unidade_tramitacao_destino'] != \ - self.instance.unidade_tramitacao_destino: + if ultima_tramitacao != obj: + if cd['unidade_tramitacao_destino'] != \ + obj.unidade_tramitacao_destino: self.logger.error("Você não pode mudar a Unidade de Destino desta " "tramitação para {}, pois irá conflitar com a Unidade " "Local da tramitação seguinte ({})." - .format(self.cleaned_data['unidade_tramitacao_destino'], - self.instance.unidade_tramitacao_destino)) + .format(cd['unidade_tramitacao_destino'], + obj.unidade_tramitacao_destino)) raise ValidationError( 'Você não pode mudar a Unidade de Destino desta ' 'tramitação, pois irá conflitar com a Unidade ' 'Local da tramitação seguinte') + + # Se não houve qualquer alteração em um dos dados, mantém o usuário e ip + if not (cd['data_tramitacao'] != obj.data_tramitacao or \ + cd['unidade_tramitacao_destino'] != obj.unidade_tramitacao_destino or \ + cd['status'] != obj.status or cd['texto'] != obj.texto or \ + cd['data_encaminhamento'] != obj.data_encaminhamento or \ + cd['data_fim_prazo'] != obj.data_fim_prazo or \ + cd['urgente'] != obj.urgente or \ + cd['turno'] != obj.turno): + cd['user'] = obj.user + cd['ip'] = obj.ip + + cd['data_tramitacao'] = obj.data_tramitacao + cd['unidade_tramitacao_local'] = obj.unidade_tramitacao_local - self.cleaned_data['data_tramitacao'] = \ - self.instance.data_tramitacao - self.cleaned_data['unidade_tramitacao_local'] = \ - self.instance.unidade_tramitacao_local + return cd - return self.cleaned_data + @transaction.atomic + def save(self, commit=True): + ant_tram_principal = Tramitacao.objects.get(id=self.instance.id) + nova_tram_principal = super(TramitacaoUpdateForm, self).save(commit) + materia = nova_tram_principal.materia + materia.em_tramitacao = False if nova_tram_principal.status.indicador == "F" else True + materia.save() + lista_anexadas = lista_anexados(materia) + for ma in lista_anexadas: + tram_anexada = ma.tramitacao_set.last() + if compara_tramitacoes_mat(ant_tram_principal, tram_anexada): + tram_anexada.status = nova_tram_principal.status + tram_anexada.data_tramitacao = nova_tram_principal.data_tramitacao + tram_anexada.unidade_tramitacao_local = nova_tram_principal.unidade_tramitacao_local + tram_anexada.data_encaminhamento = nova_tram_principal.data_encaminhamento + tram_anexada.unidade_tramitacao_destino = nova_tram_principal.unidade_tramitacao_destino + tram_anexada.urgente = nova_tram_principal.urgente + tram_anexada.turno = nova_tram_principal.turno + tram_anexada.texto = nova_tram_principal.texto + tram_anexada.data_fim_prazo = nova_tram_principal.data_fim_prazo + tram_anexada.user = nova_tram_principal.user + tram_anexada.ip = nova_tram_principal.ip + tram_anexada.save() + + ma.em_tramitacao = False if nova_tram_principal.status.indicador == "F" else True + ma.save() + return nova_tram_principal class LegislacaoCitadaForm(ModelForm): @@ -710,7 +852,7 @@ class AnexadaForm(ModelForm): empty_label='Selecione', ) - numero = forms.CharField(label='Número', required=True) + numero = forms.IntegerField(label='Número', required=True) ano = forms.CharField(label='Ano', required=True) @@ -726,6 +868,13 @@ class AnexadaForm(ModelForm): cleaned_data = self.cleaned_data + data_anexacao = cleaned_data['data_anexacao'] + data_desanexacao = cleaned_data['data_desanexacao'] if cleaned_data['data_desanexacao'] else data_anexacao + + if data_anexacao > data_desanexacao: + self.logger.error("Data de anexação posterior à data de desanexação.") + raise ValidationError(_("Data de anexação posterior à data de desanexação.")) + try: self.logger.info("Tentando obter objeto MateriaLegislativa (numero={}, ano={}, tipo={})." .format(cleaned_data['numero'], cleaned_data['ano'], cleaned_data['tipo'])) @@ -734,8 +883,8 @@ class AnexadaForm(ModelForm): ano=cleaned_data['ano'], tipo=cleaned_data['tipo']) except ObjectDoesNotExist: - msg = _('A MateriaLegislativa a ser anexada (numero={}, ano={}, tipo={}) não existe no cadastro' - ' de matérias legislativas.'.format(cleaned_data['numero'], cleaned_data['ano'], cleaned_data['tipo'])) + msg = _('A {} {}/{} não existe no cadastro de matérias legislativas.' + .format(cleaned_data['tipo'], cleaned_data['numero'], cleaned_data['ano'])) self.logger.error("A matéria a ser anexada não existe no cadastro" " de matérias legislativas.") raise ValidationError(msg) @@ -745,12 +894,34 @@ class AnexadaForm(ModelForm): self.logger.error("Matéria não pode ser anexada a si mesma.") raise ValidationError(_('Matéria não pode ser anexada a si mesma')) - is_anexada = Anexada.objects.filter(materia_principal=materia_principal, - materia_anexada=materia_anexada - ).exists() + is_anexada = Anexada.objects.filter( + materia_principal=materia_principal, + materia_anexada=materia_anexada + ).exclude(pk=self.instance.pk).exists() + if is_anexada: self.logger.error("Matéria já se encontra anexada.") raise ValidationError(_('Matéria já se encontra anexada')) + + ciclico = False + anexadas_anexada = Anexada.objects.filter(materia_principal=materia_anexada) + + while anexadas_anexada and not ciclico: + anexadas = [] + + for anexa in anexadas_anexada: + + if materia_principal == anexa.materia_anexada: + ciclico = True + else: + for a in Anexada.objects.filter(materia_principal=anexa.materia_anexada): + anexadas.append(a) + + anexadas_anexada = anexadas + + if ciclico: + self.logger.error("A matéria não pode ser anexada por uma de suas anexadas.") + raise ValidationError(_("A matéria não pode ser anexada por uma de suas anexadas.")) cleaned_data['materia_anexada'] = materia_anexada @@ -896,7 +1067,7 @@ class MateriaLegislativaFilterSet(django_filters.FilterSet): ('tipo_listagem', 4) ]) - self.form.helper = FormHelper() + self.form.helper = SaplFormHelper() self.form.helper.form_method = 'GET' self.form.helper.layout = Layout( Fieldset(_('Pesquisa Básica'), @@ -1016,7 +1187,7 @@ class AutoriaForm(ModelForm): ('autor', 4), ('primeiro_autor', 4)]) - self.helper = FormHelper() + self.helper = SaplFormHelper() self.helper.layout = Layout( Fieldset(_('Autoria'), row1, 'data_relativa', form_actions(label='Salvar'))) @@ -1077,7 +1248,7 @@ class AutoriaMultiCreateForm(Form): row2 = to_row([('autor', 12), ]) - self.helper = FormHelper() + self.helper = SaplFormHelper() self.helper.layout = Layout( Fieldset( _('Autorias'), row1, row2, 'data_relativa', 'autores', @@ -1117,13 +1288,35 @@ class AcessorioEmLoteFilterSet(django_filters.FilterSet): row1 = to_row([('tipo', 12)]) row2 = to_row([('data_apresentacao', 12)]) - self.form.helper = FormHelper() + self.form.helper = SaplFormHelper() self.form.helper.form_method = 'GET' self.form.helper.layout = Layout( Fieldset(_('Documentos Acessórios em Lote'), row1, row2, form_actions(label='Pesquisar'))) +class AnexadaEmLoteFilterSet(django_filters.FilterSet): + + class Meta(FilterOverridesMetaMixin): + model = MateriaLegislativa + fields = ['tipo', 'data_apresentacao'] + + def __init__(self, *args, **kwargs): + super(AnexadaEmLoteFilterSet, self).__init__(*args, **kwargs) + + self.filters['tipo'].label = 'Tipo de Matéria' + self.filters['data_apresentacao'].label = 'Data (Inicial - Final)' + + row1 = to_row([('tipo', 12)]) + row2 = to_row([('data_apresentacao', 12)]) + + self.form.helper = SaplFormHelper() + self.form.helper.form_method = 'GET' + self.form.helper.layout = Layout( + Fieldset(_('Pesquisa de Matérias'), + row1, row2, form_actions(label='Pesquisar'))) + + class PrimeiraTramitacaoEmLoteFilterSet(django_filters.FilterSet): class Meta(FilterOverridesMetaMixin): @@ -1142,7 +1335,7 @@ class PrimeiraTramitacaoEmLoteFilterSet(django_filters.FilterSet): row1 = to_row([('tipo', 12)]) row2 = to_row([('data_apresentacao', 12)]) - self.form.helper = FormHelper() + self.form.helper = SaplFormHelper() self.form.helper.form_method = 'GET' self.form.helper.layout = Layout( Fieldset(_('Primeira Tramitação'), @@ -1177,7 +1370,7 @@ class TramitacaoEmLoteFilterSet(django_filters.FilterSet): ('tramitacao__status', 4)]) row2 = to_row([('data_apresentacao', 12)]) - self.form.helper = FormHelper() + self.form.helper = SaplFormHelper() self.form.helper.form_method = 'GET' self.form.helper.layout = Layout( Fieldset(_('Tramitação em Lote'), @@ -1241,7 +1434,7 @@ class TipoProposicaoForm(ModelForm): ) ) - self.helper = FormHelper() + self.helper = SaplFormHelper() self.helper.layout = SaplFormLayout(tipo_select) super(TipoProposicaoForm, self).__init__(*args, **kwargs) @@ -1331,7 +1524,7 @@ class TipoProposicaoSelect(Select): return option -class ProposicaoForm(forms.ModelForm): +class ProposicaoForm(FileFieldCheckMixin, forms.ModelForm): logger = logging.getLogger(__name__) @@ -1368,6 +1561,9 @@ class ProposicaoForm(forms.ModelForm): widget=widgets.HiddenInput(), required=False) + numero_materia_futuro = forms.IntegerField( + label='Número (Opcional)', required=False) + class Meta: model = Proposicao fields = ['tipo', @@ -1381,7 +1577,8 @@ class ProposicaoForm(forms.ModelForm): 'numero_materia', 'ano_materia', 'tipo_texto', - 'hash_code'] + 'hash_code', + 'numero_materia_futuro'] widgets = { 'descricao': widgets.Textarea(attrs={'rows': 4}), @@ -1389,10 +1586,10 @@ class ProposicaoForm(forms.ModelForm): 'hash_code': forms.HiddenInput(), } def __init__(self, *args, **kwargs): - self.texto_articulado_proposicao = sapl.base.models.AppConfig.attr( + self.texto_articulado_proposicao = AppConfig.attr( 'texto_articulado_proposicao') - self.receber_recibo = sapl.base.models.AppConfig.attr( + self.receber_recibo = AppConfig.attr( 'receber_recibo_proposicao') if not self.texto_articulado_proposicao: @@ -1414,6 +1611,12 @@ class ProposicaoForm(forms.ModelForm): ] + if AppConfig.objects.last().escolher_numero_materia_proposicao: + fields.append(to_column(('numero_materia_futuro', 12)),) + else: + if 'numero_materia_futuro' in self._meta.fields: + self._meta.fields.remove('numero_materia_futuro') + if self.texto_articulado_proposicao: fields.append( to_column((InlineRadios('tipo_texto'), 5)),) @@ -1431,7 +1634,7 @@ class ProposicaoForm(forms.ModelForm): ('ano_materia', 6)]), ), 12)), ) - self.helper = FormHelper() + self.helper = SaplFormHelper() self.helper.layout = SaplFormLayout(*fields) super(ProposicaoForm, self).__init__(*args, **kwargs) @@ -1490,6 +1693,15 @@ class ProposicaoForm(forms.ModelForm): cd.get('ano_materia', ''), cd.get('numero_materia', '')) + if cd['numero_materia_futuro'] and \ + 'tipo' in cd and \ + MateriaLegislativa.objects.filter(tipo=cd['tipo'].tipo_conteudo_related, + ano=timezone.now().year, + numero=cd['numero_materia_futuro']): + raise ValidationError(_("A matéria {} {}/{} já existe.".format(cd['tipo'].tipo_conteudo_related.descricao, + cd['numero_materia_futuro'], + timezone.now().year))) + if tm and am and nm: try: self.logger.debug("Tentando obter objeto MateriaLegislativa (tipo_id={}, ano={}, numero={})." @@ -1532,12 +1744,17 @@ class ProposicaoForm(forms.ModelForm): return super().save(commit) inst.ano = timezone.now().year - numero__max = Proposicao.objects.filter( - autor=inst.autor, - ano=timezone.now().year).aggregate(Max('numero_proposicao')) + sequencia_numeracao = AppConfig.attr('sequencia_numeracao_proposicao') + if sequencia_numeracao == 'A': + numero__max = Proposicao.objects.filter( + autor=inst.autor, + ano=timezone.now().year).aggregate(Max('numero_proposicao')) + elif sequencia_numeracao == 'B': + numero__max = Proposicao.objects.filter( + ano=timezone.now().year).aggregate(Max('numero_proposicao')) numero__max = numero__max['numero_proposicao__max'] inst.numero_proposicao = ( - numero__max + 1) if numero__max else 1 + numero__max + 1) if numero__max else 1 self.gerar_hash(inst, receber_recibo) @@ -1579,7 +1796,7 @@ class DevolverProposicaoForm(forms.ModelForm): ) ) - self.helper = FormHelper() + self.helper = SaplFormHelper() self.helper.layout = Layout(*fields) def clean(self): @@ -1634,7 +1851,7 @@ class ConfirmarProposicaoForm(ProposicaoForm): required=False, widget=widgets.TextInput( attrs={'readonly': 'readonly'})) - regime_tramitacao = forms.ModelChoiceField( + regime_tramitacao = forms.ModelChoiceField(label="Regime de tramitação", required=False, queryset=RegimeTramitacao.objects.all()) gerar_protocolo = forms.ChoiceField( @@ -1654,21 +1871,20 @@ class ConfirmarProposicaoForm(ProposicaoForm): 'descricao', 'observacao', 'gerar_protocolo', - 'numero_de_paginas' + 'numero_de_paginas', + 'numero_materia_futuro' ] widgets = { 'descricao': widgets.Textarea( attrs={'readonly': 'readonly', 'rows': 4}), 'data_envio': widgets.DateTimeInput( attrs={'readonly': 'readonly'}), - } def __init__(self, *args, **kwargs): self.proposicao_incorporacao_obrigatoria = \ - sapl.base.models.AppConfig.attr( - 'proposicao_incorporacao_obrigatoria') + AppConfig.attr('proposicao_incorporacao_obrigatoria') if self.proposicao_incorporacao_obrigatoria != 'C': if 'gerar_protocolo' in self._meta.fields: @@ -1700,14 +1916,19 @@ class ConfirmarProposicaoForm(ProposicaoForm): # esta chamada isola o __init__ de ProposicaoForm super(ProposicaoForm, self).__init__(*args, **kwargs) + if self.instance.tipo.content_type.model_class() ==\ + TipoMateriaLegislativa: + self.fields['regime_tramitacao'].required = True + fields = [ Fieldset( _('Dados Básicos'), to_row( [ - ('tipo_readonly', 4), + ('tipo_readonly', 3), ('data_envio', 3), - ('autor_readonly', 5), + ('autor_readonly', 3), + ('numero_materia_futuro', 3), ('descricao', 12), ('observacao', 12) ] @@ -1715,6 +1936,11 @@ class ConfirmarProposicaoForm(ProposicaoForm): ) ] + if not AppConfig.objects.last().escolher_numero_materia_proposicao or \ + not self.instance.numero_materia_futuro: + if 'numero_materia_futuro' in self._meta.fields: + del fields[0][0][3] + fields.append( Fieldset( _('Vinculado a Matéria Legislativa'), @@ -1762,11 +1988,13 @@ class ConfirmarProposicaoForm(ProposicaoForm): fields.append( Fieldset(_('Registro de Incorporação'), Row(*itens_incorporacao))) - self.helper = FormHelper() + self.helper = SaplFormHelper() self.helper.layout = Layout(*fields) self.fields['tipo_readonly'].initial = self.instance.tipo.descricao self.fields['autor_readonly'].initial = str(self.instance.autor) + if self.instance.numero_materia_futuro: + self.fields['numero_materia_futuro'].initial = self.instance.numero_materia_futuro if self.instance.materia_de_vinculo: self.fields[ @@ -1788,7 +2016,7 @@ class ConfirmarProposicaoForm(ProposicaoForm): if not self.is_valid(): return self.cleaned_data - numeracao = sapl.base.models.AppConfig.attr('sequencia_numeracao') + numeracao = AppConfig.attr('sequencia_numeracao_proposicao') if not numeracao: self.logger.error("A sequência de numeração (por ano ou geral)" @@ -1865,8 +2093,8 @@ class ConfirmarProposicaoForm(ProposicaoForm): try: self.logger.debug( "Tentando obter modelo de sequência de numeração.") - numeracao = sapl.base.models.AppConfig.objects.last( - ).sequencia_numeracao + numeracao = AppConfig.objects.last( + ).sequencia_numeracao_protocolo except AttributeError as e: self.logger.error("Erro ao obter modelo. " + str(e)) pass @@ -1892,12 +2120,16 @@ class ConfirmarProposicaoForm(ProposicaoForm): elif numeracao == 'U': numero = MateriaLegislativa.objects.filter( tipo=tipo).aggregate(Max('numero')) - if numeracao is None: numero['numero__max'] = 0 - max_numero = numero['numero__max'] + \ - 1 if numero['numero__max'] else 1 + if cd['numero_materia_futuro'] and not MateriaLegislativa.objects.filter(tipo=tipo, + ano=ano, + numero=cd['numero_materia_futuro']): + max_numero = cd['numero_materia_futuro'] + else: + max_numero = numero['numero__max'] + \ + 1 if numero['numero__max'] else 1 # dados básicos materia = MateriaLegislativa() @@ -2013,7 +2245,7 @@ class ConfirmarProposicaoForm(ProposicaoForm): GenericForeignKey """ - numeracao = sapl.base.models.AppConfig.attr('sequencia_numeracao') + numeracao = AppConfig.attr('sequencia_numeracao_protocolo') if numeracao == 'A': nm = Protocolo.objects.filter( ano=timezone.now().year).aggregate(Max('numero')) @@ -2118,7 +2350,7 @@ class EtiquetaPesquisaForm(forms.Form): [('processo_inicial', 6), ('processo_final', 6)]) - self.helper = FormHelper() + self.helper = SaplFormHelper() self.helper.layout = Layout( Fieldset( ('Formulário de Etiqueta'), @@ -2203,7 +2435,7 @@ class FichaPesquisaForm(forms.Form): ('data_inicial', 3), ('data_final', 3)]) - self.helper = FormHelper() + self.helper = SaplFormHelper() self.helper.layout = Layout( Fieldset( ('Formulário de Ficha'), @@ -2244,7 +2476,7 @@ class FichaSelecionaForm(forms.Form): row1 = to_row( [('materia', 12)]) - self.helper = FormHelper() + self.helper = SaplFormHelper() self.helper.layout = Layout( Fieldset( ('Selecione a ficha que deseja imprimir'), @@ -2314,7 +2546,7 @@ class ExcluirTramitacaoEmLote(forms.Form): [('unidade_tramitacao_local', 6), ('unidade_tramitacao_destino', 6)]) - self.helper = FormHelper() + self.helper = SaplFormHelper() self.helper.layout = Layout( Fieldset(_('Dados das Tramitações'), row1, @@ -2323,3 +2555,74 @@ class ExcluirTramitacaoEmLote(forms.Form): form_actions(label='Excluir') ) ) + + +class MateriaPesquisaSimplesForm(forms.Form): + tipo_materia = forms.ModelChoiceField( + label=TipoMateriaLegislativa._meta.verbose_name, + queryset=TipoMateriaLegislativa.objects.all(), + required=False, + empty_label='Selecione') + + data_inicial = forms.DateField( + label='Data Inicial', + required=False, + widget=forms.DateInput(format='%d/%m/%Y') + ) + + data_final = forms.DateField( + label='Data Final', + required=False, + widget=forms.DateInput(format='%d/%m/%Y') + ) + + titulo = forms.CharField( + label='Título do Relatório', + required=False, + max_length=150) + + logger = logging.getLogger(__name__) + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + row1 = to_row( + [('tipo_materia', 6), + ('data_inicial', 3), + ('data_final', 3)]) + + row2 = to_row( + [('titulo', 12)]) + + self.helper = SaplFormHelper() + self.helper.layout = Layout( + Fieldset( + 'Índice de Materias', + row1, row2, + form_actions(label='Pesquisar') + ) + ) + + def clean(self): + super().clean() + + if not self.is_valid(): + return self.cleaned_data + + cleaned_data = self.cleaned_data + data_inicial = cleaned_data['data_inicial'] + data_final = cleaned_data['data_final'] + + if data_inicial or data_final: + if not (data_inicial and data_final): + self.logger.error("Caso pesquise por data, os campos de Data Inicial e " + "Data Final devem ser preenchidos obrigatoriamente") + raise ValidationError(_('Caso pesquise por data, os campos de Data Inicial e ' + 'Data Final devem ser preenchidos obrigatoriamente')) + elif data_inicial > data_final: + self.logger.error("Data Final ({}) menor que a Data Inicial ({}).".format( + data_final, data_inicial)) + raise ValidationError( + _('A Data Final não pode ser menor que a Data Inicial')) + + return cleaned_data diff --git a/sapl/materia/migrations/0041_proposicao_numero_materia_futuro.py b/sapl/materia/migrations/0041_proposicao_numero_materia_futuro.py new file mode 100644 index 000000000..2fb21a4fb --- /dev/null +++ b/sapl/materia/migrations/0041_proposicao_numero_materia_futuro.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.20 on 2019-02-15 11:10 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('materia', '0040_auto_20190211_1602'), + ] + + operations = [ + migrations.AddField( + model_name='proposicao', + name='numero_materia_futuro', + field=models.PositiveIntegerField(blank=True, null=True, verbose_name='Número Matéria'), + ), + ] diff --git a/sapl/materia/migrations/0042_tipomaterialegislativa_sequencia_regimental.py b/sapl/materia/migrations/0042_tipomaterialegislativa_sequencia_regimental.py new file mode 100644 index 000000000..81d1ccbcb --- /dev/null +++ b/sapl/materia/migrations/0042_tipomaterialegislativa_sequencia_regimental.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.20 on 2019-03-20 11:26 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('materia', '0041_proposicao_numero_materia_futuro'), + ] + + operations = [ + migrations.AddField( + model_name='tipomaterialegislativa', + name='sequencia_regimental', + field=models.PositiveIntegerField( + default=0, help_text='A sequência regimental diz respeito ao que define o regimento da Casa Legislativa sobre qual a ordem de entrada das proposições nas Sessões Plenárias.', verbose_name='Sequência Regimental'), + ), + ] diff --git a/sapl/materia/migrations/0043_auto_20190320_1749.py b/sapl/materia/migrations/0043_auto_20190320_1749.py new file mode 100644 index 000000000..16c05e21b --- /dev/null +++ b/sapl/materia/migrations/0043_auto_20190320_1749.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.20 on 2019-03-20 20:49 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('materia', '0042_tipomaterialegislativa_sequencia_regimental'), + ] + + operations = [ + migrations.AlterModelOptions( + name='tipomaterialegislativa', + options={'ordering': ['sequencia_regimental', 'descricao'], 'verbose_name': 'Tipo de Matéria Legislativa', 'verbose_name_plural': 'Tipos de Matérias Legislativas'}, + ), + ] diff --git a/sapl/materia/migrations/0044_auto_20190327_1409.py b/sapl/materia/migrations/0044_auto_20190327_1409.py new file mode 100644 index 000000000..5322d7833 --- /dev/null +++ b/sapl/materia/migrations/0044_auto_20190327_1409.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.20 on 2019-03-27 17:09 +from __future__ import unicode_literals + +from django.db import migrations, models +import sapl.materia.models +import sapl.utils + + +class Migration(migrations.Migration): + + dependencies = [ + ('materia', '0043_auto_20190320_1749'), + ] + + operations = [ + migrations.AlterField( + model_name='documentoacessorio', + name='arquivo', + field=models.FileField(blank=True, max_length=255, null=True, upload_to=sapl.materia.models.anexo_upload_path, validators=[sapl.utils.restringe_tipos_de_arquivo_txt], verbose_name='Texto Integral'), + ), + ] diff --git a/sapl/materia/migrations/0045_auto_20190415_1050.py b/sapl/materia/migrations/0045_auto_20190415_1050.py new file mode 100644 index 000000000..55f59dd49 --- /dev/null +++ b/sapl/materia/migrations/0045_auto_20190415_1050.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.20 on 2019-04-15 13:50 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('materia', '0044_auto_20190327_1409'), + ] + + operations = [ + migrations.AlterField( + model_name='tipomaterialegislativa', + name='sequencia_numeracao', + field=models.CharField(blank=True, choices=[('A', 'Sequencial por ano para cada autor'), ('B', 'Sequencial por ano indepententemente do autor'), ('L', 'Sequencial por legislatura'), ('U', 'Sequencial único')], max_length=1, verbose_name='Sequência de numeração'), + ), + ] diff --git a/sapl/materia/migrations/0046_auto_20190417_0941.py b/sapl/materia/migrations/0046_auto_20190417_0941.py new file mode 100644 index 000000000..f98170042 --- /dev/null +++ b/sapl/materia/migrations/0046_auto_20190417_0941.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.20 on 2019-04-17 12:41 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('materia', '0045_auto_20190415_1050'), + ] + + operations = [ + migrations.AlterField( + model_name='tipomaterialegislativa', + name='sequencia_numeracao', + field=models.CharField(blank=True, choices=[('A', 'Sequencial por ano para cada autor'), ('L', 'Sequencial por legislatura'), ('U', 'Sequencial único')], max_length=1, verbose_name='Sequência de numeração'), + ), + ] diff --git a/sapl/materia/migrations/0046_auto_20190417_1212.py b/sapl/materia/migrations/0046_auto_20190417_1212.py new file mode 100644 index 000000000..397114479 --- /dev/null +++ b/sapl/materia/migrations/0046_auto_20190417_1212.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.20 on 2019-04-17 15:12 +from __future__ import unicode_literals + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('materia', '0045_auto_20190415_1050'), + ] + + operations = [ + migrations.AddField( + model_name='tramitacao', + name='ip', + field=models.CharField(blank=True, default='', max_length=30, verbose_name='IP'), + ), + migrations.AddField( + model_name='tramitacao', + name='user', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL, verbose_name='Usuário'), + ), + ] diff --git a/sapl/materia/migrations/0047_auto_20190417_1432.py b/sapl/materia/migrations/0047_auto_20190417_1432.py new file mode 100644 index 000000000..12783fe3a --- /dev/null +++ b/sapl/materia/migrations/0047_auto_20190417_1432.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.20 on 2019-04-17 17:32 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('materia', '0046_auto_20190417_0941'), + ] + + operations = [ + migrations.AlterField( + model_name='tipomaterialegislativa', + name='sequencia_numeracao', + field=models.CharField(blank=True, choices=[('A', 'Sequencial por ano'), ('L', 'Sequencial por legislatura'), ('U', 'Sequencial único')], max_length=1, verbose_name='Sequência de numeração'), + ), + ] diff --git a/sapl/materia/migrations/0048_merge_20190426_0828.py b/sapl/materia/migrations/0048_merge_20190426_0828.py new file mode 100644 index 000000000..94d624b1d --- /dev/null +++ b/sapl/materia/migrations/0048_merge_20190426_0828.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.20 on 2019-04-26 11:28 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('materia', '0046_auto_20190417_1212'), + ('materia', '0047_auto_20190417_1432'), + ] + + operations = [ + ] diff --git a/sapl/materia/models.py b/sapl/materia/models.py index d2ecb70a3..aad491b01 100644 --- a/sapl/materia/models.py +++ b/sapl/materia/models.py @@ -10,7 +10,7 @@ from django.utils.translation import ugettext_lazy as _ from model_utils import Choices import reversion -from sapl.base.models import SEQUENCIA_NUMERACAO, Autor +from sapl.base.models import SEQUENCIA_NUMERACAO_PROTOCOLO, Autor from sapl.comissoes.models import Comissao from sapl.compilacao.models import (PerfilEstruturalTextoArticulado, TextoArticulado) @@ -18,7 +18,7 @@ from sapl.parlamentares.models import Parlamentar #from sapl.protocoloadm.models import Protocolo from sapl.utils import (RANGE_ANOS, YES_NO_CHOICES, SaplGenericForeignKey, SaplGenericRelation, restringe_tipos_de_arquivo_txt, - texto_upload_path) + texto_upload_path, get_settings_auth_user_model) EM_TRAMITACAO = [(1, 'Sim'), @@ -78,8 +78,37 @@ class TipoProposicao(models.Model): return self.descricao +class TipoMateriaManager(models.Manager): + + def reordene(self, exclude_pk=None): + tipos = self.get_queryset() + if exclude_pk: + tipos = tipos.exclude(pk=exclude_pk) + for sr, t in enumerate(tipos, 1): + t.sequencia_regimental = sr + t.save() + + def reposicione(self, pk, idx): + tipos = self.reordene(exclude_pk=pk) + + self.get_queryset( + ).filter( + sequencia_regimental__gte=idx + ).update( + sequencia_regimental=models.F('sequencia_regimental') + 1 + ) + + self.get_queryset( + ).filter( + pk=pk + ).update( + sequencia_regimental=idx + ) + + @reversion.register() class TipoMateriaLegislativa(models.Model): + objects = TipoMateriaManager() sigla = models.CharField(max_length=5, verbose_name=_('Sigla')) descricao = models.CharField(max_length=50, verbose_name=_('Descrição ')) # XXX o que é isso ? @@ -99,12 +128,19 @@ class TipoMateriaLegislativa(models.Model): max_length=1, blank=True, verbose_name=_('Sequência de numeração'), - choices=SEQUENCIA_NUMERACAO) + choices=SEQUENCIA_NUMERACAO_PROTOCOLO) + + sequencia_regimental = models.PositiveIntegerField( + default=0, + verbose_name=_('Sequência Regimental'), + help_text=_('A sequência regimental diz respeito ao que define ' + 'o regimento da Casa Legislativa sobre qual a ordem ' + 'de entrada das proposições nas Sessões Plenárias.')) class Meta: verbose_name = _('Tipo de Matéria Legislativa') verbose_name_plural = _('Tipos de Matérias Legislativas') - ordering = ['descricao'] + ordering = ['sequencia_regimental', 'descricao'] def __str__(self): return self.descricao @@ -461,6 +497,7 @@ class DocumentoAcessorio(models.Model): arquivo = models.FileField( blank=True, null=True, + max_length=255, upload_to=anexo_upload_path, verbose_name=_('Texto Integral'), validators=[restringe_tipos_de_arquivo_txt]) @@ -630,10 +667,15 @@ class Relatoria(models.Model): verbose_name_plural = _('Relatorias') def __str__(self): - return _('%(materia)s - %(tipo)s - %(data)s') % { + if self.tipo_fim_relatoria: + return _('%(materia)s - %(tipo)s - %(data)s') % { + 'materia': self.materia, + 'tipo': self.tipo_fim_relatoria, + 'data': self.data_designacao_relator.strftime("%d/%m/%Y")} + else: + return _('%(materia)s - %(data)s') % { 'materia': self.materia, - 'tipo': self.tipo_fim_relatoria, - 'data': self.data_designacao_relator} + 'data': self.data_designacao_relator.strftime("%d/%m/%Y")} @reversion.register() @@ -687,6 +729,9 @@ class Proposicao(models.Model): numero_proposicao = models.PositiveIntegerField( blank=True, null=True, verbose_name=_('Número')) + numero_materia_futuro = models.PositiveIntegerField( + blank=True, null=True, verbose_name=_('Número Matéria')) + hash_code = models.CharField(verbose_name=_('Código do Documento'), max_length=200, blank=True) @@ -917,7 +962,8 @@ class Tramitacao(models.Model): ('B', 'primeira_votacao', _('1ª Votação')), ('C', 'segunda_terceira_votacao', _('2ª e 3ª Votação')), ('D', 'deliberacao', _('Deliberação')), - ('E', 'primeira_segunda_votacao_urgencia', _('1ª e 2ª votações em regime de urgência')) + ('E', 'primeira_segunda_votacao_urgencia', _( + '1ª e 2ª votações em regime de urgência')) ) @@ -957,6 +1003,15 @@ class Tramitacao(models.Model): texto = models.TextField(verbose_name=_('Texto da Ação')) data_fim_prazo = models.DateField( blank=True, null=True, verbose_name=_('Data Fim Prazo')) + user = models.ForeignKey(get_settings_auth_user_model(), + verbose_name=_('Usuário'), + on_delete=models.PROTECT, + null=True, + blank=True) + ip = models.CharField(verbose_name=_('IP'), + max_length=30, + blank=True, + default='') class Meta: verbose_name = _('Tramitação') diff --git a/sapl/materia/tests/test_materia.py b/sapl/materia/tests/test_materia.py index 80f3f244f..1938d04f4 100644 --- a/sapl/materia/tests/test_materia.py +++ b/sapl/materia/tests/test_materia.py @@ -1,3 +1,4 @@ +from datetime import date from django.contrib.auth import get_user_model from django.contrib.contenttypes.models import ContentType from django.core.files.uploadedfile import SimpleUploadedFile @@ -14,10 +15,67 @@ from sapl.materia.models import (Anexada, Autoria, DespachoInicial, StatusTramitacao, TipoDocumento, TipoMateriaLegislativa, TipoProposicao, Tramitacao, UnidadeTramitacao) +from sapl.materia.forms import (TramitacaoForm, compara_tramitacoes_mat, + TramitacaoUpdateForm) from sapl.norma.models import (LegislacaoCitada, NormaJuridica, TipoNormaJuridica) from sapl.parlamentares.models import Legislatura -from sapl.utils import models_with_gr_for_model +from sapl.utils import models_with_gr_for_model, lista_anexados + + +@pytest.mark.django_db(transaction=False) +def test_lista_materias_anexadas(): + tipo_materia = mommy.make( + TipoMateriaLegislativa, + descricao="Tipo_Teste" + ) + regime_tramitacao = mommy.make( + RegimeTramitacao, + descricao="Regime_Teste" + ) + materia_principal = mommy.make( + MateriaLegislativa, + numero=20, + ano=2018, + data_apresentacao="2018-01-04", + regime_tramitacao=regime_tramitacao, + tipo=tipo_materia + ) + materia_anexada = mommy.make( + MateriaLegislativa, + numero=21, + ano=2019, + data_apresentacao="2019-05-04", + regime_tramitacao=regime_tramitacao, + tipo=tipo_materia + ) + materia_anexada_anexada = mommy.make( + MateriaLegislativa, + numero=22, + ano=2020, + data_apresentacao="2020-01-05", + regime_tramitacao=regime_tramitacao, + tipo=tipo_materia + ) + + mommy.make( + Anexada, + materia_principal=materia_principal, + materia_anexada=materia_anexada, + data_anexacao="2019-05-11" + ) + mommy.make( + Anexada, + materia_principal=materia_anexada, + materia_anexada=materia_anexada_anexada, + data_anexacao="2020-11-05" + ) + + lista = lista_anexados(materia_principal) + + assert len(lista) == 2 + assert lista[0] == materia_anexada + assert lista[1] == materia_anexada_anexada @pytest.mark.django_db(transaction=False) @@ -581,3 +639,178 @@ def test_numeracao_materia_legislativa_por_ano(admin_client): response_content = eval(response.content.decode('ascii')) esperado_outro_ano = eval('{"ano": "2010", "numero": 1}') assert response_content['numero'] == esperado_outro_ano['numero'] + + +@pytest.mark.django_db(transaction=False) +def test_tramitacoes_materias_anexadas(admin_client): + tipo_materia = mommy.make( + TipoMateriaLegislativa, + descricao="Tipo_Teste" + ) + materia_principal = mommy.make( + MateriaLegislativa, + ano=2018, + data_apresentacao="2018-01-04", + tipo=tipo_materia + ) + materia_anexada = mommy.make( + MateriaLegislativa, + ano=2019, + data_apresentacao="2019-05-04", + tipo=tipo_materia + ) + materia_anexada_anexada = mommy.make( + MateriaLegislativa, + ano=2020, + data_apresentacao="2020-01-05", + tipo=tipo_materia + ) + + mommy.make( + Anexada, + materia_principal=materia_principal, + materia_anexada=materia_anexada, + data_anexacao="2019-05-11" + ) + mommy.make( + Anexada, + materia_principal=materia_anexada, + materia_anexada=materia_anexada_anexada, + data_anexacao="2020-11-05" + ) + + + unidade_tramitacao_local_1 = make_unidade_tramitacao(descricao="Teste 1") + unidade_tramitacao_destino_1 = make_unidade_tramitacao(descricao="Teste 2") + unidade_tramitacao_destino_2 = make_unidade_tramitacao(descricao="Teste 3") + + status = mommy.make( + StatusTramitacao, + indicador='R') + + # Teste criação de Tramitacao + form = TramitacaoForm(data={}) + form.data = {'data_tramitacao':date(2019, 5, 6), + 'unidade_tramitacao_local':unidade_tramitacao_local_1.pk, + 'unidade_tramitacao_destino':unidade_tramitacao_destino_1.pk, + 'status':status.pk, + 'urgente': False, + 'texto': "Texto de teste"} + form.instance.materia_id=materia_principal.pk + + assert form.is_valid() + + tramitacao_principal = form.save() + tramitacao_anexada = materia_anexada.tramitacao_set.last() + tramitacao_anexada_anexada = materia_anexada_anexada.tramitacao_set.last() + + # Verifica se foram criadas as tramitações para as matérias anexadas e anexadas às anexadas + assert materia_principal.tramitacao_set.last() == tramitacao_principal + assert tramitacao_principal.materia.em_tramitacao == (tramitacao_principal.status.indicador != "F") + assert compara_tramitacoes_mat(tramitacao_principal, tramitacao_anexada) + assert MateriaLegislativa.objects.get(id=materia_anexada.pk).em_tramitacao \ + == (tramitacao_anexada.status.indicador != "F") + assert compara_tramitacoes_mat(tramitacao_anexada_anexada, tramitacao_principal) + assert MateriaLegislativa.objects.get(id=materia_anexada_anexada.pk).em_tramitacao \ + == (tramitacao_anexada_anexada.status.indicador != "F") + + + # Teste Edição de Tramitacao + form = TramitacaoUpdateForm(data={}) + # Alterando unidade_tramitacao_destino + form.data = {'data_tramitacao':tramitacao_principal.data_tramitacao, + 'unidade_tramitacao_local':tramitacao_principal.unidade_tramitacao_local.pk, + 'unidade_tramitacao_destino':unidade_tramitacao_destino_2.pk, + 'status':tramitacao_principal.status.pk, + 'urgente': tramitacao_principal.urgente, + 'texto': tramitacao_principal.texto} + form.instance = tramitacao_principal + + assert form.is_valid() + tramitacao_principal = form.save() + tramitacao_anexada = materia_anexada.tramitacao_set.last() + tramitacao_anexada_anexada = materia_anexada_anexada.tramitacao_set.last() + + assert tramitacao_principal.unidade_tramitacao_destino == unidade_tramitacao_destino_2 + assert tramitacao_anexada.unidade_tramitacao_destino == unidade_tramitacao_destino_2 + assert tramitacao_anexada_anexada.unidade_tramitacao_destino == unidade_tramitacao_destino_2 + + + # Teste Remoção de Tramitacao + url = reverse('sapl.materia:tramitacao_delete', + kwargs={'pk': tramitacao_principal.pk}) + response = admin_client.post(url, {'confirmar':'confirmar'} ,follow=True) + assert Tramitacao.objects.filter(id=tramitacao_principal.pk).count() == 0 + assert Tramitacao.objects.filter(id=tramitacao_anexada.pk).count() == 0 + assert Tramitacao.objects.filter(id=tramitacao_anexada_anexada.pk).count() == 0 + + + # Testes para quando as tramitações das anexadas divergem + form = TramitacaoForm(data={}) + form.data = {'data_tramitacao':date(2019, 5, 6), + 'unidade_tramitacao_local':unidade_tramitacao_local_1.pk, + 'unidade_tramitacao_destino':unidade_tramitacao_destino_1.pk, + 'status':status.pk, + 'urgente': False, + 'texto': "Texto de teste"} + form.instance.materia_id=materia_principal.pk + + assert form.is_valid() + + tramitacao_principal = form.save() + tramitacao_anexada = materia_anexada.tramitacao_set.last() + tramitacao_anexada_anexada = materia_anexada_anexada.tramitacao_set.last() + + form = TramitacaoUpdateForm(data={}) + # Alterando unidade_tramitacao_destino + form.data = {'data_tramitacao':tramitacao_anexada.data_tramitacao, + 'unidade_tramitacao_local':tramitacao_anexada.unidade_tramitacao_local.pk, + 'unidade_tramitacao_destino':unidade_tramitacao_destino_2.pk, + 'status':tramitacao_anexada.status.pk, + 'urgente': tramitacao_anexada.urgente, + 'texto': tramitacao_anexada.texto} + form.instance = tramitacao_anexada + + assert form.is_valid() + + tramitacao_anexada = form.save() + tramitacao_anexada_anexada = materia_anexada_anexada.tramitacao_set.last() + + assert tramitacao_principal.unidade_tramitacao_destino == unidade_tramitacao_destino_1 + assert tramitacao_anexada.unidade_tramitacao_destino == unidade_tramitacao_destino_2 + assert tramitacao_anexada_anexada.unidade_tramitacao_destino == unidade_tramitacao_destino_2 + + # Editando a tramitação principal, as tramitações anexadas não devem ser editadas + form = TramitacaoUpdateForm(data={}) + # Alterando o texto + form.data = {'data_tramitacao':tramitacao_principal.data_tramitacao, + 'unidade_tramitacao_local':tramitacao_principal.unidade_tramitacao_local.pk, + 'unidade_tramitacao_destino':tramitacao_principal.unidade_tramitacao_destino.pk, + 'status':tramitacao_principal.status.pk, + 'urgente': tramitacao_principal.urgente, + 'texto': "Testando a alteração"} + form.instance = tramitacao_principal + + assert form.is_valid() + tramitacao_principal = form.save() + tramitacao_anexada = materia_anexada.tramitacao_set.last() + tramitacao_anexada_anexada = materia_anexada_anexada.tramitacao_set.last() + + assert tramitacao_principal.texto == "Testando a alteração" + assert not tramitacao_anexada.texto == "Testando a alteração" + assert not tramitacao_anexada_anexada.texto == "Testando a alteração" + + # Removendo a tramitação pricipal, as tramitações anexadas não devem ser removidas, pois divergiram + url = reverse('sapl.materia:tramitacao_delete', + kwargs={'pk': tramitacao_principal.pk}) + response = admin_client.post(url, {'confirmar':'confirmar'} ,follow=True) + assert Tramitacao.objects.filter(id=tramitacao_principal.pk).count() == 0 + assert Tramitacao.objects.filter(id=tramitacao_anexada.pk).count() == 1 + assert Tramitacao.objects.filter(id=tramitacao_anexada_anexada.pk).count() == 1 + + # Removendo a tramitação anexada, a tramitação anexada à anexada deve ser removida + url = reverse('sapl.materia:tramitacao_delete', + kwargs={'pk': tramitacao_anexada.pk}) + response = admin_client.post(url, {'confirmar':'confirmar'} ,follow=True) + assert Tramitacao.objects.filter(id=tramitacao_anexada.pk).count() == 0 + assert Tramitacao.objects.filter(id=tramitacao_anexada_anexada.pk).count() == 0 \ No newline at end of file diff --git a/sapl/materia/tests/test_materia_form.py b/sapl/materia/tests/test_materia_form.py index b67ef2733..e29d89815 100644 --- a/sapl/materia/tests/test_materia_form.py +++ b/sapl/materia/tests/test_materia_form.py @@ -2,6 +2,7 @@ import pytest from django.utils.translation import ugettext as _ from model_mommy import mommy +from sapl.comissoes.models import Comissao, TipoComissao from sapl.materia import forms from sapl.materia.models import MateriaLegislativa, TipoMateriaLegislativa @@ -172,15 +173,23 @@ def test_valida_campos_obrigatorios_devolver_proposicao_form(): @pytest.mark.django_db(transaction=False) def test_valida_campos_obrigatorios_relatoria_form(): - form = forms.RelatoriaForm(data={}) + tipo_comissao = mommy.make(TipoComissao) + comissao = mommy.make(Comissao, + tipo=tipo_comissao, + nome='Comissao Teste', + sigla='T', + data_criacao='2016-03-21') + form = forms.RelatoriaForm(initial={'comissao':comissao}, data={}) assert not form.is_valid() errors = form.errors + assert errors['parlamentar'] == [_('Este campo é obrigatório.')] assert errors['data_designacao_relator'] == [_('Este campo é obrigatório.')] + assert errors['composicao'] == [_('Este campo é obrigatório.')] - assert len(errors) == 2 + assert len(errors) == 3 @pytest.mark.django_db(transaction=False) diff --git a/sapl/materia/urls.py b/sapl/materia/urls.py index e446e6a64..962dc2cff 100644 --- a/sapl/materia/urls.py +++ b/sapl/materia/urls.py @@ -8,6 +8,7 @@ from sapl.materia.views import (AcompanhamentoConfirmarView, CriarProtocoloMateriaView, DespachoInicialCrud, DocumentoAcessorioCrud, DocumentoAcessorioEmLoteView, + MateriaAnexadaEmLoteView, EtiquetaPesquisaView, FichaPesquisaView, FichaSelecionaView, ImpressosView, LegislacaoCitadaCrud, MateriaAssuntoCrud, @@ -24,7 +25,8 @@ from sapl.materia.views import (AcompanhamentoConfirmarView, TipoProposicaoCrud, TramitacaoCrud, TramitacaoEmLoteView, UnidadeTramitacaoCrud, proposicao_texto, recuperar_materia, - ExcluirTramitacaoEmLoteView, RetornarProposicao) + ExcluirTramitacaoEmLoteView, RetornarProposicao, + MateriaPesquisaSimplesView) from sapl.norma.views import NormaPesquisaSimplesView from sapl.protocoloadm.views import (FichaPesquisaAdmView, FichaSelecionaAdmView) @@ -48,6 +50,9 @@ urlpatterns_impressos = [ url(r'^materia/impressos/norma-pesquisa/$', NormaPesquisaSimplesView.as_view(), name='impressos_norma_pesquisa'), + url(r'^materia/impressos/materia-pesquisa/$', + MateriaPesquisaSimplesView.as_view(), + name='impressos_materia_pesquisa'), url(r'^materia/impressos/ficha-pesquisa-adm/$', FichaPesquisaAdmView.as_view(), name= 'impressos_ficha_pesquisa_adm'), @@ -93,6 +98,8 @@ urlpatterns_materia = [ url(r'^materia/acessorio-em-lote', DocumentoAcessorioEmLoteView.as_view(), name='acessorio_em_lote'), + url(r'^materia/(?P\d+)/anexada-em-lote', MateriaAnexadaEmLoteView.as_view(), + name='anexada_em_lote'), url(r'^materia/primeira-tramitacao-em-lote', PrimeiraTramitacaoEmLoteView.as_view(), name='primeira_tramitacao_em_lote'), diff --git a/sapl/materia/views.py b/sapl/materia/views.py index 505f04758..059ec38b2 100644 --- a/sapl/materia/views.py +++ b/sapl/materia/views.py @@ -1,17 +1,22 @@ -from datetime import datetime import logging +import os +import shutil +import tempfile +import weasyprint +import itertools + +from datetime import datetime from random import choice from string import ascii_letters, digits -from crispy_forms.helper import FormHelper from crispy_forms.layout import HTML from django.conf import settings from django.contrib import messages from django.contrib.auth.decorators import permission_required from django.contrib.auth.mixins import PermissionRequiredMixin -from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned +from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned, ValidationError from django.core.urlresolvers import reverse -from django.db.models import Max +from django.db.models import Max, Q from django.http import HttpResponse, JsonResponse from django.http.response import Http404, HttpResponseRedirect from django.shortcuts import get_object_or_404, redirect @@ -28,10 +33,11 @@ import sapl from sapl.base.email_utils import do_envia_email_confirmacao from sapl.base.models import Autor, CasaLegislativa, AppConfig as BaseAppConfig from sapl.base.signals import tramitacao_signal -from sapl.comissoes.models import Comissao, Participacao +from sapl.comissoes.models import Comissao, Participacao, Composicao from sapl.compilacao.models import (STATUS_TA_IMMUTABLE_RESTRICT, STATUS_TA_PRIVATE) from sapl.compilacao.views import IntegracaoTaView +from sapl.crispy_layout_mixin import SaplFormHelper from sapl.crispy_layout_mixin import SaplFormLayout, form_actions from sapl.crud.base import (RP_DETAIL, RP_LIST, Crud, CrudAux, MasterDetailCrud, @@ -41,16 +47,18 @@ from sapl.materia.forms import (AnexadaForm, AutoriaForm, ConfirmarProposicaoForm, DevolverProposicaoForm, LegislacaoCitadaForm, OrgaoForm, ProposicaoForm, TipoProposicaoForm, - TramitacaoForm, TramitacaoUpdateForm) + TramitacaoForm, TramitacaoUpdateForm, MateriaPesquisaSimplesForm) from sapl.norma.models import LegislacaoCitada from sapl.parlamentares.models import Legislatura from sapl.protocoloadm.models import Protocolo +from sapl.settings import MEDIA_ROOT from sapl.utils import (YES_NO_CHOICES, autor_label, autor_modal, SEPARADOR_HASH_PROPOSICAO, - gerar_hash_arquivo, get_base_url, + gerar_hash_arquivo, get_base_url, get_client_ip, get_mime_type_from_file_extension, montar_row_autor, - show_results_filter_set, mail_service_configured) + show_results_filter_set, mail_service_configured, lista_anexados) from .forms import (AcessorioEmLoteFilterSet, AcompanhamentoMateriaForm, + AnexadaEmLoteFilterSet, AdicionarVariasAutoriasFilterSet, DespachoInicialForm, DocumentoAcessorioForm, EtiquetaPesquisaForm, FichaPesquisaForm, FichaSelecionaForm, MateriaAssuntoForm, @@ -61,7 +69,7 @@ from .forms import (AcessorioEmLoteFilterSet, AcompanhamentoMateriaForm, filtra_tramitacao_destino, filtra_tramitacao_destino_and_status, filtra_tramitacao_status, - ExcluirTramitacaoEmLote) + ExcluirTramitacaoEmLote, compara_tramitacoes_mat) from .models import (AcompanhamentoMateria, Anexada, AssuntoMateria, Autoria, DespachoInicial, DocumentoAcessorio, MateriaAssunto, MateriaLegislativa, Numeracao, Orgao, Origem, Proposicao, @@ -74,9 +82,6 @@ AssuntoMateriaCrud = CrudAux.build(AssuntoMateria, 'assunto_materia') OrigemCrud = CrudAux.build(Origem, '') -TipoMateriaCrud = CrudAux.build( - TipoMateriaLegislativa, 'tipo_materia_legislativa') - RegimeTramitacaoCrud = CrudAux.build( RegimeTramitacao, 'regime_tramitacao') @@ -213,7 +218,8 @@ class CriarProtocoloMateriaView(CreateView): context['form'].fields['ano'].initial = protocolo.ano if protocolo: if protocolo.timestamp: - context['form'].fields['data_apresentacao'].initial = protocolo.timestamp.date() + context['form'].fields['data_apresentacao'].initial = protocolo.timestamp.date( + ) elif protocolo.timestamp_data_hora_manual: context['form'].fields['data_apresentacao'].initial = protocolo.timestamp_data_hora_manual.date() elif protocolo.data: @@ -327,7 +333,7 @@ def recuperar_materia(request): logger.debug("user=" + username + ". Tentando obter numeração da matéria.") numeracao = sapl.base.models.AppConfig.objects.last( - ).sequencia_numeracao + ).sequencia_numeracao_proposicao except AttributeError as e: logger.error("user=" + username + ". " + str(e) + " Numeracao da matéria definida como None.") @@ -812,8 +818,12 @@ class ProposicaoCrud(Crud): self.logger.debug("user=" + username + ". Tentando obter número do objeto MateriaLegislativa com " "atributos tipo={} e ano={}." .format(p.tipo.tipo_conteudo_related, p.ano)) - numero = MateriaLegislativa.objects.filter(tipo=p.tipo.tipo_conteudo_related, - ano=p.ano).last().numero + 1 + + if p.numero_materia_futuro: + numero = p.numero_materia_futuro + else: + numero = MateriaLegislativa.objects.filter(tipo=p.tipo.tipo_conteudo_related, + ano=p.ano).last().numero + 1 messages.success(request, _( '%s : nº %s de %s
    Atenção! Este número é apenas um provável ' 'número que pode não corresponder com a realidade' @@ -1101,45 +1111,13 @@ class RelatoriaCrud(MasterDetailCrud): class CreateView(MasterDetailCrud.CreateView): form_class = RelatoriaForm + layout_key = None logger = logging.getLogger(__name__) - def get_context_data(self, **kwargs): - context = super().get_context_data(**kwargs) - username = self.request.user.username - - try: - self.logger.debug("user=" + username + ". Tentando obter objeto Comissao de pk={}.".format( - context['form'].initial['comissao'])) - comissao = Comissao.objects.get( - pk=context['form'].initial['comissao']) - except: - self.logger.error("user=" + username + ". Objeto Comissão de pk={} não encontrado.".format( - context['form'].initial['comissao'])) - pass - - else: - self.logger.info("user=" + username + ". Objeto Comissao de pk={} obtido com sucesso.".format( - context['form'].initial['comissao'])) - composicao = comissao.composicao_set.order_by( - '-periodo__data_inicio').first() - participacao = Participacao.objects.filter( - composicao=composicao) - - parlamentares = [] - parlamentares.append(['', '---------']) - for p in participacao: - if p.titular: - parlamentares.append( - [p.parlamentar.id, p.parlamentar.nome_parlamentar]) - context['form'].fields['parlamentar'].choices = parlamentares - - return context - def get_initial(self): materia = MateriaLegislativa.objects.get(id=self.kwargs['pk']) - loc_atual = Tramitacao.objects.filter( - materia=materia).last() + loc_atual = Tramitacao.objects.filter(materia=materia).last() if loc_atual is None: localizacao = 0 @@ -1154,37 +1132,28 @@ class RelatoriaCrud(MasterDetailCrud): class UpdateView(MasterDetailCrud.UpdateView): form_class = RelatoriaForm - + layout_key = None logger = logging.getLogger(__name__) - def get_context_data(self, **kwargs): - - context = super().get_context_data(**kwargs) - username = self.request.user.username - - try: - self.logger.debug("user=" + username + ". Tentando obter objeto Comissao de pk={}.".format( - context['form'].initial['comissao'])) - comissao = Comissao.objects.get( - pk=context['form'].initial['comissao']) - except ObjectDoesNotExist: - self.logger.error("user=" + username + ". Objeto Comissão de pk={} não encontrado.".format( - context['form'].initial['comissao'])) - pass - else: - self.logger.info("user=" + username + ". Objeto Comissao de pk={} obtido com sucesso.".format( - context['form'].initial['comissao'])) - composicao = comissao.composicao_set.order_by( - '-periodo__data_inicio').first() - participacao = Participacao.objects.filter( - composicao=composicao) - - parlamentares = [[p.parlamentar.id, p.parlamentar.nome_parlamentar] for - p in participacao if p.titular] - - context['form'].fields['parlamentar'].choices = parlamentares - - return context + def get_initial(self): + relatoria = Relatoria.objects.get(id=self.kwargs['pk']) + parlamentar = relatoria.parlamentar + comissao = relatoria.comissao + composicoes = [p.composicao for p in + Participacao.objects.filter( + parlamentar=parlamentar, + composicao__comissao=comissao)] + data_designacao = relatoria.data_designacao_relator + composicao = '' + for c in composicoes: + data_inicial = c.periodo.data_inicio + data_fim = c.periodo.data_fim if c.periodo.data_fim else timezone.now().date() + if data_inicial <= data_designacao <= data_fim: + composicao = c.id + break + return {'comissao': relatoria.comissao.id, + 'parlamentar': relatoria.parlamentar.id, + 'composicao': composicao} class TramitacaoCrud(MasterDetailCrud): @@ -1220,6 +1189,8 @@ class TramitacaoCrud(MasterDetailCrud): else: initial['unidade_tramitacao_local'] = '' initial['data_tramitacao'] = timezone.now().date() + initial['ip'] = get_client_ip(self.request) + initial['user'] = self.request.user return initial def get_context_data(self, **kwargs): @@ -1232,6 +1203,7 @@ class TramitacaoCrud(MasterDetailCrud): '-timestamp', '-id').first() + #TODO: Esta checagem foi inserida na issue #2027, mas é mesmo necessária? if ultima_tramitacao: if ultima_tramitacao.unidade_tramitacao_destino: context['form'].fields[ @@ -1245,6 +1217,15 @@ class TramitacaoCrud(MasterDetailCrud): ' da última tramitação não pode ser vazia!') messages.add_message(self.request, messages.ERROR, msg) + primeira_tramitacao = not(Tramitacao.objects.filter( + materia_id=int(kwargs['root_pk'])).exists()) + + # Se não for a primeira tramitação daquela matéria, o campo + # não pode ser modificado + if not primeira_tramitacao: + context['form'].fields[ + 'unidade_tramitacao_local'].widget.attrs['disabled'] = True + return context def form_valid(self, form): @@ -1252,12 +1233,6 @@ class TramitacaoCrud(MasterDetailCrud): self.object = form.save() username = self.request.user.username - if form.instance.status.indicador == 'F': - form.instance.materia.em_tramitacao = False - else: - form.instance.materia.em_tramitacao = True - form.instance.materia.save() - try: self.logger.debug("user=" + username + ". Tentando enviar Tramitacao (sender={}, post={}, request={})." .format(Tramitacao, self.object, self.request)) @@ -1265,7 +1240,6 @@ class TramitacaoCrud(MasterDetailCrud): post=self.object, request=self.request) except Exception as e: - # TODO log error msg = _('Tramitação criada, mas e-mail de acompanhamento ' 'de matéria não enviado. Há problemas na configuração ' 'do e-mail.') @@ -1282,16 +1256,16 @@ class TramitacaoCrud(MasterDetailCrud): layout_key = 'TramitacaoUpdate' + def get_initial(self): + initial = super(UpdateView, self).get_initial() + initial['ip'] = get_client_ip(self.request) + initial['user'] = self.request.user + return initial + def form_valid(self, form): self.object = form.save() username = self.request.user.username - if form.instance.status.indicador == 'F': - form.instance.materia.em_tramitacao = False - else: - form.instance.materia.em_tramitacao = True - form.instance.materia.save() - try: self.logger.debug("user=" + username + ". Tentando enviar Tramitacao (sender={}, post={}, request={}" .format(Tramitacao, self.object, self.request)) @@ -1299,7 +1273,6 @@ class TramitacaoCrud(MasterDetailCrud): post=self.object, request=self.request) except Exception: - # TODO log error msg = _('Tramitação atualizada, mas e-mail de acompanhamento ' 'de matéria não enviado. Há problemas na configuração ' 'do e-mail.') @@ -1325,18 +1298,17 @@ class TramitacaoCrud(MasterDetailCrud): def delete(self, request, *args, **kwargs): tramitacao = Tramitacao.objects.get(id=self.kwargs['pk']) - materia = MateriaLegislativa.objects.get(id=tramitacao.materia.id) + materia = tramitacao.materia url = reverse('sapl.materia:tramitacao_list', - kwargs={'pk': tramitacao.materia.id}) - + kwargs={'pk': materia.id}) + ultima_tramitacao = materia.tramitacao_set.order_by( '-data_tramitacao', '-timestamp', '-id').first() - username = request.user.username - if tramitacao.pk != ultima_tramitacao.pk: + username = request.user.username self.logger.error("user=" + username + ". Não é possível deletar a tramitação de pk={}. " "Somente a última tramitação (pk={}) pode ser deletada!." .format(tramitacao.pk, ultima_tramitacao.pk)) @@ -1344,13 +1316,28 @@ class TramitacaoCrud(MasterDetailCrud): messages.add_message(request, messages.ERROR, msg) return HttpResponseRedirect(url) else: - tramitacao.delete() + tramitacoes_deletar = [tramitacao.id] + mat_anexadas = lista_anexados(materia) + for ma in mat_anexadas: + tram_anexada = ma.tramitacao_set.last() + if compara_tramitacoes_mat(tram_anexada, tramitacao): + tramitacoes_deletar.append(tram_anexada.id) + Tramitacao.objects.filter(id__in=tramitacoes_deletar).delete() return HttpResponseRedirect(url) + class DetailView(MasterDetailCrud.DetailView): + + template_name = "materia/tramitacao_detail.html" + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + context['user'] = self.request.user + return context + def montar_helper_documento_acessorio(self): autor_row = montar_row_autor('autor') - self.helper = FormHelper() + self.helper = SaplFormHelper() self.helper.layout = SaplFormLayout(*self.get_layout()) # Adiciona o novo campo 'autor' e mecanismo de busca @@ -1617,6 +1604,20 @@ class MateriaLegislativaCrud(Crud): form_class = MateriaLegislativaForm + def form_valid(self, form): + self.object = form.save() + username = self.request.user.username + + if Anexada.objects.filter(materia_principal=self.kwargs['pk']).exists(): + materia = MateriaLegislativa.objects.get(pk=self.kwargs['pk']) + anexadas = lista_anexados(materia) + + for anexada in anexadas: + anexada.em_tramitacao = True if form.instance.em_tramitacao else False + anexada.save() + + return super().form_valid(form) + @property def cancel_url(self): return self.search_url @@ -1917,19 +1918,50 @@ class AcompanhamentoMateriaView(CreateView): confirmar o acompanhamento desta matéria.') messages.add_message(request, messages.SUCCESS, msg) + # Se o elemento existir e o email não foi confirmado: + # gerar novo hash e reenviar mensagem de email + elif not acompanhar[0].confirmado: + acompanhar = acompanhar[0] + acompanhar.hash = hash_txt + acompanhar.save() + + base_url = get_base_url(request) + + destinatario = AcompanhamentoMateria.objects.get( + materia=materia, + email=email, + confirmado=False + ) + + casa = CasaLegislativa.objects.first() + + do_envia_email_confirmacao(base_url, + casa, + "materia", + materia, + destinatario) + + self.logger.debug('user=' + usuario.username + '. Foi enviado um e-mail de confirmação. Confira sua caixa \ + de mensagens e clique no link que nós enviamos para \ + confirmar o acompanhamento desta matéria.') + + msg = _('Foi enviado um e-mail de confirmação. Confira sua caixa \ + de mensagens e clique no link que nós enviamos para \ + confirmar o acompanhamento desta matéria.') + messages.add_message(request, messages.SUCCESS, msg) + # Caso esse Acompanhamento já exista # avisa ao usuário que essa matéria já está sendo acompanhada else: self.logger.debug("user=" + usuario.username + ". Este e-mail já está acompanhando essa matéria.") msg = _('Este e-mail já está acompanhando essa matéria.') - messages.add_message(request, messages.INFO, msg) + messages.add_message(request, messages.ERROR, msg) return self.render_to_response( {'form': form, - 'materia': materia, - 'error': _('Essa matéria já está\ - sendo acompanhada por este e-mail.')}) + 'materia': materia + }) return HttpResponseRedirect(self.get_success_url()) else: return self.render_to_response( @@ -1945,6 +1977,7 @@ class DocumentoAcessorioEmLoteView(PermissionRequiredMixin, FilterView): filterset_class = AcessorioEmLoteFilterSet template_name = 'materia/em_lote/acessorio.html' permission_required = ('materia.add_documentoacessorio',) + logger = logging.getLogger(__name__) def get_context_data(self, **kwargs): context = super(DocumentoAcessorioEmLoteView, @@ -1966,6 +1999,7 @@ class DocumentoAcessorioEmLoteView(PermissionRequiredMixin, FilterView): return context def post(self, request, *args, **kwargs): + username = request.user.username marcadas = request.POST.getlist('materia_id') if len(marcadas) == 0: @@ -1977,22 +2011,182 @@ class DocumentoAcessorioEmLoteView(PermissionRequiredMixin, FilterView): tz = timezone.get_current_timezone() + if len(request.POST['autor']) > 50: + msg = _('Autor tem que ter menos do que 50 caracteres.') + messages.add_message(request, messages.ERROR, msg) + return self.get(request, self.kwargs) + + tmp_name = os.path.join(MEDIA_ROOT, request.FILES['arquivo'].name) + with open(tmp_name, 'wb') as destination: + for chunk in request.FILES['arquivo'].chunks(): + destination.write(chunk) + try: + doc_data = tz.localize(datetime.strptime( + request.POST['data'], "%d/%m/%Y")) + except Exception as e: + msg = _('Formato da data incorreto. O formato deve ser da forma dd/mm/aaaa.') + messages.add_message(request, messages.ERROR, msg) + self.logger.error("User={}. {}. Data inserida: {}".format(username, str(msg), request.POST['data'])) + os.remove(tmp_name) + return self.get(request, self.kwargs) + for materia_id in marcadas: doc = DocumentoAcessorio() doc.materia_id = materia_id doc.tipo = tipo - doc.arquivo = request.FILES['arquivo'] doc.nome = request.POST['nome'] - doc.data = tz.localize(datetime.strptime( - request.POST['data'], "%d/%m/%Y")) + doc.data = doc_data doc.autor = request.POST['autor'] doc.ementa = request.POST['ementa'] + doc.arquivo.name = tmp_name + try: + doc.clean_fields() + except ValidationError as e: + for m in [ '%s: %s' % (DocumentoAcessorio()._meta.get_field(k).verbose_name, '
    '.join(v)) + for k,v in e.message_dict.items() ]: + # Insere as mensagens de erro no formato: + # 'verbose_name do nome do campo': 'mensagem de erro' + messages.add_message(request, messages.ERROR, m) + self.logger.error("User={}. {}. Nome do arquivo: {}.".format(username, str(msg), request.FILES['arquivo'].name)) + os.remove(tmp_name) + return self.get(request, self.kwargs) doc.save() + diretorio = os.path.join(MEDIA_ROOT, + 'sapl/public/documentoacessorio', + str(doc_data.year), + str(doc.id)) + if not os.path.exists(diretorio): + os.makedirs(diretorio) + file_path = os.path.join(diretorio, + request.FILES['arquivo'].name) + shutil.copy2(tmp_name, file_path) + doc.arquivo.name = file_path.split(MEDIA_ROOT + "/")[1] # Retira MEDIA_ROOT do nome + doc.save() + os.remove(tmp_name) + msg = _('Documento(s) criado(s).') messages.add_message(request, messages.SUCCESS, msg) return self.get(request, self.kwargs) +class MateriaAnexadaEmLoteView(PermissionRequiredMixin, FilterView): + filterset_class = AnexadaEmLoteFilterSet + template_name = 'materia/em_lote/anexada.html' + permission_required = ('materia.add_documentoacessorio',) + + def get_context_data(self, **kwargs): + context = super(MateriaAnexadaEmLoteView, + self).get_context_data(**kwargs) + + context['root_pk'] = self.kwargs['pk'] + + context['subnav_template_name'] = 'materia/subnav.yaml' + + context['title'] = _('Matérias Anexadas em Lote') + + # Verifica se os campos foram preenchidos + if not self.request.GET.get('tipo', " "): + msg =_('Por favor, selecione um tipo de matéria.') + messages.add_message(self.request, messages.ERROR, msg) + + if not self.request.GET.get('data_apresentacao_0', " ") or not self.request.GET.get('data_apresentacao_1', " "): + msg =_('Por favor, preencha as datas.') + messages.add_message(self.request, messages.ERROR, msg) + + return context + + if not self.request.GET.get('data_apresentacao_0', " ") or not self.request.GET.get('data_apresentacao_1', " "): + msg =_('Por favor, preencha as datas.') + messages.add_message(self.request, messages.ERROR, msg) + return context + + qr = self.request.GET.copy() + context['object_list'] = context['object_list'].order_by( + 'numero', '-ano') + principal = MateriaLegislativa.objects.get(pk=self.kwargs['pk']) + not_list = [self.kwargs['pk']] + \ + [m for m in principal.materia_principal_set.all().values_list('materia_anexada_id', flat=True)] + context['object_list'] = context['object_list'].exclude(pk__in=not_list) + + context['temp_object_list'] = context['object_list'] + context['object_list'] = [] + for obj in context['temp_object_list']: + materia_anexada = obj + ciclico = False + anexadas_anexada = Anexada.objects.filter( + materia_principal = materia_anexada + ) + + while anexadas_anexada and not ciclico: + anexadas = [] + + for anexa in anexadas_anexada: + + if principal == anexa.materia_anexada: + ciclico = True + else: + for a in Anexada.objects.filter(materia_principal=anexa.materia_anexada): + anexadas.append(a) + + anexadas_anexada = anexadas + + if not ciclico: + context['object_list'].append(obj) + + context['numero_res'] = len(context['object_list']) + + context['filter_url'] = ('&' + qr.urlencode()) if len(qr) > 0 else '' + + context['show_results'] = show_results_filter_set(qr) + + return context + + def post(self, request, *args, **kwargs): + marcadas = request.POST.getlist('materia_id') + + data_anexacao = datetime.strptime( + request.POST['data_anexacao'], "%d/%m/%Y").date() + + if request.POST['data_desanexacao'] == '': + data_desanexacao = None + v_data_desanexacao = data_anexacao + else: + data_desanexacao = datetime.strptime( + request.POST['data_desanexacao'], "%d/%m/%Y").date() + v_data_desanexacao = data_desanexacao + + if len(marcadas) == 0: + msg = _('Nenhuma máteria foi selecionada.') + messages.add_message(request, messages.ERROR, msg) + + if data_anexacao > v_data_desanexacao: + msg = _('Data de anexação posterior à data de desanexação.') + messages.add_message(request, messages.ERROR, msg) + + return self.get(request, self.kwargs) + + if data_anexacao > v_data_desanexacao: + msg = _('Data de anexação posterior à data de desanexação.') + messages.add_message(request, messages.ERROR, msg) + return self.get(request, self.kwargs) + + principal = MateriaLegislativa.objects.get(pk=kwargs['pk']) + for materia in MateriaLegislativa.objects.filter(id__in=marcadas): + + anexada = Anexada() + anexada.materia_principal = principal + anexada.materia_anexada = materia + anexada.data_anexacao = data_anexacao + anexada.data_desanexacao = data_desanexacao + anexada.save() + + msg = _('Matéria(s) anexada(s).') + messages.add_message(request, messages.SUCCESS, msg) + + sucess_url = reverse('sapl_index') + 'materia/' + kwargs['pk'] + '/anexada' + return HttpResponseRedirect(sucess_url) + + class PrimeiraTramitacaoEmLoteView(PermissionRequiredMixin, FilterView): filterset_class = PrimeiraTramitacaoEmLoteFilterSet template_name = 'materia/em_lote/tramitacao.html' @@ -2091,8 +2285,18 @@ class PrimeiraTramitacaoEmLoteView(PermissionRequiredMixin, FilterView): # issue https://github.com/interlegis/sapl/issues/1123 # TODO: usar Form urgente = request.POST['urgente'] == 'True' - flag_error = False - for materia_id in marcadas: + flag_error = False + + materias_principais = [m for m in MateriaLegislativa.objects.filter(id__in=marcadas)] + materias_anexadas = [m.anexadas.all() for m in MateriaLegislativa.objects.filter(id__in=marcadas) if m.anexadas.all()] + materias_anexadas = list(itertools.chain.from_iterable(materias_anexadas)) + tramitacao_local = int(request.POST['unidade_tramitacao_local']) + materias_anexadas = list(filter(lambda ma : not ma.tramitacao_set.all() or \ + ma.tramitacao_set.last().unidade_tramitacao_destino.id == tramitacao_local, + materias_anexadas)) + materias = set(materias_principais + materias_anexadas) + + for materia in materias: try: data_tramitacao = tz.localize(datetime.strptime( request.POST['data_tramitacao'], "%d/%m/%Y")) @@ -2101,8 +2305,10 @@ class PrimeiraTramitacaoEmLoteView(PermissionRequiredMixin, FilterView): messages.add_message(request, messages.ERROR, msg) return self.get(request, self.kwargs) + user = request.user + ip = get_client_ip(request) t = Tramitacao( - materia_id=materia_id, + materia=materia, data_tramitacao=data_tramitacao, data_encaminhamento=data_encaminhamento, data_fim_prazo=data_fim_prazo, @@ -2113,7 +2319,9 @@ class PrimeiraTramitacaoEmLoteView(PermissionRequiredMixin, FilterView): urgente=urgente, status_id=request.POST['status'], turno=request.POST['turno'], - texto=request.POST['texto'] + texto=request.POST['texto'], + user=user, + ip=ip ) t.save() try: @@ -2136,7 +2344,7 @@ class PrimeiraTramitacaoEmLoteView(PermissionRequiredMixin, FilterView): status = StatusTramitacao.objects.get(id=request.POST['status']) - for materia in MateriaLegislativa.objects.filter(id__in=marcadas): + for materia in materias: if status.indicador == 'F': materia.em_tramitacao = False elif self.primeira_tramitacao: @@ -2183,13 +2391,11 @@ class ImpressosView(PermissionRequiredMixin, TemplateView): def gerar_pdf_impressos(request, context, template_name): template = loader.get_template(template_name) html = template.render(context, request) - - pdf = weasyprint.HTML(string=html, base_url=request.build_absolute_uri() - ).write_pdf() + pdf = weasyprint.HTML( + string=html, base_url=request.build_absolute_uri()).write_pdf() response = HttpResponse(pdf, content_type='application/pdf') - response['Content-Disposition'] = ( - 'inline; filename="relatorio_impressos.pdf"') + response['Content-Disposition'] = 'inline; filename="relatorio_impressos.pdf"' response['Content-Transfer-Encoding'] = 'binary' return response @@ -2197,7 +2403,7 @@ def gerar_pdf_impressos(request, context, template_name): class EtiquetaPesquisaView(PermissionRequiredMixin, FormView): form_class = EtiquetaPesquisaForm - template_name = 'materia/impressos/etiqueta.html' + template_name = 'materia/impressos/impressos_form.html' permission_required = ('materia.can_access_impressos', ) def form_valid(self, form): @@ -2238,7 +2444,7 @@ class EtiquetaPesquisaView(PermissionRequiredMixin, FormView): class FichaPesquisaView(PermissionRequiredMixin, FormView): form_class = FichaPesquisaForm - template_name = 'materia/impressos/ficha.html' + template_name = 'materia/impressos/impressos_form.html' permission_required = ('materia.can_access_impressos', ) def form_valid(self, form): @@ -2256,7 +2462,7 @@ class FichaPesquisaView(PermissionRequiredMixin, FormView): class FichaSelecionaView(PermissionRequiredMixin, FormView): logger = logging.getLogger(__name__) form_class = FichaSelecionaForm - template_name = 'materia/impressos/ficha_seleciona.html' + template_name = 'materia/impressos/impressos_form.html' permission_required = ('materia.can_access_impressos', ) def get_context_data(self, **kwargs): @@ -2353,3 +2559,76 @@ class ExcluirTramitacaoEmLoteView(PermissionRequiredMixin, FormView): tramitacao.delete() return redirect(self.get_success_url()) + + +class MateriaPesquisaSimplesView(PermissionRequiredMixin, FormView): + form_class = MateriaPesquisaSimplesForm + template_name = 'materia/impressos/impressos_form.html' + permission_required = ('materia.can_access_impressos', ) + + def form_valid(self, form): + template_materia = 'materia/impressos/materias_pdf.html' + + kwargs = {} + if form.cleaned_data.get('tipo_materia'): + kwargs.update({'tipo': form.cleaned_data['tipo_materia']}) + + if form.cleaned_data.get('data_inicial'): + kwargs.update({'data__gte': form.cleaned_data['data_inicial'], + 'data__lte': form.cleaned_data['data_final']}) + + materias = MateriaLegislativa.objects.filter( + **kwargs).order_by('-numero', 'ano') + + quantidade_materias = materias.count() + materias = materias[:2000] if quantidade_materias > 2000 else materias + + context = {'quantidade': quantidade_materias, + 'titulo': form.cleaned_data['titulo'], + 'materias': materias} + + return gerar_pdf_impressos(self.request, context, template_materia) + + +class TipoMateriaCrud(CrudAux): + model = TipoMateriaLegislativa + + class DetailView(CrudAux.DetailView): + layout_key = 'TipoMateriaLegislativaDetail' + + class DeleteView(CrudAux.DeleteView): + def delete(self, request, *args, **kwargs): + d = CrudAux.DeleteView.delete(self, request, *args, **kwargs) + TipoMateriaLegislativa.objects.reordene() + return d + + class ListView(CrudAux.ListView): + paginate_by = None + layout_key = 'TipoMateriaLegislativaDetail' + template_name = "materia/tipomaterialegislativa_list.html" + + def hook_sigla(self, obj, default, url): + return '{}'.format( + url, obj.id, obj.sigla), '' + + def get(self, request, *args, **kwargs): + if TipoMateriaLegislativa.objects.filter( + sequencia_regimental=0).exists(): + TipoMateriaLegislativa.objects.reordene() + return CrudAux.ListView.get(self, request, *args, **kwargs) + + class CreateView(CrudAux.CreateView): + + def form_valid(self, form): + fv = super().form_valid(form) + + if not TipoMateriaLegislativa.objects.exclude( + sequencia_regimental=0).exists(): + TipoMateriaLegislativa.objects.reordene() + else: + sr__max = TipoMateriaLegislativa.objects.all().aggregate( + Max('sequencia_regimental')) + self.object.sequencia_regimental = sr__max['sequencia_regimental__max'] + 1 + self.object.save() + + return fv diff --git a/sapl/norma/forms.py b/sapl/norma/forms.py index bf676c242..5a9e1b3f6 100644 --- a/sapl/norma/forms.py +++ b/sapl/norma/forms.py @@ -1,7 +1,7 @@ import logging -from crispy_forms.helper import FormHelper +from sapl.crispy_layout_mixin import SaplFormHelper from crispy_forms.layout import Fieldset, Layout from django import forms from django.core.exceptions import ObjectDoesNotExist, ValidationError @@ -17,8 +17,8 @@ from sapl.crispy_layout_mixin import form_actions, to_row from sapl.materia.forms import choice_anos_com_materias from sapl.materia.models import MateriaLegislativa, TipoMateriaLegislativa from sapl.settings import MAX_DOC_UPLOAD_SIZE -from sapl.utils import NormaPesquisaOrderingFilter, RangeWidgetOverride,\ - choice_anos_com_normas, FilterOverridesMetaMixin +from sapl.utils import NormaPesquisaOrderingFilter, RangeWidgetOverride, \ + choice_anos_com_normas, FilterOverridesMetaMixin, FileFieldCheckMixin from .models import (AnexoNormaJuridica, AssuntoNorma, NormaJuridica, NormaRelacionada, TipoNormaJuridica, AutoriaNorma) @@ -71,7 +71,7 @@ class NormaFilterSet(django_filters.FilterSet): row4 = to_row([('data_vigencia', 12)]) row5 = to_row([('o', 6), ('indexacao', 6)]) - self.form.helper = FormHelper() + self.form.helper = SaplFormHelper() self.form.helper.form_method = 'GET' self.form.helper.layout = Layout( Fieldset(_('Pesquisa de Norma'), @@ -88,7 +88,7 @@ class NormaFilterSet(django_filters.FilterSet): return queryset.filter(q) -class NormaJuridicaForm(ModelForm): +class NormaJuridicaForm(FileFieldCheckMixin, ModelForm): # Campos de MateriaLegislativa tipo_materia = forms.ModelChoiceField( @@ -200,6 +200,8 @@ class NormaJuridicaForm(ModelForm): return cleaned_data def clean_texto_integral(self): + super(NormaJuridicaForm, self).clean() + texto_integral = self.cleaned_data.get('texto_integral', False) if texto_integral and texto_integral.size > MAX_DOC_UPLOAD_SIZE: max_size = str(MAX_DOC_UPLOAD_SIZE / (1024 * 1024)) @@ -238,7 +240,7 @@ class AutoriaNormaForm(ModelForm): ('autor', 4), ('primeiro_autor', 4)]) - self.helper = FormHelper() + self.helper = SaplFormHelper() self.helper.layout = Layout( Fieldset(_('Autoria'), row1, 'data_relativa', form_actions(label='Salvar'))) @@ -269,7 +271,7 @@ class AutoriaNormaForm(ModelForm): return cd -class AnexoNormaJuridicaForm(ModelForm): +class AnexoNormaJuridicaForm(FileFieldCheckMixin, ModelForm): class Meta: model = AnexoNormaJuridica fields = ['norma', 'anexo_arquivo', 'assunto_anexo'] @@ -296,11 +298,10 @@ class AnexoNormaJuridicaForm(ModelForm): def save(self, commit=False): anexo = self.instance anexo.ano = self.cleaned_data['norma'].ano - anexo = super(AnexoNormaJuridicaForm, self).save(commit=True) anexo.norma = self.cleaned_data['norma'] anexo.assunto_anexo = self.cleaned_data['assunto_anexo'] anexo.anexo_arquivo = self.cleaned_data['anexo_arquivo'] - anexo.save() + anexo = super(AnexoNormaJuridicaForm, self).save(commit=True) return anexo @@ -390,7 +391,7 @@ class NormaPesquisaSimplesForm(forms.Form): logger = logging.getLogger(__name__) def __init__(self, *args, **kwargs): - super(NormaPesquisaSimplesForm, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) row1 = to_row( [('tipo_norma', 6), @@ -400,39 +401,33 @@ class NormaPesquisaSimplesForm(forms.Form): row2 = to_row( [('titulo', 12)]) - self.helper = FormHelper() + self.helper = SaplFormHelper() self.helper.layout = Layout( Fieldset( - ('Índice de Normas'), + 'Índice de Normas', row1, row2, form_actions(label='Pesquisar') ) ) def clean(self): - super(NormaPesquisaSimplesForm, self).clean() + super().clean() if not self.is_valid(): return self.cleaned_data cleaned_data = self.cleaned_data - data_inicial = cleaned_data['data_inicial'] data_final = cleaned_data['data_final'] - if (data_inicial and data_final and - data_inicial > data_final): - self.logger.error("Data Final ({}) menor que a Data Inicial ({}).".format( - data_final, data_inicial)) - raise ValidationError(_( - 'A Data Final não pode ser menor que a Data Inicial')) - else: - condicao1 = data_inicial and not data_final - condicao2 = not data_inicial and data_final - if condicao1 or condicao2: + if data_inicial or data_final: + if not(data_inicial and data_final): self.logger.error("Caso pesquise por data, os campos de Data Inicial e " "Data Final devem ser preenchidos obrigatoriamente") - raise ValidationError(_('Caso pesquise por data, os campos de Data Inicial e ' + + raise ValidationError(_('Caso pesquise por data, os campos de Data Inicial e ' 'Data Final devem ser preenchidos obrigatoriamente')) + elif data_inicial > data_final: + self.logger.error("Data Final ({}) menor que a Data Inicial ({}).".format(data_final, data_inicial)) + raise ValidationError(_('A Data Final não pode ser menor que a Data Inicial')) return cleaned_data diff --git a/sapl/norma/migrations/0023_auto_20190219_1535.py b/sapl/norma/migrations/0023_auto_20190219_1535.py new file mode 100644 index 000000000..badf71a81 --- /dev/null +++ b/sapl/norma/migrations/0023_auto_20190219_1535.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.20 on 2019-02-19 18:35 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('norma', '0022_auto_20190108_1606'), + ] + + operations = [ + migrations.AlterModelOptions( + name='normarelacionada', + options={'ordering': ('norma_principal__ano', 'norma_relacionada__ano'), 'verbose_name': 'Norma Relacionada', 'verbose_name_plural': 'Normas Relacionadas'}, + ), + ] diff --git a/sapl/norma/migrations/0024_auto_20190425_0917.py b/sapl/norma/migrations/0024_auto_20190425_0917.py new file mode 100644 index 000000000..078bae0ee --- /dev/null +++ b/sapl/norma/migrations/0024_auto_20190425_0917.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.20 on 2019-04-25 12:17 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('norma', '0023_auto_20190219_1535'), + ] + + operations = [ + migrations.AlterModelOptions( + name='normarelacionada', + options={'ordering': ('norma_principal__data', 'norma_relacionada__data'), 'verbose_name': 'Norma Relacionada', 'verbose_name_plural': 'Normas Relacionadas'}, + ), + ] diff --git a/sapl/norma/models.py b/sapl/norma/models.py index eef004d4f..6b3a18a1f 100644 --- a/sapl/norma/models.py +++ b/sapl/norma/models.py @@ -145,9 +145,11 @@ class NormaJuridica(models.Model): def get_normas_relacionadas(self): principais = NormaRelacionada.objects.filter( - norma_principal=self.id) + norma_principal=self.id).order_by('norma_principal__data', + 'norma_relacionada__data') relacionadas = NormaRelacionada.objects.filter( - norma_relacionada=self.id) + norma_relacionada=self.id).order_by('norma_principal__data', + 'norma_relacionada__data') return (principais, relacionadas) def get_anexos_norma_juridica(self): @@ -311,6 +313,7 @@ class NormaRelacionada(models.Model): class Meta: verbose_name = _('Norma Relacionada') verbose_name_plural = _('Normas Relacionadas') + ordering = ('norma_principal__data', 'norma_relacionada__data') def __str__(self): return _('Principal: %(norma_principal)s' @@ -348,3 +351,20 @@ class AnexoNormaJuridica(models.Model): def __str__(self): return _('Anexo: %(anexo)s da norma %(norma)s') % { 'anexo': self.anexo_arquivo, 'norma': self.norma} + + def save(self, force_insert=False, force_update=False, using=None, + update_fields=None): + + if not self.pk and self.anexo_arquivo: + anexo_arquivo = self.anexo_arquivo + self.anexo_arquivo = None + models.Model.save(self, force_insert=force_insert, + force_update=force_update, + using=using, + update_fields=update_fields) + self.anexo_arquivo = anexo_arquivo + + return models.Model.save(self, force_insert=force_insert, + force_update=force_update, + using=using, + update_fields=update_fields) \ No newline at end of file diff --git a/sapl/norma/views.py b/sapl/norma/views.py index 1d0f9b28e..336c16933 100644 --- a/sapl/norma/views.py +++ b/sapl/norma/views.py @@ -345,12 +345,10 @@ class ImpressosView(PermissionRequiredMixin, TemplateView): def gerar_pdf_impressos(request, context, template_name): template = loader.get_template(template_name) html = template.render(context, request) - pdf = weasyprint.HTML(string=html, base_url=request.build_absolute_uri() - ).write_pdf() + pdf = weasyprint.HTML(string=html, base_url=request.build_absolute_uri()).write_pdf() response = HttpResponse(pdf, content_type='application/pdf') - response['Content-Disposition'] = ( - 'inline; filename="relatorio_impressos.pdf"') + response['Content-Disposition'] = 'inline; filename="relatorio_impressos.pdf"' response['Content-Transfer-Encoding'] = 'binary' return response @@ -358,29 +356,28 @@ def gerar_pdf_impressos(request, context, template_name): class NormaPesquisaSimplesView(PermissionRequiredMixin, FormView): form_class = NormaPesquisaSimplesForm - template_name = 'materia/impressos/norma.html' + template_name = 'materia/impressos/impressos_form.html' permission_required = ('materia.can_access_impressos', ) def form_valid(self, form): - normas = NormaJuridica.objects.all().order_by( - 'numero') template_norma = 'materia/impressos/normas_pdf.html' titulo = form.cleaned_data['titulo'] - if form.cleaned_data['tipo_norma']: - normas = normas.filter(tipo=form.cleaned_data['tipo_norma']) + kwargs = {} + if form.cleaned_data.get('tipo_norma'): + kwargs.update({'tipo': form.cleaned_data['tipo_norma']}) - if form.cleaned_data['data_inicial']: - normas = normas.filter( - data__gte=form.cleaned_data['data_inicial'], - data__lte=form.cleaned_data['data_final']) + if form.cleaned_data.get('data_inicial'): + kwargs.update({'data__gte': form.cleaned_data['data_inicial'], + 'data__lte': form.cleaned_data['data_final']}) - qtd_resultados = len(normas) - if qtd_resultados > 2000: - normas = normas[:2000] + normas = NormaJuridica.objects.filter(**kwargs).order_by('-numero', 'ano') - context = {'quantidade': qtd_resultados, + quantidade_normas = normas.count() + normas = normas[:2000] if quantidade_normas > 2000 else normas + + context = {'quantidade': quantidade_normas, 'titulo': titulo, 'normas': normas} diff --git a/sapl/parlamentares/forms.py b/sapl/parlamentares/forms.py index 0c0f8887d..2003a4679 100755 --- a/sapl/parlamentares/forms.py +++ b/sapl/parlamentares/forms.py @@ -1,7 +1,7 @@ from datetime import timedelta import logging -from crispy_forms.helper import FormHelper +from sapl.crispy_layout_mixin import SaplFormHelper from crispy_forms.layout import Fieldset, Layout from django import forms from django.contrib.auth import get_user_model @@ -15,13 +15,15 @@ from django.utils import timezone from django.utils.translation import ugettext_lazy as _ from floppyforms.widgets import ClearableFileInput from image_cropping.widgets import CropWidget, ImageCropWidget +from sapl.utils import FileFieldCheckMixin from sapl.base.models import Autor, TipoAutor from sapl.crispy_layout_mixin import form_actions, to_row from sapl.rules import SAPL_GROUP_VOTANTE +import django_filters from .models import (ComposicaoColigacao, Filiacao, Frente, Legislatura, - Mandato, Parlamentar, Votante) + Mandato, Parlamentar, Votante, Bloco) class ImageThumbnailFileInput(ClearableFileInput): @@ -196,7 +198,7 @@ class LegislaturaForm(ModelForm): return data -class ParlamentarForm(ModelForm): +class ParlamentarForm(FileFieldCheckMixin, ModelForm): class Meta: model = Parlamentar @@ -209,19 +211,30 @@ class ParlamentarForm(ModelForm): attrs={'id': 'texto-rico'})} -class ParlamentarCreateForm(ParlamentarForm): +class ParlamentarFilterSet(django_filters.FilterSet): + nome_parlamentar = django_filters.CharFilter( + label=_('Nome do Parlamentar'), + lookup_expr='icontains') - legislatura = forms.ModelChoiceField( - label=_('Legislatura'), - required=True, - queryset=Legislatura.objects.all().order_by('-data_inicio'), - empty_label='----------', - ) + class Meta: + model = Parlamentar + fields = ['nome_parlamentar'] - data_expedicao_diploma = forms.DateField( - label=_('Expedição do Diploma'), - required=True, - ) + def __init__(self, *args, **kwargs): + super(ParlamentarFilterSet, self).__init__(*args, **kwargs) + + row0 = to_row([('nome_parlamentar', 12)]) + + self.form.helper = SaplFormHelper() + self.form.helper.form_method = 'GET' + self.form.helper.layout = Layout( + Fieldset(_('Pesquisa de Parlamentar'), + row0, + form_actions(label='Pesquisar')) + ) + + +class ParlamentarCreateForm(ParlamentarForm): class Meta(ParlamentarForm.Meta): widgets = { @@ -230,16 +243,24 @@ class ParlamentarCreateForm(ParlamentarForm): attrs={'id': 'texto-rico'}) } + def clean(self): + super().clean() + + if not self.is_valid(): + return self.cleaned_data + + cleaned_data = self.cleaned_data + parlamentar = Parlamentar.objects.filter(nome_parlamentar=cleaned_data['nome_parlamentar']).exists() + + if parlamentar: + self.logger.error('Parlamentar já cadastrado.') + raise ValidationError('Parlamentar já cadastrado.') + + return cleaned_data + @transaction.atomic def save(self, commit=True): parlamentar = super(ParlamentarCreateForm, self).save(commit) - legislatura = self.cleaned_data['legislatura'] - Mandato.objects.create( - parlamentar=parlamentar, - legislatura=legislatura, - data_inicio_mandato=legislatura.data_inicio, - data_fim_mandato=legislatura.data_fim, - data_expedicao_diploma=self.cleaned_data['data_expedicao_diploma']) content_type = ContentType.objects.get_for_model(Parlamentar) object_id = parlamentar.pk tipo = TipoAutor.objects.get(content_type=content_type) @@ -446,7 +467,7 @@ class VotanteForm(ModelForm): def __init__(self, *args, **kwargs): row1 = to_row([('username', 4)]) - self.helper = FormHelper() + self.helper = SaplFormHelper() self.helper.layout = Layout( Fieldset(_('Votante'), row1, form_actions(label='Salvar')) @@ -500,3 +521,100 @@ class VotanteForm(ModelForm): votante.user = u votante.save() return votante + + +class VincularParlamentarForm(forms.Form): + logger = logging.getLogger(__name__) + + parlamentar = forms.ModelChoiceField( + label=Parlamentar._meta.verbose_name, + queryset=Parlamentar.objects.filter(ativo=True), + required=True, + empty_label='Selecione' + ) + + legislatura = forms.ModelChoiceField( + label=Legislatura._meta.verbose_name, + queryset=Legislatura.objects.all(), + required=True, + empty_label='Selecione' + ) + + data_expedicao_diploma = forms.DateField( + label='Data de Expedição do Diploma', + required=False, + widget=forms.DateInput(format='%d/%m/%Y') + ) + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + row1 = to_row([ + ('parlamentar', 6), + ('legislatura', 3), + ('data_expedicao_diploma', 3) + ]) + + self.helper = SaplFormHelper() + self.helper.layout = Layout( + Fieldset( + 'Vincular Parlamentar', + row1, + form_actions(label='Vincular') + ) + ) + + def clean(self): + super().clean() + + if not self.is_valid(): + return self.cleaned_data + + cleaned_data = self.cleaned_data + parlamentar = cleaned_data['parlamentar'] + legislatura = cleaned_data['legislatura'] + data_expedicao_diploma = cleaned_data['data_expedicao_diploma'] + + if parlamentar.mandato_set.filter(legislatura=legislatura): + self.logger.error('Parlamentar já está vinculado a legislatura informada.') + raise ValidationError(_('Parlamentar já está vinculado a legislatura informada.')) + elif data_expedicao_diploma and legislatura.data_inicio <= data_expedicao_diploma: + self.logger.error('Data da Expedição do Diploma deve ser anterior a data de início da Legislatura.') + raise ValidationError(_('Data da Expedição do Diploma deve ser anterior a data de início da Legislatura.')) + + return cleaned_data + + +class BlocoForm(ModelForm): + + class Meta: + model = Bloco + fields = ['nome', 'partidos', 'data_criacao', + 'data_extincao', 'descricao'] + + def clean(self): + super(BlocoForm, self).clean() + + if not self.is_valid(): + return self.cleaned_data + + if self.cleaned_data['data_extincao']: + if (self.cleaned_data['data_extincao'] < + self.cleaned_data['data_criacao']): + msg = _('Data de extinção não pode ser menor que a de criação') + raise ValidationError(msg) + return self.cleaned_data + + @transaction.atomic + def save(self, commit=True): + bloco = super(BlocoForm, self).save(commit) + content_type = ContentType.objects.get_for_model(Bloco) + object_id = bloco.pk + tipo = TipoAutor.objects.get(content_type=content_type) + Autor.objects.create( + content_type=content_type, + object_id=object_id, + tipo=tipo, + nome=bloco.nome + ) + return bloco \ No newline at end of file diff --git a/sapl/parlamentares/migrations/0026_bloco.py b/sapl/parlamentares/migrations/0026_bloco.py new file mode 100644 index 000000000..2ba9f2c07 --- /dev/null +++ b/sapl/parlamentares/migrations/0026_bloco.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.20 on 2019-04-30 11:28 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('parlamentares', '0025_auto_20180924_1724'), + ('sessao', '0039_auto_20190430_0825') + ] + + state_operations = [ + migrations.CreateModel( + name='Bloco', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('nome', models.CharField(max_length=80, verbose_name='Nome do Bloco')), + ('data_criacao', models.DateField(null=True, verbose_name='Data Criação')), + ('data_extincao', models.DateField(blank=True, null=True, verbose_name='Data Dissolução')), + ('descricao', models.TextField(blank=True, verbose_name='Descrição')), + ('partidos', models.ManyToManyField(blank=True, to='parlamentares.Partido', verbose_name='Partidos')), + ], + options={ + 'db_table': 'parlamentares_bloco', + 'verbose_name': 'Bloco Parlamentar', + 'verbose_name_plural': 'Blocos Parlamentares', + }, + bases=(models.Model,), + ), + ] + + operations = [ + migrations.SeparateDatabaseAndState(state_operations=state_operations) + ] diff --git a/sapl/parlamentares/migrations/0027_auto_20190430_0839.py b/sapl/parlamentares/migrations/0027_auto_20190430_0839.py new file mode 100644 index 000000000..454101416 --- /dev/null +++ b/sapl/parlamentares/migrations/0027_auto_20190430_0839.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.20 on 2019-04-30 11:39 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('parlamentares', '0026_bloco'), + ] + + operations = [ + migrations.AlterModelTable( + name='bloco', + table=None, + ), + ] diff --git a/sapl/parlamentares/models.py b/sapl/parlamentares/models.py index d88f2fd87..d50600eba 100644 --- a/sapl/parlamentares/models.py +++ b/sapl/parlamentares/models.py @@ -568,3 +568,37 @@ class Votante(models.Model): def __str__(self): return self.user.username + + +@reversion.register() +class Bloco(models.Model): + ''' + * blocos podem existir por mais de uma legislatura + ''' + nome = models.CharField( + max_length=80, verbose_name=_('Nome do Bloco')) + partidos = models.ManyToManyField( + Partido, blank=True, verbose_name=_('Partidos')) + data_criacao = models.DateField( + blank=False, null=True, verbose_name=_('Data Criação')) + data_extincao = models.DateField( + blank=True, null=True, verbose_name=_('Data Dissolução')) + descricao = models.TextField(blank=True, verbose_name=_('Descrição')) + + # campo conceitual de reversão genérica para o model Autor que dá a + # o meio possível de localização de tipos de autores. + autor = SaplGenericRelation(Autor, + related_query_name='bloco_set', + fields_search=( + ('nome', '__icontains'), + ('descricao', '__icontains'), + ('partidos__sigla', '__icontains'), + ('partidos__nome', '__icontains'), + )) + + class Meta: + verbose_name = _('Bloco Parlamentar') + verbose_name_plural = _('Blocos Parlamentares') + + def __str__(self): + return self.nome diff --git a/sapl/parlamentares/tests/test_parlamentares.py b/sapl/parlamentares/tests/test_parlamentares.py index e7b885871..e5f607a09 100644 --- a/sapl/parlamentares/tests/test_parlamentares.py +++ b/sapl/parlamentares/tests/test_parlamentares.py @@ -11,7 +11,6 @@ from sapl.parlamentares.models import (Dependente, Filiacao, Legislatura, @pytest.mark.django_db(transaction=False) def test_cadastro_parlamentar(admin_client): - legislatura = mommy.make(Legislatura) url = reverse('sapl.parlamentares:parlamentar_create') response = admin_client.get(url) @@ -20,21 +19,13 @@ def test_cadastro_parlamentar(admin_client): response = admin_client.post(url, {'nome_completo': 'Teresa Barbosa', 'nome_parlamentar': 'Terezinha', 'sexo': 'F', - 'ativo': 'True', - 'legislatura': legislatura.id, - 'data_expedicao_diploma': '2001-01-01'}, + 'ativo': 'True'}, follow=True) [parlamentar] = Parlamentar.objects.all() assert parlamentar.nome_parlamentar == 'Terezinha' assert parlamentar.sexo == 'F' assert parlamentar.ativo is True - # o primeiro mandato é criado - [mandato] = Mandato.objects.all() - assert mandato.parlamentar == parlamentar - assert str(mandato.data_expedicao_diploma) == '2001-01-01' - assert mandato.legislatura == legislatura - assert mandato.data_fim_mandato == legislatura.data_fim @pytest.mark.django_db(transaction=False) @@ -42,9 +33,7 @@ def test_incluir_parlamentar_errors(admin_client): url = reverse('sapl.parlamentares:parlamentar_create') response = admin_client.post(url) erros_esperados = {campo: ['Este campo é obrigatório.'] - for campo in ['legislatura', - 'data_expedicao_diploma', - 'nome_parlamentar', + for campo in ['nome_parlamentar', 'nome_completo', 'sexo', ]} diff --git a/sapl/parlamentares/urls.py b/sapl/parlamentares/urls.py index e383421c4..4c1434333 100644 --- a/sapl/parlamentares/urls.py +++ b/sapl/parlamentares/urls.py @@ -17,7 +17,9 @@ from sapl.parlamentares.views import (CargoMesaCrud, ColigacaoCrud, frente_atualiza_lista_parlamentares, insere_parlamentar_composicao, parlamentares_frente_selected, - remove_parlamentar_composicao) + remove_parlamentar_composicao, + parlamentares_filiados, BlocoCrud, + PesquisarParlamentarView, VincularParlamentarView) from .apps import AppConfig @@ -33,13 +35,20 @@ urlpatterns = [ VotanteView.get_urls() )), + url(r'^parlamentar/pesquisar-parlamentar/', + PesquisarParlamentarView.as_view(), name='pesquisar_parlamentar'), + url(r'^parlamentar/(?P\d+)/materias$', ParlamentarMateriasView.as_view(), name='parlamentar_materias'), + url(r'^parlamentar/vincular-parlamentar/$', + VincularParlamentarView.as_view(), name='vincular_parlamentar'), + url(r'^sistema/coligacao/', include(ColigacaoCrud.get_urls() + - ComposicaoColigacaoCrud.get_urls())), - + ComposicaoColigacaoCrud.get_urls())), + url(r'^sistema/bloco/', + include(BlocoCrud.get_urls())), url(r'^sistema/frente/', include(FrenteCrud.get_urls())), url(r'^sistema/frente/atualiza-lista-parlamentares', @@ -60,6 +69,7 @@ urlpatterns = [ url(r'^sistema/parlamentar/tipo-militar/', include(TipoMilitarCrud.get_urls())), url(r'^sistema/parlamentar/partido/', include(PartidoCrud.get_urls())), + url(r'^sistema/parlamentar/partido/(?P\d+)/filiados$', parlamentares_filiados, name='parlamentares_filiados'), url(r'^sistema/mesa-diretora/sessao-legislativa/', include(SessaoLegislativaCrud.get_urls())), @@ -80,4 +90,5 @@ urlpatterns = [ url(r'^mesa-diretora/remove-parlamentar-composicao/$', remove_parlamentar_composicao, name='remove_parlamentar_composicao'), + ] diff --git a/sapl/parlamentares/views.py b/sapl/parlamentares/views.py index 3f0146cf0..874f53385 100644 --- a/sapl/parlamentares/views.py +++ b/sapl/parlamentares/views.py @@ -3,6 +3,7 @@ import json import logging from django.contrib import messages +from django.contrib.auth.mixins import PermissionRequiredMixin from django.contrib.contenttypes.models import ContentType from django.core.exceptions import MultipleObjectsReturned, ObjectDoesNotExist from django.core.urlresolvers import reverse, reverse_lazy @@ -10,6 +11,7 @@ from django.db.models import F, Q from django.db.models.aggregates import Count from django.http import JsonResponse from django.http.response import HttpResponseRedirect +from django.shortcuts import render from django.templatetags.static import static from django.utils import timezone from django.utils.datastructures import MultiValueDictKeyError @@ -17,27 +19,33 @@ from django.utils.translation import ugettext_lazy as _ from django.views.decorators.clickjacking import xframe_options_exempt from django.views.generic import FormView from django.views.generic.edit import UpdateView +from django_filters.views import FilterView +from image_cropping.utils import get_backend -from sapl.base.forms import SessaoLegislativaForm + +from sapl.base.forms import SessaoLegislativaForm, PartidoForm from sapl.base.models import Autor from sapl.comissoes.models import Participacao from sapl.crud.base import (RP_CHANGE, RP_DETAIL, RP_LIST, Crud, CrudAux, CrudBaseForListAndDetailExternalAppView, - MasterDetailCrud) + MasterDetailCrud, make_pagination) from sapl.materia.models import Autoria, Proposicao, Relatoria from sapl.parlamentares.apps import AppConfig -from sapl.utils import parlamentares_ativos +from sapl.utils import (parlamentares_ativos, show_results_filter_set) from .forms import (FiliacaoForm, FrenteForm, LegislaturaForm, MandatoForm, - ParlamentarCreateForm, ParlamentarForm, VotanteForm) + ParlamentarCreateForm, ParlamentarForm, VotanteForm, + ParlamentarFilterSet, VincularParlamentarForm, + BlocoForm) + from .models import (CargoMesa, Coligacao, ComposicaoColigacao, ComposicaoMesa, Dependente, Filiacao, Frente, Legislatura, Mandato, NivelInstrucao, Parlamentar, Partido, SessaoLegislativa, - SituacaoMilitar, TipoAfastamento, TipoDependente, Votante) + SituacaoMilitar, TipoAfastamento, TipoDependente, Votante, + Bloco) CargoMesaCrud = CrudAux.build(CargoMesa, 'cargo_mesa') -PartidoCrud = CrudAux.build(Partido, 'partidos') TipoDependenteCrud = CrudAux.build(TipoDependente, 'tipo_dependente') NivelInstrucaoCrud = CrudAux.build(NivelInstrucao, 'nivel_instrucao') TipoAfastamentoCrud = CrudAux.build(TipoAfastamento, 'tipo_afastamento') @@ -57,6 +65,16 @@ class SessaoLegislativaCrud(CrudAux): form_class = SessaoLegislativaForm +class PartidoCrud(CrudAux): + model = Partido + + class CreateView(CrudAux.CreateView): + form_class = PartidoForm + + class UpdateView(CrudAux.UpdateView): + form_class = PartidoForm + + class VotanteView(MasterDetailCrud): model = Votante parent_field = 'parlamentar' @@ -154,6 +172,63 @@ class ProposicaoParlamentarCrud(CrudBaseForListAndDetailExternalAppView): _('Texto Eletrônico')) +class PesquisarParlamentarView(FilterView): + model = Parlamentar + filterset_class = ParlamentarFilterSet + paginate_by = 10 + + def get_filterset_kwargs(self, filterset_class): + super(PesquisarParlamentarView, + self).get_filterset_kwargs(filterset_class) + + kwargs = {'data': self.request.GET or None} + + qs = self.get_queryset().order_by('nome_parlamentar').distinct() + + kwargs.update({ + 'queryset': qs, + }) + return kwargs + + def get_context_data(self, **kwargs): + context = super(PesquisarParlamentarView, + self).get_context_data(**kwargs) + + paginator = context['paginator'] + page_obj = context['page_obj'] + + context['page_range'] = make_pagination( + page_obj.number, paginator.num_pages) + + context['NO_ENTRIES_MSG'] = 'Nenhum parlamentar encontrado!' + + context['title'] = _('Parlamentares') + + return context + + def get(self, request, *args, **kwargs): + super(PesquisarParlamentarView, self).get(request) + + data = self.filterset.data + url = '' + if data: + url = "&" + str(self.request.META['QUERY_STRING']) + if url.startswith("&page"): + ponto_comeco = url.find('nome_parlamentar=') - 1 + url = url[ponto_comeco:] + + context = self.get_context_data(filter=self.filterset, + object_list=self.object_list, + filter_url=url, + numero_res=len(self.object_list) + ) + + context['show_results'] = show_results_filter_set( + self.request.GET.copy()) + + return self.render_to_response(context) + + class ParticipacaoParlamentarCrud(CrudBaseForListAndDetailExternalAppView): model = Participacao parent_field = 'parlamentar' @@ -178,7 +253,6 @@ class ParticipacaoParlamentarCrud(CrudBaseForListAndDetailExternalAppView): comissoes = [] for p in object_list: - if p.cargo.nome != 'Relator': comissao = [ (p.composicao.comissao.nome, reverse( 'sapl.comissoes:comissao_detail', kwargs={ @@ -593,7 +667,7 @@ class ParlamentarCrud(Crud): .format(legislatura.data_fim, legislatura.data_fim, legislatura.data_fim)) row[1] = ( 'O Parlamentar possui duas filiações conflitantes', - None) + None, None) # Caso encontre UMA filiação nessas condições else: @@ -678,6 +752,19 @@ class ParlamentarMateriasView(FormView): }) +def get_data_filicao(parlamentar): + return parlamentar.filiacao_set.order_by('-data').first().data.strftime('%d/%m/%Y') + + +def parlamentares_filiados(request, pk): + template_name = 'parlamentares/partido_filiados.html' + parlamentares = Parlamentar.objects.all() + partido = Partido.objects.get(pk=pk) + parlamentares_filiados = [(parlamentar, get_data_filicao(parlamentar)) for parlamentar in parlamentares if + parlamentar.filiacao_atual == partido.sigla] + return render(request, template_name, {'partido': partido, 'parlamentares': parlamentares_filiados}) + + class MesaDiretoraView(FormView): template_name = 'parlamentares/composicaomesa_form.html' success_url = reverse_lazy('sapl.parlamentares:mesa_diretora') @@ -738,9 +825,9 @@ class MesaDiretoraView(FormView): parlamentares_ocupados = [m.parlamentar for m in mesa] parlamentares_vagos = list( set( - [p.parlamentar for p in parlamentares]) - set( + [p.parlamentar for p in parlamentares if p.parlamentar.ativo]) - set( parlamentares_ocupados)) - + parlamentares_vagos.sort(key=lambda x: x.nome_parlamentar) # Se todos os cargos estiverem ocupados, a listagem de parlamentares # deve ser renderizada vazia if not cargos_vagos: @@ -808,6 +895,7 @@ def altera_field_mesa(request): [p.parlamentar for p in parlamentares]) - set( parlamentares_ocupados)) + parlamentares_vagos.sort(key=lambda x: x.nome_parlamentar) lista_sessoes = [(s.id, s.__str__()) for s in sessoes] lista_composicao = [(c.id, c.parlamentar.__str__(), c.cargo.__str__()) for c in composicao_mesa] @@ -1027,7 +1115,20 @@ def altera_field_mesa_public_view(request): partido_parlamentar_sessao_legislativa(sessao, parlamentar)) if parlamentar.fotografia: - lista_fotos.append(parlamentar.fotografia.url) + try: + thumbnail_url = get_backend().get_thumbnail_url( + parlamentar.fotografia, + { + 'size': (128, 128), + 'box': parlamentar.cropping, + 'crop': True, + 'detail': True, + } + ) + lista_fotos.append(thumbnail_url) + except Exception as e: + logger.error(e) + logger.error('erro processando arquivo: %s' % parlamentar.fotografia.path) else: lista_fotos.append(None) @@ -1039,3 +1140,40 @@ def altera_field_mesa_public_view(request): 'lista_fotos': lista_fotos, 'sessao_selecionada': sessao_selecionada, 'msg': ('', 1)}) + + +class VincularParlamentarView(PermissionRequiredMixin, FormView): + logger = logging.getLogger(__name__) + form_class = VincularParlamentarForm + template_name = 'parlamentares/vincular_parlamentar.html' + permission_required = ('parlamentares.add_parlamentar', ) + + def get_success_url(self): + return reverse('sapl.parlamentares:parlamentar_list') + + def form_valid(self, form): + kwargs = { + 'parlamentar': form.cleaned_data['parlamentar'], + 'legislatura': form.cleaned_data['legislatura'], + 'data_inicio_mandato': form.cleaned_data['legislatura'].data_inicio, + 'data_fim_mandato': form.cleaned_data['legislatura'].data_fim + } + + data_expedicao_diploma = form.cleaned_data.get('data_expedicao_diploma') + if data_expedicao_diploma: + kwargs.update({'data_expedicao_diploma': data_expedicao_diploma}) + + mandato = Mandato.objects.create(**kwargs) + mandato.save() + + return HttpResponseRedirect(self.get_success_url()) + + +class BlocoCrud(CrudAux): + model = Bloco + + class CreateView(CrudAux.CreateView): + form_class = BlocoForm + + def get_success_url(self): + return reverse('sapl.parlamentares:bloco_list') \ No newline at end of file diff --git a/sapl/protocoloadm/forms.py b/sapl/protocoloadm/forms.py index d650c7de1..a8630539c 100644 --- a/sapl/protocoloadm/forms.py +++ b/sapl/protocoloadm/forms.py @@ -2,19 +2,19 @@ import logging from crispy_forms.bootstrap import InlineRadios, Alert -from crispy_forms.helper import FormHelper +from sapl.crispy_layout_mixin import SaplFormHelper from crispy_forms.layout import HTML, Button, Column, Fieldset, Layout, Div from django import forms from django.core.exceptions import (MultipleObjectsReturned, ObjectDoesNotExist, ValidationError) -from django.db import models +from django.db import models, transaction from django.db.models import Max from django.forms import ModelForm from django.utils import timezone from django.utils.translation import ugettext_lazy as _ import django_filters -from sapl.base.models import Autor, TipoAutor +from sapl.base.models import Autor, TipoAutor, AppConfig from sapl.crispy_layout_mixin import SaplFormLayout, form_actions, to_row from sapl.materia.models import (MateriaLegislativa, TipoMateriaLegislativa, UnidadeTramitacao) @@ -23,12 +23,13 @@ from sapl.utils import (RANGE_ANOS, YES_NO_CHOICES, AnoNumeroOrderingFilter, RangeWidgetOverride, autor_label, autor_modal, choice_anos_com_protocolo, choice_force_optional, choice_anos_com_documentoadministrativo, - FilterOverridesMetaMixin, choice_anos_com_materias) + FilterOverridesMetaMixin, choice_anos_com_materias, + FileFieldCheckMixin, lista_anexados) from .models import (AcompanhamentoDocumento, DocumentoAcessorioAdministrativo, DocumentoAdministrativo, Protocolo, TipoDocumentoAdministrativo, - TramitacaoAdministrativo) + TramitacaoAdministrativo, Anexado) TIPOS_PROTOCOLO = [('0', 'Recebido'), ('1', 'Enviado'), @@ -51,16 +52,14 @@ class AcompanhamentoDocumentoForm(ModelForm): def __init__(self, *args, **kwargs): - row1 = to_row([('email', 10)]) + row1 = to_row([('email', 12)]) - row1.append( - Column(form_actions(label='Cadastrar'), css_class='col-md-2') - ) - - self.helper = FormHelper() + self.helper = SaplFormHelper() self.helper.layout = Layout( Fieldset( - _('Acompanhamento de Documento por e-mail'), row1 + _('Acompanhamento de Documento por e-mail'), + row1, + form_actions(label='Cadastrar') ) ) super(AcompanhamentoDocumentoForm, self).__init__(*args, **kwargs) @@ -136,7 +135,7 @@ class ProtocoloFilterSet(django_filters.FilterSet): row5 = to_row( [('tipo_processo', 6), ('o', 6)]) - self.form.helper = FormHelper() + self.form.helper = SaplFormHelper() self.form.helper.form_method = 'GET' self.form.helper.layout = Layout( Fieldset(_('Pesquisar Protocolo'), @@ -212,7 +211,7 @@ class DocumentoAdministrativoFilterSet(django_filters.FilterSet): ('tramitacaoadministrativo__unidade_tramitacao_destino', 5), ]) - self.form.helper = FormHelper() + self.form.helper = SaplFormHelper() self.form.helper.form_method = 'GET' self.form.helper.layout = Layout( Fieldset(_('Pesquisar Documento'), @@ -222,7 +221,7 @@ class DocumentoAdministrativoFilterSet(django_filters.FilterSet): ) -class AnularProcoloAdmForm(ModelForm): +class AnularProtocoloAdmForm(ModelForm): logger = logging.getLogger(__name__) @@ -241,7 +240,7 @@ class AnularProcoloAdmForm(ModelForm): widget=forms.Textarea) def clean(self): - super(AnularProcoloAdmForm, self).clean() + super(AnularProtocoloAdmForm, self).clean() cleaned_data = self.cleaned_data @@ -305,7 +304,7 @@ class AnularProcoloAdmForm(ModelForm): row2 = to_row( [('justificativa_anulacao', 12)]) - self.helper = FormHelper() + self.helper = SaplFormHelper() self.helper.layout = Layout( Fieldset(_('Identificação do Protocolo'), row1, @@ -314,7 +313,7 @@ class AnularProcoloAdmForm(ModelForm): form_actions(label='Anular') ) ) - super(AnularProcoloAdmForm, self).__init__( + super(AnularProtocoloAdmForm, self).__init__( *args, **kwargs) @@ -404,14 +403,21 @@ class ProtocoloDocumentForm(ModelForm): row7 = to_row( [('numero', 12)]) - self.helper = FormHelper() + fieldset = Fieldset(_('Protocolo com data e hora informados manualmente'), + row3, + css_id='protocolo_data_hora_manual') + + config = AppConfig.objects.first() + if not config.protocolo_manual: + row3 = to_row([(HTML(" "), 12)]) + fieldset = row3 + + self.helper = SaplFormHelper() self.helper.layout = Layout( Fieldset(_('Identificação de Documento'), row1, row2), - Fieldset(_('Protocolo com data e hora informados manualmente'), - row3, - css_id='protocolo_data_hora_manual'), + fieldset, row4, row5, HTML(" "), @@ -425,6 +431,10 @@ class ProtocoloDocumentForm(ModelForm): super(ProtocoloDocumentForm, self).__init__( *args, **kwargs) + if not config.protocolo_manual: + self.fields['data_hora_manual'].widget = forms.HiddenInput() + + class ProtocoloMateriaForm(ModelForm): @@ -583,14 +593,21 @@ class ProtocoloMateriaForm(ModelForm): row6 = to_row( [('numero', 12)]) - self.helper = FormHelper() + fieldset = Fieldset(_('Protocolo com data e hora informados manualmente'), + row3, + css_id='protocolo_data_hora_manual') + + config = AppConfig.objects.first() + if not config.protocolo_manual: + row3 = to_row([(HTML(" "), 12)]) + fieldset = row3 + + self.helper = SaplFormHelper() self.helper.layout = Layout( Fieldset(_('Identificação da Matéria'), row1, row2), - Fieldset(_('Protocolo com data e hora informados manualmente'), - row3, - css_id='protocolo_data_hora_manual'), + fieldset, row4, row5, HTML(" "), @@ -604,8 +621,11 @@ class ProtocoloMateriaForm(ModelForm): super(ProtocoloMateriaForm, self).__init__( *args, **kwargs) + if not config.protocolo_manual: + self.fields['data_hora_manual'].widget = forms.HiddenInput() + -class DocumentoAcessorioAdministrativoForm(ModelForm): +class DocumentoAcessorioAdministrativoForm(FileFieldCheckMixin, ModelForm): class Meta: model = DocumentoAcessorioAdministrativo @@ -630,11 +650,29 @@ class TramitacaoAdmForm(ModelForm): fields = ['data_tramitacao', 'unidade_tramitacao_local', 'status', + 'urgente', 'unidade_tramitacao_destino', 'data_encaminhamento', 'data_fim_prazo', 'texto', - ] + 'user', + 'ip'] + widgets = {'user': forms.HiddenInput(), + 'ip': forms.HiddenInput()} + + + def __init__(self, *args, **kwargs): + super(TramitacaoAdmForm, self).__init__(*args, **kwargs) + self.fields['data_tramitacao'].initial = timezone.now().date() + ust = UnidadeTramitacao.objects.select_related().all() + unidade_tramitacao_destino = [('', '---------')] + [(ut.pk, ut) + for ut in ust if ut.comissao and ut.comissao.ativa] + unidade_tramitacao_destino.extend( + [(ut.pk, ut) for ut in ust if ut.orgao]) + unidade_tramitacao_destino.extend( + [(ut.pk, ut) for ut in ust if ut.parlamentar]) + self.fields['unidade_tramitacao_destino'].choices = unidade_tramitacao_destino + self.fields['urgente'].label = "Urgente? *" def clean(self): cleaned_data = super(TramitacaoAdmForm, self).clean() @@ -707,6 +745,51 @@ class TramitacaoAdmForm(ModelForm): return self.cleaned_data + @transaction.atomic + def save(self, commit=True): + tramitacao = super(TramitacaoAdmForm, self).save(commit) + documento = tramitacao.documento + documento.tramitacao = False if tramitacao.status.indicador == "F" else True + documento.save() + + lista_tramitacao = [] + list_anexados = lista_anexados(documento, False) + for da in list_anexados: + if not da.tramitacaoadministrativo_set.all() \ + or da.tramitacaoadministrativo_set.last() \ + .unidade_tramitacao_destino == tramitacao.unidade_tramitacao_local: + da.tramitacao = False if tramitacao.status.indicador == "F" else True + da.save() + lista_tramitacao.append(TramitacaoAdministrativo( + status=tramitacao.status, + documento=da, + data_tramitacao=tramitacao.data_tramitacao, + unidade_tramitacao_local=tramitacao.unidade_tramitacao_local, + data_encaminhamento=tramitacao.data_encaminhamento, + unidade_tramitacao_destino=tramitacao.unidade_tramitacao_destino, + urgente=tramitacao.urgente, + texto=tramitacao.texto, + data_fim_prazo=tramitacao.data_fim_prazo, + user=tramitacao.user, + ip=tramitacao.ip + )) + TramitacaoAdministrativo.objects.bulk_create(lista_tramitacao) + + return tramitacao + + + +# Compara se os campos de duas tramitações são iguais, +# exceto os campos id, documento_id e timestamp +def compara_tramitacoes_doc(tramitacao1, tramitacao2): + if not tramitacao1 or not tramitacao2: + return False + + lst_items = ['id', 'documento_id', 'timestamp'] + values = [(k,v) for k,v in tramitacao1.__dict__.items() if ((k not in lst_items) and (k[0] != '_'))] + other_values = [(k,v) for k,v in tramitacao2.__dict__.items() if (k not in lst_items and k[0] != '_')] + return values == other_values + class TramitacaoAdmEditForm(TramitacaoAdmForm): @@ -722,12 +805,16 @@ class TramitacaoAdmEditForm(TramitacaoAdmForm): model = TramitacaoAdministrativo fields = ['data_tramitacao', 'unidade_tramitacao_local', - 'status', + 'status', + 'urgente', 'unidade_tramitacao_destino', 'data_encaminhamento', 'data_fim_prazo', 'texto', - ] + 'user', + 'ip'] + widgets = {'user': forms.HiddenInput(), + 'ip': forms.HiddenInput()} def clean(self): super(TramitacaoAdmEditForm, self).clean() @@ -735,33 +822,193 @@ class TramitacaoAdmEditForm(TramitacaoAdmForm): if not self.is_valid(): return self.cleaned_data + cd = self.cleaned_data + obj = self.instance + ultima_tramitacao = TramitacaoAdministrativo.objects.filter( - documento_id=self.instance.documento_id).order_by( + documento_id=obj.documento_id).order_by( '-data_tramitacao', '-id').first() # Se a Tramitação que está sendo editada não for a mais recente, # ela não pode ter seu destino alterado. - if ultima_tramitacao != self.instance: - if self.cleaned_data['unidade_tramitacao_destino'] != \ - self.instance.unidade_tramitacao_destino: + if ultima_tramitacao != obj: + if cd['unidade_tramitacao_destino'] != \ + obj.unidade_tramitacao_destino: self.logger.error('Você não pode mudar a Unidade de Destino desta ' 'tramitação (id={}), pois irá conflitar com a Unidade ' - 'Local da tramitação seguinte'.format(self.instance.documento_id)) + 'Local da tramitação seguinte'.format(obj.documento_id)) raise ValidationError( 'Você não pode mudar a Unidade de Destino desta ' 'tramitação, pois irá conflitar com a Unidade ' 'Local da tramitação seguinte') - self.cleaned_data['data_tramitacao'] = \ - self.instance.data_tramitacao - self.cleaned_data['unidade_tramitacao_local'] = \ - self.instance.unidade_tramitacao_local + # Se não houve qualquer alteração em um dos dados, mantém o usuário e ip + if not (cd['data_tramitacao'] != obj.data_tramitacao or \ + cd['unidade_tramitacao_destino'] != obj.unidade_tramitacao_destino or \ + cd['status'] != obj.status or cd['texto'] != obj.texto or \ + cd['data_encaminhamento'] != obj.data_encaminhamento or \ + cd['data_fim_prazo'] != obj.data_fim_prazo): + cd['user'] = obj.user + cd['ip'] = obj.ip - return self.cleaned_data + cd['data_tramitacao'] = obj.data_tramitacao + cd['unidade_tramitacao_local'] = obj.unidade_tramitacao_local + + return cd + + + @transaction.atomic + def save(self, commit=True): + # tram_principal = super(TramitacaoAdmEditForm, self).save(commit) + ant_tram_principal = TramitacaoAdministrativo.objects.get(id=self.instance.id) + nova_tram_principal = super(TramitacaoAdmEditForm, self).save(commit) + documento = nova_tram_principal.documento + documento.tramitacao = False if nova_tram_principal.status.indicador == "F" else True + documento.save() + + list_anexados = lista_anexados(documento, False) + for da in list_anexados: + tram_anexada = da.tramitacaoadministrativo_set.last() + if compara_tramitacoes_doc(ant_tram_principal, tram_anexada): + tram_anexada.status = nova_tram_principal.status + tram_anexada.data_tramitacao = nova_tram_principal.data_tramitacao + tram_anexada.unidade_tramitacao_local = nova_tram_principal.unidade_tramitacao_local + tram_anexada.data_encaminhamento = nova_tram_principal.data_encaminhamento + tram_anexada.unidade_tramitacao_destino = nova_tram_principal.unidade_tramitacao_destino + tram_anexada.urgente = nova_tram_principal.urgente + tram_anexada.texto = nova_tram_principal.texto + tram_anexada.data_fim_prazo = nova_tram_principal.data_fim_prazo + tram_anexada.user = nova_tram_principal.user + tram_anexada.ip = nova_tram_principal.ip + tram_anexada.save() + + da.tramitacao = False if nova_tram_principal.status.indicador == "F" else True + da.save() + return nova_tram_principal + + +class AnexadoForm(ModelForm): + + logger = logging.getLogger(__name__) + + tipo = forms.ModelChoiceField( + label='Tipo', + required=True, + queryset=TipoDocumentoAdministrativo.objects.all(), + empty_label='Selecione' + ) + + numero = forms.CharField(label='Número', required=True) + + ano = forms.CharField(label='Ano', required=True) + + def __init__(self, *args, **kwargs): + return super(AnexadoForm, self).__init__(*args, **kwargs) + + def clean(self): + super(AnexadoForm, self).clean() + + if not self.is_valid(): + return self.cleaned_data + + cleaned_data = self.cleaned_data + + data_anexacao = cleaned_data['data_anexacao'] + data_desanexacao = cleaned_data['data_desanexacao'] if cleaned_data['data_desanexacao'] else data_anexacao + + if data_anexacao > data_desanexacao: + self.logger.error("Data de anexação posterior à data de desanexação.") + raise ValidationError(_("Data de anexação posterior à data de desanexação.")) + try: + self.logger.info( + "Tentando obter objeto DocumentoAdministrativo (numero={}, ano={}, tipo={})." + .format(cleaned_data['numero'], cleaned_data['ano'], cleaned_data['tipo']) + ) + documento_anexado = DocumentoAdministrativo.objects.get( + numero=cleaned_data['numero'], + ano=cleaned_data['ano'], + tipo=cleaned_data['tipo'] + ) + except ObjectDoesNotExist: + msg = _('O {} {}/{} não existe no cadastro de documentos administrativos.' + .format(cleaned_data['tipo'], cleaned_data['numero'], cleaned_data['ano'])) + self.logger.error("O documento a ser anexado não existe no cadastro" + " de documentos administrativos") + raise ValidationError(msg) + + documento_principal = self.instance.documento_principal + if documento_principal == documento_anexado: + self.logger.error("O documento não pode ser anexado a si mesmo.") + raise ValidationError(_("O documento não pode ser anexado a si mesmo")) + + is_anexado = Anexado.objects.filter(documento_principal=documento_principal, + documento_anexado=documento_anexado + ).exclude(pk=self.instance.pk).exists() + + if is_anexado: + self.logger.error("Documento já se encontra anexado.") + raise ValidationError(_('Documento já se encontra anexado')) + + ciclico = False + anexados_anexado = Anexado.objects.filter(documento_principal=documento_anexado) + + while(anexados_anexado and not ciclico): + anexados = [] + + for anexo in anexados_anexado: + + if documento_principal == anexo.documento_anexado: + ciclico = True + else: + for a in Anexado.objects.filter(documento_principal=anexo.documento_anexado): + anexados.append(a) + + anexados_anexado = anexados + + if ciclico: + self.logger.error("O documento não pode ser anexado por um de seus anexados.") + raise ValidationError(_('O documento não pode ser anexado por um de seus anexados')) + + cleaned_data['documento_anexado'] = documento_anexado + + return cleaned_data + + def save(self, commit=False): + anexado = super(AnexadoForm, self).save(commit) + anexado.documento_anexado = self.cleaned_data['documento_anexado'] + anexado.save() + return anexado + + class Meta: + model = Anexado + fields = ['tipo', 'numero', 'ano', 'data_anexacao', 'data_desanexacao'] + + +class AnexadoEmLoteFilterSet(django_filters.FilterSet): + + class Meta(FilterOverridesMetaMixin): + model = DocumentoAdministrativo + fields = ['tipo', 'data'] + + def __init__(self, *args, **kwargs): + super(AnexadoEmLoteFilterSet, self).__init__(*args, **kwargs) + + self.filters['tipo'].label = 'Tipo de Documento*' + self.filters['data'].label = 'Data (Inicial - Final)*' + + row1 = to_row([('tipo', 12)]) + row2 = to_row([('data', 12)]) + + self.form.helper = SaplFormHelper() + self.form.helper.form_method = 'GET' + self.form.helper.layout = Layout( + Fieldset(_('Pesquisa de Documentos'), + row1, row2, form_actions(label='Pesquisar')) + ) -class DocumentoAdministrativoForm(ModelForm): +class DocumentoAdministrativoForm(FileFieldCheckMixin, ModelForm): logger = logging.getLogger(__name__) @@ -910,7 +1157,7 @@ class DocumentoAdministrativoForm(ModelForm): row7 = to_row( [('observacao', 12)]) - self.helper = FormHelper() + self.helper = SaplFormHelper() self.helper.layout = SaplFormLayout( Fieldset(_('Identificação Básica'), row1, row2, row3, row4, row5), @@ -978,7 +1225,7 @@ class DesvincularDocumentoForm(ModelForm): ('ano', 4), ('tipo', 4)]) - self.helper = FormHelper() + self.helper = SaplFormHelper() self.helper.layout = Layout( Fieldset(_('Identificação do Documento'), row1, @@ -1043,7 +1290,7 @@ class DesvincularMateriaForm(forms.Form): ('ano', 4), ('tipo', 4)]) - self.helper = FormHelper() + self.helper = SaplFormHelper() self.helper.layout = Layout( Fieldset(_('Identificação da Matéria'), row1, @@ -1111,7 +1358,7 @@ class FichaPesquisaAdmForm(forms.Form): ('data_inicial', 3), ('data_final', 3)]) - self.helper = FormHelper() + self.helper = SaplFormHelper() self.helper.layout = Layout( Fieldset( ('Formulário de Ficha'), @@ -1152,7 +1399,7 @@ class FichaSelecionaAdmForm(forms.Form): row1 = to_row( [('documento', 12)]) - self.helper = FormHelper() + self.helper = SaplFormHelper() self.helper.layout = Layout( Fieldset( ('Selecione a ficha que deseja imprimir'), diff --git a/sapl/protocoloadm/migrations/0018_auto_20190314_1532.py b/sapl/protocoloadm/migrations/0018_auto_20190314_1532.py new file mode 100644 index 000000000..20822400c --- /dev/null +++ b/sapl/protocoloadm/migrations/0018_auto_20190314_1532.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.20 on 2019-03-14 18:32 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('protocoloadm', '0017_merge_20190121_1552'), + ] + + operations = [ + migrations.CreateModel( + name='Anexado', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('data_anexacao', models.DateField(verbose_name='Data Anexação')), + ('data_desanexacao', models.DateField(blank=True, null=True, verbose_name='Data Desanexação')), + ('documento_anexado', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='documento_anexado_set', to='protocoloadm.DocumentoAdministrativo', verbose_name='Documento Anexado')), + ('documento_principal', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='documento_principal_set', to='protocoloadm.DocumentoAdministrativo', verbose_name='Documento Principal')), + ], + options={ + 'verbose_name': 'Anexado', + 'verbose_name_plural': 'Anexados', + }, + ), + migrations.AddField( + model_name='documentoadministrativo', + name='anexados', + field=models.ManyToManyField(blank=True, related_name='anexo_de', through='protocoloadm.Anexado', to='protocoloadm.DocumentoAdministrativo'), + ), + ] diff --git a/sapl/protocoloadm/migrations/0019_auto_20190426_0833.py b/sapl/protocoloadm/migrations/0019_auto_20190426_0833.py new file mode 100644 index 000000000..edfdd8904 --- /dev/null +++ b/sapl/protocoloadm/migrations/0019_auto_20190426_0833.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.20 on 2019-04-26 11:33 +from __future__ import unicode_literals + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('protocoloadm', '0018_auto_20190314_1532'), + ] + + operations = [ + migrations.AddField( + model_name='tramitacaoadministrativo', + name='ip', + field=models.CharField(blank=True, default='', max_length=30, verbose_name='IP'), + ), + migrations.AddField( + model_name='tramitacaoadministrativo', + name='user', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL, verbose_name='Usuário'), + ), + ] diff --git a/sapl/protocoloadm/migrations/0019_auto_20190429_0828.py b/sapl/protocoloadm/migrations/0019_auto_20190429_0828.py new file mode 100644 index 000000000..204db8598 --- /dev/null +++ b/sapl/protocoloadm/migrations/0019_auto_20190429_0828.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.20 on 2019-04-29 11:28 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('protocoloadm', '0018_auto_20190314_1532'), + ] + + operations = [ + migrations.AddField( + model_name='tramitacaoadministrativo', + name='urgente', + field=models.BooleanField(choices=[(True, 'Sim'), (False, 'Não')], default=False, verbose_name='Urgente ?'), + ), + migrations.AlterField( + model_name='tramitacaoadministrativo', + name='texto', + field=models.TextField(verbose_name='Texto da Ação'), + ), + ] diff --git a/sapl/protocoloadm/migrations/0020_tramitacaoadministrativo_timestamp.py b/sapl/protocoloadm/migrations/0020_tramitacaoadministrativo_timestamp.py new file mode 100644 index 000000000..0e367b306 --- /dev/null +++ b/sapl/protocoloadm/migrations/0020_tramitacaoadministrativo_timestamp.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.20 on 2019-04-29 12:15 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.utils.timezone + + +class Migration(migrations.Migration): + + dependencies = [ + ('protocoloadm', '0019_auto_20190429_0828'), + ] + + operations = [ + migrations.AddField( + model_name='tramitacaoadministrativo', + name='timestamp', + field=models.DateTimeField(default=django.utils.timezone.now), + ), + ] diff --git a/sapl/protocoloadm/migrations/0021_merge_20190429_1531.py b/sapl/protocoloadm/migrations/0021_merge_20190429_1531.py new file mode 100644 index 000000000..4613b46ce --- /dev/null +++ b/sapl/protocoloadm/migrations/0021_merge_20190429_1531.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.20 on 2019-04-29 18:31 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('protocoloadm', '0020_tramitacaoadministrativo_timestamp'), + ('protocoloadm', '0019_auto_20190426_0833'), + ] + + operations = [ + ] diff --git a/sapl/protocoloadm/models.py b/sapl/protocoloadm/models.py index e335a5db1..8396349a8 100644 --- a/sapl/protocoloadm/models.py +++ b/sapl/protocoloadm/models.py @@ -6,7 +6,8 @@ import reversion from sapl.base.models import Autor from sapl.materia.models import TipoMateriaLegislativa, UnidadeTramitacao -from sapl.utils import RANGE_ANOS, YES_NO_CHOICES, texto_upload_path +from sapl.utils import (RANGE_ANOS, YES_NO_CHOICES, texto_upload_path, + get_settings_auth_user_model) @reversion.register() @@ -170,6 +171,18 @@ class DocumentoAdministrativo(models.Model): verbose_name=_('Acesso Restrito'), blank=True) + anexados = models.ManyToManyField( + 'self', + blank=True, + through='Anexado', + symmetrical=False, + related_name='anexo_de', + through_fields=( + 'documento_principal', + 'documento_anexado' + ) + ) + class Meta: verbose_name = _('Documento Administrativo') verbose_name_plural = _('Documentos Administrativos') @@ -288,6 +301,7 @@ class TramitacaoAdministrativo(models.Model): verbose_name=_('Status')) documento = models.ForeignKey(DocumentoAdministrativo, on_delete=models.PROTECT) + timestamp = models.DateTimeField(default=timezone.now) data_tramitacao = models.DateField( verbose_name=_('Data Tramitação')) unidade_tramitacao_local = models.ForeignKey( @@ -302,10 +316,21 @@ class TramitacaoAdministrativo(models.Model): related_name='adm_tramitacoes_destino', on_delete=models.PROTECT, verbose_name=_('Unidade Destino')) - texto = models.TextField( - blank=True, verbose_name=_('Texto da Ação')) + urgente = models.BooleanField(verbose_name=_('Urgente ?'), + choices=YES_NO_CHOICES, + default=False) + texto = models.TextField(verbose_name=_('Texto da Ação')) data_fim_prazo = models.DateField( blank=True, null=True, verbose_name=_('Data Fim do Prazo')) + user = models.ForeignKey(get_settings_auth_user_model(), + verbose_name=_('Usuário'), + on_delete=models.PROTECT, + null=True, + blank=True) + ip = models.CharField(verbose_name=_('IP'), + max_length=30, + blank=True, + default='') class Meta: verbose_name = _('Tramitação de Documento Administrativo') @@ -317,6 +342,36 @@ class TramitacaoAdministrativo(models.Model): } +@reversion.register() +class Anexado(models.Model): + documento_principal = models.ForeignKey( + DocumentoAdministrativo, related_name='documento_principal_set', + on_delete = models.CASCADE, + verbose_name=_('Documento Principal') + ) + documento_anexado = models.ForeignKey( + DocumentoAdministrativo, related_name='documento_anexado_set', + on_delete = models.CASCADE, + verbose_name=_('Documento Anexado') + ) + data_anexacao = models.DateField(verbose_name=_('Data Anexação')) + data_desanexacao = models.DateField( + blank=True, null=True, verbose_name=_('Data Desanexação') + ) + + class Meta: + verbose_name = _('Anexado') + verbose_name_plural = _('Anexados') + + def __str__(self): + return _('Anexado: %(documento_anexado_tipo)s %(documento_anexado_numero)s' + '/%(documento_anexado_ano)s\n') % { + 'documento_anexado_tipo': self.documento_anexado.tipo, + 'documento_anexado_numero': self.documento_anexado.numero, + 'documento_anexado_ano': self.documento_anexado.ano + } + + @reversion.register() class AcompanhamentoDocumento(models.Model): usuario = models.CharField(max_length=50) diff --git a/sapl/protocoloadm/tests/test_protocoloadm.py b/sapl/protocoloadm/tests/test_protocoloadm.py index c7e4bc341..ce322a7ba 100644 --- a/sapl/protocoloadm/tests/test_protocoloadm.py +++ b/sapl/protocoloadm/tests/test_protocoloadm.py @@ -7,16 +7,21 @@ from django.utils.translation import ugettext_lazy as _ from model_mommy import mommy import pytest +from sapl.base.models import AppConfig +from sapl.comissoes.models import Comissao, TipoComissao from sapl.materia.models import UnidadeTramitacao -from sapl.protocoloadm.forms import (AnularProcoloAdmForm, +from sapl.protocoloadm.forms import (AnularProtocoloAdmForm, DocumentoAdministrativoForm, MateriaLegislativa, ProtocoloDocumentForm, - ProtocoloMateriaForm) + ProtocoloMateriaForm, TramitacaoAdmForm, + TramitacaoAdmEditForm, + compara_tramitacoes_doc) from sapl.protocoloadm.models import (DocumentoAdministrativo, Protocolo, StatusTramitacaoAdministrativo, TipoDocumentoAdministrativo, - TipoMateriaLegislativa, + TipoMateriaLegislativa, Anexado, TramitacaoAdministrativo) +from sapl.utils import lista_anexados @pytest.mark.django_db(transaction=False) @@ -50,7 +55,7 @@ def test_anular_protocolo_submit(admin_client): @pytest.mark.django_db(transaction=False) def test_form_anular_protocolo_inexistente(): - form = AnularProcoloAdmForm({'numero': '1', + form = AnularProtocoloAdmForm({'numero': '1', 'ano': '2016', 'justificativa_anulacao': 'TESTE'}) @@ -63,7 +68,7 @@ def test_form_anular_protocolo_inexistente(): @pytest.mark.django_db(transaction=False) def test_form_anular_protocolo_valido(): mommy.make(Protocolo, numero='1', ano='2016', anulado=False) - form = AnularProcoloAdmForm({'numero': '1', + form = AnularProtocoloAdmForm({'numero': '1', 'ano': '2016', 'justificativa_anulacao': 'TESTE'}) if not form.is_valid(): @@ -73,7 +78,7 @@ def test_form_anular_protocolo_valido(): @pytest.mark.django_db(transaction=False) def test_form_anular_protocolo_anulado(): mommy.make(Protocolo, numero='1', ano='2016', anulado=True) - form = AnularProcoloAdmForm({'numero': '1', + form = AnularProtocoloAdmForm({'numero': '1', 'ano': '2016', 'justificativa_anulacao': 'TESTE'}) assert form.errors['__all__'] == \ @@ -87,7 +92,7 @@ def test_form_anular_protocolo_campos_obrigatorios(): # TODO: generalizar para diminuir o tamanho deste método # numero ausente - form = AnularProcoloAdmForm({'numero': '', + form = AnularProtocoloAdmForm({'numero': '', 'ano': '2016', 'justificativa_anulacao': 'TESTE'}) if form.is_valid(): @@ -97,7 +102,7 @@ def test_form_anular_protocolo_campos_obrigatorios(): assert form.errors['numero'] == [_('Este campo é obrigatório.')] # ano ausente - form = AnularProcoloAdmForm({'numero': '1', + form = AnularProtocoloAdmForm({'numero': '1', 'ano': '', 'justificativa_anulacao': 'TESTE'}) if form.is_valid(): @@ -107,7 +112,7 @@ def test_form_anular_protocolo_campos_obrigatorios(): assert form.errors['ano'] == [_('Este campo é obrigatório.')] # justificativa_anulacao ausente - form = AnularProcoloAdmForm({'numero': '1', + form = AnularProtocoloAdmForm({'numero': '1', 'ano': '2016', 'justificativa_anulacao': ''}) if form.is_valid(): @@ -156,12 +161,14 @@ def test_create_tramitacao(admin_client): 'unidade_tramitacao_destino': unidade_tramitacao_local_1.pk, 'documento': documento_adm.pk, 'status': status.pk, + 'urgente': False, + 'texto': 'teste', 'data_tramitacao': date(2016, 8, 21)}, follow=True) msg = force_text(_('A origem da nova tramitação deve ser igual ao ' 'destino da última adicionada!')) - + # Verifica se a origem da nova tramitacao é igual ao destino da última assert msg in response.context_data[ 'form'].errors['__all__'] @@ -174,6 +181,8 @@ def test_create_tramitacao(admin_client): 'unidade_tramitacao_destino': unidade_tramitacao_destino_2.pk, 'documento': documento_adm.pk, 'status': status.pk, + 'urgente': False, + 'texto': 'teste', 'data_tramitacao': date(2016, 8, 20)}, follow=True) @@ -192,6 +201,8 @@ def test_create_tramitacao(admin_client): 'unidade_tramitacao_destino': unidade_tramitacao_destino_2.pk, 'documento': documento_adm.pk, 'status': status.pk, + 'urgente': False, + 'texto': 'teste', 'data_tramitacao': timezone.now().date() + timedelta( days=1)}, follow=True) @@ -211,6 +222,8 @@ def test_create_tramitacao(admin_client): 'unidade_tramitacao_destino': unidade_tramitacao_destino_2.pk, 'documento': documento_adm.pk, 'status': status.pk, + 'urgente': False, + 'texto': 'teste', 'data_tramitacao': date(2016, 8, 21), 'data_encaminhamento': date(2016, 8, 20)}, follow=True) @@ -230,6 +243,8 @@ def test_create_tramitacao(admin_client): 'unidade_tramitacao_destino': unidade_tramitacao_destino_2.pk, 'documento': documento_adm.pk, 'status': status.pk, + 'urgente': False, + 'texto': 'teste', 'data_tramitacao': date(2016, 8, 21), 'data_fim_prazo': date(2016, 8, 20)}, follow=True) @@ -249,6 +264,8 @@ def test_create_tramitacao(admin_client): 'unidade_tramitacao_destino': unidade_tramitacao_destino_2.pk, 'documento': documento_adm.pk, 'status': status.pk, + 'urgente': False, + 'texto': 'teste', 'data_tramitacao': date(2016, 8, 21)}, follow=True) @@ -260,7 +277,7 @@ def test_create_tramitacao(admin_client): @pytest.mark.django_db(transaction=False) def test_anular_protocolo_dados_invalidos(): - form = AnularProcoloAdmForm(data={}) + form = AnularProtocoloAdmForm(data={}) assert not form.is_valid() @@ -275,10 +292,10 @@ def test_anular_protocolo_dados_invalidos(): @pytest.mark.django_db(transaction=False) def test_anular_protocolo_form_anula_protocolo_inexistente(): - form = AnularProcoloAdmForm(data={'numero': '1', + form = AnularProtocoloAdmForm(data={'numero': '1', 'ano': '2017', 'justificativa_anulacao': 'teste' - }) + }) assert not form.is_valid() @@ -290,10 +307,10 @@ def test_anular_protocolo_form_anula_protocolo_inexistente(): def test_anular_protocolo_form_anula_protocolo_anulado(): mommy.make(Protocolo, numero=1, ano=2017, anulado=True) - form = AnularProcoloAdmForm(data={'numero': '1', + form = AnularProtocoloAdmForm(data={'numero': '1', 'ano': '2017', 'justificativa_anulacao': 'teste' - }) + }) assert not form.is_valid() @@ -315,10 +332,10 @@ def test_anular_protocolo_form_anula_protocolo_com_doc_vinculado(): ano=2017, numero_protocolo=1) - form = AnularProcoloAdmForm(data={'numero': '1', + form = AnularProtocoloAdmForm(data={'numero': '1', 'ano': '2017', 'justificativa_anulacao': 'teste' - }) + }) assert not form.is_valid() @@ -337,10 +354,10 @@ def test_anular_protocolo_form_anula_protocolo_com_doc_vinculado(): mommy.make(DocumentoAdministrativo, protocolo=protocolo_documento) - form = AnularProcoloAdmForm(data={'numero': '2', + form = AnularProtocoloAdmForm(data={'numero': '2', 'ano': '2017', 'justificativa_anulacao': 'teste' - }) + }) assert not form.is_valid() @@ -390,8 +407,11 @@ def test_documento_administrativo_protocolo_inexistente(): assert form.errors['__all__'] == [_('Protocolo 11/2017 inexistente.')] +@pytest.mark.django_db(transaction=False) def test_protocolo_documento_form_invalido(): + config = mommy.make(AppConfig) + form = ProtocoloDocumentForm( data={}, initial={ @@ -414,8 +434,11 @@ def test_protocolo_documento_form_invalido(): assert len(errors) == 6 +@pytest.mark.django_db(transaction=False) def test_protocolo_materia_invalido(): + config = mommy.make(AppConfig) + form = ProtocoloMateriaForm(data={}, initial={ 'user_data_hora_manual': '', @@ -436,3 +459,247 @@ def test_protocolo_materia_invalido(): assert errors['vincular_materia'] == [_('Este campo é obrigatório.')] assert len(errors) == 7 + + +@pytest.mark.django_db(transaction=False) +def test_lista_documentos_anexados(): + tipo_documento = mommy.make( + TipoDocumentoAdministrativo, + descricao="Tipo_Teste" + ) + documento_principal = mommy.make( + DocumentoAdministrativo, + numero=20, + ano=2018, + data="2018-01-04", + tipo=tipo_documento + ) + documento_anexado = mommy.make( + DocumentoAdministrativo, + numero=21, + ano=2019, + data="2019-05-04", + tipo=tipo_documento + ) + documento_anexado_anexado = mommy.make( + DocumentoAdministrativo, + numero=22, + ano=2020, + data="2020-01-05", + tipo=tipo_documento + ) + + mommy.make( + Anexado, + documento_principal=documento_principal, + documento_anexado=documento_anexado, + data_anexacao="2019-05-11" + ) + mommy.make( + Anexado, + documento_principal=documento_anexado, + documento_anexado=documento_anexado_anexado, + data_anexacao="2020-11-05" + ) + + lista = lista_anexados(documento_principal, False) + + assert len(lista) == 2 + assert lista[0] == documento_anexado + assert lista[1] == documento_anexado_anexado + + +@pytest.mark.django_db(transaction=False) +def make_unidade_tramitacao(descricao): + # Cria uma comissão para ser a unidade de tramitação + tipo_comissao = mommy.make(TipoComissao) + comissao = mommy.make(Comissao, + tipo=tipo_comissao, + nome=descricao, + sigla='T', + data_criacao='2016-03-21') + + # Testa a comissão + assert comissao.tipo == tipo_comissao + assert comissao.nome == descricao + + # Cria a unidade + unidade = mommy.make(UnidadeTramitacao, comissao=comissao) + assert unidade.comissao == comissao + + return unidade + + +@pytest.mark.django_db(transaction=False) +def test_tramitacoes_documentos_anexados(admin_client): + tipo_documento = mommy.make( + TipoDocumentoAdministrativo, + descricao="Tipo_Teste" + ) + documento_principal = mommy.make( + DocumentoAdministrativo, + numero=20, + ano=2018, + data="2018-01-04", + tipo=tipo_documento + ) + documento_anexado = mommy.make( + DocumentoAdministrativo, + numero=21, + ano=2019, + data="2019-05-04", + tipo=tipo_documento + ) + documento_anexado_anexado = mommy.make( + DocumentoAdministrativo, + numero=22, + ano=2020, + data="2020-01-05", + tipo=tipo_documento + ) + + mommy.make( + Anexado, + documento_principal=documento_principal, + documento_anexado=documento_anexado, + data_anexacao="2019-05-11" + ) + mommy.make( + Anexado, + documento_principal=documento_anexado, + documento_anexado=documento_anexado_anexado, + data_anexacao="2020-11-05" + ) + + + unidade_tramitacao_local_1 = make_unidade_tramitacao(descricao="Teste 1") + unidade_tramitacao_destino_1 = make_unidade_tramitacao(descricao="Teste 2") + unidade_tramitacao_destino_2 = make_unidade_tramitacao(descricao="Teste 3") + + status = mommy.make( + StatusTramitacaoAdministrativo, + indicador='R') + + # Teste criação de Tramitacao + form = TramitacaoAdmForm(data={}) + form.data = {'data_tramitacao':date(2019, 5, 6), + 'unidade_tramitacao_local':unidade_tramitacao_local_1.pk, + 'unidade_tramitacao_destino':unidade_tramitacao_destino_1.pk, + 'status':status.pk, + 'urgente': False, + 'texto': "Texto de teste"} + form.instance.documento_id=documento_principal.pk + + assert form.is_valid() + tramitacao_principal = form.save() + tramitacao_anexada = documento_anexado.tramitacaoadministrativo_set.last() + tramitacao_anexada_anexada = documento_anexado_anexado.tramitacaoadministrativo_set.last() + + # Verifica se foram criadas as tramitações para os documentos anexados e anexados aos anexados + assert documento_principal.tramitacaoadministrativo_set.last() == tramitacao_principal + assert tramitacao_principal.documento.tramitacao == (tramitacao_principal.status.indicador != "F") + assert compara_tramitacoes_doc(tramitacao_principal, tramitacao_anexada) + assert DocumentoAdministrativo.objects.get(id=documento_anexado.pk).tramitacao \ + == (tramitacao_anexada.status.indicador != "F") + assert compara_tramitacoes_doc(tramitacao_anexada_anexada, tramitacao_principal) + assert DocumentoAdministrativo.objects.get(id=documento_anexado_anexado.pk).tramitacao \ + == (tramitacao_anexada_anexada.status.indicador != "F") + + + # Teste Edição de Tramitacao + form = TramitacaoAdmEditForm(data={}) + # Alterando unidade_tramitacao_destino + form.data = {'data_tramitacao':tramitacao_principal.data_tramitacao, + 'unidade_tramitacao_local':tramitacao_principal.unidade_tramitacao_local.pk, + 'unidade_tramitacao_destino':unidade_tramitacao_destino_2.pk, + 'status':tramitacao_principal.status.pk, + 'urgente': tramitacao_principal.urgente, + 'texto': tramitacao_principal.texto} + form.instance = tramitacao_principal + + assert form.is_valid() + tramitacao_principal = form.save() + tramitacao_anexada = documento_anexado.tramitacaoadministrativo_set.last() + tramitacao_anexada_anexada = documento_anexado_anexado.tramitacaoadministrativo_set.last() + + assert tramitacao_principal.unidade_tramitacao_destino == unidade_tramitacao_destino_2 + assert tramitacao_anexada.unidade_tramitacao_destino == unidade_tramitacao_destino_2 + assert tramitacao_anexada_anexada.unidade_tramitacao_destino == unidade_tramitacao_destino_2 + + + # Teste Remoção de Tramitacao + url = reverse('sapl.protocoloadm:tramitacaoadministrativo_delete', + kwargs={'pk': tramitacao_principal.pk}) + response = admin_client.post(url, {'confirmar':'confirmar'} ,follow=True) + assert TramitacaoAdministrativo.objects.filter(id=tramitacao_principal.pk).count() == 0 + assert TramitacaoAdministrativo.objects.filter(id=tramitacao_anexada.pk).count() == 0 + assert TramitacaoAdministrativo.objects.filter(id=tramitacao_anexada_anexada.pk).count() == 0 + + + # Testes para quando as tramitações das anexadas divergem + form = TramitacaoAdmForm(data={}) + form.data = {'data_tramitacao':date(2019, 5, 6), + 'unidade_tramitacao_local':unidade_tramitacao_local_1.pk, + 'unidade_tramitacao_destino':unidade_tramitacao_destino_1.pk, + 'status':status.pk, + 'urgente': False, + 'texto': "Texto de teste"} + form.instance.documento_id=documento_principal.pk + + assert form.is_valid() + tramitacao_principal = form.save() + tramitacao_anexada = documento_anexado.tramitacaoadministrativo_set.last() + tramitacao_anexada_anexada = documento_anexado_anexado.tramitacaoadministrativo_set.last() + + form = TramitacaoAdmEditForm(data={}) + # Alterando unidade_tramitacao_destino + form.data = {'data_tramitacao':tramitacao_anexada.data_tramitacao, + 'unidade_tramitacao_local':tramitacao_anexada.unidade_tramitacao_local.pk, + 'unidade_tramitacao_destino':unidade_tramitacao_destino_2.pk, + 'status':tramitacao_anexada.status.pk, + 'urgente': tramitacao_anexada.urgente, + 'texto': tramitacao_anexada.texto} + form.instance = tramitacao_anexada + + assert form.is_valid() + tramitacao_anexada = form.save() + tramitacao_anexada_anexada = documento_anexado_anexado.tramitacaoadministrativo_set.last() + + assert tramitacao_principal.unidade_tramitacao_destino == unidade_tramitacao_destino_1 + assert tramitacao_anexada.unidade_tramitacao_destino == unidade_tramitacao_destino_2 + assert tramitacao_anexada_anexada.unidade_tramitacao_destino == unidade_tramitacao_destino_2 + + # Editando a tramitação principal, as tramitações anexadas não devem ser editadas + form = TramitacaoAdmEditForm(data={}) + # Alterando o texto + form.data = {'data_tramitacao':tramitacao_principal.data_tramitacao, + 'unidade_tramitacao_local':tramitacao_principal.unidade_tramitacao_local.pk, + 'unidade_tramitacao_destino':tramitacao_principal.unidade_tramitacao_destino.pk, + 'status':tramitacao_principal.status.pk, + 'urgente': tramitacao_principal.urgente, + 'texto': "Testando a alteração"} + form.instance = tramitacao_principal + + assert form.is_valid() + tramitacao_principal = form.save() + tramitacao_anexada = documento_anexado.tramitacaoadministrativo_set.last() + tramitacao_anexada_anexada = documento_anexado_anexado.tramitacaoadministrativo_set.last() + + assert tramitacao_principal.texto == "Testando a alteração" + assert not tramitacao_anexada.texto == "Testando a alteração" + assert not tramitacao_anexada_anexada.texto == "Testando a alteração" + + # Removendo a tramitação pricipal, as tramitações anexadas não devem ser removidas, pois divergiram + url = reverse('sapl.protocoloadm:tramitacaoadministrativo_delete', + kwargs={'pk': tramitacao_principal.pk}) + response = admin_client.post(url, {'confirmar':'confirmar'} ,follow=True) + assert TramitacaoAdministrativo.objects.filter(id=tramitacao_principal.pk).count() == 0 + assert TramitacaoAdministrativo.objects.filter(id=tramitacao_anexada.pk).count() == 1 + assert TramitacaoAdministrativo.objects.filter(id=tramitacao_anexada_anexada.pk).count() == 1 + + # Removendo a tramitação anexada, a tramitação anexada à anexada deve ser removida + url = reverse('sapl.protocoloadm:tramitacaoadministrativo_delete', + kwargs={'pk': tramitacao_anexada.pk}) + response = admin_client.post(url, {'confirmar':'confirmar'} ,follow=True) + assert TramitacaoAdministrativo.objects.filter(id=tramitacao_anexada.pk).count() == 0 + assert TramitacaoAdministrativo.objects.filter(id=tramitacao_anexada_anexada.pk).count() == 0 diff --git a/sapl/protocoloadm/urls.py b/sapl/protocoloadm/urls.py index 67d5edb65..e5925204d 100644 --- a/sapl/protocoloadm/urls.py +++ b/sapl/protocoloadm/urls.py @@ -21,7 +21,8 @@ from sapl.protocoloadm.views import (AcompanhamentoDocumentoView, atualizar_numero_documento, doc_texto_integral, DesvincularDocumentoView, - DesvincularMateriaView) + DesvincularMateriaView, + AnexadoCrud, DocumentoAnexadoEmLoteView) from .apps import AppConfig @@ -30,6 +31,7 @@ app_name = AppConfig.name urlpatterns_documento_administrativo = [ url(r'^docadm/', include(DocumentoAdministrativoCrud.get_urls() + + AnexadoCrud.get_urls() + TramitacaoAdmCrud.get_urls() + DocumentoAcessorioAdministrativoCrud.get_urls())), @@ -38,6 +40,9 @@ urlpatterns_documento_administrativo = [ url(r'^docadm/texto_integral/(?P\d+)$', doc_texto_integral, name='doc_texto_integral'), + + url(r'^docadm/(?P\d+)/anexado_em_lote', DocumentoAnexadoEmLoteView.as_view(), + name='anexado_em_lote'), ] urlpatterns_protocolo = [ diff --git a/sapl/protocoloadm/views.py b/sapl/protocoloadm/views.py index a750d1881..6296758ee 100755 --- a/sapl/protocoloadm/views.py +++ b/sapl/protocoloadm/views.py @@ -17,7 +17,7 @@ from django.http.response import HttpResponseRedirect from django.shortcuts import redirect from django.utils import timezone from django.utils.translation import ugettext_lazy as _ -from django.views.generic import ListView, CreateView +from django.views.generic import ListView, CreateView, UpdateView from django.views.generic.base import RedirectView, TemplateView from django.views.generic.edit import FormView from django_filters.views import FilterView @@ -27,16 +27,17 @@ from sapl.base.email_utils import do_envia_email_confirmacao from sapl.base.models import Autor, CasaLegislativa from sapl.base.signals import tramitacao_signal from sapl.comissoes.models import Comissao -from sapl.crud.base import Crud, CrudAux, MasterDetailCrud, make_pagination +from sapl.crud.base import (Crud, CrudAux, MasterDetailCrud, make_pagination, + RP_LIST, RP_DETAIL) from sapl.materia.models import MateriaLegislativa, TipoMateriaLegislativa from sapl.materia.views import gerar_pdf_impressos from sapl.parlamentares.models import Legislatura, Parlamentar from sapl.protocoloadm.models import Protocolo from sapl.utils import (create_barcode, get_base_url, get_client_ip, - get_mime_type_from_file_extension, + get_mime_type_from_file_extension, lista_anexados, show_results_filter_set, mail_service_configured) -from .forms import (AcompanhamentoDocumentoForm, AnularProcoloAdmForm, +from .forms import (AcompanhamentoDocumentoForm, AnularProtocoloAdmForm, DocumentoAcessorioAdministrativoForm, DocumentoAdministrativoFilterSet, DocumentoAdministrativoForm, FichaPesquisaAdmForm, FichaSelecionaAdmForm, ProtocoloDocumentForm, @@ -44,10 +45,12 @@ from .forms import (AcompanhamentoDocumentoForm, AnularProcoloAdmForm, TramitacaoAdmEditForm, TramitacaoAdmForm, DesvincularDocumentoForm, DesvincularMateriaForm, filtra_tramitacao_adm_destino_and_status, - filtra_tramitacao_adm_destino, filtra_tramitacao_adm_status) + filtra_tramitacao_adm_destino, filtra_tramitacao_adm_status, + AnexadoForm, AnexadoEmLoteFilterSet, + compara_tramitacoes_doc) from .models import (AcompanhamentoDocumento, DocumentoAcessorioAdministrativo, DocumentoAdministrativo, StatusTramitacaoAdministrativo, - TipoDocumentoAdministrativo, TramitacaoAdministrativo) + TipoDocumentoAdministrativo, TramitacaoAdministrativo, Anexado) TipoDocumentoAdministrativoCrud = CrudAux.build( @@ -243,7 +246,7 @@ class AcompanhamentoDocumentoView(CreateView): "documento", documento, destinatario) - self.logger.info('user={} .Foi enviado um e-mail de confirmação. Confira sua caixa ' + self.logger.info('user={}. Foi enviado um e-mail de confirmação. Confira sua caixa ' 'de mensagens e clique no link que nós enviamos para ' 'confirmar o acompanhamento deste documento.'.format(usuario.username)) msg = _('Foi enviado um e-mail de confirmação. Confira sua caixa \ @@ -251,19 +254,50 @@ class AcompanhamentoDocumentoView(CreateView): confirmar o acompanhamento deste documento.') messages.add_message(request, messages.SUCCESS, msg) + # Se o elemento existir e o email não foi confirmado: + # gerar novo hash e reenviar mensagem de email + elif not acompanhar[0].confirmado: + acompanhar = acompanhar[0] + acompanhar.hash = hash_txt + acompanhar.save() + + base_url = get_base_url(request) + + destinatario = AcompanhamentoDocumento.objects.get( + documento=documento, + email=email, + confirmado=False + ) + + casa = CasaLegislativa.objects.first() + + do_envia_email_confirmacao(base_url, + casa, + "documento", + documento, + destinatario) + + self.logger.info('user={}. Foi enviado um e-mail de confirmação. Confira sua caixa \ + de mensagens e clique no link que nós enviamos para \ + confirmar o acompanhamento deste documento.'.format(usuario.username)) + + msg = _('Foi enviado um e-mail de confirmação. Confira sua caixa \ + de mensagens e clique no link que nós enviamos para \ + confirmar o acompanhamento deste documento.') + messages.add_message(request, messages.SUCCESS, msg) + # Caso esse Acompanhamento já exista # avisa ao usuário que esse documento já está sendo acompanhado else: self.logger.info('user=' + request.user.username + '. Este e-mail já está acompanhando esse documento (pk={}).'.format(pk)) msg = _('Este e-mail já está acompanhando esse documento.') - messages.add_message(request, messages.INFO, msg) + messages.add_message(request, messages.ERROR, msg) return self.render_to_response( {'form': form, 'documento': documento, - 'error': _('Esse documento já está\ - sendo acompanhada por este e-mail.')}) + }) return HttpResponseRedirect(self.get_success_url()) else: return self.render_to_response( @@ -451,7 +485,7 @@ class ProtocoloListView(PermissionRequiredMixin, ListView): class AnularProtocoloAdmView(PermissionRequiredMixin, CreateView): template_name = 'protocoloadm/anular_protocoloadm.html' - form_class = AnularProcoloAdmForm + form_class = AnularProtocoloAdmForm form_valid_message = _('Protocolo anulado com sucesso!') permission_required = ('protocoloadm.action_anular_protocolo', ) @@ -504,12 +538,12 @@ class ProtocoloDocumentoView(PermissionRequiredMixin, def form_valid(self, form): protocolo = form.save(commit=False) username = self.request.user.username - try: - self.logger.debug("user=" + username + - ". Tentando obter sequência de numeração.") - numeracao = sapl.base.models.AppConfig.objects.last( - ).sequencia_numeracao - except AttributeError as e: + + self.logger.debug("user=" + username + + ". Tentando obter sequência de numeração.") + numeracao = sapl.base.models.AppConfig.objects.last( + ).sequencia_numeracao_protocolo + if not numeracao: self.logger.error("user=" + username + ". É preciso definir a sequencia de " "numeração na tabelas auxiliares! " + str(e)) msg = _('É preciso definir a sequencia de ' + @@ -691,12 +725,11 @@ class ProtocoloMateriaView(PermissionRequiredMixin, CreateView): def form_valid(self, form): protocolo = form.save(commit=False) username = self.request.user.username - try: - self.logger.debug("user=" + username + - ". Tentando obter sequência de numeração.") - numeracao = sapl.base.models.AppConfig.objects.last( - ).sequencia_numeracao - except AttributeError: + self.logger.debug("user=" + username + + ". Tentando obter sequência de numeração.") + numeracao = sapl.base.models.AppConfig.objects.last( + ).sequencia_numeracao_protocolo + if not numeracao: self.logger.error("user=" + username + ". É preciso definir a sequencia de " "numeração na tabelas auxiliares!") msg = _('É preciso definir a sequencia de ' + @@ -910,6 +943,154 @@ class PesquisarDocumentoAdministrativoView(DocumentoAdministrativoMixin, return self.render_to_response(context) +class AnexadoCrud(MasterDetailCrud): + model = Anexado + parent_field = 'documento_principal' + help_topic = 'documento_anexado' + public = [RP_LIST, RP_DETAIL] + + class BaseMixin(MasterDetailCrud.BaseMixin): + list_field_names = ['documento_anexado', 'data_anexacao'] + + class CreateView(MasterDetailCrud.CreateView): + form_class = AnexadoForm + + class UpdateView(MasterDetailCrud.UpdateView): + form_class = AnexadoForm + + def get_initial(self): + initial = super(UpdateView, self).get_initial() + initial['tipo'] = self.object.documento_anexado.tipo.id + initial['numero'] = self.object.documento_anexado.numero + initial['ano'] = self.object.documento_anexado.ano + return initial + + class DetailView(MasterDetailCrud.DetailView): + + @property + def layout_key(self): + return 'AnexadoDetail' + + +class DocumentoAnexadoEmLoteView(PermissionRequiredMixin, FilterView): + filterset_class = AnexadoEmLoteFilterSet + template_name = 'protocoloadm/em_lote/anexado.html' + permission_required = ('protocoloadm.add_anexado', ) + + def get_context_data(self, **kwargs): + context = super( + DocumentoAnexadoEmLoteView,self + ).get_context_data(**kwargs) + + context['root_pk'] = self.kwargs['pk'] + + context['subnav_template_name'] = 'protocoloadm/subnav.yaml' + + context['title'] = _('Documentos Anexados em Lote') + + # Verifica se os campos foram preenchidos + if not self.request.GET.get('tipo', " "): + msg =_('Por favor, selecione um tipo de documento.') + messages.add_message(self.request, messages.ERROR, msg) + + if not self.request.GET.get('data_0', " ") or not self.request.GET.get('data_1', " "): + msg =_('Por favor, preencha as datas.') + messages.add_message(self.request, messages.ERROR, msg) + + return context + + if not self.request.GET.get('data_0', " ") or not self.request.GET.get('data_1', " "): + msg =_('Por favor, preencha as datas.') + messages.add_message(self.request, messages.ERROR, msg) + return context + + qr = self.request.GET.copy() + context['temp_object_list'] = context['object_list'].order_by( + 'numero', '-ano' + ) + + context['object_list'] = [] + for obj in context['temp_object_list']: + if not obj.pk == int(context['root_pk']): + documento_principal = DocumentoAdministrativo.objects.get(id=context['root_pk']) + documento_anexado = obj + is_anexado = Anexado.objects.filter(documento_principal=documento_principal, + documento_anexado=documento_anexado).exists() + if not is_anexado: + ciclico = False + anexados_anexado = Anexado.objects.filter(documento_principal=documento_anexado) + + while anexados_anexado and not ciclico: + anexados = [] + + for anexo in anexados_anexado: + + if documento_principal == anexo.documento_anexado: + ciclico = True + else: + for a in Anexado.objects.filter(documento_principal=anexo.documento_anexado): + anexados.append(a) + + anexados_anexado = anexados + + if not ciclico: + context['object_list'].append(obj) + + context['numero_res'] = len(context['object_list']) + + context['filter_url'] = ('&' + qr.urlencode()) if len(qr) > 0 else '' + + context['show_results'] = show_results_filter_set(qr) + + return context + + def post(self, request, *args, **kwargs): + marcados = request.POST.getlist('documento_id') + + data_anexacao = datetime.strptime( + request.POST['data_anexacao'], "%d/%m/%Y" + ).date() + + if request.POST['data_desanexacao'] == '': + data_desanexacao = None + v_data_desanexacao = data_anexacao + else: + data_desanexacao = datetime.strptime( + request.POST['data_desanexacao'], "%d/%m/%Y" + ).date() + v_data_desanexacao = data_desanexacao + + if len(marcados) == 0: + msg =_('Nenhum documento foi selecionado') + messages.add_message(request, messages.ERROR, msg) + + if data_anexacao > v_data_desanexacao: + msg=_('Data de anexação posterior à data de desanexação.') + messages.add_message(request, messages.ERROR, msg) + + return self.get(request, self.kwargs) + + if data_anexacao > v_data_desanexacao: + msg =_('Data de anexação posterior à data de desanexação.') + messages.add_message(request, messages.ERROR, msg) + return self.get(request, messages.ERROR, msg) + + principal = DocumentoAdministrativo.objects.get(pk = kwargs['pk']) + for documento in DocumentoAdministrativo.objects.filter(id__in = marcados): + anexado = Anexado() + anexado.documento_principal = principal + anexado.documento_anexado = documento + anexado.data_anexacao = data_anexacao + anexado.data_desanexacao = data_desanexacao + anexado.save() + + msg = _('Documento(s) anexado(s).') + messages.add_message(request, messages.SUCCESS, msg) + + success_url = reverse('sapl_index') + 'docadm/' + kwargs['pk'] + '/anexado' + return HttpResponseRedirect(success_url) + + class TramitacaoAdmCrud(MasterDetailCrud): model = TramitacaoAdministrativo parent_field = 'documento' @@ -923,6 +1104,10 @@ class TramitacaoAdmCrud(MasterDetailCrud): form_class = TramitacaoAdmForm logger = logging.getLogger(__name__) + def get_success_url(self): + return reverse('sapl.protocoloadm:tramitacaoadministrativo_list', kwargs={ + 'pk': self.kwargs['pk']}) + def get_initial(self): initial = super(CreateView, self).get_initial() local = DocumentoAdministrativo.objects.get( @@ -936,10 +1121,33 @@ class TramitacaoAdmCrud(MasterDetailCrud): else: initial['unidade_tramitacao_local'] = '' initial['data_tramitacao'] = timezone.now().date() + initial['ip'] = get_client_ip(self.request) + initial['user'] = self.request.user return initial def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) + username = self.request.user.username + + ultima_tramitacao = TramitacaoAdministrativo.objects.filter( + documento_id=self.kwargs['pk']).order_by( + '-data_tramitacao', + '-timestamp', + '-id').first() + + #TODO: Esta checagem foi inserida na issue #2027, mas é mesmo necessária? + if ultima_tramitacao: + if ultima_tramitacao.unidade_tramitacao_destino: + context['form'].fields[ + 'unidade_tramitacao_local'].choices = [ + (ultima_tramitacao.unidade_tramitacao_destino.pk, + ultima_tramitacao.unidade_tramitacao_destino)] + else: + self.logger.error('user=' + username + '. Unidade de tramitação destino ' + 'da última tramitação não pode ser vazia!') + msg = _('Unidade de tramitação destino ' + ' da última tramitação não pode ser vazia!') + messages.add_message(self.request, messages.ERROR, msg) primeira_tramitacao = not(TramitacaoAdministrativo.objects.filter( documento_id=int(kwargs['root_pk'])).exists()) @@ -959,7 +1167,6 @@ class TramitacaoAdmCrud(MasterDetailCrud): post=self.object, request=self.request) except Exception as e: - # TODO log error self.logger.error('user=' + username + '. Tramitação criada, mas e-mail de acompanhamento de documento ' 'não enviado. A não configuração do servidor de e-mail ' 'impede o envio de aviso de tramitação. ' + str(e)) @@ -974,6 +1181,12 @@ class TramitacaoAdmCrud(MasterDetailCrud): form_class = TramitacaoAdmEditForm logger = logging.getLogger(__name__) + def get_initial(self): + initial = super(UpdateView, self).get_initial() + initial['ip'] = get_client_ip(self.request) + initial['user'] = self.request.user + return initial + def form_valid(self, form): self.object = form.save() username = self.request.user.username @@ -982,7 +1195,6 @@ class TramitacaoAdmCrud(MasterDetailCrud): post=self.object, request=self.request) except Exception as e: - # TODO log error self.logger.error('user=' + username + '. Tramitação criada, mas e-mail de acompanhamento de documento ' 'não enviado. A não configuração do servidor de e-mail ' 'impede o envio de aviso de tramitação. ' + str(e)) @@ -1003,18 +1215,26 @@ class TramitacaoAdmCrud(MasterDetailCrud): class DetailView(DocumentoAdministrativoMixin, MasterDetailCrud.DetailView): - pass + + template_name = 'protocoloadm/tramitacaoadministrativo_detail.html' + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + context['user'] = self.request.user + return context + class DeleteView(MasterDetailCrud.DeleteView): + logger = logging.getLogger(__name__) + def delete(self, request, *args, **kwargs): tramitacao = TramitacaoAdministrativo.objects.get( id=self.kwargs['pk']) - documento = DocumentoAdministrativo.objects.get( - id=tramitacao.documento.id) + documento = tramitacao.documento url = reverse( 'sapl.protocoloadm:tramitacaoadministrativo_list', - kwargs={'pk': tramitacao.documento.id}) + kwargs={'pk': documento.id}) ultima_tramitacao = \ documento.tramitacaoadministrativo_set.order_by( @@ -1022,11 +1242,21 @@ class TramitacaoAdmCrud(MasterDetailCrud): '-id').first() if tramitacao.pk != ultima_tramitacao.pk: + username = request.user.username + self.logger.error("user=" + username + ". Não é possível deletar a tramitação de pk={}. " + "Somente a última tramitação (pk={}) pode ser deletada!." + .format(tramitacao.pk, ultima_tramitacao.pk)) msg = _('Somente a última tramitação pode ser deletada!') messages.add_message(request, messages.ERROR, msg) return HttpResponseRedirect(url) else: - tramitacao.delete() + tramitacoes_deletar = [tramitacao.id] + docs_anexados = lista_anexados(documento, False) + for da in docs_anexados: + tram_anexada = da.tramitacaoadministrativo_set.last() + if compara_tramitacoes_doc(tram_anexada, tramitacao): + tramitacoes_deletar.append(tram_anexada.id) + TramitacaoAdministrativo.objects.filter(id__in=tramitacoes_deletar).delete() return HttpResponseRedirect(url) @@ -1121,7 +1351,7 @@ class ImpressosView(PermissionRequiredMixin, TemplateView): class FichaPesquisaAdmView(PermissionRequiredMixin, FormView): form_class = FichaPesquisaAdmForm - template_name = 'materia/impressos/ficha.html' + template_name = 'materia/impressos/impressos_form.html' permission_required = ('materia.can_access_impressos', ) def form_valid(self, form): @@ -1139,7 +1369,7 @@ class FichaPesquisaAdmView(PermissionRequiredMixin, FormView): class FichaSelecionaAdmView(PermissionRequiredMixin, FormView): logger = logging.getLogger(__name__) form_class = FichaSelecionaAdmForm - template_name = 'materia/impressos/ficha_seleciona.html' + template_name = 'materia/impressos/impressos_form.html' permission_required = ('materia.can_access_impressos', ) def get_context_data(self, **kwargs): @@ -1201,8 +1431,8 @@ class FichaSelecionaAdmView(PermissionRequiredMixin, FormView): self.messages.add_message(self.request, messages.INFO, mensagem) return self.render_to_response(context) - if len(documento.assunto) > 301: - documento.assunto = documento.assunto[0:300] + '[...]' + if len(documento.assunto) > 201: + documento.assunto = documento.assunto[0:200] + '[...]' context['documento'] = documento return gerar_pdf_impressos(self.request, context, diff --git a/sapl/relatorios/templates/pdf_etiqueta_protocolo_gerar.py b/sapl/relatorios/templates/pdf_etiqueta_protocolo_gerar.py index 8b9c3b2cc..027bb7f88 100755 --- a/sapl/relatorios/templates/pdf_etiqueta_protocolo_gerar.py +++ b/sapl/relatorios/templates/pdf_etiqueta_protocolo_gerar.py @@ -23,7 +23,7 @@ def cabecalho(dic_cabecalho, imagem): tmp_data += '\t\t\t\t\n' tmp_data += '\t\t\t\tSistema de Apoio ao Processo Legislativo\n' tmp_data += '\t\t\t\t\n' - tmp_data += '\t\t\t\tRelatório de Controle do Protocolo\n' + tmp_data += '\t\t\t\tRelatório de Controle do Protocolo\n' return tmp_data @@ -36,7 +36,7 @@ def rodape(lst_rodape): tmp_data += '\t\t\t\t\n' tmp_data += '\t\t\t\t' + \ lst_rodape[2] + '\n' - tmp_data += '\t\t\t\tPágina \n' + tmp_data += '\t\t\t\tPágina \n' tmp_data += '\t\t\t\t' + \ lst_rodape[0] + '\n' tmp_data += '\t\t\t\t' + \ @@ -58,7 +58,7 @@ def paraStyle(): tmp_data += '\t\t\t\n' tmp_data += '\t\t\n' tmp_data += '\t\t\n' - tmp_data += '\t\t\n' + tmp_data += '\t\t\n' tmp_data += '\t\n' return tmp_data @@ -122,7 +122,7 @@ def principal(imagem, lst_protocolos, dic_cabecalho, lst_rodape): tmp_data += '\t\n' diff --git a/sapl/relatorios/templates/pdf_sessao_plenaria_gerar.py b/sapl/relatorios/templates/pdf_sessao_plenaria_gerar.py index 02cea969d..4bbe31bed 100644 --- a/sapl/relatorios/templates/pdf_sessao_plenaria_gerar.py +++ b/sapl/relatorios/templates/pdf_sessao_plenaria_gerar.py @@ -1,19 +1,18 @@ -##parameters=rodape_dic, sessao='', imagem, inf_basicas_dic, lst_mesa, lst_presenca_sessao, lst_expedientes, lst_expediente_materia, lst_oradores_expediente, lst_presenca_ordem_dia, lst_votacao, lst_oradores +# #parameters=rodape_dic, sessao='', imagem, inf_basicas_dic, lst_mesa, lst_presenca_sessao, lst_expedientes, lst_expediente_materia, lst_oradores_expediente, lst_presenca_ordem_dia, lst_votacao, lst_oradores """Script para geração do PDF das sessoes plenarias Autor: Gustavo Lepri Atualizado por Luciano De Fázio - 22/03/2012 versão: 1.0 """ -import time import os - +import time +import logging from django.template.defaultfilters import safe from django.utils.html import strip_tags +from trml2pdf import parseString from sapl.sessao.models import ResumoOrdenacao -from trml2pdf import parseString - def cabecalho(inf_basicas_dic, imagem): """ @@ -124,7 +123,25 @@ def inf_basicas(inf_basicas_dic): dat_inicio_sessao + ' - ' + hr_inicio_sessao + '\n' tmp += '\t\tEncerramento: ' + \ - dat_fim_sessao + ' - ' + hr_fim_sessao + '\n' + dat_fim_sessao + ' - ' + hr_fim_sessao + '\n' + + return tmp + + +def multimidia(cont_mult_dic): + """ + """ + tmp = "" + + mul_audio = cont_mult_dic['multimidia_audio'] + mul_video = cont_mult_dic['multimidia_video'] + + tmp += '\t\tConteúdo Multimídia\n' + tmp += '\t\t\n' + tmp += '\t\t\t
    \n' + tmp += '\t\t
    \n' + tmp += '\t\tAudio: ' + mul_audio + '\n' + tmp += '\t\tVideo: ' + mul_video + '\n' return tmp @@ -145,7 +162,7 @@ def mesa(lst_mesa): return tmp -def presenca(lst_presenca_sessao,lst_ausencia_sessao): +def presenca(lst_presenca_sessao, lst_ausencia_sessao): """ """ @@ -202,14 +219,18 @@ def expediente_materia(lst_expediente_materia): tmp += '\t\t\n' tmp += '\t\t\t
    \n' tmp += '\t\t
    \n' - tmp += '\n' + tmp += '>\n' tmp += 'MatériaEmentaResultado da Votação\n' for expediente_materia in lst_expediente_materia: tmp += '' + str(expediente_materia['num_ordem']) + ' - ' + expediente_materia['id_materia'] + '\n' + 'Turno: ' + expediente_materia[ - 'des_turno'] + '\n' + ''+ expediente_materia['num_autores'] + ': ' + str(expediente_materia['nom_autor']) + '\n' + 'des_turno'] + '\n' + '' + expediente_materia['num_autores'] + ': ' + str(expediente_materia['nom_autor']) + '\n' + txt_ementa = expediente_materia['txt_ementa'].replace('&', '&') - if len(txt_ementa) > 1000: - txt_ementa = txt_ementa[:1000] + "..." + + # txt_ementa = dont_break_out(expediente_materia['txt_ementa']) + + # if len(txt_ementa) > 800: + # txt_ementa = txt_ementa[:800] + "..." tmp += '' + txt_ementa + '' + '' + expediente_materia['ordem_observacao'] + '\n' tmp += '' + \ str(expediente_materia['nom_resultado']) + \ @@ -224,6 +245,30 @@ def expediente_materia(lst_expediente_materia): return tmp +def expediente_materia_vot_nom(lst_expediente_materia_vot_nom): + """ + """ + tmp = '' + tmp += '\t\tVotações Nominais - Matérias do Expediente\n\n' + tmp += '\t\t\n' + tmp += '\t\t\t
    \n' + tmp += '\t\t
    \n' + tmp += '\n' + tmp += 'MatériaVotos\n' + for expediente_materia_vot_nom in lst_expediente_materia_vot_nom: + tmp += '' + str(expediente_materia_vot_nom['titulo']) + '' + if expediente_materia_vot_nom['votos']: + tmp += '' + for v in expediente_materia_vot_nom['votos']: + tmp += '' + str(v.parlamentar) + ' - ' + v.voto + '' + tmp += '' + else: + tmp += 'Matéria não votada' + tmp += '\n' + tmp += '\t\t\n' + return tmp + + def oradores_expediente(lst_oradores_expediente): """ @@ -271,7 +316,7 @@ def votacao(lst_votacao): tmp += 'MatériaEmentaResultado da Votação\n' for votacao in lst_votacao: tmp += '' + str(votacao['num_ordem']) + ' - ' + votacao['id_materia'] + '\n' + 'Turno: ' + votacao[ - 'des_turno'] + '\n' + ''+ votacao['num_autores'] +': ' + str(votacao['nom_autor']) + '\n' + 'des_turno'] + '
    \n' + '' + votacao['num_autores'] + ': ' + str(votacao['nom_autor']) + '\n' txt_ementa = votacao['txt_ementa'].replace('&', '&') if len(txt_ementa) > 1000: txt_ementa = txt_ementa[:1000] + "..." @@ -289,6 +334,48 @@ def votacao(lst_votacao): return tmp +def votacao_vot_nom(lst_votacao_vot_nom): + """ + """ + tmp = '' + tmp += '\t\tVotações Nominais - Matérias da Ordem do Dia\n\n' + tmp += '\t\t\n' + tmp += '\t\t\t
    \n' + tmp += '\t\t
    \n' + tmp += '\n' + tmp += 'MatériaVotos\n' + for votacao_vot_nom in lst_votacao_vot_nom: + tmp += '' + str(votacao_vot_nom['titulo']) + '' + if votacao_vot_nom['votos']: + tmp += '' + for v in votacao_vot_nom['votos']: + tmp += '' + str(v.parlamentar) + ' - ' + v.voto + '' + tmp += '' + else: + tmp += 'Matéria não votada' + tmp += '\n' + tmp += '\t\t\n' + return tmp + + +def oradores_ordemdia(lst_oradores_ordemdia): + """ + + """ + tmp = '' + tmp += '\t\tOradores da Ordem do Dia\n' + tmp += '\t\t\n' + tmp += '\t\t\t
    \n' + tmp += '\t\t
    \n' + for orador_ordemdia in lst_oradores_ordemdia: + tmp += '\t\t' + \ + str(orador_ordemdia['num_ordem']) + ' - ' + \ + orador_ordemdia['nome_parlamentar'] + '/' + \ + str(orador_ordemdia['sigla']) + ' - ' + \ + str(orador_ordemdia['observacao']) + '\n' + return tmp + + def oradores(lst_oradores): """ @@ -323,10 +410,11 @@ def ocorrencias(lst_ocorrencias): return tmp -def principal(rodape_dic, imagem, inf_basicas_dic, lst_mesa, lst_presenca_sessao, lst_ausencia_sessao, lst_expedientes, lst_expediente_materia, lst_oradores_expediente, lst_presenca_ordem_dia, lst_votacao, lst_oradores, lst_ocorrencias): +def principal(rodape_dic, imagem, inf_basicas_dic, cont_mult_dic, lst_mesa, lst_presenca_sessao, lst_ausencia_sessao, lst_expedientes, lst_expediente_materia, lst_expediente_materia_vot_nom, lst_oradores_expediente, lst_presenca_ordem_dia, lst_votacao, lst_votacao_vot_nom, lst_oradores_ordemdia, lst_oradores, lst_ocorrencias): """ """ arquivoPdf = str(int(time.time() * 100)) + ".pdf" + logger = logging.getLogger(__name__) tmp = '' tmp += '\n' @@ -346,41 +434,69 @@ def principal(rodape_dic, imagem, inf_basicas_dic, lst_mesa, lst_presenca_sessao ordenacao = ResumoOrdenacao.objects.first() dict_ord_template = { - 'cont_mult': '', + 'cont_mult': multimidia(cont_mult_dic), 'exp': expedientes(lst_expedientes), 'id_basica': inf_basicas(inf_basicas_dic), - 'lista_p': presenca(lst_presenca_sessao,lst_ausencia_sessao), + 'lista_p': presenca(lst_presenca_sessao, lst_ausencia_sessao), 'lista_p_o_d': presenca_ordem_dia(lst_presenca_ordem_dia), 'mat_exp': expediente_materia(lst_expediente_materia), + 'v_n_mat_exp': expediente_materia_vot_nom(lst_expediente_materia_vot_nom), 'mat_o_d': votacao(lst_votacao), + 'v_n_mat_o_d': votacao_vot_nom(lst_votacao_vot_nom), 'mesa_d': mesa(lst_mesa), 'oradores_exped': oradores_expediente(lst_oradores_expediente), + 'oradores_o_d': oradores_ordemdia(lst_oradores_ordemdia), 'oradores_expli': oradores(lst_oradores), 'ocorr_sessao': ocorrencias(lst_ocorrencias) } - + if ordenacao: - tmp += dict_ord_template[ordenacao.primeiro] - tmp += dict_ord_template[ordenacao.segundo] - tmp += dict_ord_template[ordenacao.terceiro] - tmp += dict_ord_template[ordenacao.quarto] - tmp += dict_ord_template[ordenacao.quinto] - tmp += dict_ord_template[ordenacao.sexto] - tmp += dict_ord_template[ordenacao.setimo] - tmp += dict_ord_template[ordenacao.oitavo] - tmp += dict_ord_template[ordenacao.nono] - tmp += dict_ord_template[ordenacao.decimo] - tmp += dict_ord_template[ordenacao.decimo_primeiro] + try: + tmp += dict_ord_template[ordenacao.primeiro] + tmp += dict_ord_template[ordenacao.segundo] + tmp += dict_ord_template[ordenacao.terceiro] + tmp += dict_ord_template[ordenacao.quarto] + tmp += dict_ord_template[ordenacao.quinto] + tmp += dict_ord_template[ordenacao.sexto] + tmp += dict_ord_template[ordenacao.setimo] + tmp += dict_ord_template[ordenacao.oitavo] + tmp += dict_ord_template[ordenacao.nono] + tmp += dict_ord_template[ordenacao.decimo] + tmp += dict_ord_template[ordenacao.decimo_primeiro] + tmp += dict_ord_template[ordenacao.decimo_segundo] + tmp += dict_ord_template[ordenacao.decimo_terceiro] + tmp += dict_ord_template[ordenacao.decimo_quarto] + except KeyError as e: + logger.error("KeyError: " + str(e) + ". Erro ao tentar utilizar " + "configuração de ordenação. Utilizando ordenação padrão.") + tmp += inf_basicas(inf_basicas_dic) + tmp += multimidia(cont_mult_dic) + tmp += mesa(lst_mesa) + tmp += presenca(lst_presenca_sessao, lst_ausencia_sessao) + tmp += expedientes(lst_expedientes) + tmp += expediente_materia(lst_expediente_materia) + tmp += expediente_materia_vot_nom(lst_expediente_materia_vot_nom) + tmp += oradores_expediente(lst_oradores_expediente) + tmp += presenca_ordem_dia(lst_presenca_ordem_dia) + tmp += votacao(lst_votacao) + tmp += votacao_vot_nom(lst_votacao_vot_nom) + tmp += oradores_ordemdia(lst_oradores_ordemdia) + tmp += oradores(lst_oradores) + tmp += ocorrencias(lst_ocorrencias) else: tmp += inf_basicas(inf_basicas_dic) + tmp += multimidia(cont_mult_dic) tmp += mesa(lst_mesa) - tmp += presenca(lst_presenca_sessao,lst_ausencia_sessao) + tmp += presenca(lst_presenca_sessao, lst_ausencia_sessao) tmp += expedientes(lst_expedientes) tmp += expediente_materia(lst_expediente_materia) + tmp += expediente_materia_vot_nom(lst_expediente_materia_vot_nom) tmp += oradores_expediente(lst_oradores_expediente) tmp += presenca_ordem_dia(lst_presenca_ordem_dia) tmp += votacao(lst_votacao) + tmp += votacao_vot_nom(lst_votacao_vot_nom) + tmp += oradores_ordemdia(lst_oradores_ordemdia) tmp += oradores(lst_oradores) tmp += ocorrencias(lst_ocorrencias) diff --git a/sapl/relatorios/urls.py b/sapl/relatorios/urls.py index e31f5dcab..9ca2284a0 100644 --- a/sapl/relatorios/urls.py +++ b/sapl/relatorios/urls.py @@ -5,7 +5,8 @@ from .views import (relatorio_capa_processo, relatorio_documento_administrativo, relatorio_espelho, relatorio_etiqueta_protocolo, relatorio_materia, relatorio_ordem_dia, relatorio_pauta_sessao, - relatorio_protocolo, relatorio_sessao_plenaria) + relatorio_protocolo, relatorio_sessao_plenaria, + resumo_ata_pdf) app_name = AppConfig.name @@ -28,4 +29,6 @@ urlpatterns = [ relatorio_etiqueta_protocolo, name='relatorio_etiqueta_protocolo'), url(r'^relatorios/pauta-sessao/(?P\d+)/$', relatorio_pauta_sessao, name='relatorio_pauta_sessao'), + url(r'^relatorios/(?P\d+)/resumo_ata$', + resumo_ata_pdf, name='resumo_ata_pdf'), ] diff --git a/sapl/relatorios/views.py b/sapl/relatorios/views.py index aad44fc1d..72595b239 100755 --- a/sapl/relatorios/views.py +++ b/sapl/relatorios/views.py @@ -2,12 +2,15 @@ from datetime import datetime as dt import html import logging import re +import tempfile from django.core.exceptions import ObjectDoesNotExist from django.http import Http404, HttpResponse from django.utils import timezone from django.utils.translation import ugettext_lazy as _ +from django.template.loader import render_to_string +from sapl.settings import MEDIA_URL from sapl.base.models import Autor, CasaLegislativa from sapl.comissoes.models import Comissao from sapl.materia.models import (Autoria, MateriaLegislativa, Numeracao, @@ -19,16 +22,26 @@ from sapl.sessao.models import (ExpedienteMateria, ExpedienteSessao, IntegranteMesa, JustificativaAusencia, Orador, OradorExpediente, OrdemDia, PresencaOrdemDia, SessaoPlenaria, - SessaoPlenariaPresenca, OcorrenciaSessao) + SessaoPlenariaPresenca, OcorrenciaSessao, + RegistroVotacao, VotoParlamentar, OradorOrdemDia) from sapl.settings import STATIC_ROOT from sapl.utils import LISTA_DE_UFS, TrocaTag, filiacao_data +from sapl.sessao.views import (get_identificação_basica, get_mesa_diretora, + get_presenca_sessao, get_expedientes, + get_materias_expediente, get_oradores_expediente, + get_presenca_ordem_do_dia, get_materias_ordem_do_dia, + get_oradores_ordemdia, + get_oradores_explicações_pessoais, get_ocorrencias_da_sessão, get_assinaturas) + from .templates import (pdf_capa_processo_gerar, pdf_documento_administrativo_gerar, pdf_espelho_gerar, pdf_etiqueta_protocolo_gerar, pdf_materia_gerar, pdf_ordem_dia_gerar, pdf_pauta_sessao_gerar, pdf_protocolo_gerar, pdf_sessao_plenaria_gerar) +from weasyprint import HTML, CSS + def get_kwargs_params(request, fields): kwargs = {} @@ -506,6 +519,18 @@ def get_sessao_plenaria(sessao, casa): inf_basicas_dic["dat_fim_sessao"] = '' inf_basicas_dic["hr_fim_sessao"] = sessao.hora_fim inf_basicas_dic["nom_camara"] = casa.nome + + # Conteudo multimidia + cont_mult_dic = {} + if sessao.url_audio: + cont_mult_dic['multimidia_audio'] = str(sessao.url_audio) + else: + cont_mult_dic['multimidia_audio'] = 'Indisponível' + + if sessao.url_video: + cont_mult_dic['multimidia_video'] = str(sessao.url_video) + else: + cont_mult_dic['multimidia_video'] = 'Indisponível' # Lista da composicao da mesa diretora lst_mesa = [] @@ -566,6 +591,10 @@ def get_sessao_plenaria(sessao, casa): # unescape HTML codes # https://github.com/interlegis/sapl/issues/1046 conteudo = re.sub('style=".*?"', '', conteudo) + conteudo = re.sub('class=".*?"', '', conteudo) + conteudo = re.sub('align=".*?"', '', conteudo) # OSTicket Ticket #796450 + conteudo = re.sub('', '

    ', conteudo) + conteudo = re.sub('', '
    ', conteudo) # OSTicket Ticket #796450 conteudo = html.unescape(conteudo) # escape special character '&' @@ -633,6 +662,28 @@ def get_sessao_plenaria(sessao, casa): dic_expediente_materia["votacao_observacao"] = ' ' lst_expediente_materia.append(dic_expediente_materia) + # Lista dos votos nominais das matérias do Expediente + lst_expediente_materia_vot_nom = [] + + materias_expediente_votacao_nominal = ExpedienteMateria.objects.filter( + sessao_plenaria=sessao, + tipo_votacao=2).order_by('-materia') + + for mevn in materias_expediente_votacao_nominal: + votos_materia = [] + titulo_materia = mevn.materia + registro = RegistroVotacao.objects.filter(expediente=mevn) + + if registro: + for vp in VotoParlamentar.objects.filter(votacao=registro).order_by('parlamentar'): + votos_materia.append(vp) + + dic_expediente_materia_vot_nom = { + 'titulo': titulo_materia, + 'votos': votos_materia + } + lst_expediente_materia_vot_nom.append(dic_expediente_materia_vot_nom) + # Lista dos oradores do Expediente lst_oradores_expediente = [] for orador_expediente in OradorExpediente.objects.filter( @@ -722,6 +773,57 @@ def get_sessao_plenaria(sessao, casa): dic_votacao["nom_resultado"] = "Matéria não votada" lst_votacao.append(dic_votacao) + # Lista dos votos nominais das matérias da Ordem do Dia + lst_votacao_vot_nom = [] + + materias_ordem_dia_votacao_nominal = OrdemDia.objects.filter( + sessao_plenaria=sessao, + tipo_votacao=2).order_by('-materia') + + for modvn in materias_ordem_dia_votacao_nominal: + votos_materia_od = [] + t_materia = modvn.materia + registro_od = RegistroVotacao.objects.filter(ordem=modvn) + + if registro_od: + for vp_od in VotoParlamentar.objects.filter(votacao=registro_od).order_by('parlamentar'): + votos_materia_od.append(vp_od) + + dic_votacao_vot_nom = { + 'titulo': t_materia, + 'votos': votos_materia_od + } + lst_votacao_vot_nom.append(dic_votacao_vot_nom) + + # Lista dos oradores da Ordem do Dia + lst_oradores_ordemdia = [] + + oradores_ordem_dia = OradorOrdemDia.objects.filter( + sessao_plenaria=sessao + ).order_by('numero_ordem') + + for orador_ordemdia in oradores_ordem_dia: + parlamentar_orador = Parlamentar.objects.get( + id=orador_ordemdia.parlamentar.id + ) + + sigla_partido = Filiacao.objects.filter( + parlamentar=parlamentar_orador + ).first() + + if not sigla_partido: + sigla_p = "" + else: + sigla_p = sigla_partido.partido.sigla + + dic_oradores_ordemdia = { + 'num_ordem': orador_ordemdia.numero_ordem, + 'nome_parlamentar': parlamentar_orador.nome_parlamentar, + 'observacao': orador_ordemdia.observacao, + 'sigla': sigla_p + } + lst_oradores_ordemdia.append(dic_oradores_ordemdia) + # Lista dos oradores nas Explicações Pessoais lst_oradores = [] for orador in Orador.objects.filter( @@ -762,14 +864,18 @@ def get_sessao_plenaria(sessao, casa): lst_ocorrencias.append(o) return (inf_basicas_dic, + cont_mult_dic, lst_mesa, lst_presenca_sessao, lst_ausencia_sessao, lst_expedientes, lst_expediente_materia, + lst_expediente_materia_vot_nom, lst_oradores_expediente, lst_presenca_ordem_dia, lst_votacao, + lst_votacao_vot_nom, + lst_oradores_ordemdia, lst_oradores, lst_ocorrencias) @@ -817,14 +923,18 @@ def relatorio_sessao_plenaria(request, pk): raise Http404('Essa página não existe') (inf_basicas_dic, + cont_mult_dic, lst_mesa, lst_presenca_sessao, lst_ausencia_sessao, lst_expedientes, lst_expediente_materia, + lst_expediente_materia_vot_nom, lst_oradores_expediente, lst_presenca_ordem_dia, lst_votacao, + lst_votacao_vot_nom, + lst_oradores_ordemdia, lst_oradores, lst_ocorrencias) = get_sessao_plenaria(sessao, casa) @@ -838,14 +948,18 @@ def relatorio_sessao_plenaria(request, pk): rodape, imagem, inf_basicas_dic, + cont_mult_dic, lst_mesa, lst_presenca_sessao, lst_ausencia_sessao, lst_expedientes, lst_expediente_materia, + lst_expediente_materia_vot_nom, lst_oradores_expediente, lst_presenca_ordem_dia, lst_votacao, + lst_votacao_vot_nom, + lst_oradores_ordemdia, lst_oradores, lst_ocorrencias) @@ -1148,3 +1262,67 @@ def get_pauta_sessao(sessao, casa): return (lst_expediente_materia, lst_votacao, inf_basicas_dic) + +def make_pdf(base_url,main_template,header_template,main_css='',header_css=''): + html = HTML(base_url=base_url, string=main_template) + main_doc = html.render(stylesheets=[]) + + def get_page_body(boxes): + for box in boxes: + if box.element_tag == 'body': + return box + return get_page_body(box.all_children()) + + # Template of header + html = HTML(base_url=base_url,string=header_template) + header = html.render(stylesheets=[CSS(string='@page {size:A4; margin:1cm;}')]) + + header_page = header.pages[0] + header_body = get_page_body(header_page._page_box.all_children()) + header_body = header_body.copy_with_children(header_body.all_children()) + + for page in main_doc.pages: + page_body = get_page_body(page._page_box.all_children()) + page_body.children += header_body.all_children() + + pdf_file = main_doc.write_pdf() + + return pdf_file + + +def resumo_ata_pdf(request,pk): + base_url = request.build_absolute_uri() + casa = CasaLegislativa.objects.first() + rodape = ' '.join(get_rodape(casa)) + + sessao_plenaria = SessaoPlenaria.objects.get(pk=pk) + + context = {} + context.update(get_identificação_basica(sessao_plenaria)) + context.update(get_mesa_diretora(sessao_plenaria)) + context.update(get_presenca_sessao(sessao_plenaria)) + context.update(get_expedientes(sessao_plenaria)) + context.update(get_materias_expediente(sessao_plenaria)) + context.update(get_oradores_expediente(sessao_plenaria)) + context.update(get_presenca_ordem_do_dia(sessao_plenaria)) + context.update(get_materias_ordem_do_dia(sessao_plenaria)) + context.update(get_oradores_ordemdia(sessao_plenaria)) + context.update(get_oradores_explicações_pessoais(sessao_plenaria)) + context.update(get_ocorrencias_da_sessão(sessao_plenaria)) + context.update(get_assinaturas(sessao_plenaria)) + context.update({'object': sessao_plenaria}) + context.update({'data': dt.today().strftime('%d/%m/%Y')}) + context.update({'rodape': rodape}) + header_context = {"casa": casa, 'logotipo':casa.logotipo, 'MEDIA_URL': MEDIA_URL} + + html_template = render_to_string('relatorios/relatorio_ata.html', context) + html_header = render_to_string('relatorios/header_ata.html', header_context) + + pdf_file = make_pdf(base_url=base_url,main_template=html_template,header_template=html_header) + + response = HttpResponse(content_type='application/pdf;') + response['Content-Disposition'] = 'inline; filename=relatorio.pdf' + response['Content-Transfer-Encoding'] = 'binary' + response.write(pdf_file) + + return response \ No newline at end of file diff --git a/sapl/rules/map_rules.py b/sapl/rules/map_rules.py index 13c89a547..75fc76f33 100644 --- a/sapl/rules/map_rules.py +++ b/sapl/rules/map_rules.py @@ -60,6 +60,7 @@ rules_group_administrativo = { 'can_access_impressos'], __perms_publicas__), # TODO: tratar em sapl.api a questão de ostencivo e restritivo (protocoloadm.DocumentoAdministrativo, __base__, set()), + (protocoloadm.Anexado, __base__, set()), (protocoloadm.DocumentoAcessorioAdministrativo, __base__, set()), (protocoloadm.TramitacaoAdministrativo, __base__, set()), ] @@ -118,6 +119,8 @@ rules_group_materia = { (materia.Autoria, __base__, __perms_publicas__), (materia.DespachoInicial, __base__, __perms_publicas__), (materia.DocumentoAcessorio, __base__, __perms_publicas__), + (materia.MateriaAssunto, __base__, __perms_publicas__), + (materia.AssuntoMateria, __base__, __perms_publicas__), (materia.MateriaLegislativa, __base__ + ['can_access_impressos'], __perms_publicas__), @@ -175,6 +178,7 @@ rules_group_sessao = { (sessao.ExpedienteSessao, __base__, __perms_publicas__), (sessao.Orador, __base__, __perms_publicas__), (sessao.OradorExpediente, __base__, __perms_publicas__), + (sessao.OradorOrdemDia, __base__, __perms_publicas__), (sessao.OrdemDia, __base__, __perms_publicas__), (sessao.PresencaOrdemDia, __base__, __perms_publicas__), (sessao.RegistroVotacao, __base__, __perms_publicas__), @@ -274,6 +278,8 @@ rules_group_geral = { (parlamentares.ComposicaoMesa, __base__, __perms_publicas__), (parlamentares.Frente, __base__, __perms_publicas__), (parlamentares.Votante, __base__, __perms_publicas__), + (parlamentares.Bloco, __base__, __perms_publicas__), + (sessao.CargoBancada, __base__, __perms_publicas__), (sessao.Bancada, __base__, __perms_publicas__), @@ -282,7 +288,6 @@ rules_group_geral = { (sessao.TipoExpediente, __base__, __perms_publicas__), (sessao.TipoJustificativa, __base__, __perms_publicas__), (sessao.JustificativaAusencia, __base__, __perms_publicas__), - (sessao.Bloco, __base__, __perms_publicas__), (sessao.ResumoOrdenacao, __base__, __perms_publicas__), (sessao.TipoRetiradaPauta, __base__, __perms_publicas__), diff --git a/sapl/sessao/forms.py b/sapl/sessao/forms.py index 2b8ec4742..1e14b0f45 100644 --- a/sapl/sessao/forms.py +++ b/sapl/sessao/forms.py @@ -1,6 +1,6 @@ from datetime import datetime -from crispy_forms.helper import FormHelper +from sapl.crispy_layout_mixin import SaplFormHelper from crispy_forms.layout import HTML, Button, Fieldset, Layout from django import forms from django.contrib.contenttypes.models import ContentType @@ -20,32 +20,21 @@ from sapl.materia.models import (MateriaLegislativa, StatusTramitacao, from sapl.parlamentares.models import Parlamentar, Mandato from sapl.utils import (RANGE_DIAS_MES, RANGE_MESES, MateriaPesquisaOrderingFilter, autor_label, - autor_modal, timezone, choice_anos_com_sessaoplenaria) + autor_modal, timezone, choice_anos_com_sessaoplenaria, + FileFieldCheckMixin) -from .models import (Bancada, Bloco, ExpedienteMateria, JustificativaAusencia, +from .models import (Bancada, ExpedienteMateria, JustificativaAusencia, Orador, OradorExpediente, OrdemDia, PresencaOrdemDia, SessaoPlenaria, SessaoPlenariaPresenca, TipoResultadoVotacao, - OcorrenciaSessao, RetiradaPauta, TipoRetiradaPauta) + OcorrenciaSessao, RetiradaPauta, TipoRetiradaPauta, OradorOrdemDia, ORDENACAO_RESUMO, + ResumoOrdenacao) MES_CHOICES = RANGE_MESES DIA_CHOICES = RANGE_DIAS_MES -ORDENACAO_RESUMO = [('cont_mult', 'Conteúdo Multimídia'), - ('exp', 'Expedientes'), - ('id_basica', 'Identificação Básica'), - ('lista_p', 'Lista de Presença'), - ('lista_p_o_d', 'Lista de Presença Ordem do Dia'), - ('mat_exp', 'Matérias do Expediente'), - ('mat_o_d', 'Matérias da Ordem do Dia'), - ('mesa_d', 'Mesa Diretora'), - ('oradores_exped', 'Oradores do Expediente'), - ('oradores_expli', 'Oradores das Explicações Pessoais'), - ('ocorr_sessao', 'Ocorrências da Sessão')] - - -class SessaoPlenariaForm(ModelForm): +class SessaoPlenariaForm(FileFieldCheckMixin, ModelForm): class Meta: model = SessaoPlenaria @@ -207,7 +196,7 @@ class RetiradaPautaForm(ModelForm): ('expediente', 6)]) row3 = to_row([('observacao', 12)]) - self.helper = FormHelper() + self.helper = SaplFormHelper() self.helper.layout = SaplFormLayout( Fieldset(_('Retirada de Pauta'), row1, row2, row3)) @@ -327,41 +316,6 @@ class BancadaForm(ModelForm): return bancada -class BlocoForm(ModelForm): - - class Meta: - model = Bloco - fields = ['nome', 'partidos', 'data_criacao', - 'data_extincao', 'descricao'] - - def clean(self): - super(BlocoForm, self).clean() - - if not self.is_valid(): - return self.cleaned_data - - if self.cleaned_data['data_extincao']: - if (self.cleaned_data['data_extincao'] < - self.cleaned_data['data_criacao']): - msg = _('Data de extinção não pode ser menor que a de criação') - raise ValidationError(msg) - return self.cleaned_data - - @transaction.atomic - def save(self, commit=True): - bloco = super(BlocoForm, self).save(commit) - content_type = ContentType.objects.get_for_model(Bloco) - object_id = bloco.pk - tipo = TipoAutor.objects.get(content_type=content_type) - Autor.objects.create( - content_type=content_type, - object_id=object_id, - tipo=tipo, - nome=bloco.nome - ) - return bloco - - class ExpedienteMateriaForm(ModelForm): _model = ExpedienteMateria @@ -591,7 +545,7 @@ class SessaoPlenariaFilterSet(django_filters.FilterSet): ('data_inicio__day', 3), ('tipo', 3)]) - self.form.helper = FormHelper() + self.form.helper = SaplFormHelper() self.form.helper.form_method = 'GET' self.form.helper.layout = Layout( Fieldset(self.titulo, @@ -664,7 +618,7 @@ class AdicionarVariasMateriasFilterSet(MateriaLegislativaFilterSet): row9 = to_row( [('ementa', 12)]) - self.form.helper = FormHelper() + self.form.helper = SaplFormHelper() self.form.helper.form_method = 'GET' self.form.helper.layout = Layout( Fieldset(_('Pesquisa de Matéria'), @@ -689,7 +643,29 @@ class OradorForm(ModelForm): self.fields['parlamentar'].queryset = Parlamentar.objects.filter( id__in=ids).order_by('nome_parlamentar') + + def clean(self): + super(OradorForm, self).clean() + cleaned_data = self.cleaned_data + if not self.is_valid(): + return self.cleaned_data + + sessao_id = self.initial['id_sessao'] + numero = self.initial.get('numero') + numero_ordem = cleaned_data['numero_ordem'] + ordem = Orador.objects.filter( + sessao_plenaria_id=sessao_id, + numero_ordem=numero_ordem + ).exists() + + if ordem and numero_ordem != numero: + raise ValidationError(_( + "Já existe orador nesta posição de ordem de pronunciamento" + )) + + return self.cleaned_data + class Meta: model = Orador exclude = ['sessao_plenaria'] @@ -733,37 +709,112 @@ class OradorExpedienteForm(ModelForm): exclude = ['sessao_plenaria'] +class OradorOrdemDiaForm(ModelForm): + + def __init__(self, *args, **kwargs): + super(OradorOrdemDiaForm, self).__init__(*args, **kwargs) + + id_sessao = int(self.initial['id_sessao']) + + ids = [p.parlamentar.id for p in PresencaOrdemDia.objects.filter( + sessao_plenaria_id=id_sessao + )] + + self.fields['parlamentar'].queryset = Parlamentar.objects.filter( + id__in=ids + ).order_by('nome_parlamentar') + + + def clean(self): + super(OradorOrdemDiaForm, self).clean() + cleaned_data = self.cleaned_data + + if not self.is_valid(): + return self.cleaned_data + + sessao_id = self.initial['id_sessao'] + numero = self.initial.get('numero') + numero_ordem = cleaned_data['numero_ordem'] + ordem = OradorOrdemDia.objects.filter( + sessao_plenaria_id=sessao_id, + numero_ordem=numero_ordem + ).exists() + + if ordem and numero_ordem != numero: + raise ValidationError(_( + "Já existe orador nesta posição de ordem de pronunciamento" + )) + + return self.cleaned_data + + class Meta: + model = OradorOrdemDia + exclude = ['sessao_plenaria'] + + class PautaSessaoFilterSet(SessaoPlenariaFilterSet): titulo = _('Pesquisa de Pauta de Sessão') class ResumoOrdenacaoForm(forms.Form): - primeiro = forms.ChoiceField(label=_('1°'), - choices=ORDENACAO_RESUMO) - segundo = forms.ChoiceField(label=_('2°'), - choices=ORDENACAO_RESUMO) - terceiro = forms.ChoiceField(label='3°', - choices=ORDENACAO_RESUMO) - quarto = forms.ChoiceField(label=_('4°'), - choices=ORDENACAO_RESUMO) - quinto = forms.ChoiceField(label=_('5°'), - choices=ORDENACAO_RESUMO) - sexto = forms.ChoiceField(label=_('6°'), - choices=ORDENACAO_RESUMO) - setimo = forms.ChoiceField(label=_('7°'), - choices=ORDENACAO_RESUMO) - oitavo = forms.ChoiceField(label=_('8°'), - choices=ORDENACAO_RESUMO) - nono = forms.ChoiceField(label=_('9°'), - choices=ORDENACAO_RESUMO) - decimo = forms.ChoiceField(label='10°', - choices=ORDENACAO_RESUMO) - decimo_primeiro = forms.ChoiceField(label='11°', - choices=ORDENACAO_RESUMO) + primeiro = forms.ChoiceField( + label='1°', + choices=ORDENACAO_RESUMO + ) + segundo = forms.ChoiceField( + label='2°', + choices=ORDENACAO_RESUMO + ) + terceiro = forms.ChoiceField( + label='3°', + choices=ORDENACAO_RESUMO + ) + quarto = forms.ChoiceField( + label='4°', + choices=ORDENACAO_RESUMO + ) + quinto = forms.ChoiceField( + label='5°', + choices=ORDENACAO_RESUMO + ) + sexto = forms.ChoiceField( + label='6°', + choices=ORDENACAO_RESUMO + ) + setimo = forms.ChoiceField( + label='7°', + choices=ORDENACAO_RESUMO + ) + oitavo = forms.ChoiceField( + label='8°', + choices=ORDENACAO_RESUMO + ) + nono = forms.ChoiceField( + label='9°', + choices=ORDENACAO_RESUMO + ) + decimo = forms.ChoiceField( + label='10°', + choices=ORDENACAO_RESUMO + ) + decimo_primeiro = forms.ChoiceField( + label='11°', + choices=ORDENACAO_RESUMO + ) + decimo_segundo = forms.ChoiceField( + label='12°', + choices=ORDENACAO_RESUMO + ) + decimo_terceiro = forms.ChoiceField( + label='13°', + choices=ORDENACAO_RESUMO + ) + decimo_quarto = forms.ChoiceField( + label='14°', + choices=ORDENACAO_RESUMO + ) def __init__(self, *args, **kwargs): - super(ResumoOrdenacaoForm, self).__init__(*args, **kwargs) - row1 = to_row( [('primeiro', 12)]) row2 = to_row( @@ -786,15 +837,25 @@ class ResumoOrdenacaoForm(forms.Form): [('decimo', 12)]) row11 = to_row( [('decimo_primeiro', 12)]) + row12 = to_row( + [('decimo_segundo', 12)]) + row13 = to_row( + [('decimo_terceiro', 12)]) + row14 = to_row( + [('decimo_quarto', 12)] + ) - self.helper = FormHelper() + self.helper = SaplFormHelper() self.helper.layout = Layout( Fieldset(_(''), row1, row2, row3, row4, row5, - row6, row7, row8, row9, row10, row11, + row6, row7, row8, row9, row10, + row11, row12, row13, row14, form_actions(label='Atualizar')) ) + super().__init__(*args, **kwargs) + def clean(self): super(ResumoOrdenacaoForm, self).clean() @@ -813,6 +874,27 @@ class ResumoOrdenacaoForm(forms.Form): 'Não é possível ter campos repetidos')) return self.cleaned_data + def save(self): + ordenacao = ResumoOrdenacao.objects.get() + cleaned_data = self.cleaned_data + + ordenacao.primeiro = cleaned_data['primeiro'] + ordenacao.segundo = cleaned_data['segundo'] + ordenacao.terceiro = cleaned_data['terceiro'] + ordenacao.quarto = cleaned_data['quarto'] + ordenacao.quinto = cleaned_data['quinto'] + ordenacao.sexto = cleaned_data['sexto'] + ordenacao.setimo = cleaned_data['setimo'] + ordenacao.oitavo = cleaned_data['oitavo'] + ordenacao.nono = cleaned_data['nono'] + ordenacao.decimo = cleaned_data['decimo'] + ordenacao.decimo_primeiro = cleaned_data['decimo_primeiro'] + ordenacao.decimo_segundo = cleaned_data['decimo_segundo'] + ordenacao.decimo_terceiro = cleaned_data['decimo_terceiro'] + ordenacao.decimo_quarto = cleaned_data['decimo_quarto'] + + ordenacao.save() + class JustificativaAusenciaForm(ModelForm): @@ -853,7 +935,7 @@ class JustificativaAusenciaForm(ModelForm): row8 = to_row( [('observacao', 12)]) - self.helper = FormHelper() + self.helper = SaplFormHelper() self.helper.layout = SaplFormLayout( Fieldset(_('Justificativa de Ausência'), row1, row2, row3, @@ -915,79 +997,3 @@ class JustificativaAusenciaForm(ModelForm): justificativa.materias_do_expediente.clear() justificativa.materias_da_ordem_do_dia.clear() return justificativa - - -class VotacaoEmBlocoFilterSet(MateriaLegislativaFilterSet): - - o = MateriaPesquisaOrderingFilter() - tramitacao__status = django_filters.ModelChoiceFilter( - required=True, - queryset=StatusTramitacao.objects.all(), - label=_('Status da Matéria')) - - class Meta: - model = MateriaLegislativa - fields = ['tramitacao__status', - 'numero', - 'numero_protocolo', - 'ano', - 'tipo', - 'data_apresentacao', - 'data_publicacao', - 'autoria__autor__tipo', - # FIXME 'autoria__autor__partido', - 'relatoria__parlamentar_id', - 'local_origem_externa', - 'em_tramitacao', - ] - - def __init__(self, *args, **kwargs): - super(MateriaLegislativaFilterSet, self).__init__(*args, **kwargs) - - self.filters['tipo'].label = 'Tipo de Matéria' - self.filters['autoria__autor__tipo'].label = 'Tipo de Autor' - # self.filters['autoria__autor__partido'].label = 'Partido do Autor' - self.filters['relatoria__parlamentar_id'].label = 'Relatoria' - - row1 = to_row( - [('tramitacao__status', 12)]) - row2 = to_row( - [('tipo', 12)]) - row3 = to_row( - [('numero', 4), - ('ano', 4), - ('numero_protocolo', 4)]) - row4 = to_row( - [('data_apresentacao', 6), - ('data_publicacao', 6)]) - row5 = to_row( - [('autoria__autor', 0), - (Button('pesquisar', - 'Pesquisar Autor', - css_class='btn btn-primary btn-sm'), 2), - (Button('limpar', - 'limpar Autor', - css_class='btn btn-primary btn-sm'), 10)]) - row6 = to_row( - [('autoria__autor__tipo', 6), - # ('autoria__autor__partido', 6) - ]) - row7 = to_row( - [('relatoria__parlamentar_id', 6), - ('local_origem_externa', 6)]) - row8 = to_row( - [('em_tramitacao', 6), - ('o', 6)]) - row9 = to_row( - [('ementa', 12)]) - - self.form.helper = FormHelper() - self.form.helper.form_method = 'GET' - self.form.helper.layout = Layout( - Fieldset(_('Pesquisa de Matéria'), - row1, row2, row3, - HTML(autor_label), - HTML(autor_modal), - row4, row5, row6, row7, row8, row9, - form_actions(label='Pesquisar')) - ) diff --git a/sapl/sessao/migrations/0028_auto_20181031_0902.py b/sapl/sessao/migrations/0028_auto_20181031_0902.py index 3ff3c0dc5..c08a01640 100644 --- a/sapl/sessao/migrations/0028_auto_20181031_0902.py +++ b/sapl/sessao/migrations/0028_auto_20181031_0902.py @@ -38,7 +38,7 @@ class Migration(migrations.Migration): ], options={ 'verbose_name_plural': 'Tipos de Retirada de Pauta', - 'verbose_name': 'Tipo de Retidara de Pauta', + 'verbose_name': 'Tipo de Retirada de Pauta', 'ordering': ['descricao'], }, ), diff --git a/sapl/sessao/migrations/0033_auto_20190228_1803.py b/sapl/sessao/migrations/0033_auto_20190228_1803.py new file mode 100644 index 000000000..097107f3f --- /dev/null +++ b/sapl/sessao/migrations/0033_auto_20190228_1803.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.20 on 2019-02-28 21:03 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('sessao', '0032_merge_20181122_1527'), + ] + + operations = [ + migrations.AddField( + model_name='resumoordenacao', + name='decimo_segundo', + field=models.CharField(default='Votos Nominais Mat Expediente', max_length=30), + ), + migrations.AddField( + model_name='resumoordenacao', + name='decimo_terceiro', + field=models.CharField(default='Votos Nominais Mat Ordem Dia', max_length=30), + ), + ] diff --git a/sapl/sessao/migrations/0034_oradorordemdia.py b/sapl/sessao/migrations/0034_oradorordemdia.py new file mode 100644 index 000000000..b0e5976fb --- /dev/null +++ b/sapl/sessao/migrations/0034_oradorordemdia.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.20 on 2019-03-26 16:14 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion +import sapl.sessao.models + + +class Migration(migrations.Migration): + + dependencies = [ + ('parlamentares', '0025_auto_20180924_1724'), + ('sessao', '0033_auto_20190228_1803'), + ] + + operations = [ + migrations.CreateModel( + name='OradorOrdemDia', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('numero_ordem', models.PositiveIntegerField(verbose_name='Ordem de pronunciamento')), + ('url_discurso', models.URLField(blank=True, max_length=150, verbose_name='URL Vídeo')), + ('observacao', models.CharField(blank=True, max_length=150, verbose_name='Observação')), + ('upload_anexo', models.FileField(blank=True, null=True, upload_to=sapl.sessao.models.anexo_upload_path, verbose_name='Anexo do Orador')), + ('parlamentar', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='parlamentares.Parlamentar', verbose_name='Parlamentar')), + ('sessao_plenaria', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='sessao.SessaoPlenaria')), + ], + options={ + 'verbose_name_plural': 'Oradores da Ordem do Dia', + 'verbose_name': 'Orador da Ordem do Dia', + }, + ), + ] diff --git a/sapl/sessao/migrations/0035_resumoordenacao_decimo_quarto.py b/sapl/sessao/migrations/0035_resumoordenacao_decimo_quarto.py new file mode 100644 index 000000000..f8e00489e --- /dev/null +++ b/sapl/sessao/migrations/0035_resumoordenacao_decimo_quarto.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.20 on 2019-03-26 18:14 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('sessao', '0034_oradorordemdia'), + ] + + operations = [ + migrations.AddField( + model_name='resumoordenacao', + name='decimo_quarto', + field=models.CharField(default='Oradores da Ordem do Dia', max_length=30), + ), + ] diff --git a/sapl/sessao/migrations/0036_auto_20190412_1106.py b/sapl/sessao/migrations/0036_auto_20190412_1106.py new file mode 100644 index 000000000..fe8f563cc --- /dev/null +++ b/sapl/sessao/migrations/0036_auto_20190412_1106.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.20 on 2019-04-12 14:06 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('sessao', '0035_resumoordenacao_decimo_quarto'), + ] + + operations = [ + migrations.AlterField( + model_name='expedientesessao', + name='sessao_plenaria', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='expedientesessao_set', to='sessao.SessaoPlenaria'), + ), + ] diff --git a/sapl/sessao/migrations/0037_auto_20190415_1324.py b/sapl/sessao/migrations/0037_auto_20190415_1324.py new file mode 100644 index 000000000..ed1137e4b --- /dev/null +++ b/sapl/sessao/migrations/0037_auto_20190415_1324.py @@ -0,0 +1,33 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.20 on 2019-04-15 16:24 +from __future__ import unicode_literals + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('sessao', '0036_auto_20190412_1106'), + ] + + operations = [ + migrations.AddField( + model_name='registrovotacao', + name='data_hora', + field=models.DateTimeField(auto_now_add=True, null=True, verbose_name='Data/Hora'), + ), + migrations.AddField( + model_name='registrovotacao', + name='ip', + field=models.CharField(blank=True, default='', max_length=30, verbose_name='IP'), + ), + migrations.AddField( + model_name='registrovotacao', + name='user', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL), + ), + ] diff --git a/sapl/sessao/migrations/0037_auto_20190415_1635.py b/sapl/sessao/migrations/0037_auto_20190415_1635.py new file mode 100644 index 000000000..701d9eee9 --- /dev/null +++ b/sapl/sessao/migrations/0037_auto_20190415_1635.py @@ -0,0 +1,85 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.20 on 2019-04-15 19:35 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('sessao', '0036_auto_20190412_1106'), + ] + + operations = [ + migrations.AlterField( + model_name='resumoordenacao', + name='decimo', + field=models.CharField(default='mat_o_d', max_length=50), + ), + migrations.AlterField( + model_name='resumoordenacao', + name='decimo_primeiro', + field=models.CharField(default='v_n_mat_o_d', max_length=50), + ), + migrations.AlterField( + model_name='resumoordenacao', + name='decimo_quarto', + field=models.CharField(default='ocorr_sessao', max_length=50), + ), + migrations.AlterField( + model_name='resumoordenacao', + name='decimo_segundo', + field=models.CharField(default='oradores_o_d', max_length=50), + ), + migrations.AlterField( + model_name='resumoordenacao', + name='decimo_terceiro', + field=models.CharField(default='oradores_expli', max_length=50), + ), + migrations.AlterField( + model_name='resumoordenacao', + name='nono', + field=models.CharField(default='lista_p_o_d', max_length=50), + ), + migrations.AlterField( + model_name='resumoordenacao', + name='oitavo', + field=models.CharField(default='oradores_exped', max_length=50), + ), + migrations.AlterField( + model_name='resumoordenacao', + name='primeiro', + field=models.CharField(default='id_basica', max_length=50), + ), + migrations.AlterField( + model_name='resumoordenacao', + name='quarto', + field=models.CharField(default='lista_p', max_length=50), + ), + migrations.AlterField( + model_name='resumoordenacao', + name='quinto', + field=models.CharField(default='exp', max_length=50), + ), + migrations.AlterField( + model_name='resumoordenacao', + name='segundo', + field=models.CharField(default='cont_mult', max_length=50), + ), + migrations.AlterField( + model_name='resumoordenacao', + name='setimo', + field=models.CharField(default='v_n_mat_exp', max_length=50), + ), + migrations.AlterField( + model_name='resumoordenacao', + name='sexto', + field=models.CharField(default='mat_exp', max_length=50), + ), + migrations.AlterField( + model_name='resumoordenacao', + name='terceiro', + field=models.CharField(default='mesa_d', max_length=50), + ), + ] diff --git a/sapl/sessao/migrations/0038_merge_20190415_1800.py b/sapl/sessao/migrations/0038_merge_20190415_1800.py new file mode 100644 index 000000000..0bf3ab4ea --- /dev/null +++ b/sapl/sessao/migrations/0038_merge_20190415_1800.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.20 on 2019-04-15 21:00 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('sessao', '0037_auto_20190415_1635'), + ('sessao', '0037_auto_20190415_1324'), + ] + + operations = [ + ] diff --git a/sapl/sessao/migrations/0039_auto_20190430_0825.py b/sapl/sessao/migrations/0039_auto_20190430_0825.py new file mode 100644 index 000000000..5a1c717f5 --- /dev/null +++ b/sapl/sessao/migrations/0039_auto_20190430_0825.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.20 on 2019-04-30 11:25 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('sessao', '0038_merge_20190415_1800'), + ] + + database_operations = [migrations.AlterModelTable('Bloco', 'parlamentares_bloco')] + + state_operations = [migrations.DeleteModel('Bloco')] + + operations = [ + migrations.SeparateDatabaseAndState( + database_operations=database_operations, + state_operations=state_operations) + ] \ No newline at end of file diff --git a/sapl/sessao/models.py b/sapl/sessao/models.py index 8cc162a05..53721499b 100644 --- a/sapl/sessao/models.py +++ b/sapl/sessao/models.py @@ -290,8 +290,11 @@ class TipoExpediente(models.Model): @reversion.register() class ExpedienteSessao(models.Model): # ExpedienteSessaoPlenaria - sessao_plenaria = models.ForeignKey(SessaoPlenaria, - on_delete=models.CASCADE) + sessao_plenaria = models.ForeignKey( + SessaoPlenaria, + on_delete=models.CASCADE, + related_name='expedientesessao_set' + ) tipo = models.ForeignKey(TipoExpediente, on_delete=models.PROTECT) conteudo = models.TextField( blank=True, verbose_name=_('Conteúdo do expediente')) @@ -378,6 +381,14 @@ class OradorExpediente(AbstractOrador): # OradoresExpediente verbose_name_plural = _('Oradores do Expediente') +@reversion.register() +class OradorOrdemDia(AbstractOrador): # OradoresOrdemDia + + class Meta: + verbose_name = _('Orador da Ordem do Dia') + verbose_name_plural = _('Oradores da Ordem do Dia') + + @reversion.register() class OrdemDia(AbstractOrdemDia): @@ -445,6 +456,19 @@ class RegistroVotacao(models.Model): verbose_name=_('Abstenções')) observacao = models.TextField( blank=True, verbose_name=_('Observações')) + user = models.ForeignKey(get_settings_auth_user_model(), + on_delete=models.PROTECT, + null=True, + blank=True) + ip = models.CharField(verbose_name=_('IP'), + max_length=30, + blank=True, + default='') + data_hora = models.DateTimeField( + verbose_name=_('Data/Hora'), + auto_now_add=True, + blank=True, + null=True) class Meta: verbose_name = _('Votação') @@ -526,38 +550,22 @@ class SessaoPlenariaPresenca(models.Model): ordering = ['parlamentar__nome_parlamentar'] -@reversion.register() -class Bloco(models.Model): - ''' - * blocos podem existir por mais de uma legislatura - ''' - nome = models.CharField( - max_length=80, verbose_name=_('Nome do Bloco')) - partidos = models.ManyToManyField( - Partido, blank=True, verbose_name=_('Partidos')) - data_criacao = models.DateField( - blank=False, null=True, verbose_name=_('Data Criação')) - data_extincao = models.DateField( - blank=True, null=True, verbose_name=_('Data Dissolução')) - descricao = models.TextField(blank=True, verbose_name=_('Descrição')) - - # campo conceitual de reversão genérica para o model Autor que dá a - # o meio possível de localização de tipos de autores. - autor = SaplGenericRelation(Autor, - related_query_name='bloco_set', - fields_search=( - ('nome', '__icontains'), - ('descricao', '__icontains'), - ('partidos__sigla', '__icontains'), - ('partidos__nome', '__icontains'), - )) - - class Meta: - verbose_name = _('Bloco Parlamentar') - verbose_name_plural = _('Blocos Parlamentares') - - def __str__(self): - return self.nome +ORDENACAO_RESUMO = [ + ('id_basica', 'Identificação Básica'), + ('cont_mult', 'Conteúdo Multimídia'), + ('mesa_d', 'Mesa Diretora'), + ('lista_p', 'Lista de Presença'), + ('exp', 'Expedientes'), + ('mat_exp', 'Matérias do Expediente'), + ('v_n_mat_exp', 'Votações Nominais - Matérias do Expediente'), + ('oradores_exped', 'Oradores do Expediente'), + ('lista_p_o_d', 'Lista de Presença Ordem do Dia'), + ('mat_o_d', 'Matérias da Ordem do Dia'), + ('v_n_mat_o_d', 'Votações Nominais - Matérias da Ordem do Dia'), + ('oradores_o_d', 'Oradores da Ordem do Dia'), + ('oradores_expli', 'Oradores das Explicações Pessoais'), + ('ocorr_sessao', 'Ocorrências da Sessão') +] @reversion.register() @@ -566,17 +574,62 @@ class ResumoOrdenacao(models.Model): Tabela para registrar em qual ordem serão renderizados os componentes da tela de resumo de uma sessão ''' - primeiro = models.CharField(max_length=30) - segundo = models.CharField(max_length=30) - terceiro = models.CharField(max_length=30) - quarto = models.CharField(max_length=30) - quinto = models.CharField(max_length=30) - sexto = models.CharField(max_length=30) - setimo = models.CharField(max_length=30) - oitavo = models.CharField(max_length=30) - nono = models.CharField(max_length=30) - decimo = models.CharField(max_length=30) - decimo_primeiro = models.CharField(max_length=30,default="Ocorrências da Sessão") + primeiro = models.CharField( + max_length=50, + default=ORDENACAO_RESUMO[0][0] + ) + segundo = models.CharField( + max_length=50, + default=ORDENACAO_RESUMO[1][0] + ) + terceiro = models.CharField( + max_length=50, + default=ORDENACAO_RESUMO[2][0] + ) + quarto = models.CharField( + max_length=50, + default=ORDENACAO_RESUMO[3][0] + ) + quinto = models.CharField( + max_length=50, + default=ORDENACAO_RESUMO[4][0] + ) + sexto = models.CharField( + max_length=50, + default=ORDENACAO_RESUMO[5][0] + ) + setimo = models.CharField( + max_length=50, + default=ORDENACAO_RESUMO[6][0] + ) + oitavo = models.CharField( + max_length=50, + default=ORDENACAO_RESUMO[7][0] + ) + nono = models.CharField( + max_length=50, + default=ORDENACAO_RESUMO[8][0] + ) + decimo = models.CharField( + max_length=50, + default=ORDENACAO_RESUMO[9][0] + ) + decimo_primeiro = models.CharField( + max_length=50, + default=ORDENACAO_RESUMO[10][0] + ) + decimo_segundo = models.CharField( + max_length=50, + default=ORDENACAO_RESUMO[11][0] + ) + decimo_terceiro = models.CharField( + max_length=50, + default=ORDENACAO_RESUMO[12][0] + ) + decimo_quarto = models.CharField( + max_length=50, + default=ORDENACAO_RESUMO[13][0] + ) class Meta: verbose_name = _('Ordenação do Resumo de uma Sessão') @@ -585,12 +638,13 @@ class ResumoOrdenacao(models.Model): def __str__(self): return 'Ordenação do Resumo de uma Sessão' + @reversion.register() class TipoRetiradaPauta(models.Model): descricao = models.CharField(max_length=150, verbose_name=_('Descrição')) class Meta: - verbose_name = _('Tipo de Retidara de Pauta') + verbose_name = _('Tipo de Retirada de Pauta') verbose_name_plural = _('Tipos de Retirada de Pauta') ordering = ['descricao'] @@ -676,6 +730,7 @@ class JustificativaAusencia(models.Model): using=using, update_fields=update_fields) + class RetiradaPauta(models.Model): materia = models.ForeignKey(MateriaLegislativa, on_delete=models.CASCADE, diff --git a/sapl/sessao/tests/test_sessao_view.py b/sapl/sessao/tests/test_sessao_view.py index 17b77e8d8..edab4c6fa 100644 --- a/sapl/sessao/tests/test_sessao_view.py +++ b/sapl/sessao/tests/test_sessao_view.py @@ -4,7 +4,21 @@ from django.utils.translation import ugettext_lazy as _ from model_mommy import mommy from sapl.parlamentares.models import Legislatura, SessaoLegislativa -from sapl.sessao.models import SessaoPlenaria, TipoSessaoPlenaria +from sapl.sessao.models import (SessaoPlenaria, TipoSessaoPlenaria, + IntegranteMesa, SessaoPlenariaPresenca, + JustificativaAusencia, ExpedienteSessao, + TipoExpediente, ExpedienteMateria, + Orador, OcorrenciaSessao) + +from sapl.parlamentares.models import Parlamentar, CargoMesa, Filiacao + +from sapl.sessao.views import (get_identificação_basica, get_conteudo_multimidia, + get_mesa_diretora, get_presenca_sessao, + get_expedientes, get_materias_expediente, + get_oradores_expediente, get_presenca_ordem_do_dia, + get_materias_ordem_do_dia, get_oradores_explicações_pessoais, + get_ocorrencias_da_sessão + ) @pytest.mark.django_db(transaction=False) @@ -47,3 +61,90 @@ def test_incluir_sessao_errors(admin_client): [_('Este campo é obrigatório.')]) assert (response.context_data['form'].errors['hora_inicio'] == [_('Este campo é obrigatório.')]) + +@pytest.mark.django_db(transaction=False) +class TestResumoView(): + def setup(self): + self.sessao_plenaria = mommy.make(SessaoPlenaria) + self.parlamentar = mommy.make(Parlamentar) + self.cargo_mesa = mommy.make(CargoMesa) + + self.integrante_mesa = IntegranteMesa(sessao_plenaria=self.sessao_plenaria, + parlamentar=self.parlamentar, + cargo=self.cargo_mesa) + self.integrante_mesa.save() + + def test_get_identificação_basica(self): + id_basica = get_identificação_basica(self.sessao_plenaria) + info_basica = id_basica['basica'] + assert info_basica[0] == 'Tipo de Sessão: ' + str(self.sessao_plenaria.tipo) + + data_inicio = self.sessao_plenaria.data_inicio + abertura = data_inicio.strftime('%d/%m/%Y') if data_inicio else '' + assert info_basica[1] == 'Abertura: ' + abertura +' - '+ self.sessao_plenaria.hora_inicio + + data_fim = self.sessao_plenaria.data_fim + encerramento = data_fim.strftime('%d/%m/%Y') + ' -' if data_fim else '' + assert info_basica[2] == 'Encerramento: ' + encerramento +' '+ self.sessao_plenaria.hora_fim + + def test_get_conteudo_multimidia(self): + multimidia = get_conteudo_multimidia(self.sessao_plenaria) + url_audio = _('Audio: Indisponível') + multimidia_video = _('Video: Indisponível') + + if self.sessao_plenaria.url_audio: + url_audio = _('Audio: ') + str(sessao_plenaria.url_audio) + if self.sessao_plenaria.url_video: + multimidia_video = _('Video: ') + str(sessao_plenaria.url_video) + + assert multimidia == {'multimidia_audio':url_audio, + 'multimidia_video':multimidia_video} + + def test_get_mesa_diretora(self): + mesa = get_mesa_diretora(self.sessao_plenaria) + assert mesa == {'mesa':[{ + 'cargo': self.cargo_mesa, + 'parlamentar': self.parlamentar + }]} + + def test_get_presenca_sessao(self): + justificativa = mommy.make(JustificativaAusencia,sessao_plenaria=self.sessao_plenaria) + presenca = mommy.make(SessaoPlenariaPresenca,sessao_plenaria=self.sessao_plenaria) + + resposta_presenca = get_presenca_sessao(self.sessao_plenaria) + assert resposta_presenca['presenca_sessao'] == [presenca.parlamentar] + assert resposta_presenca['justificativa_ausencia'][0] == justificativa + + def test_get_expedientes(self): + tipo_expediente = mommy.make(TipoExpediente) + expediente = mommy.make(ExpedienteSessao,sessao_plenaria=self.sessao_plenaria,tipo=tipo_expediente) + + resposta_expediente = get_expedientes(self.sessao_plenaria) + + assert resposta_expediente['expedientes'] == [{ + 'conteudo': expediente.conteudo, + 'tipo': tipo_expediente + }] + + def test_get_materias_expediente(self): + pass + + def test_get_oradores_explicações_pessoais(self): + parlamentar = mommy.make(Parlamentar) + partido_sigla = mommy.make(Filiacao, parlamentar=parlamentar) + orador = mommy.make(Orador,sessao_plenaria=self.sessao_plenaria,parlamentar=parlamentar) + + resultado_get_oradores = get_oradores_explicações_pessoais(self.sessao_plenaria) + + assert resultado_get_oradores['oradores_explicacoes'] == [{ + 'numero_ordem': orador.numero_ordem, + 'parlamentar': parlamentar, + 'sgl_partido': partido_sigla.partido.sigla + }] + + def test_get_ocorrencias_da_sessão(self): + ocorrencia = mommy.make(OcorrenciaSessao, sessao_plenaria=self.sessao_plenaria) + resultado_get_ocorrencia = get_ocorrencias_da_sessão(self.sessao_plenaria) + + assert resultado_get_ocorrencia['ocorrencias_da_sessao'][0] == ocorrencia + diff --git a/sapl/sessao/urls.py b/sapl/sessao/urls.py index 9bcb414b6..d5d709967 100644 --- a/sapl/sessao/urls.py +++ b/sapl/sessao/urls.py @@ -2,9 +2,10 @@ from django.conf.urls import include, url from sapl.sessao.views import (AdicionarVariasMateriasExpediente, AdicionarVariasMateriasOrdemDia, BancadaCrud, - BlocoCrud, CargoBancadaCrud, - ExpedienteMateriaCrud, ExpedienteView, JustificativaAusenciaCrud, - OcorrenciaSessaoView, MateriaOrdemDiaCrud, MesaView, OradorCrud, + CargoBancadaCrud, ExpedienteMateriaCrud, + ExpedienteView, JustificativaAusenciaCrud, + OcorrenciaSessaoView, MateriaOrdemDiaCrud, OradorOrdemDiaCrud, + MesaView, OradorCrud, OradorExpedienteCrud, PainelView, PautaSessaoDetailView, PautaSessaoView, PesquisarPautaSessaoView, @@ -12,7 +13,7 @@ from sapl.sessao.views import (AdicionarVariasMateriasExpediente, PresencaOrdemDiaView, PresencaView, ResumoOrdenacaoView, ResumoView, ResumoAtaView, RetiradaPautaCrud, SessaoCrud, TipoJustificativaCrud, TipoExpedienteCrud, TipoResultadoVotacaoCrud, - TipoExpedienteCrud, TipoResultadoVotacaoCrud,TipoRetiradaPautaCrud, + TipoExpedienteCrud, TipoResultadoVotacaoCrud, TipoRetiradaPautaCrud, TipoSessaoCrud, VotacaoEditView, VotacaoExpedienteEditView, VotacaoExpedienteView, VotacaoNominalEditView, @@ -28,9 +29,11 @@ from sapl.sessao.views import (AdicionarVariasMateriasExpediente, remove_parlamentar_composicao, reordernar_materias_expediente, reordernar_materias_ordem, + renumerar_materias_ordem, + renumerar_materias_expediente, sessao_legislativa_legislatura_ajax, VotacaoEmBlocoOrdemDia, VotacaoEmBlocoExpediente, - VotacaoEmBlocoSimbolicaView,VotacaoEmBlocoNominalView) + VotacaoEmBlocoSimbolicaView, VotacaoEmBlocoNominalView) from .apps import AppConfig @@ -42,7 +45,9 @@ urlpatterns = [ OradorExpedienteCrud.get_urls() + ExpedienteMateriaCrud.get_urls() + JustificativaAusenciaCrud.get_urls() + - MateriaOrdemDiaCrud.get_urls() + RetiradaPautaCrud.get_urls())), + MateriaOrdemDiaCrud.get_urls() + + OradorOrdemDiaCrud.get_urls() + + RetiradaPautaCrud.get_urls())), url(r'^sessao/(?P\d+)/mesa$', MesaView.as_view(), name='mesa'), @@ -59,9 +64,13 @@ urlpatterns = [ name='remove_parlamentar_composicao'), url(r'^sessao/recuperar-materia/', recuperar_materia), - url(r'^sessao/recuperar-numero-sessao/', recuperar_numero_sessao), + url(r'^sessao/recuperar-numero-sessao/', + recuperar_numero_sessao, + name='recuperar_numero_sessao_view' + ), url(r'^sessao/sessao-legislativa-legislatura-ajax/', - sessao_legislativa_legislatura_ajax), + sessao_legislativa_legislatura_ajax, + name='sessao_legislativa_legislatura_ajax_view'), url(r'^sessao/(?P\d+)/(?P\d+)/abrir-votacao$', abrir_votacao, @@ -71,6 +80,10 @@ urlpatterns = [ name="reordenar_expediente"), url(r'^sessao/(?P\d+)/reordenar-ordem$', reordernar_materias_ordem, name="reordenar_ordem"), + url(r'^sessao/(?P\d+)/renumerar-ordem$', renumerar_materias_ordem, + name="renumerar_ordem"), + url(r'^sessao/(?P\d+)/renumerar-materias-expediente$', renumerar_materias_expediente, + name="renumerar_materias_expediente"), url(r'^sistema/sessao-plenaria/tipo/', include(TipoSessaoCrud.get_urls())), url(r'^sistema/sessao-plenaria/tipo-resultado-votacao/', @@ -78,13 +91,11 @@ urlpatterns = [ url(r'^sistema/sessao-plenaria/tipo-expediente/', include(TipoExpedienteCrud.get_urls())), url(r'^sistema/sessao-plenaria/tipo-justificativa/', - include(TipoJustificativaCrud.get_urls())), + include(TipoJustificativaCrud.get_urls())), url(r'^sistema/sessao-plenaria/tipo-retirada-pauta/', include(TipoRetiradaPautaCrud.get_urls())), url(r'^sistema/bancada/', include(BancadaCrud.get_urls())), - url(r'^sistema/bloco/', - include(BlocoCrud.get_urls())), url(r'^sistema/cargo-bancada/', include(CargoBancadaCrud.get_urls())), url(r'^sistema/resumo-ordenacao/', @@ -123,7 +134,7 @@ urlpatterns = [ url(r'^sessao/(?P\d+)/votacao_bloco/votnom$', VotacaoEmBlocoNominalView.as_view(), name='votacaobloconom'), url(r'^sessao/(?P\d+)/votacao_bloco/votsimb$', - VotacaoEmBlocoSimbolicaView.as_view(), name='votacaoblocosimb'), + VotacaoEmBlocoSimbolicaView.as_view(), name='votacaoblocosimb'), url(r'^sessao/(?P\d+)/votacao_bloco_expediente$', VotacaoEmBlocoExpediente.as_view(), name='votacao_bloco_expediente'), @@ -145,7 +156,7 @@ urlpatterns = [ VotacaoEditView.as_view(), name='votacaosecretaedit'), url(r'^sessao/(?P\d+)/matordemdia/votsimb/(?P\d+)/(?P\d+)$', VotacaoView.as_view(), name='votacaosimbolica'), - + url(r'^sessao/(?P\d+)/matordemdia/votsimbbloco/$', VotacaoView.as_view(), name='votacaosimbolicabloco'), diff --git a/sapl/sessao/views.py b/sapl/sessao/views.py index 096130162..191de0b8f 100755 --- a/sapl/sessao/views.py +++ b/sapl/sessao/views.py @@ -1,6 +1,5 @@ import logging -from operator import itemgetter from re import sub from django.contrib import messages @@ -29,28 +28,28 @@ from sapl.crud.base import (RP_DETAIL, RP_LIST, Crud, CrudAux, MasterDetailCrud, PermissionRequiredForAppCrudMixin, make_pagination) from sapl.materia.forms import filtra_tramitacao_status -from sapl.materia.models import (Autoria, DocumentoAcessorio, - TipoMateriaLegislativa, Tramitacao) +from sapl.materia.models import (Autoria, TipoMateriaLegislativa, + Tramitacao) from sapl.materia.views import MateriaLegislativaPesquisaView from sapl.parlamentares.models import (Filiacao, Legislatura, Mandato, Parlamentar, SessaoLegislativa) from sapl.sessao.apps import AppConfig from sapl.sessao.forms import ExpedienteMateriaForm, OrdemDiaForm -from sapl.utils import show_results_filter_set, remover_acentos +from sapl.utils import show_results_filter_set, remover_acentos, get_client_ip -from .forms import (AdicionarVariasMateriasFilterSet, BancadaForm, BlocoForm, +from .forms import (AdicionarVariasMateriasFilterSet, BancadaForm, ExpedienteForm, JustificativaAusenciaForm, OcorrenciaSessaoForm, ListMateriaForm, MesaForm, OradorExpedienteForm, OradorForm, PautaSessaoFilterSet, PresencaForm, ResumoOrdenacaoForm, SessaoPlenariaFilterSet, SessaoPlenariaForm, VotacaoEditForm, VotacaoForm, - VotacaoNominalForm, RetiradaPautaForm) -from .models import (Bancada, Bloco, CargoBancada, CargoMesa, + VotacaoNominalForm, RetiradaPautaForm, OradorOrdemDiaForm) +from .models import (Bancada, CargoBancada, CargoMesa, ExpedienteMateria, ExpedienteSessao, OcorrenciaSessao, IntegranteMesa, MateriaLegislativa, Orador, OradorExpediente, OrdemDia, PresencaOrdemDia, RegistroVotacao, ResumoOrdenacao, SessaoPlenaria, SessaoPlenariaPresenca, TipoExpediente, TipoResultadoVotacao, TipoSessaoPlenaria, VotoParlamentar, TipoRetiradaPauta, - RetiradaPauta, TipoJustificativa, JustificativaAusencia) + RetiradaPauta, TipoJustificativa, JustificativaAusencia, OradorOrdemDia, ORDENACAO_RESUMO) TipoSessaoCrud = CrudAux.build(TipoSessaoPlenaria, 'tipo_sessao_plenaria') @@ -64,18 +63,39 @@ TipoRetiradaPautaCrud = CrudAux.build(TipoRetiradaPauta, 'tipo_retirada_pauta') def reordernar_materias_expediente(request, pk): expedientes = ExpedienteMateria.objects.filter( - sessao_plenaria_id=pk) + sessao_plenaria_id=pk + ).order_by( + 'materia__tipo__sequencia_regimental', + 'materia__ano', + 'materia__numero' + ) for exp_num, e in enumerate(expedientes, 1): e.numero_ordem = exp_num e.save() + return HttpResponseRedirect( reverse('sapl.sessao:expedientemateria_list', kwargs={'pk': pk})) def reordernar_materias_ordem(request, pk): ordens = OrdemDia.objects.filter( - sessao_plenaria_id=pk) + sessao_plenaria_id=pk + ).order_by( + 'materia__tipo__sequencia_regimental', + 'materia__ano', + 'materia__numero' + ) + for ordem_num, o in enumerate(ordens, 1): + o.numero_ordem = ordem_num + o.save() + + return HttpResponseRedirect( + reverse('sapl.sessao:ordemdia_list', kwargs={'pk': pk})) + +def renumerar_materias_ordem(request, pk): + ordens = OrdemDia.objects.filter(sessao_plenaria_id=pk) + for ordem_num, o in enumerate(ordens, 1): o.numero_ordem = ordem_num o.save() @@ -83,6 +103,15 @@ def reordernar_materias_ordem(request, pk): return HttpResponseRedirect( reverse('sapl.sessao:ordemdia_list', kwargs={'pk': pk})) +def renumerar_materias_expediente(request, pk): + expedientes = ExpedienteMateria.objects.filter(sessao_plenaria_id=pk) + + for exp_num, e in enumerate(expedientes, 1): + e.numero_ordem = exp_num + e.save() + + return HttpResponseRedirect( + reverse('sapl.sessao:expedientemateria_list', kwargs={'pk': pk})) def verifica_presenca(request, model, spk): logger = logging.getLogger(__name__) @@ -589,6 +618,31 @@ class OradorExpedienteCrud(OradorCrud): 'numero': self.object.numero_ordem} +class OradorOrdemDiaCrud(OradorCrud): + model = OradorOrdemDia + + class CreateView(MasterDetailCrud.CreateView): + form_class = OradorOrdemDiaForm + + def get_initial(self): + return {'id_sessao': self.kwargs['pk']} + + def get_success_url(self): + return reverse('sapl.sessao:oradorordemdia_list', + kwargs={'pk': self.kwargs['pk']}) + + class UpdateView(MasterDetailCrud.UpdateView): + form_class = OradorOrdemDiaForm + + def get_initial(self): + initial = super(UpdateView, self).get_initial() + + initial.update({'id_sessao': self.object.sessao_plenaria.id}) + initial.update({'numero': self.object.numero_ordem}) + + return initial + + class OradorCrud(OradorCrud): model = Orador @@ -610,6 +664,7 @@ class OradorCrud(OradorCrud): def get_initial(self): initial = super(UpdateView, self).get_initial() initial.update({'id_sessao': self.object.sessao_plenaria.id}) + initial.update({'numero':self.object.numero_ordem}) return initial @@ -624,16 +679,6 @@ class BancadaCrud(CrudAux): return reverse('sapl.sessao:bancada_list') -class BlocoCrud(CrudAux): - model = Bloco - - class CreateView(CrudAux.CreateView): - form_class = BlocoForm - - def get_success_url(self): - return reverse('sapl.sessao:bloco_list') - - def recuperar_numero_sessao(request): try: sessao = SessaoPlenaria.objects.filter( @@ -1023,9 +1068,6 @@ class ListMateriaOrdemDiaView(FormMixin, DetailView): return self.get(self, request, args, kwargs) -def ordenar_integrantes_por_cargo(integrantes): - return sorted(integrantes, key=lambda k: k['cargo'].id) - class MesaView(FormMixin, DetailView): template_name = 'sessao/mesa.html' @@ -1244,43 +1286,38 @@ class ResumoOrdenacaoView(PermissionRequiredMixin, FormView): form_class = ResumoOrdenacaoForm permission_required = {'sessao.change_resumoordenacao'} - def get_success_url(self): - return reverse('sapl.base:sistema') + def get_tupla(self, tupla_key): + for tupla in ORDENACAO_RESUMO: + if tupla[0] == tupla_key: + return tupla def get_initial(self): - initial = super(ResumoOrdenacaoView, self).get_initial() - ordenacao = ResumoOrdenacao.objects.first() - if ordenacao: - initial.update({'primeiro': ordenacao.primeiro, - 'segundo': ordenacao.segundo, - 'terceiro': ordenacao.terceiro, - 'quarto': ordenacao.quarto, - 'quinto': ordenacao.quinto, - 'sexto': ordenacao.sexto, - 'setimo': ordenacao.setimo, - 'oitavo': ordenacao.oitavo, - 'nono': ordenacao.nono, - 'decimo': ordenacao.decimo, - 'decimo_primeiro': ordenacao.decimo_primeiro}) - return initial - - def form_valid(self, form): ordenacao = ResumoOrdenacao.objects.get_or_create()[0] - ordenacao.primeiro = form.cleaned_data['primeiro'] - ordenacao.segundo = form.cleaned_data['segundo'] - ordenacao.terceiro = form.cleaned_data['terceiro'] - ordenacao.quarto = form.cleaned_data['quarto'] - ordenacao.quinto = form.cleaned_data['quinto'] - ordenacao.sexto = form.cleaned_data['sexto'] - ordenacao.setimo = form.cleaned_data['setimo'] - ordenacao.oitavo = form.cleaned_data['oitavo'] - ordenacao.nono = form.cleaned_data['nono'] - ordenacao.decimo = form.cleaned_data['decimo'] - ordenacao.decimo_primeiro = form.cleaned_data['decimo_primeiro'] + initial = { + 'primeiro': self.get_tupla(ordenacao.primeiro), + 'segundo': self.get_tupla(ordenacao.segundo), + 'terceiro': self.get_tupla(ordenacao.terceiro), + 'quarto': self.get_tupla(ordenacao.quarto), + 'quinto': self.get_tupla(ordenacao.quinto), + 'sexto': self.get_tupla(ordenacao.sexto), + 'setimo': self.get_tupla(ordenacao.setimo), + 'oitavo': self.get_tupla(ordenacao.oitavo), + 'nono': self.get_tupla(ordenacao.nono), + 'decimo': self.get_tupla(ordenacao.decimo), + 'decimo_primeiro': self.get_tupla(ordenacao.decimo_primeiro), + 'decimo_segundo': self.get_tupla(ordenacao.decimo_segundo), + 'decimo_terceiro': self.get_tupla(ordenacao.decimo_terceiro), + 'decimo_quarto': self.get_tupla(ordenacao.decimo_quarto) + } + + return initial - ordenacao.save() + def get_success_url(self): + return reverse('sapl.base:sistema') + def form_valid(self, form): + form.save() return HttpResponseRedirect(self.get_success_url()) @@ -1292,264 +1329,417 @@ def get_turno(turno): return '' -class ResumoView(DetailView): - template_name = 'sessao/resumo.html' - model = SessaoPlenaria +def get_identificação_basica(sessao_plenaria): + # ===================================================================== + # Identificação Básica + data_inicio = sessao_plenaria.data_inicio + abertura = data_inicio.strftime('%d/%m/%Y') if data_inicio else '' + data_fim = sessao_plenaria.data_fim + encerramento = data_fim.strftime('%d/%m/%Y') + ' -' if data_fim else '' + return({'basica': [ + _('Tipo de Sessão: %(tipo)s') % {'tipo': sessao_plenaria.tipo}, + _('Abertura: %(abertura)s - %(hora_inicio)s') % { + 'abertura': abertura, 'hora_inicio': sessao_plenaria.hora_inicio}, + _('Encerramento: %(encerramento)s %(hora_fim)s') % { + 'encerramento': encerramento, 'hora_fim': sessao_plenaria.hora_fim} + ], + 'sessaoplenaria': sessao_plenaria}) + + +def get_conteudo_multimidia(sessao_plenaria): + context = {} + if sessao_plenaria.url_audio: + context['multimidia_audio'] = _( + 'Audio: ') + str(sessao_plenaria.url_audio) + else: + context['multimidia_audio'] = _('Audio: Indisponível') + if sessao_plenaria.url_video: + context['multimidia_video'] = _( + 'Video: ') + str(sessao_plenaria.url_video) + else: + context['multimidia_video'] = _('Video: Indisponível') + return context - def get(self, request, *args, **kwargs): - self.object = self.get_object() - context = self.get_context_data(object=self.object) - # ===================================================================== - # Identificação Básica - data_inicio = self.object.data_inicio - abertura = data_inicio.strftime('%d/%m/%Y') if data_inicio else '' +def get_mesa_diretora(sessao_plenaria): + mesa = IntegranteMesa.objects.filter(sessao_plenaria=sessao_plenaria).order_by('cargo_id') + integrantes = [{'parlamentar': m.parlamentar, + 'cargo': m.cargo} for m in mesa] + return {'mesa': integrantes} - data_fim = self.object.data_fim - encerramento = data_fim.strftime('%d/%m/%Y') + ' -' if data_fim else '' - context.update({'basica': [ - _('Tipo de Sessão: %(tipo)s') % {'tipo': self.object.tipo}, - _('Abertura: %(abertura)s - %(hora_inicio)s') % { - 'abertura': abertura, 'hora_inicio': self.object.hora_inicio}, - _('Encerramento: %(encerramento)s %(hora_fim)s') % { - 'encerramento': encerramento, 'hora_fim': self.object.hora_fim} - ]}) - # ===================================================================== - # Conteúdo Multimídia - if self.object.url_audio: - context.update({'multimidia_audio': - _('Audio: ') + str(self.object.url_audio)}) - else: - context.update({'multimidia_audio': _('Audio: Indisponível')}) +def get_presenca_sessao(sessao_plenaria): - if self.object.url_video: - context.update({'multimidia_video': - _('Video: ') + str(self.object.url_video)}) - else: - context.update({'multimidia_video': _('Video: Indisponível')}) + parlamentares_sessao = [p.parlamentar for p in SessaoPlenariaPresenca.objects.filter( + sessao_plenaria_id=sessao_plenaria.id + ).order_by('parlamentar__nome_parlamentar')] - # ===================================================================== - # Mesa Diretora - mesa = IntegranteMesa.objects.filter( - sessao_plenaria=self.object) + ausentes_sessao = JustificativaAusencia.objects.filter( + sessao_plenaria_id=sessao_plenaria.id + ).order_by('parlamentar__nome_parlamentar') - integrantes = [] - for m in mesa: - parlamentar = Parlamentar.objects.get( - id=m.parlamentar_id) - cargo = CargoMesa.objects.get( - id=m.cargo_id) - integrante = {'parlamentar': parlamentar, 'cargo': cargo} - integrantes.append(integrante) + return ({'presenca_sessao': parlamentares_sessao, + 'justificativa_ausencia': ausentes_sessao}) - context.update({'mesa': ordenar_integrantes_por_cargo(integrantes)}) - # ===================================================================== - # Presença Sessão - presencas = SessaoPlenariaPresenca.objects.filter( - sessao_plenaria_id=self.object.id - ).order_by('parlamentar__nome_parlamentar') +def get_expedientes(sessao_plenaria): + expediente = ExpedienteSessao.objects.filter( + sessao_plenaria_id=sessao_plenaria.id).order_by('tipo__nome') + expedientes = [] + for e in expediente: + tipo = TipoExpediente.objects.get(id=e.tipo_id) + conteudo = e.conteudo + ex = {'tipo': tipo, 'conteudo': conteudo} + expedientes.append(ex) + return ({'expedientes': expedientes}) - parlamentares_sessao = [p.parlamentar for p in presencas] - ausentes_sessao = JustificativaAusencia.objects.filter( - sessao_plenaria_id=self.object.id - ).order_by('parlamentar__nome_parlamentar') +def get_materias_expediente(sessao_plenaria): + materias = ExpedienteMateria.objects.filter( + sessao_plenaria_id=sessao_plenaria.id) - context.update({'presenca_sessao': parlamentares_sessao, - 'justificativa_ausencia': ausentes_sessao}) + materias_expediente = [] + for m in materias: - # ===================================================================== - # Expedientes - expediente = ExpedienteSessao.objects.filter( - sessao_plenaria_id=self.object.id).order_by('tipo__nome') + ementa = m.materia.ementa + titulo = m.materia + numero = m.numero_ordem - expedientes = [] - for e in expediente: - tipo = TipoExpediente.objects.get(id=e.tipo_id) - conteudo = e.conteudo - ex = {'tipo': tipo, 'conteudo': conteudo} - expedientes.append(ex) - context.update({'expedientes': expedientes}) + tramitacao = '' + tramitacoes = Tramitacao.objects.filter(materia=m.materia).order_by('-pk') + for aux_tramitacao in tramitacoes: + if aux_tramitacao.turno: + tramitacao = aux_tramitacao + break - # ===================================================================== - # Matérias Expediente - materias = ExpedienteMateria.objects.filter( - sessao_plenaria_id=self.object.id) + turno = None + if tramitacao: + turno = get_turno(tramitacao.turno) + + rv = m.registrovotacao_set.first() + rp = m.retiradapauta_set.filter(materia=m.materia).first() + if rv: + resultado = rv.tipo_resultado_votacao.nome + resultado_observacao = rv.observacao + elif rp: + resultado = rp.tipo_de_retirada.descricao + resultado_observacao = rp.observacao + else: + resultado = _('Matéria não votada') + resultado_observacao = _(' ') + + autoria = Autoria.objects.filter(materia_id=m.materia_id) + autor = [str(x.autor) for x in autoria] + + mat = {'ementa': ementa, + 'titulo': titulo, + 'numero': numero, + 'turno': turno, + 'resultado': resultado, + 'resultado_observacao': resultado_observacao, + 'autor': autor, + 'numero_protocolo': m.materia.numero_protocolo, + 'numero_processo': m.materia.numeracao_set.last(), + 'observacao': m.observacao + } + materias_expediente.append(mat) + + context = {'materia_expediente': materias_expediente} + return context - materias_expediente = [] - for m in materias: - ementa = m.materia.ementa - titulo = m.materia - numero = m.numero_ordem - tramitacao = m.materia.tramitacao_set.last() - turno = None +def get_oradores_expediente(sessao_plenaria): + oradores = [] + for orador in OradorExpediente.objects.filter( + sessao_plenaria_id=sessao_plenaria.id).order_by('numero_ordem'): + numero_ordem = orador.numero_ordem + url_discurso = orador.url_discurso + observacao = orador.observacao + parlamentar = Parlamentar.objects.get( + id=orador.parlamentar_id) + ora = {'numero_ordem': numero_ordem, + 'url_discurso': url_discurso, + 'parlamentar': parlamentar, + 'observacao': observacao + } + oradores.append(ora) + return {'oradores': oradores} - if tramitacao: - turno = get_turno(tramitacao.turno) - rv = m.registrovotacao_set.first() - rp = m.retiradapauta_set.filter(materia=m.materia).first() - if rv: - resultado = rv.tipo_resultado_votacao.nome - resultado_observacao = rv.observacao - elif rp: - resultado = rp.tipo_de_retirada.descricao - resultado_observacao = rp.observacao - else: - resultado = _('Matéria não votada') - resultado_observacao = _(' ') +def get_presenca_ordem_do_dia(sessao_plenaria): + parlamentares_ordem = [p.parlamentar for p in PresencaOrdemDia.objects.filter( + sessao_plenaria_id=sessao_plenaria.id + ).order_by('parlamentar__nome_parlamentar')] - autoria = Autoria.objects.filter(materia_id=m.materia_id) - autor = [str(x.autor) for x in autoria] + return {'presenca_ordem': parlamentares_ordem} - mat = {'ementa': ementa, - 'titulo': titulo, - 'numero': numero, - 'turno': turno, - 'resultado': resultado, - 'resultado_observacao': resultado_observacao, - 'autor': autor, - 'numero_protocolo': m.materia.numero_protocolo, - 'numero_processo': m.materia.numeracao_set.last(), - 'observacao': m.observacao - } - materias_expediente.append(mat) - context.update({'materia_expediente': materias_expediente}) +def get_assinaturas(sessao_plenaria): + mesa_dia = get_mesa_diretora(sessao_plenaria)['mesa'] - # ===================================================================== - # Oradores Expediente - oradores = [] - for orador in OradorExpediente.objects.filter( - sessao_plenaria_id=self.object.id).order_by('numero_ordem'): - numero_ordem = orador.numero_ordem - url_discurso = orador.url_discurso - observacao = orador.observacao - parlamentar = Parlamentar.objects.get( - id=orador.parlamentar_id) - ora = {'numero_ordem': numero_ordem, - 'url_discurso': url_discurso, - 'parlamentar': parlamentar, - 'observacao': observacao - } - oradores.append(ora) + presidente_dia = [next(iter( + [m['parlamentar'] for m in mesa_dia if m['cargo'].descricao == 'Presidente']), + '')] - context.update({'oradores': oradores}) + parlamentares_ordem = [p.parlamentar for p in PresencaOrdemDia.objects.filter( + sessao_plenaria_id=sessao_plenaria.id + ).order_by('parlamentar__nome_parlamentar')] - # ===================================================================== - # Presença Ordem do Dia - presencas = PresencaOrdemDia.objects.filter( - sessao_plenaria_id=self.object.id - ).order_by('parlamentar__nome_parlamentar') + parlamentares_mesa = [m['parlamentar'] for m in mesa_dia] + + # filtra parlamentares retirando os que sao da mesa + parlamentares_ordem = [p for p in parlamentares_ordem if p not in parlamentares_mesa] + + context = {} + + config_assinatura_ata = AppsAppConfig.objects.first().assinatura_ata + if config_assinatura_ata == 'T' and parlamentares_ordem: + context.update( + {'texto_assinatura': 'Assinatura de Todos os Parlamentares Presentes na Sessão'}) + context.update({'assinatura_mesa': mesa_dia, + 'assinatura_presentes': parlamentares_ordem}) + elif config_assinatura_ata == 'M' and mesa_dia: + context.update( + {'texto_assinatura': 'Assinatura da Mesa Diretora da Sessão'}) + context.update({'assinatura_mesa': mesa_dia}) + elif config_assinatura_ata == 'P' and presidente_dia: + context.update( + {'texto_assinatura': 'Assinatura do Presidente da Sessão'}) + context.update({'assinatura_mesa': presidente_dia}) + + return context - parlamentares_mesa_dia = [m['parlamentar'] for m in context['mesa']] - # composicao_mesa = ComposicaoMesa.objects.filter(sessao_legislativa=sessao) - presidente_dia = '' - for m in context['mesa']: - if m['cargo'].descricao == 'Presidente': - presidente_dia = [m['parlamentar']] +def get_materias_ordem_do_dia(sessao_plenaria): + ordem = OrdemDia.objects.filter(sessao_plenaria_id=sessao_plenaria.id) + materias_ordem = [] + for o in ordem: + ementa = o.materia.ementa + ementa_observacao = o.observacao + titulo = o.materia + numero = o.numero_ordem + + tramitacao = '' + tramitacoes = Tramitacao.objects.filter(materia=o.materia).order_by('-pk') + for aux_tramitacao in tramitacoes: + if aux_tramitacao.turno: + tramitacao = aux_tramitacao break - parlamentares_ordem = [p.parlamentar for p in presencas] + turno = None + if tramitacao: + turno = get_turno(tramitacao.turno) - context.update({'presenca_ordem': parlamentares_ordem}) + # Verificar resultado + rv = o.registrovotacao_set.filter(materia=o.materia).first() + rp = o.retiradapauta_set.filter(materia=o.materia).first() + if rv: + resultado = rv.tipo_resultado_votacao.nome + resultado_observacao = rv.observacao - config_assinatura_ata = AppsAppConfig.objects.first().assinatura_ata - if config_assinatura_ata == 'T' and parlamentares_ordem: - context.update( - {'texto_assinatura': 'Assinatura de Todos os Parlamentares Presentes na Sessão'}) - context.update({'assinatura_presentes': parlamentares_ordem}) - elif config_assinatura_ata == 'M' and parlamentares_mesa_dia: - context.update( - {'texto_assinatura': 'Assinatura da Mesa Diretora da Sessão'}) - context.update({'assinatura_presentes': parlamentares_mesa_dia}) - elif config_assinatura_ata == 'P' and presidente_dia: - context.update( - {'texto_assinatura': 'Assinatura do Presidente da Sessão'}) - context.update({'assinatura_presentes': presidente_dia}) + elif rp: + resultado = rp.tipo_de_retirada.descricao + resultado_observacao = rp.observacao - # ===================================================================== - # Matérias Ordem do Dia - ordem = OrdemDia.objects.filter( - sessao_plenaria_id=self.object.id) - materias_ordem = [] - for o in ordem: - ementa = o.materia.ementa - ementa_observacao = o.observacao - titulo = o.materia - numero = o.numero_ordem - tramitacao = o.materia.tramitacao_set.last() - turno = None - if tramitacao: - turno = get_turno(tramitacao.turno) + else: + resultado = _('Matéria não votada') + resultado_observacao = _(' ') + + voto_sim = "" + voto_nao = "" + voto_abstencoes = "" + voto_nominal = [] + + if o.tipo_votacao == 2: + votos = VotoParlamentar.objects.filter(ordem=o.id) + for voto in votos: + aux_voto = (voto.parlamentar.nome_completo, voto.voto) + voto_nominal.append(aux_voto) + try: + voto = RegistroVotacao.objects.filter(ordem=o.id).last() + voto_sim = voto.numero_votos_sim + voto_nao = voto.numero_votos_nao + voto_abstencoes = voto.numero_abstencoes + except AttributeError: + voto_sim = " Não Informado" + voto_nao = " Não Informado" + voto_abstencoes = " Não Informado" + + autoria = Autoria.objects.filter( + materia_id=o.materia_id) + autor = [str(x.autor) for x in autoria] + mat = {'ementa': ementa, + 'ementa_observacao': ementa_observacao, + 'titulo': titulo, + 'numero': numero, + 'turno': turno, + 'resultado': resultado, + 'resultado_observacao': resultado_observacao, + 'autor': autor, + 'numero_protocolo': o.materia.numero_protocolo, + 'numero_processo': o.materia.numeracao_set.last(), + 'tipo_votacao': o.TIPO_VOTACAO_CHOICES[o.tipo_votacao], + 'voto_sim': voto_sim, + 'voto_nao': voto_nao, + 'voto_abstencoes': voto_abstencoes, + 'voto_nominal': voto_nominal, + } + materias_ordem.append(mat) + + context = {'materias_ordem': materias_ordem} + return context - # Verificar resultado - rv = o.registrovotacao_set.filter(materia=o.materia).first() - rp = o.retiradapauta_set.filter(materia=o.materia).first() - if rv: - resultado = rv.tipo_resultado_votacao.nome - resultado_observacao = rv.observacao - elif rp: - resultado = rp.tipo_de_retirada.descricao - resultado_observacao = rp.observacao + +def get_oradores_ordemdia(sessao_plenaria): + oradores = [] + + oradores_ordem_dia = OradorOrdemDia.objects.filter( + sessao_plenaria_id=sessao_plenaria.id + ).order_by('numero_ordem') + + for orador in oradores_ordem_dia: + numero_ordem = orador.numero_ordem + url_discurso = orador.url_discurso + observacao = orador.observacao + parlamentar = Parlamentar.objects.get( + id=orador.parlamentar_id + ) + o = { + 'numero_ordem': numero_ordem, + 'url_discurso': url_discurso, + 'parlamentar': parlamentar, + 'observacao': observacao + } + oradores.append(o) + + context = {'oradores_ordemdia': oradores} + return context + + +def get_oradores_explicações_pessoais(sessao_plenaria): + oradores_explicacoes = [] + for orador in Orador.objects.filter( + sessao_plenaria_id=sessao_plenaria.id).order_by('numero_ordem'): + for parlamentar in Parlamentar.objects.filter( + id=orador.parlamentar.id): + partido_sigla = Filiacao.objects.filter( + parlamentar=parlamentar).last() + if not partido_sigla: + sigla = '' else: - resultado = _('Matéria não votada') - resultado_observacao = _(' ') + sigla = partido_sigla.partido.sigla + oradores = { + 'numero_ordem': orador.numero_ordem, + 'parlamentar': parlamentar, + 'sgl_partido': sigla + } + oradores_explicacoes.append(oradores) + context = {'oradores_explicacoes': oradores_explicacoes} + return context - autoria = Autoria.objects.filter( - materia_id=o.materia_id) - autor = [str(x.autor) for x in autoria] - mat = {'ementa': ementa, - 'ementa_observacao': ementa_observacao, - 'titulo': titulo, - 'numero': numero, - 'turno': turno, - 'resultado': resultado, - 'resultado_observacao': resultado_observacao, - 'autor': autor, - 'numero_protocolo': o.materia.numero_protocolo, - 'numero_processo': o.materia.numeracao_set.last() - } - materias_ordem.append(mat) +def get_ocorrencias_da_sessão(sessao_plenaria): + ocorrencias_sessao = OcorrenciaSessao.objects.filter( + sessao_plenaria_id=sessao_plenaria.id) + context = {'ocorrencias_da_sessao': ocorrencias_sessao} + return context - context.update({'materias_ordem': materias_ordem}) +class ResumoView(DetailView): + template_name = 'sessao/resumo.html' + model = SessaoPlenaria + logger = logging.getLogger(__name__) + + def get_context(self, *args, **kwargs): + self.object = self.get_object() + context = self.get_context_data(object=self.object) + + # Votos de Votação Nominal de Matérias Expediente + materias_expediente_votacao_nominal = ExpedienteMateria.objects.filter( + sessao_plenaria_id=self.object.id, + tipo_votacao=2).order_by('-materia') + + votacoes = [] + for mevn in materias_expediente_votacao_nominal: + + votos_materia = [] + titulo_materia = mevn.materia + registro = RegistroVotacao.objects.filter(expediente=mevn) + if registro: + for vp in VotoParlamentar.objects.filter(votacao=registro).order_by('parlamentar'): + votos_materia.append(vp) + + dados_votacao = { + 'titulo': titulo_materia, + 'votos': votos_materia + } + votacoes.append(dados_votacao) + + context.update({'votos_nominais_materia_expediente': votacoes}) + + # ===================================================================== + # Identificação Básica + context.update(get_identificação_basica(self.object)) + # ===================================================================== + # Conteúdo Multimídia + context.update(get_conteudo_multimidia(self.object)) + # ===================================================================== + # Mesa Diretora + context.update(get_mesa_diretora(self.object)) + # ===================================================================== + # Presença Sessão + context.update(get_presenca_sessao(self.object)) + # ===================================================================== + # Expedientes + context.update(get_expedientes(self.object)) # ===================================================================== + # Matérias Expediente + context.update(get_materias_expediente(self.object)) + # ===================================================================== + # Oradores Expediente + context.update(get_oradores_expediente(self.object)) + # ===================================================================== + # Presença Ordem do Dia + context.update(get_presenca_ordem_do_dia(self.object)) + # ===================================================================== + # Assinaturas + context.update(get_assinaturas(self.object)) + # ===================================================================== + # Matérias Ordem do Dia + # Votos de Votação Nominal de Matérias Ordem do Dia + materias_ordem_dia_votacao_nominal = OrdemDia.objects.filter( + sessao_plenaria_id=self.object.id, + tipo_votacao=2).order_by('-materia') + + votacoes_od = [] + for modvn in materias_ordem_dia_votacao_nominal: + votos_materia_od = [] + t_materia = modvn.materia + registro_od = RegistroVotacao.objects.filter(ordem=modvn) + if registro_od: + for vp_od in VotoParlamentar.objects.filter(votacao=registro_od).order_by('parlamentar'): + votos_materia_od.append(vp_od) + + dados_votacao_od = { + 'titulo': t_materia, + 'votos': votos_materia_od + } + votacoes_od.append(dados_votacao_od) + + context.update({'votos_nominais_materia_ordem_dia': votacoes_od}) + + context.update(get_materias_ordem_do_dia(self.object)) + # ===================================================================== + # Oradores Ordem do Dia + context.update(get_oradores_ordemdia(self.object)) + # ===================================================================== # Oradores nas Explicações Pessoais - oradores_explicacoes = [] - for orador in Orador.objects.filter( - sessao_plenaria_id=self.object.id).order_by('numero_ordem'): - for parlamentar in Parlamentar.objects.filter( - id=orador.parlamentar.id): - partido_sigla = Filiacao.objects.filter( - parlamentar=parlamentar).last() - if not partido_sigla: - sigla = '' - else: - sigla = partido_sigla.partido.sigla - oradores = { - 'numero_ordem': orador.numero_ordem, - 'parlamentar': parlamentar, - 'sgl_partido': sigla - } - oradores_explicacoes.append(oradores) - context.update({'oradores_explicacoes': oradores_explicacoes}) - + context.update(get_oradores_explicações_pessoais(self.object)) # ===================================================================== # Ocorrẽncias da Sessão - ocorrencias_sessao = OcorrenciaSessao.objects.filter( - sessao_plenaria_id=self.object.id) - - context.update({'ocorrencias_da_sessao': ocorrencias_sessao}) - + context.update(get_ocorrencias_da_sessão(self.object)) # ===================================================================== # Indica a ordem com a qual o template será renderizado - ordenacao = ResumoOrdenacao.objects.first() dict_ord_template = { 'cont_mult': 'conteudo_multimidia.html', 'exp': 'expedientes.html', @@ -1557,40 +1747,38 @@ class ResumoView(DetailView): 'lista_p': 'lista_presenca.html', 'lista_p_o_d': 'lista_presenca_ordem_dia.html', 'mat_exp': 'materias_expediente.html', + 'v_n_mat_exp': 'votos_nominais_materias_expediente.html', 'mat_o_d': 'materias_ordem_dia.html', + 'v_n_mat_o_d': 'votos_nominais_materias_ordem_dia.html', 'mesa_d': 'mesa_diretora.html', 'oradores_exped': 'oradores_expediente.html', + 'oradores_o_d': 'oradores_ordemdia.html', 'oradores_expli': 'oradores_explicacoes.html', 'ocorr_sessao': 'ocorrencias_da_sessao.html' } - if ordenacao: - context.update( - {'primeiro_ordenacao': dict_ord_template[ordenacao.primeiro], - 'segundo_ordenacao': dict_ord_template[ordenacao.segundo], - 'terceiro_ordenacao': dict_ord_template[ordenacao.terceiro], - 'quarto_ordenacao': dict_ord_template[ordenacao.quarto], - 'quinto_ordenacao': dict_ord_template[ordenacao.quinto], - 'sexto_ordenacao': dict_ord_template[ordenacao.sexto], - 'setimo_ordenacao': dict_ord_template[ordenacao.setimo], - 'oitavo_ordenacao': dict_ord_template[ordenacao.oitavo], - 'nono_ordenacao': dict_ord_template[ordenacao.nono], - 'decimo_ordenacao': dict_ord_template[ordenacao.decimo], - 'decimo_primeiro_ordenacao': dict_ord_template[ordenacao.decimo_primeiro]}) - else: - context.update( - {'primeiro_ordenacao': dict_ord_template['id_basica'], - 'segundo_ordenacao': dict_ord_template['cont_mult'], - 'terceiro_ordenacao': dict_ord_template['mesa_d'], - 'quarto_ordenacao': dict_ord_template['lista_p'], - 'quinto_ordenacao': dict_ord_template['exp'], - 'sexto_ordenacao': dict_ord_template['mat_exp'], - 'setimo_ordenacao': dict_ord_template['oradores_exped'], - 'oitavo_ordenacao': dict_ord_template['lista_p_o_d'], - 'nono_ordenacao': dict_ord_template['mat_o_d'], - 'decimo_ordenacao': dict_ord_template['oradores_expli'], - 'decimo_primeiro_ordenacao': dict_ord_template['ocorr_sessao']}) + ordenacao = ResumoOrdenacao.objects.get_or_create()[0] + context.update({ + 'primeiro_ordenacao': dict_ord_template[ordenacao.primeiro], + 'segundo_ordenacao': dict_ord_template[ordenacao.segundo], + 'terceiro_ordenacao': dict_ord_template[ordenacao.terceiro], + 'quarto_ordenacao': dict_ord_template[ordenacao.quarto], + 'quinto_ordenacao': dict_ord_template[ordenacao.quinto], + 'sexto_ordenacao': dict_ord_template[ordenacao.sexto], + 'setimo_ordenacao': dict_ord_template[ordenacao.setimo], + 'oitavo_ordenacao': dict_ord_template[ordenacao.oitavo], + 'nono_ordenacao': dict_ord_template[ordenacao.nono], + 'decimo_ordenacao': dict_ord_template[ordenacao.decimo], + 'decimo_primeiro_ordenacao': dict_ord_template[ordenacao.decimo_primeiro], + 'decimo_segundo_ordenacao': dict_ord_template[ordenacao.decimo_segundo], + 'decimo_terceiro_ordenacao': dict_ord_template[ordenacao.decimo_terceiro], + 'decimo_quarto_ordenacao': dict_ord_template[ordenacao.decimo_quarto] + }) + + return context + def get(self, request, *args, **kwargs): + context = self.get_context() return self.render_to_response(context) @@ -1696,6 +1884,12 @@ class OcorrenciaSessaoView(FormMixin, DetailView): logger = logging.getLogger(__name__) + def get_context_data(self, **kwargs): + context = FormMixin.get_context_data(self, **kwargs) + context['title'] = 'Ocorrências da Sessão (%s)' % ( + self.object) + return context + def delete(self): OcorrenciaSessao.objects.filter(sessao_plenaria=self.object).delete() @@ -1911,6 +2105,8 @@ class VotacaoView(SessaoPermissionMixin): votacao.ordem_id = ordem_id votacao.tipo_resultado_votacao_id = int( request.POST['resultado_votacao']) + votacao.user = request.user + votacao.ip = get_client_ip(request) votacao.save() except Exception as e: username = request.user.username @@ -2131,6 +2327,8 @@ class VotacaoNominalAbstract(SessaoPermissionMixin): votacao.numero_votos_nao = votos_nao votacao.numero_abstencoes = abstencoes votacao.observacao = request.POST.get('observacao', None) + votacao.user = request.user + votacao.ip = get_client_ip(request) votacao.materia_id = materia_votacao.materia.id if self.ordem: @@ -2158,6 +2356,8 @@ class VotacaoNominalAbstract(SessaoPermissionMixin): voto_parlamentar.voto = voto voto_parlamentar.parlamentar_id = parlamentar_id voto_parlamentar.votacao_id = votacao.id + voto_parlamentar.user = request.user + voto_parlamentar.ip = get_client_ip(request) voto_parlamentar.save() resultado = form.cleaned_data['resultado_votacao'] @@ -2595,10 +2795,10 @@ class VotacaoExpedienteView(SessaoPermissionMixin): if (int(request.POST['voto_presidente']) == 0): qtde_presentes -= 1 - if (qtde_votos > qtde_presentes or qtde_votos < qtde_presentes): + if qtde_votos != qtde_presentes: form._errors["total_votos"] = ErrorList([u""]) return self.render_to_response(context) - elif (qtde_presentes == qtde_votos): + else: try: votacao = RegistroVotacao() votacao.numero_votos_sim = int(request.POST['votos_sim']) @@ -2609,6 +2809,8 @@ class VotacaoExpedienteView(SessaoPermissionMixin): votacao.expediente_id = expediente_id votacao.tipo_resultado_votacao_id = int( request.POST['resultado_votacao']) + votacao.user = request.user + votacao.ip = get_client_ip(request) votacao.save() except Exception as e: username = request.user.username @@ -2924,7 +3126,7 @@ class PesquisarSessaoPlenariaView(FilterView): # Então a ordem da URL está diferente data = self.filterset.data if data and data.get('data_inicio__year') is not None: - url = "&" + str(self.request.environ['QUERY_STRING']) + url = "&" + str(self.request.META['QUERY_STRING']) if url.startswith("&page"): ponto_comeco = url.find('data_inicio__year=') - 1 url = url[ponto_comeco:] @@ -3005,6 +3207,7 @@ class AdicionarVariasMateriasExpediente(PermissionRequiredForAppCrudMixin, self).get_context_data(**kwargs) context['title'] = _('Pesquisar Matéria Legislativa') + context['root_pk'] = self.kwargs['pk'] self.filterset.form.fields['o'].label = _('Ordenação') @@ -3285,17 +3488,20 @@ class VotacaoEmBlocoExpediente(PermissionRequiredForAppCrudMixin, ListView): logger = logging.getLogger(__name__) def get_queryset(self): - kwargs = self.kwargs - return ExpedienteMateria.objects.filter(sessao_plenaria_id=kwargs['pk'], + return ExpedienteMateria.objects.filter(sessao_plenaria_id=self.kwargs['pk'], resultado='') def get_context_data(self, **kwargs): context = super(VotacaoEmBlocoExpediente, self).get_context_data(**kwargs) - context['turno_choices'] = Tramitacao.TURNO_CHOICES - context['title'] = SessaoPlenaria.objects.get(id=self.kwargs['pk']) context['pk'] = self.kwargs['pk'] context['root_pk'] = self.kwargs['pk'] + if not verifica_sessao_iniciada(self.request, self.kwargs['pk']): + context['sessao_iniciada'] = False + return context + context['sessao_iniciada'] = True + context['turno_choices'] = Tramitacao.TURNO_CHOICES + context['title'] = SessaoPlenaria.objects.get(id=self.kwargs['pk']) return context @@ -3314,9 +3520,13 @@ class VotacaoEmBlocoOrdemDia(PermissionRequiredForAppCrudMixin, ListView): def get_context_data(self, **kwargs): context = super(VotacaoEmBlocoOrdemDia, self).get_context_data(**kwargs) - context['turno_choices'] = Tramitacao.TURNO_CHOICES context['pk'] = self.kwargs['pk'] context['root_pk'] = self.kwargs['pk'] + if not verifica_sessao_iniciada(self.request, self.kwargs['pk']): + context['sessao_iniciada'] = False + return context + context['sessao_iniciada'] = True + context['turno_choices'] = Tramitacao.TURNO_CHOICES context['title'] = SessaoPlenaria.objects.get(id=self.kwargs['pk']) return context @@ -3335,7 +3545,9 @@ class VotacaoEmBlocoSimbolicaView(PermissionRequiredForAppCrudMixin, TemplateVie if not 'context' in locals(): context = {'pk': self.kwargs['pk'], 'root_pk': self.kwargs['pk'], - 'title': SessaoPlenaria.objects.get(id=self.kwargs['pk']) + 'title': SessaoPlenaria.objects.get(id=self.kwargs['pk']), + 'origem': request.POST['origem'], + 'subnav_template_name': 'sessao/subnav.yaml' } if 'marcadas_1' in request.POST: @@ -3386,6 +3598,8 @@ class VotacaoEmBlocoSimbolicaView(PermissionRequiredForAppCrudMixin, TemplateVie resultado = TipoResultadoVotacao.objects.get( id=request.POST['resultado_votacao']) votacao.tipo_resultado_votacao = resultado + votacao.user = request.user + votacao.ip = get_client_ip(request) votacao.save() except Exception as e: username = request.user.username @@ -3417,6 +3631,8 @@ class VotacaoEmBlocoSimbolicaView(PermissionRequiredForAppCrudMixin, TemplateVie resultado = TipoResultadoVotacao.objects.get( id=request.POST['resultado_votacao']) votacao.tipo_resultado_votacao = resultado + votacao.user = request.user + votacao.ip = get_client_ip(request) votacao.save() except Exception as e: username = request.user.username @@ -3514,6 +3730,7 @@ class VotacaoEmBlocoNominalView(PermissionRequiredForAppCrudMixin, TemplateView) context = {'pk': self.kwargs['pk'], 'root_pk': self.kwargs['pk'], 'title': SessaoPlenaria.objects.get(id=self.kwargs['pk']), + 'origem': request.POST['origem'], 'subnav_template_name': 'sessao/subnav.yaml'} if 'marcadas_2' in request.POST: @@ -3593,6 +3810,8 @@ class VotacaoEmBlocoNominalView(PermissionRequiredForAppCrudMixin, TemplateView) votacao.materia = ordem.materia votacao.ordem = ordem votacao.tipo_resultado_votacao = form.cleaned_data['resultado_votacao'] + votacao.user = request.user + votacao.ip = get_client_ip(request) votacao.save() for votos in request.POST.getlist('voto_parlamentar'): @@ -3607,6 +3826,8 @@ class VotacaoEmBlocoNominalView(PermissionRequiredForAppCrudMixin, TemplateView) voto_parlamentar.voto = voto voto_parlamentar.parlamentar_id = parlamentar_id voto_parlamentar.votacao_id = votacao.id + voto_parlamentar.user = request.user + voto_parlamentar.ip = get_client_ip(request) voto_parlamentar.save() ordem.resultado = form.cleaned_data['resultado_votacao'].nome @@ -3634,6 +3855,8 @@ class VotacaoEmBlocoNominalView(PermissionRequiredForAppCrudMixin, TemplateView) votacao.materia = expediente.materia votacao.expediente = expediente votacao.tipo_resultado_votacao = form.cleaned_data['resultado_votacao'] + votacao.user = request.user + votacao.ip = get_client_ip(request) votacao.save() # Salva os votos de cada parlamentar @@ -3649,6 +3872,8 @@ class VotacaoEmBlocoNominalView(PermissionRequiredForAppCrudMixin, TemplateView) voto_parlamentar.voto = voto voto_parlamentar.parlamentar_id = parlamentar_id voto_parlamentar.votacao_id = votacao.id + voto_parlamentar.user = request.user + voto_parlamentar.ip = get_client_ip(request) voto_parlamentar.save() expediente.resultado = form.cleaned_data['resultado_votacao'].nome @@ -3677,7 +3902,7 @@ class VotacaoEmBlocoNominalView(PermissionRequiredForAppCrudMixin, TemplateView) voto_parlamentar = VotoParlamentar.objects.filter( ordem=ordens_id[0]) else: - presencas = PresencaOrdemDia.objects.filter( + presencas = SessaoPlenariaPresenca.objects.filter( sessao_plenaria_id=self.kwargs['pk']) expedientes_id = self.request.POST.getlist('marcadas_2') voto_parlamentar = VotoParlamentar.objects.filter( @@ -3692,7 +3917,7 @@ class VotacaoEmBlocoNominalView(PermissionRequiredForAppCrudMixin, TemplateView) voto_parlamentar = VotoParlamentar.objects.filter( ordem=ordens_id[0]) else: - presencas = PresencaOrdemDia.objects.filter( + presencas = SessaoPlenariaPresenca.objects.filter( sessao_plenaria_id=self.kwargs['pk']) expedientes_id = self.request.POST.getlist('expedientes') voto_parlamentar = VotoParlamentar.objects.filter( diff --git a/sapl/settings.py b/sapl/settings.py old mode 100755 new mode 100644 index a44b3f73e..ae900faf7 --- a/sapl/settings.py +++ b/sapl/settings.py @@ -41,6 +41,8 @@ ALLOWED_HOSTS = ['*'] LOGIN_REDIRECT_URL = '/' LOGIN_URL = '/login/?next=' +SAPL_VERSION = '3.1.155' + if DEBUG: EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend' else: @@ -93,7 +95,6 @@ INSTALLED_APPS = ( 'reversion_compare', 'haystack', - 'whoosh', 'speedinfo', 'webpack_loader', @@ -104,8 +105,8 @@ INSTALLED_APPS = ( # Desabilita a indexação textual até encontramos uma solução para a issue # https://github.com/interlegis/sapl/issues/2055 HAYSTACK_SIGNAL_PROCESSOR = 'haystack.signals.BaseSignalProcessor' # Disable auto index -SEARCH_BACKEND = 'haystack.backends.whoosh_backend.WhooshEngine' -SEARCH_URL = ('PATH', PROJECT_DIR.child('whoosh')) +SEARCH_BACKEND = '' +SEARCH_URL = ['',''] # SOLR USE_SOLR = config('USE_SOLR', cast=bool, default=False) @@ -123,7 +124,7 @@ HAYSTACK_CONNECTIONS = { 'ENGINE': SEARCH_BACKEND, SEARCH_URL[0]: SEARCH_URL[1], 'BATCH_SIZE': 1000, - 'TIMEOUT': 60, + 'TIMEOUT': 20, }, } @@ -272,8 +273,8 @@ FRONTEND_CUSTOM = config('FRONTEND_CUSTOM', default=False, cast=bool) WEBPACK_LOADER = { 'DEFAULT': { 'CACHE': not DEBUG, - 'BUNDLE_DIR_NAME': 'sapl/static/', - 'STATS_FILE': (PROJECT_DIR if not FRONTEND_CUSTOM else PROJECT_DIR.parent.child('sapl-frontend')).child('webpack-stats.json'), + 'BUNDLE_DIR_NAME': 'sapl/static/sapl/frontend', + 'STATS_FILE': (BASE_DIR if not FRONTEND_CUSTOM else PROJECT_DIR.parent.child('sapl-frontend')).child('webpack-stats.json'), 'POLL_INTERVAL': 0.1, 'TIMEOUT': None, 'IGNORE': [r'.+\.hot-update.js', r'.+\.map'] diff --git a/sapl/static/css/compilacao.1e862898.css b/sapl/static/css/compilacao.1e862898.css deleted file mode 100644 index 7a3cac183..000000000 --- a/sapl/static/css/compilacao.1e862898.css +++ /dev/null @@ -1 +0,0 @@ -a:link:after,a:visited:after{content:""}.test_import:nth-child(2n){background-color:#ccc}#wait_message{display:block;position:fixed;top:0;bottom:0;left:0;right:0;background-color:hsla(0,0%,86.3%,.75);z-index:99}#wait_message #msg{position:relative;margin:20% auto;padding:1.2em 2em;max-width:600px;text-align:center;font-size:1.5em;color:#677;border:1px solid #eee;background-color:#fff!important;-webkit-box-shadow:0 1px 2px #999;box-shadow:0 1px 2px #999}.text-center{text-align:center}.cp-notify{z-index:9999;position:fixed;top:2em;left:50%;min-width:600px;-webkit-transform:translate(-50%);transform:translate(-50%);opacity:.97}.cp-notify,.cp-notify.hide{-webkit-transition:all .4s ease;transition:all .4s ease}.cp-notify.hide{opacity:0;top:-1000px;display:block!important}.cp-notify .message{padding:1em;border:2px solid rgba(0,0,0,.1);border-radius:4px;color:rgba(0,0,0,.6);line-height:1em;font-size:1.3em;text-align:center;-webkit-box-shadow:0 0 100px rgba(0,0,0,.2);box-shadow:0 0 100px rgba(0,0,0,.2)}.cp .vigencia-active{margin-top:30px;display:block}.cp .cp-linha-vigencias{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;list-style:none;margin:4rem 0 3rem;padding:0;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.cp .cp-linha-vigencias ul{list-style:none;margin:0;padding:0}.cp .cp-linha-vigencias>li{display:-webkit-box;display:-ms-flexbox;display:flex;position:relative;-webkit-box-flex:1;-ms-flex:1 1 auto;flex:1 1 auto;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;line-height:0;background:#000;height:2px;text-align:center;max-width:4rem}.cp .cp-linha-vigencias>li .circle{display:block;width:10px;line-height:0;background:#000;height:10px;margin:-5px auto 0;border-radius:50%}.cp .cp-linha-vigencias>li>a{position:absolute;white-space:nowrap;line-height:1.8rem;text-align:center}.cp .cp-linha-vigencias>li:nth-child(2n)>a{top:100%}.cp .cp-linha-vigencias>li:nth-child(odd)>a{bottom:100%}.cp .cp-linha-vigencias>li ul{z-index:1;position:absolute;display:none;background:#fff;margin:30px 0;border:1px solid #aaa;-webkit-box-shadow:0 0 10px #aaa;box-shadow:0 0 10px #aaa;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.cp .cp-linha-vigencias>li ul:before{content:" ";width:2px;height:30px;position:absolute;display:block;background-color:#aaa;bottom:100%;left:50%;margin-left:-1px}.cp .cp-linha-vigencias>li ul li{text-align:left}.cp .cp-linha-vigencias>li ul a{display:block;white-space:nowrap;line-height:2rem;padding:0 10px;font-size:1rem}.cp .cp-linha-vigencias>li ul a:hover{background:#eee}.cp .cp-linha-vigencias>li.active .circle{display:block;width:20px;line-height:0;background:#aaa;height:20px;margin:-10px auto 0}.cp .cp-linha-vigencias>li.active:not(:last-child)>a{-webkit-transform:rotate(-90deg);transform:rotate(-90deg);margin-bottom:15px}.cp .cp-linha-vigencias>li.active>a{margin-bottom:5px}.cp .cp-linha-vigencias>li.active:nth-child(2n)>a{bottom:100%;top:auto}.cp .cp-linha-vigencias>li.active ul{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}.cp .cp-linha-vigencias>li.active ul li{text-align:left;width:100%}.cp .cp-vigencias .nav-link{padding:.5rem}.cp .cp-vigencias .dropdown-toggle:after{zoom:.8;margin:0}.cp .cp-vigencias .dropdown-menu{margin-left:.5rem}.cp .cp-vigencias .dropdown-item{padding:0}.cp .cp-vigencias .dropdown-item a{line-height:1;padding:.7rem}.cp .desativado .dpt-link,.cp .desativado .dpt-link *,.cp .desativado .dtxt,.cp .desativado .dtxt *,.cp .dpt .dptt>a.desativado .dpt-link,.cp .dpt .dptt>a.desativado .dpt-link *,.cp .dpt .dptt>a.desativado .dtxt,.cp .dpt .dptt>a.desativado .dtxt *{text-decoration:line-through;color:#999!important}.cp .desativado .dpt-link * table,.cp .desativado .dpt-link * table td,.cp .desativado .dpt-link table,.cp .desativado .dpt-link table td,.cp .desativado .dtxt * table,.cp .desativado .dtxt * table td,.cp .desativado .dtxt table,.cp .desativado .dtxt table td,.cp .dpt .dptt>a.desativado .dpt-link * table,.cp .dpt .dptt>a.desativado .dpt-link * table td,.cp .dpt .dptt>a.desativado .dpt-link table,.cp .dpt .dptt>a.desativado .dpt-link table td,.cp .dpt .dptt>a.desativado .dtxt * table,.cp .dpt .dptt>a.desativado .dtxt * table td,.cp .dpt .dptt>a.desativado .dtxt table,.cp .dpt .dptt>a.desativado .dtxt table td{border:1px dotted #ccc}.cp a{text-decoration:none;cursor:pointer}.cp .diff .desativado,.cp .diff .desativado *,.cp .diff .dpt .dptt>a.desativado,.cp .diff .dpt .dptt>a.desativado *,.cp .dpt .diff .dptt>a.desativado,.cp .dpt .diff .dptt>a.desativado *{text-decoration:line-through;color:#ddd!important;font-size:90%}.cp .diff .added{color:#04de2c}.cp .dpt{font-size:1em;position:relative}.cp .dpt.indent{padding-left:1em}.cp .dpt .ementa{padding:2em 0 2em 35%;font-weight:700}.cp .dpt .anexo,.cp .dpt .capitulo,.cp .dpt .disp_finais,.cp .dpt .disp_gerais,.cp .dpt .disp_preliminares,.cp .dpt .disp_transitorias,.cp .dpt .itemsecao,.cp .dpt .livro,.cp .dpt .parte,.cp .dpt .secao,.cp .dpt .subsecao,.cp .dpt .titulo,.cp .dpt .titulo_generico{text-align:center;margin-bottom:1em;font-size:1.15em;margin-top:3em}.cp .dpt .titulo{margin-top:2em}.cp .dpt .capitulo{margin-top:1.5em;font-size:1.15em}.cp .dpt .secao{margin-top:1.2em;margin-bottom:.7em;font-weight:700;font-size:1.15em}.cp .dpt .itemsecao,.cp .dpt .subsecao{margin-top:1em;margin-bottom:.6em;font-weight:700;font-size:1.15em}.cp .dpt .artigo{font-size:1.15em;float:left}.cp .dpt .artigo .dptt{position:relative}.cp .dpt .caput{margin-top:.3333em;font-size:1.15em}.cp .dpt .paragrafo{font-size:1.1em;margin-top:.2222em}.cp .dpt .inciso{font-size:1.1em;margin-top:.1667em}.cp .dpt .alinea,.cp .dpt .item{font-size:1em;margin-top:2px}.cp .dpt .assinatura,.cp .dpt .fecho_lei{margin-top:.6em;font-size:1.15em}.cp .dpt .page-break{page-break-before:always}.cp .dpt .bloco_alteracao{padding-left:10%;font-style:italic;color:#018}.cp .dpt .bloco_alteracao a{text-decoration:underline}.cp .dpt .bloco_alteracao a,.cp .dpt .bloco_alteracao table,.cp .dpt .bloco_alteracao table td{color:#018!important}.cp .dpt .card-header{font-size:1.7rem}.cp .dpt .dn{font-weight:400;position:relative;font-size:70%}.cp .dpt .dn p,.cp .dpt .dn ul{font-weight:400;margin:0 0 0 0;list-style:none;padding:0}.cp .dpt .dn .dnl{display:block;text-align:left!important}.cp .dpt .dn .dnl *{display:inline}.cp .dpt .dn .dnl .bullet{padding:0 .333em;display:inline-block}.cp .dpt .dn .dnl .dnli{min-height:2.5em}.cp .dpt .dn .dnl .dnli:hover ul{font-size:1rem;clip:auto;opacity:1;background:hsla(0,0%,90.2%,.9)}.cp .dpt .dn .dnl .dnli:hover ul,.cp .dpt .dn .dnl .dnli ul{-webkit-transition:opacity .5s linear,clip 0s .3s;transition:opacity .5s linear,clip 0s .3s}.cp .dpt .dn .dnl .dnli ul{clip:rect(0,0,0,0);opacity:0;position:absolute;background:transparent;right:0;padding:.2em .5em 0 .5em;border:1px solid #c7e3d3;border-top:0;font-size:1.5rem}.cp .dpt .dn .dnl .dnli ul li{display:table-cell;color:#aaa}.cp .dpt .dn .dnl .dnli ul li:hover{color:#787}.cp .dpt .dn .dnl .dnli ul li .nowner,.cp .dpt .dn .dnl .dnli ul li:hover a{color:#27ae60!important}.cp .dpt .dn .dnl .dnli .ntitulo{font-weight:700;color:#03a203;text-decoration:none}.cp .dpt .dn .dnl .dnli .ntitulo a{color:#294!important}.cp .dpt .dn .dnl .dnli .ntexto{color:#06d806}.cp .dpt .dn .dnl .dnli .ntexto a{color:#03a203!important}.cp .dpt .dn .dnl:hover,.cp .dpt .dn .dnl:hover *{display:block}.cp .dpt .dn .dnl:hover>.bullet{display:none}.cp .dpt .dn .dnl:hover .dnli{margin-top:.5em;border-top:1px solid #c7e3d3}.cp .dpt .dptt{clear:left}.cp .dpt .dptt>a{color:#000}.cp .dpt .dptt>a.nota-alteracao{color:#02baf2;font-size:.75em}.cp .dpt .dptt>a.nota-alteracao:hover{text-decoration:underline}.cp .dpt .dptt .dne{position:absolute;display:block;right:0;left:0;top:0;height:0;-webkit-transform:scaleX(0);transform:scaleX(0);-webkit-transform-origin:right;transform-origin:right;-webkit-transition:all .3s ease;transition:all .3s ease;border-top:1px solid #2980b9}.cp .dpt .dptt .dne ul.btns-action{list-style:none;padding:0;position:absolute;right:0;background-color:#2980b9}.cp .dpt .dptt .dne ul.btns-action li{float:left}.cp .dpt .dptt .dne ul.btns-action li:hover{background-color:rgba(0,0,0,.1)}.cp .dpt .dptt .dne ul.btns-action li a{color:#fff;padding:.15em 1em 0;display:inline-block}.cp .dpt .dptt .dne-nota{position:relative;-webkit-transform:scaleX(1);transform:scaleX(1);height:auto;border-top:0}.cp .dpt .dptt .dne-nota ul.btns-action{display:none}.cp .dpt .dptt .dne-nota .dne-form{margin:1em -2em 0;text-align:left;font-size:1rem}.cp .dpt .dptt:hover .dne{height:.1667rem;-webkit-transform:scaleX(1);transform:scaleX(1);-webkit-transition-delay:1s;transition-delay:1s}.cp .dpt .dptt:hover .dne-nota{height:auto;-webkit-transition-delay:0s;transition-delay:0s}.cp .tipo-vigencias{margin-bottom:-6px;opacity:.8}.cp .tipo-vigencias div a{color:#fff}.cp .tipo-vigencias:hover,.cp:hover{opacity:1}.cp.cpe .desativado,.cp.cpe .dpt .dptt>a.desativado{text-decoration:line-through;color:#999!important}.cp.cpe .desativado table,.cp.cpe .desativado table td,.cp.cpe .dpt .dptt>a.desativado table,.cp.cpe .dpt .dptt>a.desativado table td{border:1px dotted #ccc}.cp.cpe a.nota-alteracao{color:#02baf2!important;font-size:.8rem}.cp.cpe .btn-sm{line-height:1rem}.cp.cpe .btn-outline-primary{background-color:#fff}.cp.cpe .btn-outline-primary:hover{background-color:#02baf2}.cp.cpe .dpt{display:block}.cp.cpe .dpt>.dpt-actions-fixed{position:absolute;right:-1em;top:-.8em;z-index:3;opacity:0}.cp.cpe .dpt>.dpt-actions-fixed .activate{display:none}.cp.cpe .dpt>.dpt-actions-fixed .deactivate{display:inline}.cp.cpe .dpt>.dpt-actions-fixed .btn-dpt-edit.btn-outline-primary{color:#333}.cp.cpe .dpt>.dpt-actions-fixed .btn-dpt-edit.btn-outline-primary:hover{color:#fff;background-color:#02baf2}.cp.cpe .dpt>.dpt-actions,.cp.cpe .dpt>.dpt-actions-bottom{display:none}.cp.cpe .dpt>.dpt-text{cursor:text;min-height:30px;border:1px solid transparent}.cp.cpe .dpt>.dpt-text.hover-fixed,.cp.cpe .dpt>.dpt-text:hover{background-color:rgba(0,0,0,.01);color:#2980b9;border:1px solid #eee;-webkit-transition:color .3s ease;transition:color .3s ease}.cp.cpe .dpt>.dpt-text.artigo{float:none}.cp.cpe .dpt>.dpt-text a.link-rotulo{color:#000}.cp.cpe .dpt:hover>.dpt-actions-fixed{opacity:1}.cp.cpe .dpt:hover>.dpt-actions-fixed:hover~.dpt-text{background-color:rgba(0,0,0,.01);color:#2980b9;border:1px solid #eee;-webkit-transition:color .3s ease;transition:color .3s ease}.cp.cpe .dpt .semtexto{font-weight:700;color:#9aaed6}.cp.cpe .dpt .semtexto:hover{color:#5f76a4}.cp.cpe .dpt-alts{margin:0;margin-bottom:1em;padding:0;background-color:transparent;min-height:100px;border:2px dashed #fff}.cp.cpe .dpt-alts:hover{border-color:#d9ddde}.cp.cpe .dpt-alts:empty{border-color:#ddd}.cp.cpe .dpt-alts.drag{width:100%!important;border-color:#d9ddde}.cp.cpe .dpt-alts.drag .dpt{-webkit-transition-duration:0s!important;transition-duration:0s!important}.cp.cpe .dpt-alts .dpt{width:100%!important;-webkit-box-shadow:0 -1px 0 #e5e5e5,0 0 2px rgba(0,0,0,.12),0 2px 4px rgba(0,0,0,.24);box-shadow:0 -1px 0 #e5e5e5,0 0 2px rgba(0,0,0,.12),0 2px 4px rgba(0,0,0,.24);padding:0;margin:0;background-color:#edf0f1;height:auto!important;min-height:2em;z-index:1}.cp.cpe .dpt-alts .dpt:not(:first-child){border-top:1px solid #fff}.cp.cpe .dpt-alts .dpt>.dpt-text{padding:.3em 1em;margin-top:0;margin-bottom:0}.cp.cpe .dpt-alts .dpt>.dpt-text a.link-rotulo{text-decoration:underline}.cp.cpe .dpt-alts .dpt-selected.dpt{margin:0 -.5em}.cp.cpe .dpt-selected.dpt{width:auto!important;margin:1em -.5em;border:1px solid #ddd!important;padding:0;background-color:#fafafa;border-radius:3px;z-index:4}.cp.cpe .dpt-selected.dpt>.dpt-text{border:1px solid transparent}.cp.cpe .dpt-selected.dpt>.dpt-text:hover{border:1px solid transparent;background-color:transparent}.cp.cpe .dpt-selected.dpt>.dpt-form{margin:0 1rem}.cp.cpe .dpt-selected.dpt>.dpt-actions,.cp.cpe .dpt-selected.dpt>.dpt-actions-bottom{display:table;background-color:#e5e5e5;padding:.8rem .6rem .2rem .6rem;margin-bottom:0;width:100%}.cp.cpe .dpt-selected.dpt>.dpt-actions-bottom>.btn-action,.cp.cpe .dpt-selected.dpt>.dpt-actions>.btn-action{display:table-cell;float:none}.cp.cpe .dpt-selected.dpt>.dpt-actions-bottom .btns-excluir .btn-danger,.cp.cpe .dpt-selected.dpt>.dpt-actions .btns-excluir .btn-danger{display:inline-block;opacity:.3;color:#fff}.cp.cpe .dpt-selected.dpt>.dpt-actions-bottom .btns-excluir .btn-danger:hover,.cp.cpe .dpt-selected.dpt>.dpt-actions .btns-excluir .btn-danger:hover{opacity:1}.cp.cpe .dpt-selected.dpt>.dpt-actions-bottom{margin:0;padding-bottom:.8rem}.cp.cpe .dpt-selected .dpt-block{border-top:1px solid #e5e5e5!important;opacity:.6;-webkit-transition:opacity .4s ease;transition:opacity .4s ease}.cp.cpe .dpt-selected .dpt-block:hover{opacity:1}.cp.cpe .dpt-selected .dpt-text{opacity:.7;margin:0;padding:.7em}.cp.cpe .dpt-selected .dpt-text:hover{opacity:1;background-color:#f5f5f5}.cp.cpe .dpt-selected .dpt-alts{margin:1em}.cp.cpe .dpt-selected .dpt-alts .dpt{-webkit-box-shadow:0 0 0;box-shadow:0 0 0}.cp.cpe .dpt-selected>.dpt-actions-fixed{opacity:1;top:-15px;right:.5em}.cp.cpe .dpt-selected>.dpt-actions-fixed .activate{display:inline}.cp.cpe .dpt-selected>.dpt-actions-fixed .deactivate{display:none}.cp.cpe .dpt-selected>.dpt-actions-fixed .btn-dpt-edit{padding:0 6px;line-height:1.2rem}.cp.cpe .dpt-selected>.dpt-actions-fixed .btn-dpt-edit.btn-outline-primary,.cp.cpe .dpt-selected>.dpt-actions-fixed .btn-dpt-edit.btn-outline-primary:hover{background-color:#fad46b;border:1px solid #444}.cp.cpe .dpt-selected .btns-tipos-editor{padding:0}.cp.cpe .dpt-selected .dropdown-menu.dropdown-menu-left{right:auto!important;left:0;padding:0}.cp.cpe .dpt-selected .dropdown-menu li{line-height:1}.cp.cpe .dpt-selected .dropdown-menu li a{display:block;line-height:1.5rem;padding:0 .5rem;white-space:nowrap}.cp.cpe .dpt-selected .dropdown-menu li a:hover{background-color:#f0f0f0}.cp.cpe .dpt-selected .dropdown-menu li:not(:last-child){border-bottom:1px solid #f0f0f0}.cp.cpe .dpt-selected .btn-group .radius-right{border-bottom-right-radius:.2rem!important;border-top-right-radius:.2rem!important}.cp.cpe .dpt-selected:hover>.dpt-actions-fixed{opacity:1}.cp.cpe1_old_apagar{margin-bottom:15em}.cp.cpe1_old_apagar .desativado,.cp.cpe1_old_apagar .desativado *,.cp.cpe1_old_apagar .dpt .dptt>a.desativado,.cp.cpe1_old_apagar .dpt .dptt>a.desativado *{text-decoration:line-through;color:#999!important}.cp.cpe1_old_apagar .desativado * table,.cp.cpe1_old_apagar .desativado * table td,.cp.cpe1_old_apagar .desativado table,.cp.cpe1_old_apagar .desativado table td,.cp.cpe1_old_apagar .dpt .dptt>a.desativado * table,.cp.cpe1_old_apagar .dpt .dptt>a.desativado * table td,.cp.cpe1_old_apagar .dpt .dptt>a.desativado table,.cp.cpe1_old_apagar .dpt .dptt>a.desativado table td{border:1px dotted #ccc}.cp.cpe1_old_apagar a{text-decoration:none;cursor:pointer}.cp.cpe1_old_apagar .dpt{position:relative;display:block}.cp.cpe1_old_apagar .dpt .semtexto{font-weight:700;color:#bfd1f6}.cp.cpe1_old_apagar .dpt .artigo{float:none}.cp.cpe1_old_apagar .dpt .caput{margin-top:0}.cp.cpe1_old_apagar .dpt-selected .csform .dpt>.actions_left,.cp.cpe1_old_apagar .dpt>.actions_right{color:#fff;right:0;position:absolute;opacity:0;-webkit-transition:all .4s ease-in-out;transition:all .4s ease-in-out;z-index:1000}.cp.cpe1_old_apagar .dpt-selected .csform .dpt>.actions_left a.btn-bloco,.cp.cpe1_old_apagar .dpt>.actions_right a.btn-bloco{background-color:#3498db;color:#fff!important;padding:8px 18px 6px;display:inline-block;line-height:1;float:right}.cp.cpe1_old_apagar .dpt-selected .csform .dpt>.actions_left a.btn-bloco:hover,.cp.cpe1_old_apagar .dpt>.actions_right a.btn-bloco:hover{opacity:1;background-image:-webkit-gradient(linear,left top,left bottom,from(#1c81c4),to(#0b6dad));background-image:linear-gradient(180deg,#1c81c4,#0b6dad)}.cp.cpe1_old_apagar .dpt-selected .csform .dpt:hover>.actions_left,.cp.cpe1_old_apagar .dpt:hover>.actions_right{opacity:1}.cp.cpe1_old_apagar .dpt .bloco{display:block;clear:both}.cp.cpe1_old_apagar .dpt .bloco :hover{color:#27ae60}.cp.cpe1_old_apagar .dpt .bloco .de{cursor:pointer}.cp.cpe1_old_apagar .dpt .articulacao{border-top:2px solid #e5e5e5;margin:2em 0}.cp.cpe1_old_apagar .dpt .bloco_alteracao{margin:1em 0;padding:0;background-color:transparent;min-height:100px;border:2px dashed #fff}.cp.cpe1_old_apagar .dpt .bloco_alteracao:hover{border-color:#d9ddde}.cp.cpe1_old_apagar .dpt .bloco_alteracao.drag{width:100%!important;border-color:#d9ddde}.cp.cpe1_old_apagar .dpt .bloco_alteracao.drag .dpt{-webkit-transition-duration:0s!important;transition-duration:0s!important}.cp.cpe1_old_apagar .dpt .bloco_alteracao .dpt{width:100%!important;-webkit-box-shadow:0 -1px 0 #e5e5e5,0 0 2px rgba(0,0,0,.12),0 2px 4px rgba(0,0,0,.24);box-shadow:0 -1px 0 #e5e5e5,0 0 2px rgba(0,0,0,.12),0 2px 4px rgba(0,0,0,.24);padding:.3em 1em;margin:0;background-color:#edf0f1;z-index:1}.cp.cpe1_old_apagar .dpt .bloco_alteracao .dpt:not(:first-child){border-top:1px solid #fff}.cp.cpe1_old_apagar .dpt .bloco_alteracao .dpt.ui-draggable div{cursor:pointer}.cp.cpe1_old_apagar .dpt .bloco_alteracao .dpt.dpt-comp-selected{-webkit-transition:all .3s ease;transition:all .3s ease;width:auto!important;margin:2em -3.7em;-webkit-box-shadow:0 0 6px rgba(0,0,0,.16),0 6px 12px rgba(0,0,0,.32);box-shadow:0 0 6px rgba(0,0,0,.16),0 6px 12px rgba(0,0,0,.32)}.cp.cpe1_old_apagar .dpt-selected{font-size:1em;border:0 solid #ccc;margin:1em -1.8em 1em -1.8em;padding:2.2em 2.2em 1.6em 2.2em;-webkit-box-shadow:-4px 15px 15px rgba(0,0,0,.1),0 6px 6px rgba(0,0,0,.23);box-shadow:-4px 15px 15px rgba(0,0,0,.1),0 6px 6px rgba(0,0,0,.23);background-image:-webkit-gradient(linear,left top,left bottom,from(#eaeaee),to(#ddd));background-image:linear-gradient(180deg,#eaeaee,#ddd)}.cp.cpe1_old_apagar .dpt-selected ul{list-style:none;margin:0;padding:0}.cp.cpe1_old_apagar .dpt-selected .semtexto{color:#999}.cp.cpe1_old_apagar .dpt-selected .bloco{opacity:.5}.cp.cpe1_old_apagar .dpt-selected .bloco:hover{opacity:1}.cp.cpe1_old_apagar .dpt-selected .bloco a:hover{background:transparent}.cp.cpe1_old_apagar .dpt-selected>.bloco{opacity:1;margin:1em}.cp.cpe1_old_apagar .dpt-selected .bloco_alteracao{margin:0;padding:1em;border:0 transparent;background-image:-webkit-gradient(linear,left top,left bottom,from(#eaeaee),to(#ddd));background-image:linear-gradient(180deg,#eaeaee,#ddd)}.cp.cpe1_old_apagar .dpt-selected .bloco_alteracao:hover{border-color:transparent}.cp.cpe1_old_apagar .dpt-selected .bloco_alteracao.drag{width:100%!important}.cp.cpe1_old_apagar .dpt-selected .bloco_alteracao.drag .dpt{-webkit-transition-duration:0s!important;transition-duration:0s!important}.cp.cpe1_old_apagar .dpt-selected .bloco_alteracao .dpt{width:auto!important;-webkit-transition:all .3s ease;transition:all .3s ease;background-color:#fff}.cp.cpe1_old_apagar .dpt-selected .bloco_alteracao .dpt:not(:first-child){border-top:0 solid #fff}.cp.cpe1_old_apagar .dpt-selected>.dpt{padding:0}.cp.cpe1_old_apagar .dpt-selected>.dpt:last-child{padding-bottom:1em}.cp.cpe1_old_apagar .dpt-selected .csform .dpt-selected>.actions_left a.btn-bloco,.cp.cpe1_old_apagar .dpt-selected>.actions_right a.btn-bloco{display:none}.cp.cpe1_old_apagar .dpt-selected .csform{display:block;clear:both;z-index:9;position:static}.cp.cpe1_old_apagar .dpt-selected .csform .btns-action{-webkit-animation:fadeIn 1s ease-in-out;-moz-animation:fadeIn 1s ease-in-out;-o-animation:fadeIn 1s ease-in-out;opacity:1;position:absolute;display:table;-webkit-transition:all .4s ease-in-out;transition:all .4s ease-in-out}.cp.cpe1_old_apagar .dpt-selected .csform .btns-action a{color:#16407c;display:block;background:transparent;vertical-align:middle;text-align:center;font-weight:400;text-shadow:0 0 10px rgba(0,0,0,.3);padding:.33em .4em}.cp.cpe1_old_apagar .dpt-selected .csform .btns-action>li{position:relative;display:table-cell;vertical-align:top}.cp.cpe1_old_apagar .dpt-selected .csform .btns-action>li:hover{background-color:hsla(0,0%,100%,.5)}.cp.cpe1_old_apagar .dpt-selected .csform .btns-action>li:hover>a{text-shadow:0 0 5px #777;color:#0a5}.cp.cpe1_old_apagar .dpt-selected .csform .label_status{position:absolute;bottom:0;right:0;color:#889;padding:.3em;font-size:80%;text-align:right;z-index:15;display:table}.cp.cpe1_old_apagar .dpt-selected .csform .label_status li{display:table-cell;padding:0 .5em}.cp.cpe1_old_apagar .dpt-selected .csform .actions_parents{z-index:11;top:0;left:0}.cp.cpe1_old_apagar .dpt-selected .csform .actions_parents a{padding:.62em}.cp.cpe1_old_apagar .dpt-selected .csform .actions_parents div,.cp.cpe1_old_apagar .dpt-selected .csform .actions_parents li{font-size:80%;display:table-cell;vertical-align:middle;border-right:1px solid #ccc}.cp.cpe1_old_apagar .dpt-selected .csform .actions_parents div{padding:0 .4em;font-stretch:condensed;font-variant:small-caps;font-weight:700;text-shadow:0 0 10px #fff}.cp.cpe1_old_apagar .dpt-selected .csform .actions_parents>li:hover a{color:#16407c;font-weight:400}.cp.cpe1_old_apagar .dpt-selected .csform .actions_bottom,.cp.cpe1_old_apagar .dpt-selected .csform .actions_top{top:0;right:0}.cp.cpe1_old_apagar .dpt-selected .csform .actions_bottom a,.cp.cpe1_old_apagar .dpt-selected .csform .actions_top a{padding-right:1em;padding-left:1em}.cp.cpe1_old_apagar .dpt-selected .csform .actions_bottom li,.cp.cpe1_old_apagar .dpt-selected .csform .actions_top li{display:table-cell;vertical-align:middle;border-left:1px solid #ccc}.cp.cpe1_old_apagar .dpt-selected .csform .actions_bottom{top:auto;left:0;bottom:0;display:inline-block;border-top:1px solid #ccc}.cp.cpe1_old_apagar .dpt-selected .csform .actions_bottom a{padding:0 .4em}.cp.cpe1_old_apagar .dpt-selected .csform .actions_bottom li{border:0;border-right:1px solid #ccc}.cp.cpe1_old_apagar .dpt-selected .csform .actions_left,.cp.cpe1_old_apagar .dpt-selected .csform .actions_right{top:2.2em;right:0;bottom:0;display:block}.cp.cpe1_old_apagar .dpt-selected .csform .actions_left li,.cp.cpe1_old_apagar .dpt-selected .csform .actions_right li{width:2.2em;display:block;border-bottom:1px solid #ccc}.cp.cpe1_old_apagar .dpt-selected .csform .actions_left li:first-child,.cp.cpe1_old_apagar .dpt-selected .csform .actions_right li:first-child{border-top:1px solid #ccc}.cp.cpe1_old_apagar .dpt-selected .csform .actions_left{right:auto;left:0}.cp.cpe1_old_apagar .dpt-selected .csform .actions_inserts{background:transparent;position:relative;z-index:19;display:table;width:100%}.cp.cpe1_old_apagar .dpt-selected .csform .actions_inserts>li{display:table-cell}.cp.cpe1_old_apagar .dpt-selected .csform .actions_inserts>li:hover>a{background-image:-webkit-gradient(linear,left top,left bottom,from(#1c81c4),to(#0b6dad));background-image:linear-gradient(180deg,#1c81c4,#0b6dad)}.cp.cpe1_old_apagar .dpt-selected .csform .actions_inserts>li>a{background-image:-webkit-gradient(linear,left top,left bottom,from(#3498db),to(#2980c9));background-image:linear-gradient(180deg,#3498db,#2980c9);border-right:1px solid #fff;padding:.2em;display:block;color:#fff;text-align:center;white-space:nowrap}.cp.cpe1_old_apagar .dpt-selected .csform .actions_inserts>li>a.btn-excluir,.cp.cpe1_old_apagar .dpt-selected .csform .actions_inserts>li>a.btn-salvar{text-align:left;background:#a70808;color:#c99;padding-left:1.7em;position:relative}.cp.cpe1_old_apagar .dpt-selected .csform .actions_inserts>li>a.btn-excluir:hover,.cp.cpe1_old_apagar .dpt-selected .csform .actions_inserts>li>a.btn-salvar:hover{background-color:#c70808;color:#ecc}.cp.cpe1_old_apagar .dpt-selected .csform .actions_inserts>li>a.btn-excluir:before,.cp.cpe1_old_apagar .dpt-selected .csform .actions_inserts>li>a.btn-salvar:before{z-index:20;position:absolute;background:url(/static/img/icon_delete_white.png) no-repeat 50% 50%;content:"";top:0;left:0;display:block;color:#000;margin-left:.4em;height:100%;width:2em}.cp.cpe1_old_apagar .dpt-selected .csform .actions_inserts>li>a.btn-salvar{background:#1f8b4d;color:#fff}.cp.cpe1_old_apagar .dpt-selected .csform .actions_inserts>li>a.btn-salvar:hover{background:#2d9c5c;color:#fff}.cp.cpe1_old_apagar .dpt-selected .csform .actions_inserts>li>a.btn-salvar:before{background:url(/static/img/icon_save_white.png) no-repeat 50% 50%}.cp.cpe1_old_apagar .dpt-selected .csform .actions_inserts>li>a span{padding:0 .7em}.cp.cpe1_old_apagar .dpt-selected .csform .actions_inserts>li:last-child>a{border-right:0 solid #fff}.cp.cpe1_old_apagar .dpt-selected .csform .actions_inserts>li>ul li:nth-child(2n) a{background:#3385ca}.cp.cpe1_old_apagar .dpt-selected .csform .actions_inserts>li>ul li a{border-right:1px solid #fff;display:block;color:#fff;background:#2980c9;font-size:80%;padding:.23em 1em}.cp.cpe1_old_apagar .dpt-selected .csform .actions_inserts>li>ul li a:hover{background:#0a5ea4}.cp.cpe1_old_apagar .dpt-selected .csform .actions_inserts>li.add_prior{table-layout:fixed}.cp.cpe1_old_apagar .dpt-selected .csform .actions_inserts>li.add_prior>ul{-webkit-transform:translateY(30px);transform:translateY(30px);-webkit-transition:opacity .1s linear,clip 0s .3s,-webkit-transform .1s linear;transition:opacity .1s linear,clip 0s .3s,-webkit-transform .1s linear;transition:transform .1s linear,opacity .1s linear,clip 0s .3s;transition:transform .1s linear,opacity .1s linear,clip 0s .3s,-webkit-transform .1s linear;clip:rect(0,0,0,0);opacity:0;position:absolute;margin-left:.5em;-webkit-box-shadow:0 6px 18px rgba(0,0,0,.19),0 2px 6px rgba(0,0,0,.23);box-shadow:0 6px 18px rgba(0,0,0,.19),0 2px 6px rgba(0,0,0,.23);-webkit-transition-delay:.4s;transition-delay:.4s}.cp.cpe1_old_apagar .dpt-selected .csform .actions_inserts>li.add_prior>ul li a{border-right:0!important}.cp.cpe1_old_apagar .dpt-selected .csform .actions_inserts>li.add_prior>ul li:first-child:before{border-width:.375rem;border-style:inset inset solid;content:"";display:block;height:0;width:0;border-color:transparent transparent #3385ca;position:absolute;top:-.71rem;left:.9375rem}.cp.cpe1_old_apagar .dpt-selected .csform .actions_inserts>li.add_prior>ul li:first-child:hover:before{border-color:transparent transparent #0a5ea4}.cp.cpe1_old_apagar .dpt-selected .csform .actions_inserts>li.add_prior>ul:after{content:"";position:absolute;z-index:-1;left:0;top:-25px;height:25px;width:100%;-webkit-transition:all .3s cubic-bezier(.55,0,.1,1);transition:all .3s cubic-bezier(.55,0,.1,1)}.cp.cpe1_old_apagar .dpt-selected .csform .actions_inserts>li.add_prior:hover>ul{-webkit-transform:translateY(7px);transform:translateY(7px);-webkit-transition:opacity .4s linear,clip 0s .2s,-webkit-transform .4s linear;transition:opacity .4s linear,clip 0s .2s,-webkit-transform .4s linear;transition:transform .4s linear,opacity .4s linear,clip 0s .2s;transition:transform .4s linear,opacity .4s linear,clip 0s .2s,-webkit-transform .4s linear;opacity:1;clip:rect(-100px,2000px,2000px,-100px)}.cp.cpe1_old_apagar .dpt-selected .csform .actions_inserts>li.menu_excluir{display:block;position:static}.cp.cpe1_old_apagar .dpt-selected .csform .actions_inserts>li.menu_excluir>ul{-webkit-transform:translateY(30px);transform:translateY(30px);-webkit-transition:opacity .1s linear,clip 0s .3s,-webkit-transform .1s linear;transition:opacity .1s linear,clip 0s .3s,-webkit-transform .1s linear;transition:transform .1s linear,opacity .1s linear,clip 0s .3s;transition:transform .1s linear,opacity .1s linear,clip 0s .3s,-webkit-transform .1s linear;clip:rect(0,0,0,0);opacity:0;position:absolute;margin-left:.5em;-webkit-box-shadow:0 6px 18px rgba(0,0,0,.19),0 2px 6px rgba(0,0,0,.23);box-shadow:0 6px 18px rgba(0,0,0,.19),0 2px 6px rgba(0,0,0,.23);-webkit-transition-delay:.4s;transition-delay:.4s}.cp.cpe1_old_apagar .dpt-selected .csform .actions_inserts>li.menu_excluir>ul li a{border-right:0!important}.cp.cpe1_old_apagar .dpt-selected .csform .actions_inserts>li.menu_excluir>ul li:first-child:before{border-width:.375rem;border-style:inset inset solid;content:"";display:block;height:0;width:0;border-color:transparent transparent #3385ca;position:absolute;top:-.71rem;left:.9375rem}.cp.cpe1_old_apagar .dpt-selected .csform .actions_inserts>li.menu_excluir>ul li:first-child:hover:before{border-color:transparent transparent #0a5ea4}.cp.cpe1_old_apagar .dpt-selected .csform .actions_inserts>li.menu_excluir>ul:after{content:"";position:absolute;z-index:-1;left:0;top:-25px;height:25px;width:100%;-webkit-transition:all .3s cubic-bezier(.55,0,.1,1);transition:all .3s cubic-bezier(.55,0,.1,1)}.cp.cpe1_old_apagar .dpt-selected .csform .actions_inserts>li.menu_excluir:hover>ul{-webkit-transform:translateY(7px);transform:translateY(7px);-webkit-transition:opacity .4s linear,clip 0s .2s,-webkit-transform .4s linear;transition:opacity .4s linear,clip 0s .2s,-webkit-transform .4s linear;transition:transform .4s linear,opacity .4s linear,clip 0s .2s;transition:transform .4s linear,opacity .4s linear,clip 0s .2s,-webkit-transform .4s linear;opacity:1;clip:rect(-100px,2000px,2000px,-100px)}.cp.cpe1_old_apagar .dpt-selected .csform .actions_inserts>li.menu_excluir>ul{right:.5em}.cp.cpe1_old_apagar .dpt-selected .csform .actions_inserts>li.menu_excluir>ul li a{background-color:#a70808}.cp.cpe1_old_apagar .dpt-selected .csform .actions_inserts>li.menu_excluir>ul li a:hover{background:#c70808}.cp.cpe1_old_apagar .dpt-selected .csform .actions_inserts>li.menu_excluir>ul li:first-child:before{border-color:transparent transparent #a70808;right:10%;left:auto}.cp.cpe1_old_apagar .dpt-selected .csform .actions_inserts>li.menu_excluir>ul li:first-child:hover:before{border-color:transparent transparent #c70808}.cp.cpe1_old_apagar .dpt-selected .csform .menu_flutuante,.cp.cpe1_old_apagar .dpt-selected .csform .menu_flutuante_fixo{z-index:2000}.cp.cpe1_old_apagar .dpt-selected .csform .menu_flutuante>li>ul,.cp.cpe1_old_apagar .dpt-selected .csform .menu_flutuante_fixo>li>ul{-webkit-transform:translateY(30px);transform:translateY(30px);-webkit-transition:opacity .1s linear,clip 0s .3s,-webkit-transform .1s linear;transition:opacity .1s linear,clip 0s .3s,-webkit-transform .1s linear;transition:transform .1s linear,opacity .1s linear,clip 0s .3s;transition:transform .1s linear,opacity .1s linear,clip 0s .3s,-webkit-transform .1s linear;clip:rect(0,0,0,0);opacity:0;position:absolute;margin-left:.5em;-webkit-box-shadow:0 6px 18px rgba(0,0,0,.19),0 2px 6px rgba(0,0,0,.23);box-shadow:0 6px 18px rgba(0,0,0,.19),0 2px 6px rgba(0,0,0,.23);-webkit-transition-delay:.4s;transition-delay:.4s}.cp.cpe1_old_apagar .dpt-selected .csform .menu_flutuante>li>ul li a,.cp.cpe1_old_apagar .dpt-selected .csform .menu_flutuante_fixo>li>ul li a{border-right:0!important}.cp.cpe1_old_apagar .dpt-selected .csform .menu_flutuante>li>ul li:first-child:before,.cp.cpe1_old_apagar .dpt-selected .csform .menu_flutuante_fixo>li>ul li:first-child:before{border-width:.375rem;border-style:inset inset solid;content:"";display:block;height:0;width:0;border-color:transparent transparent #3385ca;position:absolute;top:-.71rem;left:.9375rem}.cp.cpe1_old_apagar .dpt-selected .csform .menu_flutuante>li>ul li:first-child:hover:before,.cp.cpe1_old_apagar .dpt-selected .csform .menu_flutuante_fixo>li>ul li:first-child:hover:before{border-color:transparent transparent #0a5ea4}.cp.cpe1_old_apagar .dpt-selected .csform .menu_flutuante>li>ul:after,.cp.cpe1_old_apagar .dpt-selected .csform .menu_flutuante_fixo>li>ul:after{content:"";position:absolute;z-index:-1;left:0;top:-25px;height:25px;width:100%;-webkit-transition:all .3s cubic-bezier(.55,0,.1,1);transition:all .3s cubic-bezier(.55,0,.1,1)}.cp.cpe1_old_apagar .dpt-selected .csform .menu_flutuante>li:hover>ul,.cp.cpe1_old_apagar .dpt-selected .csform .menu_flutuante_fixo>li:hover>ul{-webkit-transform:translateY(7px);transform:translateY(7px);-webkit-transition:opacity .4s linear,clip 0s .2s,-webkit-transform .4s linear;transition:opacity .4s linear,clip 0s .2s,-webkit-transform .4s linear;transition:transform .4s linear,opacity .4s linear,clip 0s .2s;transition:transform .4s linear,opacity .4s linear,clip 0s .2s,-webkit-transform .4s linear;opacity:1;clip:rect(-100px,2000px,2000px,-100px)}.cp.cpe1_old_apagar .dpt-selected .csform .menu_flutuante>li.menu_excluir>ul li:first-child:before,.cp.cpe1_old_apagar .dpt-selected .csform .menu_flutuante_fixo>li.menu_excluir>ul li:first-child:before{right:auto;left:.9375rem}.cp.cpe1_old_apagar .dpt-selected .csform textarea{margin:0;resize:vertical;min-height:12.6em;border:0;font-size:120%;width:100%}.cp.cpe1_old_apagar .dpt-selected .csform textarea:focus{background:#fff}.cp.cpe1_old_apagar .dpt-selected .csform textarea::-webkit-input-placeholder{color:#c70808;opacity:.6;font-size:80%}.cp.cpe1_old_apagar .dpt-selected .csform textarea:-moz-placeholder,.cp.cpe1_old_apagar .dpt-selected .csform textarea::-moz-placeholder{color:#c70808}.cp.cpe1_old_apagar .dpt-selected .csform textarea:-ms-input-placeholder{color:#c70808;opacity:.6}.cp.cpe1_old_apagar .selected{background-color:hsla(0,0%,100%,.5)}.cp.cpe1_old_apagar .selected a:hover{color:#16407c!important;font-weight:400!important}.lista-dispositivo,.result-busca-dispositivo{padding:0 0 1em;min-height:3em}.lista-dispositivo ul,.result-busca-dispositivo ul{list-style:none;margin:0;padding:1em 0 0;-webkit-transition:all 2s linear;transition:all 2s linear;clear:both}.lista-dispositivo ul li,.result-busca-dispositivo ul li{display:table;border-collapse:separate;border-bottom:1px solid #fff;width:100%}.lista-dispositivo ul li.ta_title,.result-busca-dispositivo ul li.ta_title{background-color:rgba(0,0,0,.15);border-radius:4px 4px 0 0;width:100%}.lista-dispositivo ul li:last-child .itemlabel,.result-busca-dispositivo ul li:last-child .itemlabel{border-radius:0 0 4px 0;margin:0}.lista-dispositivo ul li:last-child .iteminput,.result-busca-dispositivo ul li:last-child .iteminput{border-radius:0 0 0 4px}.lista-dispositivo ul li .iteminput,.result-busca-dispositivo ul li .iteminput{background-color:rgba(0,0,0,.1);border-right:1px solid #fff;display:table-cell;padding:.5em;vertical-align:middle;text-align:center}.lista-dispositivo ul li .iteminput input,.result-busca-dispositivo ul li .iteminput input{margin:0}.lista-dispositivo ul li .itemlabel,.result-busca-dispositivo ul li .itemlabel{background-color:rgba(0,0,0,.1);display:table-cell;padding:.5em;vertical-align:middle;width:100%}.lista-dispositivo ul li .itemlabel .artigo,.result-busca-dispositivo ul li .itemlabel .artigo{float:none}.lista-dispositivo .nomenclatura_heranca,.result-busca-dispositivo .nomenclatura_heranca{font-size:90%;color:#057dba;display:inline}.lista-dispositivo.controls-radio-checkbox{border:0}.label_vigencia{border-top:1px solid #fff;display:inline-block;color:#555}.label_vigencia span{color:grey}.cp-nav-parents .dropdown-menu{left:0;right:auto;padding:0}.cp-nav-parents .dropdown-menu:before{content:"";position:absolute;top:-11px;width:100%;height:11px}.cp-nav-parents:hover .dropdown-menu{display:block}.cp-nav-parents a.active small{color:#fff!important}.table-notificacoes tbody tr td{border-top:1px solid #fff;padding:5px;vertical-align:middle}.table-notificacoes tbody tr td ul{margin:0}.table-notificacoes tbody tr td ul li:hover{background-color:rgba(0,0,0,.1)}.btn-modal-open{float:right}.modal .modal-content .alert:only-child{margin:0}.class_color_container{background:#ddd!important}.clear{clear:both}.mce-tinymce.mce-container{border:1px solid #ccc!important;margin-right:2px}.mce-btn button:hover{background-color:rgba(0,0,0,.1)!important;text-shadow:0 0 5px #fff;-webkit-box-shadow:0 0 5px #777;box-shadow:0 0 5px #777}.mce-menu{background:#eee!important}.displaynone{display:none!important}@media only screen and (max-width:800px){.cp .fixed{z-index:98;position:relative}.cp.cpe1 .dpt-selected{margin:1em 0}.cp.cpe1 .dpt-selected .csform .actions_parents,.cp.cpe1 .dpt-selected .csform .label_status{font-size:80%;position:static!important;display:block!important;padding:0;height:auto!important;left:0;right:auto;text-align:left}.cp.cpe1 .dpt-selected .csform .actions_parents div,.cp.cpe1 .dpt-selected .csform .actions_parents li,.cp.cpe1 .dpt-selected .csform .label_status div,.cp.cpe1 .dpt-selected .csform .label_status li{display:inline-block!important}.cp.cpe1 .dpt-selected .csform .actions_inserts>li>ul{-webkit-transform:translateY(30px);transform:translateY(30px);-webkit-transition:opacity .1s linear,clip 0s .3s,-webkit-transform .1s linear;transition:opacity .1s linear,clip 0s .3s,-webkit-transform .1s linear;transition:transform .1s linear,opacity .1s linear,clip 0s .3s;transition:transform .1s linear,opacity .1s linear,clip 0s .3s,-webkit-transform .1s linear;clip:rect(0,0,0,0);opacity:0;position:absolute;margin-left:.5em;-webkit-box-shadow:0 6px 18px rgba(0,0,0,.19),0 2px 6px rgba(0,0,0,.23);box-shadow:0 6px 18px rgba(0,0,0,.19),0 2px 6px rgba(0,0,0,.23);-webkit-transition-delay:.4s;transition-delay:.4s}.cp.cpe1 .dpt-selected .csform .actions_inserts>li>ul li a{border-right:0!important}.cp.cpe1 .dpt-selected .csform .actions_inserts>li>ul li:first-child:before{border-width:.375rem;border-style:inset inset solid;content:"";display:block;height:0;width:0;border-color:transparent transparent #3385ca;position:absolute;top:-.71rem;left:.9375rem}.cp.cpe1 .dpt-selected .csform .actions_inserts>li>ul li:first-child:hover:before{border-color:transparent transparent #0a5ea4}.cp.cpe1 .dpt-selected .csform .actions_inserts>li>ul:after{content:"";position:absolute;z-index:-1;left:0;top:-25px;height:25px;width:100%;-webkit-transition:all .3s cubic-bezier(.55,0,.1,1);transition:all .3s cubic-bezier(.55,0,.1,1)}.cp.cpe1 .dpt-selected .csform .actions_inserts>li:hover>ul{-webkit-transform:translateY(7px);transform:translateY(7px);-webkit-transition:opacity .4s linear,clip 0s .2s,-webkit-transform .4s linear;transition:opacity .4s linear,clip 0s .2s,-webkit-transform .4s linear;transition:transform .4s linear,opacity .4s linear,clip 0s .2s;transition:transform .4s linear,opacity .4s linear,clip 0s .2s,-webkit-transform .4s linear;opacity:1;clip:rect(-100px,2000px,2000px,-100px)}.cp.cpe1 .dpt-selected .csform .actions_inserts>li>a span{display:none}.cp.cpe1 .cp.cpe1_old_apagar .dpt-selected .csform .actions_inserts>li>a.btn-salvar,.cp.cpe1 .dpt-selected .csform .actions_inserts>li>a.btn-excluir,.cp.cpe1 .dpt-selected .csform .actions_inserts>li>a.btn-salvar,.cp.cpe1_old_apagar .cp.cpe1 .dpt-selected .csform .actions_inserts>li>a.btn-salvar{padding-left:0;min-width:1em}.cp.cpe1 .cp.cpe1_old_apagar .dpt-selected .csform .actions_inserts>li>a.btn-salvar:before,.cp.cpe1 .dpt-selected .csform .actions_inserts>li>a.btn-excluir:before,.cp.cpe1 .dpt-selected .csform .actions_inserts>li>a.btn-salvar:before,.cp.cpe1_old_apagar .cp.cpe1 .dpt-selected .csform .actions_inserts>li>a.btn-salvar:before{width:100%;margin:0}.cp.cpe1 .dpt-selected .csform .actions_inserts>li.add_in,.cp.cpe1 .dpt-selected .csform .actions_inserts>li.add_next,.cp.cpe1 .dpt-selected .csform .actions_inserts>li.add_prior{position:static}.cp.cpe1 .dpt-selected .csform .actions_inserts>li.add_in>ul{left:1em!important;right:1em!important;margin-left:0}.cp.cpe1 .dpt-selected .csform .actions_inserts>li.add_in>ul li:first-child:before{left:37%}.cp.cpe1 .dpt-selected .csform .actions_inserts>li.add_next>ul{left:0!important;right:1em!important}.cp.cpe1 .dpt-selected .csform .actions_inserts>li.add_prior>ul{left:1em!important;right:0!important;margin-left:0;margin-right:.5em}.cp.cpe1 .dpt-selected .csform .actions_inserts>li.add_prior>ul li:first-child:before{right:42%;left:auto}.cp.cpe1 .dpt-selected .csform .actions_inserts>li.menu_excluir>ul{left:10%!important;right:0!important;margin-left:0;margin-right:.5em}.cp.cpe1 .dpt-selected .csform .actions_inserts>li.menu_excluir>ul li:first-child:before{right:0;left:auto}}@media print{#btn_font_mais,#btn_font_menos,.button,.cp .vigencias,.dne,.menu-icon,.tipo-vigencias,.toggle-topbar{display:none}.container{width:100%}} \ No newline at end of file diff --git a/sapl/static/css/global.d2e0a784.css b/sapl/static/css/global.d2e0a784.css deleted file mode 100644 index d6ae71228..000000000 --- a/sapl/static/css/global.d2e0a784.css +++ /dev/null @@ -1 +0,0 @@ -*,:after,:before{-webkit-box-sizing:border-box;box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:rgba(0,0,0,0)}article,aside,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:left;background-color:#fff}[tabindex="-1"]:focus{outline:0!important}hr{-webkit-box-sizing:content-box;box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[data-original-title],abbr[title]{text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;border-bottom:0;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{font-style:normal;line-height:inherit}address,dl,ol,ul{margin-bottom:1rem}dl,ol,ul{margin-top:0}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#02baf2;text-decoration:none;background-color:transparent}a:hover{color:#0180a6;text-decoration:underline}a:not([href]):not([tabindex]),a:not([href]):not([tabindex]):focus,a:not([href]):not([tabindex]):hover{color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus{outline:0}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:1em}pre{margin-top:0;margin-bottom:1rem;overflow:auto}figure{margin:0 0 1rem}img{border-style:none}img,svg{vertical-align:middle}svg{overflow:hidden}table{border-collapse:collapse}caption{padding-top:.75rem;padding-bottom:.75rem;color:#6c757d;text-align:left;caption-side:bottom}th{text-align:inherit}label{display:inline-block;margin-bottom:.5rem}button{border-radius:0}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{padding:0;border-style:none}input[type=checkbox],input[type=radio]{-webkit-box-sizing:border-box;box-sizing:border-box;padding:0}input[type=date],input[type=datetime-local],input[type=month],input[type=time]{-webkit-appearance:listbox}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;max-width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit;color:inherit;white-space:normal}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item;cursor:pointer}template{display:none}[hidden]{display:none!important}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{margin-bottom:.5rem;font-family:inherit;font-weight:500;line-height:1.2;color:inherit}.h1,h1{font-size:2.5rem}.h2,h2{font-size:2rem}.h3,h3{font-size:1.75rem}.h4,h4{font-size:1.5rem}.h5,h5{font-size:1.25rem}.h6,h6{font-size:1rem}.lead{font-size:1.25rem;font-weight:300}.display-1{font-size:6rem}.display-1,.display-2{font-weight:300;line-height:1.2}.display-2{font-size:5.5rem}.display-3{font-size:4.5rem}.display-3,.display-4{font-weight:300;line-height:1.2}.display-4{font-size:3.5rem}hr{margin-top:1rem;margin-bottom:1rem;border:0;border-top:1px solid rgba(0,0,0,.1)}.small,small{font-size:80%;font-weight:400}.mark,mark{padding:.2em;background-color:#fcf8e3}.list-inline,.list-unstyled{padding-left:0;list-style:none}.list-inline-item{display:inline-block}.list-inline-item:not(:last-child){margin-right:.5rem}.initialism{font-size:90%;text-transform:uppercase}.blockquote{margin-bottom:1rem;font-size:1.25rem}.blockquote-footer{display:block;font-size:80%;color:#6c757d}.blockquote-footer:before{content:"\2014\00A0"}.img-fluid,.img-thumbnail{max-width:100%;height:auto}.img-thumbnail{padding:.25rem;background-color:#fff;border:1px solid #dee2e6;border-radius:.25rem}.figure{display:inline-block}.figure-img{margin-bottom:.5rem;line-height:1}.figure-caption{font-size:90%;color:#6c757d}code{font-size:87.5%;color:#e83e8c;word-break:break-word}a>code{color:inherit}kbd{padding:.2rem .4rem;font-size:87.5%;color:#fff;background-color:#212529;border-radius:.2rem}kbd kbd{padding:0;font-size:100%;font-weight:700}pre{display:block;font-size:87.5%;color:#212529}pre code{font-size:inherit;color:inherit;word-break:normal}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:576px){.container{max-width:540px}}@media (min-width:768px){.container{max-width:720px}}@media (min-width:992px){.container{max-width:960px}}@media (min-width:1200px){.container{max-width:1140px}}.container-fluid{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-15px;margin-left:-15px}.no-gutters{margin-right:0;margin-left:0}.no-gutters>.col,.no-gutters>[class*=col-]{padding-right:0;padding-left:0}.col,.col-1,.col-2,.col-3,.col-4,.col-5,.col-6,.col-7,.col-8,.col-9,.col-10,.col-11,.col-12,.col-auto,.col-lg,.col-lg-1,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-auto,.col-md,.col-md-1,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-10,.col-md-11,.col-md-12,.col-md-auto,.col-sm,.col-sm-1,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-auto,.col-xl,.col-xl-1,.col-xl-2,.col-xl-3,.col-xl-4,.col-xl-5,.col-xl-6,.col-xl-7,.col-xl-8,.col-xl-9,.col-xl-10,.col-xl-11,.col-xl-12,.col-xl-auto{position:relative;width:100%;padding-right:15px;padding-left:15px}.col{-ms-flex-preferred-size:0;flex-basis:0;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-1,.col-auto{-webkit-box-flex:0}.col-1{-ms-flex:0 0 8.33333%;flex:0 0 8.33333%;max-width:8.33333%}.col-2{-ms-flex:0 0 16.66667%;flex:0 0 16.66667%;max-width:16.66667%}.col-2,.col-3{-webkit-box-flex:0}.col-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-4{-ms-flex:0 0 33.33333%;flex:0 0 33.33333%;max-width:33.33333%}.col-4,.col-5{-webkit-box-flex:0}.col-5{-ms-flex:0 0 41.66667%;flex:0 0 41.66667%;max-width:41.66667%}.col-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-6,.col-7{-webkit-box-flex:0}.col-7{-ms-flex:0 0 58.33333%;flex:0 0 58.33333%;max-width:58.33333%}.col-8{-ms-flex:0 0 66.66667%;flex:0 0 66.66667%;max-width:66.66667%}.col-8,.col-9{-webkit-box-flex:0}.col-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-10{-ms-flex:0 0 83.33333%;flex:0 0 83.33333%;max-width:83.33333%}.col-10,.col-11{-webkit-box-flex:0}.col-11{-ms-flex:0 0 91.66667%;flex:0 0 91.66667%;max-width:91.66667%}.col-12{-webkit-box-flex:0;-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-first{-webkit-box-ordinal-group:0;-ms-flex-order:-1;order:-1}.order-last{-webkit-box-ordinal-group:14;-ms-flex-order:13;order:13}.order-0{-webkit-box-ordinal-group:1;-ms-flex-order:0;order:0}.order-1{-webkit-box-ordinal-group:2;-ms-flex-order:1;order:1}.order-2{-webkit-box-ordinal-group:3;-ms-flex-order:2;order:2}.order-3{-webkit-box-ordinal-group:4;-ms-flex-order:3;order:3}.order-4{-webkit-box-ordinal-group:5;-ms-flex-order:4;order:4}.order-5{-webkit-box-ordinal-group:6;-ms-flex-order:5;order:5}.order-6{-webkit-box-ordinal-group:7;-ms-flex-order:6;order:6}.order-7{-webkit-box-ordinal-group:8;-ms-flex-order:7;order:7}.order-8{-webkit-box-ordinal-group:9;-ms-flex-order:8;order:8}.order-9{-webkit-box-ordinal-group:10;-ms-flex-order:9;order:9}.order-10{-webkit-box-ordinal-group:11;-ms-flex-order:10;order:10}.order-11{-webkit-box-ordinal-group:12;-ms-flex-order:11;order:11}.order-12{-webkit-box-ordinal-group:13;-ms-flex-order:12;order:12}.offset-1{margin-left:8.33333%}.offset-2{margin-left:16.66667%}.offset-3{margin-left:25%}.offset-4{margin-left:33.33333%}.offset-5{margin-left:41.66667%}.offset-6{margin-left:50%}.offset-7{margin-left:58.33333%}.offset-8{margin-left:66.66667%}.offset-9{margin-left:75%}.offset-10{margin-left:83.33333%}.offset-11{margin-left:91.66667%}@media (min-width:576px){.col-sm{-ms-flex-preferred-size:0;flex-basis:0;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-sm-auto{-webkit-box-flex:0;-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-sm-1{-webkit-box-flex:0;-ms-flex:0 0 8.33333%;flex:0 0 8.33333%;max-width:8.33333%}.col-sm-2{-webkit-box-flex:0;-ms-flex:0 0 16.66667%;flex:0 0 16.66667%;max-width:16.66667%}.col-sm-3{-webkit-box-flex:0;-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-sm-4{-webkit-box-flex:0;-ms-flex:0 0 33.33333%;flex:0 0 33.33333%;max-width:33.33333%}.col-sm-5{-webkit-box-flex:0;-ms-flex:0 0 41.66667%;flex:0 0 41.66667%;max-width:41.66667%}.col-sm-6{-webkit-box-flex:0;-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-sm-7{-webkit-box-flex:0;-ms-flex:0 0 58.33333%;flex:0 0 58.33333%;max-width:58.33333%}.col-sm-8{-webkit-box-flex:0;-ms-flex:0 0 66.66667%;flex:0 0 66.66667%;max-width:66.66667%}.col-sm-9{-webkit-box-flex:0;-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-sm-10{-webkit-box-flex:0;-ms-flex:0 0 83.33333%;flex:0 0 83.33333%;max-width:83.33333%}.col-sm-11{-webkit-box-flex:0;-ms-flex:0 0 91.66667%;flex:0 0 91.66667%;max-width:91.66667%}.col-sm-12{-webkit-box-flex:0;-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-sm-first{-webkit-box-ordinal-group:0;-ms-flex-order:-1;order:-1}.order-sm-last{-webkit-box-ordinal-group:14;-ms-flex-order:13;order:13}.order-sm-0{-webkit-box-ordinal-group:1;-ms-flex-order:0;order:0}.order-sm-1{-webkit-box-ordinal-group:2;-ms-flex-order:1;order:1}.order-sm-2{-webkit-box-ordinal-group:3;-ms-flex-order:2;order:2}.order-sm-3{-webkit-box-ordinal-group:4;-ms-flex-order:3;order:3}.order-sm-4{-webkit-box-ordinal-group:5;-ms-flex-order:4;order:4}.order-sm-5{-webkit-box-ordinal-group:6;-ms-flex-order:5;order:5}.order-sm-6{-webkit-box-ordinal-group:7;-ms-flex-order:6;order:6}.order-sm-7{-webkit-box-ordinal-group:8;-ms-flex-order:7;order:7}.order-sm-8{-webkit-box-ordinal-group:9;-ms-flex-order:8;order:8}.order-sm-9{-webkit-box-ordinal-group:10;-ms-flex-order:9;order:9}.order-sm-10{-webkit-box-ordinal-group:11;-ms-flex-order:10;order:10}.order-sm-11{-webkit-box-ordinal-group:12;-ms-flex-order:11;order:11}.order-sm-12{-webkit-box-ordinal-group:13;-ms-flex-order:12;order:12}.offset-sm-0{margin-left:0}.offset-sm-1{margin-left:8.33333%}.offset-sm-2{margin-left:16.66667%}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:33.33333%}.offset-sm-5{margin-left:41.66667%}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:58.33333%}.offset-sm-8{margin-left:66.66667%}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:83.33333%}.offset-sm-11{margin-left:91.66667%}}@media (min-width:768px){.col-md{-ms-flex-preferred-size:0;flex-basis:0;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-md-auto{-webkit-box-flex:0;-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-md-1{-webkit-box-flex:0;-ms-flex:0 0 8.33333%;flex:0 0 8.33333%;max-width:8.33333%}.col-md-2{-webkit-box-flex:0;-ms-flex:0 0 16.66667%;flex:0 0 16.66667%;max-width:16.66667%}.col-md-3{-webkit-box-flex:0;-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-md-4{-webkit-box-flex:0;-ms-flex:0 0 33.33333%;flex:0 0 33.33333%;max-width:33.33333%}.col-md-5{-webkit-box-flex:0;-ms-flex:0 0 41.66667%;flex:0 0 41.66667%;max-width:41.66667%}.col-md-6{-webkit-box-flex:0;-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-md-7{-webkit-box-flex:0;-ms-flex:0 0 58.33333%;flex:0 0 58.33333%;max-width:58.33333%}.col-md-8{-webkit-box-flex:0;-ms-flex:0 0 66.66667%;flex:0 0 66.66667%;max-width:66.66667%}.col-md-9{-webkit-box-flex:0;-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-md-10{-webkit-box-flex:0;-ms-flex:0 0 83.33333%;flex:0 0 83.33333%;max-width:83.33333%}.col-md-11{-webkit-box-flex:0;-ms-flex:0 0 91.66667%;flex:0 0 91.66667%;max-width:91.66667%}.col-md-12{-webkit-box-flex:0;-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-md-first{-webkit-box-ordinal-group:0;-ms-flex-order:-1;order:-1}.order-md-last{-webkit-box-ordinal-group:14;-ms-flex-order:13;order:13}.order-md-0{-webkit-box-ordinal-group:1;-ms-flex-order:0;order:0}.order-md-1{-webkit-box-ordinal-group:2;-ms-flex-order:1;order:1}.order-md-2{-webkit-box-ordinal-group:3;-ms-flex-order:2;order:2}.order-md-3{-webkit-box-ordinal-group:4;-ms-flex-order:3;order:3}.order-md-4{-webkit-box-ordinal-group:5;-ms-flex-order:4;order:4}.order-md-5{-webkit-box-ordinal-group:6;-ms-flex-order:5;order:5}.order-md-6{-webkit-box-ordinal-group:7;-ms-flex-order:6;order:6}.order-md-7{-webkit-box-ordinal-group:8;-ms-flex-order:7;order:7}.order-md-8{-webkit-box-ordinal-group:9;-ms-flex-order:8;order:8}.order-md-9{-webkit-box-ordinal-group:10;-ms-flex-order:9;order:9}.order-md-10{-webkit-box-ordinal-group:11;-ms-flex-order:10;order:10}.order-md-11{-webkit-box-ordinal-group:12;-ms-flex-order:11;order:11}.order-md-12{-webkit-box-ordinal-group:13;-ms-flex-order:12;order:12}.offset-md-0{margin-left:0}.offset-md-1{margin-left:8.33333%}.offset-md-2{margin-left:16.66667%}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:33.33333%}.offset-md-5{margin-left:41.66667%}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:58.33333%}.offset-md-8{margin-left:66.66667%}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:83.33333%}.offset-md-11{margin-left:91.66667%}}@media (min-width:992px){.col-lg{-ms-flex-preferred-size:0;flex-basis:0;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-lg-auto{-webkit-box-flex:0;-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-lg-1{-webkit-box-flex:0;-ms-flex:0 0 8.33333%;flex:0 0 8.33333%;max-width:8.33333%}.col-lg-2{-webkit-box-flex:0;-ms-flex:0 0 16.66667%;flex:0 0 16.66667%;max-width:16.66667%}.col-lg-3{-webkit-box-flex:0;-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-lg-4{-webkit-box-flex:0;-ms-flex:0 0 33.33333%;flex:0 0 33.33333%;max-width:33.33333%}.col-lg-5{-webkit-box-flex:0;-ms-flex:0 0 41.66667%;flex:0 0 41.66667%;max-width:41.66667%}.col-lg-6{-webkit-box-flex:0;-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-lg-7{-webkit-box-flex:0;-ms-flex:0 0 58.33333%;flex:0 0 58.33333%;max-width:58.33333%}.col-lg-8{-webkit-box-flex:0;-ms-flex:0 0 66.66667%;flex:0 0 66.66667%;max-width:66.66667%}.col-lg-9{-webkit-box-flex:0;-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-lg-10{-webkit-box-flex:0;-ms-flex:0 0 83.33333%;flex:0 0 83.33333%;max-width:83.33333%}.col-lg-11{-webkit-box-flex:0;-ms-flex:0 0 91.66667%;flex:0 0 91.66667%;max-width:91.66667%}.col-lg-12{-webkit-box-flex:0;-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-lg-first{-webkit-box-ordinal-group:0;-ms-flex-order:-1;order:-1}.order-lg-last{-webkit-box-ordinal-group:14;-ms-flex-order:13;order:13}.order-lg-0{-webkit-box-ordinal-group:1;-ms-flex-order:0;order:0}.order-lg-1{-webkit-box-ordinal-group:2;-ms-flex-order:1;order:1}.order-lg-2{-webkit-box-ordinal-group:3;-ms-flex-order:2;order:2}.order-lg-3{-webkit-box-ordinal-group:4;-ms-flex-order:3;order:3}.order-lg-4{-webkit-box-ordinal-group:5;-ms-flex-order:4;order:4}.order-lg-5{-webkit-box-ordinal-group:6;-ms-flex-order:5;order:5}.order-lg-6{-webkit-box-ordinal-group:7;-ms-flex-order:6;order:6}.order-lg-7{-webkit-box-ordinal-group:8;-ms-flex-order:7;order:7}.order-lg-8{-webkit-box-ordinal-group:9;-ms-flex-order:8;order:8}.order-lg-9{-webkit-box-ordinal-group:10;-ms-flex-order:9;order:9}.order-lg-10{-webkit-box-ordinal-group:11;-ms-flex-order:10;order:10}.order-lg-11{-webkit-box-ordinal-group:12;-ms-flex-order:11;order:11}.order-lg-12{-webkit-box-ordinal-group:13;-ms-flex-order:12;order:12}.offset-lg-0{margin-left:0}.offset-lg-1{margin-left:8.33333%}.offset-lg-2{margin-left:16.66667%}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:33.33333%}.offset-lg-5{margin-left:41.66667%}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:58.33333%}.offset-lg-8{margin-left:66.66667%}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:83.33333%}.offset-lg-11{margin-left:91.66667%}}@media (min-width:1200px){.col-xl{-ms-flex-preferred-size:0;flex-basis:0;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-xl-auto{-webkit-box-flex:0;-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-xl-1{-webkit-box-flex:0;-ms-flex:0 0 8.33333%;flex:0 0 8.33333%;max-width:8.33333%}.col-xl-2{-webkit-box-flex:0;-ms-flex:0 0 16.66667%;flex:0 0 16.66667%;max-width:16.66667%}.col-xl-3{-webkit-box-flex:0;-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-xl-4{-webkit-box-flex:0;-ms-flex:0 0 33.33333%;flex:0 0 33.33333%;max-width:33.33333%}.col-xl-5{-webkit-box-flex:0;-ms-flex:0 0 41.66667%;flex:0 0 41.66667%;max-width:41.66667%}.col-xl-6{-webkit-box-flex:0;-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-xl-7{-webkit-box-flex:0;-ms-flex:0 0 58.33333%;flex:0 0 58.33333%;max-width:58.33333%}.col-xl-8{-webkit-box-flex:0;-ms-flex:0 0 66.66667%;flex:0 0 66.66667%;max-width:66.66667%}.col-xl-9{-webkit-box-flex:0;-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-xl-10{-webkit-box-flex:0;-ms-flex:0 0 83.33333%;flex:0 0 83.33333%;max-width:83.33333%}.col-xl-11{-webkit-box-flex:0;-ms-flex:0 0 91.66667%;flex:0 0 91.66667%;max-width:91.66667%}.col-xl-12{-webkit-box-flex:0;-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-xl-first{-webkit-box-ordinal-group:0;-ms-flex-order:-1;order:-1}.order-xl-last{-webkit-box-ordinal-group:14;-ms-flex-order:13;order:13}.order-xl-0{-webkit-box-ordinal-group:1;-ms-flex-order:0;order:0}.order-xl-1{-webkit-box-ordinal-group:2;-ms-flex-order:1;order:1}.order-xl-2{-webkit-box-ordinal-group:3;-ms-flex-order:2;order:2}.order-xl-3{-webkit-box-ordinal-group:4;-ms-flex-order:3;order:3}.order-xl-4{-webkit-box-ordinal-group:5;-ms-flex-order:4;order:4}.order-xl-5{-webkit-box-ordinal-group:6;-ms-flex-order:5;order:5}.order-xl-6{-webkit-box-ordinal-group:7;-ms-flex-order:6;order:6}.order-xl-7{-webkit-box-ordinal-group:8;-ms-flex-order:7;order:7}.order-xl-8{-webkit-box-ordinal-group:9;-ms-flex-order:8;order:8}.order-xl-9{-webkit-box-ordinal-group:10;-ms-flex-order:9;order:9}.order-xl-10{-webkit-box-ordinal-group:11;-ms-flex-order:10;order:10}.order-xl-11{-webkit-box-ordinal-group:12;-ms-flex-order:11;order:11}.order-xl-12{-webkit-box-ordinal-group:13;-ms-flex-order:12;order:12}.offset-xl-0{margin-left:0}.offset-xl-1{margin-left:8.33333%}.offset-xl-2{margin-left:16.66667%}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:33.33333%}.offset-xl-5{margin-left:41.66667%}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:58.33333%}.offset-xl-8{margin-left:66.66667%}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:83.33333%}.offset-xl-11{margin-left:91.66667%}}.table{width:100%;margin-bottom:1rem;background-color:transparent}.table td,.table th{padding:.75rem;vertical-align:top;border-top:1px solid #dee2e6}.table thead th{vertical-align:bottom;border-bottom:2px solid #dee2e6}.table tbody+tbody{border-top:2px solid #dee2e6}.table .table{background-color:#fff}.table-sm td,.table-sm th{padding:.3rem}.table-bordered,.table-bordered td,.table-bordered th{border:1px solid #dee2e6}.table-bordered thead td,.table-bordered thead th{border-bottom-width:2px}.table-borderless tbody+tbody,.table-borderless td,.table-borderless th,.table-borderless thead th{border:0}.table-striped tbody tr:nth-of-type(odd){background-color:rgba(0,0,0,.05)}.table-hover tbody tr:hover{background-color:rgba(0,0,0,.075)}.table-primary,.table-primary>td,.table-primary>th{background-color:#b8ecfb}.table-primary tbody+tbody,.table-primary td,.table-primary th,.table-primary thead th{border-color:#7bdbf8}.table-hover .table-primary:hover,.table-hover .table-primary:hover>td,.table-hover .table-primary:hover>th{background-color:#a0e6fa}.table-secondary,.table-secondary>td,.table-secondary>th{background-color:#d6d8db}.table-secondary tbody+tbody,.table-secondary td,.table-secondary th,.table-secondary thead th{border-color:#b3b7bb}.table-hover .table-secondary:hover,.table-hover .table-secondary:hover>td,.table-hover .table-secondary:hover>th{background-color:#c8cbcf}.table-success,.table-success>td,.table-success>th{background-color:#c3e6cb}.table-success tbody+tbody,.table-success td,.table-success th,.table-success thead th{border-color:#8fd19e}.table-hover .table-success:hover,.table-hover .table-success:hover>td,.table-hover .table-success:hover>th{background-color:#b1dfbb}.table-info,.table-info>td,.table-info>th{background-color:#bee5eb}.table-info tbody+tbody,.table-info td,.table-info th,.table-info thead th{border-color:#86cfda}.table-hover .table-info:hover,.table-hover .table-info:hover>td,.table-hover .table-info:hover>th{background-color:#abdde5}.table-warning,.table-warning>td,.table-warning>th{background-color:#ffeeba}.table-warning tbody+tbody,.table-warning td,.table-warning th,.table-warning thead th{border-color:#ffdf7e}.table-hover .table-warning:hover,.table-hover .table-warning:hover>td,.table-hover .table-warning:hover>th{background-color:#ffe8a1}.table-danger,.table-danger>td,.table-danger>th{background-color:#fdcbcb}.table-danger tbody+tbody,.table-danger td,.table-danger th,.table-danger thead th{border-color:#fb9e9e}.table-hover .table-danger:hover,.table-hover .table-danger:hover>td,.table-hover .table-danger:hover>th{background-color:#fcb2b2}.table-light,.table-light>td,.table-light>th{background-color:#fdfdfe}.table-light tbody+tbody,.table-light td,.table-light th,.table-light thead th{border-color:#fbfcfc}.table-hover .table-light:hover,.table-hover .table-light:hover>td,.table-hover .table-light:hover>th{background-color:#ececf6}.table-dark,.table-dark>td,.table-dark>th{background-color:#c6c8ca}.table-dark tbody+tbody,.table-dark td,.table-dark th,.table-dark thead th{border-color:#95999c}.table-hover .table-dark:hover,.table-hover .table-dark:hover>td,.table-hover .table-dark:hover>th{background-color:#b9bbbe}.table-active,.table-active>td,.table-active>th,.table-hover .table-active:hover,.table-hover .table-active:hover>td,.table-hover .table-active:hover>th{background-color:rgba(0,0,0,.075)}.table .thead-dark th{color:#fff;background-color:#212529;border-color:#32383e}.table .thead-light th{color:#495057;background-color:#e9ecef;border-color:#dee2e6}.table-dark{color:#fff;background-color:#212529}.table-dark td,.table-dark th,.table-dark thead th{border-color:#32383e}.table-dark.table-bordered{border:0}.table-dark.table-striped tbody tr:nth-of-type(odd){background-color:hsla(0,0%,100%,.05)}.table-dark.table-hover tbody tr:hover{background-color:hsla(0,0%,100%,.075)}@media (max-width:575.98px){.table-responsive-sm{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar}.table-responsive-sm>.table-bordered{border:0}}@media (max-width:767.98px){.table-responsive-md{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar}.table-responsive-md>.table-bordered{border:0}}@media (max-width:991.98px){.table-responsive-lg{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar}.table-responsive-lg>.table-bordered{border:0}}@media (max-width:1199.98px){.table-responsive-xl{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar}.table-responsive-xl>.table-bordered{border:0}}.table-responsive{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar}.table-responsive>.table-bordered{border:0}.form-control{display:block;width:100%;height:calc(2.25rem + 2px);padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;background-color:#fff;background-clip:padding-box;border:1px solid #ced4da;border-radius:.25rem;-webkit-transition:border-color .15s ease-in-out,-webkit-box-shadow .15s ease-in-out;transition:border-color .15s ease-in-out,-webkit-box-shadow .15s ease-in-out;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out,-webkit-box-shadow .15s ease-in-out}@media screen and (prefers-reduced-motion:reduce){.form-control{-webkit-transition:none;transition:none}}.form-control::-ms-expand{background-color:transparent;border:0}.form-control:focus{color:#495057;background-color:#fff;border-color:#76defe;outline:0;-webkit-box-shadow:0 0 0 .2rem rgba(2,186,242,.25);box-shadow:0 0 0 .2rem rgba(2,186,242,.25)}.form-control::-webkit-input-placeholder{color:#6c757d;opacity:1}.form-control:-ms-input-placeholder{color:#6c757d;opacity:1}.form-control::-ms-input-placeholder{color:#6c757d;opacity:1}.form-control::placeholder{color:#6c757d;opacity:1}.form-control:disabled,.form-control[readonly]{background-color:#e9ecef;opacity:1}select.form-control:focus::-ms-value{color:#495057;background-color:#fff}.form-control-file,.form-control-range{display:block;width:100%}.col-form-label{padding-top:calc(.375rem + 1px);padding-bottom:calc(.375rem + 1px);margin-bottom:0;font-size:inherit;line-height:1.5}.col-form-label-lg{padding-top:calc(.5rem + 1px);padding-bottom:calc(.5rem + 1px);font-size:1.25rem;line-height:1.5}.col-form-label-sm{padding-top:calc(.25rem + 1px);padding-bottom:calc(.25rem + 1px);font-size:.875rem;line-height:1.5}.form-control-plaintext{display:block;width:100%;padding-top:.375rem;padding-bottom:.375rem;margin-bottom:0;line-height:1.5;color:#212529;background-color:transparent;border:solid transparent;border-width:1px 0}.form-control-plaintext.form-control-lg,.form-control-plaintext.form-control-sm{padding-right:0;padding-left:0}.form-control-sm{height:calc(1.8125rem + 2px);padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.form-control-lg{height:calc(2.875rem + 2px);padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}select.form-control[multiple],select.form-control[size],textarea.form-control{height:auto}.form-group{margin-bottom:1rem}.form-text{display:block;margin-top:.25rem}.form-row{display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-5px;margin-left:-5px}.form-row>.col,.form-row>[class*=col-]{padding-right:5px;padding-left:5px}.form-check{position:relative;display:block;padding-left:1.25rem}.form-check-input{position:absolute;margin-top:.3rem;margin-left:-1.25rem}.form-check-input:disabled~.form-check-label{color:#6c757d}.form-check-label{margin-bottom:0}.form-check-inline{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;padding-left:0;margin-right:.75rem}.form-check-inline .form-check-input{position:static;margin-top:0;margin-right:.3125rem;margin-left:0}.valid-feedback{display:none;width:100%;margin-top:.25rem;font-size:80%;color:#28a745}.valid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;line-height:1.5;color:#fff;background-color:rgba(40,167,69,.9);border-radius:.25rem}.form-control.is-valid,.was-validated .form-control:valid{border-color:#28a745;padding-right:2.25rem;background-repeat:no-repeat;background-position:center right .5625rem;background-size:1.125rem 1.125rem;background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3E%3C/svg%3E")}.form-control.is-valid:focus,.was-validated .form-control:valid:focus{border-color:#28a745;-webkit-box-shadow:0 0 0 .2rem rgba(40,167,69,.25);box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.form-control.is-valid~.valid-feedback,.form-control.is-valid~.valid-tooltip,.was-validated .form-control:valid~.valid-feedback,.was-validated .form-control:valid~.valid-tooltip{display:block}.was-validated textarea.form-control:valid,textarea.form-control.is-valid{padding-right:2.25rem;background-position:top .5625rem right .5625rem}.custom-select.is-valid,.was-validated .custom-select:valid{border-color:#28a745;padding-right:3.4375rem;background:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3E%3Cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3E%3C/svg%3E") no-repeat right .75rem center/8px 10px,url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3E%3C/svg%3E") no-repeat center right 1.75rem/1.125rem 1.125rem}.custom-select.is-valid:focus,.was-validated .custom-select:valid:focus{border-color:#28a745;-webkit-box-shadow:0 0 0 .2rem rgba(40,167,69,.25);box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.custom-select.is-valid~.valid-feedback,.custom-select.is-valid~.valid-tooltip,.form-control-file.is-valid~.valid-feedback,.form-control-file.is-valid~.valid-tooltip,.was-validated .custom-select:valid~.valid-feedback,.was-validated .custom-select:valid~.valid-tooltip,.was-validated .form-control-file:valid~.valid-feedback,.was-validated .form-control-file:valid~.valid-tooltip{display:block}.form-check-input.is-valid~.form-check-label,.was-validated .form-check-input:valid~.form-check-label{color:#28a745}.form-check-input.is-valid~.valid-feedback,.form-check-input.is-valid~.valid-tooltip,.was-validated .form-check-input:valid~.valid-feedback,.was-validated .form-check-input:valid~.valid-tooltip{display:block}.custom-control-input.is-valid~.custom-control-label,.was-validated .custom-control-input:valid~.custom-control-label{color:#28a745}.custom-control-input.is-valid~.custom-control-label:before,.was-validated .custom-control-input:valid~.custom-control-label:before{border-color:#28a745}.custom-control-input.is-valid~.valid-feedback,.custom-control-input.is-valid~.valid-tooltip,.was-validated .custom-control-input:valid~.valid-feedback,.was-validated .custom-control-input:valid~.valid-tooltip{display:block}.custom-control-input.is-valid:checked~.custom-control-label:before,.was-validated .custom-control-input:valid:checked~.custom-control-label:before{border-color:#34ce57;background-color:#34ce57}.custom-control-input.is-valid:focus~.custom-control-label:before,.was-validated .custom-control-input:valid:focus~.custom-control-label:before{-webkit-box-shadow:0 0 0 .2rem rgba(40,167,69,.25);box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.custom-control-input.is-valid:focus:not(:checked)~.custom-control-label:before,.custom-file-input.is-valid~.custom-file-label,.was-validated .custom-control-input:valid:focus:not(:checked)~.custom-control-label:before,.was-validated .custom-file-input:valid~.custom-file-label{border-color:#28a745}.custom-file-input.is-valid~.valid-feedback,.custom-file-input.is-valid~.valid-tooltip,.was-validated .custom-file-input:valid~.valid-feedback,.was-validated .custom-file-input:valid~.valid-tooltip{display:block}.custom-file-input.is-valid:focus~.custom-file-label,.was-validated .custom-file-input:valid:focus~.custom-file-label{border-color:#28a745;-webkit-box-shadow:0 0 0 .2rem rgba(40,167,69,.25);box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.invalid-feedback{display:none;width:100%;margin-top:.25rem;font-size:80%;color:#f84545}.invalid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;line-height:1.5;color:#fff;background-color:rgba(248,69,69,.9);border-radius:.25rem}.form-control.is-invalid,.was-validated .form-control:invalid{border-color:#f84545;padding-right:2.25rem;background-repeat:no-repeat;background-position:center right .5625rem;background-size:1.125rem 1.125rem;background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23f84545' viewBox='-2 -2 7 7'%3E%3Cpath stroke='%23d9534f' d='M0 0l3 3m0-3L0 3'/%3E%3Ccircle r='.5'/%3E%3Ccircle cx='3' r='.5'/%3E%3Ccircle cy='3' r='.5'/%3E%3Ccircle cx='3' cy='3' r='.5'/%3E%3C/svg%3E")}.form-control.is-invalid:focus,.was-validated .form-control:invalid:focus{border-color:#f84545;-webkit-box-shadow:0 0 0 .2rem rgba(248,69,69,.25);box-shadow:0 0 0 .2rem rgba(248,69,69,.25)}.form-control.is-invalid~.invalid-feedback,.form-control.is-invalid~.invalid-tooltip,.was-validated .form-control:invalid~.invalid-feedback,.was-validated .form-control:invalid~.invalid-tooltip{display:block}.was-validated textarea.form-control:invalid,textarea.form-control.is-invalid{padding-right:2.25rem;background-position:top .5625rem right .5625rem}.custom-select.is-invalid,.was-validated .custom-select:invalid{border-color:#f84545;padding-right:3.4375rem;background:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3E%3Cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3E%3C/svg%3E") no-repeat right .75rem center/8px 10px,url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23f84545' viewBox='-2 -2 7 7'%3E%3Cpath stroke='%23d9534f' d='M0 0l3 3m0-3L0 3'/%3E%3Ccircle r='.5'/%3E%3Ccircle cx='3' r='.5'/%3E%3Ccircle cy='3' r='.5'/%3E%3Ccircle cx='3' cy='3' r='.5'/%3E%3C/svg%3E") no-repeat center right 1.75rem/1.125rem 1.125rem}.custom-select.is-invalid:focus,.was-validated .custom-select:invalid:focus{border-color:#f84545;-webkit-box-shadow:0 0 0 .2rem rgba(248,69,69,.25);box-shadow:0 0 0 .2rem rgba(248,69,69,.25)}.custom-select.is-invalid~.invalid-feedback,.custom-select.is-invalid~.invalid-tooltip,.form-control-file.is-invalid~.invalid-feedback,.form-control-file.is-invalid~.invalid-tooltip,.was-validated .custom-select:invalid~.invalid-feedback,.was-validated .custom-select:invalid~.invalid-tooltip,.was-validated .form-control-file:invalid~.invalid-feedback,.was-validated .form-control-file:invalid~.invalid-tooltip{display:block}.form-check-input.is-invalid~.form-check-label,.was-validated .form-check-input:invalid~.form-check-label{color:#f84545}.form-check-input.is-invalid~.invalid-feedback,.form-check-input.is-invalid~.invalid-tooltip,.was-validated .form-check-input:invalid~.invalid-feedback,.was-validated .form-check-input:invalid~.invalid-tooltip{display:block}.custom-control-input.is-invalid~.custom-control-label,.was-validated .custom-control-input:invalid~.custom-control-label{color:#f84545}.custom-control-input.is-invalid~.custom-control-label:before,.was-validated .custom-control-input:invalid~.custom-control-label:before{border-color:#f84545}.custom-control-input.is-invalid~.invalid-feedback,.custom-control-input.is-invalid~.invalid-tooltip,.was-validated .custom-control-input:invalid~.invalid-feedback,.was-validated .custom-control-input:invalid~.invalid-tooltip{display:block}.custom-control-input.is-invalid:checked~.custom-control-label:before,.was-validated .custom-control-input:invalid:checked~.custom-control-label:before{border-color:#fa7676;background-color:#fa7676}.custom-control-input.is-invalid:focus~.custom-control-label:before,.was-validated .custom-control-input:invalid:focus~.custom-control-label:before{-webkit-box-shadow:0 0 0 .2rem rgba(248,69,69,.25);box-shadow:0 0 0 .2rem rgba(248,69,69,.25)}.custom-control-input.is-invalid:focus:not(:checked)~.custom-control-label:before,.custom-file-input.is-invalid~.custom-file-label,.was-validated .custom-control-input:invalid:focus:not(:checked)~.custom-control-label:before,.was-validated .custom-file-input:invalid~.custom-file-label{border-color:#f84545}.custom-file-input.is-invalid~.invalid-feedback,.custom-file-input.is-invalid~.invalid-tooltip,.was-validated .custom-file-input:invalid~.invalid-feedback,.was-validated .custom-file-input:invalid~.invalid-tooltip{display:block}.custom-file-input.is-invalid:focus~.custom-file-label,.was-validated .custom-file-input:invalid:focus~.custom-file-label{border-color:#f84545;-webkit-box-shadow:0 0 0 .2rem rgba(248,69,69,.25);box-shadow:0 0 0 .2rem rgba(248,69,69,.25)}.form-inline{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-flow:row wrap;flex-flow:row wrap;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.form-inline .form-check{width:100%}@media (min-width:576px){.form-inline label{-ms-flex-align:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.form-inline .form-group,.form-inline label{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;align-items:center;margin-bottom:0}.form-inline .form-group{-webkit-box-flex:0;-ms-flex:0 0 auto;flex:0 0 auto;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-flow:row wrap;flex-flow:row wrap;-ms-flex-align:center}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-plaintext{display:inline-block}.form-inline .custom-select,.form-inline .input-group{width:auto}.form-inline .form-check{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;width:auto;padding-left:0}.form-inline .form-check-input{position:relative;margin-top:0;margin-right:.25rem;margin-left:0}.form-inline .custom-control{-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.form-inline .custom-control-label{margin-bottom:0}}.btn{display:inline-block;font-weight:400;color:#212529;text-align:center;vertical-align:middle;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-color:transparent;border:1px solid transparent;padding:.375rem .75rem;font-size:1rem;line-height:1.5;border-radius:.25rem;-webkit-transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,-webkit-box-shadow .15s ease-in-out;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,-webkit-box-shadow .15s ease-in-out;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,-webkit-box-shadow .15s ease-in-out}@media screen and (prefers-reduced-motion:reduce){.btn{-webkit-transition:none;transition:none}}.btn:hover{color:#212529;text-decoration:none}.btn.focus,.btn:focus{outline:0;-webkit-box-shadow:0 0 0 .2rem rgba(2,186,242,.25);box-shadow:0 0 0 .2rem rgba(2,186,242,.25)}.btn.disabled,.btn:disabled{opacity:.65}.btn:not(:disabled):not(.disabled){cursor:pointer}a.btn.disabled,fieldset:disabled a.btn{pointer-events:none}.btn-primary{color:#fff;background-color:#02baf2;border-color:#02baf2}.btn-primary:hover{color:#fff;background-color:#029dcc;border-color:#0293bf}.btn-primary.focus,.btn-primary:focus{-webkit-box-shadow:0 0 0 .2rem rgba(40,196,244,.5);box-shadow:0 0 0 .2rem rgba(40,196,244,.5)}.btn-primary.disabled,.btn-primary:disabled{color:#fff;background-color:#02baf2;border-color:#02baf2}.btn-primary:not(:disabled):not(.disabled).active,.btn-primary:not(:disabled):not(.disabled):active,.show>.btn-primary.dropdown-toggle{color:#fff;background-color:#0293bf;border-color:#0189b3}.btn-primary:not(:disabled):not(.disabled).active:focus,.btn-primary:not(:disabled):not(.disabled):active:focus,.show>.btn-primary.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 .2rem rgba(40,196,244,.5);box-shadow:0 0 0 .2rem rgba(40,196,244,.5)}.btn-secondary{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-secondary:hover{color:#fff;background-color:#5a6268;border-color:#545b62}.btn-secondary.focus,.btn-secondary:focus{-webkit-box-shadow:0 0 0 .2rem rgba(130,138,145,.5);box-shadow:0 0 0 .2rem rgba(130,138,145,.5)}.btn-secondary.disabled,.btn-secondary:disabled{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-secondary:not(:disabled):not(.disabled).active,.btn-secondary:not(:disabled):not(.disabled):active,.show>.btn-secondary.dropdown-toggle{color:#fff;background-color:#545b62;border-color:#4e555b}.btn-secondary:not(:disabled):not(.disabled).active:focus,.btn-secondary:not(:disabled):not(.disabled):active:focus,.show>.btn-secondary.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 .2rem rgba(130,138,145,.5);box-shadow:0 0 0 .2rem rgba(130,138,145,.5)}.btn-success{color:#fff;background-color:#28a745;border-color:#28a745}.btn-success:hover{color:#fff;background-color:#218838;border-color:#1e7e34}.btn-success.focus,.btn-success:focus{-webkit-box-shadow:0 0 0 .2rem rgba(72,180,97,.5);box-shadow:0 0 0 .2rem rgba(72,180,97,.5)}.btn-success.disabled,.btn-success:disabled{color:#fff;background-color:#28a745;border-color:#28a745}.btn-success:not(:disabled):not(.disabled).active,.btn-success:not(:disabled):not(.disabled):active,.show>.btn-success.dropdown-toggle{color:#fff;background-color:#1e7e34;border-color:#1c7430}.btn-success:not(:disabled):not(.disabled).active:focus,.btn-success:not(:disabled):not(.disabled):active:focus,.show>.btn-success.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 .2rem rgba(72,180,97,.5);box-shadow:0 0 0 .2rem rgba(72,180,97,.5)}.btn-info{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-info:hover{color:#fff;background-color:#138496;border-color:#117a8b}.btn-info.focus,.btn-info:focus{-webkit-box-shadow:0 0 0 .2rem rgba(58,176,195,.5);box-shadow:0 0 0 .2rem rgba(58,176,195,.5)}.btn-info.disabled,.btn-info:disabled{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-info:not(:disabled):not(.disabled).active,.btn-info:not(:disabled):not(.disabled):active,.show>.btn-info.dropdown-toggle{color:#fff;background-color:#117a8b;border-color:#10707f}.btn-info:not(:disabled):not(.disabled).active:focus,.btn-info:not(:disabled):not(.disabled):active:focus,.show>.btn-info.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 .2rem rgba(58,176,195,.5);box-shadow:0 0 0 .2rem rgba(58,176,195,.5)}.btn-warning{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-warning:hover{color:#212529;background-color:#e0a800;border-color:#d39e00}.btn-warning.focus,.btn-warning:focus{-webkit-box-shadow:0 0 0 .2rem rgba(222,170,12,.5);box-shadow:0 0 0 .2rem rgba(222,170,12,.5)}.btn-warning.disabled,.btn-warning:disabled{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-warning:not(:disabled):not(.disabled).active,.btn-warning:not(:disabled):not(.disabled):active,.show>.btn-warning.dropdown-toggle{color:#212529;background-color:#d39e00;border-color:#c69500}.btn-warning:not(:disabled):not(.disabled).active:focus,.btn-warning:not(:disabled):not(.disabled):active:focus,.show>.btn-warning.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 .2rem rgba(222,170,12,.5);box-shadow:0 0 0 .2rem rgba(222,170,12,.5)}.btn-danger{color:#fff;background-color:#f84545;border-color:#f84545}.btn-danger:hover{color:#fff;background-color:#f72020;border-color:#f61414}.btn-danger.focus,.btn-danger:focus{-webkit-box-shadow:0 0 0 .2rem rgba(249,97,97,.5);box-shadow:0 0 0 .2rem rgba(249,97,97,.5)}.btn-danger.disabled,.btn-danger:disabled{color:#fff;background-color:#f84545;border-color:#f84545}.btn-danger:not(:disabled):not(.disabled).active,.btn-danger:not(:disabled):not(.disabled):active,.show>.btn-danger.dropdown-toggle{color:#fff;background-color:#f61414;border-color:#f40909}.btn-danger:not(:disabled):not(.disabled).active:focus,.btn-danger:not(:disabled):not(.disabled):active:focus,.show>.btn-danger.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 .2rem rgba(249,97,97,.5);box-shadow:0 0 0 .2rem rgba(249,97,97,.5)}.btn-light{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light:hover{color:#212529;background-color:#e2e6ea;border-color:#dae0e5}.btn-light.focus,.btn-light:focus{-webkit-box-shadow:0 0 0 .2rem rgba(216,217,219,.5);box-shadow:0 0 0 .2rem rgba(216,217,219,.5)}.btn-light.disabled,.btn-light:disabled{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light:not(:disabled):not(.disabled).active,.btn-light:not(:disabled):not(.disabled):active,.show>.btn-light.dropdown-toggle{color:#212529;background-color:#dae0e5;border-color:#d3d9df}.btn-light:not(:disabled):not(.disabled).active:focus,.btn-light:not(:disabled):not(.disabled):active:focus,.show>.btn-light.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 .2rem rgba(216,217,219,.5);box-shadow:0 0 0 .2rem rgba(216,217,219,.5)}.btn-dark{color:#fff;background-color:#343a40;border-color:#343a40}.btn-dark:hover{color:#fff;background-color:#23272b;border-color:#1d2124}.btn-dark.focus,.btn-dark:focus{-webkit-box-shadow:0 0 0 .2rem rgba(82,88,93,.5);box-shadow:0 0 0 .2rem rgba(82,88,93,.5)}.btn-dark.disabled,.btn-dark:disabled{color:#fff;background-color:#343a40;border-color:#343a40}.btn-dark:not(:disabled):not(.disabled).active,.btn-dark:not(:disabled):not(.disabled):active,.show>.btn-dark.dropdown-toggle{color:#fff;background-color:#1d2124;border-color:#171a1d}.btn-dark:not(:disabled):not(.disabled).active:focus,.btn-dark:not(:disabled):not(.disabled):active:focus,.show>.btn-dark.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 .2rem rgba(82,88,93,.5);box-shadow:0 0 0 .2rem rgba(82,88,93,.5)}.btn-link{font-weight:400;color:#02baf2}.btn-link:hover{color:#0180a6;text-decoration:underline}.btn-link.focus,.btn-link:focus{text-decoration:underline;-webkit-box-shadow:none;box-shadow:none}.btn-link.disabled,.btn-link:disabled{color:#6c757d;pointer-events:none}.btn-group-lg>.btn,.btn-lg{padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}.btn-group-sm>.btn,.btn-sm{padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:.5rem}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{-webkit-transition:opacity .15s linear;transition:opacity .15s linear}@media screen and (prefers-reduced-motion:reduce){.fade{-webkit-transition:none;transition:none}}.fade:not(.show){opacity:0}.collapse:not(.show){display:none}.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition:height .35s ease;transition:height .35s ease}@media screen and (prefers-reduced-motion:reduce){.collapsing{-webkit-transition:none;transition:none}}.dropdown,.dropleft,.dropright,.dropup{position:relative}.dropdown-toggle:after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid;border-right:.3em solid transparent;border-bottom:0;border-left:.3em solid transparent}.dropdown-toggle:empty:after{margin-left:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:10rem;padding:.5rem 0;margin:.125rem 0 0;font-size:1rem;color:#212529;text-align:left;list-style:none;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.15);border-radius:.25rem}.dropdown-menu-right{right:0;left:auto}@media (min-width:576px){.dropdown-menu-sm-right{right:0;left:auto}}@media (min-width:768px){.dropdown-menu-md-right{right:0;left:auto}}@media (min-width:992px){.dropdown-menu-lg-right{right:0;left:auto}}@media (min-width:1200px){.dropdown-menu-xl-right{right:0;left:auto}}.dropdown-menu-left{right:auto;left:0}@media (min-width:576px){.dropdown-menu-sm-left{right:auto;left:0}}@media (min-width:768px){.dropdown-menu-md-left{right:auto;left:0}}@media (min-width:992px){.dropdown-menu-lg-left{right:auto;left:0}}@media (min-width:1200px){.dropdown-menu-xl-left{right:auto;left:0}}.dropup .dropdown-menu{top:auto;bottom:100%;margin-top:0;margin-bottom:.125rem}.dropup .dropdown-toggle:after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:0;border-right:.3em solid transparent;border-bottom:.3em solid;border-left:.3em solid transparent}.dropup .dropdown-toggle:empty:after{margin-left:0}.dropright .dropdown-menu{top:0;right:auto;left:100%;margin-top:0;margin-left:.125rem}.dropright .dropdown-toggle:after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:0;border-bottom:.3em solid transparent;border-left:.3em solid}.dropright .dropdown-toggle:empty:after{margin-left:0}.dropright .dropdown-toggle:after{vertical-align:0}.dropleft .dropdown-menu{top:0;right:100%;left:auto;margin-top:0;margin-right:.125rem}.dropleft .dropdown-toggle:after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";display:none}.dropleft .dropdown-toggle:before{display:inline-block;margin-right:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:.3em solid;border-bottom:.3em solid transparent}.dropleft .dropdown-toggle:empty:after{margin-left:0}.dropleft .dropdown-toggle:before{vertical-align:0}.dropdown-menu[x-placement^=bottom],.dropdown-menu[x-placement^=left],.dropdown-menu[x-placement^=right],.dropdown-menu[x-placement^=top]{right:auto;bottom:auto}.dropdown-divider{height:0;margin:.5rem 0;overflow:hidden;border-top:1px solid #e9ecef}.dropdown-item{display:block;width:100%;padding:.25rem 1.5rem;clear:both;font-weight:400;color:#212529;text-align:inherit;white-space:nowrap;background-color:transparent;border:0}.dropdown-item:first-child{border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.dropdown-item:last-child{border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.dropdown-item:focus,.dropdown-item:hover{color:#16181b;text-decoration:none;background-color:#f8f9fa}.dropdown-item.active,.dropdown-item:active{color:#fff;text-decoration:none;background-color:#02baf2}.dropdown-item.disabled,.dropdown-item:disabled{color:#6c757d;pointer-events:none;background-color:transparent}.dropdown-menu.show{display:block}.dropdown-header{display:block;padding:.5rem 1.5rem;margin-bottom:0;font-size:.875rem;color:#6c757d;white-space:nowrap}.dropdown-item-text{display:block;padding:.25rem 1.5rem;color:#212529}.btn-group,.btn-group-vertical{position:relative;display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;-webkit-box-flex:1;-ms-flex:1 1 auto;flex:1 1 auto}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:hover,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus,.btn-group>.btn:hover{z-index:1}.btn-toolbar{display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start}.btn-toolbar .input-group{width:auto}.btn-group>.btn-group:not(:first-child),.btn-group>.btn:not(:first-child){margin-left:-1px}.btn-group>.btn-group:not(:last-child)>.btn,.btn-group>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:not(:first-child)>.btn,.btn-group>.btn:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.dropdown-toggle-split{padding-right:.5625rem;padding-left:.5625rem}.dropdown-toggle-split:after,.dropright .dropdown-toggle-split:after,.dropup .dropdown-toggle-split:after{margin-left:0}.dropleft .dropdown-toggle-split:before{margin-right:0}.btn-group-sm>.btn+.dropdown-toggle-split,.btn-sm+.dropdown-toggle-split{padding-right:.375rem;padding-left:.375rem}.btn-group-lg>.btn+.dropdown-toggle-split,.btn-lg+.dropdown-toggle-split{padding-right:.75rem;padding-left:.75rem}.btn-group-vertical{-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;-webkit-box-align:start;-ms-flex-align:start;align-items:flex-start;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group{width:100%}.btn-group-vertical>.btn-group:not(:first-child),.btn-group-vertical>.btn:not(:first-child){margin-top:-1px}.btn-group-vertical>.btn-group:not(:last-child)>.btn,.btn-group-vertical>.btn:not(:last-child):not(.dropdown-toggle){border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:not(:first-child)>.btn,.btn-group-vertical>.btn:not(:first-child){border-top-left-radius:0;border-top-right-radius:0}.btn-group-toggle>.btn,.btn-group-toggle>.btn-group>.btn{margin-bottom:0}.btn-group-toggle>.btn-group>.btn input[type=checkbox],.btn-group-toggle>.btn-group>.btn input[type=radio],.btn-group-toggle>.btn input[type=checkbox],.btn-group-toggle>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-box-align:stretch;-ms-flex-align:stretch;align-items:stretch;width:100%}.input-group>.custom-file,.input-group>.custom-select,.input-group>.form-control,.input-group>.form-control-plaintext{position:relative;-webkit-box-flex:1;-ms-flex:1 1 auto;flex:1 1 auto;width:1%;margin-bottom:0}.input-group>.custom-file+.custom-file,.input-group>.custom-file+.custom-select,.input-group>.custom-file+.form-control,.input-group>.custom-select+.custom-file,.input-group>.custom-select+.custom-select,.input-group>.custom-select+.form-control,.input-group>.form-control+.custom-file,.input-group>.form-control+.custom-select,.input-group>.form-control+.form-control,.input-group>.form-control-plaintext+.custom-file,.input-group>.form-control-plaintext+.custom-select,.input-group>.form-control-plaintext+.form-control{margin-left:-1px}.input-group>.custom-file .custom-file-input:focus~.custom-file-label,.input-group>.custom-select:focus,.input-group>.form-control:focus{z-index:3}.input-group>.custom-file .custom-file-input:focus{z-index:4}.input-group>.custom-select:not(:last-child),.input-group>.form-control:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.custom-select:not(:first-child),.input-group>.form-control:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.input-group>.custom-file{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.input-group>.custom-file:not(:last-child) .custom-file-label,.input-group>.custom-file:not(:last-child) .custom-file-label:after{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.custom-file:not(:first-child) .custom-file-label{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-append,.input-group-prepend{display:-webkit-box;display:-ms-flexbox;display:flex}.input-group-append .btn,.input-group-prepend .btn{position:relative;z-index:2}.input-group-append .btn:focus,.input-group-prepend .btn:focus{z-index:3}.input-group-append .btn+.btn,.input-group-append .btn+.input-group-text,.input-group-append .input-group-text+.btn,.input-group-append .input-group-text+.input-group-text,.input-group-prepend .btn+.btn,.input-group-prepend .btn+.input-group-text,.input-group-prepend .input-group-text+.btn,.input-group-prepend .input-group-text+.input-group-text{margin-left:-1px}.input-group-prepend{margin-right:-1px}.input-group-append{margin-left:-1px}.input-group-text{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;padding:.375rem .75rem;margin-bottom:0;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;text-align:center;white-space:nowrap;background-color:#e9ecef;border:1px solid #ced4da;border-radius:.25rem}.input-group-text input[type=checkbox],.input-group-text input[type=radio]{margin-top:0}.input-group-lg>.custom-select,.input-group-lg>.form-control:not(textarea){height:calc(2.875rem + 2px)}.input-group-lg>.custom-select,.input-group-lg>.form-control,.input-group-lg>.input-group-append>.btn,.input-group-lg>.input-group-append>.input-group-text,.input-group-lg>.input-group-prepend>.btn,.input-group-lg>.input-group-prepend>.input-group-text{padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}.input-group-sm>.custom-select,.input-group-sm>.form-control:not(textarea){height:calc(1.8125rem + 2px)}.input-group-sm>.custom-select,.input-group-sm>.form-control,.input-group-sm>.input-group-append>.btn,.input-group-sm>.input-group-append>.input-group-text,.input-group-sm>.input-group-prepend>.btn,.input-group-sm>.input-group-prepend>.input-group-text{padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.input-group-lg>.custom-select,.input-group-sm>.custom-select{padding-right:1.75rem}.input-group>.input-group-append:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group>.input-group-append:last-child>.input-group-text:not(:last-child),.input-group>.input-group-append:not(:last-child)>.btn,.input-group>.input-group-append:not(:last-child)>.input-group-text,.input-group>.input-group-prepend>.btn,.input-group>.input-group-prepend>.input-group-text{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.input-group-append>.btn,.input-group>.input-group-append>.input-group-text,.input-group>.input-group-prepend:first-child>.btn:not(:first-child),.input-group>.input-group-prepend:first-child>.input-group-text:not(:first-child),.input-group>.input-group-prepend:not(:first-child)>.btn,.input-group>.input-group-prepend:not(:first-child)>.input-group-text{border-top-left-radius:0;border-bottom-left-radius:0}.custom-control{position:relative;display:block;min-height:1.5rem;padding-left:1.5rem}.custom-control-inline{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;margin-right:1rem}.custom-control-input{position:absolute;z-index:-1;opacity:0}.custom-control-input:checked~.custom-control-label:before{color:#fff;border-color:#02baf2;background-color:#02baf2}.custom-control-input:focus~.custom-control-label:before{-webkit-box-shadow:0 0 0 .2rem rgba(2,186,242,.25);box-shadow:0 0 0 .2rem rgba(2,186,242,.25)}.custom-control-input:focus:not(:checked)~.custom-control-label:before{border-color:#76defe}.custom-control-input:not(:disabled):active~.custom-control-label:before{color:#fff;background-color:#a8eafe;border-color:#a8eafe}.custom-control-input:disabled~.custom-control-label{color:#6c757d}.custom-control-input:disabled~.custom-control-label:before{background-color:#e9ecef}.custom-control-label{position:relative;margin-bottom:0;vertical-align:top}.custom-control-label:before{pointer-events:none;background-color:#fff;border:1px solid #adb5bd}.custom-control-label:after,.custom-control-label:before{position:absolute;top:.25rem;left:-1.5rem;display:block;width:1rem;height:1rem;content:""}.custom-control-label:after{background-repeat:no-repeat;background-position:50%;background-size:50% 50%}.custom-checkbox .custom-control-label:before{border-radius:.25rem}.custom-checkbox .custom-control-input:checked~.custom-control-label:after{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26l2.974 2.99L8 2.193z'/%3E%3C/svg%3E")}.custom-checkbox .custom-control-input:indeterminate~.custom-control-label:before{border-color:#02baf2;background-color:#02baf2}.custom-checkbox .custom-control-input:indeterminate~.custom-control-label:after{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 4'%3E%3Cpath stroke='%23fff' d='M0 2h4'/%3E%3C/svg%3E")}.custom-checkbox .custom-control-input:disabled:checked~.custom-control-label:before{background-color:rgba(2,186,242,.5)}.custom-checkbox .custom-control-input:disabled:indeterminate~.custom-control-label:before{background-color:rgba(2,186,242,.5)}.custom-radio .custom-control-label:before{border-radius:50%}.custom-radio .custom-control-input:checked~.custom-control-label:after{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23fff'/%3E%3C/svg%3E")}.custom-radio .custom-control-input:disabled:checked~.custom-control-label:before{background-color:rgba(2,186,242,.5)}.custom-switch{padding-left:2.25rem}.custom-switch .custom-control-label:before{left:-2.25rem;width:1.75rem;pointer-events:all;border-radius:.5rem}.custom-switch .custom-control-label:after{top:calc(.25rem + 2px);left:calc(-2.25rem + 2px);width:calc(1rem - 4px);height:calc(1rem - 4px);background-color:#adb5bd;border-radius:.5rem;-webkit-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,-webkit-transform .15s ease-in-out,-webkit-box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,-webkit-transform .15s ease-in-out,-webkit-box-shadow .15s ease-in-out;transition:transform .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:transform .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,-webkit-transform .15s ease-in-out,-webkit-box-shadow .15s ease-in-out}@media screen and (prefers-reduced-motion:reduce){.custom-switch .custom-control-label:after{-webkit-transition:none;transition:none}}.custom-switch .custom-control-input:checked~.custom-control-label:after{background-color:#fff;-webkit-transform:translateX(.75rem);transform:translateX(.75rem)}.custom-switch .custom-control-input:disabled:checked~.custom-control-label:before{background-color:rgba(2,186,242,.5)}.custom-select{display:inline-block;width:100%;height:calc(2.25rem + 2px);padding:.375rem 1.75rem .375rem .75rem;font-weight:400;line-height:1.5;color:#495057;vertical-align:middle;background:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3E%3Cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3E%3C/svg%3E") no-repeat right .75rem center/8px 10px;background-color:#fff;border:1px solid #ced4da;border-radius:.25rem;-webkit-appearance:none;-moz-appearance:none;appearance:none}.custom-select:focus{border-color:#76defe;outline:0;-webkit-box-shadow:0 0 0 .2rem rgba(118,222,254,.5);box-shadow:0 0 0 .2rem rgba(118,222,254,.5)}.custom-select:focus::-ms-value{color:#495057;background-color:#fff}.custom-select[multiple],.custom-select[size]:not([size="1"]){height:auto;padding-right:.75rem;background-image:none}.custom-select:disabled{color:#6c757d;background-color:#e9ecef}.custom-select::-ms-expand{opacity:0}.custom-select-sm{height:calc(1.8125rem + 2px);padding-top:.25rem;padding-bottom:.25rem;padding-left:.5rem;font-size:.875rem}.custom-select-lg{height:calc(2.875rem + 2px);padding-top:.5rem;padding-bottom:.5rem;padding-left:1rem;font-size:1.25rem}.custom-file{display:inline-block;margin-bottom:0}.custom-file,.custom-file-input{position:relative;width:100%;height:calc(2.25rem + 2px)}.custom-file-input{z-index:2;margin:0;opacity:0}.custom-file-input:focus~.custom-file-label{border-color:#76defe;-webkit-box-shadow:0 0 0 .2rem rgba(2,186,242,.25);box-shadow:0 0 0 .2rem rgba(2,186,242,.25)}.custom-file-input:disabled~.custom-file-label{background-color:#e9ecef}.custom-file-input:lang(en)~.custom-file-label:after{content:"Browse"}.custom-file-input~.custom-file-label[data-browse]:after{content:attr(data-browse)}.custom-file-label{left:0;z-index:1;height:calc(2.25rem + 2px);font-weight:400;background-color:#fff;border:1px solid #ced4da;border-radius:.25rem}.custom-file-label,.custom-file-label:after{position:absolute;top:0;right:0;padding:.375rem .75rem;line-height:1.5;color:#495057}.custom-file-label:after{bottom:0;z-index:3;display:block;height:2.25rem;content:"Browse";background-color:#e9ecef;border-left:inherit;border-radius:0 .25rem .25rem 0}.custom-range{width:100%;height:1.4rem;padding:0;background-color:transparent;-webkit-appearance:none;-moz-appearance:none;appearance:none}.custom-range:focus{outline:none}.custom-range:focus::-webkit-slider-thumb{-webkit-box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(2,186,242,.25);box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(2,186,242,.25)}.custom-range:focus::-moz-range-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(2,186,242,.25)}.custom-range:focus::-ms-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(2,186,242,.25)}.custom-range::-moz-focus-outer{border:0}.custom-range::-webkit-slider-thumb{width:1rem;height:1rem;margin-top:-.25rem;background-color:#02baf2;border:0;border-radius:1rem;-webkit-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,-webkit-box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,-webkit-box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,-webkit-box-shadow .15s ease-in-out;-webkit-appearance:none;appearance:none}@media screen and (prefers-reduced-motion:reduce){.custom-range::-webkit-slider-thumb{-webkit-transition:none;transition:none}}.custom-range::-webkit-slider-thumb:active{background-color:#a8eafe}.custom-range::-webkit-slider-runnable-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.custom-range::-moz-range-thumb{width:1rem;height:1rem;background-color:#02baf2;border:0;border-radius:1rem;-webkit-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,-webkit-box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,-webkit-box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,-webkit-box-shadow .15s ease-in-out;-moz-appearance:none;appearance:none}@media screen and (prefers-reduced-motion:reduce){.custom-range::-moz-range-thumb{-webkit-transition:none;transition:none}}.custom-range::-moz-range-thumb:active{background-color:#a8eafe}.custom-range::-moz-range-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.custom-range::-ms-thumb{width:1rem;height:1rem;margin-top:0;margin-right:.2rem;margin-left:.2rem;background-color:#02baf2;border:0;border-radius:1rem;-webkit-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,-webkit-box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,-webkit-box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,-webkit-box-shadow .15s ease-in-out;appearance:none}@media screen and (prefers-reduced-motion:reduce){.custom-range::-ms-thumb{-webkit-transition:none;transition:none}}.custom-range::-ms-thumb:active{background-color:#a8eafe}.custom-range::-ms-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:transparent;border-color:transparent;border-width:.5rem}.custom-range::-ms-fill-lower,.custom-range::-ms-fill-upper{background-color:#dee2e6;border-radius:1rem}.custom-range::-ms-fill-upper{margin-right:15px}.custom-range:disabled::-webkit-slider-thumb{background-color:#adb5bd}.custom-range:disabled::-webkit-slider-runnable-track{cursor:default}.custom-range:disabled::-moz-range-thumb{background-color:#adb5bd}.custom-range:disabled::-moz-range-track{cursor:default}.custom-range:disabled::-ms-thumb{background-color:#adb5bd}.custom-control-label:before,.custom-file-label,.custom-select{-webkit-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,-webkit-box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,-webkit-box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,-webkit-box-shadow .15s ease-in-out}@media screen and (prefers-reduced-motion:reduce){.custom-control-label:before,.custom-file-label,.custom-select{-webkit-transition:none;transition:none}}.nav{display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding-left:0;margin-bottom:0;list-style:none}.nav-link{display:block;padding:.5rem 1rem}.nav-link:focus,.nav-link:hover{text-decoration:none}.nav-link.disabled{color:#6c757d;pointer-events:none;cursor:default}.nav-tabs{border-bottom:1px solid #dee2e6}.nav-tabs .nav-item{margin-bottom:-1px}.nav-tabs .nav-link{border:1px solid transparent;border-top-left-radius:.25rem;border-top-right-radius:.25rem}.nav-tabs .nav-link:focus,.nav-tabs .nav-link:hover{border-color:#e9ecef #e9ecef #dee2e6}.nav-tabs .nav-link.disabled{color:#6c757d;background-color:transparent;border-color:transparent}.nav-tabs .nav-item.show .nav-link,.nav-tabs .nav-link.active{color:#495057;background-color:#fff;border-color:#dee2e6 #dee2e6 #fff}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.nav-pills .nav-link{border-radius:.25rem}.nav-pills .nav-link.active,.nav-pills .show>.nav-link{color:#fff;background-color:#02baf2}.nav-fill .nav-item{-webkit-box-flex:1;-ms-flex:1 1 auto;flex:1 1 auto;text-align:center}.nav-justified .nav-item{-ms-flex-preferred-size:0;flex-basis:0;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;text-align:center}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.navbar{position:relative;padding:.5rem 1rem}.navbar,.navbar>.container,.navbar>.container-fluid{display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between}.navbar-brand{display:inline-block;padding-top:.3125rem;padding-bottom:.3125rem;margin-right:1rem;font-size:1.25rem;line-height:inherit;white-space:nowrap}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-nav{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;padding-left:0;margin-bottom:0;list-style:none}.navbar-nav .nav-link{padding-right:0;padding-left:0}.navbar-nav .dropdown-menu{position:static;float:none}.navbar-text{display:inline-block;padding-top:.5rem;padding-bottom:.5rem}.navbar-collapse{-ms-flex-preferred-size:100%;flex-basis:100%;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.navbar-toggler{padding:.25rem .75rem;font-size:1.25rem;line-height:1;background-color:transparent;border:1px solid transparent;border-radius:.25rem}.navbar-toggler:focus,.navbar-toggler:hover{text-decoration:none}.navbar-toggler:not(:disabled):not(.disabled){cursor:pointer}.navbar-toggler-icon{display:inline-block;width:1.5em;height:1.5em;vertical-align:middle;content:"";background:no-repeat 50%;background-size:100% 100%}@media (max-width:575.98px){.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid{padding-right:0;padding-left:0}}@media (min-width:576px){.navbar-expand-sm{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-sm,.navbar-expand-sm .navbar-nav{-webkit-box-orient:horizontal;-webkit-box-direction:normal}.navbar-expand-sm .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-sm .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-sm .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-sm .navbar-collapse{display:-webkit-box!important;display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-sm .navbar-toggler{display:none}}@media (max-width:767.98px){.navbar-expand-md>.container,.navbar-expand-md>.container-fluid{padding-right:0;padding-left:0}}@media (min-width:768px){.navbar-expand-md{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-md,.navbar-expand-md .navbar-nav{-webkit-box-orient:horizontal;-webkit-box-direction:normal}.navbar-expand-md .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-md .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-md .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-md>.container,.navbar-expand-md>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-md .navbar-collapse{display:-webkit-box!important;display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-md .navbar-toggler{display:none}}@media (max-width:991.98px){.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid{padding-right:0;padding-left:0}}@media (min-width:992px){.navbar-expand-lg{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-lg,.navbar-expand-lg .navbar-nav{-webkit-box-orient:horizontal;-webkit-box-direction:normal}.navbar-expand-lg .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-lg .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-lg .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-lg .navbar-collapse{display:-webkit-box!important;display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-lg .navbar-toggler{display:none}}@media (max-width:1199.98px){.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid{padding-right:0;padding-left:0}}@media (min-width:1200px){.navbar-expand-xl{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-xl,.navbar-expand-xl .navbar-nav{-webkit-box-orient:horizontal;-webkit-box-direction:normal}.navbar-expand-xl .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-xl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xl .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-xl .navbar-collapse{display:-webkit-box!important;display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-xl .navbar-toggler{display:none}}.navbar-expand{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-flow:row nowrap;flex-flow:row nowrap;-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand>.container,.navbar-expand>.container-fluid{padding-right:0;padding-left:0}.navbar-expand .navbar-nav{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row}.navbar-expand .navbar-nav .dropdown-menu{position:absolute}.navbar-expand .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand>.container,.navbar-expand>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand .navbar-collapse{display:-webkit-box!important;display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand .navbar-toggler{display:none}.navbar-light .navbar-brand,.navbar-light .navbar-brand:focus,.navbar-light .navbar-brand:hover{color:rgba(0,0,0,.9)}.navbar-light .navbar-nav .nav-link{color:rgba(0,0,0,.5)}.navbar-light .navbar-nav .nav-link:focus,.navbar-light .navbar-nav .nav-link:hover{color:rgba(0,0,0,.7)}.navbar-light .navbar-nav .nav-link.disabled{color:rgba(0,0,0,.3)}.navbar-light .navbar-nav .active>.nav-link,.navbar-light .navbar-nav .nav-link.active,.navbar-light .navbar-nav .nav-link.show,.navbar-light .navbar-nav .show>.nav-link{color:rgba(0,0,0,.9)}.navbar-light .navbar-toggler{color:rgba(0,0,0,.5);border-color:rgba(0,0,0,.1)}.navbar-light .navbar-toggler-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='rgba(0, 0, 0, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E")}.navbar-light .navbar-text{color:rgba(0,0,0,.5)}.navbar-light .navbar-text a,.navbar-light .navbar-text a:focus,.navbar-light .navbar-text a:hover{color:rgba(0,0,0,.9)}.navbar-dark .navbar-brand,.navbar-dark .navbar-brand:focus,.navbar-dark .navbar-brand:hover{color:#fff}.navbar-dark .navbar-nav .nav-link{color:hsla(0,0%,100%,.5)}.navbar-dark .navbar-nav .nav-link:focus,.navbar-dark .navbar-nav .nav-link:hover{color:hsla(0,0%,100%,.75)}.navbar-dark .navbar-nav .nav-link.disabled{color:hsla(0,0%,100%,.25)}.navbar-dark .navbar-nav .active>.nav-link,.navbar-dark .navbar-nav .nav-link.active,.navbar-dark .navbar-nav .nav-link.show,.navbar-dark .navbar-nav .show>.nav-link{color:#fff}.navbar-dark .navbar-toggler{color:hsla(0,0%,100%,.5);border-color:hsla(0,0%,100%,.1)}.navbar-dark .navbar-toggler-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='rgba(255, 255, 255, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E")}.navbar-dark .navbar-text{color:hsla(0,0%,100%,.5)}.navbar-dark .navbar-text a,.navbar-dark .navbar-text a:focus,.navbar-dark .navbar-text a:hover{color:#fff}.card{position:relative;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;min-width:0;word-wrap:break-word;background-color:#fff;background-clip:border-box;border:1px solid rgba(0,0,0,.125);border-radius:.25rem}.card>hr{margin-right:0;margin-left:0}.card>.list-group:first-child .list-group-item:first-child{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.card>.list-group:last-child .list-group-item:last-child{border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.card-body{-webkit-box-flex:1;-ms-flex:1 1 auto;flex:1 1 auto;padding:1.25rem}.card-title{margin-bottom:.75rem}.card-subtitle{margin-top:-.375rem}.card-subtitle,.card-text:last-child{margin-bottom:0}.card-link:hover{text-decoration:none}.card-link+.card-link{margin-left:1.25rem}.card-header{padding:.75rem 1.25rem;margin-bottom:0;color:inherit;background-color:rgba(0,0,0,.03);border-bottom:1px solid rgba(0,0,0,.125)}.card-header:first-child{border-radius:calc(.25rem - 1px) calc(.25rem - 1px) 0 0}.card-header+.list-group .list-group-item:first-child{border-top:0}.card-footer{padding:.75rem 1.25rem;background-color:rgba(0,0,0,.03);border-top:1px solid rgba(0,0,0,.125)}.card-footer:last-child{border-radius:0 0 calc(.25rem - 1px) calc(.25rem - 1px)}.card-header-tabs{margin-bottom:-.75rem;border-bottom:0}.card-header-pills,.card-header-tabs{margin-right:-.625rem;margin-left:-.625rem}.card-img-overlay{position:absolute;top:0;right:0;bottom:0;left:0;padding:1.25rem}.card-img{width:100%;border-radius:calc(.25rem - 1px)}.card-img-top{width:100%;border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.card-img-bottom{width:100%;border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.card-deck{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}.card-deck .card{margin-bottom:15px}@media (min-width:576px){.card-deck{-webkit-box-orient:horizontal;-ms-flex-flow:row wrap;flex-flow:row wrap;margin-right:-15px;margin-left:-15px}.card-deck,.card-deck .card{-webkit-box-direction:normal}.card-deck .card{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-flex:1;-ms-flex:1 0 0%;flex:1 0 0%;-webkit-box-orient:vertical;-ms-flex-direction:column;flex-direction:column;margin-right:15px;margin-bottom:0;margin-left:15px}}.card-group{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}.card-group>.card{margin-bottom:15px}@media (min-width:576px){.card-group{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-flow:row wrap;flex-flow:row wrap}.card-group>.card{-webkit-box-flex:1;-ms-flex:1 0 0%;flex:1 0 0%;margin-bottom:0}.card-group>.card+.card{margin-left:0;border-left:0}.card-group>.card:first-child{border-top-right-radius:0;border-bottom-right-radius:0}.card-group>.card:first-child .card-header,.card-group>.card:first-child .card-img-top{border-top-right-radius:0}.card-group>.card:first-child .card-footer,.card-group>.card:first-child .card-img-bottom{border-bottom-right-radius:0}.card-group>.card:last-child{border-top-left-radius:0;border-bottom-left-radius:0}.card-group>.card:last-child .card-header,.card-group>.card:last-child .card-img-top{border-top-left-radius:0}.card-group>.card:last-child .card-footer,.card-group>.card:last-child .card-img-bottom{border-bottom-left-radius:0}.card-group>.card:only-child{border-radius:.25rem}.card-group>.card:only-child .card-header,.card-group>.card:only-child .card-img-top{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.card-group>.card:only-child .card-footer,.card-group>.card:only-child .card-img-bottom{border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.card-group>.card:not(:first-child):not(:last-child):not(:only-child),.card-group>.card:not(:first-child):not(:last-child):not(:only-child) .card-footer,.card-group>.card:not(:first-child):not(:last-child):not(:only-child) .card-header,.card-group>.card:not(:first-child):not(:last-child):not(:only-child) .card-img-bottom,.card-group>.card:not(:first-child):not(:last-child):not(:only-child) .card-img-top{border-radius:0}}.card-columns .card{margin-bottom:.75rem}@media (min-width:576px){.card-columns{-webkit-column-count:3;column-count:3;-webkit-column-gap:1.25rem;column-gap:1.25rem;orphans:1;widows:1}.card-columns .card{display:inline-block;width:100%}}.accordion .card{overflow:hidden}.accordion .card:not(:first-of-type) .card-header:first-child{border-radius:0}.accordion .card:not(:first-of-type):not(:last-of-type){border-bottom:0;border-radius:0}.accordion .card:first-of-type{border-bottom:0;border-bottom-right-radius:0;border-bottom-left-radius:0}.accordion .card:last-of-type{border-top-left-radius:0;border-top-right-radius:0}.accordion .card .card-header{margin-bottom:-1px}.breadcrumb{display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding:.75rem 1rem;margin-bottom:1rem;list-style:none;background-color:#e9ecef;border-radius:.25rem}.breadcrumb-item+.breadcrumb-item{padding-left:.5rem}.breadcrumb-item+.breadcrumb-item:before{display:inline-block;padding-right:.5rem;color:#6c757d;content:"/"}.breadcrumb-item+.breadcrumb-item:hover:before{text-decoration:underline;text-decoration:none}.breadcrumb-item.active{color:#6c757d}.pagination{display:-webkit-box;display:-ms-flexbox;display:flex;padding-left:0;list-style:none;border-radius:.25rem}.page-link{position:relative;display:block;padding:.5rem .75rem;margin-left:-1px;line-height:1.25;color:#02baf2;background-color:#fff;border:1px solid #dee2e6}.page-link:hover{z-index:2;color:#0180a6;text-decoration:none;background-color:#e9ecef;border-color:#dee2e6}.page-link:focus{z-index:2;outline:0;-webkit-box-shadow:0 0 0 .2rem rgba(2,186,242,.25);box-shadow:0 0 0 .2rem rgba(2,186,242,.25)}.page-link:not(:disabled):not(.disabled){cursor:pointer}.page-item:first-child .page-link{margin-left:0;border-top-left-radius:.25rem;border-bottom-left-radius:.25rem}.page-item:last-child .page-link{border-top-right-radius:.25rem;border-bottom-right-radius:.25rem}.page-item.active .page-link{z-index:1;color:#fff;background-color:#02baf2;border-color:#02baf2}.page-item.disabled .page-link{color:#6c757d;pointer-events:none;cursor:auto;background-color:#fff;border-color:#dee2e6}.pagination-lg .page-link{padding:.75rem 1.5rem;font-size:1.25rem;line-height:1.5}.pagination-lg .page-item:first-child .page-link{border-top-left-radius:.3rem;border-bottom-left-radius:.3rem}.pagination-lg .page-item:last-child .page-link{border-top-right-radius:.3rem;border-bottom-right-radius:.3rem}.pagination-sm .page-link{padding:.25rem .5rem;font-size:.875rem;line-height:1.5}.pagination-sm .page-item:first-child .page-link{border-top-left-radius:.2rem;border-bottom-left-radius:.2rem}.pagination-sm .page-item:last-child .page-link{border-top-right-radius:.2rem;border-bottom-right-radius:.2rem}.badge{display:inline-block;padding:.25em .4em;font-size:75%;font-weight:700;line-height:1;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25rem}a.badge:focus,a.badge:hover{text-decoration:none}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.badge-pill{padding-right:.6em;padding-left:.6em;border-radius:10rem}.badge-primary{color:#fff;background-color:#02baf2}a.badge-primary:focus,a.badge-primary:hover{color:#fff;background-color:#0293bf}.badge-secondary{color:#fff;background-color:#6c757d}a.badge-secondary:focus,a.badge-secondary:hover{color:#fff;background-color:#545b62}.badge-success{color:#fff;background-color:#28a745}a.badge-success:focus,a.badge-success:hover{color:#fff;background-color:#1e7e34}.badge-info{color:#fff;background-color:#17a2b8}a.badge-info:focus,a.badge-info:hover{color:#fff;background-color:#117a8b}.badge-warning{color:#212529;background-color:#ffc107}a.badge-warning:focus,a.badge-warning:hover{color:#212529;background-color:#d39e00}.badge-danger{color:#fff;background-color:#f84545}a.badge-danger:focus,a.badge-danger:hover{color:#fff;background-color:#f61414}.badge-light{color:#212529;background-color:#f8f9fa}a.badge-light:focus,a.badge-light:hover{color:#212529;background-color:#dae0e5}.badge-dark{color:#fff;background-color:#343a40}a.badge-dark:focus,a.badge-dark:hover{color:#fff;background-color:#1d2124}.jumbotron{padding:2rem 1rem;margin-bottom:2rem;background-color:#e9ecef;border-radius:.3rem}@media (min-width:576px){.jumbotron{padding:4rem 2rem}}.jumbotron-fluid{padding-right:0;padding-left:0;border-radius:0}.alert{position:relative;padding:.75rem 1.25rem;margin-bottom:1rem;border:1px solid transparent;border-radius:.25rem}.alert-heading{color:inherit}.alert-link{font-weight:700}.alert-dismissible{padding-right:4rem}.alert-dismissible .close{position:absolute;top:0;right:0;padding:.75rem 1.25rem;color:inherit}.alert-primary{color:#01617e;background-color:#ccf1fc;border-color:#b8ecfb}.alert-primary hr{border-top-color:#a0e6fa}.alert-primary .alert-link{color:#013a4b}.alert-secondary{color:#383d41;background-color:#e2e3e5;border-color:#d6d8db}.alert-secondary hr{border-top-color:#c8cbcf}.alert-secondary .alert-link{color:#202326}.alert-success{color:#155724;background-color:#d4edda;border-color:#c3e6cb}.alert-success hr{border-top-color:#b1dfbb}.alert-success .alert-link{color:#0b2e13}.alert-info{color:#0c5460;background-color:#d1ecf1;border-color:#bee5eb}.alert-info hr{border-top-color:#abdde5}.alert-info .alert-link{color:#062c33}.alert-warning{color:#856404;background-color:#fff3cd;border-color:#ffeeba}.alert-warning hr{border-top-color:#ffe8a1}.alert-warning .alert-link{color:#533f03}.alert-danger{color:#812424;background-color:#fedada;border-color:#fdcbcb}.alert-danger hr{border-top-color:#fcb2b2}.alert-danger .alert-link{color:#591919}.alert-light{color:#818182;background-color:#fefefe;border-color:#fdfdfe}.alert-light hr{border-top-color:#ececf6}.alert-light .alert-link{color:#686868}.alert-dark{color:#1b1e21;background-color:#d6d8d9;border-color:#c6c8ca}.alert-dark hr{border-top-color:#b9bbbe}.alert-dark .alert-link{color:#040505}@-webkit-keyframes progress-bar-stripes{0%{background-position:1rem 0}to{background-position:0 0}}@keyframes progress-bar-stripes{0%{background-position:1rem 0}to{background-position:0 0}}.progress{height:1rem;overflow:hidden;font-size:.75rem;background-color:#e9ecef;border-radius:.25rem}.progress,.progress-bar{display:-webkit-box;display:-ms-flexbox;display:flex}.progress-bar{-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;color:#fff;text-align:center;white-space:nowrap;background-color:#02baf2;-webkit-transition:width .6s ease;transition:width .6s ease}@media screen and (prefers-reduced-motion:reduce){.progress-bar{-webkit-transition:none;transition:none}}.progress-bar-striped{background-image:linear-gradient(45deg,hsla(0,0%,100%,.15) 25%,transparent 0,transparent 50%,hsla(0,0%,100%,.15) 0,hsla(0,0%,100%,.15) 75%,transparent 0,transparent);background-size:1rem 1rem}.progress-bar-animated{-webkit-animation:progress-bar-stripes 1s linear infinite;animation:progress-bar-stripes 1s linear infinite}.media{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:start;-ms-flex-align:start;align-items:flex-start}.media-body{-webkit-box-flex:1;-ms-flex:1;flex:1}.list-group{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;padding-left:0;margin-bottom:0}.list-group-item-action{width:100%;color:#495057;text-align:inherit}.list-group-item-action:focus,.list-group-item-action:hover{color:#495057;text-decoration:none;background-color:#f8f9fa}.list-group-item-action:active{color:#212529;background-color:#e9ecef}.list-group-item{position:relative;display:block;padding:.75rem 1.25rem;margin-bottom:-1px;background-color:#fff;border:1px solid rgba(0,0,0,.125)}.list-group-item:first-child{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.list-group-item:focus,.list-group-item:hover{z-index:1;text-decoration:none}.list-group-item.disabled,.list-group-item:disabled{color:#6c757d;pointer-events:none;background-color:#fff}.list-group-item.active{z-index:2;color:#fff;background-color:#02baf2;border-color:#02baf2}.list-group-flush .list-group-item{border-right:0;border-left:0;border-radius:0}.list-group-flush .list-group-item:last-child{margin-bottom:-1px}.list-group-flush:first-child .list-group-item:first-child{border-top:0}.list-group-flush:last-child .list-group-item:last-child{margin-bottom:0;border-bottom:0}.list-group-item-primary{color:#01617e;background-color:#b8ecfb}.list-group-item-primary.list-group-item-action:focus,.list-group-item-primary.list-group-item-action:hover{color:#01617e;background-color:#a0e6fa}.list-group-item-primary.list-group-item-action.active{color:#fff;background-color:#01617e;border-color:#01617e}.list-group-item-secondary{color:#383d41;background-color:#d6d8db}.list-group-item-secondary.list-group-item-action:focus,.list-group-item-secondary.list-group-item-action:hover{color:#383d41;background-color:#c8cbcf}.list-group-item-secondary.list-group-item-action.active{color:#fff;background-color:#383d41;border-color:#383d41}.list-group-item-success{color:#155724;background-color:#c3e6cb}.list-group-item-success.list-group-item-action:focus,.list-group-item-success.list-group-item-action:hover{color:#155724;background-color:#b1dfbb}.list-group-item-success.list-group-item-action.active{color:#fff;background-color:#155724;border-color:#155724}.list-group-item-info{color:#0c5460;background-color:#bee5eb}.list-group-item-info.list-group-item-action:focus,.list-group-item-info.list-group-item-action:hover{color:#0c5460;background-color:#abdde5}.list-group-item-info.list-group-item-action.active{color:#fff;background-color:#0c5460;border-color:#0c5460}.list-group-item-warning{color:#856404;background-color:#ffeeba}.list-group-item-warning.list-group-item-action:focus,.list-group-item-warning.list-group-item-action:hover{color:#856404;background-color:#ffe8a1}.list-group-item-warning.list-group-item-action.active{color:#fff;background-color:#856404;border-color:#856404}.list-group-item-danger{color:#812424;background-color:#fdcbcb}.list-group-item-danger.list-group-item-action:focus,.list-group-item-danger.list-group-item-action:hover{color:#812424;background-color:#fcb2b2}.list-group-item-danger.list-group-item-action.active{color:#fff;background-color:#812424;border-color:#812424}.list-group-item-light{color:#818182;background-color:#fdfdfe}.list-group-item-light.list-group-item-action:focus,.list-group-item-light.list-group-item-action:hover{color:#818182;background-color:#ececf6}.list-group-item-light.list-group-item-action.active{color:#fff;background-color:#818182;border-color:#818182}.list-group-item-dark{color:#1b1e21;background-color:#c6c8ca}.list-group-item-dark.list-group-item-action:focus,.list-group-item-dark.list-group-item-action:hover{color:#1b1e21;background-color:#b9bbbe}.list-group-item-dark.list-group-item-action.active{color:#fff;background-color:#1b1e21;border-color:#1b1e21}.close{float:right;font-size:1.5rem;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;opacity:.5}.close:hover{color:#000;text-decoration:none}.close:not(:disabled):not(.disabled){cursor:pointer}.close:not(:disabled):not(.disabled):focus,.close:not(:disabled):not(.disabled):hover{opacity:.75}button.close{padding:0;background-color:transparent;border:0;-webkit-appearance:none;-moz-appearance:none;appearance:none}a.close.disabled{pointer-events:none}.toast{max-width:350px;overflow:hidden;font-size:.875rem;background-color:hsla(0,0%,100%,.85);background-clip:padding-box;border:1px solid rgba(0,0,0,.1);border-radius:.25rem;-webkit-box-shadow:0 .25rem .75rem rgba(0,0,0,.1);box-shadow:0 .25rem .75rem rgba(0,0,0,.1);-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);opacity:0}.toast:not(:last-child){margin-bottom:.75rem}.toast.showing{opacity:1}.toast.show{display:block;opacity:1}.toast.hide{display:none}.toast-header{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;padding:.25rem .75rem;color:#6c757d;background-color:hsla(0,0%,100%,.85);background-clip:padding-box;border-bottom:1px solid rgba(0,0,0,.05)}.toast-body{padding:.75rem}.modal-open{overflow:hidden}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal{position:fixed;top:0;left:0;z-index:1050;display:none;width:100%;height:100%;overflow:hidden;outline:0}.modal-dialog{position:relative;width:auto;margin:.5rem;pointer-events:none}.modal.fade .modal-dialog{-webkit-transition:-webkit-transform .3s ease-out;transition:-webkit-transform .3s ease-out;transition:transform .3s ease-out;transition:transform .3s ease-out,-webkit-transform .3s ease-out;-webkit-transform:translateY(-50px);transform:translateY(-50px)}@media screen and (prefers-reduced-motion:reduce){.modal.fade .modal-dialog{-webkit-transition:none;transition:none}}.modal.show .modal-dialog{-webkit-transform:none;transform:none}.modal-dialog-centered{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;min-height:calc(100% - 1rem)}.modal-dialog-centered:before{display:block;height:calc(100vh - 1rem);content:""}.modal-content{position:relative;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;width:100%;pointer-events:auto;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem;outline:0}.modal-backdrop{position:fixed;top:0;left:0;z-index:1040;width:100vw;height:100vh;background-color:#000}.modal-backdrop.fade{opacity:0}.modal-backdrop.show{opacity:.5}.modal-header{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:start;-ms-flex-align:start;align-items:flex-start;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;padding:1rem 1rem;border-bottom:1px solid #e9ecef;border-top-left-radius:.3rem;border-top-right-radius:.3rem}.modal-header .close{padding:1rem 1rem;margin:-1rem -1rem -1rem auto}.modal-title{margin-bottom:0;line-height:1.5}.modal-body{position:relative;-webkit-box-flex:1;-ms-flex:1 1 auto;flex:1 1 auto;padding:1rem}.modal-footer{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:end;-ms-flex-pack:end;justify-content:flex-end;padding:1rem;border-top:1px solid #e9ecef;border-bottom-right-radius:.3rem;border-bottom-left-radius:.3rem}.modal-footer>:not(:first-child){margin-left:.25rem}.modal-footer>:not(:last-child){margin-right:.25rem}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:576px){.modal-dialog{max-width:500px;margin:1.75rem auto}.modal-dialog-centered{min-height:calc(100% - 3.5rem)}.modal-dialog-centered:before{height:calc(100vh - 3.5rem)}.modal-sm{max-width:300px}}@media (min-width:992px){.modal-lg,.modal-xl{max-width:800px}}@media (min-width:1200px){.modal-xl{max-width:1140px}}.tooltip{position:absolute;z-index:1070;display:block;margin:0;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;opacity:0}.tooltip.show{opacity:.9}.tooltip .arrow{position:absolute;display:block;width:.8rem;height:.4rem}.tooltip .arrow:before{position:absolute;content:"";border-color:transparent;border-style:solid}.bs-tooltip-auto[x-placement^=top],.bs-tooltip-top{padding:.4rem 0}.bs-tooltip-auto[x-placement^=top] .arrow,.bs-tooltip-top .arrow{bottom:0}.bs-tooltip-auto[x-placement^=top] .arrow:before,.bs-tooltip-top .arrow:before{top:0;border-width:.4rem .4rem 0;border-top-color:#000}.bs-tooltip-auto[x-placement^=right],.bs-tooltip-right{padding:0 .4rem}.bs-tooltip-auto[x-placement^=right] .arrow,.bs-tooltip-right .arrow{left:0;width:.4rem;height:.8rem}.bs-tooltip-auto[x-placement^=right] .arrow:before,.bs-tooltip-right .arrow:before{right:0;border-width:.4rem .4rem .4rem 0;border-right-color:#000}.bs-tooltip-auto[x-placement^=bottom],.bs-tooltip-bottom{padding:.4rem 0}.bs-tooltip-auto[x-placement^=bottom] .arrow,.bs-tooltip-bottom .arrow{top:0}.bs-tooltip-auto[x-placement^=bottom] .arrow:before,.bs-tooltip-bottom .arrow:before{bottom:0;border-width:0 .4rem .4rem;border-bottom-color:#000}.bs-tooltip-auto[x-placement^=left],.bs-tooltip-left{padding:0 .4rem}.bs-tooltip-auto[x-placement^=left] .arrow,.bs-tooltip-left .arrow{right:0;width:.4rem;height:.8rem}.bs-tooltip-auto[x-placement^=left] .arrow:before,.bs-tooltip-left .arrow:before{left:0;border-width:.4rem 0 .4rem .4rem;border-left-color:#000}.tooltip-inner{max-width:200px;padding:.25rem .5rem;color:#fff;text-align:center;background-color:#000;border-radius:.25rem}.popover{top:0;left:0;z-index:1060;max-width:276px;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem}.popover,.popover .arrow{position:absolute;display:block}.popover .arrow{width:1rem;height:.5rem;margin:0 .3rem}.popover .arrow:after,.popover .arrow:before{position:absolute;display:block;content:"";border-color:transparent;border-style:solid}.bs-popover-auto[x-placement^=top],.bs-popover-top{margin-bottom:.5rem}.bs-popover-auto[x-placement^=top] .arrow,.bs-popover-top .arrow{bottom:calc(-.5rem + -1px)}.bs-popover-auto[x-placement^=top] .arrow:after,.bs-popover-auto[x-placement^=top] .arrow:before,.bs-popover-top .arrow:after,.bs-popover-top .arrow:before{border-width:.5rem .5rem 0}.bs-popover-auto[x-placement^=top] .arrow:before,.bs-popover-top .arrow:before{bottom:0;border-top-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=top] .arrow:after,.bs-popover-top .arrow:after{bottom:1px;border-top-color:#fff}.bs-popover-auto[x-placement^=right],.bs-popover-right{margin-left:.5rem}.bs-popover-auto[x-placement^=right] .arrow,.bs-popover-right .arrow{left:calc(-.5rem + -1px);width:.5rem;height:1rem;margin:.3rem 0}.bs-popover-auto[x-placement^=right] .arrow:after,.bs-popover-auto[x-placement^=right] .arrow:before,.bs-popover-right .arrow:after,.bs-popover-right .arrow:before{border-width:.5rem .5rem .5rem 0}.bs-popover-auto[x-placement^=right] .arrow:before,.bs-popover-right .arrow:before{left:0;border-right-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=right] .arrow:after,.bs-popover-right .arrow:after{left:1px;border-right-color:#fff}.bs-popover-auto[x-placement^=bottom],.bs-popover-bottom{margin-top:.5rem}.bs-popover-auto[x-placement^=bottom] .arrow,.bs-popover-bottom .arrow{top:calc(-.5rem + -1px)}.bs-popover-auto[x-placement^=bottom] .arrow:after,.bs-popover-auto[x-placement^=bottom] .arrow:before,.bs-popover-bottom .arrow:after,.bs-popover-bottom .arrow:before{border-width:0 .5rem .5rem .5rem}.bs-popover-auto[x-placement^=bottom] .arrow:before,.bs-popover-bottom .arrow:before{top:0;border-bottom-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=bottom] .arrow:after,.bs-popover-bottom .arrow:after{top:1px;border-bottom-color:#fff}.bs-popover-auto[x-placement^=bottom] .popover-header:before,.bs-popover-bottom .popover-header:before{position:absolute;top:0;left:50%;display:block;width:1rem;margin-left:-.5rem;content:"";border-bottom:1px solid #f7f7f7}.bs-popover-auto[x-placement^=left],.bs-popover-left{margin-right:.5rem}.bs-popover-auto[x-placement^=left] .arrow,.bs-popover-left .arrow{right:calc(-.5rem + -1px);width:.5rem;height:1rem;margin:.3rem 0}.bs-popover-auto[x-placement^=left] .arrow:after,.bs-popover-auto[x-placement^=left] .arrow:before,.bs-popover-left .arrow:after,.bs-popover-left .arrow:before{border-width:.5rem 0 .5rem .5rem}.bs-popover-auto[x-placement^=left] .arrow:before,.bs-popover-left .arrow:before{right:0;border-left-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=left] .arrow:after,.bs-popover-left .arrow:after{right:1px;border-left-color:#fff}.popover-header{padding:.5rem .75rem;margin-bottom:0;font-size:1rem;color:inherit;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-top-left-radius:calc(.3rem - 1px);border-top-right-radius:calc(.3rem - 1px)}.popover-header:empty{display:none}.popover-body{padding:.5rem .75rem;color:#212529}.carousel{position:relative}.carousel.pointer-event{-ms-touch-action:pan-y;touch-action:pan-y}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner:after{display:block;clear:both;content:""}.carousel-item{position:relative;display:none;float:left;width:100%;margin-right:-100%;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-transition:-webkit-transform .6s ease-in-out;transition:-webkit-transform .6s ease-in-out;transition:transform .6s ease-in-out;transition:transform .6s ease-in-out,-webkit-transform .6s ease-in-out}@media screen and (prefers-reduced-motion:reduce){.carousel-item{-webkit-transition:none;transition:none}}.carousel-item-next,.carousel-item-prev,.carousel-item.active{display:block}.active.carousel-item-right,.carousel-item-next:not(.carousel-item-left){-webkit-transform:translateX(100%);transform:translateX(100%)}.active.carousel-item-left,.carousel-item-prev:not(.carousel-item-right){-webkit-transform:translateX(-100%);transform:translateX(-100%)}.carousel-fade .carousel-item{opacity:0;-webkit-transition-property:opacity;transition-property:opacity;-webkit-transform:none;transform:none}.carousel-fade .carousel-item-next.carousel-item-left,.carousel-fade .carousel-item-prev.carousel-item-right,.carousel-fade .carousel-item.active{z-index:1;opacity:1}.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-right{z-index:0;opacity:0;-webkit-transition:opacity 0s .6s;transition:opacity 0s .6s}@media screen and (prefers-reduced-motion:reduce){.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-right{-webkit-transition:none;transition:none}}.carousel-control-next,.carousel-control-prev{position:absolute;top:0;bottom:0;z-index:1;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;width:15%;color:#fff;text-align:center;opacity:.5;-webkit-transition:opacity .15s ease;transition:opacity .15s ease}@media screen and (prefers-reduced-motion:reduce){.carousel-control-next,.carousel-control-prev{-webkit-transition:none;transition:none}}.carousel-control-next:focus,.carousel-control-next:hover,.carousel-control-prev:focus,.carousel-control-prev:hover{color:#fff;text-decoration:none;outline:0;opacity:.9}.carousel-control-prev{left:0}.carousel-control-next{right:0}.carousel-control-next-icon,.carousel-control-prev-icon{display:inline-block;width:20px;height:20px;background:transparent no-repeat 50%;background-size:100% 100%}.carousel-control-prev-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 8 8'%3E%3Cpath d='M5.25 0l-4 4 4 4 1.5-1.5L4.25 4l2.5-2.5L5.25 0z'/%3E%3C/svg%3E")}.carousel-control-next-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 8 8'%3E%3Cpath d='M2.75 0l-1.5 1.5L3.75 4l-2.5 2.5L2.75 8l4-4-4-4z'/%3E%3C/svg%3E")}.carousel-indicators{position:absolute;right:0;bottom:0;left:0;z-index:15;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;padding-left:0;margin-right:15%;margin-left:15%;list-style:none}.carousel-indicators li{-webkit-box-sizing:content-box;box-sizing:content-box;-webkit-box-flex:0;-ms-flex:0 1 auto;flex:0 1 auto;width:30px;height:3px;margin-right:3px;margin-left:3px;text-indent:-999px;cursor:pointer;background-color:#fff;background-clip:padding-box;border-top:10px solid transparent;border-bottom:10px solid transparent;opacity:.5;-webkit-transition:opacity .6s ease;transition:opacity .6s ease}@media screen and (prefers-reduced-motion:reduce){.carousel-indicators li{-webkit-transition:none;transition:none}}.carousel-indicators .active{opacity:1}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center}@-webkit-keyframes spinner-border{to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}@keyframes spinner-border{to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}.spinner-border{display:inline-block;width:2rem;height:2rem;vertical-align:text-bottom;border:.25em solid currentColor;border-right-color:transparent;border-radius:50%;-webkit-animation:spinner-border .75s linear infinite;animation:spinner-border .75s linear infinite}.spinner-border-sm{width:1rem;height:1rem;border-width:.2em}@-webkit-keyframes spinner-grow{0%{-webkit-transform:scale(0);transform:scale(0)}50%{opacity:1}}@keyframes spinner-grow{0%{-webkit-transform:scale(0);transform:scale(0)}50%{opacity:1}}.spinner-grow{display:inline-block;width:2rem;height:2rem;vertical-align:text-bottom;background-color:currentColor;border-radius:50%;opacity:0;-webkit-animation:spinner-grow .75s linear infinite;animation:spinner-grow .75s linear infinite}.spinner-grow-sm{width:1rem;height:1rem}.align-baseline{vertical-align:baseline!important}.align-top{vertical-align:top!important}.align-middle{vertical-align:middle!important}.align-bottom{vertical-align:bottom!important}.align-text-bottom{vertical-align:text-bottom!important}.align-text-top{vertical-align:text-top!important}.bg-primary{background-color:#02baf2!important}a.bg-primary:focus,a.bg-primary:hover,button.bg-primary:focus,button.bg-primary:hover{background-color:#0293bf!important}.bg-secondary{background-color:#6c757d!important}a.bg-secondary:focus,a.bg-secondary:hover,button.bg-secondary:focus,button.bg-secondary:hover{background-color:#545b62!important}.bg-success{background-color:#28a745!important}a.bg-success:focus,a.bg-success:hover,button.bg-success:focus,button.bg-success:hover{background-color:#1e7e34!important}.bg-info{background-color:#17a2b8!important}a.bg-info:focus,a.bg-info:hover,button.bg-info:focus,button.bg-info:hover{background-color:#117a8b!important}.bg-warning{background-color:#ffc107!important}a.bg-warning:focus,a.bg-warning:hover,button.bg-warning:focus,button.bg-warning:hover{background-color:#d39e00!important}.bg-danger{background-color:#f84545!important}a.bg-danger:focus,a.bg-danger:hover,button.bg-danger:focus,button.bg-danger:hover{background-color:#f61414!important}.bg-light{background-color:#f8f9fa!important}a.bg-light:focus,a.bg-light:hover,button.bg-light:focus,button.bg-light:hover{background-color:#dae0e5!important}.bg-dark{background-color:#343a40!important}a.bg-dark:focus,a.bg-dark:hover,button.bg-dark:focus,button.bg-dark:hover{background-color:#1d2124!important}.bg-white{background-color:#fff!important}.bg-transparent{background-color:transparent!important}.border{border:1px solid #dee2e6!important}.border-top{border-top:1px solid #dee2e6!important}.border-right{border-right:1px solid #dee2e6!important}.border-bottom{border-bottom:1px solid #dee2e6!important}.border-left{border-left:1px solid #dee2e6!important}.border-0{border:0!important}.border-top-0{border-top:0!important}.border-right-0{border-right:0!important}.border-bottom-0{border-bottom:0!important}.border-left-0{border-left:0!important}.border-primary{border-color:#02baf2!important}.border-secondary{border-color:#6c757d!important}.border-success{border-color:#28a745!important}.border-info{border-color:#17a2b8!important}.border-warning{border-color:#ffc107!important}.border-danger{border-color:#f84545!important}.border-light{border-color:#f8f9fa!important}.border-dark{border-color:#343a40!important}.border-white{border-color:#fff!important}.rounded{border-radius:.25rem!important}.rounded-top{border-top-left-radius:.25rem!important}.rounded-right,.rounded-top{border-top-right-radius:.25rem!important}.rounded-bottom,.rounded-right{border-bottom-right-radius:.25rem!important}.rounded-bottom,.rounded-left{border-bottom-left-radius:.25rem!important}.rounded-left{border-top-left-radius:.25rem!important}.rounded-circle{border-radius:50%!important}.rounded-pill{border-radius:50rem!important}.rounded-0{border-radius:0!important}.clearfix:after{display:block;clear:both;content:""}.d-none{display:none!important}.d-inline{display:inline!important}.d-inline-block{display:inline-block!important}.d-block{display:block!important}.d-table{display:table!important}.d-table-row{display:table-row!important}.d-table-cell{display:table-cell!important}.d-flex{display:-webkit-box!important;display:-ms-flexbox!important;display:flex!important}.d-inline-flex{display:-webkit-inline-box!important;display:-ms-inline-flexbox!important;display:inline-flex!important}@media (min-width:576px){.d-sm-none{display:none!important}.d-sm-inline{display:inline!important}.d-sm-inline-block{display:inline-block!important}.d-sm-block{display:block!important}.d-sm-table{display:table!important}.d-sm-table-row{display:table-row!important}.d-sm-table-cell{display:table-cell!important}.d-sm-flex{display:-webkit-box!important;display:-ms-flexbox!important;display:flex!important}.d-sm-inline-flex{display:-webkit-inline-box!important;display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:768px){.d-md-none{display:none!important}.d-md-inline{display:inline!important}.d-md-inline-block{display:inline-block!important}.d-md-block{display:block!important}.d-md-table{display:table!important}.d-md-table-row{display:table-row!important}.d-md-table-cell{display:table-cell!important}.d-md-flex{display:-webkit-box!important;display:-ms-flexbox!important;display:flex!important}.d-md-inline-flex{display:-webkit-inline-box!important;display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:992px){.d-lg-none{display:none!important}.d-lg-inline{display:inline!important}.d-lg-inline-block{display:inline-block!important}.d-lg-block{display:block!important}.d-lg-table{display:table!important}.d-lg-table-row{display:table-row!important}.d-lg-table-cell{display:table-cell!important}.d-lg-flex{display:-webkit-box!important;display:-ms-flexbox!important;display:flex!important}.d-lg-inline-flex{display:-webkit-inline-box!important;display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:1200px){.d-xl-none{display:none!important}.d-xl-inline{display:inline!important}.d-xl-inline-block{display:inline-block!important}.d-xl-block{display:block!important}.d-xl-table{display:table!important}.d-xl-table-row{display:table-row!important}.d-xl-table-cell{display:table-cell!important}.d-xl-flex{display:-webkit-box!important;display:-ms-flexbox!important;display:flex!important}.d-xl-inline-flex{display:-webkit-inline-box!important;display:-ms-inline-flexbox!important;display:inline-flex!important}}@media print{.d-print-none{display:none!important}.d-print-inline{display:inline!important}.d-print-inline-block{display:inline-block!important}.d-print-block{display:block!important}.d-print-table{display:table!important}.d-print-table-row{display:table-row!important}.d-print-table-cell{display:table-cell!important}.d-print-flex{display:-webkit-box!important;display:-ms-flexbox!important;display:flex!important}.d-print-inline-flex{display:-webkit-inline-box!important;display:-ms-inline-flexbox!important;display:inline-flex!important}}.embed-responsive{position:relative;display:block;width:100%;padding:0;overflow:hidden}.embed-responsive:before{display:block;content:""}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-21by9:before{padding-top:42.85714%}.embed-responsive-16by9:before{padding-top:56.25%}.embed-responsive-3by4:before{padding-top:133.33333%}.embed-responsive-1by1:before{padding-top:100%}.flex-row{-webkit-box-orient:horizontal!important;-ms-flex-direction:row!important;flex-direction:row!important}.flex-column,.flex-row{-webkit-box-direction:normal!important}.flex-column{-webkit-box-orient:vertical!important;-ms-flex-direction:column!important;flex-direction:column!important}.flex-row-reverse{-webkit-box-orient:horizontal!important;-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-column-reverse,.flex-row-reverse{-webkit-box-direction:reverse!important}.flex-column-reverse{-webkit-box-orient:vertical!important;-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-fill{-webkit-box-flex:1!important;-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-grow-0{-webkit-box-flex:0!important;-ms-flex-positive:0!important;flex-grow:0!important}.flex-grow-1{-webkit-box-flex:1!important;-ms-flex-positive:1!important;flex-grow:1!important}.flex-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-start{-webkit-box-pack:start!important;-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-end{-webkit-box-pack:end!important;-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-center{-webkit-box-pack:center!important;-ms-flex-pack:center!important;justify-content:center!important}.justify-content-between{-webkit-box-pack:justify!important;-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-start{-webkit-box-align:start!important;-ms-flex-align:start!important;align-items:flex-start!important}.align-items-end{-webkit-box-align:end!important;-ms-flex-align:end!important;align-items:flex-end!important}.align-items-center{-webkit-box-align:center!important;-ms-flex-align:center!important;align-items:center!important}.align-items-baseline{-webkit-box-align:baseline!important;-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-stretch{-webkit-box-align:stretch!important;-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}@media (min-width:576px){.flex-sm-row{-webkit-box-orient:horizontal!important;-ms-flex-direction:row!important;flex-direction:row!important}.flex-sm-column,.flex-sm-row{-webkit-box-direction:normal!important}.flex-sm-column{-webkit-box-orient:vertical!important;-ms-flex-direction:column!important;flex-direction:column!important}.flex-sm-row-reverse{-webkit-box-orient:horizontal!important;-webkit-box-direction:reverse!important;-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-sm-column-reverse{-webkit-box-orient:vertical!important;-webkit-box-direction:reverse!important;-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-sm-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-sm-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-sm-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-sm-fill{-webkit-box-flex:1!important;-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-sm-grow-0{-webkit-box-flex:0!important;-ms-flex-positive:0!important;flex-grow:0!important}.flex-sm-grow-1{-webkit-box-flex:1!important;-ms-flex-positive:1!important;flex-grow:1!important}.flex-sm-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-sm-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-sm-start{-webkit-box-pack:start!important;-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-sm-end{-webkit-box-pack:end!important;-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-sm-center{-webkit-box-pack:center!important;-ms-flex-pack:center!important;justify-content:center!important}.justify-content-sm-between{-webkit-box-pack:justify!important;-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-sm-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-sm-start{-webkit-box-align:start!important;-ms-flex-align:start!important;align-items:flex-start!important}.align-items-sm-end{-webkit-box-align:end!important;-ms-flex-align:end!important;align-items:flex-end!important}.align-items-sm-center{-webkit-box-align:center!important;-ms-flex-align:center!important;align-items:center!important}.align-items-sm-baseline{-webkit-box-align:baseline!important;-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-sm-stretch{-webkit-box-align:stretch!important;-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-sm-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-sm-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-sm-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-sm-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-sm-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-sm-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-sm-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-sm-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-sm-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-sm-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-sm-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-sm-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:768px){.flex-md-row{-webkit-box-orient:horizontal!important;-ms-flex-direction:row!important;flex-direction:row!important}.flex-md-column,.flex-md-row{-webkit-box-direction:normal!important}.flex-md-column{-webkit-box-orient:vertical!important;-ms-flex-direction:column!important;flex-direction:column!important}.flex-md-row-reverse{-webkit-box-orient:horizontal!important;-webkit-box-direction:reverse!important;-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-md-column-reverse{-webkit-box-orient:vertical!important;-webkit-box-direction:reverse!important;-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-md-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-md-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-md-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-md-fill{-webkit-box-flex:1!important;-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-md-grow-0{-webkit-box-flex:0!important;-ms-flex-positive:0!important;flex-grow:0!important}.flex-md-grow-1{-webkit-box-flex:1!important;-ms-flex-positive:1!important;flex-grow:1!important}.flex-md-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-md-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-md-start{-webkit-box-pack:start!important;-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-md-end{-webkit-box-pack:end!important;-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-md-center{-webkit-box-pack:center!important;-ms-flex-pack:center!important;justify-content:center!important}.justify-content-md-between{-webkit-box-pack:justify!important;-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-md-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-md-start{-webkit-box-align:start!important;-ms-flex-align:start!important;align-items:flex-start!important}.align-items-md-end{-webkit-box-align:end!important;-ms-flex-align:end!important;align-items:flex-end!important}.align-items-md-center{-webkit-box-align:center!important;-ms-flex-align:center!important;align-items:center!important}.align-items-md-baseline{-webkit-box-align:baseline!important;-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-md-stretch{-webkit-box-align:stretch!important;-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-md-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-md-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-md-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-md-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-md-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-md-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-md-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-md-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-md-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-md-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-md-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-md-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:992px){.flex-lg-row{-webkit-box-orient:horizontal!important;-ms-flex-direction:row!important;flex-direction:row!important}.flex-lg-column,.flex-lg-row{-webkit-box-direction:normal!important}.flex-lg-column{-webkit-box-orient:vertical!important;-ms-flex-direction:column!important;flex-direction:column!important}.flex-lg-row-reverse{-webkit-box-orient:horizontal!important;-webkit-box-direction:reverse!important;-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-lg-column-reverse{-webkit-box-orient:vertical!important;-webkit-box-direction:reverse!important;-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-lg-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-lg-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-lg-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-lg-fill{-webkit-box-flex:1!important;-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-lg-grow-0{-webkit-box-flex:0!important;-ms-flex-positive:0!important;flex-grow:0!important}.flex-lg-grow-1{-webkit-box-flex:1!important;-ms-flex-positive:1!important;flex-grow:1!important}.flex-lg-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-lg-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-lg-start{-webkit-box-pack:start!important;-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-lg-end{-webkit-box-pack:end!important;-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-lg-center{-webkit-box-pack:center!important;-ms-flex-pack:center!important;justify-content:center!important}.justify-content-lg-between{-webkit-box-pack:justify!important;-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-lg-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-lg-start{-webkit-box-align:start!important;-ms-flex-align:start!important;align-items:flex-start!important}.align-items-lg-end{-webkit-box-align:end!important;-ms-flex-align:end!important;align-items:flex-end!important}.align-items-lg-center{-webkit-box-align:center!important;-ms-flex-align:center!important;align-items:center!important}.align-items-lg-baseline{-webkit-box-align:baseline!important;-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-lg-stretch{-webkit-box-align:stretch!important;-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-lg-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-lg-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-lg-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-lg-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-lg-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-lg-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-lg-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-lg-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-lg-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-lg-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-lg-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-lg-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:1200px){.flex-xl-row{-webkit-box-orient:horizontal!important;-ms-flex-direction:row!important;flex-direction:row!important}.flex-xl-column,.flex-xl-row{-webkit-box-direction:normal!important}.flex-xl-column{-webkit-box-orient:vertical!important;-ms-flex-direction:column!important;flex-direction:column!important}.flex-xl-row-reverse{-webkit-box-orient:horizontal!important;-webkit-box-direction:reverse!important;-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-xl-column-reverse{-webkit-box-orient:vertical!important;-webkit-box-direction:reverse!important;-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-xl-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-xl-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-xl-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-xl-fill{-webkit-box-flex:1!important;-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-xl-grow-0{-webkit-box-flex:0!important;-ms-flex-positive:0!important;flex-grow:0!important}.flex-xl-grow-1{-webkit-box-flex:1!important;-ms-flex-positive:1!important;flex-grow:1!important}.flex-xl-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-xl-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-xl-start{-webkit-box-pack:start!important;-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-xl-end{-webkit-box-pack:end!important;-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-xl-center{-webkit-box-pack:center!important;-ms-flex-pack:center!important;justify-content:center!important}.justify-content-xl-between{-webkit-box-pack:justify!important;-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-xl-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-xl-start{-webkit-box-align:start!important;-ms-flex-align:start!important;align-items:flex-start!important}.align-items-xl-end{-webkit-box-align:end!important;-ms-flex-align:end!important;align-items:flex-end!important}.align-items-xl-center{-webkit-box-align:center!important;-ms-flex-align:center!important;align-items:center!important}.align-items-xl-baseline{-webkit-box-align:baseline!important;-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-xl-stretch{-webkit-box-align:stretch!important;-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-xl-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-xl-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-xl-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-xl-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-xl-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-xl-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-xl-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-xl-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-xl-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-xl-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-xl-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-xl-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}.float-left{float:left!important}.float-right{float:right!important}.float-none{float:none!important}@media (min-width:576px){.float-sm-left{float:left!important}.float-sm-right{float:right!important}.float-sm-none{float:none!important}}@media (min-width:768px){.float-md-left{float:left!important}.float-md-right{float:right!important}.float-md-none{float:none!important}}@media (min-width:992px){.float-lg-left{float:left!important}.float-lg-right{float:right!important}.float-lg-none{float:none!important}}@media (min-width:1200px){.float-xl-left{float:left!important}.float-xl-right{float:right!important}.float-xl-none{float:none!important}}.overflow-auto{overflow:auto!important}.overflow-hidden{overflow:hidden!important}.position-static{position:static!important}.position-relative{position:relative!important}.position-absolute{position:absolute!important}.position-fixed{position:fixed!important}.position-sticky{position:-webkit-sticky!important;position:sticky!important}.fixed-top{top:0}.fixed-bottom,.fixed-top{position:fixed;right:0;left:0;z-index:1030}.fixed-bottom{bottom:0}@supports ((position:-webkit-sticky) or (position:sticky)){.sticky-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}.sr-only{position:absolute;width:1px;height:1px;padding:0;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;overflow:visible;clip:auto;white-space:normal}.shadow-sm{-webkit-box-shadow:0 .125rem .25rem rgba(0,0,0,.075)!important;box-shadow:0 .125rem .25rem rgba(0,0,0,.075)!important}.shadow{-webkit-box-shadow:0 .5rem 1rem rgba(0,0,0,.15)!important;box-shadow:0 .5rem 1rem rgba(0,0,0,.15)!important}.shadow-lg{-webkit-box-shadow:0 1rem 3rem rgba(0,0,0,.175)!important;box-shadow:0 1rem 3rem rgba(0,0,0,.175)!important}.shadow-none{-webkit-box-shadow:none!important;box-shadow:none!important}.w-25{width:25%!important}.w-50{width:50%!important}.w-75{width:75%!important}.w-100{width:100%!important}.w-auto{width:auto!important}.h-25{height:25%!important}.h-50{height:50%!important}.h-75{height:75%!important}.h-100{height:100%!important}.h-auto{height:auto!important}.mw-100{max-width:100%!important}.mh-100{max-height:100%!important}.min-vw-100{min-width:100vw!important}.min-vh-100{min-height:100vh!important}.vw-100{width:100vw!important}.vh-100{height:100vh!important}.m-0{margin:0!important}.mt-0,.my-0{margin-top:0!important}.mr-0,.mx-0{margin-right:0!important}.mb-0,.my-0{margin-bottom:0!important}.ml-0,.mx-0{margin-left:0!important}.m-1{margin:.25rem!important}.mt-1,.my-1{margin-top:.25rem!important}.mr-1,.mx-1{margin-right:.25rem!important}.mb-1,.my-1{margin-bottom:.25rem!important}.ml-1,.mx-1{margin-left:.25rem!important}.m-2{margin:.5rem!important}.mt-2,.my-2{margin-top:.5rem!important}.mr-2,.mx-2{margin-right:.5rem!important}.mb-2,.my-2{margin-bottom:.5rem!important}.ml-2,.mx-2{margin-left:.5rem!important}.m-3{margin:1rem!important}.mt-3,.my-3{margin-top:1rem!important}.mr-3,.mx-3{margin-right:1rem!important}.mb-3,.my-3{margin-bottom:1rem!important}.ml-3,.mx-3{margin-left:1rem!important}.m-4{margin:1.5rem!important}.mt-4,.my-4{margin-top:1.5rem!important}.mr-4,.mx-4{margin-right:1.5rem!important}.mb-4,.my-4{margin-bottom:1.5rem!important}.ml-4,.mx-4{margin-left:1.5rem!important}.m-5{margin:3rem!important}.mt-5,.my-5{margin-top:3rem!important}.mr-5,.mx-5{margin-right:3rem!important}.mb-5,.my-5{margin-bottom:3rem!important}.ml-5,.mx-5{margin-left:3rem!important}.p-0{padding:0!important}.pt-0,.py-0{padding-top:0!important}.pr-0,.px-0{padding-right:0!important}.pb-0,.py-0{padding-bottom:0!important}.pl-0,.px-0{padding-left:0!important}.p-1{padding:.25rem!important}.pt-1,.py-1{padding-top:.25rem!important}.pr-1,.px-1{padding-right:.25rem!important}.pb-1,.py-1{padding-bottom:.25rem!important}.pl-1,.px-1{padding-left:.25rem!important}.p-2{padding:.5rem!important}.pt-2,.py-2{padding-top:.5rem!important}.pr-2,.px-2{padding-right:.5rem!important}.pb-2,.py-2{padding-bottom:.5rem!important}.pl-2,.px-2{padding-left:.5rem!important}.p-3{padding:1rem!important}.pt-3,.py-3{padding-top:1rem!important}.pr-3,.px-3{padding-right:1rem!important}.pb-3,.py-3{padding-bottom:1rem!important}.pl-3,.px-3{padding-left:1rem!important}.p-4{padding:1.5rem!important}.pt-4,.py-4{padding-top:1.5rem!important}.pr-4,.px-4{padding-right:1.5rem!important}.pb-4,.py-4{padding-bottom:1.5rem!important}.pl-4,.px-4{padding-left:1.5rem!important}.p-5{padding:3rem!important}.pt-5,.py-5{padding-top:3rem!important}.pr-5,.px-5{padding-right:3rem!important}.pb-5,.py-5{padding-bottom:3rem!important}.pl-5,.px-5{padding-left:3rem!important}.m-n1{margin:-.25rem!important}.mt-n1,.my-n1{margin-top:-.25rem!important}.mr-n1,.mx-n1{margin-right:-.25rem!important}.mb-n1,.my-n1{margin-bottom:-.25rem!important}.ml-n1,.mx-n1{margin-left:-.25rem!important}.m-n2{margin:-.5rem!important}.mt-n2,.my-n2{margin-top:-.5rem!important}.mr-n2,.mx-n2{margin-right:-.5rem!important}.mb-n2,.my-n2{margin-bottom:-.5rem!important}.ml-n2,.mx-n2{margin-left:-.5rem!important}.m-n3{margin:-1rem!important}.mt-n3,.my-n3{margin-top:-1rem!important}.mr-n3,.mx-n3{margin-right:-1rem!important}.mb-n3,.my-n3{margin-bottom:-1rem!important}.ml-n3,.mx-n3{margin-left:-1rem!important}.m-n4{margin:-1.5rem!important}.mt-n4,.my-n4{margin-top:-1.5rem!important}.mr-n4,.mx-n4{margin-right:-1.5rem!important}.mb-n4,.my-n4{margin-bottom:-1.5rem!important}.ml-n4,.mx-n4{margin-left:-1.5rem!important}.m-n5{margin:-3rem!important}.mt-n5,.my-n5{margin-top:-3rem!important}.mr-n5,.mx-n5{margin-right:-3rem!important}.mb-n5,.my-n5{margin-bottom:-3rem!important}.ml-n5,.mx-n5{margin-left:-3rem!important}.m-auto{margin:auto!important}.mt-auto,.my-auto{margin-top:auto!important}.mr-auto,.mx-auto{margin-right:auto!important}.mb-auto,.my-auto{margin-bottom:auto!important}.ml-auto,.mx-auto{margin-left:auto!important}@media (min-width:576px){.m-sm-0{margin:0!important}.mt-sm-0,.my-sm-0{margin-top:0!important}.mr-sm-0,.mx-sm-0{margin-right:0!important}.mb-sm-0,.my-sm-0{margin-bottom:0!important}.ml-sm-0,.mx-sm-0{margin-left:0!important}.m-sm-1{margin:.25rem!important}.mt-sm-1,.my-sm-1{margin-top:.25rem!important}.mr-sm-1,.mx-sm-1{margin-right:.25rem!important}.mb-sm-1,.my-sm-1{margin-bottom:.25rem!important}.ml-sm-1,.mx-sm-1{margin-left:.25rem!important}.m-sm-2{margin:.5rem!important}.mt-sm-2,.my-sm-2{margin-top:.5rem!important}.mr-sm-2,.mx-sm-2{margin-right:.5rem!important}.mb-sm-2,.my-sm-2{margin-bottom:.5rem!important}.ml-sm-2,.mx-sm-2{margin-left:.5rem!important}.m-sm-3{margin:1rem!important}.mt-sm-3,.my-sm-3{margin-top:1rem!important}.mr-sm-3,.mx-sm-3{margin-right:1rem!important}.mb-sm-3,.my-sm-3{margin-bottom:1rem!important}.ml-sm-3,.mx-sm-3{margin-left:1rem!important}.m-sm-4{margin:1.5rem!important}.mt-sm-4,.my-sm-4{margin-top:1.5rem!important}.mr-sm-4,.mx-sm-4{margin-right:1.5rem!important}.mb-sm-4,.my-sm-4{margin-bottom:1.5rem!important}.ml-sm-4,.mx-sm-4{margin-left:1.5rem!important}.m-sm-5{margin:3rem!important}.mt-sm-5,.my-sm-5{margin-top:3rem!important}.mr-sm-5,.mx-sm-5{margin-right:3rem!important}.mb-sm-5,.my-sm-5{margin-bottom:3rem!important}.ml-sm-5,.mx-sm-5{margin-left:3rem!important}.p-sm-0{padding:0!important}.pt-sm-0,.py-sm-0{padding-top:0!important}.pr-sm-0,.px-sm-0{padding-right:0!important}.pb-sm-0,.py-sm-0{padding-bottom:0!important}.pl-sm-0,.px-sm-0{padding-left:0!important}.p-sm-1{padding:.25rem!important}.pt-sm-1,.py-sm-1{padding-top:.25rem!important}.pr-sm-1,.px-sm-1{padding-right:.25rem!important}.pb-sm-1,.py-sm-1{padding-bottom:.25rem!important}.pl-sm-1,.px-sm-1{padding-left:.25rem!important}.p-sm-2{padding:.5rem!important}.pt-sm-2,.py-sm-2{padding-top:.5rem!important}.pr-sm-2,.px-sm-2{padding-right:.5rem!important}.pb-sm-2,.py-sm-2{padding-bottom:.5rem!important}.pl-sm-2,.px-sm-2{padding-left:.5rem!important}.p-sm-3{padding:1rem!important}.pt-sm-3,.py-sm-3{padding-top:1rem!important}.pr-sm-3,.px-sm-3{padding-right:1rem!important}.pb-sm-3,.py-sm-3{padding-bottom:1rem!important}.pl-sm-3,.px-sm-3{padding-left:1rem!important}.p-sm-4{padding:1.5rem!important}.pt-sm-4,.py-sm-4{padding-top:1.5rem!important}.pr-sm-4,.px-sm-4{padding-right:1.5rem!important}.pb-sm-4,.py-sm-4{padding-bottom:1.5rem!important}.pl-sm-4,.px-sm-4{padding-left:1.5rem!important}.p-sm-5{padding:3rem!important}.pt-sm-5,.py-sm-5{padding-top:3rem!important}.pr-sm-5,.px-sm-5{padding-right:3rem!important}.pb-sm-5,.py-sm-5{padding-bottom:3rem!important}.pl-sm-5,.px-sm-5{padding-left:3rem!important}.m-sm-n1{margin:-.25rem!important}.mt-sm-n1,.my-sm-n1{margin-top:-.25rem!important}.mr-sm-n1,.mx-sm-n1{margin-right:-.25rem!important}.mb-sm-n1,.my-sm-n1{margin-bottom:-.25rem!important}.ml-sm-n1,.mx-sm-n1{margin-left:-.25rem!important}.m-sm-n2{margin:-.5rem!important}.mt-sm-n2,.my-sm-n2{margin-top:-.5rem!important}.mr-sm-n2,.mx-sm-n2{margin-right:-.5rem!important}.mb-sm-n2,.my-sm-n2{margin-bottom:-.5rem!important}.ml-sm-n2,.mx-sm-n2{margin-left:-.5rem!important}.m-sm-n3{margin:-1rem!important}.mt-sm-n3,.my-sm-n3{margin-top:-1rem!important}.mr-sm-n3,.mx-sm-n3{margin-right:-1rem!important}.mb-sm-n3,.my-sm-n3{margin-bottom:-1rem!important}.ml-sm-n3,.mx-sm-n3{margin-left:-1rem!important}.m-sm-n4{margin:-1.5rem!important}.mt-sm-n4,.my-sm-n4{margin-top:-1.5rem!important}.mr-sm-n4,.mx-sm-n4{margin-right:-1.5rem!important}.mb-sm-n4,.my-sm-n4{margin-bottom:-1.5rem!important}.ml-sm-n4,.mx-sm-n4{margin-left:-1.5rem!important}.m-sm-n5{margin:-3rem!important}.mt-sm-n5,.my-sm-n5{margin-top:-3rem!important}.mr-sm-n5,.mx-sm-n5{margin-right:-3rem!important}.mb-sm-n5,.my-sm-n5{margin-bottom:-3rem!important}.ml-sm-n5,.mx-sm-n5{margin-left:-3rem!important}.m-sm-auto{margin:auto!important}.mt-sm-auto,.my-sm-auto{margin-top:auto!important}.mr-sm-auto,.mx-sm-auto{margin-right:auto!important}.mb-sm-auto,.my-sm-auto{margin-bottom:auto!important}.ml-sm-auto,.mx-sm-auto{margin-left:auto!important}}@media (min-width:768px){.m-md-0{margin:0!important}.mt-md-0,.my-md-0{margin-top:0!important}.mr-md-0,.mx-md-0{margin-right:0!important}.mb-md-0,.my-md-0{margin-bottom:0!important}.ml-md-0,.mx-md-0{margin-left:0!important}.m-md-1{margin:.25rem!important}.mt-md-1,.my-md-1{margin-top:.25rem!important}.mr-md-1,.mx-md-1{margin-right:.25rem!important}.mb-md-1,.my-md-1{margin-bottom:.25rem!important}.ml-md-1,.mx-md-1{margin-left:.25rem!important}.m-md-2{margin:.5rem!important}.mt-md-2,.my-md-2{margin-top:.5rem!important}.mr-md-2,.mx-md-2{margin-right:.5rem!important}.mb-md-2,.my-md-2{margin-bottom:.5rem!important}.ml-md-2,.mx-md-2{margin-left:.5rem!important}.m-md-3{margin:1rem!important}.mt-md-3,.my-md-3{margin-top:1rem!important}.mr-md-3,.mx-md-3{margin-right:1rem!important}.mb-md-3,.my-md-3{margin-bottom:1rem!important}.ml-md-3,.mx-md-3{margin-left:1rem!important}.m-md-4{margin:1.5rem!important}.mt-md-4,.my-md-4{margin-top:1.5rem!important}.mr-md-4,.mx-md-4{margin-right:1.5rem!important}.mb-md-4,.my-md-4{margin-bottom:1.5rem!important}.ml-md-4,.mx-md-4{margin-left:1.5rem!important}.m-md-5{margin:3rem!important}.mt-md-5,.my-md-5{margin-top:3rem!important}.mr-md-5,.mx-md-5{margin-right:3rem!important}.mb-md-5,.my-md-5{margin-bottom:3rem!important}.ml-md-5,.mx-md-5{margin-left:3rem!important}.p-md-0{padding:0!important}.pt-md-0,.py-md-0{padding-top:0!important}.pr-md-0,.px-md-0{padding-right:0!important}.pb-md-0,.py-md-0{padding-bottom:0!important}.pl-md-0,.px-md-0{padding-left:0!important}.p-md-1{padding:.25rem!important}.pt-md-1,.py-md-1{padding-top:.25rem!important}.pr-md-1,.px-md-1{padding-right:.25rem!important}.pb-md-1,.py-md-1{padding-bottom:.25rem!important}.pl-md-1,.px-md-1{padding-left:.25rem!important}.p-md-2{padding:.5rem!important}.pt-md-2,.py-md-2{padding-top:.5rem!important}.pr-md-2,.px-md-2{padding-right:.5rem!important}.pb-md-2,.py-md-2{padding-bottom:.5rem!important}.pl-md-2,.px-md-2{padding-left:.5rem!important}.p-md-3{padding:1rem!important}.pt-md-3,.py-md-3{padding-top:1rem!important}.pr-md-3,.px-md-3{padding-right:1rem!important}.pb-md-3,.py-md-3{padding-bottom:1rem!important}.pl-md-3,.px-md-3{padding-left:1rem!important}.p-md-4{padding:1.5rem!important}.pt-md-4,.py-md-4{padding-top:1.5rem!important}.pr-md-4,.px-md-4{padding-right:1.5rem!important}.pb-md-4,.py-md-4{padding-bottom:1.5rem!important}.pl-md-4,.px-md-4{padding-left:1.5rem!important}.p-md-5{padding:3rem!important}.pt-md-5,.py-md-5{padding-top:3rem!important}.pr-md-5,.px-md-5{padding-right:3rem!important}.pb-md-5,.py-md-5{padding-bottom:3rem!important}.pl-md-5,.px-md-5{padding-left:3rem!important}.m-md-n1{margin:-.25rem!important}.mt-md-n1,.my-md-n1{margin-top:-.25rem!important}.mr-md-n1,.mx-md-n1{margin-right:-.25rem!important}.mb-md-n1,.my-md-n1{margin-bottom:-.25rem!important}.ml-md-n1,.mx-md-n1{margin-left:-.25rem!important}.m-md-n2{margin:-.5rem!important}.mt-md-n2,.my-md-n2{margin-top:-.5rem!important}.mr-md-n2,.mx-md-n2{margin-right:-.5rem!important}.mb-md-n2,.my-md-n2{margin-bottom:-.5rem!important}.ml-md-n2,.mx-md-n2{margin-left:-.5rem!important}.m-md-n3{margin:-1rem!important}.mt-md-n3,.my-md-n3{margin-top:-1rem!important}.mr-md-n3,.mx-md-n3{margin-right:-1rem!important}.mb-md-n3,.my-md-n3{margin-bottom:-1rem!important}.ml-md-n3,.mx-md-n3{margin-left:-1rem!important}.m-md-n4{margin:-1.5rem!important}.mt-md-n4,.my-md-n4{margin-top:-1.5rem!important}.mr-md-n4,.mx-md-n4{margin-right:-1.5rem!important}.mb-md-n4,.my-md-n4{margin-bottom:-1.5rem!important}.ml-md-n4,.mx-md-n4{margin-left:-1.5rem!important}.m-md-n5{margin:-3rem!important}.mt-md-n5,.my-md-n5{margin-top:-3rem!important}.mr-md-n5,.mx-md-n5{margin-right:-3rem!important}.mb-md-n5,.my-md-n5{margin-bottom:-3rem!important}.ml-md-n5,.mx-md-n5{margin-left:-3rem!important}.m-md-auto{margin:auto!important}.mt-md-auto,.my-md-auto{margin-top:auto!important}.mr-md-auto,.mx-md-auto{margin-right:auto!important}.mb-md-auto,.my-md-auto{margin-bottom:auto!important}.ml-md-auto,.mx-md-auto{margin-left:auto!important}}@media (min-width:992px){.m-lg-0{margin:0!important}.mt-lg-0,.my-lg-0{margin-top:0!important}.mr-lg-0,.mx-lg-0{margin-right:0!important}.mb-lg-0,.my-lg-0{margin-bottom:0!important}.ml-lg-0,.mx-lg-0{margin-left:0!important}.m-lg-1{margin:.25rem!important}.mt-lg-1,.my-lg-1{margin-top:.25rem!important}.mr-lg-1,.mx-lg-1{margin-right:.25rem!important}.mb-lg-1,.my-lg-1{margin-bottom:.25rem!important}.ml-lg-1,.mx-lg-1{margin-left:.25rem!important}.m-lg-2{margin:.5rem!important}.mt-lg-2,.my-lg-2{margin-top:.5rem!important}.mr-lg-2,.mx-lg-2{margin-right:.5rem!important}.mb-lg-2,.my-lg-2{margin-bottom:.5rem!important}.ml-lg-2,.mx-lg-2{margin-left:.5rem!important}.m-lg-3{margin:1rem!important}.mt-lg-3,.my-lg-3{margin-top:1rem!important}.mr-lg-3,.mx-lg-3{margin-right:1rem!important}.mb-lg-3,.my-lg-3{margin-bottom:1rem!important}.ml-lg-3,.mx-lg-3{margin-left:1rem!important}.m-lg-4{margin:1.5rem!important}.mt-lg-4,.my-lg-4{margin-top:1.5rem!important}.mr-lg-4,.mx-lg-4{margin-right:1.5rem!important}.mb-lg-4,.my-lg-4{margin-bottom:1.5rem!important}.ml-lg-4,.mx-lg-4{margin-left:1.5rem!important}.m-lg-5{margin:3rem!important}.mt-lg-5,.my-lg-5{margin-top:3rem!important}.mr-lg-5,.mx-lg-5{margin-right:3rem!important}.mb-lg-5,.my-lg-5{margin-bottom:3rem!important}.ml-lg-5,.mx-lg-5{margin-left:3rem!important}.p-lg-0{padding:0!important}.pt-lg-0,.py-lg-0{padding-top:0!important}.pr-lg-0,.px-lg-0{padding-right:0!important}.pb-lg-0,.py-lg-0{padding-bottom:0!important}.pl-lg-0,.px-lg-0{padding-left:0!important}.p-lg-1{padding:.25rem!important}.pt-lg-1,.py-lg-1{padding-top:.25rem!important}.pr-lg-1,.px-lg-1{padding-right:.25rem!important}.pb-lg-1,.py-lg-1{padding-bottom:.25rem!important}.pl-lg-1,.px-lg-1{padding-left:.25rem!important}.p-lg-2{padding:.5rem!important}.pt-lg-2,.py-lg-2{padding-top:.5rem!important}.pr-lg-2,.px-lg-2{padding-right:.5rem!important}.pb-lg-2,.py-lg-2{padding-bottom:.5rem!important}.pl-lg-2,.px-lg-2{padding-left:.5rem!important}.p-lg-3{padding:1rem!important}.pt-lg-3,.py-lg-3{padding-top:1rem!important}.pr-lg-3,.px-lg-3{padding-right:1rem!important}.pb-lg-3,.py-lg-3{padding-bottom:1rem!important}.pl-lg-3,.px-lg-3{padding-left:1rem!important}.p-lg-4{padding:1.5rem!important}.pt-lg-4,.py-lg-4{padding-top:1.5rem!important}.pr-lg-4,.px-lg-4{padding-right:1.5rem!important}.pb-lg-4,.py-lg-4{padding-bottom:1.5rem!important}.pl-lg-4,.px-lg-4{padding-left:1.5rem!important}.p-lg-5{padding:3rem!important}.pt-lg-5,.py-lg-5{padding-top:3rem!important}.pr-lg-5,.px-lg-5{padding-right:3rem!important}.pb-lg-5,.py-lg-5{padding-bottom:3rem!important}.pl-lg-5,.px-lg-5{padding-left:3rem!important}.m-lg-n1{margin:-.25rem!important}.mt-lg-n1,.my-lg-n1{margin-top:-.25rem!important}.mr-lg-n1,.mx-lg-n1{margin-right:-.25rem!important}.mb-lg-n1,.my-lg-n1{margin-bottom:-.25rem!important}.ml-lg-n1,.mx-lg-n1{margin-left:-.25rem!important}.m-lg-n2{margin:-.5rem!important}.mt-lg-n2,.my-lg-n2{margin-top:-.5rem!important}.mr-lg-n2,.mx-lg-n2{margin-right:-.5rem!important}.mb-lg-n2,.my-lg-n2{margin-bottom:-.5rem!important}.ml-lg-n2,.mx-lg-n2{margin-left:-.5rem!important}.m-lg-n3{margin:-1rem!important}.mt-lg-n3,.my-lg-n3{margin-top:-1rem!important}.mr-lg-n3,.mx-lg-n3{margin-right:-1rem!important}.mb-lg-n3,.my-lg-n3{margin-bottom:-1rem!important}.ml-lg-n3,.mx-lg-n3{margin-left:-1rem!important}.m-lg-n4{margin:-1.5rem!important}.mt-lg-n4,.my-lg-n4{margin-top:-1.5rem!important}.mr-lg-n4,.mx-lg-n4{margin-right:-1.5rem!important}.mb-lg-n4,.my-lg-n4{margin-bottom:-1.5rem!important}.ml-lg-n4,.mx-lg-n4{margin-left:-1.5rem!important}.m-lg-n5{margin:-3rem!important}.mt-lg-n5,.my-lg-n5{margin-top:-3rem!important}.mr-lg-n5,.mx-lg-n5{margin-right:-3rem!important}.mb-lg-n5,.my-lg-n5{margin-bottom:-3rem!important}.ml-lg-n5,.mx-lg-n5{margin-left:-3rem!important}.m-lg-auto{margin:auto!important}.mt-lg-auto,.my-lg-auto{margin-top:auto!important}.mr-lg-auto,.mx-lg-auto{margin-right:auto!important}.mb-lg-auto,.my-lg-auto{margin-bottom:auto!important}.ml-lg-auto,.mx-lg-auto{margin-left:auto!important}}@media (min-width:1200px){.m-xl-0{margin:0!important}.mt-xl-0,.my-xl-0{margin-top:0!important}.mr-xl-0,.mx-xl-0{margin-right:0!important}.mb-xl-0,.my-xl-0{margin-bottom:0!important}.ml-xl-0,.mx-xl-0{margin-left:0!important}.m-xl-1{margin:.25rem!important}.mt-xl-1,.my-xl-1{margin-top:.25rem!important}.mr-xl-1,.mx-xl-1{margin-right:.25rem!important}.mb-xl-1,.my-xl-1{margin-bottom:.25rem!important}.ml-xl-1,.mx-xl-1{margin-left:.25rem!important}.m-xl-2{margin:.5rem!important}.mt-xl-2,.my-xl-2{margin-top:.5rem!important}.mr-xl-2,.mx-xl-2{margin-right:.5rem!important}.mb-xl-2,.my-xl-2{margin-bottom:.5rem!important}.ml-xl-2,.mx-xl-2{margin-left:.5rem!important}.m-xl-3{margin:1rem!important}.mt-xl-3,.my-xl-3{margin-top:1rem!important}.mr-xl-3,.mx-xl-3{margin-right:1rem!important}.mb-xl-3,.my-xl-3{margin-bottom:1rem!important}.ml-xl-3,.mx-xl-3{margin-left:1rem!important}.m-xl-4{margin:1.5rem!important}.mt-xl-4,.my-xl-4{margin-top:1.5rem!important}.mr-xl-4,.mx-xl-4{margin-right:1.5rem!important}.mb-xl-4,.my-xl-4{margin-bottom:1.5rem!important}.ml-xl-4,.mx-xl-4{margin-left:1.5rem!important}.m-xl-5{margin:3rem!important}.mt-xl-5,.my-xl-5{margin-top:3rem!important}.mr-xl-5,.mx-xl-5{margin-right:3rem!important}.mb-xl-5,.my-xl-5{margin-bottom:3rem!important}.ml-xl-5,.mx-xl-5{margin-left:3rem!important}.p-xl-0{padding:0!important}.pt-xl-0,.py-xl-0{padding-top:0!important}.pr-xl-0,.px-xl-0{padding-right:0!important}.pb-xl-0,.py-xl-0{padding-bottom:0!important}.pl-xl-0,.px-xl-0{padding-left:0!important}.p-xl-1{padding:.25rem!important}.pt-xl-1,.py-xl-1{padding-top:.25rem!important}.pr-xl-1,.px-xl-1{padding-right:.25rem!important}.pb-xl-1,.py-xl-1{padding-bottom:.25rem!important}.pl-xl-1,.px-xl-1{padding-left:.25rem!important}.p-xl-2{padding:.5rem!important}.pt-xl-2,.py-xl-2{padding-top:.5rem!important}.pr-xl-2,.px-xl-2{padding-right:.5rem!important}.pb-xl-2,.py-xl-2{padding-bottom:.5rem!important}.pl-xl-2,.px-xl-2{padding-left:.5rem!important}.p-xl-3{padding:1rem!important}.pt-xl-3,.py-xl-3{padding-top:1rem!important}.pr-xl-3,.px-xl-3{padding-right:1rem!important}.pb-xl-3,.py-xl-3{padding-bottom:1rem!important}.pl-xl-3,.px-xl-3{padding-left:1rem!important}.p-xl-4{padding:1.5rem!important}.pt-xl-4,.py-xl-4{padding-top:1.5rem!important}.pr-xl-4,.px-xl-4{padding-right:1.5rem!important}.pb-xl-4,.py-xl-4{padding-bottom:1.5rem!important}.pl-xl-4,.px-xl-4{padding-left:1.5rem!important}.p-xl-5{padding:3rem!important}.pt-xl-5,.py-xl-5{padding-top:3rem!important}.pr-xl-5,.px-xl-5{padding-right:3rem!important}.pb-xl-5,.py-xl-5{padding-bottom:3rem!important}.pl-xl-5,.px-xl-5{padding-left:3rem!important}.m-xl-n1{margin:-.25rem!important}.mt-xl-n1,.my-xl-n1{margin-top:-.25rem!important}.mr-xl-n1,.mx-xl-n1{margin-right:-.25rem!important}.mb-xl-n1,.my-xl-n1{margin-bottom:-.25rem!important}.ml-xl-n1,.mx-xl-n1{margin-left:-.25rem!important}.m-xl-n2{margin:-.5rem!important}.mt-xl-n2,.my-xl-n2{margin-top:-.5rem!important}.mr-xl-n2,.mx-xl-n2{margin-right:-.5rem!important}.mb-xl-n2,.my-xl-n2{margin-bottom:-.5rem!important}.ml-xl-n2,.mx-xl-n2{margin-left:-.5rem!important}.m-xl-n3{margin:-1rem!important}.mt-xl-n3,.my-xl-n3{margin-top:-1rem!important}.mr-xl-n3,.mx-xl-n3{margin-right:-1rem!important}.mb-xl-n3,.my-xl-n3{margin-bottom:-1rem!important}.ml-xl-n3,.mx-xl-n3{margin-left:-1rem!important}.m-xl-n4{margin:-1.5rem!important}.mt-xl-n4,.my-xl-n4{margin-top:-1.5rem!important}.mr-xl-n4,.mx-xl-n4{margin-right:-1.5rem!important}.mb-xl-n4,.my-xl-n4{margin-bottom:-1.5rem!important}.ml-xl-n4,.mx-xl-n4{margin-left:-1.5rem!important}.m-xl-n5{margin:-3rem!important}.mt-xl-n5,.my-xl-n5{margin-top:-3rem!important}.mr-xl-n5,.mx-xl-n5{margin-right:-3rem!important}.mb-xl-n5,.my-xl-n5{margin-bottom:-3rem!important}.ml-xl-n5,.mx-xl-n5{margin-left:-3rem!important}.m-xl-auto{margin:auto!important}.mt-xl-auto,.my-xl-auto{margin-top:auto!important}.mr-xl-auto,.mx-xl-auto{margin-right:auto!important}.mb-xl-auto,.my-xl-auto{margin-bottom:auto!important}.ml-xl-auto,.mx-xl-auto{margin-left:auto!important}}.text-monospace{font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-justify{text-align:justify!important}.text-wrap{white-space:normal!important}.text-nowrap{white-space:nowrap!important}.text-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.text-left{text-align:left!important}.text-right{text-align:right!important}.text-center{text-align:center!important}@media (min-width:576px){.text-sm-left{text-align:left!important}.text-sm-right{text-align:right!important}.text-sm-center{text-align:center!important}}@media (min-width:768px){.text-md-left{text-align:left!important}.text-md-right{text-align:right!important}.text-md-center{text-align:center!important}}@media (min-width:992px){.text-lg-left{text-align:left!important}.text-lg-right{text-align:right!important}.text-lg-center{text-align:center!important}}@media (min-width:1200px){.text-xl-left{text-align:left!important}.text-xl-right{text-align:right!important}.text-xl-center{text-align:center!important}}.text-lowercase{text-transform:lowercase!important}.text-uppercase{text-transform:uppercase!important}.text-capitalize{text-transform:capitalize!important}.font-weight-light{font-weight:300!important}.font-weight-lighter{font-weight:lighter!important}.font-weight-normal{font-weight:400!important}.font-weight-bold{font-weight:700!important}.font-weight-bolder{font-weight:bolder!important}.font-italic{font-style:italic!important}.text-white{color:#fff!important}.text-primary{color:#02baf2!important}a.text-primary:focus,a.text-primary:hover{color:#0180a6!important}.text-secondary{color:#6c757d!important}a.text-secondary:focus,a.text-secondary:hover{color:#494f54!important}.text-success{color:#28a745!important}a.text-success:focus,a.text-success:hover{color:#19692c!important}.text-info{color:#17a2b8!important}a.text-info:focus,a.text-info:hover{color:#0f6674!important}.text-warning{color:#ffc107!important}a.text-warning:focus,a.text-warning:hover{color:#ba8b00!important}.text-danger{color:#f84545!important}a.text-danger:focus,a.text-danger:hover{color:#e80909!important}.text-light{color:#f8f9fa!important}a.text-light:focus,a.text-light:hover{color:#cbd3da!important}.text-dark{color:#343a40!important}a.text-dark:focus,a.text-dark:hover{color:#121416!important}.text-body{color:#212529!important}.text-muted{color:#6c757d!important}.text-black-50{color:rgba(0,0,0,.5)!important}.text-white-50{color:hsla(0,0%,100%,.5)!important}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.text-decoration-none{text-decoration:none!important}.text-reset{color:inherit!important}.visible{visibility:visible!important}.invisible{visibility:hidden!important}@media print{*,:after,:before{text-shadow:none!important;-webkit-box-shadow:none!important;box-shadow:none!important}a:not(.btn){text-decoration:underline}abbr[title]:after{content:" (" attr(title) ")"}pre{white-space:pre-wrap!important}blockquote,pre{border:1px solid #adb5bd;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}@page{size:a3}.container,body{min-width:992px!important}.navbar{display:none}.badge{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #dee2e6!important}.table-dark{color:inherit}.table-dark tbody+tbody,.table-dark td,.table-dark th,.table-dark thead th{border-color:#dee2e6}.table .thead-dark th{color:inherit;border-color:#dee2e6}}.btn-outline-primary{color:#02baf2;border-color:#02baf2;border-color:#d6e1e5}.btn-outline-primary:hover{color:#fff;background-color:#02baf2;border-color:#02baf2}.btn-outline-primary.focus,.btn-outline-primary:focus{-webkit-box-shadow:0 0 0 .2rem rgba(2,186,242,.5);box-shadow:0 0 0 .2rem rgba(2,186,242,.5)}.btn-outline-primary.disabled,.btn-outline-primary:disabled{color:#02baf2;background-color:transparent}.btn-outline-primary:not(:disabled):not(.disabled).active,.btn-outline-primary:not(:disabled):not(.disabled):active,.show>.btn-outline-primary.dropdown-toggle{color:#fff;background-color:#02baf2;border-color:#02baf2}.btn-outline-primary:not(:disabled):not(.disabled).active:focus,.btn-outline-primary:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-primary.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 .2rem rgba(2,186,242,.5);box-shadow:0 0 0 .2rem rgba(2,186,242,.5)}.btn-outline-secondary{color:#6c757d;border-color:#6c757d;border-color:#d6e1e5}.btn-outline-secondary:hover{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-outline-secondary.focus,.btn-outline-secondary:focus{-webkit-box-shadow:0 0 0 .2rem rgba(108,117,125,.5);box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.btn-outline-secondary.disabled,.btn-outline-secondary:disabled{color:#6c757d;background-color:transparent}.btn-outline-secondary:not(:disabled):not(.disabled).active,.btn-outline-secondary:not(:disabled):not(.disabled):active,.show>.btn-outline-secondary.dropdown-toggle{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-outline-secondary:not(:disabled):not(.disabled).active:focus,.btn-outline-secondary:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-secondary.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 .2rem rgba(108,117,125,.5);box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.btn-outline-success{color:#28a745;border-color:#28a745;border-color:#d6e1e5}.btn-outline-success:hover{color:#fff;background-color:#28a745;border-color:#28a745}.btn-outline-success.focus,.btn-outline-success:focus{-webkit-box-shadow:0 0 0 .2rem rgba(40,167,69,.5);box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-outline-success.disabled,.btn-outline-success:disabled{color:#28a745;background-color:transparent}.btn-outline-success:not(:disabled):not(.disabled).active,.btn-outline-success:not(:disabled):not(.disabled):active,.show>.btn-outline-success.dropdown-toggle{color:#fff;background-color:#28a745;border-color:#28a745}.btn-outline-success:not(:disabled):not(.disabled).active:focus,.btn-outline-success:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-success.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 .2rem rgba(40,167,69,.5);box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-outline-info{color:#17a2b8;border-color:#17a2b8;border-color:#d6e1e5}.btn-outline-info:hover{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-outline-info.focus,.btn-outline-info:focus{-webkit-box-shadow:0 0 0 .2rem rgba(23,162,184,.5);box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-outline-info.disabled,.btn-outline-info:disabled{color:#17a2b8;background-color:transparent}.btn-outline-info:not(:disabled):not(.disabled).active,.btn-outline-info:not(:disabled):not(.disabled):active,.show>.btn-outline-info.dropdown-toggle{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-outline-info:not(:disabled):not(.disabled).active:focus,.btn-outline-info:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-info.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 .2rem rgba(23,162,184,.5);box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-outline-warning{color:#ffc107;border-color:#ffc107;border-color:#d6e1e5}.btn-outline-warning:hover{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-outline-warning.focus,.btn-outline-warning:focus{-webkit-box-shadow:0 0 0 .2rem rgba(255,193,7,.5);box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-outline-warning.disabled,.btn-outline-warning:disabled{color:#ffc107;background-color:transparent}.btn-outline-warning:not(:disabled):not(.disabled).active,.btn-outline-warning:not(:disabled):not(.disabled):active,.show>.btn-outline-warning.dropdown-toggle{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-outline-warning:not(:disabled):not(.disabled).active:focus,.btn-outline-warning:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-warning.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 .2rem rgba(255,193,7,.5);box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-outline-danger{color:#f84545;border-color:#f84545;border-color:#d6e1e5}.btn-outline-danger:hover{color:#fff;background-color:#f84545;border-color:#f84545}.btn-outline-danger.focus,.btn-outline-danger:focus{-webkit-box-shadow:0 0 0 .2rem rgba(248,69,69,.5);box-shadow:0 0 0 .2rem rgba(248,69,69,.5)}.btn-outline-danger.disabled,.btn-outline-danger:disabled{color:#f84545;background-color:transparent}.btn-outline-danger:not(:disabled):not(.disabled).active,.btn-outline-danger:not(:disabled):not(.disabled):active,.show>.btn-outline-danger.dropdown-toggle{color:#fff;background-color:#f84545;border-color:#f84545}.btn-outline-danger:not(:disabled):not(.disabled).active:focus,.btn-outline-danger:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-danger.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 .2rem rgba(248,69,69,.5);box-shadow:0 0 0 .2rem rgba(248,69,69,.5)}.btn-outline-light{color:#f8f9fa;border-color:#f8f9fa;border-color:#d6e1e5}.btn-outline-light:hover{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light.focus,.btn-outline-light:focus{-webkit-box-shadow:0 0 0 .2rem rgba(248,249,250,.5);box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-outline-light.disabled,.btn-outline-light:disabled{color:#f8f9fa;background-color:transparent}.btn-outline-light:not(:disabled):not(.disabled).active,.btn-outline-light:not(:disabled):not(.disabled):active,.show>.btn-outline-light.dropdown-toggle{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light:not(:disabled):not(.disabled).active:focus,.btn-outline-light:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-light.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 .2rem rgba(248,249,250,.5);box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-outline-dark{color:#343a40;border-color:#343a40;border-color:#d6e1e5}.btn-outline-dark:hover{color:#fff;background-color:#343a40;border-color:#343a40}.btn-outline-dark.focus,.btn-outline-dark:focus{-webkit-box-shadow:0 0 0 .2rem rgba(52,58,64,.5);box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-outline-dark.disabled,.btn-outline-dark:disabled{color:#343a40;background-color:transparent}.btn-outline-dark:not(:disabled):not(.disabled).active,.btn-outline-dark:not(:disabled):not(.disabled):active,.show>.btn-outline-dark.dropdown-toggle{color:#fff;background-color:#343a40;border-color:#343a40}.btn-outline-dark:not(:disabled):not(.disabled).active:focus,.btn-outline-dark:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-dark.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 .2rem rgba(52,58,64,.5);box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.navbar{padding:0}.navbar-nav{-ms-flex-preferred-size:100%;flex-basis:100%}.navbar-nav a{white-space:nowrap;color:#93a4aa}.navbar-nav .nav-link{padding-top:0;padding-bottom:0;line-height:4.5rem}.navbar-nav .dropdown-menu{background-color:#20282a;margin:0;border:0;border-radius:0;padding:0}.navbar-nav .dropdown-item{padding:0}.navbar-nav .dropdown-item:first-child,.navbar-nav .dropdown-item:last-child{border-radius:0}.navbar-nav .dropdown-item a{padding:0 15px;line-height:2.3rem;display:block;text-decoration:none;min-width:15rem}.navbar-nav .dropdown-item:hover{background-color:#364347}.navbar-nav .dropdown-item:hover a{color:#fff}.navbar-nav .pesquisa .dropdown-menu{min-width:15rem}.navbar-nav .search-form{padding:10px;min-width:20%}.navbar-nav a:not([href]):not([tabindex]){color:#777}.navbar-nav.justify-content-end .dropdown-menu{left:auto;right:0}.nav-pills .dropdown-menu{padding:0}.nav-pills .dropdown-item a{display:block}.nav-pills .dropdown-item.active a{color:#fff}@media (max-width:1199px){nav .container{max-width:none;-ms-flex-preferred-size:100%;flex-basis:100%}nav .caret{margin-left:-3px}}@media (max-width:1091px){.navbar-expand-lg .navbar-nav .nav-link{padding-left:4px;padding-right:4px;font-size:.95rem}}@media (max-width:991px){.navbar{padding:5px}.navbar-nav .nav-link{line-height:2.5rem}.navbar-nav.justify-content-end{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;-webkit-box-pack:start!important;-ms-flex-pack:start!important;justify-content:flex-start!important}.navbar-nav.justify-content-end>li{display:inline-block}}html{position:relative;min-height:100%}a,dd,div,dl,dt,form,h1,h2,h3,h4,h5,h6,img,p{margin:0;padding:0}.h1,h1{font-size:30px}.h2,h2{font-size:24px}.h3,h3{font-size:20px}.h4,h4{font-size:16px}.h5,h5{font-size:14px}.h6,h6{font-size:12px}p{margin:.25em 0}p.control-label{font-weight:700}label{margin-bottom:0;line-height:1}input[type=checkbox],input[type=radio]{margin:0 5px 0 0;position:relative}fieldset fieldset{font-size:95%}fieldset fieldset legend{font-size:18px}ul{margin:0}.hidden{display:none}.form-control-static{padding:.2em 0;padding-left:12px;background-color:#f7f7f7}.form-control-static:empty{display:none}.legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#333;border:0;border-bottom:1px solid #e5e5e5;clear:both}.page-header{margin:20px 0 10px}.caret.top{-webkit-transform:rotate(180deg);transform:rotate(180deg)}.lista-parlamentares .table td{vertical-align:middle}small{color:#777}.container-tabaux .sidebar-tabaux{background:#fafafa;margin-top:-70px;padding:10px;border:1px solid #eee}.container-tabaux .sidebar-tabaux .navbar-right{margin:0}.container-tabaux .sidebar-tabaux .nav-pills>li+li{margin-left:0}.container-tabaux .sidebar-tabaux li{width:100%}.container-tabaux .sidebar-tabaux span{display:none}.container-tabaux .sidebar-tabaux .dropdown-menu{padding:0;right:10px;margin-top:-5px;overflow:hidden}.container-tabaux .sidebar-tabaux .dropdown-menu a{border:0}.container-tabaux ul{list-style:none;padding:0}.container-tabaux .list{font-family:SourceSansProSemiBold,Helvetica,Arial,sans-serif;font-size:0}.container-tabaux .list,.container-tabaux .list ul{display:table;width:100%;margin:0}.container-tabaux .list li{width:50%;display:inline-block;position:relative}.container-tabaux .list>li{width:100%;border-bottom:1px solid #eee;padding-bottom:20px;margin-bottom:20px}.container-tabaux .list .head_title{color:#364347;font-size:1.7rem;text-transform:none}.container-tabaux .list a span{display:none}@media print{a[href]:after{content:none!important}}.container-home{position:relative;padding:2em 1.5em 1.5em 1.5em;max-width:1000px;margin:0 auto}.container-home a:hover{color:#444;-webkit-transition:.3s ease-in;-moz-transition:.3s ease-in;-o-transition:.3s ease-in}.container-home #homeIndex{text-align:center}.container-home .homeBanner span{color:#fff;font-size:32px;font-weight:600;display:inline-block;vertical-align:middle;padding:2px 45px 4px;border:2px solid}.container-home .homeBanner:after{display:inline-block;vertical-align:middle;height:100%}.container-home .homeBlock{display:inline-block;position:relative;background-color:#f3f3f3;width:190px;height:260px;margin:3px;text-align:center;font-size:0;overflow:hidden}.container-home .homeBlock>a{display:block;position:absolute;width:100%;height:100%;top:0;left:0}.container-home .homeBlock:after{content:"";display:inline-block;vertical-align:middle;height:100%;overflow:visible;clear:none;visibility:initial}.container-home .homeContent{position:relative;padding:10px;text-align:justify;font-size:14px;color:#fff;opacity:0;-webkit-transition:opacity .5s ease;transition:opacity .5s ease;display:inline-block;vertical-align:middle}.container-home .homeContent p{display:block;line-height:13px;font-size:80%;color:#fff}.container-home .homeIcon{position:relative;display:inline-block;width:105px;height:105px;border-radius:50%;background:#364347;z-index:1}.container-home .homeIcon:before{content:"";position:absolute;width:100%;height:100%;border-radius:50%;background:#364347;top:0;left:0;-webkit-transform:scale(.95);transform:scale(.95);-webkit-transition:-webkit-transform .6s ease;transition:-webkit-transform .6s ease;transition:transform .6s ease;transition:transform .6s ease,-webkit-transform .6s ease}.container-home .homeIcon img{position:absolute;margin:auto;top:0;bottom:0;right:0;left:0;-webkit-transition:opacity .4s ease .4s;transition:opacity .4s ease .4s}.container-home .homeFront{position:absolute;top:46%;width:100%;font-size:0;-webkit-transform:translateY(-60%);transform:translateY(-60%)}.container-home .homeFront h2{position:absolute;margin-top:18px;font-size:22px;font-weight:700;color:#595959!important;width:100%;padding:0 6%;z-index:0}.container-home .homeTitle{display:block;height:32px;text-align:center;width:100%;opacity:0;-webkit-transition:opacity .4s ease;transition:opacity .4s ease}.container-home .homeTitle:before{content:"";display:inline-block;vertical-align:middle;height:100%}.container-home .homeTitle h2{display:inline-block;vertical-align:middle;max-width:110px;font-size:14px;color:#fff!important;line-height:1em}.container-home .homeTitle img{display:inline-block;vertical-align:middle;height:30px;margin-right:5px}.container-home .homeBlock:hover .homeIcon:before{-webkit-transform:scale(3.6) translateY(7px);transform:scale(3.6) translateY(7px)}.container-home .homeBlock:hover .homeContent{opacity:1;-webkit-transition-delay:.2s;transition-delay:.2s}.container-home .homeBlock:hover .homeIcon img{opacity:0;-webkit-transition-duration:.2s;transition-duration:.2s;-webkit-transition-delay:0s;transition-delay:0s}.container-home .homeBlock:hover .homeTitle{opacity:1}.masthead{padding:10px}.masthead .navbar-brand{padding:0;color:inherit;font-size:24px}.masthead .navbar-brand img.img-responsive{height:95px;display:inline-block}.masthead .navbar-brand small{color:#93a4aa;font-size:75%;line-height:25px}.masthead .navbar-brand .vcenter{display:inline-block;vertical-align:middle;float:none;padding:10px;line-height:1.5rem}.masthead .nav-link{padding:.3rem .5rem;margin:0 1px}.masthead .nav-link:hover{background-color:#b2c6cd;color:#fff}@media (max-width:1091px){.masthead .navbar-brand{font-size:22px}.masthead .navbar-brand img.img-responsive{height:60px}}body{margin-bottom:160px}.footer{background:#364347;color:#fff;text-align:center;position:absolute;width:100%;bottom:0}.footer p{color:#fff;margin-top:10px}.footer .container{padding-top:25px}@media (max-width:991px){.footer{position:relative}}.jcrop-holder{-ms-touch-action:none;direction:ltr;text-align:left}.jcrop-hline,.jcrop-vline{background:#fff url(data:image/gif;base64,R0lGODlhCAAIAJEAAKqqqv///wAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQJCgAAACwAAAAACAAIAAACDZQFCadrzVRMB9FZ5SwAIfkECQoAAAAsAAAAAAgACAAAAg+ELqCYaudeW9ChyOyltQAAIfkECQoAAAAsAAAAAAgACAAAAg8EhGKXm+rQYtC0WGl9oAAAIfkECQoAAAAsAAAAAAgACAAAAg+EhWKQernaYmjCWLF7qAAAIfkECQoAAAAsAAAAAAgACAAAAg2EISmna81UTAfRWeUsACH5BAkKAAAALAAAAAAIAAgAAAIPFA6imGrnXlvQocjspbUAACH5BAkKAAAALAAAAAAIAAgAAAIPlIBgl5vq0GLQtFhpfaIAACH5BAUKAAAALAAAAAAIAAgAAAIPlIFgknq52mJowlixe6gAADs=);font-size:0;position:absolute}.jcrop-vline{height:100%;width:1px!important}.jcrop-vline.right{right:0}.jcrop-hline{height:1px!important;width:100%}.jcrop-hline.bottom{bottom:0}.jcrop-tracker{-webkit-tap-highlight-color:transparent;-webkit-touch-callout:none;-webkit-user-select:none;height:100%;width:100%}.jcrop-handle{background-color:#333;border:1px solid #eee;font-size:1px;height:7px;width:7px}.jcrop-handle.ord-n{left:50%;margin-left:-4px;margin-top:-4px;top:0}.jcrop-handle.ord-s{bottom:0;left:50%;margin-bottom:-4px;margin-left:-4px}.jcrop-handle.ord-e{margin-right:-4px;margin-top:-4px;right:0;top:50%}.jcrop-handle.ord-w{left:0;margin-left:-4px;margin-top:-4px;top:50%}.jcrop-handle.ord-nw{left:0;margin-left:-4px;margin-top:-4px;top:0}.jcrop-handle.ord-ne{margin-right:-4px;margin-top:-4px;right:0;top:0}.jcrop-handle.ord-se{bottom:0;margin-bottom:-4px;margin-right:-4px;right:0}.jcrop-handle.ord-sw{bottom:0;left:0;margin-bottom:-4px;margin-left:-4px}.jcrop-dragbar.ord-n,.jcrop-dragbar.ord-s{height:7px;width:100%}.jcrop-dragbar.ord-e,.jcrop-dragbar.ord-w{height:100%;width:7px}.jcrop-dragbar.ord-n{margin-top:-4px}.jcrop-dragbar.ord-s{bottom:0;margin-bottom:-4px}.jcrop-dragbar.ord-e{margin-right:-4px;right:0}.jcrop-dragbar.ord-w{margin-left:-4px}.jcrop-light .jcrop-hline,.jcrop-light .jcrop-vline{background:#fff;filter:alpha(opacity=70)!important;opacity:.7!important}.jcrop-light .jcrop-handle{-moz-border-radius:3px;-webkit-border-radius:3px;background-color:#000;border-color:#fff;border-radius:3px}.jcrop-dark .jcrop-hline,.jcrop-dark .jcrop-vline{background:#000;filter:alpha(opacity=70)!important;opacity:.7!important}.jcrop-dark .jcrop-handle{-moz-border-radius:3px;-webkit-border-radius:3px;background-color:#fff;border-color:#000;border-radius:3px}.solid-line .jcrop-hline,.solid-line .jcrop-vline{background:#fff}.jcrop-holder img,img.jcrop-preview{max-width:none}div.jcrop-image.size-warning .jcrop-hline,div.jcrop-image.size-warning .jcrop-vline{border:1px solid red;background:none}body.change-form .allow-fullsize+.help,body.change-form .jcrop-holder+.help{clear:left}body.change-form .jcrop-holder{float:left}div.allow-fullsize{padding:5px 0 0 10px} \ No newline at end of file diff --git a/sapl/static/css/painel.61177241.css b/sapl/static/css/painel.61177241.css deleted file mode 100644 index 1bd8169d7..000000000 --- a/sapl/static/css/painel.61177241.css +++ /dev/null @@ -1 +0,0 @@ -.painel-principal{background:#1c1b1b;font-family:Verdana}.painel-principal .text-title{color:#4fa64d}.painel-principal .text-subtitle{color:#459170}.painel-principal .text-value{color:#fff}.painel-principal .logo-painel{max-width:100%}.painel-principal .painels{-ms-flex-wrap:wrap;flex-wrap:wrap} \ No newline at end of file diff --git a/sapl/static/fonts/fa-brands-400.662c24d0.woff2 b/sapl/static/fonts/fa-brands-400.662c24d0.woff2 deleted file mode 100644 index d9f97dfba..000000000 Binary files a/sapl/static/fonts/fa-brands-400.662c24d0.woff2 and /dev/null differ diff --git a/sapl/static/fonts/fa-regular-400.6a9d786e.woff2 b/sapl/static/fonts/fa-regular-400.6a9d786e.woff2 deleted file mode 100644 index 0f55b0698..000000000 Binary files a/sapl/static/fonts/fa-regular-400.6a9d786e.woff2 and /dev/null differ diff --git a/sapl/static/fonts/fa-solid-900.3638e62e.woff2 b/sapl/static/fonts/fa-solid-900.3638e62e.woff2 deleted file mode 100644 index 318cd3da9..000000000 Binary files a/sapl/static/fonts/fa-solid-900.3638e62e.woff2 and /dev/null differ diff --git a/sapl/static/js/chunk-vendors.406f40ec.js b/sapl/static/js/chunk-vendors.406f40ec.js deleted file mode 100644 index 0eac61136..000000000 --- a/sapl/static/js/chunk-vendors.406f40ec.js +++ /dev/null @@ -1,2572 +0,0 @@ -(window["webpackJsonp"] = window["webpackJsonp"] || []).push([["chunk-vendors"],{ - -/***/ "014b": -/*!************************************************************!*\ - !*** ./node_modules/core-js/library/modules/es6.symbol.js ***! - \************************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("\n// ECMAScript 6 symbols shim\nvar global = __webpack_require__(/*! ./_global */ \"e53d\");\nvar has = __webpack_require__(/*! ./_has */ \"07e3\");\nvar DESCRIPTORS = __webpack_require__(/*! ./_descriptors */ \"8e60\");\nvar $export = __webpack_require__(/*! ./_export */ \"63b6\");\nvar redefine = __webpack_require__(/*! ./_redefine */ \"9138\");\nvar META = __webpack_require__(/*! ./_meta */ \"ebfd\").KEY;\nvar $fails = __webpack_require__(/*! ./_fails */ \"294c\");\nvar shared = __webpack_require__(/*! ./_shared */ \"dbdb\");\nvar setToStringTag = __webpack_require__(/*! ./_set-to-string-tag */ \"45f2\");\nvar uid = __webpack_require__(/*! ./_uid */ \"62a0\");\nvar wks = __webpack_require__(/*! ./_wks */ \"5168\");\nvar wksExt = __webpack_require__(/*! ./_wks-ext */ \"ccb9\");\nvar wksDefine = __webpack_require__(/*! ./_wks-define */ \"6718\");\nvar enumKeys = __webpack_require__(/*! ./_enum-keys */ \"47ee\");\nvar isArray = __webpack_require__(/*! ./_is-array */ \"9003\");\nvar anObject = __webpack_require__(/*! ./_an-object */ \"e4ae\");\nvar isObject = __webpack_require__(/*! ./_is-object */ \"f772\");\nvar toIObject = __webpack_require__(/*! ./_to-iobject */ \"36c3\");\nvar toPrimitive = __webpack_require__(/*! ./_to-primitive */ \"1bc3\");\nvar createDesc = __webpack_require__(/*! ./_property-desc */ \"aebd\");\nvar _create = __webpack_require__(/*! ./_object-create */ \"a159\");\nvar gOPNExt = __webpack_require__(/*! ./_object-gopn-ext */ \"0395\");\nvar $GOPD = __webpack_require__(/*! ./_object-gopd */ \"bf0b\");\nvar $DP = __webpack_require__(/*! ./_object-dp */ \"d9f6\");\nvar $keys = __webpack_require__(/*! ./_object-keys */ \"c3a1\");\nvar gOPD = $GOPD.f;\nvar dP = $DP.f;\nvar gOPN = gOPNExt.f;\nvar $Symbol = global.Symbol;\nvar $JSON = global.JSON;\nvar _stringify = $JSON && $JSON.stringify;\nvar PROTOTYPE = 'prototype';\nvar HIDDEN = wks('_hidden');\nvar TO_PRIMITIVE = wks('toPrimitive');\nvar isEnum = {}.propertyIsEnumerable;\nvar SymbolRegistry = shared('symbol-registry');\nvar AllSymbols = shared('symbols');\nvar OPSymbols = shared('op-symbols');\nvar ObjectProto = Object[PROTOTYPE];\nvar USE_NATIVE = typeof $Symbol == 'function';\nvar QObject = global.QObject;\n// Don't use setters in Qt Script, https://github.com/zloirock/core-js/issues/173\nvar setter = !QObject || !QObject[PROTOTYPE] || !QObject[PROTOTYPE].findChild;\n\n// fallback for old Android, https://code.google.com/p/v8/issues/detail?id=687\nvar setSymbolDesc = DESCRIPTORS && $fails(function () {\n return _create(dP({}, 'a', {\n get: function () { return dP(this, 'a', { value: 7 }).a; }\n })).a != 7;\n}) ? function (it, key, D) {\n var protoDesc = gOPD(ObjectProto, key);\n if (protoDesc) delete ObjectProto[key];\n dP(it, key, D);\n if (protoDesc && it !== ObjectProto) dP(ObjectProto, key, protoDesc);\n} : dP;\n\nvar wrap = function (tag) {\n var sym = AllSymbols[tag] = _create($Symbol[PROTOTYPE]);\n sym._k = tag;\n return sym;\n};\n\nvar isSymbol = USE_NATIVE && typeof $Symbol.iterator == 'symbol' ? function (it) {\n return typeof it == 'symbol';\n} : function (it) {\n return it instanceof $Symbol;\n};\n\nvar $defineProperty = function defineProperty(it, key, D) {\n if (it === ObjectProto) $defineProperty(OPSymbols, key, D);\n anObject(it);\n key = toPrimitive(key, true);\n anObject(D);\n if (has(AllSymbols, key)) {\n if (!D.enumerable) {\n if (!has(it, HIDDEN)) dP(it, HIDDEN, createDesc(1, {}));\n it[HIDDEN][key] = true;\n } else {\n if (has(it, HIDDEN) && it[HIDDEN][key]) it[HIDDEN][key] = false;\n D = _create(D, { enumerable: createDesc(0, false) });\n } return setSymbolDesc(it, key, D);\n } return dP(it, key, D);\n};\nvar $defineProperties = function defineProperties(it, P) {\n anObject(it);\n var keys = enumKeys(P = toIObject(P));\n var i = 0;\n var l = keys.length;\n var key;\n while (l > i) $defineProperty(it, key = keys[i++], P[key]);\n return it;\n};\nvar $create = function create(it, P) {\n return P === undefined ? _create(it) : $defineProperties(_create(it), P);\n};\nvar $propertyIsEnumerable = function propertyIsEnumerable(key) {\n var E = isEnum.call(this, key = toPrimitive(key, true));\n if (this === ObjectProto && has(AllSymbols, key) && !has(OPSymbols, key)) return false;\n return E || !has(this, key) || !has(AllSymbols, key) || has(this, HIDDEN) && this[HIDDEN][key] ? E : true;\n};\nvar $getOwnPropertyDescriptor = function getOwnPropertyDescriptor(it, key) {\n it = toIObject(it);\n key = toPrimitive(key, true);\n if (it === ObjectProto && has(AllSymbols, key) && !has(OPSymbols, key)) return;\n var D = gOPD(it, key);\n if (D && has(AllSymbols, key) && !(has(it, HIDDEN) && it[HIDDEN][key])) D.enumerable = true;\n return D;\n};\nvar $getOwnPropertyNames = function getOwnPropertyNames(it) {\n var names = gOPN(toIObject(it));\n var result = [];\n var i = 0;\n var key;\n while (names.length > i) {\n if (!has(AllSymbols, key = names[i++]) && key != HIDDEN && key != META) result.push(key);\n } return result;\n};\nvar $getOwnPropertySymbols = function getOwnPropertySymbols(it) {\n var IS_OP = it === ObjectProto;\n var names = gOPN(IS_OP ? OPSymbols : toIObject(it));\n var result = [];\n var i = 0;\n var key;\n while (names.length > i) {\n if (has(AllSymbols, key = names[i++]) && (IS_OP ? has(ObjectProto, key) : true)) result.push(AllSymbols[key]);\n } return result;\n};\n\n// 19.4.1.1 Symbol([description])\nif (!USE_NATIVE) {\n $Symbol = function Symbol() {\n if (this instanceof $Symbol) throw TypeError('Symbol is not a constructor!');\n var tag = uid(arguments.length > 0 ? arguments[0] : undefined);\n var $set = function (value) {\n if (this === ObjectProto) $set.call(OPSymbols, value);\n if (has(this, HIDDEN) && has(this[HIDDEN], tag)) this[HIDDEN][tag] = false;\n setSymbolDesc(this, tag, createDesc(1, value));\n };\n if (DESCRIPTORS && setter) setSymbolDesc(ObjectProto, tag, { configurable: true, set: $set });\n return wrap(tag);\n };\n redefine($Symbol[PROTOTYPE], 'toString', function toString() {\n return this._k;\n });\n\n $GOPD.f = $getOwnPropertyDescriptor;\n $DP.f = $defineProperty;\n __webpack_require__(/*! ./_object-gopn */ \"6abf\").f = gOPNExt.f = $getOwnPropertyNames;\n __webpack_require__(/*! ./_object-pie */ \"355d\").f = $propertyIsEnumerable;\n __webpack_require__(/*! ./_object-gops */ \"9aa9\").f = $getOwnPropertySymbols;\n\n if (DESCRIPTORS && !__webpack_require__(/*! ./_library */ \"b8e3\")) {\n redefine(ObjectProto, 'propertyIsEnumerable', $propertyIsEnumerable, true);\n }\n\n wksExt.f = function (name) {\n return wrap(wks(name));\n };\n}\n\n$export($export.G + $export.W + $export.F * !USE_NATIVE, { Symbol: $Symbol });\n\nfor (var es6Symbols = (\n // 19.4.2.2, 19.4.2.3, 19.4.2.4, 19.4.2.6, 19.4.2.8, 19.4.2.9, 19.4.2.10, 19.4.2.11, 19.4.2.12, 19.4.2.13, 19.4.2.14\n 'hasInstance,isConcatSpreadable,iterator,match,replace,search,species,split,toPrimitive,toStringTag,unscopables'\n).split(','), j = 0; es6Symbols.length > j;)wks(es6Symbols[j++]);\n\nfor (var wellKnownSymbols = $keys(wks.store), k = 0; wellKnownSymbols.length > k;) wksDefine(wellKnownSymbols[k++]);\n\n$export($export.S + $export.F * !USE_NATIVE, 'Symbol', {\n // 19.4.2.1 Symbol.for(key)\n 'for': function (key) {\n return has(SymbolRegistry, key += '')\n ? SymbolRegistry[key]\n : SymbolRegistry[key] = $Symbol(key);\n },\n // 19.4.2.5 Symbol.keyFor(sym)\n keyFor: function keyFor(sym) {\n if (!isSymbol(sym)) throw TypeError(sym + ' is not a symbol!');\n for (var key in SymbolRegistry) if (SymbolRegistry[key] === sym) return key;\n },\n useSetter: function () { setter = true; },\n useSimple: function () { setter = false; }\n});\n\n$export($export.S + $export.F * !USE_NATIVE, 'Object', {\n // 19.1.2.2 Object.create(O [, Properties])\n create: $create,\n // 19.1.2.4 Object.defineProperty(O, P, Attributes)\n defineProperty: $defineProperty,\n // 19.1.2.3 Object.defineProperties(O, Properties)\n defineProperties: $defineProperties,\n // 19.1.2.6 Object.getOwnPropertyDescriptor(O, P)\n getOwnPropertyDescriptor: $getOwnPropertyDescriptor,\n // 19.1.2.7 Object.getOwnPropertyNames(O)\n getOwnPropertyNames: $getOwnPropertyNames,\n // 19.1.2.8 Object.getOwnPropertySymbols(O)\n getOwnPropertySymbols: $getOwnPropertySymbols\n});\n\n// 24.3.2 JSON.stringify(value [, replacer [, space]])\n$JSON && $export($export.S + $export.F * (!USE_NATIVE || $fails(function () {\n var S = $Symbol();\n // MS Edge converts symbol values to JSON as {}\n // WebKit converts symbol values to JSON as null\n // V8 throws on boxed symbols\n return _stringify([S]) != '[null]' || _stringify({ a: S }) != '{}' || _stringify(Object(S)) != '{}';\n})), 'JSON', {\n stringify: function stringify(it) {\n var args = [it];\n var i = 1;\n var replacer, $replacer;\n while (arguments.length > i) args.push(arguments[i++]);\n $replacer = replacer = args[1];\n if (!isObject(replacer) && it === undefined || isSymbol(it)) return; // IE8 returns string on undefined\n if (!isArray(replacer)) replacer = function (key, value) {\n if (typeof $replacer == 'function') value = $replacer.call(this, key, value);\n if (!isSymbol(value)) return value;\n };\n args[1] = replacer;\n return _stringify.apply($JSON, args);\n }\n});\n\n// 19.4.3.4 Symbol.prototype[@@toPrimitive](hint)\n$Symbol[PROTOTYPE][TO_PRIMITIVE] || __webpack_require__(/*! ./_hide */ \"35e8\")($Symbol[PROTOTYPE], TO_PRIMITIVE, $Symbol[PROTOTYPE].valueOf);\n// 19.4.3.5 Symbol.prototype[@@toStringTag]\nsetToStringTag($Symbol, 'Symbol');\n// 20.2.1.9 Math[@@toStringTag]\nsetToStringTag(Math, 'Math', true);\n// 24.3.3 JSON[@@toStringTag]\nsetToStringTag(global.JSON, 'JSON', true);\n//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMDE0Yi5qcyIsInNvdXJjZXMiOlsid2VicGFjazovLy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL2xpYnJhcnkvbW9kdWxlcy9lczYuc3ltYm9sLmpzPzAxNGIiXSwic291cmNlc0NvbnRlbnQiOlsiJ3VzZSBzdHJpY3QnO1xuLy8gRUNNQVNjcmlwdCA2IHN5bWJvbHMgc2hpbVxudmFyIGdsb2JhbCA9IHJlcXVpcmUoJy4vX2dsb2JhbCcpO1xudmFyIGhhcyA9IHJlcXVpcmUoJy4vX2hhcycpO1xudmFyIERFU0NSSVBUT1JTID0gcmVxdWlyZSgnLi9fZGVzY3JpcHRvcnMnKTtcbnZhciAkZXhwb3J0ID0gcmVxdWlyZSgnLi9fZXhwb3J0Jyk7XG52YXIgcmVkZWZpbmUgPSByZXF1aXJlKCcuL19yZWRlZmluZScpO1xudmFyIE1FVEEgPSByZXF1aXJlKCcuL19tZXRhJykuS0VZO1xudmFyICRmYWlscyA9IHJlcXVpcmUoJy4vX2ZhaWxzJyk7XG52YXIgc2hhcmVkID0gcmVxdWlyZSgnLi9fc2hhcmVkJyk7XG52YXIgc2V0VG9TdHJpbmdUYWcgPSByZXF1aXJlKCcuL19zZXQtdG8tc3RyaW5nLXRhZycpO1xudmFyIHVpZCA9IHJlcXVpcmUoJy4vX3VpZCcpO1xudmFyIHdrcyA9IHJlcXVpcmUoJy4vX3drcycpO1xudmFyIHdrc0V4dCA9IHJlcXVpcmUoJy4vX3drcy1leHQnKTtcbnZhciB3a3NEZWZpbmUgPSByZXF1aXJlKCcuL193a3MtZGVmaW5lJyk7XG52YXIgZW51bUtleXMgPSByZXF1aXJlKCcuL19lbnVtLWtleXMnKTtcbnZhciBpc0FycmF5ID0gcmVxdWlyZSgnLi9faXMtYXJyYXknKTtcbnZhciBhbk9iamVjdCA9IHJlcXVpcmUoJy4vX2FuLW9iamVjdCcpO1xudmFyIGlzT2JqZWN0ID0gcmVxdWlyZSgnLi9faXMtb2JqZWN0Jyk7XG52YXIgdG9JT2JqZWN0ID0gcmVxdWlyZSgnLi9fdG8taW9iamVjdCcpO1xudmFyIHRvUHJpbWl0aXZlID0gcmVxdWlyZSgnLi9fdG8tcHJpbWl0aXZlJyk7XG52YXIgY3JlYXRlRGVzYyA9IHJlcXVpcmUoJy4vX3Byb3BlcnR5LWRlc2MnKTtcbnZhciBfY3JlYXRlID0gcmVxdWlyZSgnLi9fb2JqZWN0LWNyZWF0ZScpO1xudmFyIGdPUE5FeHQgPSByZXF1aXJlKCcuL19vYmplY3QtZ29wbi1leHQnKTtcbnZhciAkR09QRCA9IHJlcXVpcmUoJy4vX29iamVjdC1nb3BkJyk7XG52YXIgJERQID0gcmVxdWlyZSgnLi9fb2JqZWN0LWRwJyk7XG52YXIgJGtleXMgPSByZXF1aXJlKCcuL19vYmplY3Qta2V5cycpO1xudmFyIGdPUEQgPSAkR09QRC5mO1xudmFyIGRQID0gJERQLmY7XG52YXIgZ09QTiA9IGdPUE5FeHQuZjtcbnZhciAkU3ltYm9sID0gZ2xvYmFsLlN5bWJvbDtcbnZhciAkSlNPTiA9IGdsb2JhbC5KU09OO1xudmFyIF9zdHJpbmdpZnkgPSAkSlNPTiAmJiAkSlNPTi5zdHJpbmdpZnk7XG52YXIgUFJPVE9UWVBFID0gJ3Byb3RvdHlwZSc7XG52YXIgSElEREVOID0gd2tzKCdfaGlkZGVuJyk7XG52YXIgVE9fUFJJTUlUSVZFID0gd2tzKCd0b1ByaW1pdGl2ZScpO1xudmFyIGlzRW51bSA9IHt9LnByb3BlcnR5SXNFbnVtZXJhYmxlO1xudmFyIFN5bWJvbFJlZ2lzdHJ5ID0gc2hhcmVkKCdzeW1ib2wtcmVnaXN0cnknKTtcbnZhciBBbGxTeW1ib2xzID0gc2hhcmVkKCdzeW1ib2xzJyk7XG52YXIgT1BTeW1ib2xzID0gc2hhcmVkKCdvcC1zeW1ib2xzJyk7XG52YXIgT2JqZWN0UHJvdG8gPSBPYmplY3RbUFJPVE9UWVBFXTtcbnZhciBVU0VfTkFUSVZFID0gdHlwZW9mICRTeW1ib2wgPT0gJ2Z1bmN0aW9uJztcbnZhciBRT2JqZWN0ID0gZ2xvYmFsLlFPYmplY3Q7XG4vLyBEb24ndCB1c2Ugc2V0dGVycyBpbiBRdCBTY3JpcHQsIGh0dHBzOi8vZ2l0aHViLmNvbS96bG9pcm9jay9jb3JlLWpzL2lzc3Vlcy8xNzNcbnZhciBzZXR0ZXIgPSAhUU9iamVjdCB8fCAhUU9iamVjdFtQUk9UT1RZUEVdIHx8ICFRT2JqZWN0W1BST1RPVFlQRV0uZmluZENoaWxkO1xuXG4vLyBmYWxsYmFjayBmb3Igb2xkIEFuZHJvaWQsIGh0dHBzOi8vY29kZS5nb29nbGUuY29tL3AvdjgvaXNzdWVzL2RldGFpbD9pZD02ODdcbnZhciBzZXRTeW1ib2xEZXNjID0gREVTQ1JJUFRPUlMgJiYgJGZhaWxzKGZ1bmN0aW9uICgpIHtcbiAgcmV0dXJuIF9jcmVhdGUoZFAoe30sICdhJywge1xuICAgIGdldDogZnVuY3Rpb24gKCkgeyByZXR1cm4gZFAodGhpcywgJ2EnLCB7IHZhbHVlOiA3IH0pLmE7IH1cbiAgfSkpLmEgIT0gNztcbn0pID8gZnVuY3Rpb24gKGl0LCBrZXksIEQpIHtcbiAgdmFyIHByb3RvRGVzYyA9IGdPUEQoT2JqZWN0UHJvdG8sIGtleSk7XG4gIGlmIChwcm90b0Rlc2MpIGRlbGV0ZSBPYmplY3RQcm90b1trZXldO1xuICBkUChpdCwga2V5LCBEKTtcbiAgaWYgKHByb3RvRGVzYyAmJiBpdCAhPT0gT2JqZWN0UHJvdG8pIGRQKE9iamVjdFByb3RvLCBrZXksIHByb3RvRGVzYyk7XG59IDogZFA7XG5cbnZhciB3cmFwID0gZnVuY3Rpb24gKHRhZykge1xuICB2YXIgc3ltID0gQWxsU3ltYm9sc1t0YWddID0gX2NyZWF0ZSgkU3ltYm9sW1BST1RPVFlQRV0pO1xuICBzeW0uX2sgPSB0YWc7XG4gIHJldHVybiBzeW07XG59O1xuXG52YXIgaXNTeW1ib2wgPSBVU0VfTkFUSVZFICYmIHR5cGVvZiAkU3ltYm9sLml0ZXJhdG9yID09ICdzeW1ib2wnID8gZnVuY3Rpb24gKGl0KSB7XG4gIHJldHVybiB0eXBlb2YgaXQgPT0gJ3N5bWJvbCc7XG59IDogZnVuY3Rpb24gKGl0KSB7XG4gIHJldHVybiBpdCBpbnN0YW5jZW9mICRTeW1ib2w7XG59O1xuXG52YXIgJGRlZmluZVByb3BlcnR5ID0gZnVuY3Rpb24gZGVmaW5lUHJvcGVydHkoaXQsIGtleSwgRCkge1xuICBpZiAoaXQgPT09IE9iamVjdFByb3RvKSAkZGVmaW5lUHJvcGVydHkoT1BTeW1ib2xzLCBrZXksIEQpO1xuICBhbk9iamVjdChpdCk7XG4gIGtleSA9IHRvUHJpbWl0aXZlKGtleSwgdHJ1ZSk7XG4gIGFuT2JqZWN0KEQpO1xuICBpZiAoaGFzKEFsbFN5bWJvbHMsIGtleSkpIHtcbiAgICBpZiAoIUQuZW51bWVyYWJsZSkge1xuICAgICAgaWYgKCFoYXMoaXQsIEhJRERFTikpIGRQKGl0LCBISURERU4sIGNyZWF0ZURlc2MoMSwge30pKTtcbiAgICAgIGl0W0hJRERFTl1ba2V5XSA9IHRydWU7XG4gICAgfSBlbHNlIHtcbiAgICAgIGlmIChoYXMoaXQsIEhJRERFTikgJiYgaXRbSElEREVOXVtrZXldKSBpdFtISURERU5dW2tleV0gPSBmYWxzZTtcbiAgICAgIEQgPSBfY3JlYXRlKEQsIHsgZW51bWVyYWJsZTogY3JlYXRlRGVzYygwLCBmYWxzZSkgfSk7XG4gICAgfSByZXR1cm4gc2V0U3ltYm9sRGVzYyhpdCwga2V5LCBEKTtcbiAgfSByZXR1cm4gZFAoaXQsIGtleSwgRCk7XG59O1xudmFyICRkZWZpbmVQcm9wZXJ0aWVzID0gZnVuY3Rpb24gZGVmaW5lUHJvcGVydGllcyhpdCwgUCkge1xuICBhbk9iamVjdChpdCk7XG4gIHZhciBrZXlzID0gZW51bUtleXMoUCA9IHRvSU9iamVjdChQKSk7XG4gIHZhciBpID0gMDtcbiAgdmFyIGwgPSBrZXlzLmxlbmd0aDtcbiAgdmFyIGtleTtcbiAgd2hpbGUgKGwgPiBpKSAkZGVmaW5lUHJvcGVydHkoaXQsIGtleSA9IGtleXNbaSsrXSwgUFtrZXldKTtcbiAgcmV0dXJuIGl0O1xufTtcbnZhciAkY3JlYXRlID0gZnVuY3Rpb24gY3JlYXRlKGl0LCBQKSB7XG4gIHJldHVybiBQID09PSB1bmRlZmluZWQgPyBfY3JlYXRlKGl0KSA6ICRkZWZpbmVQcm9wZXJ0aWVzKF9jcmVhdGUoaXQpLCBQKTtcbn07XG52YXIgJHByb3BlcnR5SXNFbnVtZXJhYmxlID0gZnVuY3Rpb24gcHJvcGVydHlJc0VudW1lcmFibGUoa2V5KSB7XG4gIHZhciBFID0gaXNFbnVtLmNhbGwodGhpcywga2V5ID0gdG9QcmltaXRpdmUoa2V5LCB0cnVlKSk7XG4gIGlmICh0aGlzID09PSBPYmplY3RQcm90byAmJiBoYXMoQWxsU3ltYm9scywga2V5KSAmJiAhaGFzKE9QU3ltYm9scywga2V5KSkgcmV0dXJuIGZhbHNlO1xuICByZXR1cm4gRSB8fCAhaGFzKHRoaXMsIGtleSkgfHwgIWhhcyhBbGxTeW1ib2xzLCBrZXkpIHx8IGhhcyh0aGlzLCBISURERU4pICYmIHRoaXNbSElEREVOXVtrZXldID8gRSA6IHRydWU7XG59O1xudmFyICRnZXRPd25Qcm9wZXJ0eURlc2NyaXB0b3IgPSBmdW5jdGlvbiBnZXRPd25Qcm9wZXJ0eURlc2NyaXB0b3IoaXQsIGtleSkge1xuICBpdCA9IHRvSU9iamVjdChpdCk7XG4gIGtleSA9IHRvUHJpbWl0aXZlKGtleSwgdHJ1ZSk7XG4gIGlmIChpdCA9PT0gT2JqZWN0UHJvdG8gJiYgaGFzKEFsbFN5bWJvbHMsIGtleSkgJiYgIWhhcyhPUFN5bWJvbHMsIGtleSkpIHJldHVybjtcbiAgdmFyIEQgPSBnT1BEKGl0LCBrZXkpO1xuICBpZiAoRCAmJiBoYXMoQWxsU3ltYm9scywga2V5KSAmJiAhKGhhcyhpdCwgSElEREVOKSAmJiBpdFtISURERU5dW2tleV0pKSBELmVudW1lcmFibGUgPSB0cnVlO1xuICByZXR1cm4gRDtcbn07XG52YXIgJGdldE93blByb3BlcnR5TmFtZXMgPSBmdW5jdGlvbiBnZXRPd25Qcm9wZXJ0eU5hbWVzKGl0KSB7XG4gIHZhciBuYW1lcyA9IGdPUE4odG9JT2JqZWN0KGl0KSk7XG4gIHZhciByZXN1bHQgPSBbXTtcbiAgdmFyIGkgPSAwO1xuICB2YXIga2V5O1xuICB3aGlsZSAobmFtZXMubGVuZ3RoID4gaSkge1xuICAgIGlmICghaGFzKEFsbFN5bWJvbHMsIGtleSA9IG5hbWVzW2krK10pICYmIGtleSAhPSBISURERU4gJiYga2V5ICE9IE1FVEEpIHJlc3VsdC5wdXNoKGtleSk7XG4gIH0gcmV0dXJuIHJlc3VsdDtcbn07XG52YXIgJGdldE93blByb3BlcnR5U3ltYm9scyA9IGZ1bmN0aW9uIGdldE93blByb3BlcnR5U3ltYm9scyhpdCkge1xuICB2YXIgSVNfT1AgPSBpdCA9PT0gT2JqZWN0UHJvdG87XG4gIHZhciBuYW1lcyA9IGdPUE4oSVNfT1AgPyBPUFN5bWJvbHMgOiB0b0lPYmplY3QoaXQpKTtcbiAgdmFyIHJlc3VsdCA9IFtdO1xuICB2YXIgaSA9IDA7XG4gIHZhciBrZXk7XG4gIHdoaWxlIChuYW1lcy5sZW5ndGggPiBpKSB7XG4gICAgaWYgKGhhcyhBbGxTeW1ib2xzLCBrZXkgPSBuYW1lc1tpKytdKSAmJiAoSVNfT1AgPyBoYXMoT2JqZWN0UHJvdG8sIGtleSkgOiB0cnVlKSkgcmVzdWx0LnB1c2goQWxsU3ltYm9sc1trZXldKTtcbiAgfSByZXR1cm4gcmVzdWx0O1xufTtcblxuLy8gMTkuNC4xLjEgU3ltYm9sKFtkZXNjcmlwdGlvbl0pXG5pZiAoIVVTRV9OQVRJVkUpIHtcbiAgJFN5bWJvbCA9IGZ1bmN0aW9uIFN5bWJvbCgpIHtcbiAgICBpZiAodGhpcyBpbnN0YW5jZW9mICRTeW1ib2wpIHRocm93IFR5cGVFcnJvcignU3ltYm9sIGlzIG5vdCBhIGNvbnN0cnVjdG9yIScpO1xuICAgIHZhciB0YWcgPSB1aWQoYXJndW1lbnRzLmxlbmd0aCA+IDAgPyBhcmd1bWVudHNbMF0gOiB1bmRlZmluZWQpO1xuICAgIHZhciAkc2V0ID0gZnVuY3Rpb24gKHZhbHVlKSB7XG4gICAgICBpZiAodGhpcyA9PT0gT2JqZWN0UHJvdG8pICRzZXQuY2FsbChPUFN5bWJvbHMsIHZhbHVlKTtcbiAgICAgIGlmIChoYXModGhpcywgSElEREVOKSAmJiBoYXModGhpc1tISURERU5dLCB0YWcpKSB0aGlzW0hJRERFTl1bdGFnXSA9IGZhbHNlO1xuICAgICAgc2V0U3ltYm9sRGVzYyh0aGlzLCB0YWcsIGNyZWF0ZURlc2MoMSwgdmFsdWUpKTtcbiAgICB9O1xuICAgIGlmIChERVNDUklQVE9SUyAmJiBzZXR0ZXIpIHNldFN5bWJvbERlc2MoT2JqZWN0UHJvdG8sIHRhZywgeyBjb25maWd1cmFibGU6IHRydWUsIHNldDogJHNldCB9KTtcbiAgICByZXR1cm4gd3JhcCh0YWcpO1xuICB9O1xuICByZWRlZmluZSgkU3ltYm9sW1BST1RPVFlQRV0sICd0b1N0cmluZycsIGZ1bmN0aW9uIHRvU3RyaW5nKCkge1xuICAgIHJldHVybiB0aGlzLl9rO1xuICB9KTtcblxuICAkR09QRC5mID0gJGdldE93blByb3BlcnR5RGVzY3JpcHRvcjtcbiAgJERQLmYgPSAkZGVmaW5lUHJvcGVydHk7XG4gIHJlcXVpcmUoJy4vX29iamVjdC1nb3BuJykuZiA9IGdPUE5FeHQuZiA9ICRnZXRPd25Qcm9wZXJ0eU5hbWVzO1xuICByZXF1aXJlKCcuL19vYmplY3QtcGllJykuZiA9ICRwcm9wZXJ0eUlzRW51bWVyYWJsZTtcbiAgcmVxdWlyZSgnLi9fb2JqZWN0LWdvcHMnKS5mID0gJGdldE93blByb3BlcnR5U3ltYm9scztcblxuICBpZiAoREVTQ1JJUFRPUlMgJiYgIXJlcXVpcmUoJy4vX2xpYnJhcnknKSkge1xuICAgIHJlZGVmaW5lKE9iamVjdFByb3RvLCAncHJvcGVydHlJc0VudW1lcmFibGUnLCAkcHJvcGVydHlJc0VudW1lcmFibGUsIHRydWUpO1xuICB9XG5cbiAgd2tzRXh0LmYgPSBmdW5jdGlvbiAobmFtZSkge1xuICAgIHJldHVybiB3cmFwKHdrcyhuYW1lKSk7XG4gIH07XG59XG5cbiRleHBvcnQoJGV4cG9ydC5HICsgJGV4cG9ydC5XICsgJGV4cG9ydC5GICogIVVTRV9OQVRJVkUsIHsgU3ltYm9sOiAkU3ltYm9sIH0pO1xuXG5mb3IgKHZhciBlczZTeW1ib2xzID0gKFxuICAvLyAxOS40LjIuMiwgMTkuNC4yLjMsIDE5LjQuMi40LCAxOS40LjIuNiwgMTkuNC4yLjgsIDE5LjQuMi45LCAxOS40LjIuMTAsIDE5LjQuMi4xMSwgMTkuNC4yLjEyLCAxOS40LjIuMTMsIDE5LjQuMi4xNFxuICAnaGFzSW5zdGFuY2UsaXNDb25jYXRTcHJlYWRhYmxlLGl0ZXJhdG9yLG1hdGNoLHJlcGxhY2Usc2VhcmNoLHNwZWNpZXMsc3BsaXQsdG9QcmltaXRpdmUsdG9TdHJpbmdUYWcsdW5zY29wYWJsZXMnXG4pLnNwbGl0KCcsJyksIGogPSAwOyBlczZTeW1ib2xzLmxlbmd0aCA+IGo7KXdrcyhlczZTeW1ib2xzW2orK10pO1xuXG5mb3IgKHZhciB3ZWxsS25vd25TeW1ib2xzID0gJGtleXMod2tzLnN0b3JlKSwgayA9IDA7IHdlbGxLbm93blN5bWJvbHMubGVuZ3RoID4gazspIHdrc0RlZmluZSh3ZWxsS25vd25TeW1ib2xzW2srK10pO1xuXG4kZXhwb3J0KCRleHBvcnQuUyArICRleHBvcnQuRiAqICFVU0VfTkFUSVZFLCAnU3ltYm9sJywge1xuICAvLyAxOS40LjIuMSBTeW1ib2wuZm9yKGtleSlcbiAgJ2Zvcic6IGZ1bmN0aW9uIChrZXkpIHtcbiAgICByZXR1cm4gaGFzKFN5bWJvbFJlZ2lzdHJ5LCBrZXkgKz0gJycpXG4gICAgICA/IFN5bWJvbFJlZ2lzdHJ5W2tleV1cbiAgICAgIDogU3ltYm9sUmVnaXN0cnlba2V5XSA9ICRTeW1ib2woa2V5KTtcbiAgfSxcbiAgLy8gMTkuNC4yLjUgU3ltYm9sLmtleUZvcihzeW0pXG4gIGtleUZvcjogZnVuY3Rpb24ga2V5Rm9yKHN5bSkge1xuICAgIGlmICghaXNTeW1ib2woc3ltKSkgdGhyb3cgVHlwZUVycm9yKHN5bSArICcgaXMgbm90IGEgc3ltYm9sIScpO1xuICAgIGZvciAodmFyIGtleSBpbiBTeW1ib2xSZWdpc3RyeSkgaWYgKFN5bWJvbFJlZ2lzdHJ5W2tleV0gPT09IHN5bSkgcmV0dXJuIGtleTtcbiAgfSxcbiAgdXNlU2V0dGVyOiBmdW5jdGlvbiAoKSB7IHNldHRlciA9IHRydWU7IH0sXG4gIHVzZVNpbXBsZTogZnVuY3Rpb24gKCkgeyBzZXR0ZXIgPSBmYWxzZTsgfVxufSk7XG5cbiRleHBvcnQoJGV4cG9ydC5TICsgJGV4cG9ydC5GICogIVVTRV9OQVRJVkUsICdPYmplY3QnLCB7XG4gIC8vIDE5LjEuMi4yIE9iamVjdC5jcmVhdGUoTyBbLCBQcm9wZXJ0aWVzXSlcbiAgY3JlYXRlOiAkY3JlYXRlLFxuICAvLyAxOS4xLjIuNCBPYmplY3QuZGVmaW5lUHJvcGVydHkoTywgUCwgQXR0cmlidXRlcylcbiAgZGVmaW5lUHJvcGVydHk6ICRkZWZpbmVQcm9wZXJ0eSxcbiAgLy8gMTkuMS4yLjMgT2JqZWN0LmRlZmluZVByb3BlcnRpZXMoTywgUHJvcGVydGllcylcbiAgZGVmaW5lUHJvcGVydGllczogJGRlZmluZVByb3BlcnRpZXMsXG4gIC8vIDE5LjEuMi42IE9iamVjdC5nZXRPd25Qcm9wZXJ0eURlc2NyaXB0b3IoTywgUClcbiAgZ2V0T3duUHJvcGVydHlEZXNjcmlwdG9yOiAkZ2V0T3duUHJvcGVydHlEZXNjcmlwdG9yLFxuICAvLyAxOS4xLjIuNyBPYmplY3QuZ2V0T3duUHJvcGVydHlOYW1lcyhPKVxuICBnZXRPd25Qcm9wZXJ0eU5hbWVzOiAkZ2V0T3duUHJvcGVydHlOYW1lcyxcbiAgLy8gMTkuMS4yLjggT2JqZWN0LmdldE93blByb3BlcnR5U3ltYm9scyhPKVxuICBnZXRPd25Qcm9wZXJ0eVN5bWJvbHM6ICRnZXRPd25Qcm9wZXJ0eVN5bWJvbHNcbn0pO1xuXG4vLyAyNC4zLjIgSlNPTi5zdHJpbmdpZnkodmFsdWUgWywgcmVwbGFjZXIgWywgc3BhY2VdXSlcbiRKU09OICYmICRleHBvcnQoJGV4cG9ydC5TICsgJGV4cG9ydC5GICogKCFVU0VfTkFUSVZFIHx8ICRmYWlscyhmdW5jdGlvbiAoKSB7XG4gIHZhciBTID0gJFN5bWJvbCgpO1xuICAvLyBNUyBFZGdlIGNvbnZlcnRzIHN5bWJvbCB2YWx1ZXMgdG8gSlNPTiBhcyB7fVxuICAvLyBXZWJLaXQgY29udmVydHMgc3ltYm9sIHZhbHVlcyB0byBKU09OIGFzIG51bGxcbiAgLy8gVjggdGhyb3dzIG9uIGJveGVkIHN5bWJvbHNcbiAgcmV0dXJuIF9zdHJpbmdpZnkoW1NdKSAhPSAnW251bGxdJyB8fCBfc3RyaW5naWZ5KHsgYTogUyB9KSAhPSAne30nIHx8IF9zdHJpbmdpZnkoT2JqZWN0KFMpKSAhPSAne30nO1xufSkpLCAnSlNPTicsIHtcbiAgc3RyaW5naWZ5OiBmdW5jdGlvbiBzdHJpbmdpZnkoaXQpIHtcbiAgICB2YXIgYXJncyA9IFtpdF07XG4gICAgdmFyIGkgPSAxO1xuICAgIHZhciByZXBsYWNlciwgJHJlcGxhY2VyO1xuICAgIHdoaWxlIChhcmd1bWVudHMubGVuZ3RoID4gaSkgYXJncy5wdXNoKGFyZ3VtZW50c1tpKytdKTtcbiAgICAkcmVwbGFjZXIgPSByZXBsYWNlciA9IGFyZ3NbMV07XG4gICAgaWYgKCFpc09iamVjdChyZXBsYWNlcikgJiYgaXQgPT09IHVuZGVmaW5lZCB8fCBpc1N5bWJvbChpdCkpIHJldHVybjsgLy8gSUU4IHJldHVybnMgc3RyaW5nIG9uIHVuZGVmaW5lZFxuICAgIGlmICghaXNBcnJheShyZXBsYWNlcikpIHJlcGxhY2VyID0gZnVuY3Rpb24gKGtleSwgdmFsdWUpIHtcbiAgICAgIGlmICh0eXBlb2YgJHJlcGxhY2VyID09ICdmdW5jdGlvbicpIHZhbHVlID0gJHJlcGxhY2VyLmNhbGwodGhpcywga2V5LCB2YWx1ZSk7XG4gICAgICBpZiAoIWlzU3ltYm9sKHZhbHVlKSkgcmV0dXJuIHZhbHVlO1xuICAgIH07XG4gICAgYXJnc1sxXSA9IHJlcGxhY2VyO1xuICAgIHJldHVybiBfc3RyaW5naWZ5LmFwcGx5KCRKU09OLCBhcmdzKTtcbiAgfVxufSk7XG5cbi8vIDE5LjQuMy40IFN5bWJvbC5wcm90b3R5cGVbQEB0b1ByaW1pdGl2ZV0oaGludClcbiRTeW1ib2xbUFJPVE9UWVBFXVtUT19QUklNSVRJVkVdIHx8IHJlcXVpcmUoJy4vX2hpZGUnKSgkU3ltYm9sW1BST1RPVFlQRV0sIFRPX1BSSU1JVElWRSwgJFN5bWJvbFtQUk9UT1RZUEVdLnZhbHVlT2YpO1xuLy8gMTkuNC4zLjUgU3ltYm9sLnByb3RvdHlwZVtAQHRvU3RyaW5nVGFnXVxuc2V0VG9TdHJpbmdUYWcoJFN5bWJvbCwgJ1N5bWJvbCcpO1xuLy8gMjAuMi4xLjkgTWF0aFtAQHRvU3RyaW5nVGFnXVxuc2V0VG9TdHJpbmdUYWcoTWF0aCwgJ01hdGgnLCB0cnVlKTtcbi8vIDI0LjMuMyBKU09OW0BAdG9TdHJpbmdUYWddXG5zZXRUb1N0cmluZ1RhZyhnbG9iYWwuSlNPTiwgJ0pTT04nLCB0cnVlKTtcbiJdLCJtYXBwaW5ncyI6IkFBQUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOyIsInNvdXJjZVJvb3QiOiIifQ==\n//# sourceURL=webpack-internal:///014b\n"); - -/***/ }), - -/***/ "01f9": -/*!******************************************************!*\ - !*** ./node_modules/core-js/modules/_iter-define.js ***! - \******************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("\nvar LIBRARY = __webpack_require__(/*! ./_library */ \"2d00\");\nvar $export = __webpack_require__(/*! ./_export */ \"5ca1\");\nvar redefine = __webpack_require__(/*! ./_redefine */ \"2aba\");\nvar hide = __webpack_require__(/*! ./_hide */ \"32e9\");\nvar Iterators = __webpack_require__(/*! ./_iterators */ \"84f2\");\nvar $iterCreate = __webpack_require__(/*! ./_iter-create */ \"41a0\");\nvar setToStringTag = __webpack_require__(/*! ./_set-to-string-tag */ \"7f20\");\nvar getPrototypeOf = __webpack_require__(/*! ./_object-gpo */ \"38fd\");\nvar ITERATOR = __webpack_require__(/*! ./_wks */ \"2b4c\")('iterator');\nvar BUGGY = !([].keys && 'next' in [].keys()); // Safari has buggy iterators w/o `next`\nvar FF_ITERATOR = '@@iterator';\nvar KEYS = 'keys';\nvar VALUES = 'values';\n\nvar returnThis = function () { return this; };\n\nmodule.exports = function (Base, NAME, Constructor, next, DEFAULT, IS_SET, FORCED) {\n $iterCreate(Constructor, NAME, next);\n var getMethod = function (kind) {\n if (!BUGGY && kind in proto) return proto[kind];\n switch (kind) {\n case KEYS: return function keys() { return new Constructor(this, kind); };\n case VALUES: return function values() { return new Constructor(this, kind); };\n } return function entries() { return new Constructor(this, kind); };\n };\n var TAG = NAME + ' Iterator';\n var DEF_VALUES = DEFAULT == VALUES;\n var VALUES_BUG = false;\n var proto = Base.prototype;\n var $native = proto[ITERATOR] || proto[FF_ITERATOR] || DEFAULT && proto[DEFAULT];\n var $default = $native || getMethod(DEFAULT);\n var $entries = DEFAULT ? !DEF_VALUES ? $default : getMethod('entries') : undefined;\n var $anyNative = NAME == 'Array' ? proto.entries || $native : $native;\n var methods, key, IteratorPrototype;\n // Fix native\n if ($anyNative) {\n IteratorPrototype = getPrototypeOf($anyNative.call(new Base()));\n if (IteratorPrototype !== Object.prototype && IteratorPrototype.next) {\n // Set @@toStringTag to native iterators\n setToStringTag(IteratorPrototype, TAG, true);\n // fix for some old engines\n if (!LIBRARY && typeof IteratorPrototype[ITERATOR] != 'function') hide(IteratorPrototype, ITERATOR, returnThis);\n }\n }\n // fix Array#{values, @@iterator}.name in V8 / FF\n if (DEF_VALUES && $native && $native.name !== VALUES) {\n VALUES_BUG = true;\n $default = function values() { return $native.call(this); };\n }\n // Define iterator\n if ((!LIBRARY || FORCED) && (BUGGY || VALUES_BUG || !proto[ITERATOR])) {\n hide(proto, ITERATOR, $default);\n }\n // Plug for library\n Iterators[NAME] = $default;\n Iterators[TAG] = returnThis;\n if (DEFAULT) {\n methods = {\n values: DEF_VALUES ? $default : getMethod(VALUES),\n keys: IS_SET ? $default : getMethod(KEYS),\n entries: $entries\n };\n if (FORCED) for (key in methods) {\n if (!(key in proto)) redefine(proto, key, methods[key]);\n } else $export($export.P + $export.F * (BUGGY || VALUES_BUG), NAME, methods);\n }\n return methods;\n};\n//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMDFmOS5qcyIsInNvdXJjZXMiOlsid2VicGFjazovLy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvX2l0ZXItZGVmaW5lLmpzPzAxZjkiXSwic291cmNlc0NvbnRlbnQiOlsiJ3VzZSBzdHJpY3QnO1xudmFyIExJQlJBUlkgPSByZXF1aXJlKCcuL19saWJyYXJ5Jyk7XG52YXIgJGV4cG9ydCA9IHJlcXVpcmUoJy4vX2V4cG9ydCcpO1xudmFyIHJlZGVmaW5lID0gcmVxdWlyZSgnLi9fcmVkZWZpbmUnKTtcbnZhciBoaWRlID0gcmVxdWlyZSgnLi9faGlkZScpO1xudmFyIEl0ZXJhdG9ycyA9IHJlcXVpcmUoJy4vX2l0ZXJhdG9ycycpO1xudmFyICRpdGVyQ3JlYXRlID0gcmVxdWlyZSgnLi9faXRlci1jcmVhdGUnKTtcbnZhciBzZXRUb1N0cmluZ1RhZyA9IHJlcXVpcmUoJy4vX3NldC10by1zdHJpbmctdGFnJyk7XG52YXIgZ2V0UHJvdG90eXBlT2YgPSByZXF1aXJlKCcuL19vYmplY3QtZ3BvJyk7XG52YXIgSVRFUkFUT1IgPSByZXF1aXJlKCcuL193a3MnKSgnaXRlcmF0b3InKTtcbnZhciBCVUdHWSA9ICEoW10ua2V5cyAmJiAnbmV4dCcgaW4gW10ua2V5cygpKTsgLy8gU2FmYXJpIGhhcyBidWdneSBpdGVyYXRvcnMgdy9vIGBuZXh0YFxudmFyIEZGX0lURVJBVE9SID0gJ0BAaXRlcmF0b3InO1xudmFyIEtFWVMgPSAna2V5cyc7XG52YXIgVkFMVUVTID0gJ3ZhbHVlcyc7XG5cbnZhciByZXR1cm5UaGlzID0gZnVuY3Rpb24gKCkgeyByZXR1cm4gdGhpczsgfTtcblxubW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbiAoQmFzZSwgTkFNRSwgQ29uc3RydWN0b3IsIG5leHQsIERFRkFVTFQsIElTX1NFVCwgRk9SQ0VEKSB7XG4gICRpdGVyQ3JlYXRlKENvbnN0cnVjdG9yLCBOQU1FLCBuZXh0KTtcbiAgdmFyIGdldE1ldGhvZCA9IGZ1bmN0aW9uIChraW5kKSB7XG4gICAgaWYgKCFCVUdHWSAmJiBraW5kIGluIHByb3RvKSByZXR1cm4gcHJvdG9ba2luZF07XG4gICAgc3dpdGNoIChraW5kKSB7XG4gICAgICBjYXNlIEtFWVM6IHJldHVybiBmdW5jdGlvbiBrZXlzKCkgeyByZXR1cm4gbmV3IENvbnN0cnVjdG9yKHRoaXMsIGtpbmQpOyB9O1xuICAgICAgY2FzZSBWQUxVRVM6IHJldHVybiBmdW5jdGlvbiB2YWx1ZXMoKSB7IHJldHVybiBuZXcgQ29uc3RydWN0b3IodGhpcywga2luZCk7IH07XG4gICAgfSByZXR1cm4gZnVuY3Rpb24gZW50cmllcygpIHsgcmV0dXJuIG5ldyBDb25zdHJ1Y3Rvcih0aGlzLCBraW5kKTsgfTtcbiAgfTtcbiAgdmFyIFRBRyA9IE5BTUUgKyAnIEl0ZXJhdG9yJztcbiAgdmFyIERFRl9WQUxVRVMgPSBERUZBVUxUID09IFZBTFVFUztcbiAgdmFyIFZBTFVFU19CVUcgPSBmYWxzZTtcbiAgdmFyIHByb3RvID0gQmFzZS5wcm90b3R5cGU7XG4gIHZhciAkbmF0aXZlID0gcHJvdG9bSVRFUkFUT1JdIHx8IHByb3RvW0ZGX0lURVJBVE9SXSB8fCBERUZBVUxUICYmIHByb3RvW0RFRkFVTFRdO1xuICB2YXIgJGRlZmF1bHQgPSAkbmF0aXZlIHx8IGdldE1ldGhvZChERUZBVUxUKTtcbiAgdmFyICRlbnRyaWVzID0gREVGQVVMVCA/ICFERUZfVkFMVUVTID8gJGRlZmF1bHQgOiBnZXRNZXRob2QoJ2VudHJpZXMnKSA6IHVuZGVmaW5lZDtcbiAgdmFyICRhbnlOYXRpdmUgPSBOQU1FID09ICdBcnJheScgPyBwcm90by5lbnRyaWVzIHx8ICRuYXRpdmUgOiAkbmF0aXZlO1xuICB2YXIgbWV0aG9kcywga2V5LCBJdGVyYXRvclByb3RvdHlwZTtcbiAgLy8gRml4IG5hdGl2ZVxuICBpZiAoJGFueU5hdGl2ZSkge1xuICAgIEl0ZXJhdG9yUHJvdG90eXBlID0gZ2V0UHJvdG90eXBlT2YoJGFueU5hdGl2ZS5jYWxsKG5ldyBCYXNlKCkpKTtcbiAgICBpZiAoSXRlcmF0b3JQcm90b3R5cGUgIT09IE9iamVjdC5wcm90b3R5cGUgJiYgSXRlcmF0b3JQcm90b3R5cGUubmV4dCkge1xuICAgICAgLy8gU2V0IEBAdG9TdHJpbmdUYWcgdG8gbmF0aXZlIGl0ZXJhdG9yc1xuICAgICAgc2V0VG9TdHJpbmdUYWcoSXRlcmF0b3JQcm90b3R5cGUsIFRBRywgdHJ1ZSk7XG4gICAgICAvLyBmaXggZm9yIHNvbWUgb2xkIGVuZ2luZXNcbiAgICAgIGlmICghTElCUkFSWSAmJiB0eXBlb2YgSXRlcmF0b3JQcm90b3R5cGVbSVRFUkFUT1JdICE9ICdmdW5jdGlvbicpIGhpZGUoSXRlcmF0b3JQcm90b3R5cGUsIElURVJBVE9SLCByZXR1cm5UaGlzKTtcbiAgICB9XG4gIH1cbiAgLy8gZml4IEFycmF5I3t2YWx1ZXMsIEBAaXRlcmF0b3J9Lm5hbWUgaW4gVjggLyBGRlxuICBpZiAoREVGX1ZBTFVFUyAmJiAkbmF0aXZlICYmICRuYXRpdmUubmFtZSAhPT0gVkFMVUVTKSB7XG4gICAgVkFMVUVTX0JVRyA9IHRydWU7XG4gICAgJGRlZmF1bHQgPSBmdW5jdGlvbiB2YWx1ZXMoKSB7IHJldHVybiAkbmF0aXZlLmNhbGwodGhpcyk7IH07XG4gIH1cbiAgLy8gRGVmaW5lIGl0ZXJhdG9yXG4gIGlmICgoIUxJQlJBUlkgfHwgRk9SQ0VEKSAmJiAoQlVHR1kgfHwgVkFMVUVTX0JVRyB8fCAhcHJvdG9bSVRFUkFUT1JdKSkge1xuICAgIGhpZGUocHJvdG8sIElURVJBVE9SLCAkZGVmYXVsdCk7XG4gIH1cbiAgLy8gUGx1ZyBmb3IgbGlicmFyeVxuICBJdGVyYXRvcnNbTkFNRV0gPSAkZGVmYXVsdDtcbiAgSXRlcmF0b3JzW1RBR10gPSByZXR1cm5UaGlzO1xuICBpZiAoREVGQVVMVCkge1xuICAgIG1ldGhvZHMgPSB7XG4gICAgICB2YWx1ZXM6IERFRl9WQUxVRVMgPyAkZGVmYXVsdCA6IGdldE1ldGhvZChWQUxVRVMpLFxuICAgICAga2V5czogSVNfU0VUID8gJGRlZmF1bHQgOiBnZXRNZXRob2QoS0VZUyksXG4gICAgICBlbnRyaWVzOiAkZW50cmllc1xuICAgIH07XG4gICAgaWYgKEZPUkNFRCkgZm9yIChrZXkgaW4gbWV0aG9kcykge1xuICAgICAgaWYgKCEoa2V5IGluIHByb3RvKSkgcmVkZWZpbmUocHJvdG8sIGtleSwgbWV0aG9kc1trZXldKTtcbiAgICB9IGVsc2UgJGV4cG9ydCgkZXhwb3J0LlAgKyAkZXhwb3J0LkYgKiAoQlVHR1kgfHwgVkFMVUVTX0JVRyksIE5BTUUsIG1ldGhvZHMpO1xuICB9XG4gIHJldHVybiBtZXRob2RzO1xufTtcbiJdLCJtYXBwaW5ncyI6IkFBQUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOyIsInNvdXJjZVJvb3QiOiIifQ==\n//# sourceURL=webpack-internal:///01f9\n"); - -/***/ }), - -/***/ "02f4": -/*!****************************************************!*\ - !*** ./node_modules/core-js/modules/_string-at.js ***! - \****************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -eval("var toInteger = __webpack_require__(/*! ./_to-integer */ \"4588\");\nvar defined = __webpack_require__(/*! ./_defined */ \"be13\");\n// true -> String#at\n// false -> String#codePointAt\nmodule.exports = function (TO_STRING) {\n return function (that, pos) {\n var s = String(defined(that));\n var i = toInteger(pos);\n var l = s.length;\n var a, b;\n if (i < 0 || i >= l) return TO_STRING ? '' : undefined;\n a = s.charCodeAt(i);\n return a < 0xd800 || a > 0xdbff || i + 1 === l || (b = s.charCodeAt(i + 1)) < 0xdc00 || b > 0xdfff\n ? TO_STRING ? s.charAt(i) : a\n : TO_STRING ? s.slice(i, i + 2) : (a - 0xd800 << 10) + (b - 0xdc00) + 0x10000;\n };\n};\n//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMDJmNC5qcyIsInNvdXJjZXMiOlsid2VicGFjazovLy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvX3N0cmluZy1hdC5qcz8wMmY0Il0sInNvdXJjZXNDb250ZW50IjpbInZhciB0b0ludGVnZXIgPSByZXF1aXJlKCcuL190by1pbnRlZ2VyJyk7XG52YXIgZGVmaW5lZCA9IHJlcXVpcmUoJy4vX2RlZmluZWQnKTtcbi8vIHRydWUgIC0+IFN0cmluZyNhdFxuLy8gZmFsc2UgLT4gU3RyaW5nI2NvZGVQb2ludEF0XG5tb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uIChUT19TVFJJTkcpIHtcbiAgcmV0dXJuIGZ1bmN0aW9uICh0aGF0LCBwb3MpIHtcbiAgICB2YXIgcyA9IFN0cmluZyhkZWZpbmVkKHRoYXQpKTtcbiAgICB2YXIgaSA9IHRvSW50ZWdlcihwb3MpO1xuICAgIHZhciBsID0gcy5sZW5ndGg7XG4gICAgdmFyIGEsIGI7XG4gICAgaWYgKGkgPCAwIHx8IGkgPj0gbCkgcmV0dXJuIFRPX1NUUklORyA/ICcnIDogdW5kZWZpbmVkO1xuICAgIGEgPSBzLmNoYXJDb2RlQXQoaSk7XG4gICAgcmV0dXJuIGEgPCAweGQ4MDAgfHwgYSA+IDB4ZGJmZiB8fCBpICsgMSA9PT0gbCB8fCAoYiA9IHMuY2hhckNvZGVBdChpICsgMSkpIDwgMHhkYzAwIHx8IGIgPiAweGRmZmZcbiAgICAgID8gVE9fU1RSSU5HID8gcy5jaGFyQXQoaSkgOiBhXG4gICAgICA6IFRPX1NUUklORyA/IHMuc2xpY2UoaSwgaSArIDIpIDogKGEgLSAweGQ4MDAgPDwgMTApICsgKGIgLSAweGRjMDApICsgMHgxMDAwMDtcbiAgfTtcbn07XG4iXSwibWFwcGluZ3MiOiJBQUFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Iiwic291cmNlUm9vdCI6IiJ9\n//# sourceURL=webpack-internal:///02f4\n"); - -/***/ }), - -/***/ "0390": -/*!***************************************************************!*\ - !*** ./node_modules/core-js/modules/_advance-string-index.js ***! - \***************************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("\nvar at = __webpack_require__(/*! ./_string-at */ \"02f4\")(true);\n\n // `AdvanceStringIndex` abstract operation\n// https://tc39.github.io/ecma262/#sec-advancestringindex\nmodule.exports = function (S, index, unicode) {\n return index + (unicode ? at(S, index).length : 1);\n};\n//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMDM5MC5qcyIsInNvdXJjZXMiOlsid2VicGFjazovLy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvX2FkdmFuY2Utc3RyaW5nLWluZGV4LmpzPzAzOTAiXSwic291cmNlc0NvbnRlbnQiOlsiJ3VzZSBzdHJpY3QnO1xudmFyIGF0ID0gcmVxdWlyZSgnLi9fc3RyaW5nLWF0JykodHJ1ZSk7XG5cbiAvLyBgQWR2YW5jZVN0cmluZ0luZGV4YCBhYnN0cmFjdCBvcGVyYXRpb25cbi8vIGh0dHBzOi8vdGMzOS5naXRodWIuaW8vZWNtYTI2Mi8jc2VjLWFkdmFuY2VzdHJpbmdpbmRleFxubW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbiAoUywgaW5kZXgsIHVuaWNvZGUpIHtcbiAgcmV0dXJuIGluZGV4ICsgKHVuaWNvZGUgPyBhdChTLCBpbmRleCkubGVuZ3RoIDogMSk7XG59O1xuIl0sIm1hcHBpbmdzIjoiQUFBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOyIsInNvdXJjZVJvb3QiOiIifQ==\n//# sourceURL=webpack-internal:///0390\n"); - -/***/ }), - -/***/ "0395": -/*!******************************************************************!*\ - !*** ./node_modules/core-js/library/modules/_object-gopn-ext.js ***! - \******************************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -eval("// fallback for IE11 buggy Object.getOwnPropertyNames with iframe and window\nvar toIObject = __webpack_require__(/*! ./_to-iobject */ \"36c3\");\nvar gOPN = __webpack_require__(/*! ./_object-gopn */ \"6abf\").f;\nvar toString = {}.toString;\n\nvar windowNames = typeof window == 'object' && window && Object.getOwnPropertyNames\n ? Object.getOwnPropertyNames(window) : [];\n\nvar getWindowNames = function (it) {\n try {\n return gOPN(it);\n } catch (e) {\n return windowNames.slice();\n }\n};\n\nmodule.exports.f = function getOwnPropertyNames(it) {\n return windowNames && toString.call(it) == '[object Window]' ? getWindowNames(it) : gOPN(toIObject(it));\n};\n//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMDM5NS5qcyIsInNvdXJjZXMiOlsid2VicGFjazovLy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL2xpYnJhcnkvbW9kdWxlcy9fb2JqZWN0LWdvcG4tZXh0LmpzPzAzOTUiXSwic291cmNlc0NvbnRlbnQiOlsiLy8gZmFsbGJhY2sgZm9yIElFMTEgYnVnZ3kgT2JqZWN0LmdldE93blByb3BlcnR5TmFtZXMgd2l0aCBpZnJhbWUgYW5kIHdpbmRvd1xudmFyIHRvSU9iamVjdCA9IHJlcXVpcmUoJy4vX3RvLWlvYmplY3QnKTtcbnZhciBnT1BOID0gcmVxdWlyZSgnLi9fb2JqZWN0LWdvcG4nKS5mO1xudmFyIHRvU3RyaW5nID0ge30udG9TdHJpbmc7XG5cbnZhciB3aW5kb3dOYW1lcyA9IHR5cGVvZiB3aW5kb3cgPT0gJ29iamVjdCcgJiYgd2luZG93ICYmIE9iamVjdC5nZXRPd25Qcm9wZXJ0eU5hbWVzXG4gID8gT2JqZWN0LmdldE93blByb3BlcnR5TmFtZXMod2luZG93KSA6IFtdO1xuXG52YXIgZ2V0V2luZG93TmFtZXMgPSBmdW5jdGlvbiAoaXQpIHtcbiAgdHJ5IHtcbiAgICByZXR1cm4gZ09QTihpdCk7XG4gIH0gY2F0Y2ggKGUpIHtcbiAgICByZXR1cm4gd2luZG93TmFtZXMuc2xpY2UoKTtcbiAgfVxufTtcblxubW9kdWxlLmV4cG9ydHMuZiA9IGZ1bmN0aW9uIGdldE93blByb3BlcnR5TmFtZXMoaXQpIHtcbiAgcmV0dXJuIHdpbmRvd05hbWVzICYmIHRvU3RyaW5nLmNhbGwoaXQpID09ICdbb2JqZWN0IFdpbmRvd10nID8gZ2V0V2luZG93TmFtZXMoaXQpIDogZ09QTih0b0lPYmplY3QoaXQpKTtcbn07XG4iXSwibWFwcGluZ3MiOiJBQUFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOyIsInNvdXJjZVJvb3QiOiIifQ==\n//# sourceURL=webpack-internal:///0395\n"); - -/***/ }), - -/***/ "0773": -/*!**********************************************************!*\ - !*** ./node_modules/jquery-ui/themes/base/draggable.css ***! - \**********************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -eval("// extracted by mini-css-extract-plugin//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMDc3My5qcyIsInNvdXJjZXMiOlsid2VicGFjazovLy8uL25vZGVfbW9kdWxlcy9qcXVlcnktdWkvdGhlbWVzL2Jhc2UvZHJhZ2dhYmxlLmNzcz8wMDQwIl0sInNvdXJjZXNDb250ZW50IjpbIi8vIGV4dHJhY3RlZCBieSBtaW5pLWNzcy1leHRyYWN0LXBsdWdpbiJdLCJtYXBwaW5ncyI6IkFBQUEiLCJzb3VyY2VSb290IjoiIn0=\n//# sourceURL=webpack-internal:///0773\n"); - -/***/ }), - -/***/ "07d1": -/*!*****************************************************!*\ - !*** ./node_modules/tinymce/plugins/table/index.js ***! - \*****************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -eval("// Exports the \"table\" plugin for usage with module loaders\n// Usage:\n// CommonJS:\n// require('tinymce/plugins/table')\n// ES2015:\n// import 'tinymce/plugins/table'\n__webpack_require__(/*! ./plugin.js */ \"94ce\");//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMDdkMS5qcyIsInNvdXJjZXMiOlsid2VicGFjazovLy8uL25vZGVfbW9kdWxlcy90aW55bWNlL3BsdWdpbnMvdGFibGUvaW5kZXguanM/MDdkMSJdLCJzb3VyY2VzQ29udGVudCI6WyIvLyBFeHBvcnRzIHRoZSBcInRhYmxlXCIgcGx1Z2luIGZvciB1c2FnZSB3aXRoIG1vZHVsZSBsb2FkZXJzXG4vLyBVc2FnZTpcbi8vICAgQ29tbW9uSlM6XG4vLyAgICAgcmVxdWlyZSgndGlueW1jZS9wbHVnaW5zL3RhYmxlJylcbi8vICAgRVMyMDE1OlxuLy8gICAgIGltcG9ydCAndGlueW1jZS9wbHVnaW5zL3RhYmxlJ1xucmVxdWlyZSgnLi9wbHVnaW4uanMnKTsiXSwibWFwcGluZ3MiOiJBQUFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBIiwic291cmNlUm9vdCI6IiJ9\n//# sourceURL=webpack-internal:///07d1\n"); - -/***/ }), - -/***/ "07e3": -/*!******************************************************!*\ - !*** ./node_modules/core-js/library/modules/_has.js ***! - \******************************************************/ -/*! no static exports found */ -/***/ (function(module, exports) { - -eval("var hasOwnProperty = {}.hasOwnProperty;\nmodule.exports = function (it, key) {\n return hasOwnProperty.call(it, key);\n};\n//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMDdlMy5qcyIsInNvdXJjZXMiOlsid2VicGFjazovLy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL2xpYnJhcnkvbW9kdWxlcy9faGFzLmpzPzA3ZTMiXSwic291cmNlc0NvbnRlbnQiOlsidmFyIGhhc093blByb3BlcnR5ID0ge30uaGFzT3duUHJvcGVydHk7XG5tb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uIChpdCwga2V5KSB7XG4gIHJldHVybiBoYXNPd25Qcm9wZXJ0eS5jYWxsKGl0LCBrZXkpO1xufTtcbiJdLCJtYXBwaW5ncyI6IkFBQUE7QUFDQTtBQUNBO0FBQ0E7Iiwic291cmNlUm9vdCI6IiJ9\n//# sourceURL=webpack-internal:///07e3\n"); - -/***/ }), - -/***/ "097d": -/*!*************************************************************!*\ - !*** ./node_modules/core-js/modules/es7.promise.finally.js ***! - \*************************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("// https://github.com/tc39/proposal-promise-finally\n\nvar $export = __webpack_require__(/*! ./_export */ \"5ca1\");\nvar core = __webpack_require__(/*! ./_core */ \"8378\");\nvar global = __webpack_require__(/*! ./_global */ \"7726\");\nvar speciesConstructor = __webpack_require__(/*! ./_species-constructor */ \"ebd6\");\nvar promiseResolve = __webpack_require__(/*! ./_promise-resolve */ \"bcaa\");\n\n$export($export.P + $export.R, 'Promise', { 'finally': function (onFinally) {\n var C = speciesConstructor(this, core.Promise || global.Promise);\n var isFunction = typeof onFinally == 'function';\n return this.then(\n isFunction ? function (x) {\n return promiseResolve(C, onFinally()).then(function () { return x; });\n } : onFinally,\n isFunction ? function (e) {\n return promiseResolve(C, onFinally()).then(function () { throw e; });\n } : onFinally\n );\n} });\n//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMDk3ZC5qcyIsInNvdXJjZXMiOlsid2VicGFjazovLy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvZXM3LnByb21pc2UuZmluYWxseS5qcz8wOTdkIl0sInNvdXJjZXNDb250ZW50IjpbIi8vIGh0dHBzOi8vZ2l0aHViLmNvbS90YzM5L3Byb3Bvc2FsLXByb21pc2UtZmluYWxseVxuJ3VzZSBzdHJpY3QnO1xudmFyICRleHBvcnQgPSByZXF1aXJlKCcuL19leHBvcnQnKTtcbnZhciBjb3JlID0gcmVxdWlyZSgnLi9fY29yZScpO1xudmFyIGdsb2JhbCA9IHJlcXVpcmUoJy4vX2dsb2JhbCcpO1xudmFyIHNwZWNpZXNDb25zdHJ1Y3RvciA9IHJlcXVpcmUoJy4vX3NwZWNpZXMtY29uc3RydWN0b3InKTtcbnZhciBwcm9taXNlUmVzb2x2ZSA9IHJlcXVpcmUoJy4vX3Byb21pc2UtcmVzb2x2ZScpO1xuXG4kZXhwb3J0KCRleHBvcnQuUCArICRleHBvcnQuUiwgJ1Byb21pc2UnLCB7ICdmaW5hbGx5JzogZnVuY3Rpb24gKG9uRmluYWxseSkge1xuICB2YXIgQyA9IHNwZWNpZXNDb25zdHJ1Y3Rvcih0aGlzLCBjb3JlLlByb21pc2UgfHwgZ2xvYmFsLlByb21pc2UpO1xuICB2YXIgaXNGdW5jdGlvbiA9IHR5cGVvZiBvbkZpbmFsbHkgPT0gJ2Z1bmN0aW9uJztcbiAgcmV0dXJuIHRoaXMudGhlbihcbiAgICBpc0Z1bmN0aW9uID8gZnVuY3Rpb24gKHgpIHtcbiAgICAgIHJldHVybiBwcm9taXNlUmVzb2x2ZShDLCBvbkZpbmFsbHkoKSkudGhlbihmdW5jdGlvbiAoKSB7IHJldHVybiB4OyB9KTtcbiAgICB9IDogb25GaW5hbGx5LFxuICAgIGlzRnVuY3Rpb24gPyBmdW5jdGlvbiAoZSkge1xuICAgICAgcmV0dXJuIHByb21pc2VSZXNvbHZlKEMsIG9uRmluYWxseSgpKS50aGVuKGZ1bmN0aW9uICgpIHsgdGhyb3cgZTsgfSk7XG4gICAgfSA6IG9uRmluYWxseVxuICApO1xufSB9KTtcbiJdLCJtYXBwaW5ncyI6IkFBQUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTsiLCJzb3VyY2VSb290IjoiIn0=\n//# sourceURL=webpack-internal:///097d\n"); - -/***/ }), - -/***/ "0a49": -/*!********************************************************!*\ - !*** ./node_modules/core-js/modules/_array-methods.js ***! - \********************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -eval("// 0 -> Array#forEach\n// 1 -> Array#map\n// 2 -> Array#filter\n// 3 -> Array#some\n// 4 -> Array#every\n// 5 -> Array#find\n// 6 -> Array#findIndex\nvar ctx = __webpack_require__(/*! ./_ctx */ \"9b43\");\nvar IObject = __webpack_require__(/*! ./_iobject */ \"626a\");\nvar toObject = __webpack_require__(/*! ./_to-object */ \"4bf8\");\nvar toLength = __webpack_require__(/*! ./_to-length */ \"9def\");\nvar asc = __webpack_require__(/*! ./_array-species-create */ \"cd1c\");\nmodule.exports = function (TYPE, $create) {\n var IS_MAP = TYPE == 1;\n var IS_FILTER = TYPE == 2;\n var IS_SOME = TYPE == 3;\n var IS_EVERY = TYPE == 4;\n var IS_FIND_INDEX = TYPE == 6;\n var NO_HOLES = TYPE == 5 || IS_FIND_INDEX;\n var create = $create || asc;\n return function ($this, callbackfn, that) {\n var O = toObject($this);\n var self = IObject(O);\n var f = ctx(callbackfn, that, 3);\n var length = toLength(self.length);\n var index = 0;\n var result = IS_MAP ? create($this, length) : IS_FILTER ? create($this, 0) : undefined;\n var val, res;\n for (;length > index; index++) if (NO_HOLES || index in self) {\n val = self[index];\n res = f(val, index, O);\n if (TYPE) {\n if (IS_MAP) result[index] = res; // map\n else if (res) switch (TYPE) {\n case 3: return true; // some\n case 5: return val; // find\n case 6: return index; // findIndex\n case 2: result.push(val); // filter\n } else if (IS_EVERY) return false; // every\n }\n }\n return IS_FIND_INDEX ? -1 : IS_SOME || IS_EVERY ? IS_EVERY : result;\n };\n};\n//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMGE0OS5qcyIsInNvdXJjZXMiOlsid2VicGFjazovLy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvX2FycmF5LW1ldGhvZHMuanM/MGE0OSJdLCJzb3VyY2VzQ29udGVudCI6WyIvLyAwIC0+IEFycmF5I2ZvckVhY2hcbi8vIDEgLT4gQXJyYXkjbWFwXG4vLyAyIC0+IEFycmF5I2ZpbHRlclxuLy8gMyAtPiBBcnJheSNzb21lXG4vLyA0IC0+IEFycmF5I2V2ZXJ5XG4vLyA1IC0+IEFycmF5I2ZpbmRcbi8vIDYgLT4gQXJyYXkjZmluZEluZGV4XG52YXIgY3R4ID0gcmVxdWlyZSgnLi9fY3R4Jyk7XG52YXIgSU9iamVjdCA9IHJlcXVpcmUoJy4vX2lvYmplY3QnKTtcbnZhciB0b09iamVjdCA9IHJlcXVpcmUoJy4vX3RvLW9iamVjdCcpO1xudmFyIHRvTGVuZ3RoID0gcmVxdWlyZSgnLi9fdG8tbGVuZ3RoJyk7XG52YXIgYXNjID0gcmVxdWlyZSgnLi9fYXJyYXktc3BlY2llcy1jcmVhdGUnKTtcbm1vZHVsZS5leHBvcnRzID0gZnVuY3Rpb24gKFRZUEUsICRjcmVhdGUpIHtcbiAgdmFyIElTX01BUCA9IFRZUEUgPT0gMTtcbiAgdmFyIElTX0ZJTFRFUiA9IFRZUEUgPT0gMjtcbiAgdmFyIElTX1NPTUUgPSBUWVBFID09IDM7XG4gIHZhciBJU19FVkVSWSA9IFRZUEUgPT0gNDtcbiAgdmFyIElTX0ZJTkRfSU5ERVggPSBUWVBFID09IDY7XG4gIHZhciBOT19IT0xFUyA9IFRZUEUgPT0gNSB8fCBJU19GSU5EX0lOREVYO1xuICB2YXIgY3JlYXRlID0gJGNyZWF0ZSB8fCBhc2M7XG4gIHJldHVybiBmdW5jdGlvbiAoJHRoaXMsIGNhbGxiYWNrZm4sIHRoYXQpIHtcbiAgICB2YXIgTyA9IHRvT2JqZWN0KCR0aGlzKTtcbiAgICB2YXIgc2VsZiA9IElPYmplY3QoTyk7XG4gICAgdmFyIGYgPSBjdHgoY2FsbGJhY2tmbiwgdGhhdCwgMyk7XG4gICAgdmFyIGxlbmd0aCA9IHRvTGVuZ3RoKHNlbGYubGVuZ3RoKTtcbiAgICB2YXIgaW5kZXggPSAwO1xuICAgIHZhciByZXN1bHQgPSBJU19NQVAgPyBjcmVhdGUoJHRoaXMsIGxlbmd0aCkgOiBJU19GSUxURVIgPyBjcmVhdGUoJHRoaXMsIDApIDogdW5kZWZpbmVkO1xuICAgIHZhciB2YWwsIHJlcztcbiAgICBmb3IgKDtsZW5ndGggPiBpbmRleDsgaW5kZXgrKykgaWYgKE5PX0hPTEVTIHx8IGluZGV4IGluIHNlbGYpIHtcbiAgICAgIHZhbCA9IHNlbGZbaW5kZXhdO1xuICAgICAgcmVzID0gZih2YWwsIGluZGV4LCBPKTtcbiAgICAgIGlmIChUWVBFKSB7XG4gICAgICAgIGlmIChJU19NQVApIHJlc3VsdFtpbmRleF0gPSByZXM7ICAgLy8gbWFwXG4gICAgICAgIGVsc2UgaWYgKHJlcykgc3dpdGNoIChUWVBFKSB7XG4gICAgICAgICAgY2FzZSAzOiByZXR1cm4gdHJ1ZTsgICAgICAgICAgICAgLy8gc29tZVxuICAgICAgICAgIGNhc2UgNTogcmV0dXJuIHZhbDsgICAgICAgICAgICAgIC8vIGZpbmRcbiAgICAgICAgICBjYXNlIDY6IHJldHVybiBpbmRleDsgICAgICAgICAgICAvLyBmaW5kSW5kZXhcbiAgICAgICAgICBjYXNlIDI6IHJlc3VsdC5wdXNoKHZhbCk7ICAgICAgICAvLyBmaWx0ZXJcbiAgICAgICAgfSBlbHNlIGlmIChJU19FVkVSWSkgcmV0dXJuIGZhbHNlOyAvLyBldmVyeVxuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gSVNfRklORF9JTkRFWCA/IC0xIDogSVNfU09NRSB8fCBJU19FVkVSWSA/IElTX0VWRVJZIDogcmVzdWx0O1xuICB9O1xufTtcbiJdLCJtYXBwaW5ncyI6IkFBQUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTsiLCJzb3VyY2VSb290IjoiIn0=\n//# sourceURL=webpack-internal:///0a49\n"); - -/***/ }), - -/***/ "0ae9": -/*!*******************************************!*\ - !*** ./node_modules/jquery-ui/ui/data.js ***! - \*******************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -eval("var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/*!\n * jQuery UI :data 1.12.1\n * http://jqueryui.com\n *\n * Copyright jQuery Foundation and other contributors\n * Released under the MIT license.\n * http://jquery.org/license\n */\n\n//>>label: :data Selector\n//>>group: Core\n//>>description: Selects elements which have data stored under the specified key.\n//>>docs: http://api.jqueryui.com/data-selector/\n\n( function( factory ) {\n\tif ( true ) {\n\n\t\t// AMD. Register as an anonymous module.\n\t\t!(__WEBPACK_AMD_DEFINE_ARRAY__ = [ __webpack_require__(/*! jquery */ \"1157\"), __webpack_require__(/*! ./version */ \"4309\") ], __WEBPACK_AMD_DEFINE_FACTORY__ = (factory),\n\t\t\t\t__WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ?\n\t\t\t\t(__WEBPACK_AMD_DEFINE_FACTORY__.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__)) : __WEBPACK_AMD_DEFINE_FACTORY__),\n\t\t\t\t__WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));\n\t} else {}\n} ( function( $ ) {\nreturn $.extend( $.expr[ \":\" ], {\n\tdata: $.expr.createPseudo ?\n\t\t$.expr.createPseudo( function( dataName ) {\n\t\t\treturn function( elem ) {\n\t\t\t\treturn !!$.data( elem, dataName );\n\t\t\t};\n\t\t} ) :\n\n\t\t// Support: jQuery <1.8\n\t\tfunction( elem, i, match ) {\n\t\t\treturn !!$.data( elem, match[ 3 ] );\n\t\t}\n} );\n} ) );\n//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMGFlOS5qcyIsInNvdXJjZXMiOlsid2VicGFjazovLy8uL25vZGVfbW9kdWxlcy9qcXVlcnktdWkvdWkvZGF0YS5qcz8wYWU5Il0sInNvdXJjZXNDb250ZW50IjpbIi8qIVxuICogalF1ZXJ5IFVJIDpkYXRhIDEuMTIuMVxuICogaHR0cDovL2pxdWVyeXVpLmNvbVxuICpcbiAqIENvcHlyaWdodCBqUXVlcnkgRm91bmRhdGlvbiBhbmQgb3RoZXIgY29udHJpYnV0b3JzXG4gKiBSZWxlYXNlZCB1bmRlciB0aGUgTUlUIGxpY2Vuc2UuXG4gKiBodHRwOi8vanF1ZXJ5Lm9yZy9saWNlbnNlXG4gKi9cblxuLy8+PmxhYmVsOiA6ZGF0YSBTZWxlY3RvclxuLy8+Pmdyb3VwOiBDb3JlXG4vLz4+ZGVzY3JpcHRpb246IFNlbGVjdHMgZWxlbWVudHMgd2hpY2ggaGF2ZSBkYXRhIHN0b3JlZCB1bmRlciB0aGUgc3BlY2lmaWVkIGtleS5cbi8vPj5kb2NzOiBodHRwOi8vYXBpLmpxdWVyeXVpLmNvbS9kYXRhLXNlbGVjdG9yL1xuXG4oIGZ1bmN0aW9uKCBmYWN0b3J5ICkge1xuXHRpZiAoIHR5cGVvZiBkZWZpbmUgPT09IFwiZnVuY3Rpb25cIiAmJiBkZWZpbmUuYW1kICkge1xuXG5cdFx0Ly8gQU1ELiBSZWdpc3RlciBhcyBhbiBhbm9ueW1vdXMgbW9kdWxlLlxuXHRcdGRlZmluZSggWyBcImpxdWVyeVwiLCBcIi4vdmVyc2lvblwiIF0sIGZhY3RvcnkgKTtcblx0fSBlbHNlIHtcblxuXHRcdC8vIEJyb3dzZXIgZ2xvYmFsc1xuXHRcdGZhY3RvcnkoIGpRdWVyeSApO1xuXHR9XG59ICggZnVuY3Rpb24oICQgKSB7XG5yZXR1cm4gJC5leHRlbmQoICQuZXhwclsgXCI6XCIgXSwge1xuXHRkYXRhOiAkLmV4cHIuY3JlYXRlUHNldWRvID9cblx0XHQkLmV4cHIuY3JlYXRlUHNldWRvKCBmdW5jdGlvbiggZGF0YU5hbWUgKSB7XG5cdFx0XHRyZXR1cm4gZnVuY3Rpb24oIGVsZW0gKSB7XG5cdFx0XHRcdHJldHVybiAhISQuZGF0YSggZWxlbSwgZGF0YU5hbWUgKTtcblx0XHRcdH07XG5cdFx0fSApIDpcblxuXHRcdC8vIFN1cHBvcnQ6IGpRdWVyeSA8MS44XG5cdFx0ZnVuY3Rpb24oIGVsZW0sIGksIG1hdGNoICkge1xuXHRcdFx0cmV0dXJuICEhJC5kYXRhKCBlbGVtLCBtYXRjaFsgMyBdICk7XG5cdFx0fVxufSApO1xufSApICk7XG4iXSwibWFwcGluZ3MiOiJBQUFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQUE7QUFBQTtBQUFBO0FBQ0EsVUFJQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTsiLCJzb3VyY2VSb290IjoiIn0=\n//# sourceURL=webpack-internal:///0ae9\n"); - -/***/ }), - -/***/ "0bfb": -/*!************************************************!*\ - !*** ./node_modules/core-js/modules/_flags.js ***! - \************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("\n// 21.2.5.3 get RegExp.prototype.flags\nvar anObject = __webpack_require__(/*! ./_an-object */ \"cb7c\");\nmodule.exports = function () {\n var that = anObject(this);\n var result = '';\n if (that.global) result += 'g';\n if (that.ignoreCase) result += 'i';\n if (that.multiline) result += 'm';\n if (that.unicode) result += 'u';\n if (that.sticky) result += 'y';\n return result;\n};\n//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMGJmYi5qcyIsInNvdXJjZXMiOlsid2VicGFjazovLy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvX2ZsYWdzLmpzPzBiZmIiXSwic291cmNlc0NvbnRlbnQiOlsiJ3VzZSBzdHJpY3QnO1xuLy8gMjEuMi41LjMgZ2V0IFJlZ0V4cC5wcm90b3R5cGUuZmxhZ3NcbnZhciBhbk9iamVjdCA9IHJlcXVpcmUoJy4vX2FuLW9iamVjdCcpO1xubW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbiAoKSB7XG4gIHZhciB0aGF0ID0gYW5PYmplY3QodGhpcyk7XG4gIHZhciByZXN1bHQgPSAnJztcbiAgaWYgKHRoYXQuZ2xvYmFsKSByZXN1bHQgKz0gJ2cnO1xuICBpZiAodGhhdC5pZ25vcmVDYXNlKSByZXN1bHQgKz0gJ2knO1xuICBpZiAodGhhdC5tdWx0aWxpbmUpIHJlc3VsdCArPSAnbSc7XG4gIGlmICh0aGF0LnVuaWNvZGUpIHJlc3VsdCArPSAndSc7XG4gIGlmICh0aGF0LnN0aWNreSkgcmVzdWx0ICs9ICd5JztcbiAgcmV0dXJuIHJlc3VsdDtcbn07XG4iXSwibWFwcGluZ3MiOiJBQUFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOyIsInNvdXJjZVJvb3QiOiIifQ==\n//# sourceURL=webpack-internal:///0bfb\n"); - -/***/ }), - -/***/ "0d58": -/*!******************************************************!*\ - !*** ./node_modules/core-js/modules/_object-keys.js ***! - \******************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -eval("// 19.1.2.14 / 15.2.3.14 Object.keys(O)\nvar $keys = __webpack_require__(/*! ./_object-keys-internal */ \"ce10\");\nvar enumBugKeys = __webpack_require__(/*! ./_enum-bug-keys */ \"e11e\");\n\nmodule.exports = Object.keys || function keys(O) {\n return $keys(O, enumBugKeys);\n};\n//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMGQ1OC5qcyIsInNvdXJjZXMiOlsid2VicGFjazovLy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvX29iamVjdC1rZXlzLmpzPzBkNTgiXSwic291cmNlc0NvbnRlbnQiOlsiLy8gMTkuMS4yLjE0IC8gMTUuMi4zLjE0IE9iamVjdC5rZXlzKE8pXG52YXIgJGtleXMgPSByZXF1aXJlKCcuL19vYmplY3Qta2V5cy1pbnRlcm5hbCcpO1xudmFyIGVudW1CdWdLZXlzID0gcmVxdWlyZSgnLi9fZW51bS1idWcta2V5cycpO1xuXG5tb2R1bGUuZXhwb3J0cyA9IE9iamVjdC5rZXlzIHx8IGZ1bmN0aW9uIGtleXMoTykge1xuICByZXR1cm4gJGtleXMoTywgZW51bUJ1Z0tleXMpO1xufTtcbiJdLCJtYXBwaW5ncyI6IkFBQUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Iiwic291cmNlUm9vdCI6IiJ9\n//# sourceURL=webpack-internal:///0d58\n"); - -/***/ }), - -/***/ "0fc9": -/*!********************************************************************!*\ - !*** ./node_modules/core-js/library/modules/_to-absolute-index.js ***! - \********************************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -eval("var toInteger = __webpack_require__(/*! ./_to-integer */ \"3a38\");\nvar max = Math.max;\nvar min = Math.min;\nmodule.exports = function (index, length) {\n index = toInteger(index);\n return index < 0 ? max(index + length, 0) : min(index, length);\n};\n//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMGZjOS5qcyIsInNvdXJjZXMiOlsid2VicGFjazovLy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL2xpYnJhcnkvbW9kdWxlcy9fdG8tYWJzb2x1dGUtaW5kZXguanM/MGZjOSJdLCJzb3VyY2VzQ29udGVudCI6WyJ2YXIgdG9JbnRlZ2VyID0gcmVxdWlyZSgnLi9fdG8taW50ZWdlcicpO1xudmFyIG1heCA9IE1hdGgubWF4O1xudmFyIG1pbiA9IE1hdGgubWluO1xubW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbiAoaW5kZXgsIGxlbmd0aCkge1xuICBpbmRleCA9IHRvSW50ZWdlcihpbmRleCk7XG4gIHJldHVybiBpbmRleCA8IDAgPyBtYXgoaW5kZXggKyBsZW5ndGgsIDApIDogbWluKGluZGV4LCBsZW5ndGgpO1xufTtcbiJdLCJtYXBwaW5ncyI6IkFBQUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Iiwic291cmNlUm9vdCI6IiJ9\n//# sourceURL=webpack-internal:///0fc9\n"); - -/***/ }), - -/***/ "1157": -/*!********************************************!*\ - !*** ./node_modules/jquery/dist/jquery.js ***! - \********************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -eval("var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/*!\n * jQuery JavaScript Library v3.3.1\n * https://jquery.com/\n *\n * Includes Sizzle.js\n * https://sizzlejs.com/\n *\n * Copyright JS Foundation and other contributors\n * Released under the MIT license\n * https://jquery.org/license\n *\n * Date: 2018-01-20T17:24Z\n */\n( function( global, factory ) {\n\n\t\"use strict\";\n\n\tif ( true && typeof module.exports === \"object\" ) {\n\n\t\t// For CommonJS and CommonJS-like environments where a proper `window`\n\t\t// is present, execute the factory and get jQuery.\n\t\t// For environments that do not have a `window` with a `document`\n\t\t// (such as Node.js), expose a factory as module.exports.\n\t\t// This accentuates the need for the creation of a real `window`.\n\t\t// e.g. var jQuery = require(\"jquery\")(window);\n\t\t// See ticket #14549 for more info.\n\t\tmodule.exports = global.document ?\n\t\t\tfactory( global, true ) :\n\t\t\tfunction( w ) {\n\t\t\t\tif ( !w.document ) {\n\t\t\t\t\tthrow new Error( \"jQuery requires a window with a document\" );\n\t\t\t\t}\n\t\t\t\treturn factory( w );\n\t\t\t};\n\t} else {\n\t\tfactory( global );\n\t}\n\n// Pass this if window is not defined yet\n} )( typeof window !== \"undefined\" ? window : this, function( window, noGlobal ) {\n\n// Edge <= 12 - 13+, Firefox <=18 - 45+, IE 10 - 11, Safari 5.1 - 9+, iOS 6 - 9.1\n// throw exceptions when non-strict code (e.g., ASP.NET 4.5) accesses strict mode\n// arguments.callee.caller (trac-13335). But as of jQuery 3.0 (2016), strict mode should be common\n// enough that all such attempts are guarded in a try block.\n\"use strict\";\n\nvar arr = [];\n\nvar document = window.document;\n\nvar getProto = Object.getPrototypeOf;\n\nvar slice = arr.slice;\n\nvar concat = arr.concat;\n\nvar push = arr.push;\n\nvar indexOf = arr.indexOf;\n\nvar class2type = {};\n\nvar toString = class2type.toString;\n\nvar hasOwn = class2type.hasOwnProperty;\n\nvar fnToString = hasOwn.toString;\n\nvar ObjectFunctionString = fnToString.call( Object );\n\nvar support = {};\n\nvar isFunction = function isFunction( obj ) {\n\n // Support: Chrome <=57, Firefox <=52\n // In some browsers, typeof returns \"function\" for HTML elements\n // (i.e., `typeof document.createElement( \"object\" ) === \"function\"`).\n // We don't want to classify *any* DOM node as a function.\n return typeof obj === \"function\" && typeof obj.nodeType !== \"number\";\n };\n\n\nvar isWindow = function isWindow( obj ) {\n\t\treturn obj != null && obj === obj.window;\n\t};\n\n\n\n\n\tvar preservedScriptAttributes = {\n\t\ttype: true,\n\t\tsrc: true,\n\t\tnoModule: true\n\t};\n\n\tfunction DOMEval( code, doc, node ) {\n\t\tdoc = doc || document;\n\n\t\tvar i,\n\t\t\tscript = doc.createElement( \"script\" );\n\n\t\tscript.text = code;\n\t\tif ( node ) {\n\t\t\tfor ( i in preservedScriptAttributes ) {\n\t\t\t\tif ( node[ i ] ) {\n\t\t\t\t\tscript[ i ] = node[ i ];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tdoc.head.appendChild( script ).parentNode.removeChild( script );\n\t}\n\n\nfunction toType( obj ) {\n\tif ( obj == null ) {\n\t\treturn obj + \"\";\n\t}\n\n\t// Support: Android <=2.3 only (functionish RegExp)\n\treturn typeof obj === \"object\" || typeof obj === \"function\" ?\n\t\tclass2type[ toString.call( obj ) ] || \"object\" :\n\t\ttypeof obj;\n}\n/* global Symbol */\n// Defining this global in .eslintrc.json would create a danger of using the global\n// unguarded in another place, it seems safer to define global only for this module\n\n\n\nvar\n\tversion = \"3.3.1\",\n\n\t// Define a local copy of jQuery\n\tjQuery = function( selector, context ) {\n\n\t\t// The jQuery object is actually just the init constructor 'enhanced'\n\t\t// Need init if jQuery is called (just allow error to be thrown if not included)\n\t\treturn new jQuery.fn.init( selector, context );\n\t},\n\n\t// Support: Android <=4.0 only\n\t// Make sure we trim BOM and NBSP\n\trtrim = /^[\\s\\uFEFF\\xA0]+|[\\s\\uFEFF\\xA0]+$/g;\n\njQuery.fn = jQuery.prototype = {\n\n\t// The current version of jQuery being used\n\tjquery: version,\n\n\tconstructor: jQuery,\n\n\t// The default length of a jQuery object is 0\n\tlength: 0,\n\n\ttoArray: function() {\n\t\treturn slice.call( this );\n\t},\n\n\t// Get the Nth element in the matched element set OR\n\t// Get the whole matched element set as a clean array\n\tget: function( num ) {\n\n\t\t// Return all the elements in a clean array\n\t\tif ( num == null ) {\n\t\t\treturn slice.call( this );\n\t\t}\n\n\t\t// Return just the one element from the set\n\t\treturn num < 0 ? this[ num + this.length ] : this[ num ];\n\t},\n\n\t// Take an array of elements and push it onto the stack\n\t// (returning the new matched element set)\n\tpushStack: function( elems ) {\n\n\t\t// Build a new jQuery matched element set\n\t\tvar ret = jQuery.merge( this.constructor(), elems );\n\n\t\t// Add the old object onto the stack (as a reference)\n\t\tret.prevObject = this;\n\n\t\t// Return the newly-formed element set\n\t\treturn ret;\n\t},\n\n\t// Execute a callback for every element in the matched set.\n\teach: function( callback ) {\n\t\treturn jQuery.each( this, callback );\n\t},\n\n\tmap: function( callback ) {\n\t\treturn this.pushStack( jQuery.map( this, function( elem, i ) {\n\t\t\treturn callback.call( elem, i, elem );\n\t\t} ) );\n\t},\n\n\tslice: function() {\n\t\treturn this.pushStack( slice.apply( this, arguments ) );\n\t},\n\n\tfirst: function() {\n\t\treturn this.eq( 0 );\n\t},\n\n\tlast: function() {\n\t\treturn this.eq( -1 );\n\t},\n\n\teq: function( i ) {\n\t\tvar len = this.length,\n\t\t\tj = +i + ( i < 0 ? len : 0 );\n\t\treturn this.pushStack( j >= 0 && j < len ? [ this[ j ] ] : [] );\n\t},\n\n\tend: function() {\n\t\treturn this.prevObject || this.constructor();\n\t},\n\n\t// For internal use only.\n\t// Behaves like an Array's method, not like a jQuery method.\n\tpush: push,\n\tsort: arr.sort,\n\tsplice: arr.splice\n};\n\njQuery.extend = jQuery.fn.extend = function() {\n\tvar options, name, src, copy, copyIsArray, clone,\n\t\ttarget = arguments[ 0 ] || {},\n\t\ti = 1,\n\t\tlength = arguments.length,\n\t\tdeep = false;\n\n\t// Handle a deep copy situation\n\tif ( typeof target === \"boolean\" ) {\n\t\tdeep = target;\n\n\t\t// Skip the boolean and the target\n\t\ttarget = arguments[ i ] || {};\n\t\ti++;\n\t}\n\n\t// Handle case when target is a string or something (possible in deep copy)\n\tif ( typeof target !== \"object\" && !isFunction( target ) ) {\n\t\ttarget = {};\n\t}\n\n\t// Extend jQuery itself if only one argument is passed\n\tif ( i === length ) {\n\t\ttarget = this;\n\t\ti--;\n\t}\n\n\tfor ( ; i < length; i++ ) {\n\n\t\t// Only deal with non-null/undefined values\n\t\tif ( ( options = arguments[ i ] ) != null ) {\n\n\t\t\t// Extend the base object\n\t\t\tfor ( name in options ) {\n\t\t\t\tsrc = target[ name ];\n\t\t\t\tcopy = options[ name ];\n\n\t\t\t\t// Prevent never-ending loop\n\t\t\t\tif ( target === copy ) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t// Recurse if we're merging plain objects or arrays\n\t\t\t\tif ( deep && copy && ( jQuery.isPlainObject( copy ) ||\n\t\t\t\t\t( copyIsArray = Array.isArray( copy ) ) ) ) {\n\n\t\t\t\t\tif ( copyIsArray ) {\n\t\t\t\t\t\tcopyIsArray = false;\n\t\t\t\t\t\tclone = src && Array.isArray( src ) ? src : [];\n\n\t\t\t\t\t} else {\n\t\t\t\t\t\tclone = src && jQuery.isPlainObject( src ) ? src : {};\n\t\t\t\t\t}\n\n\t\t\t\t\t// Never move original objects, clone them\n\t\t\t\t\ttarget[ name ] = jQuery.extend( deep, clone, copy );\n\n\t\t\t\t// Don't bring in undefined values\n\t\t\t\t} else if ( copy !== undefined ) {\n\t\t\t\t\ttarget[ name ] = copy;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// Return the modified object\n\treturn target;\n};\n\njQuery.extend( {\n\n\t// Unique for each copy of jQuery on the page\n\texpando: \"jQuery\" + ( version + Math.random() ).replace( /\\D/g, \"\" ),\n\n\t// Assume jQuery is ready without the ready module\n\tisReady: true,\n\n\terror: function( msg ) {\n\t\tthrow new Error( msg );\n\t},\n\n\tnoop: function() {},\n\n\tisPlainObject: function( obj ) {\n\t\tvar proto, Ctor;\n\n\t\t// Detect obvious negatives\n\t\t// Use toString instead of jQuery.type to catch host objects\n\t\tif ( !obj || toString.call( obj ) !== \"[object Object]\" ) {\n\t\t\treturn false;\n\t\t}\n\n\t\tproto = getProto( obj );\n\n\t\t// Objects with no prototype (e.g., `Object.create( null )`) are plain\n\t\tif ( !proto ) {\n\t\t\treturn true;\n\t\t}\n\n\t\t// Objects with prototype are plain iff they were constructed by a global Object function\n\t\tCtor = hasOwn.call( proto, \"constructor\" ) && proto.constructor;\n\t\treturn typeof Ctor === \"function\" && fnToString.call( Ctor ) === ObjectFunctionString;\n\t},\n\n\tisEmptyObject: function( obj ) {\n\n\t\t/* eslint-disable no-unused-vars */\n\t\t// See https://github.com/eslint/eslint/issues/6125\n\t\tvar name;\n\n\t\tfor ( name in obj ) {\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t},\n\n\t// Evaluates a script in a global context\n\tglobalEval: function( code ) {\n\t\tDOMEval( code );\n\t},\n\n\teach: function( obj, callback ) {\n\t\tvar length, i = 0;\n\n\t\tif ( isArrayLike( obj ) ) {\n\t\t\tlength = obj.length;\n\t\t\tfor ( ; i < length; i++ ) {\n\t\t\t\tif ( callback.call( obj[ i ], i, obj[ i ] ) === false ) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tfor ( i in obj ) {\n\t\t\t\tif ( callback.call( obj[ i ], i, obj[ i ] ) === false ) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn obj;\n\t},\n\n\t// Support: Android <=4.0 only\n\ttrim: function( text ) {\n\t\treturn text == null ?\n\t\t\t\"\" :\n\t\t\t( text + \"\" ).replace( rtrim, \"\" );\n\t},\n\n\t// results is for internal usage only\n\tmakeArray: function( arr, results ) {\n\t\tvar ret = results || [];\n\n\t\tif ( arr != null ) {\n\t\t\tif ( isArrayLike( Object( arr ) ) ) {\n\t\t\t\tjQuery.merge( ret,\n\t\t\t\t\ttypeof arr === \"string\" ?\n\t\t\t\t\t[ arr ] : arr\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tpush.call( ret, arr );\n\t\t\t}\n\t\t}\n\n\t\treturn ret;\n\t},\n\n\tinArray: function( elem, arr, i ) {\n\t\treturn arr == null ? -1 : indexOf.call( arr, elem, i );\n\t},\n\n\t// Support: Android <=4.0 only, PhantomJS 1 only\n\t// push.apply(_, arraylike) throws on ancient WebKit\n\tmerge: function( first, second ) {\n\t\tvar len = +second.length,\n\t\t\tj = 0,\n\t\t\ti = first.length;\n\n\t\tfor ( ; j < len; j++ ) {\n\t\t\tfirst[ i++ ] = second[ j ];\n\t\t}\n\n\t\tfirst.length = i;\n\n\t\treturn first;\n\t},\n\n\tgrep: function( elems, callback, invert ) {\n\t\tvar callbackInverse,\n\t\t\tmatches = [],\n\t\t\ti = 0,\n\t\t\tlength = elems.length,\n\t\t\tcallbackExpect = !invert;\n\n\t\t// Go through the array, only saving the items\n\t\t// that pass the validator function\n\t\tfor ( ; i < length; i++ ) {\n\t\t\tcallbackInverse = !callback( elems[ i ], i );\n\t\t\tif ( callbackInverse !== callbackExpect ) {\n\t\t\t\tmatches.push( elems[ i ] );\n\t\t\t}\n\t\t}\n\n\t\treturn matches;\n\t},\n\n\t// arg is for internal usage only\n\tmap: function( elems, callback, arg ) {\n\t\tvar length, value,\n\t\t\ti = 0,\n\t\t\tret = [];\n\n\t\t// Go through the array, translating each of the items to their new values\n\t\tif ( isArrayLike( elems ) ) {\n\t\t\tlength = elems.length;\n\t\t\tfor ( ; i < length; i++ ) {\n\t\t\t\tvalue = callback( elems[ i ], i, arg );\n\n\t\t\t\tif ( value != null ) {\n\t\t\t\t\tret.push( value );\n\t\t\t\t}\n\t\t\t}\n\n\t\t// Go through every key on the object,\n\t\t} else {\n\t\t\tfor ( i in elems ) {\n\t\t\t\tvalue = callback( elems[ i ], i, arg );\n\n\t\t\t\tif ( value != null ) {\n\t\t\t\t\tret.push( value );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Flatten any nested arrays\n\t\treturn concat.apply( [], ret );\n\t},\n\n\t// A global GUID counter for objects\n\tguid: 1,\n\n\t// jQuery.support is not used in Core but other projects attach their\n\t// properties to it so it needs to exist.\n\tsupport: support\n} );\n\nif ( typeof Symbol === \"function\" ) {\n\tjQuery.fn[ Symbol.iterator ] = arr[ Symbol.iterator ];\n}\n\n// Populate the class2type map\njQuery.each( \"Boolean Number String Function Array Date RegExp Object Error Symbol\".split( \" \" ),\nfunction( i, name ) {\n\tclass2type[ \"[object \" + name + \"]\" ] = name.toLowerCase();\n} );\n\nfunction isArrayLike( obj ) {\n\n\t// Support: real iOS 8.2 only (not reproducible in simulator)\n\t// `in` check used to prevent JIT error (gh-2145)\n\t// hasOwn isn't used here due to false negatives\n\t// regarding Nodelist length in IE\n\tvar length = !!obj && \"length\" in obj && obj.length,\n\t\ttype = toType( obj );\n\n\tif ( isFunction( obj ) || isWindow( obj ) ) {\n\t\treturn false;\n\t}\n\n\treturn type === \"array\" || length === 0 ||\n\t\ttypeof length === \"number\" && length > 0 && ( length - 1 ) in obj;\n}\nvar Sizzle =\n/*!\n * Sizzle CSS Selector Engine v2.3.3\n * https://sizzlejs.com/\n *\n * Copyright jQuery Foundation and other contributors\n * Released under the MIT license\n * http://jquery.org/license\n *\n * Date: 2016-08-08\n */\n(function( window ) {\n\nvar i,\n\tsupport,\n\tExpr,\n\tgetText,\n\tisXML,\n\ttokenize,\n\tcompile,\n\tselect,\n\toutermostContext,\n\tsortInput,\n\thasDuplicate,\n\n\t// Local document vars\n\tsetDocument,\n\tdocument,\n\tdocElem,\n\tdocumentIsHTML,\n\trbuggyQSA,\n\trbuggyMatches,\n\tmatches,\n\tcontains,\n\n\t// Instance-specific data\n\texpando = \"sizzle\" + 1 * new Date(),\n\tpreferredDoc = window.document,\n\tdirruns = 0,\n\tdone = 0,\n\tclassCache = createCache(),\n\ttokenCache = createCache(),\n\tcompilerCache = createCache(),\n\tsortOrder = function( a, b ) {\n\t\tif ( a === b ) {\n\t\t\thasDuplicate = true;\n\t\t}\n\t\treturn 0;\n\t},\n\n\t// Instance methods\n\thasOwn = ({}).hasOwnProperty,\n\tarr = [],\n\tpop = arr.pop,\n\tpush_native = arr.push,\n\tpush = arr.push,\n\tslice = arr.slice,\n\t// Use a stripped-down indexOf as it's faster than native\n\t// https://jsperf.com/thor-indexof-vs-for/5\n\tindexOf = function( list, elem ) {\n\t\tvar i = 0,\n\t\t\tlen = list.length;\n\t\tfor ( ; i < len; i++ ) {\n\t\t\tif ( list[i] === elem ) {\n\t\t\t\treturn i;\n\t\t\t}\n\t\t}\n\t\treturn -1;\n\t},\n\n\tbooleans = \"checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped\",\n\n\t// Regular expressions\n\n\t// http://www.w3.org/TR/css3-selectors/#whitespace\n\twhitespace = \"[\\\\x20\\\\t\\\\r\\\\n\\\\f]\",\n\n\t// http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier\n\tidentifier = \"(?:\\\\\\\\.|[\\\\w-]|[^\\0-\\\\xa0])+\",\n\n\t// Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors\n\tattributes = \"\\\\[\" + whitespace + \"*(\" + identifier + \")(?:\" + whitespace +\n\t\t// Operator (capture 2)\n\t\t\"*([*^$|!~]?=)\" + whitespace +\n\t\t// \"Attribute values must be CSS identifiers [capture 5] or strings [capture 3 or capture 4]\"\n\t\t\"*(?:'((?:\\\\\\\\.|[^\\\\\\\\'])*)'|\\\"((?:\\\\\\\\.|[^\\\\\\\\\\\"])*)\\\"|(\" + identifier + \"))|)\" + whitespace +\n\t\t\"*\\\\]\",\n\n\tpseudos = \":(\" + identifier + \")(?:\\\\((\" +\n\t\t// To reduce the number of selectors needing tokenize in the preFilter, prefer arguments:\n\t\t// 1. quoted (capture 3; capture 4 or capture 5)\n\t\t\"('((?:\\\\\\\\.|[^\\\\\\\\'])*)'|\\\"((?:\\\\\\\\.|[^\\\\\\\\\\\"])*)\\\")|\" +\n\t\t// 2. simple (capture 6)\n\t\t\"((?:\\\\\\\\.|[^\\\\\\\\()[\\\\]]|\" + attributes + \")*)|\" +\n\t\t// 3. anything else (capture 2)\n\t\t\".*\" +\n\t\t\")\\\\)|)\",\n\n\t// Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter\n\trwhitespace = new RegExp( whitespace + \"+\", \"g\" ),\n\trtrim = new RegExp( \"^\" + whitespace + \"+|((?:^|[^\\\\\\\\])(?:\\\\\\\\.)*)\" + whitespace + \"+$\", \"g\" ),\n\n\trcomma = new RegExp( \"^\" + whitespace + \"*,\" + whitespace + \"*\" ),\n\trcombinators = new RegExp( \"^\" + whitespace + \"*([>+~]|\" + whitespace + \")\" + whitespace + \"*\" ),\n\n\trattributeQuotes = new RegExp( \"=\" + whitespace + \"*([^\\\\]'\\\"]*?)\" + whitespace + \"*\\\\]\", \"g\" ),\n\n\trpseudo = new RegExp( pseudos ),\n\tridentifier = new RegExp( \"^\" + identifier + \"$\" ),\n\n\tmatchExpr = {\n\t\t\"ID\": new RegExp( \"^#(\" + identifier + \")\" ),\n\t\t\"CLASS\": new RegExp( \"^\\\\.(\" + identifier + \")\" ),\n\t\t\"TAG\": new RegExp( \"^(\" + identifier + \"|[*])\" ),\n\t\t\"ATTR\": new RegExp( \"^\" + attributes ),\n\t\t\"PSEUDO\": new RegExp( \"^\" + pseudos ),\n\t\t\"CHILD\": new RegExp( \"^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\\\(\" + whitespace +\n\t\t\t\"*(even|odd|(([+-]|)(\\\\d*)n|)\" + whitespace + \"*(?:([+-]|)\" + whitespace +\n\t\t\t\"*(\\\\d+)|))\" + whitespace + \"*\\\\)|)\", \"i\" ),\n\t\t\"bool\": new RegExp( \"^(?:\" + booleans + \")$\", \"i\" ),\n\t\t// For use in libraries implementing .is()\n\t\t// We use this for POS matching in `select`\n\t\t\"needsContext\": new RegExp( \"^\" + whitespace + \"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\\\(\" +\n\t\t\twhitespace + \"*((?:-\\\\d)?\\\\d*)\" + whitespace + \"*\\\\)|)(?=[^-]|$)\", \"i\" )\n\t},\n\n\trinputs = /^(?:input|select|textarea|button)$/i,\n\trheader = /^h\\d$/i,\n\n\trnative = /^[^{]+\\{\\s*\\[native \\w/,\n\n\t// Easily-parseable/retrievable ID or TAG or CLASS selectors\n\trquickExpr = /^(?:#([\\w-]+)|(\\w+)|\\.([\\w-]+))$/,\n\n\trsibling = /[+~]/,\n\n\t// CSS escapes\n\t// http://www.w3.org/TR/CSS21/syndata.html#escaped-characters\n\trunescape = new RegExp( \"\\\\\\\\([\\\\da-f]{1,6}\" + whitespace + \"?|(\" + whitespace + \")|.)\", \"ig\" ),\n\tfunescape = function( _, escaped, escapedWhitespace ) {\n\t\tvar high = \"0x\" + escaped - 0x10000;\n\t\t// NaN means non-codepoint\n\t\t// Support: Firefox<24\n\t\t// Workaround erroneous numeric interpretation of +\"0x\"\n\t\treturn high !== high || escapedWhitespace ?\n\t\t\tescaped :\n\t\t\thigh < 0 ?\n\t\t\t\t// BMP codepoint\n\t\t\t\tString.fromCharCode( high + 0x10000 ) :\n\t\t\t\t// Supplemental Plane codepoint (surrogate pair)\n\t\t\t\tString.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 );\n\t},\n\n\t// CSS string/identifier serialization\n\t// https://drafts.csswg.org/cssom/#common-serializing-idioms\n\trcssescape = /([\\0-\\x1f\\x7f]|^-?\\d)|^-$|[^\\0-\\x1f\\x7f-\\uFFFF\\w-]/g,\n\tfcssescape = function( ch, asCodePoint ) {\n\t\tif ( asCodePoint ) {\n\n\t\t\t// U+0000 NULL becomes U+FFFD REPLACEMENT CHARACTER\n\t\t\tif ( ch === \"\\0\" ) {\n\t\t\t\treturn \"\\uFFFD\";\n\t\t\t}\n\n\t\t\t// Control characters and (dependent upon position) numbers get escaped as code points\n\t\t\treturn ch.slice( 0, -1 ) + \"\\\\\" + ch.charCodeAt( ch.length - 1 ).toString( 16 ) + \" \";\n\t\t}\n\n\t\t// Other potentially-special ASCII characters get backslash-escaped\n\t\treturn \"\\\\\" + ch;\n\t},\n\n\t// Used for iframes\n\t// See setDocument()\n\t// Removing the function wrapper causes a \"Permission Denied\"\n\t// error in IE\n\tunloadHandler = function() {\n\t\tsetDocument();\n\t},\n\n\tdisabledAncestor = addCombinator(\n\t\tfunction( elem ) {\n\t\t\treturn elem.disabled === true && (\"form\" in elem || \"label\" in elem);\n\t\t},\n\t\t{ dir: \"parentNode\", next: \"legend\" }\n\t);\n\n// Optimize for push.apply( _, NodeList )\ntry {\n\tpush.apply(\n\t\t(arr = slice.call( preferredDoc.childNodes )),\n\t\tpreferredDoc.childNodes\n\t);\n\t// Support: Android<4.0\n\t// Detect silently failing push.apply\n\tarr[ preferredDoc.childNodes.length ].nodeType;\n} catch ( e ) {\n\tpush = { apply: arr.length ?\n\n\t\t// Leverage slice if possible\n\t\tfunction( target, els ) {\n\t\t\tpush_native.apply( target, slice.call(els) );\n\t\t} :\n\n\t\t// Support: IE<9\n\t\t// Otherwise append directly\n\t\tfunction( target, els ) {\n\t\t\tvar j = target.length,\n\t\t\t\ti = 0;\n\t\t\t// Can't trust NodeList.length\n\t\t\twhile ( (target[j++] = els[i++]) ) {}\n\t\t\ttarget.length = j - 1;\n\t\t}\n\t};\n}\n\nfunction Sizzle( selector, context, results, seed ) {\n\tvar m, i, elem, nid, match, groups, newSelector,\n\t\tnewContext = context && context.ownerDocument,\n\n\t\t// nodeType defaults to 9, since context defaults to document\n\t\tnodeType = context ? context.nodeType : 9;\n\n\tresults = results || [];\n\n\t// Return early from calls with invalid selector or context\n\tif ( typeof selector !== \"string\" || !selector ||\n\t\tnodeType !== 1 && nodeType !== 9 && nodeType !== 11 ) {\n\n\t\treturn results;\n\t}\n\n\t// Try to shortcut find operations (as opposed to filters) in HTML documents\n\tif ( !seed ) {\n\n\t\tif ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) {\n\t\t\tsetDocument( context );\n\t\t}\n\t\tcontext = context || document;\n\n\t\tif ( documentIsHTML ) {\n\n\t\t\t// If the selector is sufficiently simple, try using a \"get*By*\" DOM method\n\t\t\t// (excepting DocumentFragment context, where the methods don't exist)\n\t\t\tif ( nodeType !== 11 && (match = rquickExpr.exec( selector )) ) {\n\n\t\t\t\t// ID selector\n\t\t\t\tif ( (m = match[1]) ) {\n\n\t\t\t\t\t// Document context\n\t\t\t\t\tif ( nodeType === 9 ) {\n\t\t\t\t\t\tif ( (elem = context.getElementById( m )) ) {\n\n\t\t\t\t\t\t\t// Support: IE, Opera, Webkit\n\t\t\t\t\t\t\t// TODO: identify versions\n\t\t\t\t\t\t\t// getElementById can match elements by name instead of ID\n\t\t\t\t\t\t\tif ( elem.id === m ) {\n\t\t\t\t\t\t\t\tresults.push( elem );\n\t\t\t\t\t\t\t\treturn results;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\treturn results;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t// Element context\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\t// Support: IE, Opera, Webkit\n\t\t\t\t\t\t// TODO: identify versions\n\t\t\t\t\t\t// getElementById can match elements by name instead of ID\n\t\t\t\t\t\tif ( newContext && (elem = newContext.getElementById( m )) &&\n\t\t\t\t\t\t\tcontains( context, elem ) &&\n\t\t\t\t\t\t\telem.id === m ) {\n\n\t\t\t\t\t\t\tresults.push( elem );\n\t\t\t\t\t\t\treturn results;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t// Type selector\n\t\t\t\t} else if ( match[2] ) {\n\t\t\t\t\tpush.apply( results, context.getElementsByTagName( selector ) );\n\t\t\t\t\treturn results;\n\n\t\t\t\t// Class selector\n\t\t\t\t} else if ( (m = match[3]) && support.getElementsByClassName &&\n\t\t\t\t\tcontext.getElementsByClassName ) {\n\n\t\t\t\t\tpush.apply( results, context.getElementsByClassName( m ) );\n\t\t\t\t\treturn results;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Take advantage of querySelectorAll\n\t\t\tif ( support.qsa &&\n\t\t\t\t!compilerCache[ selector + \" \" ] &&\n\t\t\t\t(!rbuggyQSA || !rbuggyQSA.test( selector )) ) {\n\n\t\t\t\tif ( nodeType !== 1 ) {\n\t\t\t\t\tnewContext = context;\n\t\t\t\t\tnewSelector = selector;\n\n\t\t\t\t// qSA looks outside Element context, which is not what we want\n\t\t\t\t// Thanks to Andrew Dupont for this workaround technique\n\t\t\t\t// Support: IE <=8\n\t\t\t\t// Exclude object elements\n\t\t\t\t} else if ( context.nodeName.toLowerCase() !== \"object\" ) {\n\n\t\t\t\t\t// Capture the context ID, setting it first if necessary\n\t\t\t\t\tif ( (nid = context.getAttribute( \"id\" )) ) {\n\t\t\t\t\t\tnid = nid.replace( rcssescape, fcssescape );\n\t\t\t\t\t} else {\n\t\t\t\t\t\tcontext.setAttribute( \"id\", (nid = expando) );\n\t\t\t\t\t}\n\n\t\t\t\t\t// Prefix every selector in the list\n\t\t\t\t\tgroups = tokenize( selector );\n\t\t\t\t\ti = groups.length;\n\t\t\t\t\twhile ( i-- ) {\n\t\t\t\t\t\tgroups[i] = \"#\" + nid + \" \" + toSelector( groups[i] );\n\t\t\t\t\t}\n\t\t\t\t\tnewSelector = groups.join( \",\" );\n\n\t\t\t\t\t// Expand context for sibling selectors\n\t\t\t\t\tnewContext = rsibling.test( selector ) && testContext( context.parentNode ) ||\n\t\t\t\t\t\tcontext;\n\t\t\t\t}\n\n\t\t\t\tif ( newSelector ) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tpush.apply( results,\n\t\t\t\t\t\t\tnewContext.querySelectorAll( newSelector )\n\t\t\t\t\t\t);\n\t\t\t\t\t\treturn results;\n\t\t\t\t\t} catch ( qsaError ) {\n\t\t\t\t\t} finally {\n\t\t\t\t\t\tif ( nid === expando ) {\n\t\t\t\t\t\t\tcontext.removeAttribute( \"id\" );\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// All others\n\treturn select( selector.replace( rtrim, \"$1\" ), context, results, seed );\n}\n\n/**\n * Create key-value caches of limited size\n * @returns {function(string, object)} Returns the Object data after storing it on itself with\n *\tproperty name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength)\n *\tdeleting the oldest entry\n */\nfunction createCache() {\n\tvar keys = [];\n\n\tfunction cache( key, value ) {\n\t\t// Use (key + \" \") to avoid collision with native prototype properties (see Issue #157)\n\t\tif ( keys.push( key + \" \" ) > Expr.cacheLength ) {\n\t\t\t// Only keep the most recent entries\n\t\t\tdelete cache[ keys.shift() ];\n\t\t}\n\t\treturn (cache[ key + \" \" ] = value);\n\t}\n\treturn cache;\n}\n\n/**\n * Mark a function for special use by Sizzle\n * @param {Function} fn The function to mark\n */\nfunction markFunction( fn ) {\n\tfn[ expando ] = true;\n\treturn fn;\n}\n\n/**\n * Support testing using an element\n * @param {Function} fn Passed the created element and returns a boolean result\n */\nfunction assert( fn ) {\n\tvar el = document.createElement(\"fieldset\");\n\n\ttry {\n\t\treturn !!fn( el );\n\t} catch (e) {\n\t\treturn false;\n\t} finally {\n\t\t// Remove from its parent by default\n\t\tif ( el.parentNode ) {\n\t\t\tel.parentNode.removeChild( el );\n\t\t}\n\t\t// release memory in IE\n\t\tel = null;\n\t}\n}\n\n/**\n * Adds the same handler for all of the specified attrs\n * @param {String} attrs Pipe-separated list of attributes\n * @param {Function} handler The method that will be applied\n */\nfunction addHandle( attrs, handler ) {\n\tvar arr = attrs.split(\"|\"),\n\t\ti = arr.length;\n\n\twhile ( i-- ) {\n\t\tExpr.attrHandle[ arr[i] ] = handler;\n\t}\n}\n\n/**\n * Checks document order of two siblings\n * @param {Element} a\n * @param {Element} b\n * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b\n */\nfunction siblingCheck( a, b ) {\n\tvar cur = b && a,\n\t\tdiff = cur && a.nodeType === 1 && b.nodeType === 1 &&\n\t\t\ta.sourceIndex - b.sourceIndex;\n\n\t// Use IE sourceIndex if available on both nodes\n\tif ( diff ) {\n\t\treturn diff;\n\t}\n\n\t// Check if b follows a\n\tif ( cur ) {\n\t\twhile ( (cur = cur.nextSibling) ) {\n\t\t\tif ( cur === b ) {\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn a ? 1 : -1;\n}\n\n/**\n * Returns a function to use in pseudos for input types\n * @param {String} type\n */\nfunction createInputPseudo( type ) {\n\treturn function( elem ) {\n\t\tvar name = elem.nodeName.toLowerCase();\n\t\treturn name === \"input\" && elem.type === type;\n\t};\n}\n\n/**\n * Returns a function to use in pseudos for buttons\n * @param {String} type\n */\nfunction createButtonPseudo( type ) {\n\treturn function( elem ) {\n\t\tvar name = elem.nodeName.toLowerCase();\n\t\treturn (name === \"input\" || name === \"button\") && elem.type === type;\n\t};\n}\n\n/**\n * Returns a function to use in pseudos for :enabled/:disabled\n * @param {Boolean} disabled true for :disabled; false for :enabled\n */\nfunction createDisabledPseudo( disabled ) {\n\n\t// Known :disabled false positives: fieldset[disabled] > legend:nth-of-type(n+2) :can-disable\n\treturn function( elem ) {\n\n\t\t// Only certain elements can match :enabled or :disabled\n\t\t// https://html.spec.whatwg.org/multipage/scripting.html#selector-enabled\n\t\t// https://html.spec.whatwg.org/multipage/scripting.html#selector-disabled\n\t\tif ( \"form\" in elem ) {\n\n\t\t\t// Check for inherited disabledness on relevant non-disabled elements:\n\t\t\t// * listed form-associated elements in a disabled fieldset\n\t\t\t// https://html.spec.whatwg.org/multipage/forms.html#category-listed\n\t\t\t// https://html.spec.whatwg.org/multipage/forms.html#concept-fe-disabled\n\t\t\t// * option elements in a disabled optgroup\n\t\t\t// https://html.spec.whatwg.org/multipage/forms.html#concept-option-disabled\n\t\t\t// All such elements have a \"form\" property.\n\t\t\tif ( elem.parentNode && elem.disabled === false ) {\n\n\t\t\t\t// Option elements defer to a parent optgroup if present\n\t\t\t\tif ( \"label\" in elem ) {\n\t\t\t\t\tif ( \"label\" in elem.parentNode ) {\n\t\t\t\t\t\treturn elem.parentNode.disabled === disabled;\n\t\t\t\t\t} else {\n\t\t\t\t\t\treturn elem.disabled === disabled;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Support: IE 6 - 11\n\t\t\t\t// Use the isDisabled shortcut property to check for disabled fieldset ancestors\n\t\t\t\treturn elem.isDisabled === disabled ||\n\n\t\t\t\t\t// Where there is no isDisabled, check manually\n\t\t\t\t\t/* jshint -W018 */\n\t\t\t\t\telem.isDisabled !== !disabled &&\n\t\t\t\t\t\tdisabledAncestor( elem ) === disabled;\n\t\t\t}\n\n\t\t\treturn elem.disabled === disabled;\n\n\t\t// Try to winnow out elements that can't be disabled before trusting the disabled property.\n\t\t// Some victims get caught in our net (label, legend, menu, track), but it shouldn't\n\t\t// even exist on them, let alone have a boolean value.\n\t\t} else if ( \"label\" in elem ) {\n\t\t\treturn elem.disabled === disabled;\n\t\t}\n\n\t\t// Remaining elements are neither :enabled nor :disabled\n\t\treturn false;\n\t};\n}\n\n/**\n * Returns a function to use in pseudos for positionals\n * @param {Function} fn\n */\nfunction createPositionalPseudo( fn ) {\n\treturn markFunction(function( argument ) {\n\t\targument = +argument;\n\t\treturn markFunction(function( seed, matches ) {\n\t\t\tvar j,\n\t\t\t\tmatchIndexes = fn( [], seed.length, argument ),\n\t\t\t\ti = matchIndexes.length;\n\n\t\t\t// Match elements found at the specified indexes\n\t\t\twhile ( i-- ) {\n\t\t\t\tif ( seed[ (j = matchIndexes[i]) ] ) {\n\t\t\t\t\tseed[j] = !(matches[j] = seed[j]);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t});\n}\n\n/**\n * Checks a node for validity as a Sizzle context\n * @param {Element|Object=} context\n * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value\n */\nfunction testContext( context ) {\n\treturn context && typeof context.getElementsByTagName !== \"undefined\" && context;\n}\n\n// Expose support vars for convenience\nsupport = Sizzle.support = {};\n\n/**\n * Detects XML nodes\n * @param {Element|Object} elem An element or a document\n * @returns {Boolean} True iff elem is a non-HTML XML node\n */\nisXML = Sizzle.isXML = function( elem ) {\n\t// documentElement is verified for cases where it doesn't yet exist\n\t// (such as loading iframes in IE - #4833)\n\tvar documentElement = elem && (elem.ownerDocument || elem).documentElement;\n\treturn documentElement ? documentElement.nodeName !== \"HTML\" : false;\n};\n\n/**\n * Sets document-related variables once based on the current document\n * @param {Element|Object} [doc] An element or document object to use to set the document\n * @returns {Object} Returns the current document\n */\nsetDocument = Sizzle.setDocument = function( node ) {\n\tvar hasCompare, subWindow,\n\t\tdoc = node ? node.ownerDocument || node : preferredDoc;\n\n\t// Return early if doc is invalid or already selected\n\tif ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) {\n\t\treturn document;\n\t}\n\n\t// Update global variables\n\tdocument = doc;\n\tdocElem = document.documentElement;\n\tdocumentIsHTML = !isXML( document );\n\n\t// Support: IE 9-11, Edge\n\t// Accessing iframe documents after unload throws \"permission denied\" errors (jQuery #13936)\n\tif ( preferredDoc !== document &&\n\t\t(subWindow = document.defaultView) && subWindow.top !== subWindow ) {\n\n\t\t// Support: IE 11, Edge\n\t\tif ( subWindow.addEventListener ) {\n\t\t\tsubWindow.addEventListener( \"unload\", unloadHandler, false );\n\n\t\t// Support: IE 9 - 10 only\n\t\t} else if ( subWindow.attachEvent ) {\n\t\t\tsubWindow.attachEvent( \"onunload\", unloadHandler );\n\t\t}\n\t}\n\n\t/* Attributes\n\t---------------------------------------------------------------------- */\n\n\t// Support: IE<8\n\t// Verify that getAttribute really returns attributes and not properties\n\t// (excepting IE8 booleans)\n\tsupport.attributes = assert(function( el ) {\n\t\tel.className = \"i\";\n\t\treturn !el.getAttribute(\"className\");\n\t});\n\n\t/* getElement(s)By*\n\t---------------------------------------------------------------------- */\n\n\t// Check if getElementsByTagName(\"*\") returns only elements\n\tsupport.getElementsByTagName = assert(function( el ) {\n\t\tel.appendChild( document.createComment(\"\") );\n\t\treturn !el.getElementsByTagName(\"*\").length;\n\t});\n\n\t// Support: IE<9\n\tsupport.getElementsByClassName = rnative.test( document.getElementsByClassName );\n\n\t// Support: IE<10\n\t// Check if getElementById returns elements by name\n\t// The broken getElementById methods don't pick up programmatically-set names,\n\t// so use a roundabout getElementsByName test\n\tsupport.getById = assert(function( el ) {\n\t\tdocElem.appendChild( el ).id = expando;\n\t\treturn !document.getElementsByName || !document.getElementsByName( expando ).length;\n\t});\n\n\t// ID filter and find\n\tif ( support.getById ) {\n\t\tExpr.filter[\"ID\"] = function( id ) {\n\t\t\tvar attrId = id.replace( runescape, funescape );\n\t\t\treturn function( elem ) {\n\t\t\t\treturn elem.getAttribute(\"id\") === attrId;\n\t\t\t};\n\t\t};\n\t\tExpr.find[\"ID\"] = function( id, context ) {\n\t\t\tif ( typeof context.getElementById !== \"undefined\" && documentIsHTML ) {\n\t\t\t\tvar elem = context.getElementById( id );\n\t\t\t\treturn elem ? [ elem ] : [];\n\t\t\t}\n\t\t};\n\t} else {\n\t\tExpr.filter[\"ID\"] = function( id ) {\n\t\t\tvar attrId = id.replace( runescape, funescape );\n\t\t\treturn function( elem ) {\n\t\t\t\tvar node = typeof elem.getAttributeNode !== \"undefined\" &&\n\t\t\t\t\telem.getAttributeNode(\"id\");\n\t\t\t\treturn node && node.value === attrId;\n\t\t\t};\n\t\t};\n\n\t\t// Support: IE 6 - 7 only\n\t\t// getElementById is not reliable as a find shortcut\n\t\tExpr.find[\"ID\"] = function( id, context ) {\n\t\t\tif ( typeof context.getElementById !== \"undefined\" && documentIsHTML ) {\n\t\t\t\tvar node, i, elems,\n\t\t\t\t\telem = context.getElementById( id );\n\n\t\t\t\tif ( elem ) {\n\n\t\t\t\t\t// Verify the id attribute\n\t\t\t\t\tnode = elem.getAttributeNode(\"id\");\n\t\t\t\t\tif ( node && node.value === id ) {\n\t\t\t\t\t\treturn [ elem ];\n\t\t\t\t\t}\n\n\t\t\t\t\t// Fall back on getElementsByName\n\t\t\t\t\telems = context.getElementsByName( id );\n\t\t\t\t\ti = 0;\n\t\t\t\t\twhile ( (elem = elems[i++]) ) {\n\t\t\t\t\t\tnode = elem.getAttributeNode(\"id\");\n\t\t\t\t\t\tif ( node && node.value === id ) {\n\t\t\t\t\t\t\treturn [ elem ];\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn [];\n\t\t\t}\n\t\t};\n\t}\n\n\t// Tag\n\tExpr.find[\"TAG\"] = support.getElementsByTagName ?\n\t\tfunction( tag, context ) {\n\t\t\tif ( typeof context.getElementsByTagName !== \"undefined\" ) {\n\t\t\t\treturn context.getElementsByTagName( tag );\n\n\t\t\t// DocumentFragment nodes don't have gEBTN\n\t\t\t} else if ( support.qsa ) {\n\t\t\t\treturn context.querySelectorAll( tag );\n\t\t\t}\n\t\t} :\n\n\t\tfunction( tag, context ) {\n\t\t\tvar elem,\n\t\t\t\ttmp = [],\n\t\t\t\ti = 0,\n\t\t\t\t// By happy coincidence, a (broken) gEBTN appears on DocumentFragment nodes too\n\t\t\t\tresults = context.getElementsByTagName( tag );\n\n\t\t\t// Filter out possible comments\n\t\t\tif ( tag === \"*\" ) {\n\t\t\t\twhile ( (elem = results[i++]) ) {\n\t\t\t\t\tif ( elem.nodeType === 1 ) {\n\t\t\t\t\t\ttmp.push( elem );\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn tmp;\n\t\t\t}\n\t\t\treturn results;\n\t\t};\n\n\t// Class\n\tExpr.find[\"CLASS\"] = support.getElementsByClassName && function( className, context ) {\n\t\tif ( typeof context.getElementsByClassName !== \"undefined\" && documentIsHTML ) {\n\t\t\treturn context.getElementsByClassName( className );\n\t\t}\n\t};\n\n\t/* QSA/matchesSelector\n\t---------------------------------------------------------------------- */\n\n\t// QSA and matchesSelector support\n\n\t// matchesSelector(:active) reports false when true (IE9/Opera 11.5)\n\trbuggyMatches = [];\n\n\t// qSa(:focus) reports false when true (Chrome 21)\n\t// We allow this because of a bug in IE8/9 that throws an error\n\t// whenever `document.activeElement` is accessed on an iframe\n\t// So, we allow :focus to pass through QSA all the time to avoid the IE error\n\t// See https://bugs.jquery.com/ticket/13378\n\trbuggyQSA = [];\n\n\tif ( (support.qsa = rnative.test( document.querySelectorAll )) ) {\n\t\t// Build QSA regex\n\t\t// Regex strategy adopted from Diego Perini\n\t\tassert(function( el ) {\n\t\t\t// Select is set to empty string on purpose\n\t\t\t// This is to test IE's treatment of not explicitly\n\t\t\t// setting a boolean content attribute,\n\t\t\t// since its presence should be enough\n\t\t\t// https://bugs.jquery.com/ticket/12359\n\t\t\tdocElem.appendChild( el ).innerHTML = \"\" +\n\t\t\t\t\"\";\n\n\t\t\t// Support: IE8, Opera 11-12.16\n\t\t\t// Nothing should be selected when empty strings follow ^= or $= or *=\n\t\t\t// The test attribute must be unknown in Opera but \"safe\" for WinRT\n\t\t\t// https://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section\n\t\t\tif ( el.querySelectorAll(\"[msallowcapture^='']\").length ) {\n\t\t\t\trbuggyQSA.push( \"[*^$]=\" + whitespace + \"*(?:''|\\\"\\\")\" );\n\t\t\t}\n\n\t\t\t// Support: IE8\n\t\t\t// Boolean attributes and \"value\" are not treated correctly\n\t\t\tif ( !el.querySelectorAll(\"[selected]\").length ) {\n\t\t\t\trbuggyQSA.push( \"\\\\[\" + whitespace + \"*(?:value|\" + booleans + \")\" );\n\t\t\t}\n\n\t\t\t// Support: Chrome<29, Android<4.4, Safari<7.0+, iOS<7.0+, PhantomJS<1.9.8+\n\t\t\tif ( !el.querySelectorAll( \"[id~=\" + expando + \"-]\" ).length ) {\n\t\t\t\trbuggyQSA.push(\"~=\");\n\t\t\t}\n\n\t\t\t// Webkit/Opera - :checked should return selected option elements\n\t\t\t// http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked\n\t\t\t// IE8 throws error here and will not see later tests\n\t\t\tif ( !el.querySelectorAll(\":checked\").length ) {\n\t\t\t\trbuggyQSA.push(\":checked\");\n\t\t\t}\n\n\t\t\t// Support: Safari 8+, iOS 8+\n\t\t\t// https://bugs.webkit.org/show_bug.cgi?id=136851\n\t\t\t// In-page `selector#id sibling-combinator selector` fails\n\t\t\tif ( !el.querySelectorAll( \"a#\" + expando + \"+*\" ).length ) {\n\t\t\t\trbuggyQSA.push(\".#.+[+~]\");\n\t\t\t}\n\t\t});\n\n\t\tassert(function( el ) {\n\t\t\tel.innerHTML = \"\" +\n\t\t\t\t\"\";\n\n\t\t\t// Support: Windows 8 Native Apps\n\t\t\t// The type and name attributes are restricted during .innerHTML assignment\n\t\t\tvar input = document.createElement(\"input\");\n\t\t\tinput.setAttribute( \"type\", \"hidden\" );\n\t\t\tel.appendChild( input ).setAttribute( \"name\", \"D\" );\n\n\t\t\t// Support: IE8\n\t\t\t// Enforce case-sensitivity of name attribute\n\t\t\tif ( el.querySelectorAll(\"[name=d]\").length ) {\n\t\t\t\trbuggyQSA.push( \"name\" + whitespace + \"*[*^$|!~]?=\" );\n\t\t\t}\n\n\t\t\t// FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled)\n\t\t\t// IE8 throws error here and will not see later tests\n\t\t\tif ( el.querySelectorAll(\":enabled\").length !== 2 ) {\n\t\t\t\trbuggyQSA.push( \":enabled\", \":disabled\" );\n\t\t\t}\n\n\t\t\t// Support: IE9-11+\n\t\t\t// IE's :disabled selector does not pick up the children of disabled fieldsets\n\t\t\tdocElem.appendChild( el ).disabled = true;\n\t\t\tif ( el.querySelectorAll(\":disabled\").length !== 2 ) {\n\t\t\t\trbuggyQSA.push( \":enabled\", \":disabled\" );\n\t\t\t}\n\n\t\t\t// Opera 10-11 does not throw on post-comma invalid pseudos\n\t\t\tel.querySelectorAll(\"*,:x\");\n\t\t\trbuggyQSA.push(\",.*:\");\n\t\t});\n\t}\n\n\tif ( (support.matchesSelector = rnative.test( (matches = docElem.matches ||\n\t\tdocElem.webkitMatchesSelector ||\n\t\tdocElem.mozMatchesSelector ||\n\t\tdocElem.oMatchesSelector ||\n\t\tdocElem.msMatchesSelector) )) ) {\n\n\t\tassert(function( el ) {\n\t\t\t// Check to see if it's possible to do matchesSelector\n\t\t\t// on a disconnected node (IE 9)\n\t\t\tsupport.disconnectedMatch = matches.call( el, \"*\" );\n\n\t\t\t// This should fail with an exception\n\t\t\t// Gecko does not error, returns false instead\n\t\t\tmatches.call( el, \"[s!='']:x\" );\n\t\t\trbuggyMatches.push( \"!=\", pseudos );\n\t\t});\n\t}\n\n\trbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join(\"|\") );\n\trbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join(\"|\") );\n\n\t/* Contains\n\t---------------------------------------------------------------------- */\n\thasCompare = rnative.test( docElem.compareDocumentPosition );\n\n\t// Element contains another\n\t// Purposefully self-exclusive\n\t// As in, an element does not contain itself\n\tcontains = hasCompare || rnative.test( docElem.contains ) ?\n\t\tfunction( a, b ) {\n\t\t\tvar adown = a.nodeType === 9 ? a.documentElement : a,\n\t\t\t\tbup = b && b.parentNode;\n\t\t\treturn a === bup || !!( bup && bup.nodeType === 1 && (\n\t\t\t\tadown.contains ?\n\t\t\t\t\tadown.contains( bup ) :\n\t\t\t\t\ta.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16\n\t\t\t));\n\t\t} :\n\t\tfunction( a, b ) {\n\t\t\tif ( b ) {\n\t\t\t\twhile ( (b = b.parentNode) ) {\n\t\t\t\t\tif ( b === a ) {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn false;\n\t\t};\n\n\t/* Sorting\n\t---------------------------------------------------------------------- */\n\n\t// Document order sorting\n\tsortOrder = hasCompare ?\n\tfunction( a, b ) {\n\n\t\t// Flag for duplicate removal\n\t\tif ( a === b ) {\n\t\t\thasDuplicate = true;\n\t\t\treturn 0;\n\t\t}\n\n\t\t// Sort on method existence if only one input has compareDocumentPosition\n\t\tvar compare = !a.compareDocumentPosition - !b.compareDocumentPosition;\n\t\tif ( compare ) {\n\t\t\treturn compare;\n\t\t}\n\n\t\t// Calculate position if both inputs belong to the same document\n\t\tcompare = ( a.ownerDocument || a ) === ( b.ownerDocument || b ) ?\n\t\t\ta.compareDocumentPosition( b ) :\n\n\t\t\t// Otherwise we know they are disconnected\n\t\t\t1;\n\n\t\t// Disconnected nodes\n\t\tif ( compare & 1 ||\n\t\t\t(!support.sortDetached && b.compareDocumentPosition( a ) === compare) ) {\n\n\t\t\t// Choose the first element that is related to our preferred document\n\t\t\tif ( a === document || a.ownerDocument === preferredDoc && contains(preferredDoc, a) ) {\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t\tif ( b === document || b.ownerDocument === preferredDoc && contains(preferredDoc, b) ) {\n\t\t\t\treturn 1;\n\t\t\t}\n\n\t\t\t// Maintain original order\n\t\t\treturn sortInput ?\n\t\t\t\t( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) :\n\t\t\t\t0;\n\t\t}\n\n\t\treturn compare & 4 ? -1 : 1;\n\t} :\n\tfunction( a, b ) {\n\t\t// Exit early if the nodes are identical\n\t\tif ( a === b ) {\n\t\t\thasDuplicate = true;\n\t\t\treturn 0;\n\t\t}\n\n\t\tvar cur,\n\t\t\ti = 0,\n\t\t\taup = a.parentNode,\n\t\t\tbup = b.parentNode,\n\t\t\tap = [ a ],\n\t\t\tbp = [ b ];\n\n\t\t// Parentless nodes are either documents or disconnected\n\t\tif ( !aup || !bup ) {\n\t\t\treturn a === document ? -1 :\n\t\t\t\tb === document ? 1 :\n\t\t\t\taup ? -1 :\n\t\t\t\tbup ? 1 :\n\t\t\t\tsortInput ?\n\t\t\t\t( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) :\n\t\t\t\t0;\n\n\t\t// If the nodes are siblings, we can do a quick check\n\t\t} else if ( aup === bup ) {\n\t\t\treturn siblingCheck( a, b );\n\t\t}\n\n\t\t// Otherwise we need full lists of their ancestors for comparison\n\t\tcur = a;\n\t\twhile ( (cur = cur.parentNode) ) {\n\t\t\tap.unshift( cur );\n\t\t}\n\t\tcur = b;\n\t\twhile ( (cur = cur.parentNode) ) {\n\t\t\tbp.unshift( cur );\n\t\t}\n\n\t\t// Walk down the tree looking for a discrepancy\n\t\twhile ( ap[i] === bp[i] ) {\n\t\t\ti++;\n\t\t}\n\n\t\treturn i ?\n\t\t\t// Do a sibling check if the nodes have a common ancestor\n\t\t\tsiblingCheck( ap[i], bp[i] ) :\n\n\t\t\t// Otherwise nodes in our document sort first\n\t\t\tap[i] === preferredDoc ? -1 :\n\t\t\tbp[i] === preferredDoc ? 1 :\n\t\t\t0;\n\t};\n\n\treturn document;\n};\n\nSizzle.matches = function( expr, elements ) {\n\treturn Sizzle( expr, null, null, elements );\n};\n\nSizzle.matchesSelector = function( elem, expr ) {\n\t// Set document vars if needed\n\tif ( ( elem.ownerDocument || elem ) !== document ) {\n\t\tsetDocument( elem );\n\t}\n\n\t// Make sure that attribute selectors are quoted\n\texpr = expr.replace( rattributeQuotes, \"='$1']\" );\n\n\tif ( support.matchesSelector && documentIsHTML &&\n\t\t!compilerCache[ expr + \" \" ] &&\n\t\t( !rbuggyMatches || !rbuggyMatches.test( expr ) ) &&\n\t\t( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) {\n\n\t\ttry {\n\t\t\tvar ret = matches.call( elem, expr );\n\n\t\t\t// IE 9's matchesSelector returns false on disconnected nodes\n\t\t\tif ( ret || support.disconnectedMatch ||\n\t\t\t\t\t// As well, disconnected nodes are said to be in a document\n\t\t\t\t\t// fragment in IE 9\n\t\t\t\t\telem.document && elem.document.nodeType !== 11 ) {\n\t\t\t\treturn ret;\n\t\t\t}\n\t\t} catch (e) {}\n\t}\n\n\treturn Sizzle( expr, document, null, [ elem ] ).length > 0;\n};\n\nSizzle.contains = function( context, elem ) {\n\t// Set document vars if needed\n\tif ( ( context.ownerDocument || context ) !== document ) {\n\t\tsetDocument( context );\n\t}\n\treturn contains( context, elem );\n};\n\nSizzle.attr = function( elem, name ) {\n\t// Set document vars if needed\n\tif ( ( elem.ownerDocument || elem ) !== document ) {\n\t\tsetDocument( elem );\n\t}\n\n\tvar fn = Expr.attrHandle[ name.toLowerCase() ],\n\t\t// Don't get fooled by Object.prototype properties (jQuery #13807)\n\t\tval = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ?\n\t\t\tfn( elem, name, !documentIsHTML ) :\n\t\t\tundefined;\n\n\treturn val !== undefined ?\n\t\tval :\n\t\tsupport.attributes || !documentIsHTML ?\n\t\t\telem.getAttribute( name ) :\n\t\t\t(val = elem.getAttributeNode(name)) && val.specified ?\n\t\t\t\tval.value :\n\t\t\t\tnull;\n};\n\nSizzle.escape = function( sel ) {\n\treturn (sel + \"\").replace( rcssescape, fcssescape );\n};\n\nSizzle.error = function( msg ) {\n\tthrow new Error( \"Syntax error, unrecognized expression: \" + msg );\n};\n\n/**\n * Document sorting and removing duplicates\n * @param {ArrayLike} results\n */\nSizzle.uniqueSort = function( results ) {\n\tvar elem,\n\t\tduplicates = [],\n\t\tj = 0,\n\t\ti = 0;\n\n\t// Unless we *know* we can detect duplicates, assume their presence\n\thasDuplicate = !support.detectDuplicates;\n\tsortInput = !support.sortStable && results.slice( 0 );\n\tresults.sort( sortOrder );\n\n\tif ( hasDuplicate ) {\n\t\twhile ( (elem = results[i++]) ) {\n\t\t\tif ( elem === results[ i ] ) {\n\t\t\t\tj = duplicates.push( i );\n\t\t\t}\n\t\t}\n\t\twhile ( j-- ) {\n\t\t\tresults.splice( duplicates[ j ], 1 );\n\t\t}\n\t}\n\n\t// Clear input after sorting to release objects\n\t// See https://github.com/jquery/sizzle/pull/225\n\tsortInput = null;\n\n\treturn results;\n};\n\n/**\n * Utility function for retrieving the text value of an array of DOM nodes\n * @param {Array|Element} elem\n */\ngetText = Sizzle.getText = function( elem ) {\n\tvar node,\n\t\tret = \"\",\n\t\ti = 0,\n\t\tnodeType = elem.nodeType;\n\n\tif ( !nodeType ) {\n\t\t// If no nodeType, this is expected to be an array\n\t\twhile ( (node = elem[i++]) ) {\n\t\t\t// Do not traverse comment nodes\n\t\t\tret += getText( node );\n\t\t}\n\t} else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) {\n\t\t// Use textContent for elements\n\t\t// innerText usage removed for consistency of new lines (jQuery #11153)\n\t\tif ( typeof elem.textContent === \"string\" ) {\n\t\t\treturn elem.textContent;\n\t\t} else {\n\t\t\t// Traverse its children\n\t\t\tfor ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {\n\t\t\t\tret += getText( elem );\n\t\t\t}\n\t\t}\n\t} else if ( nodeType === 3 || nodeType === 4 ) {\n\t\treturn elem.nodeValue;\n\t}\n\t// Do not include comment or processing instruction nodes\n\n\treturn ret;\n};\n\nExpr = Sizzle.selectors = {\n\n\t// Can be adjusted by the user\n\tcacheLength: 50,\n\n\tcreatePseudo: markFunction,\n\n\tmatch: matchExpr,\n\n\tattrHandle: {},\n\n\tfind: {},\n\n\trelative: {\n\t\t\">\": { dir: \"parentNode\", first: true },\n\t\t\" \": { dir: \"parentNode\" },\n\t\t\"+\": { dir: \"previousSibling\", first: true },\n\t\t\"~\": { dir: \"previousSibling\" }\n\t},\n\n\tpreFilter: {\n\t\t\"ATTR\": function( match ) {\n\t\t\tmatch[1] = match[1].replace( runescape, funescape );\n\n\t\t\t// Move the given value to match[3] whether quoted or unquoted\n\t\t\tmatch[3] = ( match[3] || match[4] || match[5] || \"\" ).replace( runescape, funescape );\n\n\t\t\tif ( match[2] === \"~=\" ) {\n\t\t\t\tmatch[3] = \" \" + match[3] + \" \";\n\t\t\t}\n\n\t\t\treturn match.slice( 0, 4 );\n\t\t},\n\n\t\t\"CHILD\": function( match ) {\n\t\t\t/* matches from matchExpr[\"CHILD\"]\n\t\t\t\t1 type (only|nth|...)\n\t\t\t\t2 what (child|of-type)\n\t\t\t\t3 argument (even|odd|\\d*|\\d*n([+-]\\d+)?|...)\n\t\t\t\t4 xn-component of xn+y argument ([+-]?\\d*n|)\n\t\t\t\t5 sign of xn-component\n\t\t\t\t6 x of xn-component\n\t\t\t\t7 sign of y-component\n\t\t\t\t8 y of y-component\n\t\t\t*/\n\t\t\tmatch[1] = match[1].toLowerCase();\n\n\t\t\tif ( match[1].slice( 0, 3 ) === \"nth\" ) {\n\t\t\t\t// nth-* requires argument\n\t\t\t\tif ( !match[3] ) {\n\t\t\t\t\tSizzle.error( match[0] );\n\t\t\t\t}\n\n\t\t\t\t// numeric x and y parameters for Expr.filter.CHILD\n\t\t\t\t// remember that false/true cast respectively to 0/1\n\t\t\t\tmatch[4] = +( match[4] ? match[5] + (match[6] || 1) : 2 * ( match[3] === \"even\" || match[3] === \"odd\" ) );\n\t\t\t\tmatch[5] = +( ( match[7] + match[8] ) || match[3] === \"odd\" );\n\n\t\t\t// other types prohibit arguments\n\t\t\t} else if ( match[3] ) {\n\t\t\t\tSizzle.error( match[0] );\n\t\t\t}\n\n\t\t\treturn match;\n\t\t},\n\n\t\t\"PSEUDO\": function( match ) {\n\t\t\tvar excess,\n\t\t\t\tunquoted = !match[6] && match[2];\n\n\t\t\tif ( matchExpr[\"CHILD\"].test( match[0] ) ) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\t// Accept quoted arguments as-is\n\t\t\tif ( match[3] ) {\n\t\t\t\tmatch[2] = match[4] || match[5] || \"\";\n\n\t\t\t// Strip excess characters from unquoted arguments\n\t\t\t} else if ( unquoted && rpseudo.test( unquoted ) &&\n\t\t\t\t// Get excess from tokenize (recursively)\n\t\t\t\t(excess = tokenize( unquoted, true )) &&\n\t\t\t\t// advance to the next closing parenthesis\n\t\t\t\t(excess = unquoted.indexOf( \")\", unquoted.length - excess ) - unquoted.length) ) {\n\n\t\t\t\t// excess is a negative index\n\t\t\t\tmatch[0] = match[0].slice( 0, excess );\n\t\t\t\tmatch[2] = unquoted.slice( 0, excess );\n\t\t\t}\n\n\t\t\t// Return only captures needed by the pseudo filter method (type and argument)\n\t\t\treturn match.slice( 0, 3 );\n\t\t}\n\t},\n\n\tfilter: {\n\n\t\t\"TAG\": function( nodeNameSelector ) {\n\t\t\tvar nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase();\n\t\t\treturn nodeNameSelector === \"*\" ?\n\t\t\t\tfunction() { return true; } :\n\t\t\t\tfunction( elem ) {\n\t\t\t\t\treturn elem.nodeName && elem.nodeName.toLowerCase() === nodeName;\n\t\t\t\t};\n\t\t},\n\n\t\t\"CLASS\": function( className ) {\n\t\t\tvar pattern = classCache[ className + \" \" ];\n\n\t\t\treturn pattern ||\n\t\t\t\t(pattern = new RegExp( \"(^|\" + whitespace + \")\" + className + \"(\" + whitespace + \"|$)\" )) &&\n\t\t\t\tclassCache( className, function( elem ) {\n\t\t\t\t\treturn pattern.test( typeof elem.className === \"string\" && elem.className || typeof elem.getAttribute !== \"undefined\" && elem.getAttribute(\"class\") || \"\" );\n\t\t\t\t});\n\t\t},\n\n\t\t\"ATTR\": function( name, operator, check ) {\n\t\t\treturn function( elem ) {\n\t\t\t\tvar result = Sizzle.attr( elem, name );\n\n\t\t\t\tif ( result == null ) {\n\t\t\t\t\treturn operator === \"!=\";\n\t\t\t\t}\n\t\t\t\tif ( !operator ) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\n\t\t\t\tresult += \"\";\n\n\t\t\t\treturn operator === \"=\" ? result === check :\n\t\t\t\t\toperator === \"!=\" ? result !== check :\n\t\t\t\t\toperator === \"^=\" ? check && result.indexOf( check ) === 0 :\n\t\t\t\t\toperator === \"*=\" ? check && result.indexOf( check ) > -1 :\n\t\t\t\t\toperator === \"$=\" ? check && result.slice( -check.length ) === check :\n\t\t\t\t\toperator === \"~=\" ? ( \" \" + result.replace( rwhitespace, \" \" ) + \" \" ).indexOf( check ) > -1 :\n\t\t\t\t\toperator === \"|=\" ? result === check || result.slice( 0, check.length + 1 ) === check + \"-\" :\n\t\t\t\t\tfalse;\n\t\t\t};\n\t\t},\n\n\t\t\"CHILD\": function( type, what, argument, first, last ) {\n\t\t\tvar simple = type.slice( 0, 3 ) !== \"nth\",\n\t\t\t\tforward = type.slice( -4 ) !== \"last\",\n\t\t\t\tofType = what === \"of-type\";\n\n\t\t\treturn first === 1 && last === 0 ?\n\n\t\t\t\t// Shortcut for :nth-*(n)\n\t\t\t\tfunction( elem ) {\n\t\t\t\t\treturn !!elem.parentNode;\n\t\t\t\t} :\n\n\t\t\t\tfunction( elem, context, xml ) {\n\t\t\t\t\tvar cache, uniqueCache, outerCache, node, nodeIndex, start,\n\t\t\t\t\t\tdir = simple !== forward ? \"nextSibling\" : \"previousSibling\",\n\t\t\t\t\t\tparent = elem.parentNode,\n\t\t\t\t\t\tname = ofType && elem.nodeName.toLowerCase(),\n\t\t\t\t\t\tuseCache = !xml && !ofType,\n\t\t\t\t\t\tdiff = false;\n\n\t\t\t\t\tif ( parent ) {\n\n\t\t\t\t\t\t// :(first|last|only)-(child|of-type)\n\t\t\t\t\t\tif ( simple ) {\n\t\t\t\t\t\t\twhile ( dir ) {\n\t\t\t\t\t\t\t\tnode = elem;\n\t\t\t\t\t\t\t\twhile ( (node = node[ dir ]) ) {\n\t\t\t\t\t\t\t\t\tif ( ofType ?\n\t\t\t\t\t\t\t\t\t\tnode.nodeName.toLowerCase() === name :\n\t\t\t\t\t\t\t\t\t\tnode.nodeType === 1 ) {\n\n\t\t\t\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t// Reverse direction for :only-* (if we haven't yet done so)\n\t\t\t\t\t\t\t\tstart = dir = type === \"only\" && !start && \"nextSibling\";\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tstart = [ forward ? parent.firstChild : parent.lastChild ];\n\n\t\t\t\t\t\t// non-xml :nth-child(...) stores cache data on `parent`\n\t\t\t\t\t\tif ( forward && useCache ) {\n\n\t\t\t\t\t\t\t// Seek `elem` from a previously-cached index\n\n\t\t\t\t\t\t\t// ...in a gzip-friendly way\n\t\t\t\t\t\t\tnode = parent;\n\t\t\t\t\t\t\touterCache = node[ expando ] || (node[ expando ] = {});\n\n\t\t\t\t\t\t\t// Support: IE <9 only\n\t\t\t\t\t\t\t// Defend against cloned attroperties (jQuery gh-1709)\n\t\t\t\t\t\t\tuniqueCache = outerCache[ node.uniqueID ] ||\n\t\t\t\t\t\t\t\t(outerCache[ node.uniqueID ] = {});\n\n\t\t\t\t\t\t\tcache = uniqueCache[ type ] || [];\n\t\t\t\t\t\t\tnodeIndex = cache[ 0 ] === dirruns && cache[ 1 ];\n\t\t\t\t\t\t\tdiff = nodeIndex && cache[ 2 ];\n\t\t\t\t\t\t\tnode = nodeIndex && parent.childNodes[ nodeIndex ];\n\n\t\t\t\t\t\t\twhile ( (node = ++nodeIndex && node && node[ dir ] ||\n\n\t\t\t\t\t\t\t\t// Fallback to seeking `elem` from the start\n\t\t\t\t\t\t\t\t(diff = nodeIndex = 0) || start.pop()) ) {\n\n\t\t\t\t\t\t\t\t// When found, cache indexes on `parent` and break\n\t\t\t\t\t\t\t\tif ( node.nodeType === 1 && ++diff && node === elem ) {\n\t\t\t\t\t\t\t\t\tuniqueCache[ type ] = [ dirruns, nodeIndex, diff ];\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t// Use previously-cached element index if available\n\t\t\t\t\t\t\tif ( useCache ) {\n\t\t\t\t\t\t\t\t// ...in a gzip-friendly way\n\t\t\t\t\t\t\t\tnode = elem;\n\t\t\t\t\t\t\t\touterCache = node[ expando ] || (node[ expando ] = {});\n\n\t\t\t\t\t\t\t\t// Support: IE <9 only\n\t\t\t\t\t\t\t\t// Defend against cloned attroperties (jQuery gh-1709)\n\t\t\t\t\t\t\t\tuniqueCache = outerCache[ node.uniqueID ] ||\n\t\t\t\t\t\t\t\t\t(outerCache[ node.uniqueID ] = {});\n\n\t\t\t\t\t\t\t\tcache = uniqueCache[ type ] || [];\n\t\t\t\t\t\t\t\tnodeIndex = cache[ 0 ] === dirruns && cache[ 1 ];\n\t\t\t\t\t\t\t\tdiff = nodeIndex;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// xml :nth-child(...)\n\t\t\t\t\t\t\t// or :nth-last-child(...) or :nth(-last)?-of-type(...)\n\t\t\t\t\t\t\tif ( diff === false ) {\n\t\t\t\t\t\t\t\t// Use the same loop as above to seek `elem` from the start\n\t\t\t\t\t\t\t\twhile ( (node = ++nodeIndex && node && node[ dir ] ||\n\t\t\t\t\t\t\t\t\t(diff = nodeIndex = 0) || start.pop()) ) {\n\n\t\t\t\t\t\t\t\t\tif ( ( ofType ?\n\t\t\t\t\t\t\t\t\t\tnode.nodeName.toLowerCase() === name :\n\t\t\t\t\t\t\t\t\t\tnode.nodeType === 1 ) &&\n\t\t\t\t\t\t\t\t\t\t++diff ) {\n\n\t\t\t\t\t\t\t\t\t\t// Cache the index of each encountered element\n\t\t\t\t\t\t\t\t\t\tif ( useCache ) {\n\t\t\t\t\t\t\t\t\t\t\touterCache = node[ expando ] || (node[ expando ] = {});\n\n\t\t\t\t\t\t\t\t\t\t\t// Support: IE <9 only\n\t\t\t\t\t\t\t\t\t\t\t// Defend against cloned attroperties (jQuery gh-1709)\n\t\t\t\t\t\t\t\t\t\t\tuniqueCache = outerCache[ node.uniqueID ] ||\n\t\t\t\t\t\t\t\t\t\t\t\t(outerCache[ node.uniqueID ] = {});\n\n\t\t\t\t\t\t\t\t\t\t\tuniqueCache[ type ] = [ dirruns, diff ];\n\t\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t\tif ( node === elem ) {\n\t\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Incorporate the offset, then check against cycle size\n\t\t\t\t\t\tdiff -= last;\n\t\t\t\t\t\treturn diff === first || ( diff % first === 0 && diff / first >= 0 );\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t},\n\n\t\t\"PSEUDO\": function( pseudo, argument ) {\n\t\t\t// pseudo-class names are case-insensitive\n\t\t\t// http://www.w3.org/TR/selectors/#pseudo-classes\n\t\t\t// Prioritize by case sensitivity in case custom pseudos are added with uppercase letters\n\t\t\t// Remember that setFilters inherits from pseudos\n\t\t\tvar args,\n\t\t\t\tfn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] ||\n\t\t\t\t\tSizzle.error( \"unsupported pseudo: \" + pseudo );\n\n\t\t\t// The user may use createPseudo to indicate that\n\t\t\t// arguments are needed to create the filter function\n\t\t\t// just as Sizzle does\n\t\t\tif ( fn[ expando ] ) {\n\t\t\t\treturn fn( argument );\n\t\t\t}\n\n\t\t\t// But maintain support for old signatures\n\t\t\tif ( fn.length > 1 ) {\n\t\t\t\targs = [ pseudo, pseudo, \"\", argument ];\n\t\t\t\treturn Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ?\n\t\t\t\t\tmarkFunction(function( seed, matches ) {\n\t\t\t\t\t\tvar idx,\n\t\t\t\t\t\t\tmatched = fn( seed, argument ),\n\t\t\t\t\t\t\ti = matched.length;\n\t\t\t\t\t\twhile ( i-- ) {\n\t\t\t\t\t\t\tidx = indexOf( seed, matched[i] );\n\t\t\t\t\t\t\tseed[ idx ] = !( matches[ idx ] = matched[i] );\n\t\t\t\t\t\t}\n\t\t\t\t\t}) :\n\t\t\t\t\tfunction( elem ) {\n\t\t\t\t\t\treturn fn( elem, 0, args );\n\t\t\t\t\t};\n\t\t\t}\n\n\t\t\treturn fn;\n\t\t}\n\t},\n\n\tpseudos: {\n\t\t// Potentially complex pseudos\n\t\t\"not\": markFunction(function( selector ) {\n\t\t\t// Trim the selector passed to compile\n\t\t\t// to avoid treating leading and trailing\n\t\t\t// spaces as combinators\n\t\t\tvar input = [],\n\t\t\t\tresults = [],\n\t\t\t\tmatcher = compile( selector.replace( rtrim, \"$1\" ) );\n\n\t\t\treturn matcher[ expando ] ?\n\t\t\t\tmarkFunction(function( seed, matches, context, xml ) {\n\t\t\t\t\tvar elem,\n\t\t\t\t\t\tunmatched = matcher( seed, null, xml, [] ),\n\t\t\t\t\t\ti = seed.length;\n\n\t\t\t\t\t// Match elements unmatched by `matcher`\n\t\t\t\t\twhile ( i-- ) {\n\t\t\t\t\t\tif ( (elem = unmatched[i]) ) {\n\t\t\t\t\t\t\tseed[i] = !(matches[i] = elem);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}) :\n\t\t\t\tfunction( elem, context, xml ) {\n\t\t\t\t\tinput[0] = elem;\n\t\t\t\t\tmatcher( input, null, xml, results );\n\t\t\t\t\t// Don't keep the element (issue #299)\n\t\t\t\t\tinput[0] = null;\n\t\t\t\t\treturn !results.pop();\n\t\t\t\t};\n\t\t}),\n\n\t\t\"has\": markFunction(function( selector ) {\n\t\t\treturn function( elem ) {\n\t\t\t\treturn Sizzle( selector, elem ).length > 0;\n\t\t\t};\n\t\t}),\n\n\t\t\"contains\": markFunction(function( text ) {\n\t\t\ttext = text.replace( runescape, funescape );\n\t\t\treturn function( elem ) {\n\t\t\t\treturn ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1;\n\t\t\t};\n\t\t}),\n\n\t\t// \"Whether an element is represented by a :lang() selector\n\t\t// is based solely on the element's language value\n\t\t// being equal to the identifier C,\n\t\t// or beginning with the identifier C immediately followed by \"-\".\n\t\t// The matching of C against the element's language value is performed case-insensitively.\n\t\t// The identifier C does not have to be a valid language name.\"\n\t\t// http://www.w3.org/TR/selectors/#lang-pseudo\n\t\t\"lang\": markFunction( function( lang ) {\n\t\t\t// lang value must be a valid identifier\n\t\t\tif ( !ridentifier.test(lang || \"\") ) {\n\t\t\t\tSizzle.error( \"unsupported lang: \" + lang );\n\t\t\t}\n\t\t\tlang = lang.replace( runescape, funescape ).toLowerCase();\n\t\t\treturn function( elem ) {\n\t\t\t\tvar elemLang;\n\t\t\t\tdo {\n\t\t\t\t\tif ( (elemLang = documentIsHTML ?\n\t\t\t\t\t\telem.lang :\n\t\t\t\t\t\telem.getAttribute(\"xml:lang\") || elem.getAttribute(\"lang\")) ) {\n\n\t\t\t\t\t\telemLang = elemLang.toLowerCase();\n\t\t\t\t\t\treturn elemLang === lang || elemLang.indexOf( lang + \"-\" ) === 0;\n\t\t\t\t\t}\n\t\t\t\t} while ( (elem = elem.parentNode) && elem.nodeType === 1 );\n\t\t\t\treturn false;\n\t\t\t};\n\t\t}),\n\n\t\t// Miscellaneous\n\t\t\"target\": function( elem ) {\n\t\t\tvar hash = window.location && window.location.hash;\n\t\t\treturn hash && hash.slice( 1 ) === elem.id;\n\t\t},\n\n\t\t\"root\": function( elem ) {\n\t\t\treturn elem === docElem;\n\t\t},\n\n\t\t\"focus\": function( elem ) {\n\t\t\treturn elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex);\n\t\t},\n\n\t\t// Boolean properties\n\t\t\"enabled\": createDisabledPseudo( false ),\n\t\t\"disabled\": createDisabledPseudo( true ),\n\n\t\t\"checked\": function( elem ) {\n\t\t\t// In CSS3, :checked should return both checked and selected elements\n\t\t\t// http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked\n\t\t\tvar nodeName = elem.nodeName.toLowerCase();\n\t\t\treturn (nodeName === \"input\" && !!elem.checked) || (nodeName === \"option\" && !!elem.selected);\n\t\t},\n\n\t\t\"selected\": function( elem ) {\n\t\t\t// Accessing this property makes selected-by-default\n\t\t\t// options in Safari work properly\n\t\t\tif ( elem.parentNode ) {\n\t\t\t\telem.parentNode.selectedIndex;\n\t\t\t}\n\n\t\t\treturn elem.selected === true;\n\t\t},\n\n\t\t// Contents\n\t\t\"empty\": function( elem ) {\n\t\t\t// http://www.w3.org/TR/selectors/#empty-pseudo\n\t\t\t// :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5),\n\t\t\t// but not by others (comment: 8; processing instruction: 7; etc.)\n\t\t\t// nodeType < 6 works because attributes (2) do not appear as children\n\t\t\tfor ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {\n\t\t\t\tif ( elem.nodeType < 6 ) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true;\n\t\t},\n\n\t\t\"parent\": function( elem ) {\n\t\t\treturn !Expr.pseudos[\"empty\"]( elem );\n\t\t},\n\n\t\t// Element/input types\n\t\t\"header\": function( elem ) {\n\t\t\treturn rheader.test( elem.nodeName );\n\t\t},\n\n\t\t\"input\": function( elem ) {\n\t\t\treturn rinputs.test( elem.nodeName );\n\t\t},\n\n\t\t\"button\": function( elem ) {\n\t\t\tvar name = elem.nodeName.toLowerCase();\n\t\t\treturn name === \"input\" && elem.type === \"button\" || name === \"button\";\n\t\t},\n\n\t\t\"text\": function( elem ) {\n\t\t\tvar attr;\n\t\t\treturn elem.nodeName.toLowerCase() === \"input\" &&\n\t\t\t\telem.type === \"text\" &&\n\n\t\t\t\t// Support: IE<8\n\t\t\t\t// New HTML5 attribute values (e.g., \"search\") appear with elem.type === \"text\"\n\t\t\t\t( (attr = elem.getAttribute(\"type\")) == null || attr.toLowerCase() === \"text\" );\n\t\t},\n\n\t\t// Position-in-collection\n\t\t\"first\": createPositionalPseudo(function() {\n\t\t\treturn [ 0 ];\n\t\t}),\n\n\t\t\"last\": createPositionalPseudo(function( matchIndexes, length ) {\n\t\t\treturn [ length - 1 ];\n\t\t}),\n\n\t\t\"eq\": createPositionalPseudo(function( matchIndexes, length, argument ) {\n\t\t\treturn [ argument < 0 ? argument + length : argument ];\n\t\t}),\n\n\t\t\"even\": createPositionalPseudo(function( matchIndexes, length ) {\n\t\t\tvar i = 0;\n\t\t\tfor ( ; i < length; i += 2 ) {\n\t\t\t\tmatchIndexes.push( i );\n\t\t\t}\n\t\t\treturn matchIndexes;\n\t\t}),\n\n\t\t\"odd\": createPositionalPseudo(function( matchIndexes, length ) {\n\t\t\tvar i = 1;\n\t\t\tfor ( ; i < length; i += 2 ) {\n\t\t\t\tmatchIndexes.push( i );\n\t\t\t}\n\t\t\treturn matchIndexes;\n\t\t}),\n\n\t\t\"lt\": createPositionalPseudo(function( matchIndexes, length, argument ) {\n\t\t\tvar i = argument < 0 ? argument + length : argument;\n\t\t\tfor ( ; --i >= 0; ) {\n\t\t\t\tmatchIndexes.push( i );\n\t\t\t}\n\t\t\treturn matchIndexes;\n\t\t}),\n\n\t\t\"gt\": createPositionalPseudo(function( matchIndexes, length, argument ) {\n\t\t\tvar i = argument < 0 ? argument + length : argument;\n\t\t\tfor ( ; ++i < length; ) {\n\t\t\t\tmatchIndexes.push( i );\n\t\t\t}\n\t\t\treturn matchIndexes;\n\t\t})\n\t}\n};\n\nExpr.pseudos[\"nth\"] = Expr.pseudos[\"eq\"];\n\n// Add button/input type pseudos\nfor ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) {\n\tExpr.pseudos[ i ] = createInputPseudo( i );\n}\nfor ( i in { submit: true, reset: true } ) {\n\tExpr.pseudos[ i ] = createButtonPseudo( i );\n}\n\n// Easy API for creating new setFilters\nfunction setFilters() {}\nsetFilters.prototype = Expr.filters = Expr.pseudos;\nExpr.setFilters = new setFilters();\n\ntokenize = Sizzle.tokenize = function( selector, parseOnly ) {\n\tvar matched, match, tokens, type,\n\t\tsoFar, groups, preFilters,\n\t\tcached = tokenCache[ selector + \" \" ];\n\n\tif ( cached ) {\n\t\treturn parseOnly ? 0 : cached.slice( 0 );\n\t}\n\n\tsoFar = selector;\n\tgroups = [];\n\tpreFilters = Expr.preFilter;\n\n\twhile ( soFar ) {\n\n\t\t// Comma and first run\n\t\tif ( !matched || (match = rcomma.exec( soFar )) ) {\n\t\t\tif ( match ) {\n\t\t\t\t// Don't consume trailing commas as valid\n\t\t\t\tsoFar = soFar.slice( match[0].length ) || soFar;\n\t\t\t}\n\t\t\tgroups.push( (tokens = []) );\n\t\t}\n\n\t\tmatched = false;\n\n\t\t// Combinators\n\t\tif ( (match = rcombinators.exec( soFar )) ) {\n\t\t\tmatched = match.shift();\n\t\t\ttokens.push({\n\t\t\t\tvalue: matched,\n\t\t\t\t// Cast descendant combinators to space\n\t\t\t\ttype: match[0].replace( rtrim, \" \" )\n\t\t\t});\n\t\t\tsoFar = soFar.slice( matched.length );\n\t\t}\n\n\t\t// Filters\n\t\tfor ( type in Expr.filter ) {\n\t\t\tif ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] ||\n\t\t\t\t(match = preFilters[ type ]( match ))) ) {\n\t\t\t\tmatched = match.shift();\n\t\t\t\ttokens.push({\n\t\t\t\t\tvalue: matched,\n\t\t\t\t\ttype: type,\n\t\t\t\t\tmatches: match\n\t\t\t\t});\n\t\t\t\tsoFar = soFar.slice( matched.length );\n\t\t\t}\n\t\t}\n\n\t\tif ( !matched ) {\n\t\t\tbreak;\n\t\t}\n\t}\n\n\t// Return the length of the invalid excess\n\t// if we're just parsing\n\t// Otherwise, throw an error or return tokens\n\treturn parseOnly ?\n\t\tsoFar.length :\n\t\tsoFar ?\n\t\t\tSizzle.error( selector ) :\n\t\t\t// Cache the tokens\n\t\t\ttokenCache( selector, groups ).slice( 0 );\n};\n\nfunction toSelector( tokens ) {\n\tvar i = 0,\n\t\tlen = tokens.length,\n\t\tselector = \"\";\n\tfor ( ; i < len; i++ ) {\n\t\tselector += tokens[i].value;\n\t}\n\treturn selector;\n}\n\nfunction addCombinator( matcher, combinator, base ) {\n\tvar dir = combinator.dir,\n\t\tskip = combinator.next,\n\t\tkey = skip || dir,\n\t\tcheckNonElements = base && key === \"parentNode\",\n\t\tdoneName = done++;\n\n\treturn combinator.first ?\n\t\t// Check against closest ancestor/preceding element\n\t\tfunction( elem, context, xml ) {\n\t\t\twhile ( (elem = elem[ dir ]) ) {\n\t\t\t\tif ( elem.nodeType === 1 || checkNonElements ) {\n\t\t\t\t\treturn matcher( elem, context, xml );\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn false;\n\t\t} :\n\n\t\t// Check against all ancestor/preceding elements\n\t\tfunction( elem, context, xml ) {\n\t\t\tvar oldCache, uniqueCache, outerCache,\n\t\t\t\tnewCache = [ dirruns, doneName ];\n\n\t\t\t// We can't set arbitrary data on XML nodes, so they don't benefit from combinator caching\n\t\t\tif ( xml ) {\n\t\t\t\twhile ( (elem = elem[ dir ]) ) {\n\t\t\t\t\tif ( elem.nodeType === 1 || checkNonElements ) {\n\t\t\t\t\t\tif ( matcher( elem, context, xml ) ) {\n\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\twhile ( (elem = elem[ dir ]) ) {\n\t\t\t\t\tif ( elem.nodeType === 1 || checkNonElements ) {\n\t\t\t\t\t\touterCache = elem[ expando ] || (elem[ expando ] = {});\n\n\t\t\t\t\t\t// Support: IE <9 only\n\t\t\t\t\t\t// Defend against cloned attroperties (jQuery gh-1709)\n\t\t\t\t\t\tuniqueCache = outerCache[ elem.uniqueID ] || (outerCache[ elem.uniqueID ] = {});\n\n\t\t\t\t\t\tif ( skip && skip === elem.nodeName.toLowerCase() ) {\n\t\t\t\t\t\t\telem = elem[ dir ] || elem;\n\t\t\t\t\t\t} else if ( (oldCache = uniqueCache[ key ]) &&\n\t\t\t\t\t\t\toldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) {\n\n\t\t\t\t\t\t\t// Assign to newCache so results back-propagate to previous elements\n\t\t\t\t\t\t\treturn (newCache[ 2 ] = oldCache[ 2 ]);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t// Reuse newcache so results back-propagate to previous elements\n\t\t\t\t\t\t\tuniqueCache[ key ] = newCache;\n\n\t\t\t\t\t\t\t// A match means we're done; a fail means we have to keep checking\n\t\t\t\t\t\t\tif ( (newCache[ 2 ] = matcher( elem, context, xml )) ) {\n\t\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn false;\n\t\t};\n}\n\nfunction elementMatcher( matchers ) {\n\treturn matchers.length > 1 ?\n\t\tfunction( elem, context, xml ) {\n\t\t\tvar i = matchers.length;\n\t\t\twhile ( i-- ) {\n\t\t\t\tif ( !matchers[i]( elem, context, xml ) ) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true;\n\t\t} :\n\t\tmatchers[0];\n}\n\nfunction multipleContexts( selector, contexts, results ) {\n\tvar i = 0,\n\t\tlen = contexts.length;\n\tfor ( ; i < len; i++ ) {\n\t\tSizzle( selector, contexts[i], results );\n\t}\n\treturn results;\n}\n\nfunction condense( unmatched, map, filter, context, xml ) {\n\tvar elem,\n\t\tnewUnmatched = [],\n\t\ti = 0,\n\t\tlen = unmatched.length,\n\t\tmapped = map != null;\n\n\tfor ( ; i < len; i++ ) {\n\t\tif ( (elem = unmatched[i]) ) {\n\t\t\tif ( !filter || filter( elem, context, xml ) ) {\n\t\t\t\tnewUnmatched.push( elem );\n\t\t\t\tif ( mapped ) {\n\t\t\t\t\tmap.push( i );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn newUnmatched;\n}\n\nfunction setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) {\n\tif ( postFilter && !postFilter[ expando ] ) {\n\t\tpostFilter = setMatcher( postFilter );\n\t}\n\tif ( postFinder && !postFinder[ expando ] ) {\n\t\tpostFinder = setMatcher( postFinder, postSelector );\n\t}\n\treturn markFunction(function( seed, results, context, xml ) {\n\t\tvar temp, i, elem,\n\t\t\tpreMap = [],\n\t\t\tpostMap = [],\n\t\t\tpreexisting = results.length,\n\n\t\t\t// Get initial elements from seed or context\n\t\t\telems = seed || multipleContexts( selector || \"*\", context.nodeType ? [ context ] : context, [] ),\n\n\t\t\t// Prefilter to get matcher input, preserving a map for seed-results synchronization\n\t\t\tmatcherIn = preFilter && ( seed || !selector ) ?\n\t\t\t\tcondense( elems, preMap, preFilter, context, xml ) :\n\t\t\t\telems,\n\n\t\t\tmatcherOut = matcher ?\n\t\t\t\t// If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results,\n\t\t\t\tpostFinder || ( seed ? preFilter : preexisting || postFilter ) ?\n\n\t\t\t\t\t// ...intermediate processing is necessary\n\t\t\t\t\t[] :\n\n\t\t\t\t\t// ...otherwise use results directly\n\t\t\t\t\tresults :\n\t\t\t\tmatcherIn;\n\n\t\t// Find primary matches\n\t\tif ( matcher ) {\n\t\t\tmatcher( matcherIn, matcherOut, context, xml );\n\t\t}\n\n\t\t// Apply postFilter\n\t\tif ( postFilter ) {\n\t\t\ttemp = condense( matcherOut, postMap );\n\t\t\tpostFilter( temp, [], context, xml );\n\n\t\t\t// Un-match failing elements by moving them back to matcherIn\n\t\t\ti = temp.length;\n\t\t\twhile ( i-- ) {\n\t\t\t\tif ( (elem = temp[i]) ) {\n\t\t\t\t\tmatcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif ( seed ) {\n\t\t\tif ( postFinder || preFilter ) {\n\t\t\t\tif ( postFinder ) {\n\t\t\t\t\t// Get the final matcherOut by condensing this intermediate into postFinder contexts\n\t\t\t\t\ttemp = [];\n\t\t\t\t\ti = matcherOut.length;\n\t\t\t\t\twhile ( i-- ) {\n\t\t\t\t\t\tif ( (elem = matcherOut[i]) ) {\n\t\t\t\t\t\t\t// Restore matcherIn since elem is not yet a final match\n\t\t\t\t\t\t\ttemp.push( (matcherIn[i] = elem) );\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tpostFinder( null, (matcherOut = []), temp, xml );\n\t\t\t\t}\n\n\t\t\t\t// Move matched elements from seed to results to keep them synchronized\n\t\t\t\ti = matcherOut.length;\n\t\t\t\twhile ( i-- ) {\n\t\t\t\t\tif ( (elem = matcherOut[i]) &&\n\t\t\t\t\t\t(temp = postFinder ? indexOf( seed, elem ) : preMap[i]) > -1 ) {\n\n\t\t\t\t\t\tseed[temp] = !(results[temp] = elem);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t// Add elements to results, through postFinder if defined\n\t\t} else {\n\t\t\tmatcherOut = condense(\n\t\t\t\tmatcherOut === results ?\n\t\t\t\t\tmatcherOut.splice( preexisting, matcherOut.length ) :\n\t\t\t\t\tmatcherOut\n\t\t\t);\n\t\t\tif ( postFinder ) {\n\t\t\t\tpostFinder( null, results, matcherOut, xml );\n\t\t\t} else {\n\t\t\t\tpush.apply( results, matcherOut );\n\t\t\t}\n\t\t}\n\t});\n}\n\nfunction matcherFromTokens( tokens ) {\n\tvar checkContext, matcher, j,\n\t\tlen = tokens.length,\n\t\tleadingRelative = Expr.relative[ tokens[0].type ],\n\t\timplicitRelative = leadingRelative || Expr.relative[\" \"],\n\t\ti = leadingRelative ? 1 : 0,\n\n\t\t// The foundational matcher ensures that elements are reachable from top-level context(s)\n\t\tmatchContext = addCombinator( function( elem ) {\n\t\t\treturn elem === checkContext;\n\t\t}, implicitRelative, true ),\n\t\tmatchAnyContext = addCombinator( function( elem ) {\n\t\t\treturn indexOf( checkContext, elem ) > -1;\n\t\t}, implicitRelative, true ),\n\t\tmatchers = [ function( elem, context, xml ) {\n\t\t\tvar ret = ( !leadingRelative && ( xml || context !== outermostContext ) ) || (\n\t\t\t\t(checkContext = context).nodeType ?\n\t\t\t\t\tmatchContext( elem, context, xml ) :\n\t\t\t\t\tmatchAnyContext( elem, context, xml ) );\n\t\t\t// Avoid hanging onto element (issue #299)\n\t\t\tcheckContext = null;\n\t\t\treturn ret;\n\t\t} ];\n\n\tfor ( ; i < len; i++ ) {\n\t\tif ( (matcher = Expr.relative[ tokens[i].type ]) ) {\n\t\t\tmatchers = [ addCombinator(elementMatcher( matchers ), matcher) ];\n\t\t} else {\n\t\t\tmatcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches );\n\n\t\t\t// Return special upon seeing a positional matcher\n\t\t\tif ( matcher[ expando ] ) {\n\t\t\t\t// Find the next relative operator (if any) for proper handling\n\t\t\t\tj = ++i;\n\t\t\t\tfor ( ; j < len; j++ ) {\n\t\t\t\t\tif ( Expr.relative[ tokens[j].type ] ) {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn setMatcher(\n\t\t\t\t\ti > 1 && elementMatcher( matchers ),\n\t\t\t\t\ti > 1 && toSelector(\n\t\t\t\t\t\t// If the preceding token was a descendant combinator, insert an implicit any-element `*`\n\t\t\t\t\t\ttokens.slice( 0, i - 1 ).concat({ value: tokens[ i - 2 ].type === \" \" ? \"*\" : \"\" })\n\t\t\t\t\t).replace( rtrim, \"$1\" ),\n\t\t\t\t\tmatcher,\n\t\t\t\t\ti < j && matcherFromTokens( tokens.slice( i, j ) ),\n\t\t\t\t\tj < len && matcherFromTokens( (tokens = tokens.slice( j )) ),\n\t\t\t\t\tj < len && toSelector( tokens )\n\t\t\t\t);\n\t\t\t}\n\t\t\tmatchers.push( matcher );\n\t\t}\n\t}\n\n\treturn elementMatcher( matchers );\n}\n\nfunction matcherFromGroupMatchers( elementMatchers, setMatchers ) {\n\tvar bySet = setMatchers.length > 0,\n\t\tbyElement = elementMatchers.length > 0,\n\t\tsuperMatcher = function( seed, context, xml, results, outermost ) {\n\t\t\tvar elem, j, matcher,\n\t\t\t\tmatchedCount = 0,\n\t\t\t\ti = \"0\",\n\t\t\t\tunmatched = seed && [],\n\t\t\t\tsetMatched = [],\n\t\t\t\tcontextBackup = outermostContext,\n\t\t\t\t// We must always have either seed elements or outermost context\n\t\t\t\telems = seed || byElement && Expr.find[\"TAG\"]( \"*\", outermost ),\n\t\t\t\t// Use integer dirruns iff this is the outermost matcher\n\t\t\t\tdirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1),\n\t\t\t\tlen = elems.length;\n\n\t\t\tif ( outermost ) {\n\t\t\t\toutermostContext = context === document || context || outermost;\n\t\t\t}\n\n\t\t\t// Add elements passing elementMatchers directly to results\n\t\t\t// Support: IE<9, Safari\n\t\t\t// Tolerate NodeList properties (IE: \"length\"; Safari: ) matching elements by id\n\t\t\tfor ( ; i !== len && (elem = elems[i]) != null; i++ ) {\n\t\t\t\tif ( byElement && elem ) {\n\t\t\t\t\tj = 0;\n\t\t\t\t\tif ( !context && elem.ownerDocument !== document ) {\n\t\t\t\t\t\tsetDocument( elem );\n\t\t\t\t\t\txml = !documentIsHTML;\n\t\t\t\t\t}\n\t\t\t\t\twhile ( (matcher = elementMatchers[j++]) ) {\n\t\t\t\t\t\tif ( matcher( elem, context || document, xml) ) {\n\t\t\t\t\t\t\tresults.push( elem );\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif ( outermost ) {\n\t\t\t\t\t\tdirruns = dirrunsUnique;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Track unmatched elements for set filters\n\t\t\t\tif ( bySet ) {\n\t\t\t\t\t// They will have gone through all possible matchers\n\t\t\t\t\tif ( (elem = !matcher && elem) ) {\n\t\t\t\t\t\tmatchedCount--;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Lengthen the array for every element, matched or not\n\t\t\t\t\tif ( seed ) {\n\t\t\t\t\t\tunmatched.push( elem );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// `i` is now the count of elements visited above, and adding it to `matchedCount`\n\t\t\t// makes the latter nonnegative.\n\t\t\tmatchedCount += i;\n\n\t\t\t// Apply set filters to unmatched elements\n\t\t\t// NOTE: This can be skipped if there are no unmatched elements (i.e., `matchedCount`\n\t\t\t// equals `i`), unless we didn't visit _any_ elements in the above loop because we have\n\t\t\t// no element matchers and no seed.\n\t\t\t// Incrementing an initially-string \"0\" `i` allows `i` to remain a string only in that\n\t\t\t// case, which will result in a \"00\" `matchedCount` that differs from `i` but is also\n\t\t\t// numerically zero.\n\t\t\tif ( bySet && i !== matchedCount ) {\n\t\t\t\tj = 0;\n\t\t\t\twhile ( (matcher = setMatchers[j++]) ) {\n\t\t\t\t\tmatcher( unmatched, setMatched, context, xml );\n\t\t\t\t}\n\n\t\t\t\tif ( seed ) {\n\t\t\t\t\t// Reintegrate element matches to eliminate the need for sorting\n\t\t\t\t\tif ( matchedCount > 0 ) {\n\t\t\t\t\t\twhile ( i-- ) {\n\t\t\t\t\t\t\tif ( !(unmatched[i] || setMatched[i]) ) {\n\t\t\t\t\t\t\t\tsetMatched[i] = pop.call( results );\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// Discard index placeholder values to get only actual matches\n\t\t\t\t\tsetMatched = condense( setMatched );\n\t\t\t\t}\n\n\t\t\t\t// Add matches to results\n\t\t\t\tpush.apply( results, setMatched );\n\n\t\t\t\t// Seedless set matches succeeding multiple successful matchers stipulate sorting\n\t\t\t\tif ( outermost && !seed && setMatched.length > 0 &&\n\t\t\t\t\t( matchedCount + setMatchers.length ) > 1 ) {\n\n\t\t\t\t\tSizzle.uniqueSort( results );\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Override manipulation of globals by nested matchers\n\t\t\tif ( outermost ) {\n\t\t\t\tdirruns = dirrunsUnique;\n\t\t\t\toutermostContext = contextBackup;\n\t\t\t}\n\n\t\t\treturn unmatched;\n\t\t};\n\n\treturn bySet ?\n\t\tmarkFunction( superMatcher ) :\n\t\tsuperMatcher;\n}\n\ncompile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) {\n\tvar i,\n\t\tsetMatchers = [],\n\t\telementMatchers = [],\n\t\tcached = compilerCache[ selector + \" \" ];\n\n\tif ( !cached ) {\n\t\t// Generate a function of recursive functions that can be used to check each element\n\t\tif ( !match ) {\n\t\t\tmatch = tokenize( selector );\n\t\t}\n\t\ti = match.length;\n\t\twhile ( i-- ) {\n\t\t\tcached = matcherFromTokens( match[i] );\n\t\t\tif ( cached[ expando ] ) {\n\t\t\t\tsetMatchers.push( cached );\n\t\t\t} else {\n\t\t\t\telementMatchers.push( cached );\n\t\t\t}\n\t\t}\n\n\t\t// Cache the compiled function\n\t\tcached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) );\n\n\t\t// Save selector and tokenization\n\t\tcached.selector = selector;\n\t}\n\treturn cached;\n};\n\n/**\n * A low-level selection function that works with Sizzle's compiled\n * selector functions\n * @param {String|Function} selector A selector or a pre-compiled\n * selector function built with Sizzle.compile\n * @param {Element} context\n * @param {Array} [results]\n * @param {Array} [seed] A set of elements to match against\n */\nselect = Sizzle.select = function( selector, context, results, seed ) {\n\tvar i, tokens, token, type, find,\n\t\tcompiled = typeof selector === \"function\" && selector,\n\t\tmatch = !seed && tokenize( (selector = compiled.selector || selector) );\n\n\tresults = results || [];\n\n\t// Try to minimize operations if there is only one selector in the list and no seed\n\t// (the latter of which guarantees us context)\n\tif ( match.length === 1 ) {\n\n\t\t// Reduce context if the leading compound selector is an ID\n\t\ttokens = match[0] = match[0].slice( 0 );\n\t\tif ( tokens.length > 2 && (token = tokens[0]).type === \"ID\" &&\n\t\t\t\tcontext.nodeType === 9 && documentIsHTML && Expr.relative[ tokens[1].type ] ) {\n\n\t\t\tcontext = ( Expr.find[\"ID\"]( token.matches[0].replace(runescape, funescape), context ) || [] )[0];\n\t\t\tif ( !context ) {\n\t\t\t\treturn results;\n\n\t\t\t// Precompiled matchers will still verify ancestry, so step up a level\n\t\t\t} else if ( compiled ) {\n\t\t\t\tcontext = context.parentNode;\n\t\t\t}\n\n\t\t\tselector = selector.slice( tokens.shift().value.length );\n\t\t}\n\n\t\t// Fetch a seed set for right-to-left matching\n\t\ti = matchExpr[\"needsContext\"].test( selector ) ? 0 : tokens.length;\n\t\twhile ( i-- ) {\n\t\t\ttoken = tokens[i];\n\n\t\t\t// Abort if we hit a combinator\n\t\t\tif ( Expr.relative[ (type = token.type) ] ) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif ( (find = Expr.find[ type ]) ) {\n\t\t\t\t// Search, expanding context for leading sibling combinators\n\t\t\t\tif ( (seed = find(\n\t\t\t\t\ttoken.matches[0].replace( runescape, funescape ),\n\t\t\t\t\trsibling.test( tokens[0].type ) && testContext( context.parentNode ) || context\n\t\t\t\t)) ) {\n\n\t\t\t\t\t// If seed is empty or no tokens remain, we can return early\n\t\t\t\t\ttokens.splice( i, 1 );\n\t\t\t\t\tselector = seed.length && toSelector( tokens );\n\t\t\t\t\tif ( !selector ) {\n\t\t\t\t\t\tpush.apply( results, seed );\n\t\t\t\t\t\treturn results;\n\t\t\t\t\t}\n\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// Compile and execute a filtering function if one is not provided\n\t// Provide `match` to avoid retokenization if we modified the selector above\n\t( compiled || compile( selector, match ) )(\n\t\tseed,\n\t\tcontext,\n\t\t!documentIsHTML,\n\t\tresults,\n\t\t!context || rsibling.test( selector ) && testContext( context.parentNode ) || context\n\t);\n\treturn results;\n};\n\n// One-time assignments\n\n// Sort stability\nsupport.sortStable = expando.split(\"\").sort( sortOrder ).join(\"\") === expando;\n\n// Support: Chrome 14-35+\n// Always assume duplicates if they aren't passed to the comparison function\nsupport.detectDuplicates = !!hasDuplicate;\n\n// Initialize against the default document\nsetDocument();\n\n// Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27)\n// Detached nodes confoundingly follow *each other*\nsupport.sortDetached = assert(function( el ) {\n\t// Should return 1, but returns 4 (following)\n\treturn el.compareDocumentPosition( document.createElement(\"fieldset\") ) & 1;\n});\n\n// Support: IE<8\n// Prevent attribute/property \"interpolation\"\n// https://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx\nif ( !assert(function( el ) {\n\tel.innerHTML = \"\";\n\treturn el.firstChild.getAttribute(\"href\") === \"#\" ;\n}) ) {\n\taddHandle( \"type|href|height|width\", function( elem, name, isXML ) {\n\t\tif ( !isXML ) {\n\t\t\treturn elem.getAttribute( name, name.toLowerCase() === \"type\" ? 1 : 2 );\n\t\t}\n\t});\n}\n\n// Support: IE<9\n// Use defaultValue in place of getAttribute(\"value\")\nif ( !support.attributes || !assert(function( el ) {\n\tel.innerHTML = \"\";\n\tel.firstChild.setAttribute( \"value\", \"\" );\n\treturn el.firstChild.getAttribute( \"value\" ) === \"\";\n}) ) {\n\taddHandle( \"value\", function( elem, name, isXML ) {\n\t\tif ( !isXML && elem.nodeName.toLowerCase() === \"input\" ) {\n\t\t\treturn elem.defaultValue;\n\t\t}\n\t});\n}\n\n// Support: IE<9\n// Use getAttributeNode to fetch booleans when getAttribute lies\nif ( !assert(function( el ) {\n\treturn el.getAttribute(\"disabled\") == null;\n}) ) {\n\taddHandle( booleans, function( elem, name, isXML ) {\n\t\tvar val;\n\t\tif ( !isXML ) {\n\t\t\treturn elem[ name ] === true ? name.toLowerCase() :\n\t\t\t\t\t(val = elem.getAttributeNode( name )) && val.specified ?\n\t\t\t\t\tval.value :\n\t\t\t\tnull;\n\t\t}\n\t});\n}\n\nreturn Sizzle;\n\n})( window );\n\n\n\njQuery.find = Sizzle;\njQuery.expr = Sizzle.selectors;\n\n// Deprecated\njQuery.expr[ \":\" ] = jQuery.expr.pseudos;\njQuery.uniqueSort = jQuery.unique = Sizzle.uniqueSort;\njQuery.text = Sizzle.getText;\njQuery.isXMLDoc = Sizzle.isXML;\njQuery.contains = Sizzle.contains;\njQuery.escapeSelector = Sizzle.escape;\n\n\n\n\nvar dir = function( elem, dir, until ) {\n\tvar matched = [],\n\t\ttruncate = until !== undefined;\n\n\twhile ( ( elem = elem[ dir ] ) && elem.nodeType !== 9 ) {\n\t\tif ( elem.nodeType === 1 ) {\n\t\t\tif ( truncate && jQuery( elem ).is( until ) ) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tmatched.push( elem );\n\t\t}\n\t}\n\treturn matched;\n};\n\n\nvar siblings = function( n, elem ) {\n\tvar matched = [];\n\n\tfor ( ; n; n = n.nextSibling ) {\n\t\tif ( n.nodeType === 1 && n !== elem ) {\n\t\t\tmatched.push( n );\n\t\t}\n\t}\n\n\treturn matched;\n};\n\n\nvar rneedsContext = jQuery.expr.match.needsContext;\n\n\n\nfunction nodeName( elem, name ) {\n\n return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase();\n\n};\nvar rsingleTag = ( /^<([a-z][^\\/\\0>:\\x20\\t\\r\\n\\f]*)[\\x20\\t\\r\\n\\f]*\\/?>(?:<\\/\\1>|)$/i );\n\n\n\n// Implement the identical functionality for filter and not\nfunction winnow( elements, qualifier, not ) {\n\tif ( isFunction( qualifier ) ) {\n\t\treturn jQuery.grep( elements, function( elem, i ) {\n\t\t\treturn !!qualifier.call( elem, i, elem ) !== not;\n\t\t} );\n\t}\n\n\t// Single element\n\tif ( qualifier.nodeType ) {\n\t\treturn jQuery.grep( elements, function( elem ) {\n\t\t\treturn ( elem === qualifier ) !== not;\n\t\t} );\n\t}\n\n\t// Arraylike of elements (jQuery, arguments, Array)\n\tif ( typeof qualifier !== \"string\" ) {\n\t\treturn jQuery.grep( elements, function( elem ) {\n\t\t\treturn ( indexOf.call( qualifier, elem ) > -1 ) !== not;\n\t\t} );\n\t}\n\n\t// Filtered directly for both simple and complex selectors\n\treturn jQuery.filter( qualifier, elements, not );\n}\n\njQuery.filter = function( expr, elems, not ) {\n\tvar elem = elems[ 0 ];\n\n\tif ( not ) {\n\t\texpr = \":not(\" + expr + \")\";\n\t}\n\n\tif ( elems.length === 1 && elem.nodeType === 1 ) {\n\t\treturn jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : [];\n\t}\n\n\treturn jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) {\n\t\treturn elem.nodeType === 1;\n\t} ) );\n};\n\njQuery.fn.extend( {\n\tfind: function( selector ) {\n\t\tvar i, ret,\n\t\t\tlen = this.length,\n\t\t\tself = this;\n\n\t\tif ( typeof selector !== \"string\" ) {\n\t\t\treturn this.pushStack( jQuery( selector ).filter( function() {\n\t\t\t\tfor ( i = 0; i < len; i++ ) {\n\t\t\t\t\tif ( jQuery.contains( self[ i ], this ) ) {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} ) );\n\t\t}\n\n\t\tret = this.pushStack( [] );\n\n\t\tfor ( i = 0; i < len; i++ ) {\n\t\t\tjQuery.find( selector, self[ i ], ret );\n\t\t}\n\n\t\treturn len > 1 ? jQuery.uniqueSort( ret ) : ret;\n\t},\n\tfilter: function( selector ) {\n\t\treturn this.pushStack( winnow( this, selector || [], false ) );\n\t},\n\tnot: function( selector ) {\n\t\treturn this.pushStack( winnow( this, selector || [], true ) );\n\t},\n\tis: function( selector ) {\n\t\treturn !!winnow(\n\t\t\tthis,\n\n\t\t\t// If this is a positional/relative selector, check membership in the returned set\n\t\t\t// so $(\"p:first\").is(\"p:last\") won't return true for a doc with two \"p\".\n\t\t\ttypeof selector === \"string\" && rneedsContext.test( selector ) ?\n\t\t\t\tjQuery( selector ) :\n\t\t\t\tselector || [],\n\t\t\tfalse\n\t\t).length;\n\t}\n} );\n\n\n// Initialize a jQuery object\n\n\n// A central reference to the root jQuery(document)\nvar rootjQuery,\n\n\t// A simple way to check for HTML strings\n\t// Prioritize #id over to avoid XSS via location.hash (#9521)\n\t// Strict HTML recognition (#11290: must start with <)\n\t// Shortcut simple #id case for speed\n\trquickExpr = /^(?:\\s*(<[\\w\\W]+>)[^>]*|#([\\w-]+))$/,\n\n\tinit = jQuery.fn.init = function( selector, context, root ) {\n\t\tvar match, elem;\n\n\t\t// HANDLE: $(\"\"), $(null), $(undefined), $(false)\n\t\tif ( !selector ) {\n\t\t\treturn this;\n\t\t}\n\n\t\t// Method init() accepts an alternate rootjQuery\n\t\t// so migrate can support jQuery.sub (gh-2101)\n\t\troot = root || rootjQuery;\n\n\t\t// Handle HTML strings\n\t\tif ( typeof selector === \"string\" ) {\n\t\t\tif ( selector[ 0 ] === \"<\" &&\n\t\t\t\tselector[ selector.length - 1 ] === \">\" &&\n\t\t\t\tselector.length >= 3 ) {\n\n\t\t\t\t// Assume that strings that start and end with <> are HTML and skip the regex check\n\t\t\t\tmatch = [ null, selector, null ];\n\n\t\t\t} else {\n\t\t\t\tmatch = rquickExpr.exec( selector );\n\t\t\t}\n\n\t\t\t// Match html or make sure no context is specified for #id\n\t\t\tif ( match && ( match[ 1 ] || !context ) ) {\n\n\t\t\t\t// HANDLE: $(html) -> $(array)\n\t\t\t\tif ( match[ 1 ] ) {\n\t\t\t\t\tcontext = context instanceof jQuery ? context[ 0 ] : context;\n\n\t\t\t\t\t// Option to run scripts is true for back-compat\n\t\t\t\t\t// Intentionally let the error be thrown if parseHTML is not present\n\t\t\t\t\tjQuery.merge( this, jQuery.parseHTML(\n\t\t\t\t\t\tmatch[ 1 ],\n\t\t\t\t\t\tcontext && context.nodeType ? context.ownerDocument || context : document,\n\t\t\t\t\t\ttrue\n\t\t\t\t\t) );\n\n\t\t\t\t\t// HANDLE: $(html, props)\n\t\t\t\t\tif ( rsingleTag.test( match[ 1 ] ) && jQuery.isPlainObject( context ) ) {\n\t\t\t\t\t\tfor ( match in context ) {\n\n\t\t\t\t\t\t\t// Properties of context are called as methods if possible\n\t\t\t\t\t\t\tif ( isFunction( this[ match ] ) ) {\n\t\t\t\t\t\t\t\tthis[ match ]( context[ match ] );\n\n\t\t\t\t\t\t\t// ...and otherwise set as attributes\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tthis.attr( match, context[ match ] );\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\treturn this;\n\n\t\t\t\t// HANDLE: $(#id)\n\t\t\t\t} else {\n\t\t\t\t\telem = document.getElementById( match[ 2 ] );\n\n\t\t\t\t\tif ( elem ) {\n\n\t\t\t\t\t\t// Inject the element directly into the jQuery object\n\t\t\t\t\t\tthis[ 0 ] = elem;\n\t\t\t\t\t\tthis.length = 1;\n\t\t\t\t\t}\n\t\t\t\t\treturn this;\n\t\t\t\t}\n\n\t\t\t// HANDLE: $(expr, $(...))\n\t\t\t} else if ( !context || context.jquery ) {\n\t\t\t\treturn ( context || root ).find( selector );\n\n\t\t\t// HANDLE: $(expr, context)\n\t\t\t// (which is just equivalent to: $(context).find(expr)\n\t\t\t} else {\n\t\t\t\treturn this.constructor( context ).find( selector );\n\t\t\t}\n\n\t\t// HANDLE: $(DOMElement)\n\t\t} else if ( selector.nodeType ) {\n\t\t\tthis[ 0 ] = selector;\n\t\t\tthis.length = 1;\n\t\t\treturn this;\n\n\t\t// HANDLE: $(function)\n\t\t// Shortcut for document ready\n\t\t} else if ( isFunction( selector ) ) {\n\t\t\treturn root.ready !== undefined ?\n\t\t\t\troot.ready( selector ) :\n\n\t\t\t\t// Execute immediately if ready is not present\n\t\t\t\tselector( jQuery );\n\t\t}\n\n\t\treturn jQuery.makeArray( selector, this );\n\t};\n\n// Give the init function the jQuery prototype for later instantiation\ninit.prototype = jQuery.fn;\n\n// Initialize central reference\nrootjQuery = jQuery( document );\n\n\nvar rparentsprev = /^(?:parents|prev(?:Until|All))/,\n\n\t// Methods guaranteed to produce a unique set when starting from a unique set\n\tguaranteedUnique = {\n\t\tchildren: true,\n\t\tcontents: true,\n\t\tnext: true,\n\t\tprev: true\n\t};\n\njQuery.fn.extend( {\n\thas: function( target ) {\n\t\tvar targets = jQuery( target, this ),\n\t\t\tl = targets.length;\n\n\t\treturn this.filter( function() {\n\t\t\tvar i = 0;\n\t\t\tfor ( ; i < l; i++ ) {\n\t\t\t\tif ( jQuery.contains( this, targets[ i ] ) ) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t} );\n\t},\n\n\tclosest: function( selectors, context ) {\n\t\tvar cur,\n\t\t\ti = 0,\n\t\t\tl = this.length,\n\t\t\tmatched = [],\n\t\t\ttargets = typeof selectors !== \"string\" && jQuery( selectors );\n\n\t\t// Positional selectors never match, since there's no _selection_ context\n\t\tif ( !rneedsContext.test( selectors ) ) {\n\t\t\tfor ( ; i < l; i++ ) {\n\t\t\t\tfor ( cur = this[ i ]; cur && cur !== context; cur = cur.parentNode ) {\n\n\t\t\t\t\t// Always skip document fragments\n\t\t\t\t\tif ( cur.nodeType < 11 && ( targets ?\n\t\t\t\t\t\ttargets.index( cur ) > -1 :\n\n\t\t\t\t\t\t// Don't pass non-elements to Sizzle\n\t\t\t\t\t\tcur.nodeType === 1 &&\n\t\t\t\t\t\t\tjQuery.find.matchesSelector( cur, selectors ) ) ) {\n\n\t\t\t\t\t\tmatched.push( cur );\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn this.pushStack( matched.length > 1 ? jQuery.uniqueSort( matched ) : matched );\n\t},\n\n\t// Determine the position of an element within the set\n\tindex: function( elem ) {\n\n\t\t// No argument, return index in parent\n\t\tif ( !elem ) {\n\t\t\treturn ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1;\n\t\t}\n\n\t\t// Index in selector\n\t\tif ( typeof elem === \"string\" ) {\n\t\t\treturn indexOf.call( jQuery( elem ), this[ 0 ] );\n\t\t}\n\n\t\t// Locate the position of the desired element\n\t\treturn indexOf.call( this,\n\n\t\t\t// If it receives a jQuery object, the first element is used\n\t\t\telem.jquery ? elem[ 0 ] : elem\n\t\t);\n\t},\n\n\tadd: function( selector, context ) {\n\t\treturn this.pushStack(\n\t\t\tjQuery.uniqueSort(\n\t\t\t\tjQuery.merge( this.get(), jQuery( selector, context ) )\n\t\t\t)\n\t\t);\n\t},\n\n\taddBack: function( selector ) {\n\t\treturn this.add( selector == null ?\n\t\t\tthis.prevObject : this.prevObject.filter( selector )\n\t\t);\n\t}\n} );\n\nfunction sibling( cur, dir ) {\n\twhile ( ( cur = cur[ dir ] ) && cur.nodeType !== 1 ) {}\n\treturn cur;\n}\n\njQuery.each( {\n\tparent: function( elem ) {\n\t\tvar parent = elem.parentNode;\n\t\treturn parent && parent.nodeType !== 11 ? parent : null;\n\t},\n\tparents: function( elem ) {\n\t\treturn dir( elem, \"parentNode\" );\n\t},\n\tparentsUntil: function( elem, i, until ) {\n\t\treturn dir( elem, \"parentNode\", until );\n\t},\n\tnext: function( elem ) {\n\t\treturn sibling( elem, \"nextSibling\" );\n\t},\n\tprev: function( elem ) {\n\t\treturn sibling( elem, \"previousSibling\" );\n\t},\n\tnextAll: function( elem ) {\n\t\treturn dir( elem, \"nextSibling\" );\n\t},\n\tprevAll: function( elem ) {\n\t\treturn dir( elem, \"previousSibling\" );\n\t},\n\tnextUntil: function( elem, i, until ) {\n\t\treturn dir( elem, \"nextSibling\", until );\n\t},\n\tprevUntil: function( elem, i, until ) {\n\t\treturn dir( elem, \"previousSibling\", until );\n\t},\n\tsiblings: function( elem ) {\n\t\treturn siblings( ( elem.parentNode || {} ).firstChild, elem );\n\t},\n\tchildren: function( elem ) {\n\t\treturn siblings( elem.firstChild );\n\t},\n\tcontents: function( elem ) {\n if ( nodeName( elem, \"iframe\" ) ) {\n return elem.contentDocument;\n }\n\n // Support: IE 9 - 11 only, iOS 7 only, Android Browser <=4.3 only\n // Treat the template element as a regular one in browsers that\n // don't support it.\n if ( nodeName( elem, \"template\" ) ) {\n elem = elem.content || elem;\n }\n\n return jQuery.merge( [], elem.childNodes );\n\t}\n}, function( name, fn ) {\n\tjQuery.fn[ name ] = function( until, selector ) {\n\t\tvar matched = jQuery.map( this, fn, until );\n\n\t\tif ( name.slice( -5 ) !== \"Until\" ) {\n\t\t\tselector = until;\n\t\t}\n\n\t\tif ( selector && typeof selector === \"string\" ) {\n\t\t\tmatched = jQuery.filter( selector, matched );\n\t\t}\n\n\t\tif ( this.length > 1 ) {\n\n\t\t\t// Remove duplicates\n\t\t\tif ( !guaranteedUnique[ name ] ) {\n\t\t\t\tjQuery.uniqueSort( matched );\n\t\t\t}\n\n\t\t\t// Reverse order for parents* and prev-derivatives\n\t\t\tif ( rparentsprev.test( name ) ) {\n\t\t\t\tmatched.reverse();\n\t\t\t}\n\t\t}\n\n\t\treturn this.pushStack( matched );\n\t};\n} );\nvar rnothtmlwhite = ( /[^\\x20\\t\\r\\n\\f]+/g );\n\n\n\n// Convert String-formatted options into Object-formatted ones\nfunction createOptions( options ) {\n\tvar object = {};\n\tjQuery.each( options.match( rnothtmlwhite ) || [], function( _, flag ) {\n\t\tobject[ flag ] = true;\n\t} );\n\treturn object;\n}\n\n/*\n * Create a callback list using the following parameters:\n *\n *\toptions: an optional list of space-separated options that will change how\n *\t\t\tthe callback list behaves or a more traditional option object\n *\n * By default a callback list will act like an event callback list and can be\n * \"fired\" multiple times.\n *\n * Possible options:\n *\n *\tonce:\t\t\twill ensure the callback list can only be fired once (like a Deferred)\n *\n *\tmemory:\t\t\twill keep track of previous values and will call any callback added\n *\t\t\t\t\tafter the list has been fired right away with the latest \"memorized\"\n *\t\t\t\t\tvalues (like a Deferred)\n *\n *\tunique:\t\t\twill ensure a callback can only be added once (no duplicate in the list)\n *\n *\tstopOnFalse:\tinterrupt callings when a callback returns false\n *\n */\njQuery.Callbacks = function( options ) {\n\n\t// Convert options from String-formatted to Object-formatted if needed\n\t// (we check in cache first)\n\toptions = typeof options === \"string\" ?\n\t\tcreateOptions( options ) :\n\t\tjQuery.extend( {}, options );\n\n\tvar // Flag to know if list is currently firing\n\t\tfiring,\n\n\t\t// Last fire value for non-forgettable lists\n\t\tmemory,\n\n\t\t// Flag to know if list was already fired\n\t\tfired,\n\n\t\t// Flag to prevent firing\n\t\tlocked,\n\n\t\t// Actual callback list\n\t\tlist = [],\n\n\t\t// Queue of execution data for repeatable lists\n\t\tqueue = [],\n\n\t\t// Index of currently firing callback (modified by add/remove as needed)\n\t\tfiringIndex = -1,\n\n\t\t// Fire callbacks\n\t\tfire = function() {\n\n\t\t\t// Enforce single-firing\n\t\t\tlocked = locked || options.once;\n\n\t\t\t// Execute callbacks for all pending executions,\n\t\t\t// respecting firingIndex overrides and runtime changes\n\t\t\tfired = firing = true;\n\t\t\tfor ( ; queue.length; firingIndex = -1 ) {\n\t\t\t\tmemory = queue.shift();\n\t\t\t\twhile ( ++firingIndex < list.length ) {\n\n\t\t\t\t\t// Run callback and check for early termination\n\t\t\t\t\tif ( list[ firingIndex ].apply( memory[ 0 ], memory[ 1 ] ) === false &&\n\t\t\t\t\t\toptions.stopOnFalse ) {\n\n\t\t\t\t\t\t// Jump to end and forget the data so .add doesn't re-fire\n\t\t\t\t\t\tfiringIndex = list.length;\n\t\t\t\t\t\tmemory = false;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Forget the data if we're done with it\n\t\t\tif ( !options.memory ) {\n\t\t\t\tmemory = false;\n\t\t\t}\n\n\t\t\tfiring = false;\n\n\t\t\t// Clean up if we're done firing for good\n\t\t\tif ( locked ) {\n\n\t\t\t\t// Keep an empty list if we have data for future add calls\n\t\t\t\tif ( memory ) {\n\t\t\t\t\tlist = [];\n\n\t\t\t\t// Otherwise, this object is spent\n\t\t\t\t} else {\n\t\t\t\t\tlist = \"\";\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\n\t\t// Actual Callbacks object\n\t\tself = {\n\n\t\t\t// Add a callback or a collection of callbacks to the list\n\t\t\tadd: function() {\n\t\t\t\tif ( list ) {\n\n\t\t\t\t\t// If we have memory from a past run, we should fire after adding\n\t\t\t\t\tif ( memory && !firing ) {\n\t\t\t\t\t\tfiringIndex = list.length - 1;\n\t\t\t\t\t\tqueue.push( memory );\n\t\t\t\t\t}\n\n\t\t\t\t\t( function add( args ) {\n\t\t\t\t\t\tjQuery.each( args, function( _, arg ) {\n\t\t\t\t\t\t\tif ( isFunction( arg ) ) {\n\t\t\t\t\t\t\t\tif ( !options.unique || !self.has( arg ) ) {\n\t\t\t\t\t\t\t\t\tlist.push( arg );\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} else if ( arg && arg.length && toType( arg ) !== \"string\" ) {\n\n\t\t\t\t\t\t\t\t// Inspect recursively\n\t\t\t\t\t\t\t\tadd( arg );\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} );\n\t\t\t\t\t} )( arguments );\n\n\t\t\t\t\tif ( memory && !firing ) {\n\t\t\t\t\t\tfire();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn this;\n\t\t\t},\n\n\t\t\t// Remove a callback from the list\n\t\t\tremove: function() {\n\t\t\t\tjQuery.each( arguments, function( _, arg ) {\n\t\t\t\t\tvar index;\n\t\t\t\t\twhile ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {\n\t\t\t\t\t\tlist.splice( index, 1 );\n\n\t\t\t\t\t\t// Handle firing indexes\n\t\t\t\t\t\tif ( index <= firingIndex ) {\n\t\t\t\t\t\t\tfiringIndex--;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} );\n\t\t\t\treturn this;\n\t\t\t},\n\n\t\t\t// Check if a given callback is in the list.\n\t\t\t// If no argument is given, return whether or not list has callbacks attached.\n\t\t\thas: function( fn ) {\n\t\t\t\treturn fn ?\n\t\t\t\t\tjQuery.inArray( fn, list ) > -1 :\n\t\t\t\t\tlist.length > 0;\n\t\t\t},\n\n\t\t\t// Remove all callbacks from the list\n\t\t\tempty: function() {\n\t\t\t\tif ( list ) {\n\t\t\t\t\tlist = [];\n\t\t\t\t}\n\t\t\t\treturn this;\n\t\t\t},\n\n\t\t\t// Disable .fire and .add\n\t\t\t// Abort any current/pending executions\n\t\t\t// Clear all callbacks and values\n\t\t\tdisable: function() {\n\t\t\t\tlocked = queue = [];\n\t\t\t\tlist = memory = \"\";\n\t\t\t\treturn this;\n\t\t\t},\n\t\t\tdisabled: function() {\n\t\t\t\treturn !list;\n\t\t\t},\n\n\t\t\t// Disable .fire\n\t\t\t// Also disable .add unless we have memory (since it would have no effect)\n\t\t\t// Abort any pending executions\n\t\t\tlock: function() {\n\t\t\t\tlocked = queue = [];\n\t\t\t\tif ( !memory && !firing ) {\n\t\t\t\t\tlist = memory = \"\";\n\t\t\t\t}\n\t\t\t\treturn this;\n\t\t\t},\n\t\t\tlocked: function() {\n\t\t\t\treturn !!locked;\n\t\t\t},\n\n\t\t\t// Call all callbacks with the given context and arguments\n\t\t\tfireWith: function( context, args ) {\n\t\t\t\tif ( !locked ) {\n\t\t\t\t\targs = args || [];\n\t\t\t\t\targs = [ context, args.slice ? args.slice() : args ];\n\t\t\t\t\tqueue.push( args );\n\t\t\t\t\tif ( !firing ) {\n\t\t\t\t\t\tfire();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn this;\n\t\t\t},\n\n\t\t\t// Call all the callbacks with the given arguments\n\t\t\tfire: function() {\n\t\t\t\tself.fireWith( this, arguments );\n\t\t\t\treturn this;\n\t\t\t},\n\n\t\t\t// To know if the callbacks have already been called at least once\n\t\t\tfired: function() {\n\t\t\t\treturn !!fired;\n\t\t\t}\n\t\t};\n\n\treturn self;\n};\n\n\nfunction Identity( v ) {\n\treturn v;\n}\nfunction Thrower( ex ) {\n\tthrow ex;\n}\n\nfunction adoptValue( value, resolve, reject, noValue ) {\n\tvar method;\n\n\ttry {\n\n\t\t// Check for promise aspect first to privilege synchronous behavior\n\t\tif ( value && isFunction( ( method = value.promise ) ) ) {\n\t\t\tmethod.call( value ).done( resolve ).fail( reject );\n\n\t\t// Other thenables\n\t\t} else if ( value && isFunction( ( method = value.then ) ) ) {\n\t\t\tmethod.call( value, resolve, reject );\n\n\t\t// Other non-thenables\n\t\t} else {\n\n\t\t\t// Control `resolve` arguments by letting Array#slice cast boolean `noValue` to integer:\n\t\t\t// * false: [ value ].slice( 0 ) => resolve( value )\n\t\t\t// * true: [ value ].slice( 1 ) => resolve()\n\t\t\tresolve.apply( undefined, [ value ].slice( noValue ) );\n\t\t}\n\n\t// For Promises/A+, convert exceptions into rejections\n\t// Since jQuery.when doesn't unwrap thenables, we can skip the extra checks appearing in\n\t// Deferred#then to conditionally suppress rejection.\n\t} catch ( value ) {\n\n\t\t// Support: Android 4.0 only\n\t\t// Strict mode functions invoked without .call/.apply get global-object context\n\t\treject.apply( undefined, [ value ] );\n\t}\n}\n\njQuery.extend( {\n\n\tDeferred: function( func ) {\n\t\tvar tuples = [\n\n\t\t\t\t// action, add listener, callbacks,\n\t\t\t\t// ... .then handlers, argument index, [final state]\n\t\t\t\t[ \"notify\", \"progress\", jQuery.Callbacks( \"memory\" ),\n\t\t\t\t\tjQuery.Callbacks( \"memory\" ), 2 ],\n\t\t\t\t[ \"resolve\", \"done\", jQuery.Callbacks( \"once memory\" ),\n\t\t\t\t\tjQuery.Callbacks( \"once memory\" ), 0, \"resolved\" ],\n\t\t\t\t[ \"reject\", \"fail\", jQuery.Callbacks( \"once memory\" ),\n\t\t\t\t\tjQuery.Callbacks( \"once memory\" ), 1, \"rejected\" ]\n\t\t\t],\n\t\t\tstate = \"pending\",\n\t\t\tpromise = {\n\t\t\t\tstate: function() {\n\t\t\t\t\treturn state;\n\t\t\t\t},\n\t\t\t\talways: function() {\n\t\t\t\t\tdeferred.done( arguments ).fail( arguments );\n\t\t\t\t\treturn this;\n\t\t\t\t},\n\t\t\t\t\"catch\": function( fn ) {\n\t\t\t\t\treturn promise.then( null, fn );\n\t\t\t\t},\n\n\t\t\t\t// Keep pipe for back-compat\n\t\t\t\tpipe: function( /* fnDone, fnFail, fnProgress */ ) {\n\t\t\t\t\tvar fns = arguments;\n\n\t\t\t\t\treturn jQuery.Deferred( function( newDefer ) {\n\t\t\t\t\t\tjQuery.each( tuples, function( i, tuple ) {\n\n\t\t\t\t\t\t\t// Map tuples (progress, done, fail) to arguments (done, fail, progress)\n\t\t\t\t\t\t\tvar fn = isFunction( fns[ tuple[ 4 ] ] ) && fns[ tuple[ 4 ] ];\n\n\t\t\t\t\t\t\t// deferred.progress(function() { bind to newDefer or newDefer.notify })\n\t\t\t\t\t\t\t// deferred.done(function() { bind to newDefer or newDefer.resolve })\n\t\t\t\t\t\t\t// deferred.fail(function() { bind to newDefer or newDefer.reject })\n\t\t\t\t\t\t\tdeferred[ tuple[ 1 ] ]( function() {\n\t\t\t\t\t\t\t\tvar returned = fn && fn.apply( this, arguments );\n\t\t\t\t\t\t\t\tif ( returned && isFunction( returned.promise ) ) {\n\t\t\t\t\t\t\t\t\treturned.promise()\n\t\t\t\t\t\t\t\t\t\t.progress( newDefer.notify )\n\t\t\t\t\t\t\t\t\t\t.done( newDefer.resolve )\n\t\t\t\t\t\t\t\t\t\t.fail( newDefer.reject );\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tnewDefer[ tuple[ 0 ] + \"With\" ](\n\t\t\t\t\t\t\t\t\t\tthis,\n\t\t\t\t\t\t\t\t\t\tfn ? [ returned ] : arguments\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} );\n\t\t\t\t\t\t} );\n\t\t\t\t\t\tfns = null;\n\t\t\t\t\t} ).promise();\n\t\t\t\t},\n\t\t\t\tthen: function( onFulfilled, onRejected, onProgress ) {\n\t\t\t\t\tvar maxDepth = 0;\n\t\t\t\t\tfunction resolve( depth, deferred, handler, special ) {\n\t\t\t\t\t\treturn function() {\n\t\t\t\t\t\t\tvar that = this,\n\t\t\t\t\t\t\t\targs = arguments,\n\t\t\t\t\t\t\t\tmightThrow = function() {\n\t\t\t\t\t\t\t\t\tvar returned, then;\n\n\t\t\t\t\t\t\t\t\t// Support: Promises/A+ section 2.3.3.3.3\n\t\t\t\t\t\t\t\t\t// https://promisesaplus.com/#point-59\n\t\t\t\t\t\t\t\t\t// Ignore double-resolution attempts\n\t\t\t\t\t\t\t\t\tif ( depth < maxDepth ) {\n\t\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\treturned = handler.apply( that, args );\n\n\t\t\t\t\t\t\t\t\t// Support: Promises/A+ section 2.3.1\n\t\t\t\t\t\t\t\t\t// https://promisesaplus.com/#point-48\n\t\t\t\t\t\t\t\t\tif ( returned === deferred.promise() ) {\n\t\t\t\t\t\t\t\t\t\tthrow new TypeError( \"Thenable self-resolution\" );\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t// Support: Promises/A+ sections 2.3.3.1, 3.5\n\t\t\t\t\t\t\t\t\t// https://promisesaplus.com/#point-54\n\t\t\t\t\t\t\t\t\t// https://promisesaplus.com/#point-75\n\t\t\t\t\t\t\t\t\t// Retrieve `then` only once\n\t\t\t\t\t\t\t\t\tthen = returned &&\n\n\t\t\t\t\t\t\t\t\t\t// Support: Promises/A+ section 2.3.4\n\t\t\t\t\t\t\t\t\t\t// https://promisesaplus.com/#point-64\n\t\t\t\t\t\t\t\t\t\t// Only check objects and functions for thenability\n\t\t\t\t\t\t\t\t\t\t( typeof returned === \"object\" ||\n\t\t\t\t\t\t\t\t\t\t\ttypeof returned === \"function\" ) &&\n\t\t\t\t\t\t\t\t\t\treturned.then;\n\n\t\t\t\t\t\t\t\t\t// Handle a returned thenable\n\t\t\t\t\t\t\t\t\tif ( isFunction( then ) ) {\n\n\t\t\t\t\t\t\t\t\t\t// Special processors (notify) just wait for resolution\n\t\t\t\t\t\t\t\t\t\tif ( special ) {\n\t\t\t\t\t\t\t\t\t\t\tthen.call(\n\t\t\t\t\t\t\t\t\t\t\t\treturned,\n\t\t\t\t\t\t\t\t\t\t\t\tresolve( maxDepth, deferred, Identity, special ),\n\t\t\t\t\t\t\t\t\t\t\t\tresolve( maxDepth, deferred, Thrower, special )\n\t\t\t\t\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\t\t\t\t\t// Normal processors (resolve) also hook into progress\n\t\t\t\t\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\t\t\t\t\t// ...and disregard older resolution values\n\t\t\t\t\t\t\t\t\t\t\tmaxDepth++;\n\n\t\t\t\t\t\t\t\t\t\t\tthen.call(\n\t\t\t\t\t\t\t\t\t\t\t\treturned,\n\t\t\t\t\t\t\t\t\t\t\t\tresolve( maxDepth, deferred, Identity, special ),\n\t\t\t\t\t\t\t\t\t\t\t\tresolve( maxDepth, deferred, Thrower, special ),\n\t\t\t\t\t\t\t\t\t\t\t\tresolve( maxDepth, deferred, Identity,\n\t\t\t\t\t\t\t\t\t\t\t\t\tdeferred.notifyWith )\n\t\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t// Handle all other returned values\n\t\t\t\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\t\t\t\t// Only substitute handlers pass on context\n\t\t\t\t\t\t\t\t\t\t// and multiple values (non-spec behavior)\n\t\t\t\t\t\t\t\t\t\tif ( handler !== Identity ) {\n\t\t\t\t\t\t\t\t\t\t\tthat = undefined;\n\t\t\t\t\t\t\t\t\t\t\targs = [ returned ];\n\t\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t\t// Process the value(s)\n\t\t\t\t\t\t\t\t\t\t// Default process is resolve\n\t\t\t\t\t\t\t\t\t\t( special || deferred.resolveWith )( that, args );\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t},\n\n\t\t\t\t\t\t\t\t// Only normal processors (resolve) catch and reject exceptions\n\t\t\t\t\t\t\t\tprocess = special ?\n\t\t\t\t\t\t\t\t\tmightThrow :\n\t\t\t\t\t\t\t\t\tfunction() {\n\t\t\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\t\t\tmightThrow();\n\t\t\t\t\t\t\t\t\t\t} catch ( e ) {\n\n\t\t\t\t\t\t\t\t\t\t\tif ( jQuery.Deferred.exceptionHook ) {\n\t\t\t\t\t\t\t\t\t\t\t\tjQuery.Deferred.exceptionHook( e,\n\t\t\t\t\t\t\t\t\t\t\t\t\tprocess.stackTrace );\n\t\t\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t\t\t// Support: Promises/A+ section 2.3.3.3.4.1\n\t\t\t\t\t\t\t\t\t\t\t// https://promisesaplus.com/#point-61\n\t\t\t\t\t\t\t\t\t\t\t// Ignore post-resolution exceptions\n\t\t\t\t\t\t\t\t\t\t\tif ( depth + 1 >= maxDepth ) {\n\n\t\t\t\t\t\t\t\t\t\t\t\t// Only substitute handlers pass on context\n\t\t\t\t\t\t\t\t\t\t\t\t// and multiple values (non-spec behavior)\n\t\t\t\t\t\t\t\t\t\t\t\tif ( handler !== Thrower ) {\n\t\t\t\t\t\t\t\t\t\t\t\t\tthat = undefined;\n\t\t\t\t\t\t\t\t\t\t\t\t\targs = [ e ];\n\t\t\t\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t\t\t\tdeferred.rejectWith( that, args );\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t};\n\n\t\t\t\t\t\t\t// Support: Promises/A+ section 2.3.3.3.1\n\t\t\t\t\t\t\t// https://promisesaplus.com/#point-57\n\t\t\t\t\t\t\t// Re-resolve promises immediately to dodge false rejection from\n\t\t\t\t\t\t\t// subsequent errors\n\t\t\t\t\t\t\tif ( depth ) {\n\t\t\t\t\t\t\t\tprocess();\n\t\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\t\t// Call an optional hook to record the stack, in case of exception\n\t\t\t\t\t\t\t\t// since it's otherwise lost when execution goes async\n\t\t\t\t\t\t\t\tif ( jQuery.Deferred.getStackHook ) {\n\t\t\t\t\t\t\t\t\tprocess.stackTrace = jQuery.Deferred.getStackHook();\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\twindow.setTimeout( process );\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\n\t\t\t\t\treturn jQuery.Deferred( function( newDefer ) {\n\n\t\t\t\t\t\t// progress_handlers.add( ... )\n\t\t\t\t\t\ttuples[ 0 ][ 3 ].add(\n\t\t\t\t\t\t\tresolve(\n\t\t\t\t\t\t\t\t0,\n\t\t\t\t\t\t\t\tnewDefer,\n\t\t\t\t\t\t\t\tisFunction( onProgress ) ?\n\t\t\t\t\t\t\t\t\tonProgress :\n\t\t\t\t\t\t\t\t\tIdentity,\n\t\t\t\t\t\t\t\tnewDefer.notifyWith\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t);\n\n\t\t\t\t\t\t// fulfilled_handlers.add( ... )\n\t\t\t\t\t\ttuples[ 1 ][ 3 ].add(\n\t\t\t\t\t\t\tresolve(\n\t\t\t\t\t\t\t\t0,\n\t\t\t\t\t\t\t\tnewDefer,\n\t\t\t\t\t\t\t\tisFunction( onFulfilled ) ?\n\t\t\t\t\t\t\t\t\tonFulfilled :\n\t\t\t\t\t\t\t\t\tIdentity\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t);\n\n\t\t\t\t\t\t// rejected_handlers.add( ... )\n\t\t\t\t\t\ttuples[ 2 ][ 3 ].add(\n\t\t\t\t\t\t\tresolve(\n\t\t\t\t\t\t\t\t0,\n\t\t\t\t\t\t\t\tnewDefer,\n\t\t\t\t\t\t\t\tisFunction( onRejected ) ?\n\t\t\t\t\t\t\t\t\tonRejected :\n\t\t\t\t\t\t\t\t\tThrower\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t);\n\t\t\t\t\t} ).promise();\n\t\t\t\t},\n\n\t\t\t\t// Get a promise for this deferred\n\t\t\t\t// If obj is provided, the promise aspect is added to the object\n\t\t\t\tpromise: function( obj ) {\n\t\t\t\t\treturn obj != null ? jQuery.extend( obj, promise ) : promise;\n\t\t\t\t}\n\t\t\t},\n\t\t\tdeferred = {};\n\n\t\t// Add list-specific methods\n\t\tjQuery.each( tuples, function( i, tuple ) {\n\t\t\tvar list = tuple[ 2 ],\n\t\t\t\tstateString = tuple[ 5 ];\n\n\t\t\t// promise.progress = list.add\n\t\t\t// promise.done = list.add\n\t\t\t// promise.fail = list.add\n\t\t\tpromise[ tuple[ 1 ] ] = list.add;\n\n\t\t\t// Handle state\n\t\t\tif ( stateString ) {\n\t\t\t\tlist.add(\n\t\t\t\t\tfunction() {\n\n\t\t\t\t\t\t// state = \"resolved\" (i.e., fulfilled)\n\t\t\t\t\t\t// state = \"rejected\"\n\t\t\t\t\t\tstate = stateString;\n\t\t\t\t\t},\n\n\t\t\t\t\t// rejected_callbacks.disable\n\t\t\t\t\t// fulfilled_callbacks.disable\n\t\t\t\t\ttuples[ 3 - i ][ 2 ].disable,\n\n\t\t\t\t\t// rejected_handlers.disable\n\t\t\t\t\t// fulfilled_handlers.disable\n\t\t\t\t\ttuples[ 3 - i ][ 3 ].disable,\n\n\t\t\t\t\t// progress_callbacks.lock\n\t\t\t\t\ttuples[ 0 ][ 2 ].lock,\n\n\t\t\t\t\t// progress_handlers.lock\n\t\t\t\t\ttuples[ 0 ][ 3 ].lock\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// progress_handlers.fire\n\t\t\t// fulfilled_handlers.fire\n\t\t\t// rejected_handlers.fire\n\t\t\tlist.add( tuple[ 3 ].fire );\n\n\t\t\t// deferred.notify = function() { deferred.notifyWith(...) }\n\t\t\t// deferred.resolve = function() { deferred.resolveWith(...) }\n\t\t\t// deferred.reject = function() { deferred.rejectWith(...) }\n\t\t\tdeferred[ tuple[ 0 ] ] = function() {\n\t\t\t\tdeferred[ tuple[ 0 ] + \"With\" ]( this === deferred ? undefined : this, arguments );\n\t\t\t\treturn this;\n\t\t\t};\n\n\t\t\t// deferred.notifyWith = list.fireWith\n\t\t\t// deferred.resolveWith = list.fireWith\n\t\t\t// deferred.rejectWith = list.fireWith\n\t\t\tdeferred[ tuple[ 0 ] + \"With\" ] = list.fireWith;\n\t\t} );\n\n\t\t// Make the deferred a promise\n\t\tpromise.promise( deferred );\n\n\t\t// Call given func if any\n\t\tif ( func ) {\n\t\t\tfunc.call( deferred, deferred );\n\t\t}\n\n\t\t// All done!\n\t\treturn deferred;\n\t},\n\n\t// Deferred helper\n\twhen: function( singleValue ) {\n\t\tvar\n\n\t\t\t// count of uncompleted subordinates\n\t\t\tremaining = arguments.length,\n\n\t\t\t// count of unprocessed arguments\n\t\t\ti = remaining,\n\n\t\t\t// subordinate fulfillment data\n\t\t\tresolveContexts = Array( i ),\n\t\t\tresolveValues = slice.call( arguments ),\n\n\t\t\t// the master Deferred\n\t\t\tmaster = jQuery.Deferred(),\n\n\t\t\t// subordinate callback factory\n\t\t\tupdateFunc = function( i ) {\n\t\t\t\treturn function( value ) {\n\t\t\t\t\tresolveContexts[ i ] = this;\n\t\t\t\t\tresolveValues[ i ] = arguments.length > 1 ? slice.call( arguments ) : value;\n\t\t\t\t\tif ( !( --remaining ) ) {\n\t\t\t\t\t\tmaster.resolveWith( resolveContexts, resolveValues );\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t};\n\n\t\t// Single- and empty arguments are adopted like Promise.resolve\n\t\tif ( remaining <= 1 ) {\n\t\t\tadoptValue( singleValue, master.done( updateFunc( i ) ).resolve, master.reject,\n\t\t\t\t!remaining );\n\n\t\t\t// Use .then() to unwrap secondary thenables (cf. gh-3000)\n\t\t\tif ( master.state() === \"pending\" ||\n\t\t\t\tisFunction( resolveValues[ i ] && resolveValues[ i ].then ) ) {\n\n\t\t\t\treturn master.then();\n\t\t\t}\n\t\t}\n\n\t\t// Multiple arguments are aggregated like Promise.all array elements\n\t\twhile ( i-- ) {\n\t\t\tadoptValue( resolveValues[ i ], updateFunc( i ), master.reject );\n\t\t}\n\n\t\treturn master.promise();\n\t}\n} );\n\n\n// These usually indicate a programmer mistake during development,\n// warn about them ASAP rather than swallowing them by default.\nvar rerrorNames = /^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;\n\njQuery.Deferred.exceptionHook = function( error, stack ) {\n\n\t// Support: IE 8 - 9 only\n\t// Console exists when dev tools are open, which can happen at any time\n\tif ( window.console && window.console.warn && error && rerrorNames.test( error.name ) ) {\n\t\twindow.console.warn( \"jQuery.Deferred exception: \" + error.message, error.stack, stack );\n\t}\n};\n\n\n\n\njQuery.readyException = function( error ) {\n\twindow.setTimeout( function() {\n\t\tthrow error;\n\t} );\n};\n\n\n\n\n// The deferred used on DOM ready\nvar readyList = jQuery.Deferred();\n\njQuery.fn.ready = function( fn ) {\n\n\treadyList\n\t\t.then( fn )\n\n\t\t// Wrap jQuery.readyException in a function so that the lookup\n\t\t// happens at the time of error handling instead of callback\n\t\t// registration.\n\t\t.catch( function( error ) {\n\t\t\tjQuery.readyException( error );\n\t\t} );\n\n\treturn this;\n};\n\njQuery.extend( {\n\n\t// Is the DOM ready to be used? Set to true once it occurs.\n\tisReady: false,\n\n\t// A counter to track how many items to wait for before\n\t// the ready event fires. See #6781\n\treadyWait: 1,\n\n\t// Handle when the DOM is ready\n\tready: function( wait ) {\n\n\t\t// Abort if there are pending holds or we're already ready\n\t\tif ( wait === true ? --jQuery.readyWait : jQuery.isReady ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Remember that the DOM is ready\n\t\tjQuery.isReady = true;\n\n\t\t// If a normal DOM Ready event fired, decrement, and wait if need be\n\t\tif ( wait !== true && --jQuery.readyWait > 0 ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// If there are functions bound, to execute\n\t\treadyList.resolveWith( document, [ jQuery ] );\n\t}\n} );\n\njQuery.ready.then = readyList.then;\n\n// The ready event handler and self cleanup method\nfunction completed() {\n\tdocument.removeEventListener( \"DOMContentLoaded\", completed );\n\twindow.removeEventListener( \"load\", completed );\n\tjQuery.ready();\n}\n\n// Catch cases where $(document).ready() is called\n// after the browser event has already occurred.\n// Support: IE <=9 - 10 only\n// Older IE sometimes signals \"interactive\" too soon\nif ( document.readyState === \"complete\" ||\n\t( document.readyState !== \"loading\" && !document.documentElement.doScroll ) ) {\n\n\t// Handle it asynchronously to allow scripts the opportunity to delay ready\n\twindow.setTimeout( jQuery.ready );\n\n} else {\n\n\t// Use the handy event callback\n\tdocument.addEventListener( \"DOMContentLoaded\", completed );\n\n\t// A fallback to window.onload, that will always work\n\twindow.addEventListener( \"load\", completed );\n}\n\n\n\n\n// Multifunctional method to get and set values of a collection\n// The value/s can optionally be executed if it's a function\nvar access = function( elems, fn, key, value, chainable, emptyGet, raw ) {\n\tvar i = 0,\n\t\tlen = elems.length,\n\t\tbulk = key == null;\n\n\t// Sets many values\n\tif ( toType( key ) === \"object\" ) {\n\t\tchainable = true;\n\t\tfor ( i in key ) {\n\t\t\taccess( elems, fn, i, key[ i ], true, emptyGet, raw );\n\t\t}\n\n\t// Sets one value\n\t} else if ( value !== undefined ) {\n\t\tchainable = true;\n\n\t\tif ( !isFunction( value ) ) {\n\t\t\traw = true;\n\t\t}\n\n\t\tif ( bulk ) {\n\n\t\t\t// Bulk operations run against the entire set\n\t\t\tif ( raw ) {\n\t\t\t\tfn.call( elems, value );\n\t\t\t\tfn = null;\n\n\t\t\t// ...except when executing function values\n\t\t\t} else {\n\t\t\t\tbulk = fn;\n\t\t\t\tfn = function( elem, key, value ) {\n\t\t\t\t\treturn bulk.call( jQuery( elem ), value );\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\n\t\tif ( fn ) {\n\t\t\tfor ( ; i < len; i++ ) {\n\t\t\t\tfn(\n\t\t\t\t\telems[ i ], key, raw ?\n\t\t\t\t\tvalue :\n\t\t\t\t\tvalue.call( elems[ i ], i, fn( elems[ i ], key ) )\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\tif ( chainable ) {\n\t\treturn elems;\n\t}\n\n\t// Gets\n\tif ( bulk ) {\n\t\treturn fn.call( elems );\n\t}\n\n\treturn len ? fn( elems[ 0 ], key ) : emptyGet;\n};\n\n\n// Matches dashed string for camelizing\nvar rmsPrefix = /^-ms-/,\n\trdashAlpha = /-([a-z])/g;\n\n// Used by camelCase as callback to replace()\nfunction fcamelCase( all, letter ) {\n\treturn letter.toUpperCase();\n}\n\n// Convert dashed to camelCase; used by the css and data modules\n// Support: IE <=9 - 11, Edge 12 - 15\n// Microsoft forgot to hump their vendor prefix (#9572)\nfunction camelCase( string ) {\n\treturn string.replace( rmsPrefix, \"ms-\" ).replace( rdashAlpha, fcamelCase );\n}\nvar acceptData = function( owner ) {\n\n\t// Accepts only:\n\t// - Node\n\t// - Node.ELEMENT_NODE\n\t// - Node.DOCUMENT_NODE\n\t// - Object\n\t// - Any\n\treturn owner.nodeType === 1 || owner.nodeType === 9 || !( +owner.nodeType );\n};\n\n\n\n\nfunction Data() {\n\tthis.expando = jQuery.expando + Data.uid++;\n}\n\nData.uid = 1;\n\nData.prototype = {\n\n\tcache: function( owner ) {\n\n\t\t// Check if the owner object already has a cache\n\t\tvar value = owner[ this.expando ];\n\n\t\t// If not, create one\n\t\tif ( !value ) {\n\t\t\tvalue = {};\n\n\t\t\t// We can accept data for non-element nodes in modern browsers,\n\t\t\t// but we should not, see #8335.\n\t\t\t// Always return an empty object.\n\t\t\tif ( acceptData( owner ) ) {\n\n\t\t\t\t// If it is a node unlikely to be stringify-ed or looped over\n\t\t\t\t// use plain assignment\n\t\t\t\tif ( owner.nodeType ) {\n\t\t\t\t\towner[ this.expando ] = value;\n\n\t\t\t\t// Otherwise secure it in a non-enumerable property\n\t\t\t\t// configurable must be true to allow the property to be\n\t\t\t\t// deleted when data is removed\n\t\t\t\t} else {\n\t\t\t\t\tObject.defineProperty( owner, this.expando, {\n\t\t\t\t\t\tvalue: value,\n\t\t\t\t\t\tconfigurable: true\n\t\t\t\t\t} );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn value;\n\t},\n\tset: function( owner, data, value ) {\n\t\tvar prop,\n\t\t\tcache = this.cache( owner );\n\n\t\t// Handle: [ owner, key, value ] args\n\t\t// Always use camelCase key (gh-2257)\n\t\tif ( typeof data === \"string\" ) {\n\t\t\tcache[ camelCase( data ) ] = value;\n\n\t\t// Handle: [ owner, { properties } ] args\n\t\t} else {\n\n\t\t\t// Copy the properties one-by-one to the cache object\n\t\t\tfor ( prop in data ) {\n\t\t\t\tcache[ camelCase( prop ) ] = data[ prop ];\n\t\t\t}\n\t\t}\n\t\treturn cache;\n\t},\n\tget: function( owner, key ) {\n\t\treturn key === undefined ?\n\t\t\tthis.cache( owner ) :\n\n\t\t\t// Always use camelCase key (gh-2257)\n\t\t\towner[ this.expando ] && owner[ this.expando ][ camelCase( key ) ];\n\t},\n\taccess: function( owner, key, value ) {\n\n\t\t// In cases where either:\n\t\t//\n\t\t// 1. No key was specified\n\t\t// 2. A string key was specified, but no value provided\n\t\t//\n\t\t// Take the \"read\" path and allow the get method to determine\n\t\t// which value to return, respectively either:\n\t\t//\n\t\t// 1. The entire cache object\n\t\t// 2. The data stored at the key\n\t\t//\n\t\tif ( key === undefined ||\n\t\t\t\t( ( key && typeof key === \"string\" ) && value === undefined ) ) {\n\n\t\t\treturn this.get( owner, key );\n\t\t}\n\n\t\t// When the key is not a string, or both a key and value\n\t\t// are specified, set or extend (existing objects) with either:\n\t\t//\n\t\t// 1. An object of properties\n\t\t// 2. A key and value\n\t\t//\n\t\tthis.set( owner, key, value );\n\n\t\t// Since the \"set\" path can have two possible entry points\n\t\t// return the expected data based on which path was taken[*]\n\t\treturn value !== undefined ? value : key;\n\t},\n\tremove: function( owner, key ) {\n\t\tvar i,\n\t\t\tcache = owner[ this.expando ];\n\n\t\tif ( cache === undefined ) {\n\t\t\treturn;\n\t\t}\n\n\t\tif ( key !== undefined ) {\n\n\t\t\t// Support array or space separated string of keys\n\t\t\tif ( Array.isArray( key ) ) {\n\n\t\t\t\t// If key is an array of keys...\n\t\t\t\t// We always set camelCase keys, so remove that.\n\t\t\t\tkey = key.map( camelCase );\n\t\t\t} else {\n\t\t\t\tkey = camelCase( key );\n\n\t\t\t\t// If a key with the spaces exists, use it.\n\t\t\t\t// Otherwise, create an array by matching non-whitespace\n\t\t\t\tkey = key in cache ?\n\t\t\t\t\t[ key ] :\n\t\t\t\t\t( key.match( rnothtmlwhite ) || [] );\n\t\t\t}\n\n\t\t\ti = key.length;\n\n\t\t\twhile ( i-- ) {\n\t\t\t\tdelete cache[ key[ i ] ];\n\t\t\t}\n\t\t}\n\n\t\t// Remove the expando if there's no more data\n\t\tif ( key === undefined || jQuery.isEmptyObject( cache ) ) {\n\n\t\t\t// Support: Chrome <=35 - 45\n\t\t\t// Webkit & Blink performance suffers when deleting properties\n\t\t\t// from DOM nodes, so set to undefined instead\n\t\t\t// https://bugs.chromium.org/p/chromium/issues/detail?id=378607 (bug restricted)\n\t\t\tif ( owner.nodeType ) {\n\t\t\t\towner[ this.expando ] = undefined;\n\t\t\t} else {\n\t\t\t\tdelete owner[ this.expando ];\n\t\t\t}\n\t\t}\n\t},\n\thasData: function( owner ) {\n\t\tvar cache = owner[ this.expando ];\n\t\treturn cache !== undefined && !jQuery.isEmptyObject( cache );\n\t}\n};\nvar dataPriv = new Data();\n\nvar dataUser = new Data();\n\n\n\n//\tImplementation Summary\n//\n//\t1. Enforce API surface and semantic compatibility with 1.9.x branch\n//\t2. Improve the module's maintainability by reducing the storage\n//\t\tpaths to a single mechanism.\n//\t3. Use the same single mechanism to support \"private\" and \"user\" data.\n//\t4. _Never_ expose \"private\" data to user code (TODO: Drop _data, _removeData)\n//\t5. Avoid exposing implementation details on user objects (eg. expando properties)\n//\t6. Provide a clear path for implementation upgrade to WeakMap in 2014\n\nvar rbrace = /^(?:\\{[\\w\\W]*\\}|\\[[\\w\\W]*\\])$/,\n\trmultiDash = /[A-Z]/g;\n\nfunction getData( data ) {\n\tif ( data === \"true\" ) {\n\t\treturn true;\n\t}\n\n\tif ( data === \"false\" ) {\n\t\treturn false;\n\t}\n\n\tif ( data === \"null\" ) {\n\t\treturn null;\n\t}\n\n\t// Only convert to a number if it doesn't change the string\n\tif ( data === +data + \"\" ) {\n\t\treturn +data;\n\t}\n\n\tif ( rbrace.test( data ) ) {\n\t\treturn JSON.parse( data );\n\t}\n\n\treturn data;\n}\n\nfunction dataAttr( elem, key, data ) {\n\tvar name;\n\n\t// If nothing was found internally, try to fetch any\n\t// data from the HTML5 data-* attribute\n\tif ( data === undefined && elem.nodeType === 1 ) {\n\t\tname = \"data-\" + key.replace( rmultiDash, \"-$&\" ).toLowerCase();\n\t\tdata = elem.getAttribute( name );\n\n\t\tif ( typeof data === \"string\" ) {\n\t\t\ttry {\n\t\t\t\tdata = getData( data );\n\t\t\t} catch ( e ) {}\n\n\t\t\t// Make sure we set the data so it isn't changed later\n\t\t\tdataUser.set( elem, key, data );\n\t\t} else {\n\t\t\tdata = undefined;\n\t\t}\n\t}\n\treturn data;\n}\n\njQuery.extend( {\n\thasData: function( elem ) {\n\t\treturn dataUser.hasData( elem ) || dataPriv.hasData( elem );\n\t},\n\n\tdata: function( elem, name, data ) {\n\t\treturn dataUser.access( elem, name, data );\n\t},\n\n\tremoveData: function( elem, name ) {\n\t\tdataUser.remove( elem, name );\n\t},\n\n\t// TODO: Now that all calls to _data and _removeData have been replaced\n\t// with direct calls to dataPriv methods, these can be deprecated.\n\t_data: function( elem, name, data ) {\n\t\treturn dataPriv.access( elem, name, data );\n\t},\n\n\t_removeData: function( elem, name ) {\n\t\tdataPriv.remove( elem, name );\n\t}\n} );\n\njQuery.fn.extend( {\n\tdata: function( key, value ) {\n\t\tvar i, name, data,\n\t\t\telem = this[ 0 ],\n\t\t\tattrs = elem && elem.attributes;\n\n\t\t// Gets all values\n\t\tif ( key === undefined ) {\n\t\t\tif ( this.length ) {\n\t\t\t\tdata = dataUser.get( elem );\n\n\t\t\t\tif ( elem.nodeType === 1 && !dataPriv.get( elem, \"hasDataAttrs\" ) ) {\n\t\t\t\t\ti = attrs.length;\n\t\t\t\t\twhile ( i-- ) {\n\n\t\t\t\t\t\t// Support: IE 11 only\n\t\t\t\t\t\t// The attrs elements can be null (#14894)\n\t\t\t\t\t\tif ( attrs[ i ] ) {\n\t\t\t\t\t\t\tname = attrs[ i ].name;\n\t\t\t\t\t\t\tif ( name.indexOf( \"data-\" ) === 0 ) {\n\t\t\t\t\t\t\t\tname = camelCase( name.slice( 5 ) );\n\t\t\t\t\t\t\t\tdataAttr( elem, name, data[ name ] );\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tdataPriv.set( elem, \"hasDataAttrs\", true );\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn data;\n\t\t}\n\n\t\t// Sets multiple values\n\t\tif ( typeof key === \"object\" ) {\n\t\t\treturn this.each( function() {\n\t\t\t\tdataUser.set( this, key );\n\t\t\t} );\n\t\t}\n\n\t\treturn access( this, function( value ) {\n\t\t\tvar data;\n\n\t\t\t// The calling jQuery object (element matches) is not empty\n\t\t\t// (and therefore has an element appears at this[ 0 ]) and the\n\t\t\t// `value` parameter was not undefined. An empty jQuery object\n\t\t\t// will result in `undefined` for elem = this[ 0 ] which will\n\t\t\t// throw an exception if an attempt to read a data cache is made.\n\t\t\tif ( elem && value === undefined ) {\n\n\t\t\t\t// Attempt to get data from the cache\n\t\t\t\t// The key will always be camelCased in Data\n\t\t\t\tdata = dataUser.get( elem, key );\n\t\t\t\tif ( data !== undefined ) {\n\t\t\t\t\treturn data;\n\t\t\t\t}\n\n\t\t\t\t// Attempt to \"discover\" the data in\n\t\t\t\t// HTML5 custom data-* attrs\n\t\t\t\tdata = dataAttr( elem, key );\n\t\t\t\tif ( data !== undefined ) {\n\t\t\t\t\treturn data;\n\t\t\t\t}\n\n\t\t\t\t// We tried really hard, but the data doesn't exist.\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Set the data...\n\t\t\tthis.each( function() {\n\n\t\t\t\t// We always store the camelCased key\n\t\t\t\tdataUser.set( this, key, value );\n\t\t\t} );\n\t\t}, null, value, arguments.length > 1, null, true );\n\t},\n\n\tremoveData: function( key ) {\n\t\treturn this.each( function() {\n\t\t\tdataUser.remove( this, key );\n\t\t} );\n\t}\n} );\n\n\njQuery.extend( {\n\tqueue: function( elem, type, data ) {\n\t\tvar queue;\n\n\t\tif ( elem ) {\n\t\t\ttype = ( type || \"fx\" ) + \"queue\";\n\t\t\tqueue = dataPriv.get( elem, type );\n\n\t\t\t// Speed up dequeue by getting out quickly if this is just a lookup\n\t\t\tif ( data ) {\n\t\t\t\tif ( !queue || Array.isArray( data ) ) {\n\t\t\t\t\tqueue = dataPriv.access( elem, type, jQuery.makeArray( data ) );\n\t\t\t\t} else {\n\t\t\t\t\tqueue.push( data );\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn queue || [];\n\t\t}\n\t},\n\n\tdequeue: function( elem, type ) {\n\t\ttype = type || \"fx\";\n\n\t\tvar queue = jQuery.queue( elem, type ),\n\t\t\tstartLength = queue.length,\n\t\t\tfn = queue.shift(),\n\t\t\thooks = jQuery._queueHooks( elem, type ),\n\t\t\tnext = function() {\n\t\t\t\tjQuery.dequeue( elem, type );\n\t\t\t};\n\n\t\t// If the fx queue is dequeued, always remove the progress sentinel\n\t\tif ( fn === \"inprogress\" ) {\n\t\t\tfn = queue.shift();\n\t\t\tstartLength--;\n\t\t}\n\n\t\tif ( fn ) {\n\n\t\t\t// Add a progress sentinel to prevent the fx queue from being\n\t\t\t// automatically dequeued\n\t\t\tif ( type === \"fx\" ) {\n\t\t\t\tqueue.unshift( \"inprogress\" );\n\t\t\t}\n\n\t\t\t// Clear up the last queue stop function\n\t\t\tdelete hooks.stop;\n\t\t\tfn.call( elem, next, hooks );\n\t\t}\n\n\t\tif ( !startLength && hooks ) {\n\t\t\thooks.empty.fire();\n\t\t}\n\t},\n\n\t// Not public - generate a queueHooks object, or return the current one\n\t_queueHooks: function( elem, type ) {\n\t\tvar key = type + \"queueHooks\";\n\t\treturn dataPriv.get( elem, key ) || dataPriv.access( elem, key, {\n\t\t\tempty: jQuery.Callbacks( \"once memory\" ).add( function() {\n\t\t\t\tdataPriv.remove( elem, [ type + \"queue\", key ] );\n\t\t\t} )\n\t\t} );\n\t}\n} );\n\njQuery.fn.extend( {\n\tqueue: function( type, data ) {\n\t\tvar setter = 2;\n\n\t\tif ( typeof type !== \"string\" ) {\n\t\t\tdata = type;\n\t\t\ttype = \"fx\";\n\t\t\tsetter--;\n\t\t}\n\n\t\tif ( arguments.length < setter ) {\n\t\t\treturn jQuery.queue( this[ 0 ], type );\n\t\t}\n\n\t\treturn data === undefined ?\n\t\t\tthis :\n\t\t\tthis.each( function() {\n\t\t\t\tvar queue = jQuery.queue( this, type, data );\n\n\t\t\t\t// Ensure a hooks for this queue\n\t\t\t\tjQuery._queueHooks( this, type );\n\n\t\t\t\tif ( type === \"fx\" && queue[ 0 ] !== \"inprogress\" ) {\n\t\t\t\t\tjQuery.dequeue( this, type );\n\t\t\t\t}\n\t\t\t} );\n\t},\n\tdequeue: function( type ) {\n\t\treturn this.each( function() {\n\t\t\tjQuery.dequeue( this, type );\n\t\t} );\n\t},\n\tclearQueue: function( type ) {\n\t\treturn this.queue( type || \"fx\", [] );\n\t},\n\n\t// Get a promise resolved when queues of a certain type\n\t// are emptied (fx is the type by default)\n\tpromise: function( type, obj ) {\n\t\tvar tmp,\n\t\t\tcount = 1,\n\t\t\tdefer = jQuery.Deferred(),\n\t\t\telements = this,\n\t\t\ti = this.length,\n\t\t\tresolve = function() {\n\t\t\t\tif ( !( --count ) ) {\n\t\t\t\t\tdefer.resolveWith( elements, [ elements ] );\n\t\t\t\t}\n\t\t\t};\n\n\t\tif ( typeof type !== \"string\" ) {\n\t\t\tobj = type;\n\t\t\ttype = undefined;\n\t\t}\n\t\ttype = type || \"fx\";\n\n\t\twhile ( i-- ) {\n\t\t\ttmp = dataPriv.get( elements[ i ], type + \"queueHooks\" );\n\t\t\tif ( tmp && tmp.empty ) {\n\t\t\t\tcount++;\n\t\t\t\ttmp.empty.add( resolve );\n\t\t\t}\n\t\t}\n\t\tresolve();\n\t\treturn defer.promise( obj );\n\t}\n} );\nvar pnum = ( /[+-]?(?:\\d*\\.|)\\d+(?:[eE][+-]?\\d+|)/ ).source;\n\nvar rcssNum = new RegExp( \"^(?:([+-])=|)(\" + pnum + \")([a-z%]*)$\", \"i\" );\n\n\nvar cssExpand = [ \"Top\", \"Right\", \"Bottom\", \"Left\" ];\n\nvar isHiddenWithinTree = function( elem, el ) {\n\n\t\t// isHiddenWithinTree might be called from jQuery#filter function;\n\t\t// in that case, element will be second argument\n\t\telem = el || elem;\n\n\t\t// Inline style trumps all\n\t\treturn elem.style.display === \"none\" ||\n\t\t\telem.style.display === \"\" &&\n\n\t\t\t// Otherwise, check computed style\n\t\t\t// Support: Firefox <=43 - 45\n\t\t\t// Disconnected elements can have computed display: none, so first confirm that elem is\n\t\t\t// in the document.\n\t\t\tjQuery.contains( elem.ownerDocument, elem ) &&\n\n\t\t\tjQuery.css( elem, \"display\" ) === \"none\";\n\t};\n\nvar swap = function( elem, options, callback, args ) {\n\tvar ret, name,\n\t\told = {};\n\n\t// Remember the old values, and insert the new ones\n\tfor ( name in options ) {\n\t\told[ name ] = elem.style[ name ];\n\t\telem.style[ name ] = options[ name ];\n\t}\n\n\tret = callback.apply( elem, args || [] );\n\n\t// Revert the old values\n\tfor ( name in options ) {\n\t\telem.style[ name ] = old[ name ];\n\t}\n\n\treturn ret;\n};\n\n\n\n\nfunction adjustCSS( elem, prop, valueParts, tween ) {\n\tvar adjusted, scale,\n\t\tmaxIterations = 20,\n\t\tcurrentValue = tween ?\n\t\t\tfunction() {\n\t\t\t\treturn tween.cur();\n\t\t\t} :\n\t\t\tfunction() {\n\t\t\t\treturn jQuery.css( elem, prop, \"\" );\n\t\t\t},\n\t\tinitial = currentValue(),\n\t\tunit = valueParts && valueParts[ 3 ] || ( jQuery.cssNumber[ prop ] ? \"\" : \"px\" ),\n\n\t\t// Starting value computation is required for potential unit mismatches\n\t\tinitialInUnit = ( jQuery.cssNumber[ prop ] || unit !== \"px\" && +initial ) &&\n\t\t\trcssNum.exec( jQuery.css( elem, prop ) );\n\n\tif ( initialInUnit && initialInUnit[ 3 ] !== unit ) {\n\n\t\t// Support: Firefox <=54\n\t\t// Halve the iteration target value to prevent interference from CSS upper bounds (gh-2144)\n\t\tinitial = initial / 2;\n\n\t\t// Trust units reported by jQuery.css\n\t\tunit = unit || initialInUnit[ 3 ];\n\n\t\t// Iteratively approximate from a nonzero starting point\n\t\tinitialInUnit = +initial || 1;\n\n\t\twhile ( maxIterations-- ) {\n\n\t\t\t// Evaluate and update our best guess (doubling guesses that zero out).\n\t\t\t// Finish if the scale equals or crosses 1 (making the old*new product non-positive).\n\t\t\tjQuery.style( elem, prop, initialInUnit + unit );\n\t\t\tif ( ( 1 - scale ) * ( 1 - ( scale = currentValue() / initial || 0.5 ) ) <= 0 ) {\n\t\t\t\tmaxIterations = 0;\n\t\t\t}\n\t\t\tinitialInUnit = initialInUnit / scale;\n\n\t\t}\n\n\t\tinitialInUnit = initialInUnit * 2;\n\t\tjQuery.style( elem, prop, initialInUnit + unit );\n\n\t\t// Make sure we update the tween properties later on\n\t\tvalueParts = valueParts || [];\n\t}\n\n\tif ( valueParts ) {\n\t\tinitialInUnit = +initialInUnit || +initial || 0;\n\n\t\t// Apply relative offset (+=/-=) if specified\n\t\tadjusted = valueParts[ 1 ] ?\n\t\t\tinitialInUnit + ( valueParts[ 1 ] + 1 ) * valueParts[ 2 ] :\n\t\t\t+valueParts[ 2 ];\n\t\tif ( tween ) {\n\t\t\ttween.unit = unit;\n\t\t\ttween.start = initialInUnit;\n\t\t\ttween.end = adjusted;\n\t\t}\n\t}\n\treturn adjusted;\n}\n\n\nvar defaultDisplayMap = {};\n\nfunction getDefaultDisplay( elem ) {\n\tvar temp,\n\t\tdoc = elem.ownerDocument,\n\t\tnodeName = elem.nodeName,\n\t\tdisplay = defaultDisplayMap[ nodeName ];\n\n\tif ( display ) {\n\t\treturn display;\n\t}\n\n\ttemp = doc.body.appendChild( doc.createElement( nodeName ) );\n\tdisplay = jQuery.css( temp, \"display\" );\n\n\ttemp.parentNode.removeChild( temp );\n\n\tif ( display === \"none\" ) {\n\t\tdisplay = \"block\";\n\t}\n\tdefaultDisplayMap[ nodeName ] = display;\n\n\treturn display;\n}\n\nfunction showHide( elements, show ) {\n\tvar display, elem,\n\t\tvalues = [],\n\t\tindex = 0,\n\t\tlength = elements.length;\n\n\t// Determine new display value for elements that need to change\n\tfor ( ; index < length; index++ ) {\n\t\telem = elements[ index ];\n\t\tif ( !elem.style ) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tdisplay = elem.style.display;\n\t\tif ( show ) {\n\n\t\t\t// Since we force visibility upon cascade-hidden elements, an immediate (and slow)\n\t\t\t// check is required in this first loop unless we have a nonempty display value (either\n\t\t\t// inline or about-to-be-restored)\n\t\t\tif ( display === \"none\" ) {\n\t\t\t\tvalues[ index ] = dataPriv.get( elem, \"display\" ) || null;\n\t\t\t\tif ( !values[ index ] ) {\n\t\t\t\t\telem.style.display = \"\";\n\t\t\t\t}\n\t\t\t}\n\t\t\tif ( elem.style.display === \"\" && isHiddenWithinTree( elem ) ) {\n\t\t\t\tvalues[ index ] = getDefaultDisplay( elem );\n\t\t\t}\n\t\t} else {\n\t\t\tif ( display !== \"none\" ) {\n\t\t\t\tvalues[ index ] = \"none\";\n\n\t\t\t\t// Remember what we're overwriting\n\t\t\t\tdataPriv.set( elem, \"display\", display );\n\t\t\t}\n\t\t}\n\t}\n\n\t// Set the display of the elements in a second loop to avoid constant reflow\n\tfor ( index = 0; index < length; index++ ) {\n\t\tif ( values[ index ] != null ) {\n\t\t\telements[ index ].style.display = values[ index ];\n\t\t}\n\t}\n\n\treturn elements;\n}\n\njQuery.fn.extend( {\n\tshow: function() {\n\t\treturn showHide( this, true );\n\t},\n\thide: function() {\n\t\treturn showHide( this );\n\t},\n\ttoggle: function( state ) {\n\t\tif ( typeof state === \"boolean\" ) {\n\t\t\treturn state ? this.show() : this.hide();\n\t\t}\n\n\t\treturn this.each( function() {\n\t\t\tif ( isHiddenWithinTree( this ) ) {\n\t\t\t\tjQuery( this ).show();\n\t\t\t} else {\n\t\t\t\tjQuery( this ).hide();\n\t\t\t}\n\t\t} );\n\t}\n} );\nvar rcheckableType = ( /^(?:checkbox|radio)$/i );\n\nvar rtagName = ( /<([a-z][^\\/\\0>\\x20\\t\\r\\n\\f]+)/i );\n\nvar rscriptType = ( /^$|^module$|\\/(?:java|ecma)script/i );\n\n\n\n// We have to close these tags to support XHTML (#13200)\nvar wrapMap = {\n\n\t// Support: IE <=9 only\n\toption: [ 1, \"\" ],\n\n\t// XHTML parsers do not magically insert elements in the\n\t// same way that tag soup parsers do. So we cannot shorten\n\t// this by omitting or other required elements.\n\tthead: [ 1, \"\", \"
    \" ],\n\tcol: [ 2, \"\", \"
    \" ],\n\ttr: [ 2, \"\", \"
    \" ],\n\ttd: [ 3, \"\", \"
    \" ],\n\n\t_default: [ 0, \"\", \"\" ]\n};\n\n// Support: IE <=9 only\nwrapMap.optgroup = wrapMap.option;\n\nwrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;\nwrapMap.th = wrapMap.td;\n\n\nfunction getAll( context, tag ) {\n\n\t// Support: IE <=9 - 11 only\n\t// Use typeof to avoid zero-argument method invocation on host objects (#15151)\n\tvar ret;\n\n\tif ( typeof context.getElementsByTagName !== \"undefined\" ) {\n\t\tret = context.getElementsByTagName( tag || \"*\" );\n\n\t} else if ( typeof context.querySelectorAll !== \"undefined\" ) {\n\t\tret = context.querySelectorAll( tag || \"*\" );\n\n\t} else {\n\t\tret = [];\n\t}\n\n\tif ( tag === undefined || tag && nodeName( context, tag ) ) {\n\t\treturn jQuery.merge( [ context ], ret );\n\t}\n\n\treturn ret;\n}\n\n\n// Mark scripts as having already been evaluated\nfunction setGlobalEval( elems, refElements ) {\n\tvar i = 0,\n\t\tl = elems.length;\n\n\tfor ( ; i < l; i++ ) {\n\t\tdataPriv.set(\n\t\t\telems[ i ],\n\t\t\t\"globalEval\",\n\t\t\t!refElements || dataPriv.get( refElements[ i ], \"globalEval\" )\n\t\t);\n\t}\n}\n\n\nvar rhtml = /<|&#?\\w+;/;\n\nfunction buildFragment( elems, context, scripts, selection, ignored ) {\n\tvar elem, tmp, tag, wrap, contains, j,\n\t\tfragment = context.createDocumentFragment(),\n\t\tnodes = [],\n\t\ti = 0,\n\t\tl = elems.length;\n\n\tfor ( ; i < l; i++ ) {\n\t\telem = elems[ i ];\n\n\t\tif ( elem || elem === 0 ) {\n\n\t\t\t// Add nodes directly\n\t\t\tif ( toType( elem ) === \"object\" ) {\n\n\t\t\t\t// Support: Android <=4.0 only, PhantomJS 1 only\n\t\t\t\t// push.apply(_, arraylike) throws on ancient WebKit\n\t\t\t\tjQuery.merge( nodes, elem.nodeType ? [ elem ] : elem );\n\n\t\t\t// Convert non-html into a text node\n\t\t\t} else if ( !rhtml.test( elem ) ) {\n\t\t\t\tnodes.push( context.createTextNode( elem ) );\n\n\t\t\t// Convert html into DOM nodes\n\t\t\t} else {\n\t\t\t\ttmp = tmp || fragment.appendChild( context.createElement( \"div\" ) );\n\n\t\t\t\t// Deserialize a standard representation\n\t\t\t\ttag = ( rtagName.exec( elem ) || [ \"\", \"\" ] )[ 1 ].toLowerCase();\n\t\t\t\twrap = wrapMap[ tag ] || wrapMap._default;\n\t\t\t\ttmp.innerHTML = wrap[ 1 ] + jQuery.htmlPrefilter( elem ) + wrap[ 2 ];\n\n\t\t\t\t// Descend through wrappers to the right content\n\t\t\t\tj = wrap[ 0 ];\n\t\t\t\twhile ( j-- ) {\n\t\t\t\t\ttmp = tmp.lastChild;\n\t\t\t\t}\n\n\t\t\t\t// Support: Android <=4.0 only, PhantomJS 1 only\n\t\t\t\t// push.apply(_, arraylike) throws on ancient WebKit\n\t\t\t\tjQuery.merge( nodes, tmp.childNodes );\n\n\t\t\t\t// Remember the top-level container\n\t\t\t\ttmp = fragment.firstChild;\n\n\t\t\t\t// Ensure the created nodes are orphaned (#12392)\n\t\t\t\ttmp.textContent = \"\";\n\t\t\t}\n\t\t}\n\t}\n\n\t// Remove wrapper from fragment\n\tfragment.textContent = \"\";\n\n\ti = 0;\n\twhile ( ( elem = nodes[ i++ ] ) ) {\n\n\t\t// Skip elements already in the context collection (trac-4087)\n\t\tif ( selection && jQuery.inArray( elem, selection ) > -1 ) {\n\t\t\tif ( ignored ) {\n\t\t\t\tignored.push( elem );\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\n\t\tcontains = jQuery.contains( elem.ownerDocument, elem );\n\n\t\t// Append to fragment\n\t\ttmp = getAll( fragment.appendChild( elem ), \"script\" );\n\n\t\t// Preserve script evaluation history\n\t\tif ( contains ) {\n\t\t\tsetGlobalEval( tmp );\n\t\t}\n\n\t\t// Capture executables\n\t\tif ( scripts ) {\n\t\t\tj = 0;\n\t\t\twhile ( ( elem = tmp[ j++ ] ) ) {\n\t\t\t\tif ( rscriptType.test( elem.type || \"\" ) ) {\n\t\t\t\t\tscripts.push( elem );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn fragment;\n}\n\n\n( function() {\n\tvar fragment = document.createDocumentFragment(),\n\t\tdiv = fragment.appendChild( document.createElement( \"div\" ) ),\n\t\tinput = document.createElement( \"input\" );\n\n\t// Support: Android 4.0 - 4.3 only\n\t// Check state lost if the name is set (#11217)\n\t// Support: Windows Web Apps (WWA)\n\t// `name` and `type` must use .setAttribute for WWA (#14901)\n\tinput.setAttribute( \"type\", \"radio\" );\n\tinput.setAttribute( \"checked\", \"checked\" );\n\tinput.setAttribute( \"name\", \"t\" );\n\n\tdiv.appendChild( input );\n\n\t// Support: Android <=4.1 only\n\t// Older WebKit doesn't clone checked state correctly in fragments\n\tsupport.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked;\n\n\t// Support: IE <=11 only\n\t// Make sure textarea (and checkbox) defaultValue is properly cloned\n\tdiv.innerHTML = \"\";\n\tsupport.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue;\n} )();\nvar documentElement = document.documentElement;\n\n\n\nvar\n\trkeyEvent = /^key/,\n\trmouseEvent = /^(?:mouse|pointer|contextmenu|drag|drop)|click/,\n\trtypenamespace = /^([^.]*)(?:\\.(.+)|)/;\n\nfunction returnTrue() {\n\treturn true;\n}\n\nfunction returnFalse() {\n\treturn false;\n}\n\n// Support: IE <=9 only\n// See #13393 for more info\nfunction safeActiveElement() {\n\ttry {\n\t\treturn document.activeElement;\n\t} catch ( err ) { }\n}\n\nfunction on( elem, types, selector, data, fn, one ) {\n\tvar origFn, type;\n\n\t// Types can be a map of types/handlers\n\tif ( typeof types === \"object\" ) {\n\n\t\t// ( types-Object, selector, data )\n\t\tif ( typeof selector !== \"string\" ) {\n\n\t\t\t// ( types-Object, data )\n\t\t\tdata = data || selector;\n\t\t\tselector = undefined;\n\t\t}\n\t\tfor ( type in types ) {\n\t\t\ton( elem, type, selector, data, types[ type ], one );\n\t\t}\n\t\treturn elem;\n\t}\n\n\tif ( data == null && fn == null ) {\n\n\t\t// ( types, fn )\n\t\tfn = selector;\n\t\tdata = selector = undefined;\n\t} else if ( fn == null ) {\n\t\tif ( typeof selector === \"string\" ) {\n\n\t\t\t// ( types, selector, fn )\n\t\t\tfn = data;\n\t\t\tdata = undefined;\n\t\t} else {\n\n\t\t\t// ( types, data, fn )\n\t\t\tfn = data;\n\t\t\tdata = selector;\n\t\t\tselector = undefined;\n\t\t}\n\t}\n\tif ( fn === false ) {\n\t\tfn = returnFalse;\n\t} else if ( !fn ) {\n\t\treturn elem;\n\t}\n\n\tif ( one === 1 ) {\n\t\torigFn = fn;\n\t\tfn = function( event ) {\n\n\t\t\t// Can use an empty set, since event contains the info\n\t\t\tjQuery().off( event );\n\t\t\treturn origFn.apply( this, arguments );\n\t\t};\n\n\t\t// Use same guid so caller can remove using origFn\n\t\tfn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ );\n\t}\n\treturn elem.each( function() {\n\t\tjQuery.event.add( this, types, fn, data, selector );\n\t} );\n}\n\n/*\n * Helper functions for managing events -- not part of the public interface.\n * Props to Dean Edwards' addEvent library for many of the ideas.\n */\njQuery.event = {\n\n\tglobal: {},\n\n\tadd: function( elem, types, handler, data, selector ) {\n\n\t\tvar handleObjIn, eventHandle, tmp,\n\t\t\tevents, t, handleObj,\n\t\t\tspecial, handlers, type, namespaces, origType,\n\t\t\telemData = dataPriv.get( elem );\n\n\t\t// Don't attach events to noData or text/comment nodes (but allow plain objects)\n\t\tif ( !elemData ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Caller can pass in an object of custom data in lieu of the handler\n\t\tif ( handler.handler ) {\n\t\t\thandleObjIn = handler;\n\t\t\thandler = handleObjIn.handler;\n\t\t\tselector = handleObjIn.selector;\n\t\t}\n\n\t\t// Ensure that invalid selectors throw exceptions at attach time\n\t\t// Evaluate against documentElement in case elem is a non-element node (e.g., document)\n\t\tif ( selector ) {\n\t\t\tjQuery.find.matchesSelector( documentElement, selector );\n\t\t}\n\n\t\t// Make sure that the handler has a unique ID, used to find/remove it later\n\t\tif ( !handler.guid ) {\n\t\t\thandler.guid = jQuery.guid++;\n\t\t}\n\n\t\t// Init the element's event structure and main handler, if this is the first\n\t\tif ( !( events = elemData.events ) ) {\n\t\t\tevents = elemData.events = {};\n\t\t}\n\t\tif ( !( eventHandle = elemData.handle ) ) {\n\t\t\teventHandle = elemData.handle = function( e ) {\n\n\t\t\t\t// Discard the second event of a jQuery.event.trigger() and\n\t\t\t\t// when an event is called after a page has unloaded\n\t\t\t\treturn typeof jQuery !== \"undefined\" && jQuery.event.triggered !== e.type ?\n\t\t\t\t\tjQuery.event.dispatch.apply( elem, arguments ) : undefined;\n\t\t\t};\n\t\t}\n\n\t\t// Handle multiple events separated by a space\n\t\ttypes = ( types || \"\" ).match( rnothtmlwhite ) || [ \"\" ];\n\t\tt = types.length;\n\t\twhile ( t-- ) {\n\t\t\ttmp = rtypenamespace.exec( types[ t ] ) || [];\n\t\t\ttype = origType = tmp[ 1 ];\n\t\t\tnamespaces = ( tmp[ 2 ] || \"\" ).split( \".\" ).sort();\n\n\t\t\t// There *must* be a type, no attaching namespace-only handlers\n\t\t\tif ( !type ) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// If event changes its type, use the special event handlers for the changed type\n\t\t\tspecial = jQuery.event.special[ type ] || {};\n\n\t\t\t// If selector defined, determine special event api type, otherwise given type\n\t\t\ttype = ( selector ? special.delegateType : special.bindType ) || type;\n\n\t\t\t// Update special based on newly reset type\n\t\t\tspecial = jQuery.event.special[ type ] || {};\n\n\t\t\t// handleObj is passed to all event handlers\n\t\t\thandleObj = jQuery.extend( {\n\t\t\t\ttype: type,\n\t\t\t\torigType: origType,\n\t\t\t\tdata: data,\n\t\t\t\thandler: handler,\n\t\t\t\tguid: handler.guid,\n\t\t\t\tselector: selector,\n\t\t\t\tneedsContext: selector && jQuery.expr.match.needsContext.test( selector ),\n\t\t\t\tnamespace: namespaces.join( \".\" )\n\t\t\t}, handleObjIn );\n\n\t\t\t// Init the event handler queue if we're the first\n\t\t\tif ( !( handlers = events[ type ] ) ) {\n\t\t\t\thandlers = events[ type ] = [];\n\t\t\t\thandlers.delegateCount = 0;\n\n\t\t\t\t// Only use addEventListener if the special events handler returns false\n\t\t\t\tif ( !special.setup ||\n\t\t\t\t\tspecial.setup.call( elem, data, namespaces, eventHandle ) === false ) {\n\n\t\t\t\t\tif ( elem.addEventListener ) {\n\t\t\t\t\t\telem.addEventListener( type, eventHandle );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif ( special.add ) {\n\t\t\t\tspecial.add.call( elem, handleObj );\n\n\t\t\t\tif ( !handleObj.handler.guid ) {\n\t\t\t\t\thandleObj.handler.guid = handler.guid;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Add to the element's handler list, delegates in front\n\t\t\tif ( selector ) {\n\t\t\t\thandlers.splice( handlers.delegateCount++, 0, handleObj );\n\t\t\t} else {\n\t\t\t\thandlers.push( handleObj );\n\t\t\t}\n\n\t\t\t// Keep track of which events have ever been used, for event optimization\n\t\t\tjQuery.event.global[ type ] = true;\n\t\t}\n\n\t},\n\n\t// Detach an event or set of events from an element\n\tremove: function( elem, types, handler, selector, mappedTypes ) {\n\n\t\tvar j, origCount, tmp,\n\t\t\tevents, t, handleObj,\n\t\t\tspecial, handlers, type, namespaces, origType,\n\t\t\telemData = dataPriv.hasData( elem ) && dataPriv.get( elem );\n\n\t\tif ( !elemData || !( events = elemData.events ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Once for each type.namespace in types; type may be omitted\n\t\ttypes = ( types || \"\" ).match( rnothtmlwhite ) || [ \"\" ];\n\t\tt = types.length;\n\t\twhile ( t-- ) {\n\t\t\ttmp = rtypenamespace.exec( types[ t ] ) || [];\n\t\t\ttype = origType = tmp[ 1 ];\n\t\t\tnamespaces = ( tmp[ 2 ] || \"\" ).split( \".\" ).sort();\n\n\t\t\t// Unbind all events (on this namespace, if provided) for the element\n\t\t\tif ( !type ) {\n\t\t\t\tfor ( type in events ) {\n\t\t\t\t\tjQuery.event.remove( elem, type + types[ t ], handler, selector, true );\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tspecial = jQuery.event.special[ type ] || {};\n\t\t\ttype = ( selector ? special.delegateType : special.bindType ) || type;\n\t\t\thandlers = events[ type ] || [];\n\t\t\ttmp = tmp[ 2 ] &&\n\t\t\t\tnew RegExp( \"(^|\\\\.)\" + namespaces.join( \"\\\\.(?:.*\\\\.|)\" ) + \"(\\\\.|$)\" );\n\n\t\t\t// Remove matching events\n\t\t\torigCount = j = handlers.length;\n\t\t\twhile ( j-- ) {\n\t\t\t\thandleObj = handlers[ j ];\n\n\t\t\t\tif ( ( mappedTypes || origType === handleObj.origType ) &&\n\t\t\t\t\t( !handler || handler.guid === handleObj.guid ) &&\n\t\t\t\t\t( !tmp || tmp.test( handleObj.namespace ) ) &&\n\t\t\t\t\t( !selector || selector === handleObj.selector ||\n\t\t\t\t\t\tselector === \"**\" && handleObj.selector ) ) {\n\t\t\t\t\thandlers.splice( j, 1 );\n\n\t\t\t\t\tif ( handleObj.selector ) {\n\t\t\t\t\t\thandlers.delegateCount--;\n\t\t\t\t\t}\n\t\t\t\t\tif ( special.remove ) {\n\t\t\t\t\t\tspecial.remove.call( elem, handleObj );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Remove generic event handler if we removed something and no more handlers exist\n\t\t\t// (avoids potential for endless recursion during removal of special event handlers)\n\t\t\tif ( origCount && !handlers.length ) {\n\t\t\t\tif ( !special.teardown ||\n\t\t\t\t\tspecial.teardown.call( elem, namespaces, elemData.handle ) === false ) {\n\n\t\t\t\t\tjQuery.removeEvent( elem, type, elemData.handle );\n\t\t\t\t}\n\n\t\t\t\tdelete events[ type ];\n\t\t\t}\n\t\t}\n\n\t\t// Remove data and the expando if it's no longer used\n\t\tif ( jQuery.isEmptyObject( events ) ) {\n\t\t\tdataPriv.remove( elem, \"handle events\" );\n\t\t}\n\t},\n\n\tdispatch: function( nativeEvent ) {\n\n\t\t// Make a writable jQuery.Event from the native event object\n\t\tvar event = jQuery.event.fix( nativeEvent );\n\n\t\tvar i, j, ret, matched, handleObj, handlerQueue,\n\t\t\targs = new Array( arguments.length ),\n\t\t\thandlers = ( dataPriv.get( this, \"events\" ) || {} )[ event.type ] || [],\n\t\t\tspecial = jQuery.event.special[ event.type ] || {};\n\n\t\t// Use the fix-ed jQuery.Event rather than the (read-only) native event\n\t\targs[ 0 ] = event;\n\n\t\tfor ( i = 1; i < arguments.length; i++ ) {\n\t\t\targs[ i ] = arguments[ i ];\n\t\t}\n\n\t\tevent.delegateTarget = this;\n\n\t\t// Call the preDispatch hook for the mapped type, and let it bail if desired\n\t\tif ( special.preDispatch && special.preDispatch.call( this, event ) === false ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Determine handlers\n\t\thandlerQueue = jQuery.event.handlers.call( this, event, handlers );\n\n\t\t// Run delegates first; they may want to stop propagation beneath us\n\t\ti = 0;\n\t\twhile ( ( matched = handlerQueue[ i++ ] ) && !event.isPropagationStopped() ) {\n\t\t\tevent.currentTarget = matched.elem;\n\n\t\t\tj = 0;\n\t\t\twhile ( ( handleObj = matched.handlers[ j++ ] ) &&\n\t\t\t\t!event.isImmediatePropagationStopped() ) {\n\n\t\t\t\t// Triggered event must either 1) have no namespace, or 2) have namespace(s)\n\t\t\t\t// a subset or equal to those in the bound event (both can have no namespace).\n\t\t\t\tif ( !event.rnamespace || event.rnamespace.test( handleObj.namespace ) ) {\n\n\t\t\t\t\tevent.handleObj = handleObj;\n\t\t\t\t\tevent.data = handleObj.data;\n\n\t\t\t\t\tret = ( ( jQuery.event.special[ handleObj.origType ] || {} ).handle ||\n\t\t\t\t\t\thandleObj.handler ).apply( matched.elem, args );\n\n\t\t\t\t\tif ( ret !== undefined ) {\n\t\t\t\t\t\tif ( ( event.result = ret ) === false ) {\n\t\t\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\t\t\tevent.stopPropagation();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Call the postDispatch hook for the mapped type\n\t\tif ( special.postDispatch ) {\n\t\t\tspecial.postDispatch.call( this, event );\n\t\t}\n\n\t\treturn event.result;\n\t},\n\n\thandlers: function( event, handlers ) {\n\t\tvar i, handleObj, sel, matchedHandlers, matchedSelectors,\n\t\t\thandlerQueue = [],\n\t\t\tdelegateCount = handlers.delegateCount,\n\t\t\tcur = event.target;\n\n\t\t// Find delegate handlers\n\t\tif ( delegateCount &&\n\n\t\t\t// Support: IE <=9\n\t\t\t// Black-hole SVG instance trees (trac-13180)\n\t\t\tcur.nodeType &&\n\n\t\t\t// Support: Firefox <=42\n\t\t\t// Suppress spec-violating clicks indicating a non-primary pointer button (trac-3861)\n\t\t\t// https://www.w3.org/TR/DOM-Level-3-Events/#event-type-click\n\t\t\t// Support: IE 11 only\n\t\t\t// ...but not arrow key \"clicks\" of radio inputs, which can have `button` -1 (gh-2343)\n\t\t\t!( event.type === \"click\" && event.button >= 1 ) ) {\n\n\t\t\tfor ( ; cur !== this; cur = cur.parentNode || this ) {\n\n\t\t\t\t// Don't check non-elements (#13208)\n\t\t\t\t// Don't process clicks on disabled elements (#6911, #8165, #11382, #11764)\n\t\t\t\tif ( cur.nodeType === 1 && !( event.type === \"click\" && cur.disabled === true ) ) {\n\t\t\t\t\tmatchedHandlers = [];\n\t\t\t\t\tmatchedSelectors = {};\n\t\t\t\t\tfor ( i = 0; i < delegateCount; i++ ) {\n\t\t\t\t\t\thandleObj = handlers[ i ];\n\n\t\t\t\t\t\t// Don't conflict with Object.prototype properties (#13203)\n\t\t\t\t\t\tsel = handleObj.selector + \" \";\n\n\t\t\t\t\t\tif ( matchedSelectors[ sel ] === undefined ) {\n\t\t\t\t\t\t\tmatchedSelectors[ sel ] = handleObj.needsContext ?\n\t\t\t\t\t\t\t\tjQuery( sel, this ).index( cur ) > -1 :\n\t\t\t\t\t\t\t\tjQuery.find( sel, this, null, [ cur ] ).length;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif ( matchedSelectors[ sel ] ) {\n\t\t\t\t\t\t\tmatchedHandlers.push( handleObj );\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif ( matchedHandlers.length ) {\n\t\t\t\t\t\thandlerQueue.push( { elem: cur, handlers: matchedHandlers } );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Add the remaining (directly-bound) handlers\n\t\tcur = this;\n\t\tif ( delegateCount < handlers.length ) {\n\t\t\thandlerQueue.push( { elem: cur, handlers: handlers.slice( delegateCount ) } );\n\t\t}\n\n\t\treturn handlerQueue;\n\t},\n\n\taddProp: function( name, hook ) {\n\t\tObject.defineProperty( jQuery.Event.prototype, name, {\n\t\t\tenumerable: true,\n\t\t\tconfigurable: true,\n\n\t\t\tget: isFunction( hook ) ?\n\t\t\t\tfunction() {\n\t\t\t\t\tif ( this.originalEvent ) {\n\t\t\t\t\t\t\treturn hook( this.originalEvent );\n\t\t\t\t\t}\n\t\t\t\t} :\n\t\t\t\tfunction() {\n\t\t\t\t\tif ( this.originalEvent ) {\n\t\t\t\t\t\t\treturn this.originalEvent[ name ];\n\t\t\t\t\t}\n\t\t\t\t},\n\n\t\t\tset: function( value ) {\n\t\t\t\tObject.defineProperty( this, name, {\n\t\t\t\t\tenumerable: true,\n\t\t\t\t\tconfigurable: true,\n\t\t\t\t\twritable: true,\n\t\t\t\t\tvalue: value\n\t\t\t\t} );\n\t\t\t}\n\t\t} );\n\t},\n\n\tfix: function( originalEvent ) {\n\t\treturn originalEvent[ jQuery.expando ] ?\n\t\t\toriginalEvent :\n\t\t\tnew jQuery.Event( originalEvent );\n\t},\n\n\tspecial: {\n\t\tload: {\n\n\t\t\t// Prevent triggered image.load events from bubbling to window.load\n\t\t\tnoBubble: true\n\t\t},\n\t\tfocus: {\n\n\t\t\t// Fire native event if possible so blur/focus sequence is correct\n\t\t\ttrigger: function() {\n\t\t\t\tif ( this !== safeActiveElement() && this.focus ) {\n\t\t\t\t\tthis.focus();\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t},\n\t\t\tdelegateType: \"focusin\"\n\t\t},\n\t\tblur: {\n\t\t\ttrigger: function() {\n\t\t\t\tif ( this === safeActiveElement() && this.blur ) {\n\t\t\t\t\tthis.blur();\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t},\n\t\t\tdelegateType: \"focusout\"\n\t\t},\n\t\tclick: {\n\n\t\t\t// For checkbox, fire native event so checked state will be right\n\t\t\ttrigger: function() {\n\t\t\t\tif ( this.type === \"checkbox\" && this.click && nodeName( this, \"input\" ) ) {\n\t\t\t\t\tthis.click();\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t},\n\n\t\t\t// For cross-browser consistency, don't fire native .click() on links\n\t\t\t_default: function( event ) {\n\t\t\t\treturn nodeName( event.target, \"a\" );\n\t\t\t}\n\t\t},\n\n\t\tbeforeunload: {\n\t\t\tpostDispatch: function( event ) {\n\n\t\t\t\t// Support: Firefox 20+\n\t\t\t\t// Firefox doesn't alert if the returnValue field is not set.\n\t\t\t\tif ( event.result !== undefined && event.originalEvent ) {\n\t\t\t\t\tevent.originalEvent.returnValue = event.result;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n};\n\njQuery.removeEvent = function( elem, type, handle ) {\n\n\t// This \"if\" is needed for plain objects\n\tif ( elem.removeEventListener ) {\n\t\telem.removeEventListener( type, handle );\n\t}\n};\n\njQuery.Event = function( src, props ) {\n\n\t// Allow instantiation without the 'new' keyword\n\tif ( !( this instanceof jQuery.Event ) ) {\n\t\treturn new jQuery.Event( src, props );\n\t}\n\n\t// Event object\n\tif ( src && src.type ) {\n\t\tthis.originalEvent = src;\n\t\tthis.type = src.type;\n\n\t\t// Events bubbling up the document may have been marked as prevented\n\t\t// by a handler lower down the tree; reflect the correct value.\n\t\tthis.isDefaultPrevented = src.defaultPrevented ||\n\t\t\t\tsrc.defaultPrevented === undefined &&\n\n\t\t\t\t// Support: Android <=2.3 only\n\t\t\t\tsrc.returnValue === false ?\n\t\t\treturnTrue :\n\t\t\treturnFalse;\n\n\t\t// Create target properties\n\t\t// Support: Safari <=6 - 7 only\n\t\t// Target should not be a text node (#504, #13143)\n\t\tthis.target = ( src.target && src.target.nodeType === 3 ) ?\n\t\t\tsrc.target.parentNode :\n\t\t\tsrc.target;\n\n\t\tthis.currentTarget = src.currentTarget;\n\t\tthis.relatedTarget = src.relatedTarget;\n\n\t// Event type\n\t} else {\n\t\tthis.type = src;\n\t}\n\n\t// Put explicitly provided properties onto the event object\n\tif ( props ) {\n\t\tjQuery.extend( this, props );\n\t}\n\n\t// Create a timestamp if incoming event doesn't have one\n\tthis.timeStamp = src && src.timeStamp || Date.now();\n\n\t// Mark it as fixed\n\tthis[ jQuery.expando ] = true;\n};\n\n// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding\n// https://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html\njQuery.Event.prototype = {\n\tconstructor: jQuery.Event,\n\tisDefaultPrevented: returnFalse,\n\tisPropagationStopped: returnFalse,\n\tisImmediatePropagationStopped: returnFalse,\n\tisSimulated: false,\n\n\tpreventDefault: function() {\n\t\tvar e = this.originalEvent;\n\n\t\tthis.isDefaultPrevented = returnTrue;\n\n\t\tif ( e && !this.isSimulated ) {\n\t\t\te.preventDefault();\n\t\t}\n\t},\n\tstopPropagation: function() {\n\t\tvar e = this.originalEvent;\n\n\t\tthis.isPropagationStopped = returnTrue;\n\n\t\tif ( e && !this.isSimulated ) {\n\t\t\te.stopPropagation();\n\t\t}\n\t},\n\tstopImmediatePropagation: function() {\n\t\tvar e = this.originalEvent;\n\n\t\tthis.isImmediatePropagationStopped = returnTrue;\n\n\t\tif ( e && !this.isSimulated ) {\n\t\t\te.stopImmediatePropagation();\n\t\t}\n\n\t\tthis.stopPropagation();\n\t}\n};\n\n// Includes all common event props including KeyEvent and MouseEvent specific props\njQuery.each( {\n\taltKey: true,\n\tbubbles: true,\n\tcancelable: true,\n\tchangedTouches: true,\n\tctrlKey: true,\n\tdetail: true,\n\teventPhase: true,\n\tmetaKey: true,\n\tpageX: true,\n\tpageY: true,\n\tshiftKey: true,\n\tview: true,\n\t\"char\": true,\n\tcharCode: true,\n\tkey: true,\n\tkeyCode: true,\n\tbutton: true,\n\tbuttons: true,\n\tclientX: true,\n\tclientY: true,\n\toffsetX: true,\n\toffsetY: true,\n\tpointerId: true,\n\tpointerType: true,\n\tscreenX: true,\n\tscreenY: true,\n\ttargetTouches: true,\n\ttoElement: true,\n\ttouches: true,\n\n\twhich: function( event ) {\n\t\tvar button = event.button;\n\n\t\t// Add which for key events\n\t\tif ( event.which == null && rkeyEvent.test( event.type ) ) {\n\t\t\treturn event.charCode != null ? event.charCode : event.keyCode;\n\t\t}\n\n\t\t// Add which for click: 1 === left; 2 === middle; 3 === right\n\t\tif ( !event.which && button !== undefined && rmouseEvent.test( event.type ) ) {\n\t\t\tif ( button & 1 ) {\n\t\t\t\treturn 1;\n\t\t\t}\n\n\t\t\tif ( button & 2 ) {\n\t\t\t\treturn 3;\n\t\t\t}\n\n\t\t\tif ( button & 4 ) {\n\t\t\t\treturn 2;\n\t\t\t}\n\n\t\t\treturn 0;\n\t\t}\n\n\t\treturn event.which;\n\t}\n}, jQuery.event.addProp );\n\n// Create mouseenter/leave events using mouseover/out and event-time checks\n// so that event delegation works in jQuery.\n// Do the same for pointerenter/pointerleave and pointerover/pointerout\n//\n// Support: Safari 7 only\n// Safari sends mouseenter too often; see:\n// https://bugs.chromium.org/p/chromium/issues/detail?id=470258\n// for the description of the bug (it existed in older Chrome versions as well).\njQuery.each( {\n\tmouseenter: \"mouseover\",\n\tmouseleave: \"mouseout\",\n\tpointerenter: \"pointerover\",\n\tpointerleave: \"pointerout\"\n}, function( orig, fix ) {\n\tjQuery.event.special[ orig ] = {\n\t\tdelegateType: fix,\n\t\tbindType: fix,\n\n\t\thandle: function( event ) {\n\t\t\tvar ret,\n\t\t\t\ttarget = this,\n\t\t\t\trelated = event.relatedTarget,\n\t\t\t\thandleObj = event.handleObj;\n\n\t\t\t// For mouseenter/leave call the handler if related is outside the target.\n\t\t\t// NB: No relatedTarget if the mouse left/entered the browser window\n\t\t\tif ( !related || ( related !== target && !jQuery.contains( target, related ) ) ) {\n\t\t\t\tevent.type = handleObj.origType;\n\t\t\t\tret = handleObj.handler.apply( this, arguments );\n\t\t\t\tevent.type = fix;\n\t\t\t}\n\t\t\treturn ret;\n\t\t}\n\t};\n} );\n\njQuery.fn.extend( {\n\n\ton: function( types, selector, data, fn ) {\n\t\treturn on( this, types, selector, data, fn );\n\t},\n\tone: function( types, selector, data, fn ) {\n\t\treturn on( this, types, selector, data, fn, 1 );\n\t},\n\toff: function( types, selector, fn ) {\n\t\tvar handleObj, type;\n\t\tif ( types && types.preventDefault && types.handleObj ) {\n\n\t\t\t// ( event ) dispatched jQuery.Event\n\t\t\thandleObj = types.handleObj;\n\t\t\tjQuery( types.delegateTarget ).off(\n\t\t\t\thandleObj.namespace ?\n\t\t\t\t\thandleObj.origType + \".\" + handleObj.namespace :\n\t\t\t\t\thandleObj.origType,\n\t\t\t\thandleObj.selector,\n\t\t\t\thandleObj.handler\n\t\t\t);\n\t\t\treturn this;\n\t\t}\n\t\tif ( typeof types === \"object\" ) {\n\n\t\t\t// ( types-object [, selector] )\n\t\t\tfor ( type in types ) {\n\t\t\t\tthis.off( type, selector, types[ type ] );\n\t\t\t}\n\t\t\treturn this;\n\t\t}\n\t\tif ( selector === false || typeof selector === \"function\" ) {\n\n\t\t\t// ( types [, fn] )\n\t\t\tfn = selector;\n\t\t\tselector = undefined;\n\t\t}\n\t\tif ( fn === false ) {\n\t\t\tfn = returnFalse;\n\t\t}\n\t\treturn this.each( function() {\n\t\t\tjQuery.event.remove( this, types, fn, selector );\n\t\t} );\n\t}\n} );\n\n\nvar\n\n\t/* eslint-disable max-len */\n\n\t// See https://github.com/eslint/eslint/issues/3229\n\trxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([a-z][^\\/\\0>\\x20\\t\\r\\n\\f]*)[^>]*)\\/>/gi,\n\n\t/* eslint-enable */\n\n\t// Support: IE <=10 - 11, Edge 12 - 13 only\n\t// In IE/Edge using regex groups here causes severe slowdowns.\n\t// See https://connect.microsoft.com/IE/feedback/details/1736512/\n\trnoInnerhtml = /\\s*$/g;\n\n// Prefer a tbody over its parent table for containing new rows\nfunction manipulationTarget( elem, content ) {\n\tif ( nodeName( elem, \"table\" ) &&\n\t\tnodeName( content.nodeType !== 11 ? content : content.firstChild, \"tr\" ) ) {\n\n\t\treturn jQuery( elem ).children( \"tbody\" )[ 0 ] || elem;\n\t}\n\n\treturn elem;\n}\n\n// Replace/restore the type attribute of script elements for safe DOM manipulation\nfunction disableScript( elem ) {\n\telem.type = ( elem.getAttribute( \"type\" ) !== null ) + \"/\" + elem.type;\n\treturn elem;\n}\nfunction restoreScript( elem ) {\n\tif ( ( elem.type || \"\" ).slice( 0, 5 ) === \"true/\" ) {\n\t\telem.type = elem.type.slice( 5 );\n\t} else {\n\t\telem.removeAttribute( \"type\" );\n\t}\n\n\treturn elem;\n}\n\nfunction cloneCopyEvent( src, dest ) {\n\tvar i, l, type, pdataOld, pdataCur, udataOld, udataCur, events;\n\n\tif ( dest.nodeType !== 1 ) {\n\t\treturn;\n\t}\n\n\t// 1. Copy private data: events, handlers, etc.\n\tif ( dataPriv.hasData( src ) ) {\n\t\tpdataOld = dataPriv.access( src );\n\t\tpdataCur = dataPriv.set( dest, pdataOld );\n\t\tevents = pdataOld.events;\n\n\t\tif ( events ) {\n\t\t\tdelete pdataCur.handle;\n\t\t\tpdataCur.events = {};\n\n\t\t\tfor ( type in events ) {\n\t\t\t\tfor ( i = 0, l = events[ type ].length; i < l; i++ ) {\n\t\t\t\t\tjQuery.event.add( dest, type, events[ type ][ i ] );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// 2. Copy user data\n\tif ( dataUser.hasData( src ) ) {\n\t\tudataOld = dataUser.access( src );\n\t\tudataCur = jQuery.extend( {}, udataOld );\n\n\t\tdataUser.set( dest, udataCur );\n\t}\n}\n\n// Fix IE bugs, see support tests\nfunction fixInput( src, dest ) {\n\tvar nodeName = dest.nodeName.toLowerCase();\n\n\t// Fails to persist the checked state of a cloned checkbox or radio button.\n\tif ( nodeName === \"input\" && rcheckableType.test( src.type ) ) {\n\t\tdest.checked = src.checked;\n\n\t// Fails to return the selected option to the default selected state when cloning options\n\t} else if ( nodeName === \"input\" || nodeName === \"textarea\" ) {\n\t\tdest.defaultValue = src.defaultValue;\n\t}\n}\n\nfunction domManip( collection, args, callback, ignored ) {\n\n\t// Flatten any nested arrays\n\targs = concat.apply( [], args );\n\n\tvar fragment, first, scripts, hasScripts, node, doc,\n\t\ti = 0,\n\t\tl = collection.length,\n\t\tiNoClone = l - 1,\n\t\tvalue = args[ 0 ],\n\t\tvalueIsFunction = isFunction( value );\n\n\t// We can't cloneNode fragments that contain checked, in WebKit\n\tif ( valueIsFunction ||\n\t\t\t( l > 1 && typeof value === \"string\" &&\n\t\t\t\t!support.checkClone && rchecked.test( value ) ) ) {\n\t\treturn collection.each( function( index ) {\n\t\t\tvar self = collection.eq( index );\n\t\t\tif ( valueIsFunction ) {\n\t\t\t\targs[ 0 ] = value.call( this, index, self.html() );\n\t\t\t}\n\t\t\tdomManip( self, args, callback, ignored );\n\t\t} );\n\t}\n\n\tif ( l ) {\n\t\tfragment = buildFragment( args, collection[ 0 ].ownerDocument, false, collection, ignored );\n\t\tfirst = fragment.firstChild;\n\n\t\tif ( fragment.childNodes.length === 1 ) {\n\t\t\tfragment = first;\n\t\t}\n\n\t\t// Require either new content or an interest in ignored elements to invoke the callback\n\t\tif ( first || ignored ) {\n\t\t\tscripts = jQuery.map( getAll( fragment, \"script\" ), disableScript );\n\t\t\thasScripts = scripts.length;\n\n\t\t\t// Use the original fragment for the last item\n\t\t\t// instead of the first because it can end up\n\t\t\t// being emptied incorrectly in certain situations (#8070).\n\t\t\tfor ( ; i < l; i++ ) {\n\t\t\t\tnode = fragment;\n\n\t\t\t\tif ( i !== iNoClone ) {\n\t\t\t\t\tnode = jQuery.clone( node, true, true );\n\n\t\t\t\t\t// Keep references to cloned scripts for later restoration\n\t\t\t\t\tif ( hasScripts ) {\n\n\t\t\t\t\t\t// Support: Android <=4.0 only, PhantomJS 1 only\n\t\t\t\t\t\t// push.apply(_, arraylike) throws on ancient WebKit\n\t\t\t\t\t\tjQuery.merge( scripts, getAll( node, \"script\" ) );\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tcallback.call( collection[ i ], node, i );\n\t\t\t}\n\n\t\t\tif ( hasScripts ) {\n\t\t\t\tdoc = scripts[ scripts.length - 1 ].ownerDocument;\n\n\t\t\t\t// Reenable scripts\n\t\t\t\tjQuery.map( scripts, restoreScript );\n\n\t\t\t\t// Evaluate executable scripts on first document insertion\n\t\t\t\tfor ( i = 0; i < hasScripts; i++ ) {\n\t\t\t\t\tnode = scripts[ i ];\n\t\t\t\t\tif ( rscriptType.test( node.type || \"\" ) &&\n\t\t\t\t\t\t!dataPriv.access( node, \"globalEval\" ) &&\n\t\t\t\t\t\tjQuery.contains( doc, node ) ) {\n\n\t\t\t\t\t\tif ( node.src && ( node.type || \"\" ).toLowerCase() !== \"module\" ) {\n\n\t\t\t\t\t\t\t// Optional AJAX dependency, but won't run scripts if not present\n\t\t\t\t\t\t\tif ( jQuery._evalUrl ) {\n\t\t\t\t\t\t\t\tjQuery._evalUrl( node.src );\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tDOMEval( node.textContent.replace( rcleanScript, \"\" ), doc, node );\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn collection;\n}\n\nfunction remove( elem, selector, keepData ) {\n\tvar node,\n\t\tnodes = selector ? jQuery.filter( selector, elem ) : elem,\n\t\ti = 0;\n\n\tfor ( ; ( node = nodes[ i ] ) != null; i++ ) {\n\t\tif ( !keepData && node.nodeType === 1 ) {\n\t\t\tjQuery.cleanData( getAll( node ) );\n\t\t}\n\n\t\tif ( node.parentNode ) {\n\t\t\tif ( keepData && jQuery.contains( node.ownerDocument, node ) ) {\n\t\t\t\tsetGlobalEval( getAll( node, \"script\" ) );\n\t\t\t}\n\t\t\tnode.parentNode.removeChild( node );\n\t\t}\n\t}\n\n\treturn elem;\n}\n\njQuery.extend( {\n\thtmlPrefilter: function( html ) {\n\t\treturn html.replace( rxhtmlTag, \"<$1>\" );\n\t},\n\n\tclone: function( elem, dataAndEvents, deepDataAndEvents ) {\n\t\tvar i, l, srcElements, destElements,\n\t\t\tclone = elem.cloneNode( true ),\n\t\t\tinPage = jQuery.contains( elem.ownerDocument, elem );\n\n\t\t// Fix IE cloning issues\n\t\tif ( !support.noCloneChecked && ( elem.nodeType === 1 || elem.nodeType === 11 ) &&\n\t\t\t\t!jQuery.isXMLDoc( elem ) ) {\n\n\t\t\t// We eschew Sizzle here for performance reasons: https://jsperf.com/getall-vs-sizzle/2\n\t\t\tdestElements = getAll( clone );\n\t\t\tsrcElements = getAll( elem );\n\n\t\t\tfor ( i = 0, l = srcElements.length; i < l; i++ ) {\n\t\t\t\tfixInput( srcElements[ i ], destElements[ i ] );\n\t\t\t}\n\t\t}\n\n\t\t// Copy the events from the original to the clone\n\t\tif ( dataAndEvents ) {\n\t\t\tif ( deepDataAndEvents ) {\n\t\t\t\tsrcElements = srcElements || getAll( elem );\n\t\t\t\tdestElements = destElements || getAll( clone );\n\n\t\t\t\tfor ( i = 0, l = srcElements.length; i < l; i++ ) {\n\t\t\t\t\tcloneCopyEvent( srcElements[ i ], destElements[ i ] );\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tcloneCopyEvent( elem, clone );\n\t\t\t}\n\t\t}\n\n\t\t// Preserve script evaluation history\n\t\tdestElements = getAll( clone, \"script\" );\n\t\tif ( destElements.length > 0 ) {\n\t\t\tsetGlobalEval( destElements, !inPage && getAll( elem, \"script\" ) );\n\t\t}\n\n\t\t// Return the cloned set\n\t\treturn clone;\n\t},\n\n\tcleanData: function( elems ) {\n\t\tvar data, elem, type,\n\t\t\tspecial = jQuery.event.special,\n\t\t\ti = 0;\n\n\t\tfor ( ; ( elem = elems[ i ] ) !== undefined; i++ ) {\n\t\t\tif ( acceptData( elem ) ) {\n\t\t\t\tif ( ( data = elem[ dataPriv.expando ] ) ) {\n\t\t\t\t\tif ( data.events ) {\n\t\t\t\t\t\tfor ( type in data.events ) {\n\t\t\t\t\t\t\tif ( special[ type ] ) {\n\t\t\t\t\t\t\t\tjQuery.event.remove( elem, type );\n\n\t\t\t\t\t\t\t// This is a shortcut to avoid jQuery.event.remove's overhead\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tjQuery.removeEvent( elem, type, data.handle );\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// Support: Chrome <=35 - 45+\n\t\t\t\t\t// Assign undefined instead of using delete, see Data#remove\n\t\t\t\t\telem[ dataPriv.expando ] = undefined;\n\t\t\t\t}\n\t\t\t\tif ( elem[ dataUser.expando ] ) {\n\n\t\t\t\t\t// Support: Chrome <=35 - 45+\n\t\t\t\t\t// Assign undefined instead of using delete, see Data#remove\n\t\t\t\t\telem[ dataUser.expando ] = undefined;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n} );\n\njQuery.fn.extend( {\n\tdetach: function( selector ) {\n\t\treturn remove( this, selector, true );\n\t},\n\n\tremove: function( selector ) {\n\t\treturn remove( this, selector );\n\t},\n\n\ttext: function( value ) {\n\t\treturn access( this, function( value ) {\n\t\t\treturn value === undefined ?\n\t\t\t\tjQuery.text( this ) :\n\t\t\t\tthis.empty().each( function() {\n\t\t\t\t\tif ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {\n\t\t\t\t\t\tthis.textContent = value;\n\t\t\t\t\t}\n\t\t\t\t} );\n\t\t}, null, value, arguments.length );\n\t},\n\n\tappend: function() {\n\t\treturn domManip( this, arguments, function( elem ) {\n\t\t\tif ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {\n\t\t\t\tvar target = manipulationTarget( this, elem );\n\t\t\t\ttarget.appendChild( elem );\n\t\t\t}\n\t\t} );\n\t},\n\n\tprepend: function() {\n\t\treturn domManip( this, arguments, function( elem ) {\n\t\t\tif ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {\n\t\t\t\tvar target = manipulationTarget( this, elem );\n\t\t\t\ttarget.insertBefore( elem, target.firstChild );\n\t\t\t}\n\t\t} );\n\t},\n\n\tbefore: function() {\n\t\treturn domManip( this, arguments, function( elem ) {\n\t\t\tif ( this.parentNode ) {\n\t\t\t\tthis.parentNode.insertBefore( elem, this );\n\t\t\t}\n\t\t} );\n\t},\n\n\tafter: function() {\n\t\treturn domManip( this, arguments, function( elem ) {\n\t\t\tif ( this.parentNode ) {\n\t\t\t\tthis.parentNode.insertBefore( elem, this.nextSibling );\n\t\t\t}\n\t\t} );\n\t},\n\n\tempty: function() {\n\t\tvar elem,\n\t\t\ti = 0;\n\n\t\tfor ( ; ( elem = this[ i ] ) != null; i++ ) {\n\t\t\tif ( elem.nodeType === 1 ) {\n\n\t\t\t\t// Prevent memory leaks\n\t\t\t\tjQuery.cleanData( getAll( elem, false ) );\n\n\t\t\t\t// Remove any remaining nodes\n\t\t\t\telem.textContent = \"\";\n\t\t\t}\n\t\t}\n\n\t\treturn this;\n\t},\n\n\tclone: function( dataAndEvents, deepDataAndEvents ) {\n\t\tdataAndEvents = dataAndEvents == null ? false : dataAndEvents;\n\t\tdeepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents;\n\n\t\treturn this.map( function() {\n\t\t\treturn jQuery.clone( this, dataAndEvents, deepDataAndEvents );\n\t\t} );\n\t},\n\n\thtml: function( value ) {\n\t\treturn access( this, function( value ) {\n\t\t\tvar elem = this[ 0 ] || {},\n\t\t\t\ti = 0,\n\t\t\t\tl = this.length;\n\n\t\t\tif ( value === undefined && elem.nodeType === 1 ) {\n\t\t\t\treturn elem.innerHTML;\n\t\t\t}\n\n\t\t\t// See if we can take a shortcut and just use innerHTML\n\t\t\tif ( typeof value === \"string\" && !rnoInnerhtml.test( value ) &&\n\t\t\t\t!wrapMap[ ( rtagName.exec( value ) || [ \"\", \"\" ] )[ 1 ].toLowerCase() ] ) {\n\n\t\t\t\tvalue = jQuery.htmlPrefilter( value );\n\n\t\t\t\ttry {\n\t\t\t\t\tfor ( ; i < l; i++ ) {\n\t\t\t\t\t\telem = this[ i ] || {};\n\n\t\t\t\t\t\t// Remove element nodes and prevent memory leaks\n\t\t\t\t\t\tif ( elem.nodeType === 1 ) {\n\t\t\t\t\t\t\tjQuery.cleanData( getAll( elem, false ) );\n\t\t\t\t\t\t\telem.innerHTML = value;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\telem = 0;\n\n\t\t\t\t// If using innerHTML throws an exception, use the fallback method\n\t\t\t\t} catch ( e ) {}\n\t\t\t}\n\n\t\t\tif ( elem ) {\n\t\t\t\tthis.empty().append( value );\n\t\t\t}\n\t\t}, null, value, arguments.length );\n\t},\n\n\treplaceWith: function() {\n\t\tvar ignored = [];\n\n\t\t// Make the changes, replacing each non-ignored context element with the new content\n\t\treturn domManip( this, arguments, function( elem ) {\n\t\t\tvar parent = this.parentNode;\n\n\t\t\tif ( jQuery.inArray( this, ignored ) < 0 ) {\n\t\t\t\tjQuery.cleanData( getAll( this ) );\n\t\t\t\tif ( parent ) {\n\t\t\t\t\tparent.replaceChild( elem, this );\n\t\t\t\t}\n\t\t\t}\n\n\t\t// Force callback invocation\n\t\t}, ignored );\n\t}\n} );\n\njQuery.each( {\n\tappendTo: \"append\",\n\tprependTo: \"prepend\",\n\tinsertBefore: \"before\",\n\tinsertAfter: \"after\",\n\treplaceAll: \"replaceWith\"\n}, function( name, original ) {\n\tjQuery.fn[ name ] = function( selector ) {\n\t\tvar elems,\n\t\t\tret = [],\n\t\t\tinsert = jQuery( selector ),\n\t\t\tlast = insert.length - 1,\n\t\t\ti = 0;\n\n\t\tfor ( ; i <= last; i++ ) {\n\t\t\telems = i === last ? this : this.clone( true );\n\t\t\tjQuery( insert[ i ] )[ original ]( elems );\n\n\t\t\t// Support: Android <=4.0 only, PhantomJS 1 only\n\t\t\t// .get() because push.apply(_, arraylike) throws on ancient WebKit\n\t\t\tpush.apply( ret, elems.get() );\n\t\t}\n\n\t\treturn this.pushStack( ret );\n\t};\n} );\nvar rnumnonpx = new RegExp( \"^(\" + pnum + \")(?!px)[a-z%]+$\", \"i\" );\n\nvar getStyles = function( elem ) {\n\n\t\t// Support: IE <=11 only, Firefox <=30 (#15098, #14150)\n\t\t// IE throws on elements created in popups\n\t\t// FF meanwhile throws on frame elements through \"defaultView.getComputedStyle\"\n\t\tvar view = elem.ownerDocument.defaultView;\n\n\t\tif ( !view || !view.opener ) {\n\t\t\tview = window;\n\t\t}\n\n\t\treturn view.getComputedStyle( elem );\n\t};\n\nvar rboxStyle = new RegExp( cssExpand.join( \"|\" ), \"i\" );\n\n\n\n( function() {\n\n\t// Executing both pixelPosition & boxSizingReliable tests require only one layout\n\t// so they're executed at the same time to save the second computation.\n\tfunction computeStyleTests() {\n\n\t\t// This is a singleton, we need to execute it only once\n\t\tif ( !div ) {\n\t\t\treturn;\n\t\t}\n\n\t\tcontainer.style.cssText = \"position:absolute;left:-11111px;width:60px;\" +\n\t\t\t\"margin-top:1px;padding:0;border:0\";\n\t\tdiv.style.cssText =\n\t\t\t\"position:relative;display:block;box-sizing:border-box;overflow:scroll;\" +\n\t\t\t\"margin:auto;border:1px;padding:1px;\" +\n\t\t\t\"width:60%;top:1%\";\n\t\tdocumentElement.appendChild( container ).appendChild( div );\n\n\t\tvar divStyle = window.getComputedStyle( div );\n\t\tpixelPositionVal = divStyle.top !== \"1%\";\n\n\t\t// Support: Android 4.0 - 4.3 only, Firefox <=3 - 44\n\t\treliableMarginLeftVal = roundPixelMeasures( divStyle.marginLeft ) === 12;\n\n\t\t// Support: Android 4.0 - 4.3 only, Safari <=9.1 - 10.1, iOS <=7.0 - 9.3\n\t\t// Some styles come back with percentage values, even though they shouldn't\n\t\tdiv.style.right = \"60%\";\n\t\tpixelBoxStylesVal = roundPixelMeasures( divStyle.right ) === 36;\n\n\t\t// Support: IE 9 - 11 only\n\t\t// Detect misreporting of content dimensions for box-sizing:border-box elements\n\t\tboxSizingReliableVal = roundPixelMeasures( divStyle.width ) === 36;\n\n\t\t// Support: IE 9 only\n\t\t// Detect overflow:scroll screwiness (gh-3699)\n\t\tdiv.style.position = \"absolute\";\n\t\tscrollboxSizeVal = div.offsetWidth === 36 || \"absolute\";\n\n\t\tdocumentElement.removeChild( container );\n\n\t\t// Nullify the div so it wouldn't be stored in the memory and\n\t\t// it will also be a sign that checks already performed\n\t\tdiv = null;\n\t}\n\n\tfunction roundPixelMeasures( measure ) {\n\t\treturn Math.round( parseFloat( measure ) );\n\t}\n\n\tvar pixelPositionVal, boxSizingReliableVal, scrollboxSizeVal, pixelBoxStylesVal,\n\t\treliableMarginLeftVal,\n\t\tcontainer = document.createElement( \"div\" ),\n\t\tdiv = document.createElement( \"div\" );\n\n\t// Finish early in limited (non-browser) environments\n\tif ( !div.style ) {\n\t\treturn;\n\t}\n\n\t// Support: IE <=9 - 11 only\n\t// Style of cloned element affects source element cloned (#8908)\n\tdiv.style.backgroundClip = \"content-box\";\n\tdiv.cloneNode( true ).style.backgroundClip = \"\";\n\tsupport.clearCloneStyle = div.style.backgroundClip === \"content-box\";\n\n\tjQuery.extend( support, {\n\t\tboxSizingReliable: function() {\n\t\t\tcomputeStyleTests();\n\t\t\treturn boxSizingReliableVal;\n\t\t},\n\t\tpixelBoxStyles: function() {\n\t\t\tcomputeStyleTests();\n\t\t\treturn pixelBoxStylesVal;\n\t\t},\n\t\tpixelPosition: function() {\n\t\t\tcomputeStyleTests();\n\t\t\treturn pixelPositionVal;\n\t\t},\n\t\treliableMarginLeft: function() {\n\t\t\tcomputeStyleTests();\n\t\t\treturn reliableMarginLeftVal;\n\t\t},\n\t\tscrollboxSize: function() {\n\t\t\tcomputeStyleTests();\n\t\t\treturn scrollboxSizeVal;\n\t\t}\n\t} );\n} )();\n\n\nfunction curCSS( elem, name, computed ) {\n\tvar width, minWidth, maxWidth, ret,\n\n\t\t// Support: Firefox 51+\n\t\t// Retrieving style before computed somehow\n\t\t// fixes an issue with getting wrong values\n\t\t// on detached elements\n\t\tstyle = elem.style;\n\n\tcomputed = computed || getStyles( elem );\n\n\t// getPropertyValue is needed for:\n\t// .css('filter') (IE 9 only, #12537)\n\t// .css('--customProperty) (#3144)\n\tif ( computed ) {\n\t\tret = computed.getPropertyValue( name ) || computed[ name ];\n\n\t\tif ( ret === \"\" && !jQuery.contains( elem.ownerDocument, elem ) ) {\n\t\t\tret = jQuery.style( elem, name );\n\t\t}\n\n\t\t// A tribute to the \"awesome hack by Dean Edwards\"\n\t\t// Android Browser returns percentage for some values,\n\t\t// but width seems to be reliably pixels.\n\t\t// This is against the CSSOM draft spec:\n\t\t// https://drafts.csswg.org/cssom/#resolved-values\n\t\tif ( !support.pixelBoxStyles() && rnumnonpx.test( ret ) && rboxStyle.test( name ) ) {\n\n\t\t\t// Remember the original values\n\t\t\twidth = style.width;\n\t\t\tminWidth = style.minWidth;\n\t\t\tmaxWidth = style.maxWidth;\n\n\t\t\t// Put in the new values to get a computed value out\n\t\t\tstyle.minWidth = style.maxWidth = style.width = ret;\n\t\t\tret = computed.width;\n\n\t\t\t// Revert the changed values\n\t\t\tstyle.width = width;\n\t\t\tstyle.minWidth = minWidth;\n\t\t\tstyle.maxWidth = maxWidth;\n\t\t}\n\t}\n\n\treturn ret !== undefined ?\n\n\t\t// Support: IE <=9 - 11 only\n\t\t// IE returns zIndex value as an integer.\n\t\tret + \"\" :\n\t\tret;\n}\n\n\nfunction addGetHookIf( conditionFn, hookFn ) {\n\n\t// Define the hook, we'll check on the first run if it's really needed.\n\treturn {\n\t\tget: function() {\n\t\t\tif ( conditionFn() ) {\n\n\t\t\t\t// Hook not needed (or it's not possible to use it due\n\t\t\t\t// to missing dependency), remove it.\n\t\t\t\tdelete this.get;\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Hook needed; redefine it so that the support test is not executed again.\n\t\t\treturn ( this.get = hookFn ).apply( this, arguments );\n\t\t}\n\t};\n}\n\n\nvar\n\n\t// Swappable if display is none or starts with table\n\t// except \"table\", \"table-cell\", or \"table-caption\"\n\t// See here for display values: https://developer.mozilla.org/en-US/docs/CSS/display\n\trdisplayswap = /^(none|table(?!-c[ea]).+)/,\n\trcustomProp = /^--/,\n\tcssShow = { position: \"absolute\", visibility: \"hidden\", display: \"block\" },\n\tcssNormalTransform = {\n\t\tletterSpacing: \"0\",\n\t\tfontWeight: \"400\"\n\t},\n\n\tcssPrefixes = [ \"Webkit\", \"Moz\", \"ms\" ],\n\temptyStyle = document.createElement( \"div\" ).style;\n\n// Return a css property mapped to a potentially vendor prefixed property\nfunction vendorPropName( name ) {\n\n\t// Shortcut for names that are not vendor prefixed\n\tif ( name in emptyStyle ) {\n\t\treturn name;\n\t}\n\n\t// Check for vendor prefixed names\n\tvar capName = name[ 0 ].toUpperCase() + name.slice( 1 ),\n\t\ti = cssPrefixes.length;\n\n\twhile ( i-- ) {\n\t\tname = cssPrefixes[ i ] + capName;\n\t\tif ( name in emptyStyle ) {\n\t\t\treturn name;\n\t\t}\n\t}\n}\n\n// Return a property mapped along what jQuery.cssProps suggests or to\n// a vendor prefixed property.\nfunction finalPropName( name ) {\n\tvar ret = jQuery.cssProps[ name ];\n\tif ( !ret ) {\n\t\tret = jQuery.cssProps[ name ] = vendorPropName( name ) || name;\n\t}\n\treturn ret;\n}\n\nfunction setPositiveNumber( elem, value, subtract ) {\n\n\t// Any relative (+/-) values have already been\n\t// normalized at this point\n\tvar matches = rcssNum.exec( value );\n\treturn matches ?\n\n\t\t// Guard against undefined \"subtract\", e.g., when used as in cssHooks\n\t\tMath.max( 0, matches[ 2 ] - ( subtract || 0 ) ) + ( matches[ 3 ] || \"px\" ) :\n\t\tvalue;\n}\n\nfunction boxModelAdjustment( elem, dimension, box, isBorderBox, styles, computedVal ) {\n\tvar i = dimension === \"width\" ? 1 : 0,\n\t\textra = 0,\n\t\tdelta = 0;\n\n\t// Adjustment may not be necessary\n\tif ( box === ( isBorderBox ? \"border\" : \"content\" ) ) {\n\t\treturn 0;\n\t}\n\n\tfor ( ; i < 4; i += 2 ) {\n\n\t\t// Both box models exclude margin\n\t\tif ( box === \"margin\" ) {\n\t\t\tdelta += jQuery.css( elem, box + cssExpand[ i ], true, styles );\n\t\t}\n\n\t\t// If we get here with a content-box, we're seeking \"padding\" or \"border\" or \"margin\"\n\t\tif ( !isBorderBox ) {\n\n\t\t\t// Add padding\n\t\t\tdelta += jQuery.css( elem, \"padding\" + cssExpand[ i ], true, styles );\n\n\t\t\t// For \"border\" or \"margin\", add border\n\t\t\tif ( box !== \"padding\" ) {\n\t\t\t\tdelta += jQuery.css( elem, \"border\" + cssExpand[ i ] + \"Width\", true, styles );\n\n\t\t\t// But still keep track of it otherwise\n\t\t\t} else {\n\t\t\t\textra += jQuery.css( elem, \"border\" + cssExpand[ i ] + \"Width\", true, styles );\n\t\t\t}\n\n\t\t// If we get here with a border-box (content + padding + border), we're seeking \"content\" or\n\t\t// \"padding\" or \"margin\"\n\t\t} else {\n\n\t\t\t// For \"content\", subtract padding\n\t\t\tif ( box === \"content\" ) {\n\t\t\t\tdelta -= jQuery.css( elem, \"padding\" + cssExpand[ i ], true, styles );\n\t\t\t}\n\n\t\t\t// For \"content\" or \"padding\", subtract border\n\t\t\tif ( box !== \"margin\" ) {\n\t\t\t\tdelta -= jQuery.css( elem, \"border\" + cssExpand[ i ] + \"Width\", true, styles );\n\t\t\t}\n\t\t}\n\t}\n\n\t// Account for positive content-box scroll gutter when requested by providing computedVal\n\tif ( !isBorderBox && computedVal >= 0 ) {\n\n\t\t// offsetWidth/offsetHeight is a rounded sum of content, padding, scroll gutter, and border\n\t\t// Assuming integer scroll gutter, subtract the rest and round down\n\t\tdelta += Math.max( 0, Math.ceil(\n\t\t\telem[ \"offset\" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] -\n\t\t\tcomputedVal -\n\t\t\tdelta -\n\t\t\textra -\n\t\t\t0.5\n\t\t) );\n\t}\n\n\treturn delta;\n}\n\nfunction getWidthOrHeight( elem, dimension, extra ) {\n\n\t// Start with computed style\n\tvar styles = getStyles( elem ),\n\t\tval = curCSS( elem, dimension, styles ),\n\t\tisBorderBox = jQuery.css( elem, \"boxSizing\", false, styles ) === \"border-box\",\n\t\tvalueIsBorderBox = isBorderBox;\n\n\t// Support: Firefox <=54\n\t// Return a confounding non-pixel value or feign ignorance, as appropriate.\n\tif ( rnumnonpx.test( val ) ) {\n\t\tif ( !extra ) {\n\t\t\treturn val;\n\t\t}\n\t\tval = \"auto\";\n\t}\n\n\t// Check for style in case a browser which returns unreliable values\n\t// for getComputedStyle silently falls back to the reliable elem.style\n\tvalueIsBorderBox = valueIsBorderBox &&\n\t\t( support.boxSizingReliable() || val === elem.style[ dimension ] );\n\n\t// Fall back to offsetWidth/offsetHeight when value is \"auto\"\n\t// This happens for inline elements with no explicit setting (gh-3571)\n\t// Support: Android <=4.1 - 4.3 only\n\t// Also use offsetWidth/offsetHeight for misreported inline dimensions (gh-3602)\n\tif ( val === \"auto\" ||\n\t\t!parseFloat( val ) && jQuery.css( elem, \"display\", false, styles ) === \"inline\" ) {\n\n\t\tval = elem[ \"offset\" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ];\n\n\t\t// offsetWidth/offsetHeight provide border-box values\n\t\tvalueIsBorderBox = true;\n\t}\n\n\t// Normalize \"\" and auto\n\tval = parseFloat( val ) || 0;\n\n\t// Adjust for the element's box model\n\treturn ( val +\n\t\tboxModelAdjustment(\n\t\t\telem,\n\t\t\tdimension,\n\t\t\textra || ( isBorderBox ? \"border\" : \"content\" ),\n\t\t\tvalueIsBorderBox,\n\t\t\tstyles,\n\n\t\t\t// Provide the current computed size to request scroll gutter calculation (gh-3589)\n\t\t\tval\n\t\t)\n\t) + \"px\";\n}\n\njQuery.extend( {\n\n\t// Add in style property hooks for overriding the default\n\t// behavior of getting and setting a style property\n\tcssHooks: {\n\t\topacity: {\n\t\t\tget: function( elem, computed ) {\n\t\t\t\tif ( computed ) {\n\n\t\t\t\t\t// We should always get a number back from opacity\n\t\t\t\t\tvar ret = curCSS( elem, \"opacity\" );\n\t\t\t\t\treturn ret === \"\" ? \"1\" : ret;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t},\n\n\t// Don't automatically add \"px\" to these possibly-unitless properties\n\tcssNumber: {\n\t\t\"animationIterationCount\": true,\n\t\t\"columnCount\": true,\n\t\t\"fillOpacity\": true,\n\t\t\"flexGrow\": true,\n\t\t\"flexShrink\": true,\n\t\t\"fontWeight\": true,\n\t\t\"lineHeight\": true,\n\t\t\"opacity\": true,\n\t\t\"order\": true,\n\t\t\"orphans\": true,\n\t\t\"widows\": true,\n\t\t\"zIndex\": true,\n\t\t\"zoom\": true\n\t},\n\n\t// Add in properties whose names you wish to fix before\n\t// setting or getting the value\n\tcssProps: {},\n\n\t// Get and set the style property on a DOM Node\n\tstyle: function( elem, name, value, extra ) {\n\n\t\t// Don't set styles on text and comment nodes\n\t\tif ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Make sure that we're working with the right name\n\t\tvar ret, type, hooks,\n\t\t\torigName = camelCase( name ),\n\t\t\tisCustomProp = rcustomProp.test( name ),\n\t\t\tstyle = elem.style;\n\n\t\t// Make sure that we're working with the right name. We don't\n\t\t// want to query the value if it is a CSS custom property\n\t\t// since they are user-defined.\n\t\tif ( !isCustomProp ) {\n\t\t\tname = finalPropName( origName );\n\t\t}\n\n\t\t// Gets hook for the prefixed version, then unprefixed version\n\t\thooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];\n\n\t\t// Check if we're setting a value\n\t\tif ( value !== undefined ) {\n\t\t\ttype = typeof value;\n\n\t\t\t// Convert \"+=\" or \"-=\" to relative numbers (#7345)\n\t\t\tif ( type === \"string\" && ( ret = rcssNum.exec( value ) ) && ret[ 1 ] ) {\n\t\t\t\tvalue = adjustCSS( elem, name, ret );\n\n\t\t\t\t// Fixes bug #9237\n\t\t\t\ttype = \"number\";\n\t\t\t}\n\n\t\t\t// Make sure that null and NaN values aren't set (#7116)\n\t\t\tif ( value == null || value !== value ) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// If a number was passed in, add the unit (except for certain CSS properties)\n\t\t\tif ( type === \"number\" ) {\n\t\t\t\tvalue += ret && ret[ 3 ] || ( jQuery.cssNumber[ origName ] ? \"\" : \"px\" );\n\t\t\t}\n\n\t\t\t// background-* props affect original clone's values\n\t\t\tif ( !support.clearCloneStyle && value === \"\" && name.indexOf( \"background\" ) === 0 ) {\n\t\t\t\tstyle[ name ] = \"inherit\";\n\t\t\t}\n\n\t\t\t// If a hook was provided, use that value, otherwise just set the specified value\n\t\t\tif ( !hooks || !( \"set\" in hooks ) ||\n\t\t\t\t( value = hooks.set( elem, value, extra ) ) !== undefined ) {\n\n\t\t\t\tif ( isCustomProp ) {\n\t\t\t\t\tstyle.setProperty( name, value );\n\t\t\t\t} else {\n\t\t\t\t\tstyle[ name ] = value;\n\t\t\t\t}\n\t\t\t}\n\n\t\t} else {\n\n\t\t\t// If a hook was provided get the non-computed value from there\n\t\t\tif ( hooks && \"get\" in hooks &&\n\t\t\t\t( ret = hooks.get( elem, false, extra ) ) !== undefined ) {\n\n\t\t\t\treturn ret;\n\t\t\t}\n\n\t\t\t// Otherwise just get the value from the style object\n\t\t\treturn style[ name ];\n\t\t}\n\t},\n\n\tcss: function( elem, name, extra, styles ) {\n\t\tvar val, num, hooks,\n\t\t\torigName = camelCase( name ),\n\t\t\tisCustomProp = rcustomProp.test( name );\n\n\t\t// Make sure that we're working with the right name. We don't\n\t\t// want to modify the value if it is a CSS custom property\n\t\t// since they are user-defined.\n\t\tif ( !isCustomProp ) {\n\t\t\tname = finalPropName( origName );\n\t\t}\n\n\t\t// Try prefixed name followed by the unprefixed name\n\t\thooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];\n\n\t\t// If a hook was provided get the computed value from there\n\t\tif ( hooks && \"get\" in hooks ) {\n\t\t\tval = hooks.get( elem, true, extra );\n\t\t}\n\n\t\t// Otherwise, if a way to get the computed value exists, use that\n\t\tif ( val === undefined ) {\n\t\t\tval = curCSS( elem, name, styles );\n\t\t}\n\n\t\t// Convert \"normal\" to computed value\n\t\tif ( val === \"normal\" && name in cssNormalTransform ) {\n\t\t\tval = cssNormalTransform[ name ];\n\t\t}\n\n\t\t// Make numeric if forced or a qualifier was provided and val looks numeric\n\t\tif ( extra === \"\" || extra ) {\n\t\t\tnum = parseFloat( val );\n\t\t\treturn extra === true || isFinite( num ) ? num || 0 : val;\n\t\t}\n\n\t\treturn val;\n\t}\n} );\n\njQuery.each( [ \"height\", \"width\" ], function( i, dimension ) {\n\tjQuery.cssHooks[ dimension ] = {\n\t\tget: function( elem, computed, extra ) {\n\t\t\tif ( computed ) {\n\n\t\t\t\t// Certain elements can have dimension info if we invisibly show them\n\t\t\t\t// but it must have a current display style that would benefit\n\t\t\t\treturn rdisplayswap.test( jQuery.css( elem, \"display\" ) ) &&\n\n\t\t\t\t\t// Support: Safari 8+\n\t\t\t\t\t// Table columns in Safari have non-zero offsetWidth & zero\n\t\t\t\t\t// getBoundingClientRect().width unless display is changed.\n\t\t\t\t\t// Support: IE <=11 only\n\t\t\t\t\t// Running getBoundingClientRect on a disconnected node\n\t\t\t\t\t// in IE throws an error.\n\t\t\t\t\t( !elem.getClientRects().length || !elem.getBoundingClientRect().width ) ?\n\t\t\t\t\t\tswap( elem, cssShow, function() {\n\t\t\t\t\t\t\treturn getWidthOrHeight( elem, dimension, extra );\n\t\t\t\t\t\t} ) :\n\t\t\t\t\t\tgetWidthOrHeight( elem, dimension, extra );\n\t\t\t}\n\t\t},\n\n\t\tset: function( elem, value, extra ) {\n\t\t\tvar matches,\n\t\t\t\tstyles = getStyles( elem ),\n\t\t\t\tisBorderBox = jQuery.css( elem, \"boxSizing\", false, styles ) === \"border-box\",\n\t\t\t\tsubtract = extra && boxModelAdjustment(\n\t\t\t\t\telem,\n\t\t\t\t\tdimension,\n\t\t\t\t\textra,\n\t\t\t\t\tisBorderBox,\n\t\t\t\t\tstyles\n\t\t\t\t);\n\n\t\t\t// Account for unreliable border-box dimensions by comparing offset* to computed and\n\t\t\t// faking a content-box to get border and padding (gh-3699)\n\t\t\tif ( isBorderBox && support.scrollboxSize() === styles.position ) {\n\t\t\t\tsubtract -= Math.ceil(\n\t\t\t\t\telem[ \"offset\" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] -\n\t\t\t\t\tparseFloat( styles[ dimension ] ) -\n\t\t\t\t\tboxModelAdjustment( elem, dimension, \"border\", false, styles ) -\n\t\t\t\t\t0.5\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Convert to pixels if value adjustment is needed\n\t\t\tif ( subtract && ( matches = rcssNum.exec( value ) ) &&\n\t\t\t\t( matches[ 3 ] || \"px\" ) !== \"px\" ) {\n\n\t\t\t\telem.style[ dimension ] = value;\n\t\t\t\tvalue = jQuery.css( elem, dimension );\n\t\t\t}\n\n\t\t\treturn setPositiveNumber( elem, value, subtract );\n\t\t}\n\t};\n} );\n\njQuery.cssHooks.marginLeft = addGetHookIf( support.reliableMarginLeft,\n\tfunction( elem, computed ) {\n\t\tif ( computed ) {\n\t\t\treturn ( parseFloat( curCSS( elem, \"marginLeft\" ) ) ||\n\t\t\t\telem.getBoundingClientRect().left -\n\t\t\t\t\tswap( elem, { marginLeft: 0 }, function() {\n\t\t\t\t\t\treturn elem.getBoundingClientRect().left;\n\t\t\t\t\t} )\n\t\t\t\t) + \"px\";\n\t\t}\n\t}\n);\n\n// These hooks are used by animate to expand properties\njQuery.each( {\n\tmargin: \"\",\n\tpadding: \"\",\n\tborder: \"Width\"\n}, function( prefix, suffix ) {\n\tjQuery.cssHooks[ prefix + suffix ] = {\n\t\texpand: function( value ) {\n\t\t\tvar i = 0,\n\t\t\t\texpanded = {},\n\n\t\t\t\t// Assumes a single number if not a string\n\t\t\t\tparts = typeof value === \"string\" ? value.split( \" \" ) : [ value ];\n\n\t\t\tfor ( ; i < 4; i++ ) {\n\t\t\t\texpanded[ prefix + cssExpand[ i ] + suffix ] =\n\t\t\t\t\tparts[ i ] || parts[ i - 2 ] || parts[ 0 ];\n\t\t\t}\n\n\t\t\treturn expanded;\n\t\t}\n\t};\n\n\tif ( prefix !== \"margin\" ) {\n\t\tjQuery.cssHooks[ prefix + suffix ].set = setPositiveNumber;\n\t}\n} );\n\njQuery.fn.extend( {\n\tcss: function( name, value ) {\n\t\treturn access( this, function( elem, name, value ) {\n\t\t\tvar styles, len,\n\t\t\t\tmap = {},\n\t\t\t\ti = 0;\n\n\t\t\tif ( Array.isArray( name ) ) {\n\t\t\t\tstyles = getStyles( elem );\n\t\t\t\tlen = name.length;\n\n\t\t\t\tfor ( ; i < len; i++ ) {\n\t\t\t\t\tmap[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles );\n\t\t\t\t}\n\n\t\t\t\treturn map;\n\t\t\t}\n\n\t\t\treturn value !== undefined ?\n\t\t\t\tjQuery.style( elem, name, value ) :\n\t\t\t\tjQuery.css( elem, name );\n\t\t}, name, value, arguments.length > 1 );\n\t}\n} );\n\n\nfunction Tween( elem, options, prop, end, easing ) {\n\treturn new Tween.prototype.init( elem, options, prop, end, easing );\n}\njQuery.Tween = Tween;\n\nTween.prototype = {\n\tconstructor: Tween,\n\tinit: function( elem, options, prop, end, easing, unit ) {\n\t\tthis.elem = elem;\n\t\tthis.prop = prop;\n\t\tthis.easing = easing || jQuery.easing._default;\n\t\tthis.options = options;\n\t\tthis.start = this.now = this.cur();\n\t\tthis.end = end;\n\t\tthis.unit = unit || ( jQuery.cssNumber[ prop ] ? \"\" : \"px\" );\n\t},\n\tcur: function() {\n\t\tvar hooks = Tween.propHooks[ this.prop ];\n\n\t\treturn hooks && hooks.get ?\n\t\t\thooks.get( this ) :\n\t\t\tTween.propHooks._default.get( this );\n\t},\n\trun: function( percent ) {\n\t\tvar eased,\n\t\t\thooks = Tween.propHooks[ this.prop ];\n\n\t\tif ( this.options.duration ) {\n\t\t\tthis.pos = eased = jQuery.easing[ this.easing ](\n\t\t\t\tpercent, this.options.duration * percent, 0, 1, this.options.duration\n\t\t\t);\n\t\t} else {\n\t\t\tthis.pos = eased = percent;\n\t\t}\n\t\tthis.now = ( this.end - this.start ) * eased + this.start;\n\n\t\tif ( this.options.step ) {\n\t\t\tthis.options.step.call( this.elem, this.now, this );\n\t\t}\n\n\t\tif ( hooks && hooks.set ) {\n\t\t\thooks.set( this );\n\t\t} else {\n\t\t\tTween.propHooks._default.set( this );\n\t\t}\n\t\treturn this;\n\t}\n};\n\nTween.prototype.init.prototype = Tween.prototype;\n\nTween.propHooks = {\n\t_default: {\n\t\tget: function( tween ) {\n\t\t\tvar result;\n\n\t\t\t// Use a property on the element directly when it is not a DOM element,\n\t\t\t// or when there is no matching style property that exists.\n\t\t\tif ( tween.elem.nodeType !== 1 ||\n\t\t\t\ttween.elem[ tween.prop ] != null && tween.elem.style[ tween.prop ] == null ) {\n\t\t\t\treturn tween.elem[ tween.prop ];\n\t\t\t}\n\n\t\t\t// Passing an empty string as a 3rd parameter to .css will automatically\n\t\t\t// attempt a parseFloat and fallback to a string if the parse fails.\n\t\t\t// Simple values such as \"10px\" are parsed to Float;\n\t\t\t// complex values such as \"rotate(1rad)\" are returned as-is.\n\t\t\tresult = jQuery.css( tween.elem, tween.prop, \"\" );\n\n\t\t\t// Empty strings, null, undefined and \"auto\" are converted to 0.\n\t\t\treturn !result || result === \"auto\" ? 0 : result;\n\t\t},\n\t\tset: function( tween ) {\n\n\t\t\t// Use step hook for back compat.\n\t\t\t// Use cssHook if its there.\n\t\t\t// Use .style if available and use plain properties where available.\n\t\t\tif ( jQuery.fx.step[ tween.prop ] ) {\n\t\t\t\tjQuery.fx.step[ tween.prop ]( tween );\n\t\t\t} else if ( tween.elem.nodeType === 1 &&\n\t\t\t\t( tween.elem.style[ jQuery.cssProps[ tween.prop ] ] != null ||\n\t\t\t\t\tjQuery.cssHooks[ tween.prop ] ) ) {\n\t\t\t\tjQuery.style( tween.elem, tween.prop, tween.now + tween.unit );\n\t\t\t} else {\n\t\t\t\ttween.elem[ tween.prop ] = tween.now;\n\t\t\t}\n\t\t}\n\t}\n};\n\n// Support: IE <=9 only\n// Panic based approach to setting things on disconnected nodes\nTween.propHooks.scrollTop = Tween.propHooks.scrollLeft = {\n\tset: function( tween ) {\n\t\tif ( tween.elem.nodeType && tween.elem.parentNode ) {\n\t\t\ttween.elem[ tween.prop ] = tween.now;\n\t\t}\n\t}\n};\n\njQuery.easing = {\n\tlinear: function( p ) {\n\t\treturn p;\n\t},\n\tswing: function( p ) {\n\t\treturn 0.5 - Math.cos( p * Math.PI ) / 2;\n\t},\n\t_default: \"swing\"\n};\n\njQuery.fx = Tween.prototype.init;\n\n// Back compat <1.8 extension point\njQuery.fx.step = {};\n\n\n\n\nvar\n\tfxNow, inProgress,\n\trfxtypes = /^(?:toggle|show|hide)$/,\n\trrun = /queueHooks$/;\n\nfunction schedule() {\n\tif ( inProgress ) {\n\t\tif ( document.hidden === false && window.requestAnimationFrame ) {\n\t\t\twindow.requestAnimationFrame( schedule );\n\t\t} else {\n\t\t\twindow.setTimeout( schedule, jQuery.fx.interval );\n\t\t}\n\n\t\tjQuery.fx.tick();\n\t}\n}\n\n// Animations created synchronously will run synchronously\nfunction createFxNow() {\n\twindow.setTimeout( function() {\n\t\tfxNow = undefined;\n\t} );\n\treturn ( fxNow = Date.now() );\n}\n\n// Generate parameters to create a standard animation\nfunction genFx( type, includeWidth ) {\n\tvar which,\n\t\ti = 0,\n\t\tattrs = { height: type };\n\n\t// If we include width, step value is 1 to do all cssExpand values,\n\t// otherwise step value is 2 to skip over Left and Right\n\tincludeWidth = includeWidth ? 1 : 0;\n\tfor ( ; i < 4; i += 2 - includeWidth ) {\n\t\twhich = cssExpand[ i ];\n\t\tattrs[ \"margin\" + which ] = attrs[ \"padding\" + which ] = type;\n\t}\n\n\tif ( includeWidth ) {\n\t\tattrs.opacity = attrs.width = type;\n\t}\n\n\treturn attrs;\n}\n\nfunction createTween( value, prop, animation ) {\n\tvar tween,\n\t\tcollection = ( Animation.tweeners[ prop ] || [] ).concat( Animation.tweeners[ \"*\" ] ),\n\t\tindex = 0,\n\t\tlength = collection.length;\n\tfor ( ; index < length; index++ ) {\n\t\tif ( ( tween = collection[ index ].call( animation, prop, value ) ) ) {\n\n\t\t\t// We're done with this property\n\t\t\treturn tween;\n\t\t}\n\t}\n}\n\nfunction defaultPrefilter( elem, props, opts ) {\n\tvar prop, value, toggle, hooks, oldfire, propTween, restoreDisplay, display,\n\t\tisBox = \"width\" in props || \"height\" in props,\n\t\tanim = this,\n\t\torig = {},\n\t\tstyle = elem.style,\n\t\thidden = elem.nodeType && isHiddenWithinTree( elem ),\n\t\tdataShow = dataPriv.get( elem, \"fxshow\" );\n\n\t// Queue-skipping animations hijack the fx hooks\n\tif ( !opts.queue ) {\n\t\thooks = jQuery._queueHooks( elem, \"fx\" );\n\t\tif ( hooks.unqueued == null ) {\n\t\t\thooks.unqueued = 0;\n\t\t\toldfire = hooks.empty.fire;\n\t\t\thooks.empty.fire = function() {\n\t\t\t\tif ( !hooks.unqueued ) {\n\t\t\t\t\toldfire();\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\t\thooks.unqueued++;\n\n\t\tanim.always( function() {\n\n\t\t\t// Ensure the complete handler is called before this completes\n\t\t\tanim.always( function() {\n\t\t\t\thooks.unqueued--;\n\t\t\t\tif ( !jQuery.queue( elem, \"fx\" ).length ) {\n\t\t\t\t\thooks.empty.fire();\n\t\t\t\t}\n\t\t\t} );\n\t\t} );\n\t}\n\n\t// Detect show/hide animations\n\tfor ( prop in props ) {\n\t\tvalue = props[ prop ];\n\t\tif ( rfxtypes.test( value ) ) {\n\t\t\tdelete props[ prop ];\n\t\t\ttoggle = toggle || value === \"toggle\";\n\t\t\tif ( value === ( hidden ? \"hide\" : \"show\" ) ) {\n\n\t\t\t\t// Pretend to be hidden if this is a \"show\" and\n\t\t\t\t// there is still data from a stopped show/hide\n\t\t\t\tif ( value === \"show\" && dataShow && dataShow[ prop ] !== undefined ) {\n\t\t\t\t\thidden = true;\n\n\t\t\t\t// Ignore all other no-op show/hide data\n\t\t\t\t} else {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\t\t\torig[ prop ] = dataShow && dataShow[ prop ] || jQuery.style( elem, prop );\n\t\t}\n\t}\n\n\t// Bail out if this is a no-op like .hide().hide()\n\tpropTween = !jQuery.isEmptyObject( props );\n\tif ( !propTween && jQuery.isEmptyObject( orig ) ) {\n\t\treturn;\n\t}\n\n\t// Restrict \"overflow\" and \"display\" styles during box animations\n\tif ( isBox && elem.nodeType === 1 ) {\n\n\t\t// Support: IE <=9 - 11, Edge 12 - 15\n\t\t// Record all 3 overflow attributes because IE does not infer the shorthand\n\t\t// from identically-valued overflowX and overflowY and Edge just mirrors\n\t\t// the overflowX value there.\n\t\topts.overflow = [ style.overflow, style.overflowX, style.overflowY ];\n\n\t\t// Identify a display type, preferring old show/hide data over the CSS cascade\n\t\trestoreDisplay = dataShow && dataShow.display;\n\t\tif ( restoreDisplay == null ) {\n\t\t\trestoreDisplay = dataPriv.get( elem, \"display\" );\n\t\t}\n\t\tdisplay = jQuery.css( elem, \"display\" );\n\t\tif ( display === \"none\" ) {\n\t\t\tif ( restoreDisplay ) {\n\t\t\t\tdisplay = restoreDisplay;\n\t\t\t} else {\n\n\t\t\t\t// Get nonempty value(s) by temporarily forcing visibility\n\t\t\t\tshowHide( [ elem ], true );\n\t\t\t\trestoreDisplay = elem.style.display || restoreDisplay;\n\t\t\t\tdisplay = jQuery.css( elem, \"display\" );\n\t\t\t\tshowHide( [ elem ] );\n\t\t\t}\n\t\t}\n\n\t\t// Animate inline elements as inline-block\n\t\tif ( display === \"inline\" || display === \"inline-block\" && restoreDisplay != null ) {\n\t\t\tif ( jQuery.css( elem, \"float\" ) === \"none\" ) {\n\n\t\t\t\t// Restore the original display value at the end of pure show/hide animations\n\t\t\t\tif ( !propTween ) {\n\t\t\t\t\tanim.done( function() {\n\t\t\t\t\t\tstyle.display = restoreDisplay;\n\t\t\t\t\t} );\n\t\t\t\t\tif ( restoreDisplay == null ) {\n\t\t\t\t\t\tdisplay = style.display;\n\t\t\t\t\t\trestoreDisplay = display === \"none\" ? \"\" : display;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tstyle.display = \"inline-block\";\n\t\t\t}\n\t\t}\n\t}\n\n\tif ( opts.overflow ) {\n\t\tstyle.overflow = \"hidden\";\n\t\tanim.always( function() {\n\t\t\tstyle.overflow = opts.overflow[ 0 ];\n\t\t\tstyle.overflowX = opts.overflow[ 1 ];\n\t\t\tstyle.overflowY = opts.overflow[ 2 ];\n\t\t} );\n\t}\n\n\t// Implement show/hide animations\n\tpropTween = false;\n\tfor ( prop in orig ) {\n\n\t\t// General show/hide setup for this element animation\n\t\tif ( !propTween ) {\n\t\t\tif ( dataShow ) {\n\t\t\t\tif ( \"hidden\" in dataShow ) {\n\t\t\t\t\thidden = dataShow.hidden;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tdataShow = dataPriv.access( elem, \"fxshow\", { display: restoreDisplay } );\n\t\t\t}\n\n\t\t\t// Store hidden/visible for toggle so `.stop().toggle()` \"reverses\"\n\t\t\tif ( toggle ) {\n\t\t\t\tdataShow.hidden = !hidden;\n\t\t\t}\n\n\t\t\t// Show elements before animating them\n\t\t\tif ( hidden ) {\n\t\t\t\tshowHide( [ elem ], true );\n\t\t\t}\n\n\t\t\t/* eslint-disable no-loop-func */\n\n\t\t\tanim.done( function() {\n\n\t\t\t/* eslint-enable no-loop-func */\n\n\t\t\t\t// The final step of a \"hide\" animation is actually hiding the element\n\t\t\t\tif ( !hidden ) {\n\t\t\t\t\tshowHide( [ elem ] );\n\t\t\t\t}\n\t\t\t\tdataPriv.remove( elem, \"fxshow\" );\n\t\t\t\tfor ( prop in orig ) {\n\t\t\t\t\tjQuery.style( elem, prop, orig[ prop ] );\n\t\t\t\t}\n\t\t\t} );\n\t\t}\n\n\t\t// Per-property setup\n\t\tpropTween = createTween( hidden ? dataShow[ prop ] : 0, prop, anim );\n\t\tif ( !( prop in dataShow ) ) {\n\t\t\tdataShow[ prop ] = propTween.start;\n\t\t\tif ( hidden ) {\n\t\t\t\tpropTween.end = propTween.start;\n\t\t\t\tpropTween.start = 0;\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunction propFilter( props, specialEasing ) {\n\tvar index, name, easing, value, hooks;\n\n\t// camelCase, specialEasing and expand cssHook pass\n\tfor ( index in props ) {\n\t\tname = camelCase( index );\n\t\teasing = specialEasing[ name ];\n\t\tvalue = props[ index ];\n\t\tif ( Array.isArray( value ) ) {\n\t\t\teasing = value[ 1 ];\n\t\t\tvalue = props[ index ] = value[ 0 ];\n\t\t}\n\n\t\tif ( index !== name ) {\n\t\t\tprops[ name ] = value;\n\t\t\tdelete props[ index ];\n\t\t}\n\n\t\thooks = jQuery.cssHooks[ name ];\n\t\tif ( hooks && \"expand\" in hooks ) {\n\t\t\tvalue = hooks.expand( value );\n\t\t\tdelete props[ name ];\n\n\t\t\t// Not quite $.extend, this won't overwrite existing keys.\n\t\t\t// Reusing 'index' because we have the correct \"name\"\n\t\t\tfor ( index in value ) {\n\t\t\t\tif ( !( index in props ) ) {\n\t\t\t\t\tprops[ index ] = value[ index ];\n\t\t\t\t\tspecialEasing[ index ] = easing;\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tspecialEasing[ name ] = easing;\n\t\t}\n\t}\n}\n\nfunction Animation( elem, properties, options ) {\n\tvar result,\n\t\tstopped,\n\t\tindex = 0,\n\t\tlength = Animation.prefilters.length,\n\t\tdeferred = jQuery.Deferred().always( function() {\n\n\t\t\t// Don't match elem in the :animated selector\n\t\t\tdelete tick.elem;\n\t\t} ),\n\t\ttick = function() {\n\t\t\tif ( stopped ) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tvar currentTime = fxNow || createFxNow(),\n\t\t\t\tremaining = Math.max( 0, animation.startTime + animation.duration - currentTime ),\n\n\t\t\t\t// Support: Android 2.3 only\n\t\t\t\t// Archaic crash bug won't allow us to use `1 - ( 0.5 || 0 )` (#12497)\n\t\t\t\ttemp = remaining / animation.duration || 0,\n\t\t\t\tpercent = 1 - temp,\n\t\t\t\tindex = 0,\n\t\t\t\tlength = animation.tweens.length;\n\n\t\t\tfor ( ; index < length; index++ ) {\n\t\t\t\tanimation.tweens[ index ].run( percent );\n\t\t\t}\n\n\t\t\tdeferred.notifyWith( elem, [ animation, percent, remaining ] );\n\n\t\t\t// If there's more to do, yield\n\t\t\tif ( percent < 1 && length ) {\n\t\t\t\treturn remaining;\n\t\t\t}\n\n\t\t\t// If this was an empty animation, synthesize a final progress notification\n\t\t\tif ( !length ) {\n\t\t\t\tdeferred.notifyWith( elem, [ animation, 1, 0 ] );\n\t\t\t}\n\n\t\t\t// Resolve the animation and report its conclusion\n\t\t\tdeferred.resolveWith( elem, [ animation ] );\n\t\t\treturn false;\n\t\t},\n\t\tanimation = deferred.promise( {\n\t\t\telem: elem,\n\t\t\tprops: jQuery.extend( {}, properties ),\n\t\t\topts: jQuery.extend( true, {\n\t\t\t\tspecialEasing: {},\n\t\t\t\teasing: jQuery.easing._default\n\t\t\t}, options ),\n\t\t\toriginalProperties: properties,\n\t\t\toriginalOptions: options,\n\t\t\tstartTime: fxNow || createFxNow(),\n\t\t\tduration: options.duration,\n\t\t\ttweens: [],\n\t\t\tcreateTween: function( prop, end ) {\n\t\t\t\tvar tween = jQuery.Tween( elem, animation.opts, prop, end,\n\t\t\t\t\t\tanimation.opts.specialEasing[ prop ] || animation.opts.easing );\n\t\t\t\tanimation.tweens.push( tween );\n\t\t\t\treturn tween;\n\t\t\t},\n\t\t\tstop: function( gotoEnd ) {\n\t\t\t\tvar index = 0,\n\n\t\t\t\t\t// If we are going to the end, we want to run all the tweens\n\t\t\t\t\t// otherwise we skip this part\n\t\t\t\t\tlength = gotoEnd ? animation.tweens.length : 0;\n\t\t\t\tif ( stopped ) {\n\t\t\t\t\treturn this;\n\t\t\t\t}\n\t\t\t\tstopped = true;\n\t\t\t\tfor ( ; index < length; index++ ) {\n\t\t\t\t\tanimation.tweens[ index ].run( 1 );\n\t\t\t\t}\n\n\t\t\t\t// Resolve when we played the last frame; otherwise, reject\n\t\t\t\tif ( gotoEnd ) {\n\t\t\t\t\tdeferred.notifyWith( elem, [ animation, 1, 0 ] );\n\t\t\t\t\tdeferred.resolveWith( elem, [ animation, gotoEnd ] );\n\t\t\t\t} else {\n\t\t\t\t\tdeferred.rejectWith( elem, [ animation, gotoEnd ] );\n\t\t\t\t}\n\t\t\t\treturn this;\n\t\t\t}\n\t\t} ),\n\t\tprops = animation.props;\n\n\tpropFilter( props, animation.opts.specialEasing );\n\n\tfor ( ; index < length; index++ ) {\n\t\tresult = Animation.prefilters[ index ].call( animation, elem, props, animation.opts );\n\t\tif ( result ) {\n\t\t\tif ( isFunction( result.stop ) ) {\n\t\t\t\tjQuery._queueHooks( animation.elem, animation.opts.queue ).stop =\n\t\t\t\t\tresult.stop.bind( result );\n\t\t\t}\n\t\t\treturn result;\n\t\t}\n\t}\n\n\tjQuery.map( props, createTween, animation );\n\n\tif ( isFunction( animation.opts.start ) ) {\n\t\tanimation.opts.start.call( elem, animation );\n\t}\n\n\t// Attach callbacks from options\n\tanimation\n\t\t.progress( animation.opts.progress )\n\t\t.done( animation.opts.done, animation.opts.complete )\n\t\t.fail( animation.opts.fail )\n\t\t.always( animation.opts.always );\n\n\tjQuery.fx.timer(\n\t\tjQuery.extend( tick, {\n\t\t\telem: elem,\n\t\t\tanim: animation,\n\t\t\tqueue: animation.opts.queue\n\t\t} )\n\t);\n\n\treturn animation;\n}\n\njQuery.Animation = jQuery.extend( Animation, {\n\n\ttweeners: {\n\t\t\"*\": [ function( prop, value ) {\n\t\t\tvar tween = this.createTween( prop, value );\n\t\t\tadjustCSS( tween.elem, prop, rcssNum.exec( value ), tween );\n\t\t\treturn tween;\n\t\t} ]\n\t},\n\n\ttweener: function( props, callback ) {\n\t\tif ( isFunction( props ) ) {\n\t\t\tcallback = props;\n\t\t\tprops = [ \"*\" ];\n\t\t} else {\n\t\t\tprops = props.match( rnothtmlwhite );\n\t\t}\n\n\t\tvar prop,\n\t\t\tindex = 0,\n\t\t\tlength = props.length;\n\n\t\tfor ( ; index < length; index++ ) {\n\t\t\tprop = props[ index ];\n\t\t\tAnimation.tweeners[ prop ] = Animation.tweeners[ prop ] || [];\n\t\t\tAnimation.tweeners[ prop ].unshift( callback );\n\t\t}\n\t},\n\n\tprefilters: [ defaultPrefilter ],\n\n\tprefilter: function( callback, prepend ) {\n\t\tif ( prepend ) {\n\t\t\tAnimation.prefilters.unshift( callback );\n\t\t} else {\n\t\t\tAnimation.prefilters.push( callback );\n\t\t}\n\t}\n} );\n\njQuery.speed = function( speed, easing, fn ) {\n\tvar opt = speed && typeof speed === \"object\" ? jQuery.extend( {}, speed ) : {\n\t\tcomplete: fn || !fn && easing ||\n\t\t\tisFunction( speed ) && speed,\n\t\tduration: speed,\n\t\teasing: fn && easing || easing && !isFunction( easing ) && easing\n\t};\n\n\t// Go to the end state if fx are off\n\tif ( jQuery.fx.off ) {\n\t\topt.duration = 0;\n\n\t} else {\n\t\tif ( typeof opt.duration !== \"number\" ) {\n\t\t\tif ( opt.duration in jQuery.fx.speeds ) {\n\t\t\t\topt.duration = jQuery.fx.speeds[ opt.duration ];\n\n\t\t\t} else {\n\t\t\t\topt.duration = jQuery.fx.speeds._default;\n\t\t\t}\n\t\t}\n\t}\n\n\t// Normalize opt.queue - true/undefined/null -> \"fx\"\n\tif ( opt.queue == null || opt.queue === true ) {\n\t\topt.queue = \"fx\";\n\t}\n\n\t// Queueing\n\topt.old = opt.complete;\n\n\topt.complete = function() {\n\t\tif ( isFunction( opt.old ) ) {\n\t\t\topt.old.call( this );\n\t\t}\n\n\t\tif ( opt.queue ) {\n\t\t\tjQuery.dequeue( this, opt.queue );\n\t\t}\n\t};\n\n\treturn opt;\n};\n\njQuery.fn.extend( {\n\tfadeTo: function( speed, to, easing, callback ) {\n\n\t\t// Show any hidden elements after setting opacity to 0\n\t\treturn this.filter( isHiddenWithinTree ).css( \"opacity\", 0 ).show()\n\n\t\t\t// Animate to the value specified\n\t\t\t.end().animate( { opacity: to }, speed, easing, callback );\n\t},\n\tanimate: function( prop, speed, easing, callback ) {\n\t\tvar empty = jQuery.isEmptyObject( prop ),\n\t\t\toptall = jQuery.speed( speed, easing, callback ),\n\t\t\tdoAnimation = function() {\n\n\t\t\t\t// Operate on a copy of prop so per-property easing won't be lost\n\t\t\t\tvar anim = Animation( this, jQuery.extend( {}, prop ), optall );\n\n\t\t\t\t// Empty animations, or finishing resolves immediately\n\t\t\t\tif ( empty || dataPriv.get( this, \"finish\" ) ) {\n\t\t\t\t\tanim.stop( true );\n\t\t\t\t}\n\t\t\t};\n\t\t\tdoAnimation.finish = doAnimation;\n\n\t\treturn empty || optall.queue === false ?\n\t\t\tthis.each( doAnimation ) :\n\t\t\tthis.queue( optall.queue, doAnimation );\n\t},\n\tstop: function( type, clearQueue, gotoEnd ) {\n\t\tvar stopQueue = function( hooks ) {\n\t\t\tvar stop = hooks.stop;\n\t\t\tdelete hooks.stop;\n\t\t\tstop( gotoEnd );\n\t\t};\n\n\t\tif ( typeof type !== \"string\" ) {\n\t\t\tgotoEnd = clearQueue;\n\t\t\tclearQueue = type;\n\t\t\ttype = undefined;\n\t\t}\n\t\tif ( clearQueue && type !== false ) {\n\t\t\tthis.queue( type || \"fx\", [] );\n\t\t}\n\n\t\treturn this.each( function() {\n\t\t\tvar dequeue = true,\n\t\t\t\tindex = type != null && type + \"queueHooks\",\n\t\t\t\ttimers = jQuery.timers,\n\t\t\t\tdata = dataPriv.get( this );\n\n\t\t\tif ( index ) {\n\t\t\t\tif ( data[ index ] && data[ index ].stop ) {\n\t\t\t\t\tstopQueue( data[ index ] );\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tfor ( index in data ) {\n\t\t\t\t\tif ( data[ index ] && data[ index ].stop && rrun.test( index ) ) {\n\t\t\t\t\t\tstopQueue( data[ index ] );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfor ( index = timers.length; index--; ) {\n\t\t\t\tif ( timers[ index ].elem === this &&\n\t\t\t\t\t( type == null || timers[ index ].queue === type ) ) {\n\n\t\t\t\t\ttimers[ index ].anim.stop( gotoEnd );\n\t\t\t\t\tdequeue = false;\n\t\t\t\t\ttimers.splice( index, 1 );\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Start the next in the queue if the last step wasn't forced.\n\t\t\t// Timers currently will call their complete callbacks, which\n\t\t\t// will dequeue but only if they were gotoEnd.\n\t\t\tif ( dequeue || !gotoEnd ) {\n\t\t\t\tjQuery.dequeue( this, type );\n\t\t\t}\n\t\t} );\n\t},\n\tfinish: function( type ) {\n\t\tif ( type !== false ) {\n\t\t\ttype = type || \"fx\";\n\t\t}\n\t\treturn this.each( function() {\n\t\t\tvar index,\n\t\t\t\tdata = dataPriv.get( this ),\n\t\t\t\tqueue = data[ type + \"queue\" ],\n\t\t\t\thooks = data[ type + \"queueHooks\" ],\n\t\t\t\ttimers = jQuery.timers,\n\t\t\t\tlength = queue ? queue.length : 0;\n\n\t\t\t// Enable finishing flag on private data\n\t\t\tdata.finish = true;\n\n\t\t\t// Empty the queue first\n\t\t\tjQuery.queue( this, type, [] );\n\n\t\t\tif ( hooks && hooks.stop ) {\n\t\t\t\thooks.stop.call( this, true );\n\t\t\t}\n\n\t\t\t// Look for any active animations, and finish them\n\t\t\tfor ( index = timers.length; index--; ) {\n\t\t\t\tif ( timers[ index ].elem === this && timers[ index ].queue === type ) {\n\t\t\t\t\ttimers[ index ].anim.stop( true );\n\t\t\t\t\ttimers.splice( index, 1 );\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Look for any animations in the old queue and finish them\n\t\t\tfor ( index = 0; index < length; index++ ) {\n\t\t\t\tif ( queue[ index ] && queue[ index ].finish ) {\n\t\t\t\t\tqueue[ index ].finish.call( this );\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Turn off finishing flag\n\t\t\tdelete data.finish;\n\t\t} );\n\t}\n} );\n\njQuery.each( [ \"toggle\", \"show\", \"hide\" ], function( i, name ) {\n\tvar cssFn = jQuery.fn[ name ];\n\tjQuery.fn[ name ] = function( speed, easing, callback ) {\n\t\treturn speed == null || typeof speed === \"boolean\" ?\n\t\t\tcssFn.apply( this, arguments ) :\n\t\t\tthis.animate( genFx( name, true ), speed, easing, callback );\n\t};\n} );\n\n// Generate shortcuts for custom animations\njQuery.each( {\n\tslideDown: genFx( \"show\" ),\n\tslideUp: genFx( \"hide\" ),\n\tslideToggle: genFx( \"toggle\" ),\n\tfadeIn: { opacity: \"show\" },\n\tfadeOut: { opacity: \"hide\" },\n\tfadeToggle: { opacity: \"toggle\" }\n}, function( name, props ) {\n\tjQuery.fn[ name ] = function( speed, easing, callback ) {\n\t\treturn this.animate( props, speed, easing, callback );\n\t};\n} );\n\njQuery.timers = [];\njQuery.fx.tick = function() {\n\tvar timer,\n\t\ti = 0,\n\t\ttimers = jQuery.timers;\n\n\tfxNow = Date.now();\n\n\tfor ( ; i < timers.length; i++ ) {\n\t\ttimer = timers[ i ];\n\n\t\t// Run the timer and safely remove it when done (allowing for external removal)\n\t\tif ( !timer() && timers[ i ] === timer ) {\n\t\t\ttimers.splice( i--, 1 );\n\t\t}\n\t}\n\n\tif ( !timers.length ) {\n\t\tjQuery.fx.stop();\n\t}\n\tfxNow = undefined;\n};\n\njQuery.fx.timer = function( timer ) {\n\tjQuery.timers.push( timer );\n\tjQuery.fx.start();\n};\n\njQuery.fx.interval = 13;\njQuery.fx.start = function() {\n\tif ( inProgress ) {\n\t\treturn;\n\t}\n\n\tinProgress = true;\n\tschedule();\n};\n\njQuery.fx.stop = function() {\n\tinProgress = null;\n};\n\njQuery.fx.speeds = {\n\tslow: 600,\n\tfast: 200,\n\n\t// Default speed\n\t_default: 400\n};\n\n\n// Based off of the plugin by Clint Helfers, with permission.\n// https://web.archive.org/web/20100324014747/http://blindsignals.com/index.php/2009/07/jquery-delay/\njQuery.fn.delay = function( time, type ) {\n\ttime = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time;\n\ttype = type || \"fx\";\n\n\treturn this.queue( type, function( next, hooks ) {\n\t\tvar timeout = window.setTimeout( next, time );\n\t\thooks.stop = function() {\n\t\t\twindow.clearTimeout( timeout );\n\t\t};\n\t} );\n};\n\n\n( function() {\n\tvar input = document.createElement( \"input\" ),\n\t\tselect = document.createElement( \"select\" ),\n\t\topt = select.appendChild( document.createElement( \"option\" ) );\n\n\tinput.type = \"checkbox\";\n\n\t// Support: Android <=4.3 only\n\t// Default value for a checkbox should be \"on\"\n\tsupport.checkOn = input.value !== \"\";\n\n\t// Support: IE <=11 only\n\t// Must access selectedIndex to make default options select\n\tsupport.optSelected = opt.selected;\n\n\t// Support: IE <=11 only\n\t// An input loses its value after becoming a radio\n\tinput = document.createElement( \"input\" );\n\tinput.value = \"t\";\n\tinput.type = \"radio\";\n\tsupport.radioValue = input.value === \"t\";\n} )();\n\n\nvar boolHook,\n\tattrHandle = jQuery.expr.attrHandle;\n\njQuery.fn.extend( {\n\tattr: function( name, value ) {\n\t\treturn access( this, jQuery.attr, name, value, arguments.length > 1 );\n\t},\n\n\tremoveAttr: function( name ) {\n\t\treturn this.each( function() {\n\t\t\tjQuery.removeAttr( this, name );\n\t\t} );\n\t}\n} );\n\njQuery.extend( {\n\tattr: function( elem, name, value ) {\n\t\tvar ret, hooks,\n\t\t\tnType = elem.nodeType;\n\n\t\t// Don't get/set attributes on text, comment and attribute nodes\n\t\tif ( nType === 3 || nType === 8 || nType === 2 ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Fallback to prop when attributes are not supported\n\t\tif ( typeof elem.getAttribute === \"undefined\" ) {\n\t\t\treturn jQuery.prop( elem, name, value );\n\t\t}\n\n\t\t// Attribute hooks are determined by the lowercase version\n\t\t// Grab necessary hook if one is defined\n\t\tif ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) {\n\t\t\thooks = jQuery.attrHooks[ name.toLowerCase() ] ||\n\t\t\t\t( jQuery.expr.match.bool.test( name ) ? boolHook : undefined );\n\t\t}\n\n\t\tif ( value !== undefined ) {\n\t\t\tif ( value === null ) {\n\t\t\t\tjQuery.removeAttr( elem, name );\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif ( hooks && \"set\" in hooks &&\n\t\t\t\t( ret = hooks.set( elem, value, name ) ) !== undefined ) {\n\t\t\t\treturn ret;\n\t\t\t}\n\n\t\t\telem.setAttribute( name, value + \"\" );\n\t\t\treturn value;\n\t\t}\n\n\t\tif ( hooks && \"get\" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) {\n\t\t\treturn ret;\n\t\t}\n\n\t\tret = jQuery.find.attr( elem, name );\n\n\t\t// Non-existent attributes return null, we normalize to undefined\n\t\treturn ret == null ? undefined : ret;\n\t},\n\n\tattrHooks: {\n\t\ttype: {\n\t\t\tset: function( elem, value ) {\n\t\t\t\tif ( !support.radioValue && value === \"radio\" &&\n\t\t\t\t\tnodeName( elem, \"input\" ) ) {\n\t\t\t\t\tvar val = elem.value;\n\t\t\t\t\telem.setAttribute( \"type\", value );\n\t\t\t\t\tif ( val ) {\n\t\t\t\t\t\telem.value = val;\n\t\t\t\t\t}\n\t\t\t\t\treturn value;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t},\n\n\tremoveAttr: function( elem, value ) {\n\t\tvar name,\n\t\t\ti = 0,\n\n\t\t\t// Attribute names can contain non-HTML whitespace characters\n\t\t\t// https://html.spec.whatwg.org/multipage/syntax.html#attributes-2\n\t\t\tattrNames = value && value.match( rnothtmlwhite );\n\n\t\tif ( attrNames && elem.nodeType === 1 ) {\n\t\t\twhile ( ( name = attrNames[ i++ ] ) ) {\n\t\t\t\telem.removeAttribute( name );\n\t\t\t}\n\t\t}\n\t}\n} );\n\n// Hooks for boolean attributes\nboolHook = {\n\tset: function( elem, value, name ) {\n\t\tif ( value === false ) {\n\n\t\t\t// Remove boolean attributes when set to false\n\t\t\tjQuery.removeAttr( elem, name );\n\t\t} else {\n\t\t\telem.setAttribute( name, name );\n\t\t}\n\t\treturn name;\n\t}\n};\n\njQuery.each( jQuery.expr.match.bool.source.match( /\\w+/g ), function( i, name ) {\n\tvar getter = attrHandle[ name ] || jQuery.find.attr;\n\n\tattrHandle[ name ] = function( elem, name, isXML ) {\n\t\tvar ret, handle,\n\t\t\tlowercaseName = name.toLowerCase();\n\n\t\tif ( !isXML ) {\n\n\t\t\t// Avoid an infinite loop by temporarily removing this function from the getter\n\t\t\thandle = attrHandle[ lowercaseName ];\n\t\t\tattrHandle[ lowercaseName ] = ret;\n\t\t\tret = getter( elem, name, isXML ) != null ?\n\t\t\t\tlowercaseName :\n\t\t\t\tnull;\n\t\t\tattrHandle[ lowercaseName ] = handle;\n\t\t}\n\t\treturn ret;\n\t};\n} );\n\n\n\n\nvar rfocusable = /^(?:input|select|textarea|button)$/i,\n\trclickable = /^(?:a|area)$/i;\n\njQuery.fn.extend( {\n\tprop: function( name, value ) {\n\t\treturn access( this, jQuery.prop, name, value, arguments.length > 1 );\n\t},\n\n\tremoveProp: function( name ) {\n\t\treturn this.each( function() {\n\t\t\tdelete this[ jQuery.propFix[ name ] || name ];\n\t\t} );\n\t}\n} );\n\njQuery.extend( {\n\tprop: function( elem, name, value ) {\n\t\tvar ret, hooks,\n\t\t\tnType = elem.nodeType;\n\n\t\t// Don't get/set properties on text, comment and attribute nodes\n\t\tif ( nType === 3 || nType === 8 || nType === 2 ) {\n\t\t\treturn;\n\t\t}\n\n\t\tif ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) {\n\n\t\t\t// Fix name and attach hooks\n\t\t\tname = jQuery.propFix[ name ] || name;\n\t\t\thooks = jQuery.propHooks[ name ];\n\t\t}\n\n\t\tif ( value !== undefined ) {\n\t\t\tif ( hooks && \"set\" in hooks &&\n\t\t\t\t( ret = hooks.set( elem, value, name ) ) !== undefined ) {\n\t\t\t\treturn ret;\n\t\t\t}\n\n\t\t\treturn ( elem[ name ] = value );\n\t\t}\n\n\t\tif ( hooks && \"get\" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) {\n\t\t\treturn ret;\n\t\t}\n\n\t\treturn elem[ name ];\n\t},\n\n\tpropHooks: {\n\t\ttabIndex: {\n\t\t\tget: function( elem ) {\n\n\t\t\t\t// Support: IE <=9 - 11 only\n\t\t\t\t// elem.tabIndex doesn't always return the\n\t\t\t\t// correct value when it hasn't been explicitly set\n\t\t\t\t// https://web.archive.org/web/20141116233347/http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/\n\t\t\t\t// Use proper attribute retrieval(#12072)\n\t\t\t\tvar tabindex = jQuery.find.attr( elem, \"tabindex\" );\n\n\t\t\t\tif ( tabindex ) {\n\t\t\t\t\treturn parseInt( tabindex, 10 );\n\t\t\t\t}\n\n\t\t\t\tif (\n\t\t\t\t\trfocusable.test( elem.nodeName ) ||\n\t\t\t\t\trclickable.test( elem.nodeName ) &&\n\t\t\t\t\telem.href\n\t\t\t\t) {\n\t\t\t\t\treturn 0;\n\t\t\t\t}\n\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t}\n\t},\n\n\tpropFix: {\n\t\t\"for\": \"htmlFor\",\n\t\t\"class\": \"className\"\n\t}\n} );\n\n// Support: IE <=11 only\n// Accessing the selectedIndex property\n// forces the browser to respect setting selected\n// on the option\n// The getter ensures a default option is selected\n// when in an optgroup\n// eslint rule \"no-unused-expressions\" is disabled for this code\n// since it considers such accessions noop\nif ( !support.optSelected ) {\n\tjQuery.propHooks.selected = {\n\t\tget: function( elem ) {\n\n\t\t\t/* eslint no-unused-expressions: \"off\" */\n\n\t\t\tvar parent = elem.parentNode;\n\t\t\tif ( parent && parent.parentNode ) {\n\t\t\t\tparent.parentNode.selectedIndex;\n\t\t\t}\n\t\t\treturn null;\n\t\t},\n\t\tset: function( elem ) {\n\n\t\t\t/* eslint no-unused-expressions: \"off\" */\n\n\t\t\tvar parent = elem.parentNode;\n\t\t\tif ( parent ) {\n\t\t\t\tparent.selectedIndex;\n\n\t\t\t\tif ( parent.parentNode ) {\n\t\t\t\t\tparent.parentNode.selectedIndex;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t};\n}\n\njQuery.each( [\n\t\"tabIndex\",\n\t\"readOnly\",\n\t\"maxLength\",\n\t\"cellSpacing\",\n\t\"cellPadding\",\n\t\"rowSpan\",\n\t\"colSpan\",\n\t\"useMap\",\n\t\"frameBorder\",\n\t\"contentEditable\"\n], function() {\n\tjQuery.propFix[ this.toLowerCase() ] = this;\n} );\n\n\n\n\n\t// Strip and collapse whitespace according to HTML spec\n\t// https://infra.spec.whatwg.org/#strip-and-collapse-ascii-whitespace\n\tfunction stripAndCollapse( value ) {\n\t\tvar tokens = value.match( rnothtmlwhite ) || [];\n\t\treturn tokens.join( \" \" );\n\t}\n\n\nfunction getClass( elem ) {\n\treturn elem.getAttribute && elem.getAttribute( \"class\" ) || \"\";\n}\n\nfunction classesToArray( value ) {\n\tif ( Array.isArray( value ) ) {\n\t\treturn value;\n\t}\n\tif ( typeof value === \"string\" ) {\n\t\treturn value.match( rnothtmlwhite ) || [];\n\t}\n\treturn [];\n}\n\njQuery.fn.extend( {\n\taddClass: function( value ) {\n\t\tvar classes, elem, cur, curValue, clazz, j, finalValue,\n\t\t\ti = 0;\n\n\t\tif ( isFunction( value ) ) {\n\t\t\treturn this.each( function( j ) {\n\t\t\t\tjQuery( this ).addClass( value.call( this, j, getClass( this ) ) );\n\t\t\t} );\n\t\t}\n\n\t\tclasses = classesToArray( value );\n\n\t\tif ( classes.length ) {\n\t\t\twhile ( ( elem = this[ i++ ] ) ) {\n\t\t\t\tcurValue = getClass( elem );\n\t\t\t\tcur = elem.nodeType === 1 && ( \" \" + stripAndCollapse( curValue ) + \" \" );\n\n\t\t\t\tif ( cur ) {\n\t\t\t\t\tj = 0;\n\t\t\t\t\twhile ( ( clazz = classes[ j++ ] ) ) {\n\t\t\t\t\t\tif ( cur.indexOf( \" \" + clazz + \" \" ) < 0 ) {\n\t\t\t\t\t\t\tcur += clazz + \" \";\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// Only assign if different to avoid unneeded rendering.\n\t\t\t\t\tfinalValue = stripAndCollapse( cur );\n\t\t\t\t\tif ( curValue !== finalValue ) {\n\t\t\t\t\t\telem.setAttribute( \"class\", finalValue );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn this;\n\t},\n\n\tremoveClass: function( value ) {\n\t\tvar classes, elem, cur, curValue, clazz, j, finalValue,\n\t\t\ti = 0;\n\n\t\tif ( isFunction( value ) ) {\n\t\t\treturn this.each( function( j ) {\n\t\t\t\tjQuery( this ).removeClass( value.call( this, j, getClass( this ) ) );\n\t\t\t} );\n\t\t}\n\n\t\tif ( !arguments.length ) {\n\t\t\treturn this.attr( \"class\", \"\" );\n\t\t}\n\n\t\tclasses = classesToArray( value );\n\n\t\tif ( classes.length ) {\n\t\t\twhile ( ( elem = this[ i++ ] ) ) {\n\t\t\t\tcurValue = getClass( elem );\n\n\t\t\t\t// This expression is here for better compressibility (see addClass)\n\t\t\t\tcur = elem.nodeType === 1 && ( \" \" + stripAndCollapse( curValue ) + \" \" );\n\n\t\t\t\tif ( cur ) {\n\t\t\t\t\tj = 0;\n\t\t\t\t\twhile ( ( clazz = classes[ j++ ] ) ) {\n\n\t\t\t\t\t\t// Remove *all* instances\n\t\t\t\t\t\twhile ( cur.indexOf( \" \" + clazz + \" \" ) > -1 ) {\n\t\t\t\t\t\t\tcur = cur.replace( \" \" + clazz + \" \", \" \" );\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// Only assign if different to avoid unneeded rendering.\n\t\t\t\t\tfinalValue = stripAndCollapse( cur );\n\t\t\t\t\tif ( curValue !== finalValue ) {\n\t\t\t\t\t\telem.setAttribute( \"class\", finalValue );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn this;\n\t},\n\n\ttoggleClass: function( value, stateVal ) {\n\t\tvar type = typeof value,\n\t\t\tisValidValue = type === \"string\" || Array.isArray( value );\n\n\t\tif ( typeof stateVal === \"boolean\" && isValidValue ) {\n\t\t\treturn stateVal ? this.addClass( value ) : this.removeClass( value );\n\t\t}\n\n\t\tif ( isFunction( value ) ) {\n\t\t\treturn this.each( function( i ) {\n\t\t\t\tjQuery( this ).toggleClass(\n\t\t\t\t\tvalue.call( this, i, getClass( this ), stateVal ),\n\t\t\t\t\tstateVal\n\t\t\t\t);\n\t\t\t} );\n\t\t}\n\n\t\treturn this.each( function() {\n\t\t\tvar className, i, self, classNames;\n\n\t\t\tif ( isValidValue ) {\n\n\t\t\t\t// Toggle individual class names\n\t\t\t\ti = 0;\n\t\t\t\tself = jQuery( this );\n\t\t\t\tclassNames = classesToArray( value );\n\n\t\t\t\twhile ( ( className = classNames[ i++ ] ) ) {\n\n\t\t\t\t\t// Check each className given, space separated list\n\t\t\t\t\tif ( self.hasClass( className ) ) {\n\t\t\t\t\t\tself.removeClass( className );\n\t\t\t\t\t} else {\n\t\t\t\t\t\tself.addClass( className );\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t// Toggle whole class name\n\t\t\t} else if ( value === undefined || type === \"boolean\" ) {\n\t\t\t\tclassName = getClass( this );\n\t\t\t\tif ( className ) {\n\n\t\t\t\t\t// Store className if set\n\t\t\t\t\tdataPriv.set( this, \"__className__\", className );\n\t\t\t\t}\n\n\t\t\t\t// If the element has a class name or if we're passed `false`,\n\t\t\t\t// then remove the whole classname (if there was one, the above saved it).\n\t\t\t\t// Otherwise bring back whatever was previously saved (if anything),\n\t\t\t\t// falling back to the empty string if nothing was stored.\n\t\t\t\tif ( this.setAttribute ) {\n\t\t\t\t\tthis.setAttribute( \"class\",\n\t\t\t\t\t\tclassName || value === false ?\n\t\t\t\t\t\t\"\" :\n\t\t\t\t\t\tdataPriv.get( this, \"__className__\" ) || \"\"\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t} );\n\t},\n\n\thasClass: function( selector ) {\n\t\tvar className, elem,\n\t\t\ti = 0;\n\n\t\tclassName = \" \" + selector + \" \";\n\t\twhile ( ( elem = this[ i++ ] ) ) {\n\t\t\tif ( elem.nodeType === 1 &&\n\t\t\t\t( \" \" + stripAndCollapse( getClass( elem ) ) + \" \" ).indexOf( className ) > -1 ) {\n\t\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\n\t\treturn false;\n\t}\n} );\n\n\n\n\nvar rreturn = /\\r/g;\n\njQuery.fn.extend( {\n\tval: function( value ) {\n\t\tvar hooks, ret, valueIsFunction,\n\t\t\telem = this[ 0 ];\n\n\t\tif ( !arguments.length ) {\n\t\t\tif ( elem ) {\n\t\t\t\thooks = jQuery.valHooks[ elem.type ] ||\n\t\t\t\t\tjQuery.valHooks[ elem.nodeName.toLowerCase() ];\n\n\t\t\t\tif ( hooks &&\n\t\t\t\t\t\"get\" in hooks &&\n\t\t\t\t\t( ret = hooks.get( elem, \"value\" ) ) !== undefined\n\t\t\t\t) {\n\t\t\t\t\treturn ret;\n\t\t\t\t}\n\n\t\t\t\tret = elem.value;\n\n\t\t\t\t// Handle most common string cases\n\t\t\t\tif ( typeof ret === \"string\" ) {\n\t\t\t\t\treturn ret.replace( rreturn, \"\" );\n\t\t\t\t}\n\n\t\t\t\t// Handle cases where value is null/undef or number\n\t\t\t\treturn ret == null ? \"\" : ret;\n\t\t\t}\n\n\t\t\treturn;\n\t\t}\n\n\t\tvalueIsFunction = isFunction( value );\n\n\t\treturn this.each( function( i ) {\n\t\t\tvar val;\n\n\t\t\tif ( this.nodeType !== 1 ) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif ( valueIsFunction ) {\n\t\t\t\tval = value.call( this, i, jQuery( this ).val() );\n\t\t\t} else {\n\t\t\t\tval = value;\n\t\t\t}\n\n\t\t\t// Treat null/undefined as \"\"; convert numbers to string\n\t\t\tif ( val == null ) {\n\t\t\t\tval = \"\";\n\n\t\t\t} else if ( typeof val === \"number\" ) {\n\t\t\t\tval += \"\";\n\n\t\t\t} else if ( Array.isArray( val ) ) {\n\t\t\t\tval = jQuery.map( val, function( value ) {\n\t\t\t\t\treturn value == null ? \"\" : value + \"\";\n\t\t\t\t} );\n\t\t\t}\n\n\t\t\thooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ];\n\n\t\t\t// If set returns undefined, fall back to normal setting\n\t\t\tif ( !hooks || !( \"set\" in hooks ) || hooks.set( this, val, \"value\" ) === undefined ) {\n\t\t\t\tthis.value = val;\n\t\t\t}\n\t\t} );\n\t}\n} );\n\njQuery.extend( {\n\tvalHooks: {\n\t\toption: {\n\t\t\tget: function( elem ) {\n\n\t\t\t\tvar val = jQuery.find.attr( elem, \"value\" );\n\t\t\t\treturn val != null ?\n\t\t\t\t\tval :\n\n\t\t\t\t\t// Support: IE <=10 - 11 only\n\t\t\t\t\t// option.text throws exceptions (#14686, #14858)\n\t\t\t\t\t// Strip and collapse whitespace\n\t\t\t\t\t// https://html.spec.whatwg.org/#strip-and-collapse-whitespace\n\t\t\t\t\tstripAndCollapse( jQuery.text( elem ) );\n\t\t\t}\n\t\t},\n\t\tselect: {\n\t\t\tget: function( elem ) {\n\t\t\t\tvar value, option, i,\n\t\t\t\t\toptions = elem.options,\n\t\t\t\t\tindex = elem.selectedIndex,\n\t\t\t\t\tone = elem.type === \"select-one\",\n\t\t\t\t\tvalues = one ? null : [],\n\t\t\t\t\tmax = one ? index + 1 : options.length;\n\n\t\t\t\tif ( index < 0 ) {\n\t\t\t\t\ti = max;\n\n\t\t\t\t} else {\n\t\t\t\t\ti = one ? index : 0;\n\t\t\t\t}\n\n\t\t\t\t// Loop through all the selected options\n\t\t\t\tfor ( ; i < max; i++ ) {\n\t\t\t\t\toption = options[ i ];\n\n\t\t\t\t\t// Support: IE <=9 only\n\t\t\t\t\t// IE8-9 doesn't update selected after form reset (#2551)\n\t\t\t\t\tif ( ( option.selected || i === index ) &&\n\n\t\t\t\t\t\t\t// Don't return options that are disabled or in a disabled optgroup\n\t\t\t\t\t\t\t!option.disabled &&\n\t\t\t\t\t\t\t( !option.parentNode.disabled ||\n\t\t\t\t\t\t\t\t!nodeName( option.parentNode, \"optgroup\" ) ) ) {\n\n\t\t\t\t\t\t// Get the specific value for the option\n\t\t\t\t\t\tvalue = jQuery( option ).val();\n\n\t\t\t\t\t\t// We don't need an array for one selects\n\t\t\t\t\t\tif ( one ) {\n\t\t\t\t\t\t\treturn value;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Multi-Selects return an array\n\t\t\t\t\t\tvalues.push( value );\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn values;\n\t\t\t},\n\n\t\t\tset: function( elem, value ) {\n\t\t\t\tvar optionSet, option,\n\t\t\t\t\toptions = elem.options,\n\t\t\t\t\tvalues = jQuery.makeArray( value ),\n\t\t\t\t\ti = options.length;\n\n\t\t\t\twhile ( i-- ) {\n\t\t\t\t\toption = options[ i ];\n\n\t\t\t\t\t/* eslint-disable no-cond-assign */\n\n\t\t\t\t\tif ( option.selected =\n\t\t\t\t\t\tjQuery.inArray( jQuery.valHooks.option.get( option ), values ) > -1\n\t\t\t\t\t) {\n\t\t\t\t\t\toptionSet = true;\n\t\t\t\t\t}\n\n\t\t\t\t\t/* eslint-enable no-cond-assign */\n\t\t\t\t}\n\n\t\t\t\t// Force browsers to behave consistently when non-matching value is set\n\t\t\t\tif ( !optionSet ) {\n\t\t\t\t\telem.selectedIndex = -1;\n\t\t\t\t}\n\t\t\t\treturn values;\n\t\t\t}\n\t\t}\n\t}\n} );\n\n// Radios and checkboxes getter/setter\njQuery.each( [ \"radio\", \"checkbox\" ], function() {\n\tjQuery.valHooks[ this ] = {\n\t\tset: function( elem, value ) {\n\t\t\tif ( Array.isArray( value ) ) {\n\t\t\t\treturn ( elem.checked = jQuery.inArray( jQuery( elem ).val(), value ) > -1 );\n\t\t\t}\n\t\t}\n\t};\n\tif ( !support.checkOn ) {\n\t\tjQuery.valHooks[ this ].get = function( elem ) {\n\t\t\treturn elem.getAttribute( \"value\" ) === null ? \"on\" : elem.value;\n\t\t};\n\t}\n} );\n\n\n\n\n// Return jQuery for attributes-only inclusion\n\n\nsupport.focusin = \"onfocusin\" in window;\n\n\nvar rfocusMorph = /^(?:focusinfocus|focusoutblur)$/,\n\tstopPropagationCallback = function( e ) {\n\t\te.stopPropagation();\n\t};\n\njQuery.extend( jQuery.event, {\n\n\ttrigger: function( event, data, elem, onlyHandlers ) {\n\n\t\tvar i, cur, tmp, bubbleType, ontype, handle, special, lastElement,\n\t\t\teventPath = [ elem || document ],\n\t\t\ttype = hasOwn.call( event, \"type\" ) ? event.type : event,\n\t\t\tnamespaces = hasOwn.call( event, \"namespace\" ) ? event.namespace.split( \".\" ) : [];\n\n\t\tcur = lastElement = tmp = elem = elem || document;\n\n\t\t// Don't do events on text and comment nodes\n\t\tif ( elem.nodeType === 3 || elem.nodeType === 8 ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// focus/blur morphs to focusin/out; ensure we're not firing them right now\n\t\tif ( rfocusMorph.test( type + jQuery.event.triggered ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\tif ( type.indexOf( \".\" ) > -1 ) {\n\n\t\t\t// Namespaced trigger; create a regexp to match event type in handle()\n\t\t\tnamespaces = type.split( \".\" );\n\t\t\ttype = namespaces.shift();\n\t\t\tnamespaces.sort();\n\t\t}\n\t\tontype = type.indexOf( \":\" ) < 0 && \"on\" + type;\n\n\t\t// Caller can pass in a jQuery.Event object, Object, or just an event type string\n\t\tevent = event[ jQuery.expando ] ?\n\t\t\tevent :\n\t\t\tnew jQuery.Event( type, typeof event === \"object\" && event );\n\n\t\t// Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true)\n\t\tevent.isTrigger = onlyHandlers ? 2 : 3;\n\t\tevent.namespace = namespaces.join( \".\" );\n\t\tevent.rnamespace = event.namespace ?\n\t\t\tnew RegExp( \"(^|\\\\.)\" + namespaces.join( \"\\\\.(?:.*\\\\.|)\" ) + \"(\\\\.|$)\" ) :\n\t\t\tnull;\n\n\t\t// Clean up the event in case it is being reused\n\t\tevent.result = undefined;\n\t\tif ( !event.target ) {\n\t\t\tevent.target = elem;\n\t\t}\n\n\t\t// Clone any incoming data and prepend the event, creating the handler arg list\n\t\tdata = data == null ?\n\t\t\t[ event ] :\n\t\t\tjQuery.makeArray( data, [ event ] );\n\n\t\t// Allow special events to draw outside the lines\n\t\tspecial = jQuery.event.special[ type ] || {};\n\t\tif ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Determine event propagation path in advance, per W3C events spec (#9951)\n\t\t// Bubble up to document, then to window; watch for a global ownerDocument var (#9724)\n\t\tif ( !onlyHandlers && !special.noBubble && !isWindow( elem ) ) {\n\n\t\t\tbubbleType = special.delegateType || type;\n\t\t\tif ( !rfocusMorph.test( bubbleType + type ) ) {\n\t\t\t\tcur = cur.parentNode;\n\t\t\t}\n\t\t\tfor ( ; cur; cur = cur.parentNode ) {\n\t\t\t\teventPath.push( cur );\n\t\t\t\ttmp = cur;\n\t\t\t}\n\n\t\t\t// Only add window if we got to document (e.g., not plain obj or detached DOM)\n\t\t\tif ( tmp === ( elem.ownerDocument || document ) ) {\n\t\t\t\teventPath.push( tmp.defaultView || tmp.parentWindow || window );\n\t\t\t}\n\t\t}\n\n\t\t// Fire handlers on the event path\n\t\ti = 0;\n\t\twhile ( ( cur = eventPath[ i++ ] ) && !event.isPropagationStopped() ) {\n\t\t\tlastElement = cur;\n\t\t\tevent.type = i > 1 ?\n\t\t\t\tbubbleType :\n\t\t\t\tspecial.bindType || type;\n\n\t\t\t// jQuery handler\n\t\t\thandle = ( dataPriv.get( cur, \"events\" ) || {} )[ event.type ] &&\n\t\t\t\tdataPriv.get( cur, \"handle\" );\n\t\t\tif ( handle ) {\n\t\t\t\thandle.apply( cur, data );\n\t\t\t}\n\n\t\t\t// Native handler\n\t\t\thandle = ontype && cur[ ontype ];\n\t\t\tif ( handle && handle.apply && acceptData( cur ) ) {\n\t\t\t\tevent.result = handle.apply( cur, data );\n\t\t\t\tif ( event.result === false ) {\n\t\t\t\t\tevent.preventDefault();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tevent.type = type;\n\n\t\t// If nobody prevented the default action, do it now\n\t\tif ( !onlyHandlers && !event.isDefaultPrevented() ) {\n\n\t\t\tif ( ( !special._default ||\n\t\t\t\tspecial._default.apply( eventPath.pop(), data ) === false ) &&\n\t\t\t\tacceptData( elem ) ) {\n\n\t\t\t\t// Call a native DOM method on the target with the same name as the event.\n\t\t\t\t// Don't do default actions on window, that's where global variables be (#6170)\n\t\t\t\tif ( ontype && isFunction( elem[ type ] ) && !isWindow( elem ) ) {\n\n\t\t\t\t\t// Don't re-trigger an onFOO event when we call its FOO() method\n\t\t\t\t\ttmp = elem[ ontype ];\n\n\t\t\t\t\tif ( tmp ) {\n\t\t\t\t\t\telem[ ontype ] = null;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Prevent re-triggering of the same event, since we already bubbled it above\n\t\t\t\t\tjQuery.event.triggered = type;\n\n\t\t\t\t\tif ( event.isPropagationStopped() ) {\n\t\t\t\t\t\tlastElement.addEventListener( type, stopPropagationCallback );\n\t\t\t\t\t}\n\n\t\t\t\t\telem[ type ]();\n\n\t\t\t\t\tif ( event.isPropagationStopped() ) {\n\t\t\t\t\t\tlastElement.removeEventListener( type, stopPropagationCallback );\n\t\t\t\t\t}\n\n\t\t\t\t\tjQuery.event.triggered = undefined;\n\n\t\t\t\t\tif ( tmp ) {\n\t\t\t\t\t\telem[ ontype ] = tmp;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn event.result;\n\t},\n\n\t// Piggyback on a donor event to simulate a different one\n\t// Used only for `focus(in | out)` events\n\tsimulate: function( type, elem, event ) {\n\t\tvar e = jQuery.extend(\n\t\t\tnew jQuery.Event(),\n\t\t\tevent,\n\t\t\t{\n\t\t\t\ttype: type,\n\t\t\t\tisSimulated: true\n\t\t\t}\n\t\t);\n\n\t\tjQuery.event.trigger( e, null, elem );\n\t}\n\n} );\n\njQuery.fn.extend( {\n\n\ttrigger: function( type, data ) {\n\t\treturn this.each( function() {\n\t\t\tjQuery.event.trigger( type, data, this );\n\t\t} );\n\t},\n\ttriggerHandler: function( type, data ) {\n\t\tvar elem = this[ 0 ];\n\t\tif ( elem ) {\n\t\t\treturn jQuery.event.trigger( type, data, elem, true );\n\t\t}\n\t}\n} );\n\n\n// Support: Firefox <=44\n// Firefox doesn't have focus(in | out) events\n// Related ticket - https://bugzilla.mozilla.org/show_bug.cgi?id=687787\n//\n// Support: Chrome <=48 - 49, Safari <=9.0 - 9.1\n// focus(in | out) events fire after focus & blur events,\n// which is spec violation - http://www.w3.org/TR/DOM-Level-3-Events/#events-focusevent-event-order\n// Related ticket - https://bugs.chromium.org/p/chromium/issues/detail?id=449857\nif ( !support.focusin ) {\n\tjQuery.each( { focus: \"focusin\", blur: \"focusout\" }, function( orig, fix ) {\n\n\t\t// Attach a single capturing handler on the document while someone wants focusin/focusout\n\t\tvar handler = function( event ) {\n\t\t\tjQuery.event.simulate( fix, event.target, jQuery.event.fix( event ) );\n\t\t};\n\n\t\tjQuery.event.special[ fix ] = {\n\t\t\tsetup: function() {\n\t\t\t\tvar doc = this.ownerDocument || this,\n\t\t\t\t\tattaches = dataPriv.access( doc, fix );\n\n\t\t\t\tif ( !attaches ) {\n\t\t\t\t\tdoc.addEventListener( orig, handler, true );\n\t\t\t\t}\n\t\t\t\tdataPriv.access( doc, fix, ( attaches || 0 ) + 1 );\n\t\t\t},\n\t\t\tteardown: function() {\n\t\t\t\tvar doc = this.ownerDocument || this,\n\t\t\t\t\tattaches = dataPriv.access( doc, fix ) - 1;\n\n\t\t\t\tif ( !attaches ) {\n\t\t\t\t\tdoc.removeEventListener( orig, handler, true );\n\t\t\t\t\tdataPriv.remove( doc, fix );\n\n\t\t\t\t} else {\n\t\t\t\t\tdataPriv.access( doc, fix, attaches );\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\t} );\n}\nvar location = window.location;\n\nvar nonce = Date.now();\n\nvar rquery = ( /\\?/ );\n\n\n\n// Cross-browser xml parsing\njQuery.parseXML = function( data ) {\n\tvar xml;\n\tif ( !data || typeof data !== \"string\" ) {\n\t\treturn null;\n\t}\n\n\t// Support: IE 9 - 11 only\n\t// IE throws on parseFromString with invalid input.\n\ttry {\n\t\txml = ( new window.DOMParser() ).parseFromString( data, \"text/xml\" );\n\t} catch ( e ) {\n\t\txml = undefined;\n\t}\n\n\tif ( !xml || xml.getElementsByTagName( \"parsererror\" ).length ) {\n\t\tjQuery.error( \"Invalid XML: \" + data );\n\t}\n\treturn xml;\n};\n\n\nvar\n\trbracket = /\\[\\]$/,\n\trCRLF = /\\r?\\n/g,\n\trsubmitterTypes = /^(?:submit|button|image|reset|file)$/i,\n\trsubmittable = /^(?:input|select|textarea|keygen)/i;\n\nfunction buildParams( prefix, obj, traditional, add ) {\n\tvar name;\n\n\tif ( Array.isArray( obj ) ) {\n\n\t\t// Serialize array item.\n\t\tjQuery.each( obj, function( i, v ) {\n\t\t\tif ( traditional || rbracket.test( prefix ) ) {\n\n\t\t\t\t// Treat each array item as a scalar.\n\t\t\t\tadd( prefix, v );\n\n\t\t\t} else {\n\n\t\t\t\t// Item is non-scalar (array or object), encode its numeric index.\n\t\t\t\tbuildParams(\n\t\t\t\t\tprefix + \"[\" + ( typeof v === \"object\" && v != null ? i : \"\" ) + \"]\",\n\t\t\t\t\tv,\n\t\t\t\t\ttraditional,\n\t\t\t\t\tadd\n\t\t\t\t);\n\t\t\t}\n\t\t} );\n\n\t} else if ( !traditional && toType( obj ) === \"object\" ) {\n\n\t\t// Serialize object item.\n\t\tfor ( name in obj ) {\n\t\t\tbuildParams( prefix + \"[\" + name + \"]\", obj[ name ], traditional, add );\n\t\t}\n\n\t} else {\n\n\t\t// Serialize scalar item.\n\t\tadd( prefix, obj );\n\t}\n}\n\n// Serialize an array of form elements or a set of\n// key/values into a query string\njQuery.param = function( a, traditional ) {\n\tvar prefix,\n\t\ts = [],\n\t\tadd = function( key, valueOrFunction ) {\n\n\t\t\t// If value is a function, invoke it and use its return value\n\t\t\tvar value = isFunction( valueOrFunction ) ?\n\t\t\t\tvalueOrFunction() :\n\t\t\t\tvalueOrFunction;\n\n\t\t\ts[ s.length ] = encodeURIComponent( key ) + \"=\" +\n\t\t\t\tencodeURIComponent( value == null ? \"\" : value );\n\t\t};\n\n\t// If an array was passed in, assume that it is an array of form elements.\n\tif ( Array.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) {\n\n\t\t// Serialize the form elements\n\t\tjQuery.each( a, function() {\n\t\t\tadd( this.name, this.value );\n\t\t} );\n\n\t} else {\n\n\t\t// If traditional, encode the \"old\" way (the way 1.3.2 or older\n\t\t// did it), otherwise encode params recursively.\n\t\tfor ( prefix in a ) {\n\t\t\tbuildParams( prefix, a[ prefix ], traditional, add );\n\t\t}\n\t}\n\n\t// Return the resulting serialization\n\treturn s.join( \"&\" );\n};\n\njQuery.fn.extend( {\n\tserialize: function() {\n\t\treturn jQuery.param( this.serializeArray() );\n\t},\n\tserializeArray: function() {\n\t\treturn this.map( function() {\n\n\t\t\t// Can add propHook for \"elements\" to filter or add form elements\n\t\t\tvar elements = jQuery.prop( this, \"elements\" );\n\t\t\treturn elements ? jQuery.makeArray( elements ) : this;\n\t\t} )\n\t\t.filter( function() {\n\t\t\tvar type = this.type;\n\n\t\t\t// Use .is( \":disabled\" ) so that fieldset[disabled] works\n\t\t\treturn this.name && !jQuery( this ).is( \":disabled\" ) &&\n\t\t\t\trsubmittable.test( this.nodeName ) && !rsubmitterTypes.test( type ) &&\n\t\t\t\t( this.checked || !rcheckableType.test( type ) );\n\t\t} )\n\t\t.map( function( i, elem ) {\n\t\t\tvar val = jQuery( this ).val();\n\n\t\t\tif ( val == null ) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tif ( Array.isArray( val ) ) {\n\t\t\t\treturn jQuery.map( val, function( val ) {\n\t\t\t\t\treturn { name: elem.name, value: val.replace( rCRLF, \"\\r\\n\" ) };\n\t\t\t\t} );\n\t\t\t}\n\n\t\t\treturn { name: elem.name, value: val.replace( rCRLF, \"\\r\\n\" ) };\n\t\t} ).get();\n\t}\n} );\n\n\nvar\n\tr20 = /%20/g,\n\trhash = /#.*$/,\n\trantiCache = /([?&])_=[^&]*/,\n\trheaders = /^(.*?):[ \\t]*([^\\r\\n]*)$/mg,\n\n\t// #7653, #8125, #8152: local protocol detection\n\trlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/,\n\trnoContent = /^(?:GET|HEAD)$/,\n\trprotocol = /^\\/\\//,\n\n\t/* Prefilters\n\t * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example)\n\t * 2) These are called:\n\t * - BEFORE asking for a transport\n\t * - AFTER param serialization (s.data is a string if s.processData is true)\n\t * 3) key is the dataType\n\t * 4) the catchall symbol \"*\" can be used\n\t * 5) execution will start with transport dataType and THEN continue down to \"*\" if needed\n\t */\n\tprefilters = {},\n\n\t/* Transports bindings\n\t * 1) key is the dataType\n\t * 2) the catchall symbol \"*\" can be used\n\t * 3) selection will start with transport dataType and THEN go to \"*\" if needed\n\t */\n\ttransports = {},\n\n\t// Avoid comment-prolog char sequence (#10098); must appease lint and evade compression\n\tallTypes = \"*/\".concat( \"*\" ),\n\n\t// Anchor tag for parsing the document origin\n\toriginAnchor = document.createElement( \"a\" );\n\toriginAnchor.href = location.href;\n\n// Base \"constructor\" for jQuery.ajaxPrefilter and jQuery.ajaxTransport\nfunction addToPrefiltersOrTransports( structure ) {\n\n\t// dataTypeExpression is optional and defaults to \"*\"\n\treturn function( dataTypeExpression, func ) {\n\n\t\tif ( typeof dataTypeExpression !== \"string\" ) {\n\t\t\tfunc = dataTypeExpression;\n\t\t\tdataTypeExpression = \"*\";\n\t\t}\n\n\t\tvar dataType,\n\t\t\ti = 0,\n\t\t\tdataTypes = dataTypeExpression.toLowerCase().match( rnothtmlwhite ) || [];\n\n\t\tif ( isFunction( func ) ) {\n\n\t\t\t// For each dataType in the dataTypeExpression\n\t\t\twhile ( ( dataType = dataTypes[ i++ ] ) ) {\n\n\t\t\t\t// Prepend if requested\n\t\t\t\tif ( dataType[ 0 ] === \"+\" ) {\n\t\t\t\t\tdataType = dataType.slice( 1 ) || \"*\";\n\t\t\t\t\t( structure[ dataType ] = structure[ dataType ] || [] ).unshift( func );\n\n\t\t\t\t// Otherwise append\n\t\t\t\t} else {\n\t\t\t\t\t( structure[ dataType ] = structure[ dataType ] || [] ).push( func );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t};\n}\n\n// Base inspection function for prefilters and transports\nfunction inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR ) {\n\n\tvar inspected = {},\n\t\tseekingTransport = ( structure === transports );\n\n\tfunction inspect( dataType ) {\n\t\tvar selected;\n\t\tinspected[ dataType ] = true;\n\t\tjQuery.each( structure[ dataType ] || [], function( _, prefilterOrFactory ) {\n\t\t\tvar dataTypeOrTransport = prefilterOrFactory( options, originalOptions, jqXHR );\n\t\t\tif ( typeof dataTypeOrTransport === \"string\" &&\n\t\t\t\t!seekingTransport && !inspected[ dataTypeOrTransport ] ) {\n\n\t\t\t\toptions.dataTypes.unshift( dataTypeOrTransport );\n\t\t\t\tinspect( dataTypeOrTransport );\n\t\t\t\treturn false;\n\t\t\t} else if ( seekingTransport ) {\n\t\t\t\treturn !( selected = dataTypeOrTransport );\n\t\t\t}\n\t\t} );\n\t\treturn selected;\n\t}\n\n\treturn inspect( options.dataTypes[ 0 ] ) || !inspected[ \"*\" ] && inspect( \"*\" );\n}\n\n// A special extend for ajax options\n// that takes \"flat\" options (not to be deep extended)\n// Fixes #9887\nfunction ajaxExtend( target, src ) {\n\tvar key, deep,\n\t\tflatOptions = jQuery.ajaxSettings.flatOptions || {};\n\n\tfor ( key in src ) {\n\t\tif ( src[ key ] !== undefined ) {\n\t\t\t( flatOptions[ key ] ? target : ( deep || ( deep = {} ) ) )[ key ] = src[ key ];\n\t\t}\n\t}\n\tif ( deep ) {\n\t\tjQuery.extend( true, target, deep );\n\t}\n\n\treturn target;\n}\n\n/* Handles responses to an ajax request:\n * - finds the right dataType (mediates between content-type and expected dataType)\n * - returns the corresponding response\n */\nfunction ajaxHandleResponses( s, jqXHR, responses ) {\n\n\tvar ct, type, finalDataType, firstDataType,\n\t\tcontents = s.contents,\n\t\tdataTypes = s.dataTypes;\n\n\t// Remove auto dataType and get content-type in the process\n\twhile ( dataTypes[ 0 ] === \"*\" ) {\n\t\tdataTypes.shift();\n\t\tif ( ct === undefined ) {\n\t\t\tct = s.mimeType || jqXHR.getResponseHeader( \"Content-Type\" );\n\t\t}\n\t}\n\n\t// Check if we're dealing with a known content-type\n\tif ( ct ) {\n\t\tfor ( type in contents ) {\n\t\t\tif ( contents[ type ] && contents[ type ].test( ct ) ) {\n\t\t\t\tdataTypes.unshift( type );\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\t// Check to see if we have a response for the expected dataType\n\tif ( dataTypes[ 0 ] in responses ) {\n\t\tfinalDataType = dataTypes[ 0 ];\n\t} else {\n\n\t\t// Try convertible dataTypes\n\t\tfor ( type in responses ) {\n\t\t\tif ( !dataTypes[ 0 ] || s.converters[ type + \" \" + dataTypes[ 0 ] ] ) {\n\t\t\t\tfinalDataType = type;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif ( !firstDataType ) {\n\t\t\t\tfirstDataType = type;\n\t\t\t}\n\t\t}\n\n\t\t// Or just use first one\n\t\tfinalDataType = finalDataType || firstDataType;\n\t}\n\n\t// If we found a dataType\n\t// We add the dataType to the list if needed\n\t// and return the corresponding response\n\tif ( finalDataType ) {\n\t\tif ( finalDataType !== dataTypes[ 0 ] ) {\n\t\t\tdataTypes.unshift( finalDataType );\n\t\t}\n\t\treturn responses[ finalDataType ];\n\t}\n}\n\n/* Chain conversions given the request and the original response\n * Also sets the responseXXX fields on the jqXHR instance\n */\nfunction ajaxConvert( s, response, jqXHR, isSuccess ) {\n\tvar conv2, current, conv, tmp, prev,\n\t\tconverters = {},\n\n\t\t// Work with a copy of dataTypes in case we need to modify it for conversion\n\t\tdataTypes = s.dataTypes.slice();\n\n\t// Create converters map with lowercased keys\n\tif ( dataTypes[ 1 ] ) {\n\t\tfor ( conv in s.converters ) {\n\t\t\tconverters[ conv.toLowerCase() ] = s.converters[ conv ];\n\t\t}\n\t}\n\n\tcurrent = dataTypes.shift();\n\n\t// Convert to each sequential dataType\n\twhile ( current ) {\n\n\t\tif ( s.responseFields[ current ] ) {\n\t\t\tjqXHR[ s.responseFields[ current ] ] = response;\n\t\t}\n\n\t\t// Apply the dataFilter if provided\n\t\tif ( !prev && isSuccess && s.dataFilter ) {\n\t\t\tresponse = s.dataFilter( response, s.dataType );\n\t\t}\n\n\t\tprev = current;\n\t\tcurrent = dataTypes.shift();\n\n\t\tif ( current ) {\n\n\t\t\t// There's only work to do if current dataType is non-auto\n\t\t\tif ( current === \"*\" ) {\n\n\t\t\t\tcurrent = prev;\n\n\t\t\t// Convert response if prev dataType is non-auto and differs from current\n\t\t\t} else if ( prev !== \"*\" && prev !== current ) {\n\n\t\t\t\t// Seek a direct converter\n\t\t\t\tconv = converters[ prev + \" \" + current ] || converters[ \"* \" + current ];\n\n\t\t\t\t// If none found, seek a pair\n\t\t\t\tif ( !conv ) {\n\t\t\t\t\tfor ( conv2 in converters ) {\n\n\t\t\t\t\t\t// If conv2 outputs current\n\t\t\t\t\t\ttmp = conv2.split( \" \" );\n\t\t\t\t\t\tif ( tmp[ 1 ] === current ) {\n\n\t\t\t\t\t\t\t// If prev can be converted to accepted input\n\t\t\t\t\t\t\tconv = converters[ prev + \" \" + tmp[ 0 ] ] ||\n\t\t\t\t\t\t\t\tconverters[ \"* \" + tmp[ 0 ] ];\n\t\t\t\t\t\t\tif ( conv ) {\n\n\t\t\t\t\t\t\t\t// Condense equivalence converters\n\t\t\t\t\t\t\t\tif ( conv === true ) {\n\t\t\t\t\t\t\t\t\tconv = converters[ conv2 ];\n\n\t\t\t\t\t\t\t\t// Otherwise, insert the intermediate dataType\n\t\t\t\t\t\t\t\t} else if ( converters[ conv2 ] !== true ) {\n\t\t\t\t\t\t\t\t\tcurrent = tmp[ 0 ];\n\t\t\t\t\t\t\t\t\tdataTypes.unshift( tmp[ 1 ] );\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Apply converter (if not an equivalence)\n\t\t\t\tif ( conv !== true ) {\n\n\t\t\t\t\t// Unless errors are allowed to bubble, catch and return them\n\t\t\t\t\tif ( conv && s.throws ) {\n\t\t\t\t\t\tresponse = conv( response );\n\t\t\t\t\t} else {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tresponse = conv( response );\n\t\t\t\t\t\t} catch ( e ) {\n\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\tstate: \"parsererror\",\n\t\t\t\t\t\t\t\terror: conv ? e : \"No conversion from \" + prev + \" to \" + current\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn { state: \"success\", data: response };\n}\n\njQuery.extend( {\n\n\t// Counter for holding the number of active queries\n\tactive: 0,\n\n\t// Last-Modified header cache for next request\n\tlastModified: {},\n\tetag: {},\n\n\tajaxSettings: {\n\t\turl: location.href,\n\t\ttype: \"GET\",\n\t\tisLocal: rlocalProtocol.test( location.protocol ),\n\t\tglobal: true,\n\t\tprocessData: true,\n\t\tasync: true,\n\t\tcontentType: \"application/x-www-form-urlencoded; charset=UTF-8\",\n\n\t\t/*\n\t\ttimeout: 0,\n\t\tdata: null,\n\t\tdataType: null,\n\t\tusername: null,\n\t\tpassword: null,\n\t\tcache: null,\n\t\tthrows: false,\n\t\ttraditional: false,\n\t\theaders: {},\n\t\t*/\n\n\t\taccepts: {\n\t\t\t\"*\": allTypes,\n\t\t\ttext: \"text/plain\",\n\t\t\thtml: \"text/html\",\n\t\t\txml: \"application/xml, text/xml\",\n\t\t\tjson: \"application/json, text/javascript\"\n\t\t},\n\n\t\tcontents: {\n\t\t\txml: /\\bxml\\b/,\n\t\t\thtml: /\\bhtml/,\n\t\t\tjson: /\\bjson\\b/\n\t\t},\n\n\t\tresponseFields: {\n\t\t\txml: \"responseXML\",\n\t\t\ttext: \"responseText\",\n\t\t\tjson: \"responseJSON\"\n\t\t},\n\n\t\t// Data converters\n\t\t// Keys separate source (or catchall \"*\") and destination types with a single space\n\t\tconverters: {\n\n\t\t\t// Convert anything to text\n\t\t\t\"* text\": String,\n\n\t\t\t// Text to html (true = no transformation)\n\t\t\t\"text html\": true,\n\n\t\t\t// Evaluate text as a json expression\n\t\t\t\"text json\": JSON.parse,\n\n\t\t\t// Parse text as xml\n\t\t\t\"text xml\": jQuery.parseXML\n\t\t},\n\n\t\t// For options that shouldn't be deep extended:\n\t\t// you can add your own custom options here if\n\t\t// and when you create one that shouldn't be\n\t\t// deep extended (see ajaxExtend)\n\t\tflatOptions: {\n\t\t\turl: true,\n\t\t\tcontext: true\n\t\t}\n\t},\n\n\t// Creates a full fledged settings object into target\n\t// with both ajaxSettings and settings fields.\n\t// If target is omitted, writes into ajaxSettings.\n\tajaxSetup: function( target, settings ) {\n\t\treturn settings ?\n\n\t\t\t// Building a settings object\n\t\t\tajaxExtend( ajaxExtend( target, jQuery.ajaxSettings ), settings ) :\n\n\t\t\t// Extending ajaxSettings\n\t\t\tajaxExtend( jQuery.ajaxSettings, target );\n\t},\n\n\tajaxPrefilter: addToPrefiltersOrTransports( prefilters ),\n\tajaxTransport: addToPrefiltersOrTransports( transports ),\n\n\t// Main method\n\tajax: function( url, options ) {\n\n\t\t// If url is an object, simulate pre-1.5 signature\n\t\tif ( typeof url === \"object\" ) {\n\t\t\toptions = url;\n\t\t\turl = undefined;\n\t\t}\n\n\t\t// Force options to be an object\n\t\toptions = options || {};\n\n\t\tvar transport,\n\n\t\t\t// URL without anti-cache param\n\t\t\tcacheURL,\n\n\t\t\t// Response headers\n\t\t\tresponseHeadersString,\n\t\t\tresponseHeaders,\n\n\t\t\t// timeout handle\n\t\t\ttimeoutTimer,\n\n\t\t\t// Url cleanup var\n\t\t\turlAnchor,\n\n\t\t\t// Request state (becomes false upon send and true upon completion)\n\t\t\tcompleted,\n\n\t\t\t// To know if global events are to be dispatched\n\t\t\tfireGlobals,\n\n\t\t\t// Loop variable\n\t\t\ti,\n\n\t\t\t// uncached part of the url\n\t\t\tuncached,\n\n\t\t\t// Create the final options object\n\t\t\ts = jQuery.ajaxSetup( {}, options ),\n\n\t\t\t// Callbacks context\n\t\t\tcallbackContext = s.context || s,\n\n\t\t\t// Context for global events is callbackContext if it is a DOM node or jQuery collection\n\t\t\tglobalEventContext = s.context &&\n\t\t\t\t( callbackContext.nodeType || callbackContext.jquery ) ?\n\t\t\t\t\tjQuery( callbackContext ) :\n\t\t\t\t\tjQuery.event,\n\n\t\t\t// Deferreds\n\t\t\tdeferred = jQuery.Deferred(),\n\t\t\tcompleteDeferred = jQuery.Callbacks( \"once memory\" ),\n\n\t\t\t// Status-dependent callbacks\n\t\t\tstatusCode = s.statusCode || {},\n\n\t\t\t// Headers (they are sent all at once)\n\t\t\trequestHeaders = {},\n\t\t\trequestHeadersNames = {},\n\n\t\t\t// Default abort message\n\t\t\tstrAbort = \"canceled\",\n\n\t\t\t// Fake xhr\n\t\t\tjqXHR = {\n\t\t\t\treadyState: 0,\n\n\t\t\t\t// Builds headers hashtable if needed\n\t\t\t\tgetResponseHeader: function( key ) {\n\t\t\t\t\tvar match;\n\t\t\t\t\tif ( completed ) {\n\t\t\t\t\t\tif ( !responseHeaders ) {\n\t\t\t\t\t\t\tresponseHeaders = {};\n\t\t\t\t\t\t\twhile ( ( match = rheaders.exec( responseHeadersString ) ) ) {\n\t\t\t\t\t\t\t\tresponseHeaders[ match[ 1 ].toLowerCase() ] = match[ 2 ];\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tmatch = responseHeaders[ key.toLowerCase() ];\n\t\t\t\t\t}\n\t\t\t\t\treturn match == null ? null : match;\n\t\t\t\t},\n\n\t\t\t\t// Raw string\n\t\t\t\tgetAllResponseHeaders: function() {\n\t\t\t\t\treturn completed ? responseHeadersString : null;\n\t\t\t\t},\n\n\t\t\t\t// Caches the header\n\t\t\t\tsetRequestHeader: function( name, value ) {\n\t\t\t\t\tif ( completed == null ) {\n\t\t\t\t\t\tname = requestHeadersNames[ name.toLowerCase() ] =\n\t\t\t\t\t\t\trequestHeadersNames[ name.toLowerCase() ] || name;\n\t\t\t\t\t\trequestHeaders[ name ] = value;\n\t\t\t\t\t}\n\t\t\t\t\treturn this;\n\t\t\t\t},\n\n\t\t\t\t// Overrides response content-type header\n\t\t\t\toverrideMimeType: function( type ) {\n\t\t\t\t\tif ( completed == null ) {\n\t\t\t\t\t\ts.mimeType = type;\n\t\t\t\t\t}\n\t\t\t\t\treturn this;\n\t\t\t\t},\n\n\t\t\t\t// Status-dependent callbacks\n\t\t\t\tstatusCode: function( map ) {\n\t\t\t\t\tvar code;\n\t\t\t\t\tif ( map ) {\n\t\t\t\t\t\tif ( completed ) {\n\n\t\t\t\t\t\t\t// Execute the appropriate callbacks\n\t\t\t\t\t\t\tjqXHR.always( map[ jqXHR.status ] );\n\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\t// Lazy-add the new callbacks in a way that preserves old ones\n\t\t\t\t\t\t\tfor ( code in map ) {\n\t\t\t\t\t\t\t\tstatusCode[ code ] = [ statusCode[ code ], map[ code ] ];\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\treturn this;\n\t\t\t\t},\n\n\t\t\t\t// Cancel the request\n\t\t\t\tabort: function( statusText ) {\n\t\t\t\t\tvar finalText = statusText || strAbort;\n\t\t\t\t\tif ( transport ) {\n\t\t\t\t\t\ttransport.abort( finalText );\n\t\t\t\t\t}\n\t\t\t\t\tdone( 0, finalText );\n\t\t\t\t\treturn this;\n\t\t\t\t}\n\t\t\t};\n\n\t\t// Attach deferreds\n\t\tdeferred.promise( jqXHR );\n\n\t\t// Add protocol if not provided (prefilters might expect it)\n\t\t// Handle falsy url in the settings object (#10093: consistency with old signature)\n\t\t// We also use the url parameter if available\n\t\ts.url = ( ( url || s.url || location.href ) + \"\" )\n\t\t\t.replace( rprotocol, location.protocol + \"//\" );\n\n\t\t// Alias method option to type as per ticket #12004\n\t\ts.type = options.method || options.type || s.method || s.type;\n\n\t\t// Extract dataTypes list\n\t\ts.dataTypes = ( s.dataType || \"*\" ).toLowerCase().match( rnothtmlwhite ) || [ \"\" ];\n\n\t\t// A cross-domain request is in order when the origin doesn't match the current origin.\n\t\tif ( s.crossDomain == null ) {\n\t\t\turlAnchor = document.createElement( \"a\" );\n\n\t\t\t// Support: IE <=8 - 11, Edge 12 - 15\n\t\t\t// IE throws exception on accessing the href property if url is malformed,\n\t\t\t// e.g. http://example.com:80x/\n\t\t\ttry {\n\t\t\t\turlAnchor.href = s.url;\n\n\t\t\t\t// Support: IE <=8 - 11 only\n\t\t\t\t// Anchor's host property isn't correctly set when s.url is relative\n\t\t\t\turlAnchor.href = urlAnchor.href;\n\t\t\t\ts.crossDomain = originAnchor.protocol + \"//\" + originAnchor.host !==\n\t\t\t\t\turlAnchor.protocol + \"//\" + urlAnchor.host;\n\t\t\t} catch ( e ) {\n\n\t\t\t\t// If there is an error parsing the URL, assume it is crossDomain,\n\t\t\t\t// it can be rejected by the transport if it is invalid\n\t\t\t\ts.crossDomain = true;\n\t\t\t}\n\t\t}\n\n\t\t// Convert data if not already a string\n\t\tif ( s.data && s.processData && typeof s.data !== \"string\" ) {\n\t\t\ts.data = jQuery.param( s.data, s.traditional );\n\t\t}\n\n\t\t// Apply prefilters\n\t\tinspectPrefiltersOrTransports( prefilters, s, options, jqXHR );\n\n\t\t// If request was aborted inside a prefilter, stop there\n\t\tif ( completed ) {\n\t\t\treturn jqXHR;\n\t\t}\n\n\t\t// We can fire global events as of now if asked to\n\t\t// Don't fire events if jQuery.event is undefined in an AMD-usage scenario (#15118)\n\t\tfireGlobals = jQuery.event && s.global;\n\n\t\t// Watch for a new set of requests\n\t\tif ( fireGlobals && jQuery.active++ === 0 ) {\n\t\t\tjQuery.event.trigger( \"ajaxStart\" );\n\t\t}\n\n\t\t// Uppercase the type\n\t\ts.type = s.type.toUpperCase();\n\n\t\t// Determine if request has content\n\t\ts.hasContent = !rnoContent.test( s.type );\n\n\t\t// Save the URL in case we're toying with the If-Modified-Since\n\t\t// and/or If-None-Match header later on\n\t\t// Remove hash to simplify url manipulation\n\t\tcacheURL = s.url.replace( rhash, \"\" );\n\n\t\t// More options handling for requests with no content\n\t\tif ( !s.hasContent ) {\n\n\t\t\t// Remember the hash so we can put it back\n\t\t\tuncached = s.url.slice( cacheURL.length );\n\n\t\t\t// If data is available and should be processed, append data to url\n\t\t\tif ( s.data && ( s.processData || typeof s.data === \"string\" ) ) {\n\t\t\t\tcacheURL += ( rquery.test( cacheURL ) ? \"&\" : \"?\" ) + s.data;\n\n\t\t\t\t// #9682: remove data so that it's not used in an eventual retry\n\t\t\t\tdelete s.data;\n\t\t\t}\n\n\t\t\t// Add or update anti-cache param if needed\n\t\t\tif ( s.cache === false ) {\n\t\t\t\tcacheURL = cacheURL.replace( rantiCache, \"$1\" );\n\t\t\t\tuncached = ( rquery.test( cacheURL ) ? \"&\" : \"?\" ) + \"_=\" + ( nonce++ ) + uncached;\n\t\t\t}\n\n\t\t\t// Put hash and anti-cache on the URL that will be requested (gh-1732)\n\t\t\ts.url = cacheURL + uncached;\n\n\t\t// Change '%20' to '+' if this is encoded form body content (gh-2658)\n\t\t} else if ( s.data && s.processData &&\n\t\t\t( s.contentType || \"\" ).indexOf( \"application/x-www-form-urlencoded\" ) === 0 ) {\n\t\t\ts.data = s.data.replace( r20, \"+\" );\n\t\t}\n\n\t\t// Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.\n\t\tif ( s.ifModified ) {\n\t\t\tif ( jQuery.lastModified[ cacheURL ] ) {\n\t\t\t\tjqXHR.setRequestHeader( \"If-Modified-Since\", jQuery.lastModified[ cacheURL ] );\n\t\t\t}\n\t\t\tif ( jQuery.etag[ cacheURL ] ) {\n\t\t\t\tjqXHR.setRequestHeader( \"If-None-Match\", jQuery.etag[ cacheURL ] );\n\t\t\t}\n\t\t}\n\n\t\t// Set the correct header, if data is being sent\n\t\tif ( s.data && s.hasContent && s.contentType !== false || options.contentType ) {\n\t\t\tjqXHR.setRequestHeader( \"Content-Type\", s.contentType );\n\t\t}\n\n\t\t// Set the Accepts header for the server, depending on the dataType\n\t\tjqXHR.setRequestHeader(\n\t\t\t\"Accept\",\n\t\t\ts.dataTypes[ 0 ] && s.accepts[ s.dataTypes[ 0 ] ] ?\n\t\t\t\ts.accepts[ s.dataTypes[ 0 ] ] +\n\t\t\t\t\t( s.dataTypes[ 0 ] !== \"*\" ? \", \" + allTypes + \"; q=0.01\" : \"\" ) :\n\t\t\t\ts.accepts[ \"*\" ]\n\t\t);\n\n\t\t// Check for headers option\n\t\tfor ( i in s.headers ) {\n\t\t\tjqXHR.setRequestHeader( i, s.headers[ i ] );\n\t\t}\n\n\t\t// Allow custom headers/mimetypes and early abort\n\t\tif ( s.beforeSend &&\n\t\t\t( s.beforeSend.call( callbackContext, jqXHR, s ) === false || completed ) ) {\n\n\t\t\t// Abort if not done already and return\n\t\t\treturn jqXHR.abort();\n\t\t}\n\n\t\t// Aborting is no longer a cancellation\n\t\tstrAbort = \"abort\";\n\n\t\t// Install callbacks on deferreds\n\t\tcompleteDeferred.add( s.complete );\n\t\tjqXHR.done( s.success );\n\t\tjqXHR.fail( s.error );\n\n\t\t// Get transport\n\t\ttransport = inspectPrefiltersOrTransports( transports, s, options, jqXHR );\n\n\t\t// If no transport, we auto-abort\n\t\tif ( !transport ) {\n\t\t\tdone( -1, \"No Transport\" );\n\t\t} else {\n\t\t\tjqXHR.readyState = 1;\n\n\t\t\t// Send global event\n\t\t\tif ( fireGlobals ) {\n\t\t\t\tglobalEventContext.trigger( \"ajaxSend\", [ jqXHR, s ] );\n\t\t\t}\n\n\t\t\t// If request was aborted inside ajaxSend, stop there\n\t\t\tif ( completed ) {\n\t\t\t\treturn jqXHR;\n\t\t\t}\n\n\t\t\t// Timeout\n\t\t\tif ( s.async && s.timeout > 0 ) {\n\t\t\t\ttimeoutTimer = window.setTimeout( function() {\n\t\t\t\t\tjqXHR.abort( \"timeout\" );\n\t\t\t\t}, s.timeout );\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tcompleted = false;\n\t\t\t\ttransport.send( requestHeaders, done );\n\t\t\t} catch ( e ) {\n\n\t\t\t\t// Rethrow post-completion exceptions\n\t\t\t\tif ( completed ) {\n\t\t\t\t\tthrow e;\n\t\t\t\t}\n\n\t\t\t\t// Propagate others as results\n\t\t\t\tdone( -1, e );\n\t\t\t}\n\t\t}\n\n\t\t// Callback for when everything is done\n\t\tfunction done( status, nativeStatusText, responses, headers ) {\n\t\t\tvar isSuccess, success, error, response, modified,\n\t\t\t\tstatusText = nativeStatusText;\n\n\t\t\t// Ignore repeat invocations\n\t\t\tif ( completed ) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tcompleted = true;\n\n\t\t\t// Clear timeout if it exists\n\t\t\tif ( timeoutTimer ) {\n\t\t\t\twindow.clearTimeout( timeoutTimer );\n\t\t\t}\n\n\t\t\t// Dereference transport for early garbage collection\n\t\t\t// (no matter how long the jqXHR object will be used)\n\t\t\ttransport = undefined;\n\n\t\t\t// Cache response headers\n\t\t\tresponseHeadersString = headers || \"\";\n\n\t\t\t// Set readyState\n\t\t\tjqXHR.readyState = status > 0 ? 4 : 0;\n\n\t\t\t// Determine if successful\n\t\t\tisSuccess = status >= 200 && status < 300 || status === 304;\n\n\t\t\t// Get response data\n\t\t\tif ( responses ) {\n\t\t\t\tresponse = ajaxHandleResponses( s, jqXHR, responses );\n\t\t\t}\n\n\t\t\t// Convert no matter what (that way responseXXX fields are always set)\n\t\t\tresponse = ajaxConvert( s, response, jqXHR, isSuccess );\n\n\t\t\t// If successful, handle type chaining\n\t\t\tif ( isSuccess ) {\n\n\t\t\t\t// Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.\n\t\t\t\tif ( s.ifModified ) {\n\t\t\t\t\tmodified = jqXHR.getResponseHeader( \"Last-Modified\" );\n\t\t\t\t\tif ( modified ) {\n\t\t\t\t\t\tjQuery.lastModified[ cacheURL ] = modified;\n\t\t\t\t\t}\n\t\t\t\t\tmodified = jqXHR.getResponseHeader( \"etag\" );\n\t\t\t\t\tif ( modified ) {\n\t\t\t\t\t\tjQuery.etag[ cacheURL ] = modified;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// if no content\n\t\t\t\tif ( status === 204 || s.type === \"HEAD\" ) {\n\t\t\t\t\tstatusText = \"nocontent\";\n\n\t\t\t\t// if not modified\n\t\t\t\t} else if ( status === 304 ) {\n\t\t\t\t\tstatusText = \"notmodified\";\n\n\t\t\t\t// If we have data, let's convert it\n\t\t\t\t} else {\n\t\t\t\t\tstatusText = response.state;\n\t\t\t\t\tsuccess = response.data;\n\t\t\t\t\terror = response.error;\n\t\t\t\t\tisSuccess = !error;\n\t\t\t\t}\n\t\t\t} else {\n\n\t\t\t\t// Extract error from statusText and normalize for non-aborts\n\t\t\t\terror = statusText;\n\t\t\t\tif ( status || !statusText ) {\n\t\t\t\t\tstatusText = \"error\";\n\t\t\t\t\tif ( status < 0 ) {\n\t\t\t\t\t\tstatus = 0;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Set data for the fake xhr object\n\t\t\tjqXHR.status = status;\n\t\t\tjqXHR.statusText = ( nativeStatusText || statusText ) + \"\";\n\n\t\t\t// Success/Error\n\t\t\tif ( isSuccess ) {\n\t\t\t\tdeferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] );\n\t\t\t} else {\n\t\t\t\tdeferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] );\n\t\t\t}\n\n\t\t\t// Status-dependent callbacks\n\t\t\tjqXHR.statusCode( statusCode );\n\t\t\tstatusCode = undefined;\n\n\t\t\tif ( fireGlobals ) {\n\t\t\t\tglobalEventContext.trigger( isSuccess ? \"ajaxSuccess\" : \"ajaxError\",\n\t\t\t\t\t[ jqXHR, s, isSuccess ? success : error ] );\n\t\t\t}\n\n\t\t\t// Complete\n\t\t\tcompleteDeferred.fireWith( callbackContext, [ jqXHR, statusText ] );\n\n\t\t\tif ( fireGlobals ) {\n\t\t\t\tglobalEventContext.trigger( \"ajaxComplete\", [ jqXHR, s ] );\n\n\t\t\t\t// Handle the global AJAX counter\n\t\t\t\tif ( !( --jQuery.active ) ) {\n\t\t\t\t\tjQuery.event.trigger( \"ajaxStop\" );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn jqXHR;\n\t},\n\n\tgetJSON: function( url, data, callback ) {\n\t\treturn jQuery.get( url, data, callback, \"json\" );\n\t},\n\n\tgetScript: function( url, callback ) {\n\t\treturn jQuery.get( url, undefined, callback, \"script\" );\n\t}\n} );\n\njQuery.each( [ \"get\", \"post\" ], function( i, method ) {\n\tjQuery[ method ] = function( url, data, callback, type ) {\n\n\t\t// Shift arguments if data argument was omitted\n\t\tif ( isFunction( data ) ) {\n\t\t\ttype = type || callback;\n\t\t\tcallback = data;\n\t\t\tdata = undefined;\n\t\t}\n\n\t\t// The url can be an options object (which then must have .url)\n\t\treturn jQuery.ajax( jQuery.extend( {\n\t\t\turl: url,\n\t\t\ttype: method,\n\t\t\tdataType: type,\n\t\t\tdata: data,\n\t\t\tsuccess: callback\n\t\t}, jQuery.isPlainObject( url ) && url ) );\n\t};\n} );\n\n\njQuery._evalUrl = function( url ) {\n\treturn jQuery.ajax( {\n\t\turl: url,\n\n\t\t// Make this explicit, since user can override this through ajaxSetup (#11264)\n\t\ttype: \"GET\",\n\t\tdataType: \"script\",\n\t\tcache: true,\n\t\tasync: false,\n\t\tglobal: false,\n\t\t\"throws\": true\n\t} );\n};\n\n\njQuery.fn.extend( {\n\twrapAll: function( html ) {\n\t\tvar wrap;\n\n\t\tif ( this[ 0 ] ) {\n\t\t\tif ( isFunction( html ) ) {\n\t\t\t\thtml = html.call( this[ 0 ] );\n\t\t\t}\n\n\t\t\t// The elements to wrap the target around\n\t\t\twrap = jQuery( html, this[ 0 ].ownerDocument ).eq( 0 ).clone( true );\n\n\t\t\tif ( this[ 0 ].parentNode ) {\n\t\t\t\twrap.insertBefore( this[ 0 ] );\n\t\t\t}\n\n\t\t\twrap.map( function() {\n\t\t\t\tvar elem = this;\n\n\t\t\t\twhile ( elem.firstElementChild ) {\n\t\t\t\t\telem = elem.firstElementChild;\n\t\t\t\t}\n\n\t\t\t\treturn elem;\n\t\t\t} ).append( this );\n\t\t}\n\n\t\treturn this;\n\t},\n\n\twrapInner: function( html ) {\n\t\tif ( isFunction( html ) ) {\n\t\t\treturn this.each( function( i ) {\n\t\t\t\tjQuery( this ).wrapInner( html.call( this, i ) );\n\t\t\t} );\n\t\t}\n\n\t\treturn this.each( function() {\n\t\t\tvar self = jQuery( this ),\n\t\t\t\tcontents = self.contents();\n\n\t\t\tif ( contents.length ) {\n\t\t\t\tcontents.wrapAll( html );\n\n\t\t\t} else {\n\t\t\t\tself.append( html );\n\t\t\t}\n\t\t} );\n\t},\n\n\twrap: function( html ) {\n\t\tvar htmlIsFunction = isFunction( html );\n\n\t\treturn this.each( function( i ) {\n\t\t\tjQuery( this ).wrapAll( htmlIsFunction ? html.call( this, i ) : html );\n\t\t} );\n\t},\n\n\tunwrap: function( selector ) {\n\t\tthis.parent( selector ).not( \"body\" ).each( function() {\n\t\t\tjQuery( this ).replaceWith( this.childNodes );\n\t\t} );\n\t\treturn this;\n\t}\n} );\n\n\njQuery.expr.pseudos.hidden = function( elem ) {\n\treturn !jQuery.expr.pseudos.visible( elem );\n};\njQuery.expr.pseudos.visible = function( elem ) {\n\treturn !!( elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length );\n};\n\n\n\n\njQuery.ajaxSettings.xhr = function() {\n\ttry {\n\t\treturn new window.XMLHttpRequest();\n\t} catch ( e ) {}\n};\n\nvar xhrSuccessStatus = {\n\n\t\t// File protocol always yields status code 0, assume 200\n\t\t0: 200,\n\n\t\t// Support: IE <=9 only\n\t\t// #1450: sometimes IE returns 1223 when it should be 204\n\t\t1223: 204\n\t},\n\txhrSupported = jQuery.ajaxSettings.xhr();\n\nsupport.cors = !!xhrSupported && ( \"withCredentials\" in xhrSupported );\nsupport.ajax = xhrSupported = !!xhrSupported;\n\njQuery.ajaxTransport( function( options ) {\n\tvar callback, errorCallback;\n\n\t// Cross domain only allowed if supported through XMLHttpRequest\n\tif ( support.cors || xhrSupported && !options.crossDomain ) {\n\t\treturn {\n\t\t\tsend: function( headers, complete ) {\n\t\t\t\tvar i,\n\t\t\t\t\txhr = options.xhr();\n\n\t\t\t\txhr.open(\n\t\t\t\t\toptions.type,\n\t\t\t\t\toptions.url,\n\t\t\t\t\toptions.async,\n\t\t\t\t\toptions.username,\n\t\t\t\t\toptions.password\n\t\t\t\t);\n\n\t\t\t\t// Apply custom fields if provided\n\t\t\t\tif ( options.xhrFields ) {\n\t\t\t\t\tfor ( i in options.xhrFields ) {\n\t\t\t\t\t\txhr[ i ] = options.xhrFields[ i ];\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Override mime type if needed\n\t\t\t\tif ( options.mimeType && xhr.overrideMimeType ) {\n\t\t\t\t\txhr.overrideMimeType( options.mimeType );\n\t\t\t\t}\n\n\t\t\t\t// X-Requested-With header\n\t\t\t\t// For cross-domain requests, seeing as conditions for a preflight are\n\t\t\t\t// akin to a jigsaw puzzle, we simply never set it to be sure.\n\t\t\t\t// (it can always be set on a per-request basis or even using ajaxSetup)\n\t\t\t\t// For same-domain requests, won't change header if already provided.\n\t\t\t\tif ( !options.crossDomain && !headers[ \"X-Requested-With\" ] ) {\n\t\t\t\t\theaders[ \"X-Requested-With\" ] = \"XMLHttpRequest\";\n\t\t\t\t}\n\n\t\t\t\t// Set headers\n\t\t\t\tfor ( i in headers ) {\n\t\t\t\t\txhr.setRequestHeader( i, headers[ i ] );\n\t\t\t\t}\n\n\t\t\t\t// Callback\n\t\t\t\tcallback = function( type ) {\n\t\t\t\t\treturn function() {\n\t\t\t\t\t\tif ( callback ) {\n\t\t\t\t\t\t\tcallback = errorCallback = xhr.onload =\n\t\t\t\t\t\t\t\txhr.onerror = xhr.onabort = xhr.ontimeout =\n\t\t\t\t\t\t\t\t\txhr.onreadystatechange = null;\n\n\t\t\t\t\t\t\tif ( type === \"abort\" ) {\n\t\t\t\t\t\t\t\txhr.abort();\n\t\t\t\t\t\t\t} else if ( type === \"error\" ) {\n\n\t\t\t\t\t\t\t\t// Support: IE <=9 only\n\t\t\t\t\t\t\t\t// On a manual native abort, IE9 throws\n\t\t\t\t\t\t\t\t// errors on any property access that is not readyState\n\t\t\t\t\t\t\t\tif ( typeof xhr.status !== \"number\" ) {\n\t\t\t\t\t\t\t\t\tcomplete( 0, \"error\" );\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tcomplete(\n\n\t\t\t\t\t\t\t\t\t\t// File: protocol always yields status 0; see #8605, #14207\n\t\t\t\t\t\t\t\t\t\txhr.status,\n\t\t\t\t\t\t\t\t\t\txhr.statusText\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tcomplete(\n\t\t\t\t\t\t\t\t\txhrSuccessStatus[ xhr.status ] || xhr.status,\n\t\t\t\t\t\t\t\t\txhr.statusText,\n\n\t\t\t\t\t\t\t\t\t// Support: IE <=9 only\n\t\t\t\t\t\t\t\t\t// IE9 has no XHR2 but throws on binary (trac-11426)\n\t\t\t\t\t\t\t\t\t// For XHR2 non-text, let the caller handle it (gh-2498)\n\t\t\t\t\t\t\t\t\t( xhr.responseType || \"text\" ) !== \"text\" ||\n\t\t\t\t\t\t\t\t\ttypeof xhr.responseText !== \"string\" ?\n\t\t\t\t\t\t\t\t\t\t{ binary: xhr.response } :\n\t\t\t\t\t\t\t\t\t\t{ text: xhr.responseText },\n\t\t\t\t\t\t\t\t\txhr.getAllResponseHeaders()\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t};\n\t\t\t\t};\n\n\t\t\t\t// Listen to events\n\t\t\t\txhr.onload = callback();\n\t\t\t\terrorCallback = xhr.onerror = xhr.ontimeout = callback( \"error\" );\n\n\t\t\t\t// Support: IE 9 only\n\t\t\t\t// Use onreadystatechange to replace onabort\n\t\t\t\t// to handle uncaught aborts\n\t\t\t\tif ( xhr.onabort !== undefined ) {\n\t\t\t\t\txhr.onabort = errorCallback;\n\t\t\t\t} else {\n\t\t\t\t\txhr.onreadystatechange = function() {\n\n\t\t\t\t\t\t// Check readyState before timeout as it changes\n\t\t\t\t\t\tif ( xhr.readyState === 4 ) {\n\n\t\t\t\t\t\t\t// Allow onerror to be called first,\n\t\t\t\t\t\t\t// but that will not handle a native abort\n\t\t\t\t\t\t\t// Also, save errorCallback to a variable\n\t\t\t\t\t\t\t// as xhr.onerror cannot be accessed\n\t\t\t\t\t\t\twindow.setTimeout( function() {\n\t\t\t\t\t\t\t\tif ( callback ) {\n\t\t\t\t\t\t\t\t\terrorCallback();\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} );\n\t\t\t\t\t\t}\n\t\t\t\t\t};\n\t\t\t\t}\n\n\t\t\t\t// Create the abort callback\n\t\t\t\tcallback = callback( \"abort\" );\n\n\t\t\t\ttry {\n\n\t\t\t\t\t// Do send the request (this may raise an exception)\n\t\t\t\t\txhr.send( options.hasContent && options.data || null );\n\t\t\t\t} catch ( e ) {\n\n\t\t\t\t\t// #14683: Only rethrow if this hasn't been notified as an error yet\n\t\t\t\t\tif ( callback ) {\n\t\t\t\t\t\tthrow e;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t},\n\n\t\t\tabort: function() {\n\t\t\t\tif ( callback ) {\n\t\t\t\t\tcallback();\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\t}\n} );\n\n\n\n\n// Prevent auto-execution of scripts when no explicit dataType was provided (See gh-2432)\njQuery.ajaxPrefilter( function( s ) {\n\tif ( s.crossDomain ) {\n\t\ts.contents.script = false;\n\t}\n} );\n\n// Install script dataType\njQuery.ajaxSetup( {\n\taccepts: {\n\t\tscript: \"text/javascript, application/javascript, \" +\n\t\t\t\"application/ecmascript, application/x-ecmascript\"\n\t},\n\tcontents: {\n\t\tscript: /\\b(?:java|ecma)script\\b/\n\t},\n\tconverters: {\n\t\t\"text script\": function( text ) {\n\t\t\tjQuery.globalEval( text );\n\t\t\treturn text;\n\t\t}\n\t}\n} );\n\n// Handle cache's special case and crossDomain\njQuery.ajaxPrefilter( \"script\", function( s ) {\n\tif ( s.cache === undefined ) {\n\t\ts.cache = false;\n\t}\n\tif ( s.crossDomain ) {\n\t\ts.type = \"GET\";\n\t}\n} );\n\n// Bind script tag hack transport\njQuery.ajaxTransport( \"script\", function( s ) {\n\n\t// This transport only deals with cross domain requests\n\tif ( s.crossDomain ) {\n\t\tvar script, callback;\n\t\treturn {\n\t\t\tsend: function( _, complete ) {\n\t\t\t\tscript = jQuery( \" {% endblock foot_js %} + + diff --git a/sapl/templates/base/autores_duplicados.html b/sapl/templates/base/autores_duplicados.html new file mode 100644 index 000000000..361ff7add --- /dev/null +++ b/sapl/templates/base/autores_duplicados.html @@ -0,0 +1,30 @@ +{% extends "base.html" %} +{% load common_tags %} +{% block base_content %} +
    +

    Lista de Autores Duplicados

    +
    + {% if not autores_duplicados %} +

    {{ NO_ENTRIES_MSG }}

    + {% else %} + + + + + + + + + {% for autor in autores_duplicados %} + + + + + {% endfor %} + +
    AutorQuantidade
    {{ autor.nome }}{{ autor.count }}
    + {% endif %} +
    + {% include 'paginacao.html'%} +
    +{% endblock base_content %} \ No newline at end of file diff --git a/sapl/templates/base/bancada_comissao_autor_externo.html b/sapl/templates/base/bancada_comissao_autor_externo.html new file mode 100644 index 000000000..9edaf21f7 --- /dev/null +++ b/sapl/templates/base/bancada_comissao_autor_externo.html @@ -0,0 +1,36 @@ +{% extends "base.html" %} +{% load common_tags %} +{% block base_content %} +
    +

    Lista de Bancadas e Comissões com Autor Externo

    +
    + {% if not bancada_comissao_autor_externo %} +

    {{ NO_ENTRIES_MSG }}

    + {% else %} + + + + + + + + + + {% for autor, objeto, descricao_objeto, link in bancada_comissao_autor_externo %} + + + + + + {% endfor %} + +
    Descrição do ObjetoObjetoAutor
    {{ descricao_objeto }} + {{ objeto }} + + {{ autor.nome }} +
    + {% endif %} +
    + {% include 'paginacao.html'%} +
    +{% endblock base_content %} \ No newline at end of file diff --git a/sapl/templates/base/filiacoes_sem_data_filiacao.html b/sapl/templates/base/filiacoes_sem_data_filiacao.html new file mode 100644 index 000000000..22d24f972 --- /dev/null +++ b/sapl/templates/base/filiacoes_sem_data_filiacao.html @@ -0,0 +1,32 @@ +{% extends "base.html" %} +{% load common_tags %} +{% block base_content %} +
    +

    Lista de Filiações sem Data Filiação

    +
    + {% if not filiacoes_sem_data_filiacao %} +

    {{ NO_ENTRIES_MSG }}

    + {% else %} + + + + + + + + + {% for filiacao in filiacoes_sem_data_filiacao %} + + + + + {% endfor %} + +
    Parlamentar FiliadoPartido
    + {{ filiacao.parlamentar }} + {{ filiacao.partido }}
    + {% endif %} +
    + {% include 'paginacao.html'%} +
    +{% endblock base_content %} \ No newline at end of file diff --git a/sapl/templates/base/layouts.yaml b/sapl/templates/base/layouts.yaml index 4f6bbd45d..4768577b8 100644 --- a/sapl/templates/base/layouts.yaml +++ b/sapl/templates/base/layouts.yaml @@ -18,7 +18,9 @@ AppConfig: - esfera_federacao {% trans 'Proposições e Protocolo' %}: - - sequencia_numeracao proposicao_incorporacao_obrigatoria receber_recibo_proposicao + - sequencia_numeracao_proposicao sequencia_numeracao_protocolo + - protocolo_manual receber_recibo_proposicao + - proposicao_incorporacao_obrigatoria escolher_numero_materia_proposicao {% trans 'Textos Articulados' %}: - texto_articulado_proposicao texto_articulado_materia texto_articulado_norma @@ -30,7 +32,8 @@ AppConfig: - assinatura_ata {% trans 'Cronômetros do Painel' %}: - - cronometro_discurso cronometro_aparte cronometro_ordem cronometro_consideracoes + - cronometro_discurso cronometro_aparte + - cronometro_ordem cronometro_consideracoes {% trans 'Configurações do Painel' %}: - mostrar_brasao_painel diff --git a/sapl/templates/base/legislatura_infindavel.html b/sapl/templates/base/legislatura_infindavel.html new file mode 100644 index 000000000..a44c544e1 --- /dev/null +++ b/sapl/templates/base/legislatura_infindavel.html @@ -0,0 +1,32 @@ +{% extends "base.html" %} +{% load common_tags %} +{% block base_content %} +
    +

    Lista de Legislaturas sem Data Fim

    +
    + {% if not legislatura_infindavel %} +

    {{ NO_ENTRIES_MSG }}

    + {% else %} + + + + + + + + + + {% for legislatura in legislatura_infindavel %} + + + + + + {% endfor %} + +
    Número LegislaturaData EleiçãoData Início
    {{ legislatura.numero }}{{ legislatura.data_eleicao }}{{ legislatura.data_inicio }}
    + {% endif %} +
    + {% include 'paginacao.html'%} +
    +{% endblock base_content %} \ No newline at end of file diff --git a/sapl/templates/base/lista_inconsistencias.html b/sapl/templates/base/lista_inconsistencias.html new file mode 100644 index 000000000..121422ade --- /dev/null +++ b/sapl/templates/base/lista_inconsistencias.html @@ -0,0 +1,22 @@ +{% extends "base.html" %} +{% load common_tags %} +{% block base_content %} +
    +

    Lista de Inconsistências

    +
    + + + {% for complemento_link, nome, valor in tabela_inconsistencias %} + + + + + {% endfor %} + +
    + {{ nome }} + {{ valor }}
    +
    + {% include 'paginacao.html'%} +
    +{% endblock base_content %} \ No newline at end of file diff --git a/sapl/templates/base/mandato_sem_data_inicio.html b/sapl/templates/base/mandato_sem_data_inicio.html new file mode 100644 index 000000000..09c151d7a --- /dev/null +++ b/sapl/templates/base/mandato_sem_data_inicio.html @@ -0,0 +1,32 @@ +{% extends "base.html" %} +{% load common_tags %} +{% block base_content %} +
    +

    Lista de Mandatos sem Data Inicial

    +
    + {% if not mandato_sem_data_inicio %} +

    {{ NO_ENTRIES_MSG }}

    + {% else %} + + + + + + + + + {% for mandato in mandato_sem_data_inicio %} + + + + + {% endfor %} + +
    Parlamentar do MandatoLegislatura do Mandato
    + {{ mandato.parlamentar }} + {{ mandato.legislatura }}
    + {% endif %} +
    + {% include 'paginacao.html'%} +
    +{% endblock base_content %} \ No newline at end of file diff --git a/sapl/templates/base/materias_protocolo_inexistente.html b/sapl/templates/base/materias_protocolo_inexistente.html new file mode 100644 index 000000000..879a6d460 --- /dev/null +++ b/sapl/templates/base/materias_protocolo_inexistente.html @@ -0,0 +1,34 @@ +{% extends "base.html" %} +{% load common_tags %} +{% block base_content %} +
    +

    Lista de Matérias Legislativas com Protocolo Inexistente

    +
    + {% if not materias_protocolo_inexistente %} +

    {{ NO_ENTRIES_MSG }}

    + {% else %} + + + + + + + + + + {% for materia, ano, numero_protocolo in materias_protocolo_inexistente %} + + + + + + {% endfor %} + +
    Matéria LegislativaAnoNúmero Protocolo
    + {{ materia }} + {{ ano }}{{ numero_protocolo }}
    + {% endif %} +
    + {% include 'paginacao.html'%} +
    +{% endblock base_content %} \ No newline at end of file diff --git a/sapl/templates/base/parlamentares_duplicados.html b/sapl/templates/base/parlamentares_duplicados.html new file mode 100644 index 000000000..2e63a04bc --- /dev/null +++ b/sapl/templates/base/parlamentares_duplicados.html @@ -0,0 +1,32 @@ +{% extends "base.html" %} +{% load common_tags %} +{% block base_content %} +
    +

    Lista de Parlamentares Duplicados

    +
    + {% if not parlamentares_duplicados %} +

    {{ NO_ENTRIES_MSG }}

    + {% else %} + + + + + + + + + {% for parlamentar, quantidade in parlamentares_duplicados %} + + + + + {% endfor %} + +
    Nome do ParlamentarQuantidade
    + {{ parlamentar }} + {{ quantidade }}
    + {% endif %} +
    + {% include 'paginacao.html'%} +
    +{% endblock base_content %} \ No newline at end of file diff --git a/sapl/templates/base/parlamentares_filiacoes_intersecao.html b/sapl/templates/base/parlamentares_filiacoes_intersecao.html new file mode 100644 index 000000000..b0949e05a --- /dev/null +++ b/sapl/templates/base/parlamentares_filiacoes_intersecao.html @@ -0,0 +1,34 @@ +{% extends "base.html" %} +{% load common_tags %} +{% block base_content %} +
    +

    Lista de Parlamentares com Filiações com Interseção

    +
    + {% if not parlamentares_filiacoes_intersecao %} +

    {{ NO_ENTRIES_MSG }}

    + {% else %} + + + + + + + + + + {% for parlamentar, filiacao_a , filiacao_b in parlamentares_filiacoes_intersecao %} + + + + + + {% endfor %} + +
    ParlamentarFiliação 1Filiação 2
    + {{ parlamentar }} + {{filiacao_a.data|date:"d/m/Y"}} - {{filiacao_a.data_desfiliacao|date:"d/m/Y"}}{{filiacao_b.data|date:"d/m/Y"}} - {{filiacao_b.data_desfiliacao|date:"d/m/Y"}}
    + {% endif %} +
    + {% include 'paginacao.html'%} +
    +{% endblock base_content %} \ No newline at end of file diff --git a/sapl/templates/base/parlamentares_mandatos_intersecao.html b/sapl/templates/base/parlamentares_mandatos_intersecao.html new file mode 100644 index 000000000..2d4c915a4 --- /dev/null +++ b/sapl/templates/base/parlamentares_mandatos_intersecao.html @@ -0,0 +1,34 @@ +{% extends "base.html" %} +{% load common_tags %} +{% block base_content %} +
    +

    Lista de Parlamentares com Mandatos em Interseção

    +
    + {% if not parlamentares_mandatos_intersecao %} +

    {{ NO_ENTRIES_MSG }}

    + {% else %} + + + + + + + + + + {% for parlamentar, mandato_a, mandato_b in parlamentares_mandatos_intersecao %} + + + + + + {% endfor %} + +
    ParlamentarMandato 1Mandato 2
    + {{ parlamentar }} + {{ mandato_a.legislatura}}
    {{mandato_a.data_inicio_mandato|date:"d/m/Y"}} - {{mandato_a.data_fim_mandato|date:"d/m/Y"}}
    {{ mandato_b.legislatura }}
    {{mandato_b.data_inicio_mandato|date:"d/m/Y"}} - {{mandato_b.data_fim_mandato|date:"d/m/Y"}}
    + {% endif %} +
    + {% include 'paginacao.html'%} +
    +{% endblock base_content %} \ No newline at end of file diff --git a/sapl/templates/base/protocolos_com_materias.html b/sapl/templates/base/protocolos_com_materias.html new file mode 100644 index 000000000..1e98bad5c --- /dev/null +++ b/sapl/templates/base/protocolos_com_materias.html @@ -0,0 +1,30 @@ +{% extends "base.html" %} +{% load common_tags %} +{% block base_content %} +
    +

    Lista de Protocolos que Excedem o Limite de Matérias Vinculadas

    +
    + {% if not protocolos_com_materias %} +

    {{ NO_ENTRIES_MSG }}

    + {% else %} + + + + + + + + + {% for materia, quantidade in protocolos_com_materias %} + + + + + {% endfor %} + +
    ProtocoloQuantidade de Matérias Vinculas
    {{ materia.numero_protocolo }}/{{ materia.ano }}{{ quantidade }}
    + {% endif %} +
    + {% include 'paginacao.html'%} +
    +{% endblock base_content %} \ No newline at end of file diff --git a/sapl/templates/base/protocolos_duplicados.html b/sapl/templates/base/protocolos_duplicados.html new file mode 100644 index 000000000..15056991f --- /dev/null +++ b/sapl/templates/base/protocolos_duplicados.html @@ -0,0 +1,32 @@ +{% extends "base.html" %} +{% load common_tags %} +{% block base_content %} +
    +

    Lista de Protocolos Duplicados

    +
    + {% if not protocolos_duplicados %} +

    {{ NO_ENTRIES_MSG }}

    + {% else %} + + + + + + + + + {% for protocolo, quantidade in protocolos_duplicados %} + + + + + {% endfor %} + +
    ProtocoloQuantidade
    + {{ protocolo }} + {{ quantidade }}
    + {% endif %} +
    + {% include 'paginacao.html'%} +
    +{% endblock base_content %} \ No newline at end of file diff --git a/sapl/templates/comissoes/composicao_list.html b/sapl/templates/comissoes/composicao_list.html index 752e0c885..bc7bf8292 100644 --- a/sapl/templates/comissoes/composicao_list.html +++ b/sapl/templates/comissoes/composicao_list.html @@ -1,46 +1,45 @@ {% extends "crud/list.html" %} {% load i18n common_tags crispy_forms_tags%} -{% block base_content %} +{% block actions %} {% if user.is_authenticated and perms.comissoes.add_composicao %} -
    - {% block actions %} -
    - {% if view.create_url %} - - {% blocktrans with verbose_name=view.verbose_name %} Adicionar {{ verbose_name }} {% endblocktrans %} - - {% endif %} - {% block more_buttons %}{% endblock more_buttons %} -
    - {% endblock actions %} -
    +
    + {% if view.create_url %} + + {% blocktrans with verbose_name=view.verbose_name %} Adicionar {{ verbose_name }} {% endblocktrans %} + + {% endif %} + {% block more_buttons %}{% endblock more_buttons %} +
    {% endif %} +{% endblock actions %} -
    - Selecione o Período -
    - -
    -
    +{% block extra_content %} +
    +
    + Selecione o Período + +
    +

    +{% endblock %} + +{% block container_table_list %} {% if user.is_authenticated and perms.comissoes.add_participacao %} -
    - - Adicionar Participação em Comissão - + {% endif %} - -
    - -
    @@ -71,4 +70,4 @@
    -{% endblock base_content %} +{% endblock %} diff --git a/sapl/templates/comissoes/materias_em_tramitacao.html b/sapl/templates/comissoes/materias_em_tramitacao.html index 9f197d1ce..32860df54 100644 --- a/sapl/templates/comissoes/materias_em_tramitacao.html +++ b/sapl/templates/comissoes/materias_em_tramitacao.html @@ -12,7 +12,7 @@ {% block detail_content %}
    {{comissao}} - Há {{page_obj|length}} matéria(s) em tramitação nesta unidade.

    + Há {{qtde}} matéria(s) em tramitação nesta unidade.

    {% for materia in page_obj %} {{materia.tipo.sigla}} {{materia.numero}} {{materia.ano}} - {{materia.tipo}} diff --git a/sapl/templates/compilacao/layouts.yaml b/sapl/templates/compilacao/layouts.yaml index 20446ba19..a92c8627b 100644 --- a/sapl/templates/compilacao/layouts.yaml +++ b/sapl/templates/compilacao/layouts.yaml @@ -45,3 +45,4 @@ TipoTextoArticulado: {% trans 'Funcionalidaes' %}: - participacao_social publicacao_func - perfis + - rodape_global diff --git a/sapl/templates/compilacao/text_list.html b/sapl/templates/compilacao/text_list.html index e4bf1b7e2..4efd992a9 100644 --- a/sapl/templates/compilacao/text_list.html +++ b/sapl/templates/compilacao/text_list.html @@ -7,31 +7,18 @@ {% endblock %} -{% block extra_sections_nav %} -
  • {% trans 'Versão para Impressão' %}
  • -{% endblock %} {% block base_content %} {% block actions %} {{block.super}} {% endblock %} - {% comment %} - - {% if perms.compilacao.change_dispositivo_edicao_dinamica %} - {% block actions %} - - {% endblock actions %} - {% endif %} - {% endcomment %} - {% block detail_content %} {{block.super}} {% endblock %} {% include 'compilacao/text_list__embedded.html'%} + {{object.tipo_ta.rodape_global|dont_break_out}} + + {% endblock base_content %} diff --git a/sapl/templates/compilacao/text_list__embedded.html b/sapl/templates/compilacao/text_list__embedded.html index c863279c5..3daae5eec 100644 --- a/sapl/templates/compilacao/text_list__embedded.html +++ b/sapl/templates/compilacao/text_list__embedded.html @@ -68,61 +68,7 @@ {% endif %} {% endfor %} - {% comment "" %} - {% for key, values in view.get_vigencias.items %} - {% if forloop.first %} - - - - {% if view.inicio_vigencia and view.fim_vigencia %} - {% blocktrans with inicio_vigencia=view.inicio_vigencia fim_vigencia=view.fim_vigencia%} - Vigência entre {{inicio_vigencia}} e {{fim_vigencia}}. - {% endblocktrans%} - {% else%} - {% blocktrans with inicio_vigencia=dispositivo.inicio_vigencia%} - Vigência a partir de {{inicio_vigencia}}. - {% endblocktrans%} - {% endif %} -
    - {% if view.ta_vigencia %} - {% trans 'Dada por '%}{{ta_pub_list|lookup:view.ta_vigencia}} - {% elif view.ta_vigencia and view.ta_vigencia != 0%} - {% trans 'Dada por '%}{{dispositivo.ta_publicado}} - {% endif %} -
    - {% endif%} - {% endfor %} - {% else %} - - {% endif %} - {% endfor %} - {% endcomment %} -
    {% trans 'TMS'%} diff --git a/sapl/templates/compilacao/text_list__print_version.html b/sapl/templates/compilacao/text_list__print_version.html index ce328fc19..bd07df126 100644 --- a/sapl/templates/compilacao/text_list__print_version.html +++ b/sapl/templates/compilacao/text_list__print_version.html @@ -11,3 +11,10 @@ {% block detail_content %}{% endblock %} {% block footer_container %}{% endblock %} + + +{% block base_content %} +
    + {{ block.super }} +
    +{% endblock %} \ No newline at end of file diff --git a/sapl/templates/compilacao/text_list_bloco.html b/sapl/templates/compilacao/text_list_bloco.html index 0c2900734..2ef5e359a 100644 --- a/sapl/templates/compilacao/text_list_bloco.html +++ b/sapl/templates/compilacao/text_list_bloco.html @@ -20,7 +20,6 @@
    - {% if not dpt.tipo_dispositivo.dispositivo_de_articulacao or dpt.tipo_dispositivo.dispositivo_de_articulacao and dpt.dispositivo_subsequente %} {% if dpt.auto_inserido %} {{ dpt.dispositivo_pai.tipo_dispositivo.rotulo_prefixo_html|safe }} @@ -31,6 +30,7 @@ {{ dpt.rotulo }} {{ dpt.tipo_dispositivo.rotulo_sufixo_html|safe }} {% endif %} + {% endif %} {{ dpt.tipo_dispositivo.texto_prefixo_html|safe }}{%if dpt.texto %}{{ dpt.texto|safe }}{%else%}{%if not dpt.tipo_dispositivo.dispositivo_de_articulacao %} {% endif %}{% endif %} @@ -43,20 +43,21 @@   {% endif %} - {% endif %} - {% if user.is_authenticated and not dpt.tipo_dispositivo.dispositivo_de_articulacao%} - {% if perms.compilacao.add_nota or perms.compilacao.add_vide or perms.compilacao.change_dispositivo%} -
    -
      - {% if perms.compilacao.change_dispositivo %}
    • Ed
    • {% endif %} - {% if perms.compilacao.add_nota %}
    • N
    • {% endif %} - {% if perms.compilacao.add_vide %}
    • V
    • {% endif %} -
    -
    -
    - {% endif %} + + {% if user.is_authenticated and not dpt.tipo_dispositivo.dispositivo_de_articulacao%} + {% if perms.compilacao.add_nota or perms.compilacao.add_vide or perms.compilacao.change_dispositivo%} +
    +
      + {% if perms.compilacao.change_dispositivo %}
    • Ed
    • {% endif %} + {% if perms.compilacao.add_nota %}
    • N
    • {% endif %} + {% if perms.compilacao.add_vide %}
    • V
    • {% endif %} +
    +
    +
    {% endif %} + {% endif %} +
    {% if not dpt.tipo_dispositivo.dispositivo_de_articulacao %} diff --git a/sapl/templates/compilacao/text_list_blocoalteracao.html b/sapl/templates/compilacao/text_list_blocoalteracao.html index ed9876fd9..adc6ff2a4 100644 --- a/sapl/templates/compilacao/text_list_blocoalteracao.html +++ b/sapl/templates/compilacao/text_list_blocoalteracao.html @@ -20,15 +20,15 @@ {% if ch.rotulo %} {{ ch.rotulo }} {{ ch.tipo_dispositivo.rotulo_sufixo_html|safe }} - {{ ch.tipo_dispositivo.texto_prefixo_html|safe }}{{ ch.texto|safe }} + {{ ch.tipo_dispositivo.texto_prefixo_html|safe }}{% if ch.texto_atualizador %}{{ ch.texto_atualizador|safe }}{%else%}{{ ch.texto|safe }}{% endif %} {% elif not ch.rotulo and not ch.auto_inserido %} {{ ch.tipo_dispositivo.rotulo_sufixo_html|safe }} - {{ ch.tipo_dispositivo.texto_prefixo_html|safe }}{{ ch.texto|safe }} + {{ ch.tipo_dispositivo.texto_prefixo_html|safe }}{% if ch.texto_atualizador %}{{ ch.texto_atualizador|safe }}{%else%}{{ ch.texto|safe }}{% endif %} {% else %} {{ ch.tipo_dispositivo.rotulo_sufixo_html|safe }} - {{ ch.tipo_dispositivo.texto_prefixo_html|safe }}{{ ch.texto|safe }} + {{ ch.tipo_dispositivo.texto_prefixo_html|safe }}{% if ch.texto_atualizador %}{{ ch.texto_atualizador|safe }}{%else%}{{ ch.texto|safe }}{% endif %} {% endif %}
    diff --git a/sapl/templates/compilacao/textoarticulado_detail.html b/sapl/templates/compilacao/textoarticulado_detail.html index 9560a95f7..976f401ff 100644 --- a/sapl/templates/compilacao/textoarticulado_detail.html +++ b/sapl/templates/compilacao/textoarticulado_detail.html @@ -16,13 +16,15 @@ {% trans 'Editar Metadados do Texto Articulado' %} {% endif %} {% if object|can_use_dynamic_editing:user %} - {% trans 'Editar Texto' %} + {% trans 'Editar Texto' %} {% endif %} {% if perms.compilacao.lock_unlock_textoarticulado and not object.editable_only_by_owners%} - {% if object.editing_locked %}{% trans 'Desbloquear Edição' %}{%else%}{% trans 'Publicar Texto' %}{% endif %} + {% if object.editing_locked %}{% trans 'Desbloquear Edição' %}{%else%}{% trans 'Publicar Texto' %}{% endif %} {% endif %} - -
    +
    + {% endblock actions %} diff --git a/sapl/templates/crud/list.html b/sapl/templates/crud/list.html index 375c6edbb..51f0f5223 100644 --- a/sapl/templates/crud/list.html +++ b/sapl/templates/crud/list.html @@ -5,17 +5,18 @@
    - -{% if error %}
    {{ error }}
    {% endif %} {% crispy form %} {% endblock %} diff --git a/sapl/templates/materia/anexada_list.html b/sapl/templates/materia/anexada_list.html new file mode 100644 index 000000000..381f402b8 --- /dev/null +++ b/sapl/templates/materia/anexada_list.html @@ -0,0 +1,14 @@ +{% extends "crud/list.html" %} +{% load i18n %} +{% load common_tags %} + + +{% block more_buttons %} + +{% if perms|get_add_perm:view %} + + {% trans "Adicionar Anexada em Lote" %} + +{% endif %} + +{% endblock more_buttons %} diff --git a/sapl/templates/materia/em_lote/acessorio.html b/sapl/templates/materia/em_lote/acessorio.html index 738010083..89fc2fff8 100644 --- a/sapl/templates/materia/em_lote/acessorio.html +++ b/sapl/templates/materia/em_lote/acessorio.html @@ -79,7 +79,7 @@
    @@ -91,7 +91,9 @@ - {{materia.tipo.sigla}} {{materia.numero}}/{{materia.ano}} - {{materia.tipo.descricao}} + + {{materia.tipo.sigla}} {{materia.numero}}/{{materia.ano}} - {{materia.tipo.descricao}} + {% endfor %} @@ -107,11 +109,12 @@ {% endblock detail_content %} {% block extra_js %} {% endblock %} diff --git a/sapl/templates/materia/em_lote/anexada.html b/sapl/templates/materia/em_lote/anexada.html new file mode 100644 index 000000000..2b1c22c56 --- /dev/null +++ b/sapl/templates/materia/em_lote/anexada.html @@ -0,0 +1,84 @@ +{% extends "crud/detail.html" %} +{% load i18n crispy_forms_tags %} +{% block actions %}{% endblock %} +{% block detail_content %} + + {% if not show_results %} + {% crispy filter.form %} + {% endif %} + + {% if show_results %} + {% if numero_res > 0 %} + {% if numero_res == 1 %} +

    {% trans 'Pesquisa concluída com sucesso! Foi encontrada 1 matéria.'%}

    + {% else %} +

    Foram encontradas {{ numero_res }} matérias.

    + {% endif %} +
    + {% csrf_token %} + +
    +
    + +
    +
    + + +
    +
    +
    +
    + + +
    +
    +
    + + +
    + +
    + +
    + Matérias para Anexar em Lote + +
    +
    + +
    +
    + + + + + {% for materia in object_list %} + + + + {% endfor %} + +
    Matéria
    + + {{materia.tipo.sigla}} {{materia.numero}}/{{materia.ano}} - {{materia.tipo.descricao}} +
    +
    + +
    + {% else %} +

    Nenhuma matéria encontrada.

    + {% endif %} + {% endif %} +{% endblock detail_content %} +{% block extra_js %} + +{% endblock %} diff --git a/sapl/templates/materia/em_lote/tramitacao.html b/sapl/templates/materia/em_lote/tramitacao.html index 9ed94c84d..94cecf52f 100644 --- a/sapl/templates/materia/em_lote/tramitacao.html +++ b/sapl/templates/materia/em_lote/tramitacao.html @@ -96,7 +96,7 @@
    @@ -109,7 +109,9 @@ - {{materia.tipo.sigla}} {{materia.tipo.descricao}} {{materia.numero}}/{{materia.ano}} + + {{materia.tipo.sigla}} {{materia.tipo.descricao}} {{materia.numero}}/{{materia.ano}} + {% endfor %} @@ -125,12 +127,13 @@ {% endblock detail_content %} {% block extra_js %} {% endblock extra_js %} diff --git a/sapl/templates/materia/tipomaterialegislativa_list.html b/sapl/templates/materia/tipomaterialegislativa_list.html new file mode 100644 index 000000000..1680e89e7 --- /dev/null +++ b/sapl/templates/materia/tipomaterialegislativa_list.html @@ -0,0 +1,55 @@ +{% extends "crud/list_tabaux.html" %} +{% load i18n %} +{% load common_tags %} + +{% block container_table_list %} +
    + {{ block.super }} +
    +{% endblock %} + +{% block extra_js %} + + + +{% endblock %} diff --git a/sapl/templates/materia/tramitacao_detail.html b/sapl/templates/materia/tramitacao_detail.html new file mode 100644 index 000000000..4aaca09b6 --- /dev/null +++ b/sapl/templates/materia/tramitacao_detail.html @@ -0,0 +1,38 @@ +{% extends "crud/detail.html" %} +{% load i18n %} + +{% block detail_content %} + {{ block.super }} + {% if user.is_superuser %} +
    + {% if tramitacao.user %} +
    +
    +

    Usuário

    +
    + +
    +
    +
    + {% endif %} + {% if tramitacao.ip %} +
    +
    +

    IP

    +
    +
    +
    + {{tramitacao.ip}} +
    +
    +
    +
    +
    + {% endif %} +
    + {% endif %} +{% endblock detail_content %} \ No newline at end of file diff --git a/sapl/templates/menu_tabelas_auxiliares.yaml b/sapl/templates/menu_tabelas_auxiliares.yaml index 955e1cb28..9a9c40d8d 100644 --- a/sapl/templates/menu_tabelas_auxiliares.yaml +++ b/sapl/templates/menu_tabelas_auxiliares.yaml @@ -17,6 +17,12 @@ - title: {% trans 'Módulo Parlamentares' %} css_class: head_title children: + - title: {% trans 'Pesquisar Parlamentar' %} + url: sapl.parlamentares:pesquisar_parlamentar + css_class: btn btn-link + - title: {% trans 'Adicionar Parlamentar' %} + url: sapl.parlamentares:parlamentar_create + css_class: btn btn-link - title: {% trans 'Legislatura' %} url: sapl.parlamentares:legislatura_list css_class: btn btn-link @@ -72,7 +78,7 @@ url: sapl.parlamentares:frente_list css_class: btn btn-link - title: {% trans 'Bloco Parlamentar' %} - url: sapl.sessao:bloco_list + url: sapl.parlamentares:bloco_list css_class: btn btn-link - title: {% trans 'Módulo Proposições' %} css_class: head_title diff --git a/sapl/templates/navbar.yaml b/sapl/templates/navbar.yaml index b80cb7acd..de556d882 100644 --- a/sapl/templates/navbar.yaml +++ b/sapl/templates/navbar.yaml @@ -73,8 +73,11 @@ url: '/sistema' check_permission: base.view_tabelas_auxiliares - title: {% trans 'Administração de Usuários' %} - url: {% url 'sapl.base:user_list' %} + url: {% url 'sapl.base:usuario' %} check_permission: user.is_superuser + - title: {% trans 'Inconsistências de Dados' %} + url: {% url 'sapl.base:lista_inconsistencias' %} + check_permission: user.is_superuser {% comment %} diff --git a/sapl/templates/norma/normajuridica_form.html b/sapl/templates/norma/normajuridica_form.html index 2ed25c693..4c29fc98b 100644 --- a/sapl/templates/norma/normajuridica_form.html +++ b/sapl/templates/norma/normajuridica_form.html @@ -7,33 +7,33 @@ +{% endblock %} \ No newline at end of file diff --git a/sapl/templates/protocoloadm/layouts.yaml b/sapl/templates/protocoloadm/layouts.yaml index b1bb2f31b..d87a99b56 100644 --- a/sapl/templates/protocoloadm/layouts.yaml +++ b/sapl/templates/protocoloadm/layouts.yaml @@ -10,6 +10,8 @@ DocumentoAdministrativo: - assunto - interessado tramitacao - texto_integral + - documento_anexado_set__documento_principal|m2m_urlize_for_detail + - documento_principal_set__documento_anexado|m2m_urlize_for_detail {% trans 'Outras Informações' %}: - numero_externo - dias_prazo data_fim_prazo @@ -28,11 +30,22 @@ StatusTramitacaoAdministrativo: TramitacaoAdministrativo: {% trans 'Tramitação' %}: - - data_tramitacao:4 unidade_tramitacao_local - - status:4 unidade_tramitacao_destino - - data_encaminhamento data_fim_prazo + - data_tramitacao unidade_tramitacao_local + - unidade_tramitacao_destino data_encaminhamento data_fim_prazo + - status urgente - texto +Anexado: + {% trans 'Documento Anexado' %}: + - tipo numero ano + - data_anexacao data_desanexacao + +AnexadoDetail: + {% trans 'Documento Anexado' %}: + - documento_principal|fk_urlize_for_detail + - documento_anexado|fk_urlize_for_detail + - data_anexacao data_desanexacao + Protocolo: {% trans 'Indentificação Documento' %}: - tipo_protocolo diff --git a/sapl/templates/protocoloadm/subnav.yaml b/sapl/templates/protocoloadm/subnav.yaml index ff4feb42e..5e0c3021b 100644 --- a/sapl/templates/protocoloadm/subnav.yaml +++ b/sapl/templates/protocoloadm/subnav.yaml @@ -1,6 +1,8 @@ {% load i18n common_tags %} - title: {% trans 'Início' %} url: documentoadministrativo_detail +- title: {% trans 'Anexado' %} + url: anexado_list - title: {% trans 'Tramitação' %} url: tramitacaoadministrativo_list - title: {% trans 'Documento Acessório' %} diff --git a/sapl/templates/protocoloadm/tramitacaoadministrativo_detail.html b/sapl/templates/protocoloadm/tramitacaoadministrativo_detail.html index 3131bc4be..20e2289c9 100644 --- a/sapl/templates/protocoloadm/tramitacaoadministrativo_detail.html +++ b/sapl/templates/protocoloadm/tramitacaoadministrativo_detail.html @@ -1,5 +1,6 @@ {% extends "crud/detail.html" %} {% load i18n %} + {% block actions %} {% load common_tags %} @@ -13,3 +14,39 @@ {% endif %}
    {% endblock actions %} + +{% block detail_content %} + {{ block.super }} + {% if user.is_superuser %} +
    + {% if tramitacaoadministrativo.user %} +
    +
    +

    Usuário

    + +
    +
    + {% endif %} + {% if tramitacaoadministrativo.ip %} +
    +
    +

    IP

    +
    +
    +
    + {{tramitacaoadministrativo.ip}} +
    +
    +
    +
    +
    + {% endif %} +
    + {% endif %} +{% endblock detail_content %} diff --git a/sapl/templates/relatorios/header_ata.html b/sapl/templates/relatorios/header_ata.html new file mode 100644 index 000000000..4cc43efe6 --- /dev/null +++ b/sapl/templates/relatorios/header_ata.html @@ -0,0 +1,31 @@ +{% load common_tags %} +{% load render_bundle from webpack_loader %} +{% load webpack_static from webpack_loader %} +{% load static %} + + + + + + + + + + +
    +
    +
    + +
    +
    +
      +
    • {{casa.nome}}

    • +
    • Sistema de Apoio ao Processo Legislativo

    • +
    +
    +
    +
    +

    + + + \ No newline at end of file diff --git a/sapl/templates/relatorios/relatorio_ata.html b/sapl/templates/relatorios/relatorio_ata.html new file mode 100644 index 000000000..c559585fb --- /dev/null +++ b/sapl/templates/relatorios/relatorio_ata.html @@ -0,0 +1,104 @@ +{% load common_tags %} +{% load static %} + + + + + + +

    Extrato Eletrônico da {{sessaoplenaria}}

    + {% include 'sessao/blocos_ata/identificacao_basica.html' %} + {% include 'sessao/blocos_ata/mesa_diretora.html' %} + {% include 'sessao/blocos_ata/lista_presenca.html' %} + {% include 'sessao/blocos_ata/expedientes.html' %} + {% include 'sessao/blocos_ata/materias_expediente.html' %} + {% include 'sessao/blocos_ata/oradores_expediente.html' %} + {% include 'sessao/blocos_ata/lista_presenca_ordem_dia.html' %} + {% include 'sessao/blocos_ata/materias_ordem_dia.html' %} + {% include 'sessao/blocos_ata/oradores_ordemdia.html' %} + {% include 'sessao/blocos_ata/oradores_explicacoes.html' %} + {% include 'sessao/blocos_ata/ocorrencias_da_sessao.html' %} + + {% if assinatura_mesa or assinatura_presentes %} + +
    + {{texto_assinatura}} + + + + + + + {% for p in assinatura_mesa %} + {% if forloop.counter0|divisibleby:2 %} + + + + {% endif %} + {% endfor %} + + {% for p in assinatura_presentes %} + {% if forloop.counter0|divisibleby:2 %} + + + + {% endif %} + {% endfor %} + +
    +
    ____________________
    +

    {{p.cargo}}: {{p.parlamentar.nome_completo}} / {% if p.parlamentar|filiacao_data_filter:object.data_inicio %} {{ p.parlamentar|filiacao_data_filter:object.data_inicio }} {% else %} Sem partido {% endif %}

    +


    +
    + {% else %} +
    ____________________
    +

    {{p.cargo}}: {{p.parlamentar.nome_completo}} / {% if p.parlamentar|filiacao_data_filter:object.data_inicio %} {{ p.parlamentar|filiacao_data_filter:object.data_inicio }} {% else %} Sem partido {% endif %}

    +


    +
    +
    +
    _____________________
    +

    + {{p.nome_completo}} / {% if p|filiacao_data_filter:object.data_inicio %} {{ p|filiacao_data_filter:object.data_inicio }} {% else %} Sem partido {% endif %}

    +


    +
    + {% else %} +
    _____________________
    +

    {{p.nome_completo}} / {% if p|filiacao_data_filter:object.data_inicio %} {{ p|filiacao_data_filter:object.data_inicio }} {% else %} Sem partido {% endif %}

    +


    +
    +
    + {% endif%} +
    + + \ No newline at end of file diff --git a/sapl/templates/sessao/adicionar_varias_materias_expediente.html b/sapl/templates/sessao/adicionar_varias_materias_expediente.html index 0005e5288..3acd5ae79 100644 --- a/sapl/templates/sessao/adicionar_varias_materias_expediente.html +++ b/sapl/templates/sessao/adicionar_varias_materias_expediente.html @@ -3,24 +3,21 @@ {% load crispy_forms_tags %} {% block actions %}{% endblock %} -{% block sections_nav %} -{% endblock %} - {% block detail_content %} - {% block buttons %} + {% block buttons %} - {% if filter_url and not filter.form.errors %} - + {% if filter_url and not filter.form.errors %} + - + - {% endif %} + {% endif %} - {% endblock %} + {% endblock %} {% if filter.form.errors %} {% crispy filter.form %} diff --git a/sapl/templates/sessao/adicionar_varias_materias_ordem.html b/sapl/templates/sessao/adicionar_varias_materias_ordem.html index 1f7bbd7e3..5d6622e1f 100644 --- a/sapl/templates/sessao/adicionar_varias_materias_ordem.html +++ b/sapl/templates/sessao/adicionar_varias_materias_ordem.html @@ -4,16 +4,16 @@ {% block buttons %} - {% if filter_url and not filter.form.errors %} + {% if filter_url and not filter.form.errors %} - + - + - {% endif %} + {% endif %} - {% endblock %} \ No newline at end of file +{% endblock %} \ No newline at end of file diff --git a/sapl/templates/sessao/blocos_ata/assinaturas.html b/sapl/templates/sessao/blocos_ata/assinaturas.html index 88d9dd04a..520215fe1 100644 --- a/sapl/templates/sessao/blocos_ata/assinaturas.html +++ b/sapl/templates/sessao/blocos_ata/assinaturas.html @@ -1,15 +1,19 @@ {% load common_tags %} - -

    -

    +
    {{texto_assinatura}}


    - {% for p in assinatura_presentes %} + {% for p in assinatura_mesa %}
    ___________________________________________
    - {{p.nome_parlamentar}} / {{ p|filiacao_data_filter:object.data_inicio }} + {{p.cargo}}: {{p.parlamentar.nome_completo}} / {% if p.parlamentar|filiacao_data_filter:object.data_inicio %} {{ p.parlamentar|filiacao_data_filter:object.data_inicio }} {% else %} Sem partido {% endif %}


    {% endfor %} + {% for p in assinatura_presentes %} +
    ___________________________________________
    + {{p.nome_completo}} / {% if p|filiacao_data_filter:object.data_inicio %} {{ p|filiacao_data_filter:object.data_inicio }} {% else %} Sem partido {% endif %} +


    +
    + {% endfor %}
    \ No newline at end of file diff --git a/sapl/templates/sessao/blocos_ata/identificacao_basica.html b/sapl/templates/sessao/blocos_ata/identificacao_basica.html index a6ec9c8c0..84eeb2a11 100644 --- a/sapl/templates/sessao/blocos_ata/identificacao_basica.html +++ b/sapl/templates/sessao/blocos_ata/identificacao_basica.html @@ -2,7 +2,8 @@

    Identificação Básica: {% for b in basica %} - {{b}} ; + {{b}} + {% if not forloop.last %} ; {% endif %} {% endfor %}

    \ No newline at end of file diff --git a/sapl/templates/sessao/blocos_ata/lista_presenca.html b/sapl/templates/sessao/blocos_ata/lista_presenca.html index 68e869cb5..f015f3a3a 100644 --- a/sapl/templates/sessao/blocos_ata/lista_presenca.html +++ b/sapl/templates/sessao/blocos_ata/lista_presenca.html @@ -1,20 +1,22 @@ {% load common_tags %}
    -

    +

    {% if presenca_sessao %} Lista de Presença na Sessão: {% for p in presenca_sessao %} - {{p.nome_parlamentar}} / {{ p|filiacao_data_filter:object.data_inicio }} ; + {{p.nome_completo}} / {% if p|filiacao_data_filter:object.data_inicio %} {{ p|filiacao_data_filter:object.data_inicio }} {% else %} Sem partido {% endif %} + {% if not forloop.last %} ; {% endif %} {% endfor %} - {% endif %} -

    -

    - {% if justificativa_ausencia %} + {% endif %} +

    +

    + {% if justificativa_ausencia %} Justificativas de Ausências na Sessão: {% for j in justificativa_ausencia %} - {{j.parlamentar}} / {{ j.tipo_ausencia }} ; + {{j.parlamentar.nome_completo}} / {{ j.tipo_ausencia }} + {% if not forloop.last %} ; {% endif %} {% endfor %} - {% endif %} -

    + {% endif %} +

    diff --git a/sapl/templates/sessao/blocos_ata/lista_presenca_ordem_dia.html b/sapl/templates/sessao/blocos_ata/lista_presenca_ordem_dia.html index 685ffd2bd..58729609c 100644 --- a/sapl/templates/sessao/blocos_ata/lista_presenca_ordem_dia.html +++ b/sapl/templates/sessao/blocos_ata/lista_presenca_ordem_dia.html @@ -5,7 +5,8 @@ {% if presenca_ordem %} Lista de Presença na Ordem do Dia: {% for p in presenca_ordem %} - {{p.nome_parlamentar}} / {{ p|filiacao_data_filter:object.data_inicio }} ; + {{p.nome_completo}} / {% if p|filiacao_data_filter:object.data_inicio %} {{ p|filiacao_data_filter:object.data_inicio }} {% else %} Sem partido {% endif %} + {% if not forloop.last %} ; {% endif %} {% endfor %} {% endif %}

    diff --git a/sapl/templates/sessao/blocos_ata/materias_expediente.html b/sapl/templates/sessao/blocos_ata/materias_expediente.html index 7613448fc..4a3964993 100644 --- a/sapl/templates/sessao/blocos_ata/materias_expediente.html +++ b/sapl/templates/sessao/blocos_ata/materias_expediente.html @@ -1,27 +1,36 @@
    -

    - {% if materia_expediente %} - Matérias do Expediente: - {% for m in materia_expediente %} - {{m.numero}} - {{m.titulo}} - - {% if m.turno %} - Turno: {{m.turno}} - {% endif %} - - Autor{{ m.autor|length|pluralize:"es" }}: {{ m.autor|join:', ' }} - - {% if m.numero_protocolo %} - Número de Protocolo: {{ m.numero_protocolo }} - {% endif %} - - {% if m.numero_processo %} - Processo: {{ m.numero_processo }} - {% endif %} - - {{m.ementa|safe}} - {{m.resultado}} {{m.resultado_observacao}} - {% endfor %} +

    + {% if materia_expediente %} + Matérias do Expediente: + {% for m in materia_expediente %} + {{m.numero}} - {{m.titulo}}, + {{m.ementa|safe}} + Autor{{ m.autor|length|pluralize:"es" }}: {{ m.autor|join:', ' }}, + {% if m.numero_protocolo %} + Número de Protocolo: {{ m.numero_protocolo }}, + {% endif %} + {% if m.numero_processo %} + Processo: {{ m.numero_processo }}, + {% endif %} + {% if m.turno %} + Turno: {{m.turno}}, + {% endif %} + {% if m.tipo_votacao %} + Tipo: {{m.tipo_votacao}}, + Sim: {{ m.voto_sim }}, + Não: {{ m.voto_nao }}, + Abstenções: {{m.voto_abstencoes}}, + {% endif %} + Resultado: {{m.resultado}} + {% if m.resultado_observacao %} - {{m.resultado_observacao}} {% endif %} + {% if m.voto_nominal%} + Votos Nominais : + {% for voto in m.voto_nominal %} + {{voto.0}} - {{voto.1}} + {% if not forloop.last %} ; {% endif %} + {% endfor %} + {% endif %} + {% endfor %} {% endif %}

    \ No newline at end of file diff --git a/sapl/templates/sessao/blocos_ata/materias_ordem_dia.html b/sapl/templates/sessao/blocos_ata/materias_ordem_dia.html index c5ffd15df..6b38be5e4 100644 --- a/sapl/templates/sessao/blocos_ata/materias_ordem_dia.html +++ b/sapl/templates/sessao/blocos_ata/materias_ordem_dia.html @@ -1,24 +1,36 @@
    -

    - {% if materias_ordem %} - Matérias da Ordem do Dia: - {% for m in materias_ordem %} - {{m.numero}} - {{m.titulo}} - {% if m.turno %} - Turno:{{m.turno}} - {% endif %} - Autor{{ m.autor|length|pluralize:"es" }}: {{ m.autor|join:', ' }} - {% if m.numero_protocolo %} - Número de Protocolo: {{ m.numero_protocolo }} - {% endif %} - {% if m.numero_processo %} - Processo: {{ m.numero_processo }} - {% endif %} - {{m.ementa|safe}} - {{m.resultado}} {{m.resultado_observacao}} - {% endfor %} - {% endif %} -

    - + {% if materias_ordem %} + Matérias da Ordem do Dia: + {% for m in materias_ordem %} + {{m.numero}} - {{m.titulo}}, + {{m.ementa|safe}} + Autor{{ m.autor|length|pluralize:"es" }}: {{ m.autor|join:', ' }}, + {% if m.numero_protocolo %} + Número de Protocolo: {{ m.numero_protocolo }}, + {% endif %} + {% if m.numero_processo %} + Processo: {{ m.numero_processo }}, + {% endif %} + {% if m.turno %} + Turno: {{m.turno}}, + {% endif %} + {% if m.tipo_votacao %} + Tipo: {{m.tipo_votacao}}, + Sim: {{ m.voto_sim }}, + Não: {{ m.voto_nao }}, + Abstenções: {{m.voto_abstencoes}}, + {% endif %} + Resultado: {{m.resultado}} + {% if m.resultado_observacao %} - {{m.resultado_observacao}} {% endif %} + {% if m.voto_nominal%} + Votos Nominais : + {% for voto in m.voto_nominal %} + {{voto.0}} - {{voto.1}} + {% if not forloop.last %} ; {% endif %} + {% endfor %} + {% endif %} + {% endfor %} + {% endif %} +

    \ No newline at end of file diff --git a/sapl/templates/sessao/blocos_ata/mesa_diretora.html b/sapl/templates/sessao/blocos_ata/mesa_diretora.html index f09e9b8e0..88177d01e 100644 --- a/sapl/templates/sessao/blocos_ata/mesa_diretora.html +++ b/sapl/templates/sessao/blocos_ata/mesa_diretora.html @@ -1,12 +1,14 @@ +{% load common_tags %} +
    -

    - {% if mesa %} +

    + {% if mesa %} Mesa Diretora: {% for m in mesa %} {{m.cargo}}: - {{m.parlamentar.nome_parlamentar}} / {{ m.parlamentar.filiacao_atual }} ; + {{m.parlamentar.nome_completo}} / {% if m.parlamentar|filiacao_data_filter:object.data_inicio %} {{ m.parlamentar|filiacao_data_filter:object.data_inicio }} {% else %} Sem partido {% endif %} + {% if not forloop.last %} ; {% endif %} {% endfor %} - {% endif %} + {% endif %}

    - -
    \ No newline at end of file + diff --git a/sapl/templates/sessao/blocos_ata/ocorrencias_da_sessao.html b/sapl/templates/sessao/blocos_ata/ocorrencias_da_sessao.html index 0896cd9b0..cc1543cbb 100644 --- a/sapl/templates/sessao/blocos_ata/ocorrencias_da_sessao.html +++ b/sapl/templates/sessao/blocos_ata/ocorrencias_da_sessao.html @@ -1,8 +1,8 @@
    -

    - {% if object.ocorrenciasessao.conteudo %} - Ocorrências da Sessão: - {{object.ocorrenciasessao.conteudo|striptags|safe}} - {% endif %} +

    + {% if object.ocorrenciasessao.conteudo %} + Ocorrências da Sessão: + {{object.ocorrenciasessao.conteudo|striptags|safe}} + {% endif %}

    diff --git a/sapl/templates/sessao/blocos_ata/oradores_expediente.html b/sapl/templates/sessao/blocos_ata/oradores_expediente.html index 8bc420b3c..78e074f2a 100644 --- a/sapl/templates/sessao/blocos_ata/oradores_expediente.html +++ b/sapl/templates/sessao/blocos_ata/oradores_expediente.html @@ -1,14 +1,14 @@ +{% load common_tags %} +
    -

    +

    {% if oradores %} Oradores do Expediente: {% for o in oradores %} -

    {{o.numero_ordem}} - {{o.parlamentar}}
    -
    {{o.url_discurso}}
    -
    {{o.observacao}}
    -
    + {{o.numero_ordem}} - {{o.parlamentar.nome_completo}} / {% if o.parlamentar|filiacao_data_filter:object.data_inicio %} {{ o.parlamentar|filiacao_data_filter:object.data_inicio }} {% else %} Sem partido {% endif %} + {% if o.observacao %} - {{o.observacao}} {% endif %} + {% if not forloop.last %} ; {% endif %} {% endfor %} {% endif %} -

    - +

    diff --git a/sapl/templates/sessao/blocos_ata/oradores_explicacoes.html b/sapl/templates/sessao/blocos_ata/oradores_explicacoes.html index 18ced7b07..dd1a61e70 100644 --- a/sapl/templates/sessao/blocos_ata/oradores_explicacoes.html +++ b/sapl/templates/sessao/blocos_ata/oradores_explicacoes.html @@ -1,10 +1,13 @@ +{% load common_tags %} +

    {% if oradores_explicacoes %} - Oradores das Explicações Pessoais: + Oradores das Explicações Pessoais: {% for o in oradores_explicacoes %} - {{o.numero_ordem}} - {{o.parlamentar.nome_parlamentar}} / {{ o.parlamentar.filiacao_atual }} ; - {{o.url_discurso}} + {{o.numero_ordem}} - {{o.parlamentar.nome_completo}} / {% if o.parlamentar|filiacao_data_filter:object.data_inicio %} {{ o.parlamentar|filiacao_data_filter:object.data_inicio }} {% else %} Sem partido {% endif %} + {% if o.observacao %} - {{o.observacao}} {% endif %} + {% if not forloop.last %} ; {% endif %} {% endfor %} {% endif %}

    diff --git a/sapl/templates/sessao/blocos_ata/oradores_ordemdia.html b/sapl/templates/sessao/blocos_ata/oradores_ordemdia.html new file mode 100644 index 000000000..62ec92e81 --- /dev/null +++ b/sapl/templates/sessao/blocos_ata/oradores_ordemdia.html @@ -0,0 +1,13 @@ +
    +

    + {% if oradores_ordemdia %} + Oradores da Ordem do Dia: + {% for orador in oradores_ordemdia %} +

    {{ orador.numero_ordem }} - {{ orador.parlamentar.nome_completo }}
    +
    {{ orador.url_discurso }}
    +
    {{ orador.observacao }}
    +
    + {% endfor %} + {% endif %} +

    +
    \ No newline at end of file diff --git a/sapl/templates/sessao/blocos_ata/votos_nominais_materias_expediente.html b/sapl/templates/sessao/blocos_ata/votos_nominais_materias_expediente.html new file mode 100644 index 000000000..e69de29bb diff --git a/sapl/templates/sessao/blocos_ata/votos_nominais_materias_ordem_dia.html b/sapl/templates/sessao/blocos_ata/votos_nominais_materias_ordem_dia.html new file mode 100644 index 000000000..e69de29bb diff --git a/sapl/templates/sessao/blocos_resumo/lista_presenca.html b/sapl/templates/sessao/blocos_resumo/lista_presenca.html index 8f5ac10e2..474d3f5d8 100644 --- a/sapl/templates/sessao/blocos_resumo/lista_presenca.html +++ b/sapl/templates/sessao/blocos_resumo/lista_presenca.html @@ -4,7 +4,7 @@ Lista de Presença na Sessão
    {% for p in presenca_sessao %} -
    {{p.nome_parlamentar}} / {{ p|filiacao_data_filter:object.data_inicio }}
    +
    {{p.nome_parlamentar}} / {% if p|filiacao_data_filter:object.data_inicio %} {{ p|filiacao_data_filter:object.data_inicio }} {% else %} Sem partido {% endif %}
    {% endfor %}



    diff --git a/sapl/templates/sessao/blocos_resumo/lista_presenca_ordem_dia.html b/sapl/templates/sessao/blocos_resumo/lista_presenca_ordem_dia.html index ea58e160e..672d28063 100644 --- a/sapl/templates/sessao/blocos_resumo/lista_presenca_ordem_dia.html +++ b/sapl/templates/sessao/blocos_resumo/lista_presenca_ordem_dia.html @@ -4,7 +4,7 @@ Lista de Presença na Ordem do Dia
    {% for p in presenca_ordem %} -
    {{p.nome_parlamentar}} / {{ p|filiacao_data_filter:object.data_inicio }}
    +
    {{p.nome_parlamentar}} / {% if p|filiacao_data_filter:object.data_inicio %} {{ p|filiacao_data_filter:object.data_inicio }} {% else %} Sem partido {% endif %}
    {% endfor %}
    \ No newline at end of file diff --git a/sapl/templates/sessao/blocos_resumo/materias_expediente.html b/sapl/templates/sessao/blocos_resumo/materias_expediente.html index 48dcf9c14..f32216df1 100644 --- a/sapl/templates/sessao/blocos_resumo/materias_expediente.html +++ b/sapl/templates/sessao/blocos_resumo/materias_expediente.html @@ -1,3 +1,5 @@ + +{% load common_tags %}
    Matérias do Expediente @@ -33,7 +35,10 @@ Processo: {{ m.numero_processo }} {% endif %} - + {% endfor %} diff --git a/sapl/templates/sessao/blocos_resumo/materias_ordem_dia.html b/sapl/templates/sessao/blocos_resumo/materias_ordem_dia.html index fc8807534..e635ee959 100644 --- a/sapl/templates/sessao/blocos_resumo/materias_ordem_dia.html +++ b/sapl/templates/sessao/blocos_resumo/materias_ordem_dia.html @@ -1,3 +1,5 @@ + +{% load common_tags %}
    Matérias da Ordem do Dia
    {{m.ementa|safe}}
    {{m.observacao}}
    + {{m.ementa|dont_break_out}}
    + {{m.observacao|dont_break_out}} +
    {{m.resultado}}
    {{m.resultado_observacao}}
    @@ -33,7 +35,7 @@ Processo: {{ m.numero_processo }} {% endif %} - + {% endfor %} diff --git a/sapl/templates/sessao/blocos_resumo/mesa_diretora.html b/sapl/templates/sessao/blocos_resumo/mesa_diretora.html index 7baf0cb67..897957916 100644 --- a/sapl/templates/sessao/blocos_resumo/mesa_diretora.html +++ b/sapl/templates/sessao/blocos_resumo/mesa_diretora.html @@ -1,9 +1,11 @@ +{% load common_tags %} +
    Mesa Diretora
    {% for m in mesa %}
    {{m.cargo}}: - {{m.parlamentar.nome_parlamentar}} / {{ m.parlamentar.filiacao_atual }} + {{m.parlamentar.nome_parlamentar}} / {% if m.parlamentar|filiacao_data_filter:object.data_inicio %} {{ m.parlamentar|filiacao_data_filter:object.data_inicio }} {% else %} Sem partido {% endif %}
    {% endfor %}
    diff --git a/sapl/templates/sessao/blocos_resumo/oradores_explicacoes.html b/sapl/templates/sessao/blocos_resumo/oradores_explicacoes.html index e97ac5aea..e1a835cbc 100644 --- a/sapl/templates/sessao/blocos_resumo/oradores_explicacoes.html +++ b/sapl/templates/sessao/blocos_resumo/oradores_explicacoes.html @@ -1,3 +1,5 @@ +{% load common_tags %} +
    Oradores das Explicações Pessoais
    @@ -6,7 +8,7 @@
    {% for o in oradores_explicacoes %} -
    {{o.numero_ordem}} - {{o.parlamentar.nome_parlamentar}} / {{ o.parlamentar.filiacao_atual }}
    +
    {{o.numero_ordem}} - {{o.parlamentar.nome_parlamentar}} / {% if o.parlamentar|filiacao_data_filter:object.data_inicio %} {{ o.parlamentar|filiacao_data_filter:object.data_inicio }} {% else %} Sem partido {% endif %}
    {{o.url_discurso}}

    {% endfor %} diff --git a/sapl/templates/sessao/blocos_resumo/oradores_ordemdia.html b/sapl/templates/sessao/blocos_resumo/oradores_ordemdia.html new file mode 100644 index 000000000..a1898cce0 --- /dev/null +++ b/sapl/templates/sessao/blocos_resumo/oradores_ordemdia.html @@ -0,0 +1,15 @@ +
    + Oradores da Ordem do Dia +
    +
    Parlamentar
    +
    Discurso
    +
    Observação
    +
    +
    + {% for orador in oradores_ordemdia %} +
    {{ orador.numero_ordem }} - {{ orador.parlamentar }}
    +
    {{ orador.url_discurso }}
    +
    {{ orador.observacao }}
    + {% endfor %} +
    +
    \ No newline at end of file diff --git a/sapl/templates/sessao/blocos_resumo/votos_nominais_materias_expediente.html b/sapl/templates/sessao/blocos_resumo/votos_nominais_materias_expediente.html new file mode 100644 index 000000000..91160c869 --- /dev/null +++ b/sapl/templates/sessao/blocos_resumo/votos_nominais_materias_expediente.html @@ -0,0 +1,28 @@ +
    + Votações Nominais - Matérias do Expediente +
    +
    {{m.ementa|safe}}
    {{m.ementa_observacao}}
    {{m.ementa|dont_break_out}}
    {{m.observacao|dont_break_out}}
    {{m.resultado}}
    {{m.resultado_observacao}}
    + + + + + + + + {% for m in votos_nominais_materia_expediente %} + + + {% if m.votos %} + + {% else %} + + {% endif %} + + {% endfor %} + +
    MatériaVotos
    {{ m.titulo }} + {% for v in m.votos %} +
  • {{v.parlamentar}} - {{v.voto}}
  • + {% endfor %} +
    Matéria não votada
    +
    \ No newline at end of file diff --git a/sapl/templates/sessao/blocos_resumo/votos_nominais_materias_ordem_dia.html b/sapl/templates/sessao/blocos_resumo/votos_nominais_materias_ordem_dia.html new file mode 100644 index 000000000..d610216d7 --- /dev/null +++ b/sapl/templates/sessao/blocos_resumo/votos_nominais_materias_ordem_dia.html @@ -0,0 +1,28 @@ +
    + Votações Nominais - Matérias da Ordem do Dia +
    + + + + + + + + + {% for m in votos_nominais_materia_ordem_dia %} + + + {% if m.votos %} + + {% else %} + + {% endif %} + + {% endfor %} + +
    MatériaVotos
    {{ m.titulo }} + {% for v in m.votos %} +
  • {{v.parlamentar}} - {{v.voto}}
  • + {% endfor %} +
    Matéria não votada
    +
    \ No newline at end of file diff --git a/sapl/templates/sessao/expedientemateria_list.html b/sapl/templates/sessao/expedientemateria_list.html index a2ce5e2ab..e1bd0f781 100644 --- a/sapl/templates/sessao/expedientemateria_list.html +++ b/sapl/templates/sessao/expedientemateria_list.html @@ -7,7 +7,10 @@ {% if perms|get_add_perm:view %} - {% blocktrans with verbose_name=view.verbose_name %} Ajustar Ordenação {% endblocktrans %} + {% blocktrans with verbose_name=view.verbose_name %} Reordenar pela precedência {% endblocktrans %} + + + {% blocktrans with verbose_name=view.verbose_name %} Renumerar Expediente {% endblocktrans %} {% blocktrans with verbose_name=view.verbose_name %} Adicionar Várias Matérias {% endblocktrans %} diff --git a/sapl/templates/sessao/layouts.yaml b/sapl/templates/sessao/layouts.yaml index 55e0db570..16e353910 100644 --- a/sapl/templates/sessao/layouts.yaml +++ b/sapl/templates/sessao/layouts.yaml @@ -39,6 +39,12 @@ OradorExpediente: - url_discurso observacao - upload_anexo +OradorOrdemDia: + {% trans 'Orador da Ordem do Dia' %}: + - numero_ordem parlamentar + - url_discurso observacao + - upload_anexo + ExpedienteMateria: {% trans 'Matéria do Expediente' %}: - data_ordem numero_ordem @@ -78,13 +84,6 @@ CargoBancada: {% trans 'Cargo de Bancada' %}: - nome_cargo:8 cargo_unico -Bloco: - {% trans 'Bloco' %}: - - nome - - data_criacao data_extincao - - partidos - - descricao - TipoJustificativa: {% trans 'Tipo de Justificativa' %}: - descricao diff --git a/sapl/templates/sessao/ocorrencia_sessao.html b/sapl/templates/sessao/ocorrencia_sessao.html index 0aef620d0..c50c184a1 100644 --- a/sapl/templates/sessao/ocorrencia_sessao.html +++ b/sapl/templates/sessao/ocorrencia_sessao.html @@ -4,9 +4,6 @@ {% load common_tags %} {% block actions %}{% endblock %} - -{% block title %}Ocorrências da Sessão ({{ object }}) {% endblock %} - {% block detail_content %} {% if perms|get_add_perm:view %}
    diff --git a/sapl/templates/sessao/ordemdia_list.html b/sapl/templates/sessao/ordemdia_list.html index d12e3f356..bc95cf1f1 100644 --- a/sapl/templates/sessao/ordemdia_list.html +++ b/sapl/templates/sessao/ordemdia_list.html @@ -7,7 +7,10 @@ {% if perms|get_add_perm:view %} - {% blocktrans with verbose_name=view.verbose_name %} Ajustar Ordenação {% endblocktrans %} + {% blocktrans with verbose_name=view.verbose_name %} Reordenar pela precedência {% endblocktrans %} + + + {% blocktrans with verbose_name=view.verbose_name %} Renumerar Ordem {% endblocktrans %} {% blocktrans with verbose_name=view.verbose_name %} Adicionar Várias Matérias {% endblocktrans %} diff --git a/sapl/templates/sessao/painel.html b/sapl/templates/sessao/painel.html index 1efb65854..7d8a4a104 100644 --- a/sapl/templates/sessao/painel.html +++ b/sapl/templates/sessao/painel.html @@ -26,11 +26,11 @@


    -

    Cronômetro do Discurso

    +

    Cronômetro do Discurso

    -
    +

    @@ -41,11 +41,11 @@

    -

    Cronômetro do Aparte

    +

    Cronômetro do Aparte

    -
    +

    @@ -56,11 +56,11 @@

    -

    Cronômetro da Questão de Ordem

    +

    Cronômetro da Questão de Ordem

    -
    +

    @@ -68,25 +68,28 @@
    -
    -
    -
    -

    Cronômetro de Considerações Finais

    + +
    +
    + +
    +

    Cronômetro de Considerações Finais

    -
    +
    -
    +


    -
    + +
    -
    +
    {% endblock detail_content %} @@ -113,11 +116,11 @@ $(function() { } startTime(); - - $('#discurso').prop('disabled', true); - $('#aparte').prop('disabled', true); - $('#ordem').prop('disabled', true); - $('#consideracoes').prop('disabled', true); + var audioAlertFinish = document.getElementById("audio"); + $('#discurso').prop('disabled', false); + $('#aparte').prop('disabled', false); + $('#ordem').prop('disabled', false); + $('#consideracoes').prop('disabled', false); $('#discurso').runner({ autostart: false, @@ -127,16 +130,10 @@ $(function() { milliseconds: false }).on('runnerFinish', function(eventObject, info){ $.get('/painel/cronometro', { tipo: 'discurso', action: 'stop' } ); - + audioAlertFinish.play(); $('#discursoReset').show(); $('#discurso').runner('stop'); $('#discursoStart').text('Iniciar'); - $('#aparteStart').prop('disabled', false); - $('#aparteReset').prop('disabled', false); - $('#ordemStart').prop('disabled', false); - $('#ordemReset').prop('disabled', false); - $('#consideracoesStart').prop('disabled', false); - $('#consideracoesReset').prop('disabled', false); }); @@ -149,12 +146,6 @@ $(function() { $('#discursoReset').hide(); $('#discurso').runner('start'); $('#discursoStart').text('Parar'); - $('#aparteStart').prop('disabled', false); - $('#aparteReset').prop('disabled', false); - $('#ordemStart').prop('disabled', false); - $('#ordemReset').prop('disabled', false); - $('#consideracoesStart').prop('disabled', false); - $('#consideracoesReset').prop('disabled', false); } else { @@ -163,12 +154,6 @@ $(function() { $('#discursoReset').show(); $('#discurso').runner('stop'); $('#discursoStart').text('Iniciar'); - $('#aparteStart').prop('disabled', false); - $('#aparteReset').prop('disabled', false); - $('#ordemStart').prop('disabled', false); - $('#ordemReset').prop('disabled', false); - $('#consideracoesStart').prop('disabled', false); - $('#consideracoesReset').prop('disabled', false); } }); @@ -188,16 +173,10 @@ $(function() { milliseconds: false }).on('runnerFinish', function(eventObject, info){ $.get('/painel/cronometro', { tipo: 'aparte', action: 'stop' } ); - + audioAlertFinish.play(); $('#aparteReset').show(); $('#aparte').runner('stop'); $('#aparteStart').text('Iniciar'); - $('#discursoStart').prop('disabled', false); - $('#discursoReset').prop('disabled', false); - $('#ordemStart').prop('disabled', false); - $('#ordemReset').prop('disabled', false); - $('#consideracoesStart').prop('disabled', false); - $('#consideracoesReset').prop('disabled', false); }); @@ -209,12 +188,7 @@ $(function() { $('#aparteReset').hide(); $('#aparte').runner('start'); $('#aparteStart').text('Parar'); - $('#discursoStart').prop('disabled', false); - $('#discursoReset').prop('disabled', false); - $('#ordemStart').prop('disabled', false); - $('#ordemReset').prop('disabled', false); - $('#consideracoesStart').prop('disabled', false); - $('#consideracoesReset').prop('disabled', false); + } else { $.get('/painel/cronometro', { tipo: 'aparte', action: 'stop' } ); @@ -222,12 +196,7 @@ $(function() { $('#aparteReset').show(); $('#aparte').runner('stop'); $('#aparteStart').text('Iniciar'); - $('#discursoStart').prop('disabled', false); - $('#discursoReset').prop('disabled', false); - $('#ordemStart').prop('disabled', false); - $('#ordemReset').prop('disabled', false); - $('#consideracoesStart').prop('disabled', false); - $('#consideracoesReset').prop('disabled', false); + } }); @@ -247,16 +216,11 @@ $(function() { milliseconds: false }).on('runnerFinish', function(eventObject, info){ $.get('/painel/cronometro', { tipo: 'ordem', action: 'stop' } ); - + audioAlertFinish.play(); $('#ordemReset').show(); $('#ordem').runner('stop'); $('#ordemStart').text('Iniciar'); - $('#discursoStart').prop('disabled', false); - $('#discursoReset').prop('disabled', false); - $('#aparteStart').prop('disabled', false); - $('#aparteReset').prop('disabled', false); - $('#consideracoesStart').prop('disabled', false); - $('#consideracoesReset').prop('disabled', false); + }); $('#ordemStart').click(function() { @@ -267,12 +231,7 @@ $(function() { $('#ordemReset').hide(); $('#ordem').runner('start'); $('#ordemStart').text('Parar'); - $('#discursoStart').prop('disabled', false); - $('#discursoReset').prop('disabled', false); - $('#aparteStart').prop('disabled', false); - $('#aparteReset').prop('disabled', false); - $('#consideracoesStart').prop('disabled', false); - $('#consideracoesReset').prop('disabled', false); + } else { @@ -281,12 +240,7 @@ $(function() { $('#ordemReset').show(); $('#ordem').runner('stop'); $('#ordemStart').text('Iniciar'); - $('#discursoStart').prop('disabled', false); - $('#discursoReset').prop('disabled', false); - $('#aparteStart').prop('disabled', false); - $('#aparteReset').prop('disabled', false); - $('#consideracoesStart').prop('disabled', false); - $('#consideracoesReset').prop('disabled', false); + } }); @@ -306,16 +260,11 @@ $(function() { milliseconds: false }).on('runnerFinish', function(eventObject, info){ $.get('/painel/cronometro', { tipo: 'consideracoes', action: 'stop' } ); - + audioAlertFinish.play(); $('#consideracoesReset').show(); $('#consideracoes').runner('stop'); $('#consideracoesStart').text('Iniciar'); - $('#discursoStart').prop('disabled', false); - $('#discursoReset').prop('disabled', false); - $('#ordemStart').prop('disabled', false); - $('#ordemReset').prop('disabled', false); - $('#aparteStart').prop('disabled', false); - $('#aparteReset').prop('disabled', false); + }); @@ -327,12 +276,7 @@ $(function() { $('#consideracoesReset').hide(); $('#consideracoes').runner('start'); $('#consideracoesStart').text('Parar'); - $('#discursoStart').prop('disabled', false); - $('#discursoReset').prop('disabled', false); - $('#ordemStart').prop('disabled', false); - $('#ordemReset').prop('disabled', false); - $('#aparteStart').prop('disabled', false); - $('#aparteReset').prop('disabled', false); + } else { $.get('/painel/cronometro', { tipo: 'consideracoes', action: 'stop' } ); @@ -340,12 +284,7 @@ $(function() { $('#consideracoesReset').show(); $('#consideracoes').runner('stop'); $('#consideracoesStart').text('Iniciar'); - $('#discursoStart').prop('disabled', false); - $('#discursoReset').prop('disabled', false); - $('#ordemStart').prop('disabled', false); - $('#ordemReset').prop('disabled', false); - $('#aparteStart').prop('disabled', false); - $('#aparteReset').prop('disabled', false); + } }); diff --git a/sapl/templates/sessao/presenca.html b/sapl/templates/sessao/presenca.html index a22549e19..75b61ab45 100644 --- a/sapl/templates/sessao/presenca.html +++ b/sapl/templates/sessao/presenca.html @@ -32,7 +32,7 @@
    {% else %} @@ -40,7 +40,7 @@ {% endif %} @@ -63,7 +63,7 @@ {% if check %}
    -
    @@ -76,12 +76,12 @@ diff --git a/sapl/templates/sessao/votacao/votacao_simbolica_bloco.html b/sapl/templates/sessao/votacao/votacao_simbolica_bloco.html index 4a3bafe5a..b3cfb9a62 100644 --- a/sapl/templates/sessao/votacao/votacao_simbolica_bloco.html +++ b/sapl/templates/sessao/votacao/votacao_simbolica_bloco.html @@ -7,74 +7,87 @@
    Votação Simbólica - -
    - {% if ordens %} - {% for o in ordens %} - - Matéria: {{o.materia|safe}} -
    - Ementa: {{o.materia.ementa|safe}} -

    - {% endfor %} + {% if ordens or expedientes %} + {% if total_presentes == 0 %} + + {% if origem == 'ordem' %} +
    Voltar + {% elif origem == 'expediente' %} + Voltar + {% endif %} {% else %} - {% for e in expedientes %} - - Matéria: {{e.materia|safe}} -
    - Ementa: {{e.materia.ementa|safe}} -

    - {% endfor %} - {% endif %} - Total presentes: {{total_presentes}} (com presidente) - -
    -
    - {% if total_presentes == 0 %} - - Voltar - {% else %} - -
    -
    Sim:
    -
    Não:
    -
    Abstenções:
    -
    - -
    -
    - A totalização inclui o voto do Presidente? - + {% if ordens %} + {% for o in ordens %} + + Matéria: {{o.materia|safe}} +
    + Ementa: {{o.materia.ementa|safe}} +

    + {% endfor %} + {% else %} + {% for e in expedientes %} + + Matéria: {{e.materia|safe}} +
    + Ementa: {{e.materia.ementa|safe}} +

    + {% endfor %} + {% endif %} + Total presentes: {{total_presentes}} (com presidente) +
    +
    -
    - Resultado da Votação - +
    +
    Sim*:
    +
    Não*:
    +
    Abstenções*:
    -
    -
    -
    - Observações - +
    +
    + A totalização inclui o voto do Presidente?* + +
    + +
    + Resultado da Votação* + +
    -
    - +
    +
    + Observações + +
    +
    -

    - - +

    + + + {% endif %} + {% else %} + + {% if origem == 'ordem' %} + Voltar + {% elif origem == 'expediente' %} + Voltar + {% endif %} {% endif %} + +
    {% endblock detail_content %} diff --git a/sapl/templates/sistema.html b/sapl/templates/sistema.html index f32de0022..a3e56780e 100644 --- a/sapl/templates/sistema.html +++ b/sapl/templates/sistema.html @@ -53,7 +53,7 @@ - +
    diff --git a/sapl/urls.py b/sapl/urls.py index 904b4357e..12528e07e 100644 --- a/sapl/urls.py +++ b/sapl/urls.py @@ -60,7 +60,7 @@ urlpatterns = [ url(r'', include(sapl.api.urls)), url(r'^favicon\.ico$', RedirectView.as_view( - url='/static/img/favicon.ico', permanent=True)), + url='/static/sapl/img/favicon.ico', permanent=True)), url(r'', include(sapl.redireciona_urls.urls)), ] diff --git a/sapl/utils.py b/sapl/utils.py index 56a61d06d..53ab057a9 100644 --- a/sapl/utils.py +++ b/sapl/utils.py @@ -5,8 +5,7 @@ import os import re from unicodedata import normalize as unicodedata_normalize import unicodedata - -from crispy_forms.helper import FormHelper +import logging from crispy_forms.layout import HTML, Button from django import forms from django.apps import apps @@ -15,11 +14,14 @@ from django.contrib import admin from django.contrib.contenttypes.fields import (GenericForeignKey, GenericRel, GenericRelation) from django.core.exceptions import ValidationError +from django.core.files.uploadedfile import UploadedFile from django.core.mail import get_connection from django.db import models from django.db.models import Q +from django.forms import BaseForm from django.forms.widgets import SplitDateTimeWidget from django.utils import six, timezone +from django.utils.safestring import mark_safe from django.utils.translation import ugettext_lazy as _ import django_filters from easy_thumbnails import source_generators @@ -28,9 +30,9 @@ import magic from reversion_compare.admin import CompareVersionAdmin from unipath.path import Path +from sapl.crispy_layout_mixin import SaplFormHelper from sapl.crispy_layout_mixin import SaplFormLayout, form_actions, to_row - # (26/10/2018): O separador foi mudador de '/' para 'K' # por conta dos leitores de códigos de barra, que trocavam # a '/' por '&' ou ';' @@ -41,6 +43,24 @@ def pil_image(source, exif_orientation=False, **options): return source_generators.pil_image(source, exif_orientation, **options) +def dont_break_out(value, max_part=50): + _safe = value.split() + + def chunkstring(string): + return re.findall('.{%d}' % max_part, string) + + def __map(a): + if len(a) <= max_part: + return a + return '
    ' + '
    '.join(chunkstring(a)) + + _safe = map(__map, _safe) + _safe = ' '.join(_safe) + + _safe = mark_safe(_safe) + return value + + def clear_thumbnails_cache(queryset, field): for r in queryset: @@ -81,7 +101,6 @@ autor_label = ''' ''' - autor_modal = '''