diff --git a/apache/__init__.py b/apache/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/apache/asgi.py b/apache/asgi.py new file mode 100644 index 00000000..eb18c7b2 --- /dev/null +++ b/apache/asgi.py @@ -0,0 +1,6 @@ +import os +from channels.asgi import get_channel_layer + +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "cof.settings") + +channel_layer = get_channel_layer() diff --git a/bda/migrations/0008_py3.py b/bda/migrations/0008_py3.py new file mode 100644 index 00000000..fe6a8eaf --- /dev/null +++ b/bda/migrations/0008_py3.py @@ -0,0 +1,103 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('bda', '0007_extends_spectacle'), + ] + + operations = [ + migrations.AlterField( + model_name='choixspectacle', + name='double_choice', + field=models.CharField( + verbose_name='Nombre de places', + choices=[('1', '1 place'), + ('autoquit', '2 places si possible, 1 sinon'), + ('double', '2 places sinon rien')], + max_length=10, default='1'), + ), + migrations.AlterField( + model_name='participant', + name='paymenttype', + field=models.CharField( + blank=True, + choices=[('cash', 'Cash'), ('cb', 'CB'), + ('cheque', 'Chèque'), ('autre', 'Autre')], + max_length=6, verbose_name='Moyen de paiement'), + ), + migrations.AlterField( + model_name='salle', + name='address', + field=models.TextField(verbose_name='Adresse'), + ), + migrations.AlterField( + model_name='salle', + name='name', + field=models.CharField(verbose_name='Nom', max_length=300), + ), + migrations.AlterField( + model_name='spectacle', + name='date', + field=models.DateTimeField(verbose_name='Date & heure'), + ), + migrations.AlterField( + model_name='spectacle', + name='description', + field=models.TextField(verbose_name='Description', blank=True), + ), + migrations.AlterField( + model_name='spectacle', + name='listing', + field=models.BooleanField( + verbose_name='Les places sont sur listing'), + ), + migrations.AlterField( + model_name='spectacle', + name='price', + field=models.FloatField(verbose_name="Prix d'une place"), + ), + migrations.AlterField( + model_name='spectacle', + name='slots', + field=models.IntegerField(verbose_name='Places'), + ), + migrations.AlterField( + model_name='spectacle', + name='slots_description', + field=models.TextField(verbose_name='Description des places', + blank=True), + ), + migrations.AlterField( + model_name='spectacle', + name='title', + field=models.CharField(verbose_name='Titre', max_length=300), + ), + migrations.AlterField( + model_name='tirage', + name='active', + field=models.BooleanField(verbose_name='Tirage actif', + default=False), + ), + migrations.AlterField( + model_name='tirage', + name='fermeture', + field=models.DateTimeField( + verbose_name='Date et heure de fermerture du tirage'), + ), + migrations.AlterField( + model_name='tirage', + name='ouverture', + field=models.DateTimeField( + verbose_name="Date et heure d'ouverture du tirage"), + ), + migrations.AlterField( + model_name='tirage', + name='title', + field=models.CharField(verbose_name='Titre', max_length=300), + ), + ] diff --git a/bda/templates/descriptions.html b/bda/templates/descriptions.html index 3ab514f2..7eb8b93b 100644 --- a/bda/templates/descriptions.html +++ b/bda/templates/descriptions.html @@ -20,6 +20,7 @@ .descTable{ width: auto; margin: 0 auto 1em; + border-bottom: 2px solid; border-collapse: collapse; border-spacing: 0; font-size: 14px; @@ -46,10 +47,7 @@

{{ show.location }}

{{ show.category }}

-

{{ show.date }}

{{ show.slots }} place{{ show.slots|pluralize}} {% if show.slots_description != "" %}({{ show.slots_description }}){% endif %}- {{ show.price }}

- - -

{{ show.category }}

+

{{ show.date|date:"l j F Y - H\hi" }}

{{ show.slots }} place{{ show.slots|pluralize}} {% if show.slots_description != "" %}({{ show.slots_description }}){% endif %} - {{ show.price }} euro{{ show.price|pluralize}}

@@ -61,7 +59,7 @@ {% if show.image %} -

{{ show.title }}

+

{{ show.title }}

{% endif %} diff --git a/cof/routing.py b/cof/routing.py new file mode 100644 index 00000000..7949ee3a --- /dev/null +++ b/cof/routing.py @@ -0,0 +1,3 @@ +from kfet.routing import channel_routing as kfet_channel_routings + +channel_routing = kfet_channel_routings diff --git a/cof/settings_dev.py b/cof/settings_dev.py index 20caefb8..e219bc9c 100644 --- a/cof/settings_dev.py +++ b/cof/settings_dev.py @@ -50,6 +50,9 @@ INSTALLED_APPS = ( 'django_cas_ng', 'debug_toolbar', 'bootstrapform', + 'kfet', + 'channels', + 'widget_tweaks', ) MIDDLEWARE_CLASSES = ( @@ -58,6 +61,7 @@ MIDDLEWARE_CLASSES = ( 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', + 'kfet.middleware.KFetAuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', 'django.middleware.security.SecurityMiddleware', @@ -80,6 +84,7 @@ TEMPLATES = [ 'django.core.context_processors.media', 'django.core.context_processors.static', 'gestioncof.shared.context_processor', + 'kfet.context_processors.auth', ], }, }, @@ -87,7 +92,6 @@ TEMPLATES = [ # WSGI_APPLICATION = 'cof.wsgi.application' - # Database # https://docs.djangoproject.com/en/1.8/ref/settings/#databases @@ -120,6 +124,10 @@ USE_TZ = True STATIC_URL = '/static/' +STATICFILES_DIRS = ( + os.path.join(BASE_DIR, 'static/'), +) + # Media upload (through ImageField, SiteField) # https://docs.djangoproject.com/en/1.9/ref/models/fields/ @@ -164,6 +172,17 @@ RECAPTCHA_PUBLIC_KEY = "DUMMY" RECAPTCHA_PRIVATE_KEY = "DUMMY" RECAPTCHA_USE_SSL = True +# Channels settings + +CHANNEL_LAYERS = { + "default": { + "BACKEND": "asgi_redis.RedisChannelLayer", + "CONFIG": { + "hosts": [("redis://:password_redis@127.0.0.1:6379/0")], + }, + "ROUTING": "cof.routing.channel_routing", + } +} def show_toolbar(request): """ diff --git a/cof/urls.py b/cof/urls.py index 750107fb..ca7ea247 100644 --- a/cof/urls.py +++ b/cof/urls.py @@ -81,6 +81,7 @@ urlpatterns = [ url(r'^utile_bda/bda_diff$', gestioncof_views.liste_bdadiff), url(r'^utile_cof/diff_cof$', gestioncof_views.liste_diffcof), url(r'^utile_bda/bda_revente$', gestioncof_views.liste_bdarevente), + url(r'^k-fet/', include('kfet.urls')) ] + \ (static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) if settings.DEBUG diff --git a/gestioncof/migrations/0008_py3.py b/gestioncof/migrations/0008_py3.py new file mode 100644 index 00000000..7d94d7ce --- /dev/null +++ b/gestioncof/migrations/0008_py3.py @@ -0,0 +1,253 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +def forwards(apps, schema_editor): + Profile = apps.get_model("gestioncof", "CofProfile") + Profile.objects.update(comments="") + + +class Migration(migrations.Migration): + + dependencies = [ + ('gestioncof', '0007_alter_club'), + ] + + operations = [ + migrations.AlterField( + model_name='clipper', + name='fullname', + field=models.CharField(verbose_name='Nom complet', max_length=200), + ), + migrations.AlterField( + model_name='clipper', + name='username', + field=models.CharField(verbose_name='Identifiant', max_length=20), + ), + migrations.AlterField( + model_name='cofprofile', + name='comments', + field=models.TextField( + verbose_name="Commentaires visibles par l'utilisateur", + blank=True), + ), + migrations.AlterField( + model_name='cofprofile', + name='is_cof', + field=models.BooleanField(verbose_name='Membre du COF', + default=False), + ), + migrations.AlterField( + model_name='cofprofile', + name='login_clipper', + field=models.CharField(verbose_name='Login clipper', max_length=8, + blank=True), + ), + migrations.AlterField( + model_name='cofprofile', + name='mailing_bda', + field=models.BooleanField(verbose_name='Recevoir les mails BdA', + default=False), + ), + migrations.AlterField( + model_name='cofprofile', + name='mailing_bda_revente', + field=models.BooleanField( + verbose_name='Recevoir les mails de revente de places BdA', + default=False), + ), + migrations.AlterField( + model_name='cofprofile', + name='mailing_cof', + field=models.BooleanField(verbose_name='Recevoir les mails COF', + default=False), + ), + migrations.AlterField( + model_name='cofprofile', + name='occupation', + field=models.CharField(verbose_name='Occupation', + choices=[('exterieur', 'Extérieur'), + ('1A', '1A'), + ('2A', '2A'), + ('3A', '3A'), + ('4A', '4A'), + ('archicube', 'Archicube'), + ('doctorant', 'Doctorant'), + ('CST', 'CST')], + max_length=9, default='1A'), + ), + migrations.AlterField( + model_name='cofprofile', + name='petits_cours_accept', + field=models.BooleanField(verbose_name='Recevoir des petits cours', + default=False), + ), + migrations.AlterField( + model_name='cofprofile', + name='petits_cours_remarques', + field=models.TextField( + blank=True, + verbose_name='Remarques et précisions pour les petits cours', + default=''), + ), + migrations.AlterField( + model_name='cofprofile', + name='type_cotiz', + field=models.CharField( + verbose_name='Type de cotisation', + choices=[('etudiant', 'Normalien étudiant'), + ('normalien', 'Normalien élève'), + ('exterieur', 'Extérieur')], + max_length=9, default='normalien'), + ), + migrations.AlterField( + model_name='custommail', + name='comments', + field=models.TextField( + verbose_name='Informations contextuelles sur le mail', + blank=True), + ), + migrations.AlterField( + model_name='custommail', + name='content', + field=models.TextField(verbose_name='Contenu'), + ), + migrations.AlterField( + model_name='custommail', + name='title', + field=models.CharField(verbose_name='Titre', max_length=200), + ), + migrations.AlterField( + model_name='event', + name='description', + field=models.TextField(verbose_name='Description', blank=True), + ), + migrations.AlterField( + model_name='event', + name='end_date', + field=models.DateTimeField(null=True, verbose_name='Date de fin', + blank=True), + ), + migrations.AlterField( + model_name='event', + name='image', + field=models.ImageField(upload_to='imgs/events/', null=True, + verbose_name='Image', blank=True), + ), + migrations.AlterField( + model_name='event', + name='location', + field=models.CharField(verbose_name='Lieu', max_length=200), + ), + migrations.AlterField( + model_name='event', + name='registration_open', + field=models.BooleanField(verbose_name='Inscriptions ouvertes', + default=True), + ), + migrations.AlterField( + model_name='event', + name='title', + field=models.CharField(verbose_name='Titre', max_length=200), + ), + migrations.AlterField( + model_name='eventcommentfield', + name='fieldtype', + field=models.CharField(verbose_name='Type', + choices=[('text', 'Texte long'), + ('char', 'Texte court')], + max_length=10, default='text'), + ), + migrations.AlterField( + model_name='eventcommentfield', + name='name', + field=models.CharField(verbose_name='Champ', max_length=200), + ), + migrations.AlterField( + model_name='eventcommentvalue', + name='content', + field=models.TextField(null=True, verbose_name='Contenu', + blank=True), + ), + migrations.AlterField( + model_name='eventoption', + name='multi_choices', + field=models.BooleanField(verbose_name='Choix multiples', + default=False), + ), + migrations.AlterField( + model_name='eventoption', + name='name', + field=models.CharField(verbose_name='Option', max_length=200), + ), + migrations.AlterField( + model_name='eventoptionchoice', + name='value', + field=models.CharField(verbose_name='Valeur', max_length=200), + ), + migrations.AlterField( + model_name='petitcoursability', + name='niveau', + field=models.CharField( + choices=[('college', 'Collège'), ('lycee', 'Lycée'), + ('prepa1styear', 'Prépa 1ère année / L1'), + ('prepa2ndyear', 'Prépa 2ème année / L2'), + ('licence3', 'Licence 3'), + ('other', 'Autre (préciser dans les commentaires)')], + max_length=12, verbose_name='Niveau'), + ), + migrations.AlterField( + model_name='petitcoursattribution', + name='rank', + field=models.IntegerField(verbose_name="Rang dans l'email"), + ), + migrations.AlterField( + model_name='petitcoursattributioncounter', + name='count', + field=models.IntegerField(verbose_name="Nombre d'envois", + default=0), + ), + migrations.AlterField( + model_name='petitcoursdemande', + name='niveau', + field=models.CharField( + verbose_name='Niveau', + choices=[('college', 'Collège'), ('lycee', 'Lycée'), + ('prepa1styear', 'Prépa 1ère année / L1'), + ('prepa2ndyear', 'Prépa 2ème année / L2'), + ('licence3', 'Licence 3'), + ('other', 'Autre (préciser dans les commentaires)')], + max_length=12, default=''), + ), + migrations.AlterField( + model_name='survey', + name='old', + field=models.BooleanField(verbose_name='Archiver (sondage fini)', + default=False), + ), + migrations.AlterField( + model_name='survey', + name='survey_open', + field=models.BooleanField(verbose_name='Sondage ouvert', + default=True), + ), + migrations.AlterField( + model_name='survey', + name='title', + field=models.CharField(verbose_name='Titre', max_length=200), + ), + migrations.AlterField( + model_name='surveyquestion', + name='multi_answers', + field=models.BooleanField(verbose_name='Choix multiples', + default=False), + ), + migrations.AlterField( + model_name='surveyquestion', + name='question', + field=models.CharField(verbose_name='Question', max_length=200), + ), + migrations.RunPython(forwards, migrations.RunPython.noop), + ] diff --git a/gestioncof/models.py b/gestioncof/models.py index 382a5750..19590aff 100644 --- a/gestioncof/models.py +++ b/gestioncof/models.py @@ -62,7 +62,7 @@ class CofProfile(models.Model): mailing_bda_revente = models.BooleanField( "Recevoir les mails de revente de places BdA", default=False) comments = models.TextField( - "Commentaires visibles uniquement par le Buro", blank=True) + "Commentaires visibles par l'utilisateur", blank=True) is_buro = models.BooleanField("Membre du Burô", default=False) petits_cours_accept = models.BooleanField( "Recevoir des petits cours", default=False) diff --git a/gestioncof/templates/base.html b/gestioncof/templates/base.html index 185ebd44..2a718951 100644 --- a/gestioncof/templates/base.html +++ b/gestioncof/templates/base.html @@ -4,10 +4,12 @@ {{ site.name }} - - + + + + {% block extra_head %}{% endblock %} diff --git a/gestioncof/templates/profile.html b/gestioncof/templates/profile.html index 6ba101e9..7b185150 100644 --- a/gestioncof/templates/profile.html +++ b/gestioncof/templates/profile.html @@ -13,10 +13,18 @@ {% csrf_token %} {% for field in form %} - {{ field | bootstrap}} + {{ field | bootstrap }} {% endfor %} + {% if user.profile.comments %} +
+

Commentaires

+

+ {{ user.profile.comments }} +

+
+ {% endif %}
diff --git a/kfet/__init__.py b/kfet/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/kfet/admin.py b/kfet/admin.py new file mode 100644 index 00000000..8c38f3f3 --- /dev/null +++ b/kfet/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/kfet/autocomplete.py b/kfet/autocomplete.py new file mode 100644 index 00000000..e4203bd1 --- /dev/null +++ b/kfet/autocomplete.py @@ -0,0 +1,80 @@ +# -*- coding: utf-8 -*- + +from __future__ import (absolute_import, division, + print_function, unicode_literals) +from builtins import * + +from django.shortcuts import render +from django.http import Http404 +from django.db.models import Q +from gestioncof.models import User, Clipper +from kfet.models import Account + +def account_create(request): + if "q" not in request.GET: + raise Http404 + q = request.GET.get("q") + + if (len(q) == 0): + return render(request, "kfet/account_create_autocomplete.html") + + data = {'q': q} + + queries = {} + search_words = q.split() + + queries['kfet'] = Account.objects + queries['users_cof'] = User.objects.filter(Q(profile__is_cof = True)) + queries['users_notcof'] = User.objects.filter(Q(profile__is_cof = False)) + queries['clippers'] = Clipper.objects + + for word in search_words: + queries['kfet'] = queries['kfet'].filter( + Q(cofprofile__user__username__icontains = word) + | Q(cofprofile__user__first_name__icontains = word) + | Q(cofprofile__user__last_name__icontains = word) + ) + queries['users_cof'] = queries['users_cof'].filter( + Q(username__icontains = word) + | Q(first_name__icontains = word) + | Q(last_name__icontains = word) + ) + queries['users_notcof'] = queries['users_notcof'].filter( + Q(username__icontains = word) + | Q(first_name__icontains = word) + | Q(last_name__icontains = word) + ) + queries['clippers'] = queries['clippers'].filter( + Q(username__icontains = word) + | Q(fullname__icontains = word) + ) + + queries['kfet'] = queries['kfet'].distinct() + + usernames = list( \ + queries['kfet'].values_list('cofprofile__user__username', flat=True)) + + queries['kfet'] = [ (account, account.cofprofile.user) \ + for account in queries['kfet'] ] + + queries['users_cof'] = \ + queries['users_cof'].exclude(username__in=usernames).distinct() + queries['users_notcof'] = \ + queries['users_notcof'].exclude(username__in=usernames).distinct() + + usernames += list( \ + queries['users_cof'].values_list('username', flat=True)) + usernames += list( \ + queries['users_notcof'].values_list('username', flat=True)) + + queries['clippers'] = \ + queries['clippers'].exclude(username__in=usernames).distinct() + + data.update(queries) + + options = 0 + for query in queries.values(): + options += len(query) + data['options'] = options + + return render(request, "kfet/account_create_autocomplete.html", data) diff --git a/kfet/backends.py b/kfet/backends.py new file mode 100644 index 00000000..62b2d820 --- /dev/null +++ b/kfet/backends.py @@ -0,0 +1,43 @@ +# -*- coding: utf-8 -*- + +from __future__ import (absolute_import, division, + print_function, unicode_literals) +from builtins import * + +import hashlib + +from django.contrib.auth.models import User, Permission +from gestioncof.models import CofProfile +from kfet.models import Account, GenericTeamToken + +class KFetBackend(object): + def authenticate(self, request): + password = request.POST.get('KFETPASSWORD', '') + password = request.META.get('HTTP_KFETPASSWORD', password) + if not password: + return None + + try: + password_sha256 = hashlib.sha256(password.encode()).hexdigest() + account = Account.objects.get(password=password_sha256) + user = account.cofprofile.user + except Account.DoesNotExist: + return None + + return user + +class GenericTeamBackend(object): + def authenticate(self, username=None, token=None): + valid_token = GenericTeamToken.objects.get(token=token) + if username == 'kfet_genericteam' and valid_token: + user, created = User.objects.get_or_create(username='kfet_genericteam') + perm_is_team = Permission.objects.get(codename='is_team') + user.user_permissions.add(perm_is_team) + return user + return None + + def get_user(self, user_id): + try: + return User.objects.get(pk=user_id) + except User.DoesNotExist: + return None diff --git a/kfet/consumers.py b/kfet/consumers.py new file mode 100644 index 00000000..dcd69bdf --- /dev/null +++ b/kfet/consumers.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- + +from __future__ import (absolute_import, division, + print_function, unicode_literals) +from builtins import * + +from channels import Group +from channels.generic.websockets import JsonWebsocketConsumer + +class KPsul(JsonWebsocketConsumer): + + # Set to True if you want them, else leave out + strict_ordering = False + slight_ordering = False + + def connection_groups(self, **kwargs): + return ['kfet.kpsul'] + + def connect(self, message, **kwargs): + pass + + def receive(self, content, **kwargs): + pass + + def disconnect(self, message, **kwargs): + pass diff --git a/kfet/context_processors.py b/kfet/context_processors.py new file mode 100644 index 00000000..ef4f2e64 --- /dev/null +++ b/kfet/context_processors.py @@ -0,0 +1,15 @@ +# -*- coding: utf-8 -*- + +from __future__ import (absolute_import, division, + print_function, unicode_literals) +from builtins import * + +from django.contrib.auth.context_processors import PermWrapper + +def auth(request): + if hasattr(request, 'real_user'): + return { + 'user': request.real_user, + 'perms': PermWrapper(request.real_user), + } + return {} diff --git a/kfet/decorators.py b/kfet/decorators.py new file mode 100644 index 00000000..9af9247f --- /dev/null +++ b/kfet/decorators.py @@ -0,0 +1,12 @@ +# -*- coding: utf-8 -*- + +from __future__ import (absolute_import, division, + print_function, unicode_literals) +from builtins import * + +from django_cas_ng.decorators import user_passes_test + +def kfet_is_team(user): + return user.has_perm('kfet.is_team') + +teamkfet_required = user_passes_test(lambda u: kfet_is_team(u)) diff --git a/kfet/forms.py b/kfet/forms.py new file mode 100644 index 00000000..54c0dd38 --- /dev/null +++ b/kfet/forms.py @@ -0,0 +1,516 @@ +# -*- coding: utf-8 -*- + +from __future__ import (absolute_import, division, + print_function, unicode_literals) +from builtins import * + +from decimal import Decimal +from django import forms +from django.core.exceptions import ValidationError +from django.core.validators import MinLengthValidator +from django.contrib.auth.models import User, Group, Permission +from django.contrib.contenttypes.models import ContentType +from django.forms import modelformset_factory, inlineformset_factory +from django.forms.models import BaseInlineFormSet +from django.utils import timezone +from kfet.models import (Account, Checkout, Article, OperationGroup, Operation, + CheckoutStatement, ArticleCategory, Settings, AccountNegative, Transfer, + TransferGroup, Supplier, Inventory, InventoryArticle) +from gestioncof.models import CofProfile + +# ----- +# Widgets +# ----- + +class DateTimeWidget(forms.DateTimeInput): + def __init__(self, attrs = None): + super(DateTimeWidget, self).__init__(attrs) + self.attrs['format'] = '%Y-%m-%d %H:%M' + class Media: + css = { + 'all': ('kfet/css/bootstrap-datetimepicker.min.css',) + } + js = ( + 'kfet/js/moment.js', + 'kfet/js/moment-fr.js', + 'kfet/js/bootstrap-datetimepicker.min.js', + ) +# ----- +# Account forms +# ----- + +class AccountForm(forms.ModelForm): + + # Surcharge pour passer data à Account.save() + def save(self, data = {}, *args, **kwargs): + obj = super(AccountForm, self).save(commit = False, *args, **kwargs) + obj.save(data = data) + return obj + + class Meta: + model = Account + fields = ['trigramme', 'promo', 'nickname', 'is_frozen'] + widgets = { + 'trigramme': forms.TextInput(attrs={'autocomplete': 'off'}), + } + +class AccountBalanceForm(forms.ModelForm): + class Meta: + model = Account + fields = ['balance'] + +class AccountTriForm(AccountForm): + + def clean_trigramme(self): + trigramme = self.cleaned_data['trigramme'] + return trigramme.upper() + + class Meta(AccountForm.Meta): + fields = ['trigramme'] + +class AccountNoTriForm(AccountForm): + class Meta(AccountForm.Meta): + exclude = ['trigramme'] + +class AccountRestrictForm(AccountForm): + class Meta(AccountForm.Meta): + fields = ['is_frozen'] + +class AccountPwdForm(forms.Form): + pwd1 = forms.CharField( + widget=forms.PasswordInput) + pwd2 = forms.CharField( + widget=forms.PasswordInput) + + def clean(self): + pwd1 = self.cleaned_data.get('pwd1', '') + pwd2 = self.cleaned_data.get('pwd2', '') + if len(pwd1) < 8: + raise ValidationError("Mot de passe trop court") + if pwd1 != pwd2: + raise ValidationError("Les mots de passes sont différents") + super(AccountPwdForm, self).clean() + +class CofForm(forms.ModelForm): + def clean_is_cof(self): + instance = getattr(self, 'instance', None) + if instance and instance.pk: + return instance.is_cof + else: + return False + class Meta: + model = CofProfile + fields = ['login_clipper', 'is_cof', 'departement'] + +class CofRestrictForm(CofForm): + class Meta(CofForm.Meta): + fields = ['departement'] + +class UserForm(forms.ModelForm): + def __init__(self, *args, **kwargs): + from_clipper = kwargs.pop('from_clipper', False) + new_user = kwargs.get('instance') is None and not from_clipper + super(UserForm, self).__init__(*args, **kwargs) + if new_user: + self.fields['username'].validators = [MinLengthValidator(9)] + + class Meta: + model = User + fields = ['username', 'first_name', 'last_name', 'email'] + help_texts = { + 'username': '' + } + +class UserRestrictForm(UserForm): + class Meta(UserForm.Meta): + fields = ['first_name', 'last_name'] + +class UserRestrictTeamForm(UserForm): + class Meta(UserForm.Meta): + fields = ['first_name', 'last_name', 'email'] + +class UserGroupForm(forms.ModelForm): + groups = forms.ModelMultipleChoiceField( + Group.objects.filter(name__icontains='K-Fêt')) + class Meta: + model = User + fields = ['groups'] + +class GroupForm(forms.ModelForm): + permissions = forms.ModelMultipleChoiceField( + queryset= Permission.objects.filter(content_type__in= + ContentType.objects.filter(app_label='kfet'))) + + def clean_name(self): + name = self.cleaned_data['name'] + return 'K-Fêt %s' % name + + class Meta: + model = Group + fields = ['name', 'permissions'] + +class AccountNegativeForm(forms.ModelForm): + class Meta: + model = AccountNegative + fields = ['authz_overdraft_amount', 'authz_overdraft_until', + 'balance_offset', 'comment'] + widgets = { + 'authz_overdraft_until': DateTimeWidget(), + } + +# ----- +# Checkout forms +# ----- + +class CheckoutForm(forms.ModelForm): + class Meta: + model = Checkout + fields = ['name', 'valid_from', 'valid_to', 'balance', 'is_protected'] + widgets = { + 'valid_from': DateTimeWidget(), + 'valid_to' : DateTimeWidget(), + } + +class CheckoutRestrictForm(CheckoutForm): + class Meta(CheckoutForm.Meta): + fields = ['name', 'valid_from', 'valid_to'] + + +class CheckoutStatementCreateForm(forms.ModelForm): + balance_001 = forms.IntegerField(min_value=0, initial=0, required=False) + balance_002 = forms.IntegerField(min_value=0, initial=0, required=False) + balance_005 = forms.IntegerField(min_value=0, initial=0, required=False) + balance_01 = forms.IntegerField(min_value=0, initial=0, required=False) + balance_02 = forms.IntegerField(min_value=0, initial=0, required=False) + balance_05 = forms.IntegerField(min_value=0, initial=0, required=False) + balance_1 = forms.IntegerField(min_value=0, initial=0, required=False) + balance_2 = forms.IntegerField(min_value=0, initial=0, required=False) + balance_5 = forms.IntegerField(min_value=0, initial=0, required=False) + balance_10 = forms.IntegerField(min_value=0, initial=0, required=False) + balance_20 = forms.IntegerField(min_value=0, initial=0, required=False) + balance_50 = forms.IntegerField(min_value=0, initial=0, required=False) + balance_100 = forms.IntegerField(min_value=0, initial=0, required=False) + balance_200 = forms.IntegerField(min_value=0, initial=0, required=False) + balance_500 = forms.IntegerField(min_value=0, initial=0, required=False) + + class Meta: + model = CheckoutStatement + exclude = ['by', 'at', 'checkout', 'amount_error', 'amount_taken', + 'balance_old', 'balance_new'] + + def clean(self): + not_count = self.cleaned_data['not_count'] + if not not_count and ( + self.cleaned_data['balance_001'] is None + or self.cleaned_data['balance_002'] is None + or self.cleaned_data['balance_005'] is None + or self.cleaned_data['balance_01'] is None + or self.cleaned_data['balance_02'] is None + or self.cleaned_data['balance_05'] is None + or self.cleaned_data['balance_1'] is None + or self.cleaned_data['balance_2'] is None + or self.cleaned_data['balance_5'] is None + or self.cleaned_data['balance_10'] is None + or self.cleaned_data['balance_20'] is None + or self.cleaned_data['balance_50'] is None + or self.cleaned_data['balance_100'] is None + or self.cleaned_data['balance_200'] is None + or self.cleaned_data['balance_500'] is None): + raise ValidationError("Y'a un problème. Si tu comptes la caisse, mets au moins des 0 stp (et t'as pas idée de comment c'est long de vérifier que t'as mis des valeurs de partout...)") + super(CheckoutStatementCreateForm, self).clean() + +class CheckoutStatementUpdateForm(forms.ModelForm): + class Meta: + model = CheckoutStatement + exclude = ['by', 'at', 'checkout', 'amount_error', 'amount_taken'] + +# ----- +# Article forms +# ----- + +class ArticleForm(forms.ModelForm): + category_new = forms.CharField( + max_length=45, + required = False) + category = forms.ModelChoiceField( + queryset = ArticleCategory.objects.all(), + required = False) + + suppliers = forms.ModelMultipleChoiceField( + queryset = Supplier.objects.all(), + required = False) + supplier_new = forms.CharField( + max_length = 45, + required = False) + + def __init__(self, *args, **kwargs): + super(ArticleForm, self).__init__(*args, **kwargs) + if self.instance.pk: + self.initial['suppliers'] = self.instance.suppliers.values_list('pk', flat=True) + + def clean(self): + category = self.cleaned_data.get('category') + category_new = self.cleaned_data.get('category_new') + + if not category and not category_new: + raise ValidationError('Sélectionnez une catégorie ou créez en une') + elif not category: + category, _ = ArticleCategory.objects.get_or_create(name=category_new) + self.cleaned_data['category'] = category + + super(ArticleForm, self).clean() + + class Meta: + model = Article + fields = ['name', 'is_sold', 'price', 'stock', 'category', 'box_type', + 'box_capacity'] + +class ArticleRestrictForm(ArticleForm): + class Meta(ArticleForm.Meta): + fields = ['name', 'is_sold', 'price', 'category', 'box_type', + 'box_capacity'] + +# ----- +# K-Psul forms +# ----- + +class KPsulOperationGroupForm(forms.ModelForm): + checkout = forms.ModelChoiceField( + queryset = Checkout.objects.filter( + is_protected=False, valid_from__lte=timezone.now(), + valid_to__gte=timezone.now()), + widget = forms.HiddenInput()) + class Meta: + model = OperationGroup + fields = ['on_acc', 'checkout', 'comment'] + widgets = { + 'on_acc' : forms.HiddenInput(), + } + +class KPsulAccountForm(forms.ModelForm): + class Meta: + model = Account + fields = ['trigramme'] + widgets = { + 'trigramme': forms.TextInput( + attrs={ + 'autocomplete': 'off', + 'spellcheck': 'false', + }), + } + +class KPsulCheckoutForm(forms.Form): + checkout = forms.ModelChoiceField( + queryset=Checkout.objects.filter( + is_protected=False, valid_from__lte=timezone.now(), + valid_to__gte=timezone.now()), + widget=forms.Select(attrs={'id':'id_checkout_select'})) + +class KPsulOperationForm(forms.ModelForm): + article = forms.ModelChoiceField( + queryset=Article.objects.select_related('category').all(), + required=False, + widget = forms.HiddenInput()) + class Meta: + model = Operation + fields = ['type', 'amount', 'is_checkout', 'article', 'article_nb'] + widgets = { + 'type': forms.HiddenInput(), + 'amount': forms.HiddenInput(), + 'is_checkout': forms.HiddenInput(), + 'article_nb': forms.HiddenInput(), + } + + def clean(self): + super(KPsulOperationForm, self).clean() + type_ope = self.cleaned_data.get('type') + amount = self.cleaned_data.get('amount') + article = self.cleaned_data.get('article') + article_nb = self.cleaned_data.get('article_nb') + if type_ope and type_ope == Operation.PURCHASE: + if not article or not article_nb: + raise ValidationError( + "Un achat nécessite un article et une quantité") + if article_nb < 1: + raise ValidationError("Impossible d'acheter moins de 1 article") + self.cleaned_data['is_checkout'] = True + elif type_ope and type_ope in [Operation.DEPOSIT, Operation.WITHDRAW]: + if not amount or article or article_nb: + raise ValidationError("Bad request") + if type_ope == Operation.DEPOSIT and amount <= 0: + raise ValidationError("Charge non positive") + if type_ope == Operation.WITHDRAW and amount >= 0: + raise ValidationError("Retrait non négatif") + self.cleaned_data['article'] = None + self.cleaned_data['article_nb'] = None + +KPsulOperationFormSet = modelformset_factory( + Operation, + form = KPsulOperationForm, + can_delete = True, + extra = 0, + min_num = 1, validate_min = True) + +class AddcostForm(forms.Form): + trigramme = forms.CharField(required = False) + amount = forms.DecimalField( + required = False, + max_digits=6,decimal_places=2,min_value=Decimal(0)) + + def clean(self): + trigramme = self.cleaned_data.get('trigramme') + if trigramme: + try: + Account.objects.get(trigramme=trigramme) + except Account.DoesNotExist: + raise ValidationError('Compte invalide') + else: + self.cleaned_data['amount'] = 0 + super(AddcostForm, self).clean() + +# ----- +# Settings forms +# ----- + +class SettingsForm(forms.ModelForm): + class Meta: + model = Settings + fields = ['value_decimal', 'value_account', 'value_duration'] + + def clean(self): + name = self.instance.name + value_decimal = self.cleaned_data.get('value_decimal') + value_account = self.cleaned_data.get('value_account') + value_duration = self.cleaned_data.get('value_duration') + + type_decimal = ['SUBVENTION_COF', 'ADDCOST_AMOUNT', 'OVERDRAFT_AMOUNT'] + type_account = ['ADDCOST_FOR'] + type_duration = ['OVERDRAFT_DURATION', 'CANCEL_DURATION'] + + self.cleaned_data['name'] = name + if name in type_decimal: + if not value_decimal: + raise ValidationError('Renseignez une valeur décimale') + self.cleaned_data['value_account'] = None + self.cleaned_data['value_duration'] = None + elif name in type_account: + self.cleaned_data['value_decimal'] = None + self.cleaned_data['value_duration'] = None + elif name in type_duration: + if not value_duration: + raise ValidationError('Renseignez une durée') + self.cleaned_data['value_decimal'] = None + self.cleaned_data['value_account'] = None + super(SettingsForm, self).clean() + +class FilterHistoryForm(forms.Form): + checkouts = forms.ModelMultipleChoiceField(queryset = Checkout.objects.all()) + accounts = forms.ModelMultipleChoiceField(queryset = Account.objects.all()) + +# ----- +# Transfer forms +# ----- + +class TransferGroupForm(forms.ModelForm): + class Meta: + model = TransferGroup + fields = ['comment'] + +class TransferForm(forms.ModelForm): + from_acc = forms.ModelChoiceField( + queryset = Account.objects.exclude(trigramme__in=['LIQ', '#13']), + widget = forms.HiddenInput() + ) + to_acc = forms.ModelChoiceField( + queryset = Account.objects.exclude(trigramme__in=['LIQ', '#13']), + widget = forms.HiddenInput() + ) + + def clean_amount(self): + amount = self.cleaned_data['amount'] + if amount <= 0: + raise forms.ValidationError("Montant invalide") + return amount + + class Meta: + model = Transfer + fields = ['from_acc', 'to_acc', 'amount'] + +TransferFormSet = modelformset_factory( + Transfer, + form = TransferForm, + min_num = 1, validate_min = True, + extra = 9, +) + +# ----- +# Inventory forms +# ----- + +class InventoryArticleForm(forms.Form): + article = forms.ModelChoiceField( + queryset = Article.objects.all(), + widget = forms.HiddenInput(), + ) + stock_new = forms.IntegerField(required = False) + + def __init__(self, *args, **kwargs): + super(InventoryArticleForm, self).__init__(*args, **kwargs) + if 'initial' in kwargs: + self.name = kwargs['initial']['name'] + self.stock_old = kwargs['initial']['stock_old'] + self.category = kwargs['initial']['category'] + self.category_name = kwargs['initial']['category__name'] + +# ----- +# Order forms +# ----- + +class OrderArticleForm(forms.Form): + article = forms.ModelChoiceField( + queryset = Article.objects.all(), + widget = forms.HiddenInput(), + ) + quantity_ordered = forms.IntegerField(required = False) + + def __init__(self, *args, **kwargs): + super(OrderArticleForm, self).__init__(*args, **kwargs) + if 'initial' in kwargs: + self.name = kwargs['initial']['name'] + self.stock = kwargs['initial']['stock'] + self.category = kwargs['initial']['category'] + self.category_name = kwargs['initial']['category__name'] + self.box_capacity = kwargs['initial']['box_capacity'] + self.v_s1 = kwargs['initial']['v_s1'] + self.v_s2 = kwargs['initial']['v_s2'] + self.v_s3 = kwargs['initial']['v_s3'] + self.v_s4 = kwargs['initial']['v_s4'] + self.v_s5 = kwargs['initial']['v_s5'] + self.v_moy = kwargs['initial']['v_moy'] + self.v_et = kwargs['initial']['v_et'] + self.v_prev = kwargs['initial']['v_prev'] + self.c_rec = kwargs['initial']['c_rec'] + +class OrderArticleToInventoryForm(forms.Form): + article = forms.ModelChoiceField( + queryset = Article.objects.all(), + widget = forms.HiddenInput(), + ) + price_HT = forms.DecimalField( + max_digits = 7, decimal_places = 4, + required = False) + TVA = forms.DecimalField( + max_digits = 7, decimal_places = 2, + required = False) + rights = forms.DecimalField( + max_digits = 7, decimal_places = 4, + required = False) + quantity_received = forms.IntegerField() + + def __init__(self, *args, **kwargs): + super(OrderArticleToInventoryForm, self).__init__(*args, **kwargs) + if 'initial' in kwargs: + self.name = kwargs['initial']['name'] + self.category = kwargs['initial']['category'] + self.category_name = kwargs['initial']['category__name'] + self.quantity_ordered = kwargs['initial']['quantity_ordered'] diff --git a/kfet/middleware.py b/kfet/middleware.py new file mode 100644 index 00000000..dbb192c6 --- /dev/null +++ b/kfet/middleware.py @@ -0,0 +1,17 @@ +# -*- coding: utf-8 -*- + +from __future__ import (absolute_import, division, + print_function, unicode_literals) +from builtins import * + +from django.http import HttpResponseForbidden +from kfet.backends import KFetBackend +from kfet.models import Account + +class KFetAuthenticationMiddleware(object): + def process_request(self, request): + kfet_backend = KFetBackend() + temp_request_user = kfet_backend.authenticate(request) + if temp_request_user: + request.real_user = request.user + request.user = temp_request_user diff --git a/kfet/migrations/0001_initial.py b/kfet/migrations/0001_initial.py new file mode 100644 index 00000000..8f9b14fa --- /dev/null +++ b/kfet/migrations/0001_initial.py @@ -0,0 +1,257 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations +import django.db.models.deletion +import django.core.validators +import datetime + + +class Migration(migrations.Migration): + + dependencies = [ + ('gestioncof', '0007_alter_club'), + ] + + operations = [ + migrations.CreateModel( + name='Account', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('trigramme', models.CharField(max_length=3, validators=[django.core.validators.RegexValidator(regex='^[^a-z]{3}$')], unique=True)), + ('balance', models.DecimalField(decimal_places=2, default=0, max_digits=6)), + ('frozen', models.BooleanField(default=False)), + ('promo', models.IntegerField(null=True, blank=True, choices=[(1980, 1980), (1981, 1981), (1982, 1982), (1983, 1983), (1984, 1984), (1985, 1985), (1986, 1986), (1987, 1987), (1988, 1988), (1989, 1989), (1990, 1990), (1991, 1991), (1992, 1992), (1993, 1993), (1994, 1994), (1995, 1995), (1996, 1996), (1997, 1997), (1998, 1998), (1999, 1999), (2000, 2000), (2001, 2001), (2002, 2002), (2003, 2003), (2004, 2004), (2005, 2005), (2006, 2006), (2007, 2007), (2008, 2008), (2009, 2009), (2010, 2010), (2011, 2011), (2012, 2012), (2013, 2013), (2014, 2014), (2015, 2015), (2016, 2016)], default=2015)), + ('nickname', models.CharField(max_length=255, blank=True, default='')), + ('password', models.CharField(max_length=255, blank=True, null=True, unique=True, default=None)), + ('cofprofile', models.OneToOneField(on_delete=django.db.models.deletion.PROTECT, related_name='account_kfet', to='gestioncof.CofProfile')), + ], + ), + migrations.CreateModel( + name='AccountNegative', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('start', models.DateTimeField(default=datetime.datetime(2016, 8, 2, 10, 22, 1, 569492))), + ('balance_offset', models.DecimalField(decimal_places=2, max_digits=6)), + ('authorized_overdraft', models.DecimalField(decimal_places=2, default=0, max_digits=6)), + ('comment', models.CharField(max_length=255, blank=True)), + ('account', models.OneToOneField(on_delete=django.db.models.deletion.PROTECT, related_name='negative', to='kfet.Account')), + ], + ), + migrations.CreateModel( + name='Article', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=45)), + ('is_sold', models.BooleanField(default=True)), + ('price', models.DecimalField(decimal_places=2, max_digits=6)), + ('stock', models.IntegerField(default=0)), + ], + ), + migrations.CreateModel( + name='ArticleCategory', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=45)), + ], + ), + migrations.CreateModel( + name='ArticleRule', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('ratio', models.PositiveSmallIntegerField()), + ('article_on', models.OneToOneField(on_delete=django.db.models.deletion.PROTECT, related_name='rule_on', to='kfet.Article')), + ('article_to', models.OneToOneField(on_delete=django.db.models.deletion.PROTECT, related_name='rule_to', to='kfet.Article')), + ], + ), + migrations.CreateModel( + name='Checkout', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=45)), + ('valid_from', models.DateTimeField()), + ('valid_to', models.DateTimeField()), + ('balance', models.DecimalField(decimal_places=2, max_digits=6)), + ('is_protected', models.BooleanField(default=False)), + ('created_by', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='+', to='kfet.Account')), + ], + ), + migrations.CreateModel( + name='CheckoutTransfer', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('amount', models.DecimalField(decimal_places=2, max_digits=6)), + ('from_checkout', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='transfers_from', to='kfet.Checkout')), + ('to_checkout', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='transfers_to', to='kfet.Checkout')), + ], + ), + migrations.CreateModel( + name='Inventory', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('at', models.DateTimeField(auto_now_add=True)), + ], + ), + migrations.CreateModel( + name='InventoryArticle', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('stock_old', models.IntegerField()), + ('stock_new', models.IntegerField()), + ('stock_error', models.IntegerField(default=0)), + ('article', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='kfet.Article')), + ('inventory', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='kfet.Inventory')), + ], + ), + migrations.CreateModel( + name='Operation', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('type', models.CharField(max_length=8, choices=[('purchase', 'Achat'), ('deposit', 'Charge'), ('withdraw', 'Retrait')])), + ('amount', models.DecimalField(decimal_places=2, max_digits=6)), + ('on_checkout', models.BooleanField(default=True)), + ('canceled_at', models.DateTimeField(blank=True, null=True, default=None)), + ('addcost_amount', models.DecimalField(decimal_places=2, max_digits=6)), + ('addcost_for', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, blank=True, related_name='addcosts', to='kfet.Account', null=True, default=None)), + ('article', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, blank=True, related_name='operations', to='kfet.Article', null=True, default=None)), + ('canceled_by', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, blank=True, related_name='+', to='kfet.Account', null=True, default=None)), + ], + ), + migrations.CreateModel( + name='OperationGroup', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('at', models.DateTimeField(auto_now_add=True)), + ('amount', models.IntegerField()), + ('is_cof', models.BooleanField(default=False)), + ('comment', models.CharField(max_length=255, blank=True, default='')), + ('checkout', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='operations', to='kfet.Checkout')), + ('on_acc', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='operations', to='kfet.Account')), + ('valid_by', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, blank=True, related_name='+', to='kfet.Account', null=True, default=True)), + ], + ), + migrations.CreateModel( + name='Order', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('at', models.DateTimeField(auto_now_add=True)), + ('amount', models.DecimalField(decimal_places=2, max_digits=6)), + ], + ), + migrations.CreateModel( + name='OrderArticle', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('quantity_ordered', models.IntegerField()), + ('quantity_received', models.IntegerField()), + ('article', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='kfet.Article')), + ('order', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='kfet.Order')), + ], + ), + migrations.CreateModel( + name='Statement', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('balance_old', models.DecimalField(decimal_places=2, max_digits=6)), + ('balance_new', models.DecimalField(decimal_places=2, max_digits=6)), + ('amount_taken', models.DecimalField(decimal_places=2, max_digits=6)), + ('amount_error', models.DecimalField(decimal_places=2, max_digits=6)), + ('at', models.DateTimeField(auto_now_add=True)), + ('by', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='+', to='kfet.Account')), + ('checkout', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='statements', to='kfet.Checkout')), + ], + ), + migrations.CreateModel( + name='Supplier', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=45)), + ('address', models.TextField()), + ('email', models.EmailField(max_length=254)), + ('phone', models.CharField(max_length=10)), + ('comment', models.TextField()), + ], + ), + migrations.CreateModel( + name='SupplierArticle', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('box_type', models.CharField(max_length=7, choices=[('caisse', 'Caisse'), ('carton', 'Carton'), ('palette', 'Palette'), ('fût', 'Fût')])), + ('box_capacity', models.PositiveSmallIntegerField()), + ('price_HT', models.DecimalField(decimal_places=4, max_digits=7)), + ('TVA', models.DecimalField(decimal_places=2, max_digits=4)), + ('rights', models.DecimalField(decimal_places=4, max_digits=7)), + ('article', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='kfet.Article')), + ('supplier', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='kfet.Supplier')), + ], + ), + migrations.CreateModel( + name='Transfer', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('amount', models.DecimalField(decimal_places=2, max_digits=6)), + ('canceled_at', models.DateTimeField(blank=True, null=True, default=None)), + ('canceled_by', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, blank=True, related_name='+', to='kfet.Account', null=True, default=None)), + ('from_acc', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='transfers_from', to='kfet.Account')), + ], + ), + migrations.CreateModel( + name='TransferGroup', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('at', models.DateTimeField(auto_now_add=True)), + ('comment', models.CharField(max_length=255, blank=True, default='')), + ('valid_by', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, blank=True, related_name='+', to='kfet.Account', null=True, default=None)), + ], + ), + migrations.AddField( + model_name='transfer', + name='group', + field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='transfers', to='kfet.TransferGroup'), + ), + migrations.AddField( + model_name='transfer', + name='to_acc', + field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='transfers_to', to='kfet.Account'), + ), + migrations.AddField( + model_name='supplier', + name='articles', + field=models.ManyToManyField(related_name='suppliers', through='kfet.SupplierArticle', to='kfet.Article'), + ), + migrations.AddField( + model_name='order', + name='articles', + field=models.ManyToManyField(related_name='orders', through='kfet.OrderArticle', to='kfet.Article'), + ), + migrations.AddField( + model_name='order', + name='supplier', + field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='orders', to='kfet.Supplier'), + ), + migrations.AddField( + model_name='operation', + name='group', + field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='+', to='kfet.OperationGroup'), + ), + migrations.AddField( + model_name='inventory', + name='articles', + field=models.ManyToManyField(related_name='inventories', through='kfet.InventoryArticle', to='kfet.Article'), + ), + migrations.AddField( + model_name='inventory', + name='by', + field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='+', to='kfet.Account'), + ), + migrations.AddField( + model_name='inventory', + name='order', + field=models.OneToOneField(on_delete=django.db.models.deletion.PROTECT, blank=True, related_name='inventory', to='kfet.Order', null=True, default=None), + ), + migrations.AddField( + model_name='article', + name='category', + field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='articles', to='kfet.ArticleCategory'), + ), + ] diff --git a/kfet/migrations/0002_auto_20160802_2139.py b/kfet/migrations/0002_auto_20160802_2139.py new file mode 100644 index 00000000..0a59de44 --- /dev/null +++ b/kfet/migrations/0002_auto_20160802_2139.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations +import datetime + + +class Migration(migrations.Migration): + + dependencies = [ + ('kfet', '0001_initial'), + ] + + operations = [ + migrations.AlterModelOptions( + name='account', + options={'permissions': (('is_team', 'Is part of the team'),)}, + ), + migrations.AlterField( + model_name='accountnegative', + name='start', + field=models.DateTimeField(default=datetime.datetime(2016, 8, 2, 21, 39, 30, 52279)), + ), + ] diff --git a/kfet/migrations/0003_auto_20160802_2142.py b/kfet/migrations/0003_auto_20160802_2142.py new file mode 100644 index 00000000..586146de --- /dev/null +++ b/kfet/migrations/0003_auto_20160802_2142.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations +import datetime + + +class Migration(migrations.Migration): + + dependencies = [ + ('kfet', '0002_auto_20160802_2139'), + ] + + operations = [ + migrations.AlterField( + model_name='accountnegative', + name='start', + field=models.DateTimeField(default=datetime.datetime.now), + ), + ] diff --git a/kfet/migrations/0004_auto_20160802_2144.py b/kfet/migrations/0004_auto_20160802_2144.py new file mode 100644 index 00000000..b9e9d0d3 --- /dev/null +++ b/kfet/migrations/0004_auto_20160802_2144.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('kfet', '0003_auto_20160802_2142'), + ] + + operations = [ + migrations.AlterField( + model_name='accountnegative', + name='balance_offset', + field=models.DecimalField(decimal_places=2, max_digits=6, default=0), + ), + ] diff --git a/kfet/migrations/0005_auto_20160802_2154.py b/kfet/migrations/0005_auto_20160802_2154.py new file mode 100644 index 00000000..a01419fc --- /dev/null +++ b/kfet/migrations/0005_auto_20160802_2154.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('kfet', '0004_auto_20160802_2144'), + ] + + operations = [ + migrations.CreateModel( + name='GlobalPermissions', + fields=[ + ('id', models.AutoField(serialize=False, primary_key=True, verbose_name='ID', auto_created=True)), + ], + options={ + 'permissions': (('is_team', 'Is part of the team'),), + 'managed': False, + }, + ), + migrations.AlterModelOptions( + name='account', + options={}, + ), + ] diff --git a/kfet/migrations/0006_auto_20160804_0600.py b/kfet/migrations/0006_auto_20160804_0600.py new file mode 100644 index 00000000..063504b9 --- /dev/null +++ b/kfet/migrations/0006_auto_20160804_0600.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('kfet', '0005_auto_20160802_2154'), + ] + + operations = [ + migrations.AlterModelOptions( + name='checkout', + options={'ordering': ['-valid_to']}, + ), + migrations.RenameField( + model_name='account', + old_name='frozen', + new_name='is_frozen', + ), + migrations.AlterField( + model_name='checkout', + name='balance', + field=models.DecimalField(max_digits=6, default=0, decimal_places=2), + ), + ] diff --git a/kfet/migrations/0007_auto_20160804_0641.py b/kfet/migrations/0007_auto_20160804_0641.py new file mode 100644 index 00000000..70bff402 --- /dev/null +++ b/kfet/migrations/0007_auto_20160804_0641.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('kfet', '0006_auto_20160804_0600'), + ] + + operations = [ + migrations.AlterField( + model_name='article', + name='price', + field=models.DecimalField(default=0, max_digits=6, decimal_places=2), + ), + ] diff --git a/kfet/migrations/0008_auto_20160804_1736.py b/kfet/migrations/0008_auto_20160804_1736.py new file mode 100644 index 00000000..1abbb76a --- /dev/null +++ b/kfet/migrations/0008_auto_20160804_1736.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations +import django.core.validators + + +class Migration(migrations.Migration): + + dependencies = [ + ('kfet', '0007_auto_20160804_0641'), + ] + + operations = [ + migrations.AlterField( + model_name='account', + name='trigramme', + field=models.CharField(unique=True, validators=[django.core.validators.RegexValidator(regex='^[^a-z]{3}$')], db_index=True, max_length=3), + ), + ] diff --git a/kfet/migrations/0009_auto_20160805_0720.py b/kfet/migrations/0009_auto_20160805_0720.py new file mode 100644 index 00000000..8e9a4db9 --- /dev/null +++ b/kfet/migrations/0009_auto_20160805_0720.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('kfet', '0008_auto_20160804_1736'), + ] + + operations = [ + migrations.RenameField( + model_name='operation', + old_name='on_checkout', + new_name='is_checkout', + ), + migrations.AddField( + model_name='operation', + name='article_nb', + field=models.PositiveSmallIntegerField(default=None, null=True, blank=True), + ), + ] diff --git a/kfet/migrations/0010_auto_20160806_2343.py b/kfet/migrations/0010_auto_20160806_2343.py new file mode 100644 index 00000000..60c8cc93 --- /dev/null +++ b/kfet/migrations/0010_auto_20160806_2343.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('kfet', '0009_auto_20160805_0720'), + ] + + operations = [ + migrations.AlterField( + model_name='operation', + name='addcost_amount', + field=models.DecimalField(max_digits=6, default=0, decimal_places=2), + ), + migrations.AlterField( + model_name='operationgroup', + name='amount', + field=models.DecimalField(max_digits=6, default=0, decimal_places=2), + ), + migrations.AlterField( + model_name='operationgroup', + name='valid_by', + field=models.ForeignKey(default=None, related_name='+', to='kfet.Account', blank=True, null=True, on_delete=django.db.models.deletion.PROTECT), + ), + ] diff --git a/kfet/migrations/0011_auto_20160807_1720.py b/kfet/migrations/0011_auto_20160807_1720.py new file mode 100644 index 00000000..97525676 --- /dev/null +++ b/kfet/migrations/0011_auto_20160807_1720.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('kfet', '0010_auto_20160806_2343'), + ] + + operations = [ + migrations.AlterField( + model_name='operation', + name='amount', + field=models.DecimalField(decimal_places=2, max_digits=6, default=0, blank=True), + ), + ] diff --git a/kfet/migrations/0012_settings.py b/kfet/migrations/0012_settings.py new file mode 100644 index 00000000..8f0f6247 --- /dev/null +++ b/kfet/migrations/0012_settings.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('kfet', '0011_auto_20160807_1720'), + ] + + operations = [ + migrations.CreateModel( + name='Settings', + fields=[ + ('id', models.AutoField(serialize=False, auto_created=True, primary_key=True, verbose_name='ID')), + ('name', models.CharField(max_length=45)), + ('value_decimal', models.DecimalField(null=True, max_digits=6, decimal_places=2, blank=True, default=None)), + ], + ), + ] diff --git a/kfet/migrations/0013_auto_20160807_1840.py b/kfet/migrations/0013_auto_20160807_1840.py new file mode 100644 index 00000000..d7ce2c75 --- /dev/null +++ b/kfet/migrations/0013_auto_20160807_1840.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('kfet', '0012_settings'), + ] + + operations = [ + migrations.AlterField( + model_name='settings', + name='name', + field=models.CharField(unique=True, max_length=45), + ), + ] diff --git a/kfet/migrations/0014_auto_20160807_2314.py b/kfet/migrations/0014_auto_20160807_2314.py new file mode 100644 index 00000000..50417091 --- /dev/null +++ b/kfet/migrations/0014_auto_20160807_2314.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('kfet', '0013_auto_20160807_1840'), + ] + + operations = [ + migrations.AlterModelOptions( + name='globalpermissions', + options={'permissions': (('is_team', 'Is part of the team'), ('can_perform_deposit', 'Peut effectuer une charge')), 'managed': False}, + ), + ] diff --git a/kfet/migrations/0015_auto_20160807_2324.py b/kfet/migrations/0015_auto_20160807_2324.py new file mode 100644 index 00000000..a1789fc2 --- /dev/null +++ b/kfet/migrations/0015_auto_20160807_2324.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('kfet', '0014_auto_20160807_2314'), + ] + + operations = [ + migrations.AlterModelOptions( + name='globalpermissions', + options={'permissions': (('is_team', 'Is part of the team'), ('can_perform_deposit', 'Peut effectuer une charge'), ('can_perform_negative_operations', 'Peut enregistrer des commandes en négatif')), 'managed': False}, + ), + ] diff --git a/kfet/migrations/0016_settings_value_account.py b/kfet/migrations/0016_settings_value_account.py new file mode 100644 index 00000000..53793938 --- /dev/null +++ b/kfet/migrations/0016_settings_value_account.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('kfet', '0015_auto_20160807_2324'), + ] + + operations = [ + migrations.AddField( + model_name='settings', + name='value_account', + field=models.ForeignKey(to='kfet.Account', on_delete=django.db.models.deletion.PROTECT, default=None, null=True, blank=True), + ), + ] diff --git a/kfet/migrations/0017_auto_20160808_0234.py b/kfet/migrations/0017_auto_20160808_0234.py new file mode 100644 index 00000000..e8aa8ec0 --- /dev/null +++ b/kfet/migrations/0017_auto_20160808_0234.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('kfet', '0016_settings_value_account'), + ] + + operations = [ + migrations.AlterField( + model_name='operation', + name='addcost_amount', + field=models.DecimalField(blank=True, null=True, decimal_places=2, default=None, max_digits=6), + ), + ] diff --git a/kfet/migrations/0018_auto_20160808_0341.py b/kfet/migrations/0018_auto_20160808_0341.py new file mode 100644 index 00000000..384e82b2 --- /dev/null +++ b/kfet/migrations/0018_auto_20160808_0341.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('kfet', '0017_auto_20160808_0234'), + ] + + operations = [ + migrations.AlterModelOptions( + name='globalpermissions', + options={'permissions': (('is_team', 'Is part of the team'), ('can_perform_deposit', 'Effectuer une charge'), ('can_perform_negative_operations', 'Enregistrer des commandes en négatif'), ('override_frozen_protection', "Forcer le gel d'un compte")), 'managed': False}, + ), + ] diff --git a/kfet/migrations/0019_auto_20160808_0343.py b/kfet/migrations/0019_auto_20160808_0343.py new file mode 100644 index 00000000..6512b7ea --- /dev/null +++ b/kfet/migrations/0019_auto_20160808_0343.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('kfet', '0018_auto_20160808_0341'), + ] + + operations = [ + migrations.AlterModelOptions( + name='globalpermissions', + options={'managed': False, 'permissions': (('is_team', 'Is part of the team'), ('perform_deposit', 'Effectuer une charge'), ('perform_negative_operations', 'Enregistrer des commandes en négatif'), ('override_frozen_protection', "Forcer le gel d'un compte"))}, + ), + ] diff --git a/kfet/migrations/0020_auto_20160808_0450.py b/kfet/migrations/0020_auto_20160808_0450.py new file mode 100644 index 00000000..2ecc18ee --- /dev/null +++ b/kfet/migrations/0020_auto_20160808_0450.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations +import datetime + + +class Migration(migrations.Migration): + + dependencies = [ + ('kfet', '0019_auto_20160808_0343'), + ] + + operations = [ + migrations.AlterField( + model_name='accountnegative', + name='start', + field=models.DateTimeField(default=datetime.datetime.now, blank=True, null=True), + ), + ] diff --git a/kfet/migrations/0021_auto_20160808_0506.py b/kfet/migrations/0021_auto_20160808_0506.py new file mode 100644 index 00000000..61a7ef65 --- /dev/null +++ b/kfet/migrations/0021_auto_20160808_0506.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('kfet', '0020_auto_20160808_0450'), + ] + + operations = [ + migrations.AlterField( + model_name='accountnegative', + name='start', + field=models.DateTimeField(default=None, blank=True, null=True), + ), + ] diff --git a/kfet/migrations/0022_auto_20160808_0512.py b/kfet/migrations/0022_auto_20160808_0512.py new file mode 100644 index 00000000..ba5de03e --- /dev/null +++ b/kfet/migrations/0022_auto_20160808_0512.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('kfet', '0021_auto_20160808_0506'), + ] + + operations = [ + migrations.AlterField( + model_name='accountnegative', + name='authorized_overdraft', + field=models.DecimalField(blank=True, decimal_places=2, null=True, default=None, max_digits=6), + ), + migrations.AlterField( + model_name='accountnegative', + name='balance_offset', + field=models.DecimalField(blank=True, decimal_places=2, null=True, default=None, max_digits=6), + ), + ] diff --git a/kfet/migrations/0023_auto_20160808_0535.py b/kfet/migrations/0023_auto_20160808_0535.py new file mode 100644 index 00000000..7e4d7051 --- /dev/null +++ b/kfet/migrations/0023_auto_20160808_0535.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('kfet', '0022_auto_20160808_0512'), + ] + + operations = [ + migrations.RenameField( + model_name='accountnegative', + old_name='authorized_overdraft', + new_name='authz_overdraft_amount', + ), + migrations.AddField( + model_name='accountnegative', + name='authz_overdraft_until', + field=models.DateTimeField(null=True, default=None, blank=True), + ), + ] diff --git a/kfet/migrations/0024_settings_value_duration.py b/kfet/migrations/0024_settings_value_duration.py new file mode 100644 index 00000000..1f90b1c9 --- /dev/null +++ b/kfet/migrations/0024_settings_value_duration.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('kfet', '0023_auto_20160808_0535'), + ] + + operations = [ + migrations.AddField( + model_name='settings', + name='value_duration', + field=models.DurationField(null=True, default=None, blank=True), + ), + ] diff --git a/kfet/migrations/0025_auto_20160809_0750.py b/kfet/migrations/0025_auto_20160809_0750.py new file mode 100644 index 00000000..8ba90e2d --- /dev/null +++ b/kfet/migrations/0025_auto_20160809_0750.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('kfet', '0024_settings_value_duration'), + ] + + operations = [ + migrations.AlterModelOptions( + name='globalpermissions', + options={'permissions': (('is_team', 'Is part of the team'), ('perform_deposit', 'Effectuer une charge'), ('perform_negative_operations', 'Enregistrer des commandes en négatif'), ('override_frozen_protection', "Forcer le gel d'un compte"), ('cancel_old_operations', 'Annuler des commandes non récentes')), 'managed': False}, + ), + ] diff --git a/kfet/migrations/0026_auto_20160809_0810.py b/kfet/migrations/0026_auto_20160809_0810.py new file mode 100644 index 00000000..7e96c937 --- /dev/null +++ b/kfet/migrations/0026_auto_20160809_0810.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('kfet', '0025_auto_20160809_0750'), + ] + + operations = [ + migrations.AlterField( + model_name='settings', + name='name', + field=models.CharField(db_index=True, max_length=45, unique=True), + ), + ] diff --git a/kfet/migrations/0027_auto_20160811_0648.py b/kfet/migrations/0027_auto_20160811_0648.py new file mode 100644 index 00000000..25bd826b --- /dev/null +++ b/kfet/migrations/0027_auto_20160811_0648.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('kfet', '0026_auto_20160809_0810'), + ] + + operations = [ + migrations.CreateModel( + name='CheckoutStatement', + fields=[ + ('id', models.AutoField(verbose_name='ID', primary_key=True, serialize=False, auto_created=True)), + ('balance_old', models.DecimalField(decimal_places=2, max_digits=6)), + ('balance_new', models.DecimalField(decimal_places=2, max_digits=6)), + ('amount_taken', models.DecimalField(decimal_places=2, max_digits=6)), + ('amount_error', models.DecimalField(decimal_places=2, max_digits=6)), + ('at', models.DateTimeField(auto_now_add=True)), + ('by', models.ForeignKey(to='kfet.Account', on_delete=django.db.models.deletion.PROTECT, related_name='+')), + ('checkout', models.ForeignKey(to='kfet.Checkout', on_delete=django.db.models.deletion.PROTECT, related_name='statements')), + ], + ), + migrations.RemoveField( + model_name='statement', + name='by', + ), + migrations.RemoveField( + model_name='statement', + name='checkout', + ), + migrations.DeleteModel( + name='Statement', + ), + ] diff --git a/kfet/migrations/0028_auto_20160820_0146.py b/kfet/migrations/0028_auto_20160820_0146.py new file mode 100644 index 00000000..5f8fa377 --- /dev/null +++ b/kfet/migrations/0028_auto_20160820_0146.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('kfet', '0027_auto_20160811_0648'), + ] + + operations = [ + migrations.AlterField( + model_name='operation', + name='group', + field=models.ForeignKey(to='kfet.OperationGroup', on_delete=django.db.models.deletion.PROTECT, related_name='opes'), + ), + migrations.AlterField( + model_name='operationgroup', + name='checkout', + field=models.ForeignKey(to='kfet.Checkout', on_delete=django.db.models.deletion.PROTECT, related_name='opesgroup'), + ), + migrations.AlterField( + model_name='operationgroup', + name='on_acc', + field=models.ForeignKey(to='kfet.Account', on_delete=django.db.models.deletion.PROTECT, related_name='opesgroup'), + ), + ] diff --git a/kfet/migrations/0029_genericteamtoken.py b/kfet/migrations/0029_genericteamtoken.py new file mode 100644 index 00000000..ba13674c --- /dev/null +++ b/kfet/migrations/0029_genericteamtoken.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('kfet', '0028_auto_20160820_0146'), + ] + + operations = [ + migrations.CreateModel( + name='GenericTeamToken', + fields=[ + ('id', models.AutoField(serialize=False, primary_key=True, auto_created=True, verbose_name='ID')), + ('token', models.CharField(unique=True, max_length=50)), + ], + ), + ] diff --git a/kfet/migrations/0030_auto_20160821_0029.py b/kfet/migrations/0030_auto_20160821_0029.py new file mode 100644 index 00000000..ed54efa9 --- /dev/null +++ b/kfet/migrations/0030_auto_20160821_0029.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('kfet', '0029_genericteamtoken'), + ] + + operations = [ + migrations.AlterModelOptions( + name='globalpermissions', + options={'permissions': (('is_team', 'Is part of the team'), ('perform_deposit', 'Effectuer une charge'), ('perform_negative_operations', 'Enregistrer des commandes en négatif'), ('override_frozen_protection', "Forcer le gel d'un compte"), ('cancel_old_operations', 'Annuler des commandes non récentes'), ('manage_perms', 'Gérer les permissions K-Fêt')), 'managed': False}, + ), + ] diff --git a/kfet/migrations/0031_auto_20160822_0523.py b/kfet/migrations/0031_auto_20160822_0523.py new file mode 100644 index 00000000..e7ca4d6f --- /dev/null +++ b/kfet/migrations/0031_auto_20160822_0523.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('kfet', '0030_auto_20160821_0029'), + ] + + operations = [ + migrations.AlterModelOptions( + name='globalpermissions', + options={'permissions': (('is_team', 'Is part of the team'), ('perform_deposit', 'Effectuer une charge'), ('perform_negative_operations', 'Enregistrer des commandes en négatif'), ('override_frozen_protection', "Forcer le gel d'un compte"), ('cancel_old_operations', 'Annuler des commandes non récentes'), ('manage_perms', 'Gérer les permissions K-Fêt'), ('manage_addcosts', 'Gérer les majorations')), 'managed': False}, + ), + ] diff --git a/kfet/migrations/0032_auto_20160822_2350.py b/kfet/migrations/0032_auto_20160822_2350.py new file mode 100644 index 00000000..142fb29d --- /dev/null +++ b/kfet/migrations/0032_auto_20160822_2350.py @@ -0,0 +1,94 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('kfet', '0031_auto_20160822_0523'), + ] + + operations = [ + migrations.AddField( + model_name='checkoutstatement', + name='taken_001', + field=models.PositiveSmallIntegerField(default=0), + ), + migrations.AddField( + model_name='checkoutstatement', + name='taken_002', + field=models.PositiveSmallIntegerField(default=0), + ), + migrations.AddField( + model_name='checkoutstatement', + name='taken_005', + field=models.PositiveSmallIntegerField(default=0), + ), + migrations.AddField( + model_name='checkoutstatement', + name='taken_01', + field=models.PositiveSmallIntegerField(default=0), + ), + migrations.AddField( + model_name='checkoutstatement', + name='taken_02', + field=models.PositiveSmallIntegerField(default=0), + ), + migrations.AddField( + model_name='checkoutstatement', + name='taken_05', + field=models.PositiveSmallIntegerField(default=0), + ), + migrations.AddField( + model_name='checkoutstatement', + name='taken_1', + field=models.PositiveSmallIntegerField(default=0), + ), + migrations.AddField( + model_name='checkoutstatement', + name='taken_10', + field=models.PositiveSmallIntegerField(default=0), + ), + migrations.AddField( + model_name='checkoutstatement', + name='taken_100', + field=models.PositiveSmallIntegerField(default=0), + ), + migrations.AddField( + model_name='checkoutstatement', + name='taken_2', + field=models.PositiveSmallIntegerField(default=0), + ), + migrations.AddField( + model_name='checkoutstatement', + name='taken_20', + field=models.PositiveSmallIntegerField(default=0), + ), + migrations.AddField( + model_name='checkoutstatement', + name='taken_200', + field=models.PositiveSmallIntegerField(default=0), + ), + migrations.AddField( + model_name='checkoutstatement', + name='taken_5', + field=models.PositiveSmallIntegerField(default=0), + ), + migrations.AddField( + model_name='checkoutstatement', + name='taken_50', + field=models.PositiveSmallIntegerField(default=0), + ), + migrations.AddField( + model_name='checkoutstatement', + name='taken_500', + field=models.PositiveSmallIntegerField(default=0), + ), + migrations.AddField( + model_name='checkoutstatement', + name='taken_cheque', + field=models.PositiveSmallIntegerField(default=0), + ), + ] diff --git a/kfet/migrations/0033_checkoutstatement_not_count.py b/kfet/migrations/0033_checkoutstatement_not_count.py new file mode 100644 index 00000000..50c58256 --- /dev/null +++ b/kfet/migrations/0033_checkoutstatement_not_count.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('kfet', '0032_auto_20160822_2350'), + ] + + operations = [ + migrations.AddField( + model_name='checkoutstatement', + name='not_count', + field=models.BooleanField(default=False), + ), + ] diff --git a/kfet/migrations/0034_auto_20160823_0206.py b/kfet/migrations/0034_auto_20160823_0206.py new file mode 100644 index 00000000..90d0965c --- /dev/null +++ b/kfet/migrations/0034_auto_20160823_0206.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('kfet', '0033_checkoutstatement_not_count'), + ] + + operations = [ + migrations.AlterField( + model_name='checkoutstatement', + name='taken_cheque', + field=models.DecimalField(max_digits=6, decimal_places=2, default=0), + ), + ] diff --git a/kfet/migrations/0035_auto_20160823_1505.py b/kfet/migrations/0035_auto_20160823_1505.py new file mode 100644 index 00000000..5fd73ae8 --- /dev/null +++ b/kfet/migrations/0035_auto_20160823_1505.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('kfet', '0034_auto_20160823_0206'), + ] + + operations = [ + migrations.AlterModelOptions( + name='globalpermissions', + options={'managed': False, 'permissions': (('is_team', 'Is part of the team'), ('perform_deposit', 'Effectuer une charge'), ('perform_negative_operations', 'Enregistrer des commandes en négatif'), ('override_frozen_protection', "Forcer le gel d'un compte"), ('cancel_old_operations', 'Annuler des commandes non récentes'), ('manage_perms', 'Gérer les permissions K-Fêt'), ('manage_addcosts', 'Gérer les majorations'), ('perform_commented_operations', 'Enregistrer des commandes avec commentaires'))}, + ), + ] diff --git a/kfet/migrations/0036_auto_20160823_1910.py b/kfet/migrations/0036_auto_20160823_1910.py new file mode 100644 index 00000000..2d29fd7a --- /dev/null +++ b/kfet/migrations/0036_auto_20160823_1910.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('kfet', '0035_auto_20160823_1505'), + ] + + operations = [ + migrations.AlterModelOptions( + name='globalpermissions', + options={'managed': False, 'permissions': (('is_team', 'Is part of the team'), ('perform_deposit', 'Effectuer une charge'), ('perform_negative_operations', 'Enregistrer des commandes en négatif'), ('override_frozen_protection', "Forcer le gel d'un compte"), ('cancel_old_operations', 'Annuler des commandes non récentes'), ('manage_perms', 'Gérer les permissions K-Fêt'), ('manage_addcosts', 'Gérer les majorations'), ('perform_commented_operations', 'Enregistrer des commandes avec commentaires'), ('view_negs', 'Voir la liste des négatifs'))}, + ), + ] diff --git a/kfet/migrations/0037_auto_20160826_2333.py b/kfet/migrations/0037_auto_20160826_2333.py new file mode 100644 index 00000000..9f937b60 --- /dev/null +++ b/kfet/migrations/0037_auto_20160826_2333.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('kfet', '0036_auto_20160823_1910'), + ] + + operations = [ + migrations.AlterField( + model_name='supplierarticle', + name='TVA', + field=models.DecimalField(null=True, max_digits=4, decimal_places=2, default=None, blank=True), + ), + migrations.AlterField( + model_name='supplierarticle', + name='box_capacity', + field=models.PositiveSmallIntegerField(null=True, default=None, blank=True), + ), + migrations.AlterField( + model_name='supplierarticle', + name='box_type', + field=models.CharField(null=True, max_length=7, choices=[('caisse', 'Caisse'), ('carton', 'Carton'), ('palette', 'Palette'), ('fût', 'Fût')], default=None, blank=True), + ), + migrations.AlterField( + model_name='supplierarticle', + name='price_HT', + field=models.DecimalField(null=True, max_digits=7, decimal_places=4, default=None, blank=True), + ), + migrations.AlterField( + model_name='supplierarticle', + name='rights', + field=models.DecimalField(null=True, max_digits=7, decimal_places=4, default=None, blank=True), + ), + ] diff --git a/kfet/migrations/0038_auto_20160828_0402.py b/kfet/migrations/0038_auto_20160828_0402.py new file mode 100644 index 00000000..215591b7 --- /dev/null +++ b/kfet/migrations/0038_auto_20160828_0402.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('kfet', '0037_auto_20160826_2333'), + ] + + operations = [ + migrations.AlterModelOptions( + name='inventory', + options={'ordering': ['-at']}, + ), + migrations.RemoveField( + model_name='supplierarticle', + name='box_capacity', + ), + migrations.RemoveField( + model_name='supplierarticle', + name='box_type', + ), + migrations.AddField( + model_name='article', + name='box_capacity', + field=models.PositiveSmallIntegerField(blank=True, null=True, default=None), + ), + migrations.AddField( + model_name='article', + name='box_type', + field=models.CharField(max_length=7, blank=True, null=True, default=None, choices=[('caisse', 'Caisse'), ('carton', 'Carton'), ('palette', 'Palette'), ('fût', 'Fût')]), + ), + ] diff --git a/kfet/migrations/0039_auto_20160828_0430.py b/kfet/migrations/0039_auto_20160828_0430.py new file mode 100644 index 00000000..271fda68 --- /dev/null +++ b/kfet/migrations/0039_auto_20160828_0430.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('kfet', '0038_auto_20160828_0402'), + ] + + operations = [ + migrations.AlterField( + model_name='order', + name='amount', + field=models.DecimalField(default=0, decimal_places=2, max_digits=6), + ), + migrations.AlterField( + model_name='orderarticle', + name='quantity_received', + field=models.IntegerField(default=0), + ), + ] diff --git a/kfet/migrations/0040_auto_20160829_2035.py b/kfet/migrations/0040_auto_20160829_2035.py new file mode 100644 index 00000000..78b577a8 --- /dev/null +++ b/kfet/migrations/0040_auto_20160829_2035.py @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations +import datetime +from django.utils.timezone import utc + + +class Migration(migrations.Migration): + + dependencies = [ + ('kfet', '0039_auto_20160828_0430'), + ] + + operations = [ + migrations.AlterModelOptions( + name='order', + options={'ordering': ['-at']}, + ), + migrations.AddField( + model_name='supplierarticle', + name='at', + field=models.DateTimeField(auto_now_add=True, default=datetime.datetime(2016, 8, 29, 18, 35, 3, 419033, tzinfo=utc)), + preserve_default=False, + ), + migrations.AlterField( + model_name='article', + name='box_type', + field=models.CharField(choices=[('caisse', 'caisse'), ('carton', 'carton'), ('palette', 'palette'), ('fût', 'fût')], null=True, max_length=7, blank=True, default=None), + ), + ] diff --git a/kfet/migrations/0041_auto_20160830_1502.py b/kfet/migrations/0041_auto_20160830_1502.py new file mode 100644 index 00000000..40c83907 --- /dev/null +++ b/kfet/migrations/0041_auto_20160830_1502.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('kfet', '0040_auto_20160829_2035'), + ] + + operations = [ + migrations.AlterModelOptions( + name='globalpermissions', + options={'permissions': (('is_team', 'Is part of the team'), ('perform_deposit', 'Effectuer une charge'), ('perform_negative_operations', 'Enregistrer des commandes en négatif'), ('override_frozen_protection', "Forcer le gel d'un compte"), ('cancel_old_operations', 'Annuler des commandes non récentes'), ('manage_perms', 'Gérer les permissions K-Fêt'), ('manage_addcosts', 'Gérer les majorations'), ('perform_commented_operations', 'Enregistrer des commandes avec commentaires'), ('view_negs', 'Voir la liste des négatifs'), ('order_to_inventory', "Générer un inventaire à partir d'une commande")), 'managed': False}, + ), + ] diff --git a/kfet/migrations/0042_auto_20160831_0126.py b/kfet/migrations/0042_auto_20160831_0126.py new file mode 100644 index 00000000..3306c401 --- /dev/null +++ b/kfet/migrations/0042_auto_20160831_0126.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('kfet', '0041_auto_20160830_1502'), + ] + + operations = [ + migrations.AlterModelOptions( + name='globalpermissions', + options={'managed': False, 'permissions': (('is_team', 'Is part of the team'), ('perform_deposit', 'Effectuer une charge'), ('perform_negative_operations', 'Enregistrer des commandes en négatif'), ('override_frozen_protection', "Forcer le gel d'un compte"), ('cancel_old_operations', 'Annuler des commandes non récentes'), ('manage_perms', 'Gérer les permissions K-Fêt'), ('manage_addcosts', 'Gérer les majorations'), ('perform_commented_operations', 'Enregistrer des commandes avec commentaires'), ('view_negs', 'Voir la liste des négatifs'), ('order_to_inventory', "Générer un inventaire à partir d'une commande"), ('edit_balance_account', "Modifier la balance d'un compte"))}, + ), + ] diff --git a/kfet/migrations/0043_auto_20160901_0046.py b/kfet/migrations/0043_auto_20160901_0046.py new file mode 100644 index 00000000..2d9bf12a --- /dev/null +++ b/kfet/migrations/0043_auto_20160901_0046.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('kfet', '0042_auto_20160831_0126'), + ] + + operations = [ + migrations.AlterField( + model_name='account', + name='promo', + field=models.IntegerField(blank=True, default=2016, null=True, choices=[(1980, 1980), (1981, 1981), (1982, 1982), (1983, 1983), (1984, 1984), (1985, 1985), (1986, 1986), (1987, 1987), (1988, 1988), (1989, 1989), (1990, 1990), (1991, 1991), (1992, 1992), (1993, 1993), (1994, 1994), (1995, 1995), (1996, 1996), (1997, 1997), (1998, 1998), (1999, 1999), (2000, 2000), (2001, 2001), (2002, 2002), (2003, 2003), (2004, 2004), (2005, 2005), (2006, 2006), (2007, 2007), (2008, 2008), (2009, 2009), (2010, 2010), (2011, 2011), (2012, 2012), (2013, 2013), (2014, 2014), (2015, 2015), (2016, 2016)]), + ), + ] diff --git a/kfet/migrations/0044_auto_20160901_1614.py b/kfet/migrations/0044_auto_20160901_1614.py new file mode 100644 index 00000000..2a91206a --- /dev/null +++ b/kfet/migrations/0044_auto_20160901_1614.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('kfet', '0043_auto_20160901_0046'), + ] + + operations = [ + migrations.AlterModelOptions( + name='globalpermissions', + options={'managed': False, 'permissions': (('is_team', 'Is part of the team'), ('perform_deposit', 'Effectuer une charge'), ('perform_negative_operations', 'Enregistrer des commandes en n\xe9gatif'), ('override_frozen_protection', "Forcer le gel d'un compte"), ('cancel_old_operations', 'Annuler des commandes non r\xe9centes'), ('manage_perms', 'G\xe9rer les permissions K-F\xeat'), ('manage_addcosts', 'G\xe9rer les majorations'), ('perform_commented_operations', 'Enregistrer des commandes avec commentaires'), ('view_negs', 'Voir la liste des n\xe9gatifs'), ('order_to_inventory', "G\xe9n\xe9rer un inventaire \xe0 partir d'une commande"), ('edit_balance_account', "Modifier la balance d'un compte"), ('change_account_password', "Modifier le mot de passe d'une personne de l'\xe9quipe"))}, + ), + ] diff --git a/kfet/migrations/0045_auto_20160905_0705.py b/kfet/migrations/0045_auto_20160905_0705.py new file mode 100644 index 00000000..0673fdca --- /dev/null +++ b/kfet/migrations/0045_auto_20160905_0705.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('kfet', '0044_auto_20160901_1614'), + ] + + operations = [ + migrations.AlterModelOptions( + name='globalpermissions', + options={'managed': False, 'permissions': (('is_team', 'Is part of the team'), ('perform_deposit', 'Effectuer une charge'), ('perform_negative_operations', 'Enregistrer des commandes en n\xe9gatif'), ('override_frozen_protection', "Forcer le gel d'un compte"), ('cancel_old_operations', 'Annuler des commandes non r\xe9centes'), ('manage_perms', 'G\xe9rer les permissions K-F\xeat'), ('manage_addcosts', 'G\xe9rer les majorations'), ('perform_commented_operations', 'Enregistrer des commandes avec commentaires'), ('view_negs', 'Voir la liste des n\xe9gatifs'), ('order_to_inventory', "G\xe9n\xe9rer un inventaire \xe0 partir d'une commande"), ('edit_balance_account', "Modifier la balance d'un compte"), ('change_account_password', "Modifier le mot de passe d'une personne de l'\xe9quipe"), ('special_add_account', 'Cr\xe9er un compte avec une balance initiale'))}, + ), + migrations.AlterField( + model_name='operation', + name='type', + field=models.CharField(max_length=8, choices=[('purchase', 'Achat'), ('deposit', 'Charge'), ('withdraw', 'Retrait'), ('initial', 'Initial')]), + ), + ] diff --git a/kfet/migrations/0046_account_created_at.py b/kfet/migrations/0046_account_created_at.py new file mode 100644 index 00000000..a624c0fb --- /dev/null +++ b/kfet/migrations/0046_account_created_at.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('kfet', '0045_auto_20160905_0705'), + ] + + operations = [ + migrations.AddField( + model_name='account', + name='created_at', + field=models.DateTimeField(auto_now_add=True, null=True), + ), + ] diff --git a/kfet/migrations/__init__.py b/kfet/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/kfet/models.py b/kfet/models.py new file mode 100644 index 00000000..419cd0a0 --- /dev/null +++ b/kfet/models.py @@ -0,0 +1,684 @@ +# -*- coding: utf-8 -*- + +from __future__ import (absolute_import, division, + print_function, unicode_literals) +from builtins import * + +from django.db import models +from django.core.urlresolvers import reverse +from django.core.exceptions import PermissionDenied, ValidationError +from django.core.validators import RegexValidator +from django.contrib.auth.models import User +from gestioncof.models import CofProfile +from django.utils.six.moves import reduce +from django.utils import timezone +from django.utils.encoding import python_2_unicode_compatible +from django.db import transaction +from django.db.models import F +from django.core.cache import cache +from datetime import date, timedelta +import re + +def choices_length(choices): + return reduce(lambda m, choice: max(m, len(choice[0])), choices, 0) + +def default_promo(): + now = date.today() + return now.month <= 8 and now.year-1 or now.year + +@python_2_unicode_compatible +class Account(models.Model): + cofprofile = models.OneToOneField( + CofProfile, on_delete = models.PROTECT, + related_name = "account_kfet") + trigramme = models.CharField( + unique = True, + max_length = 3, + validators = [RegexValidator(regex='^[^a-z]{3}$')], + db_index = True) + balance = models.DecimalField( + max_digits = 6, decimal_places = 2, + default = 0) + is_frozen = models.BooleanField(default = False) + created_at = models.DateTimeField(auto_now_add = True, null = True) + # Optional + PROMO_CHOICES = [(r,r) for r in range(1980, date.today().year+1)] + promo = models.IntegerField( + choices = PROMO_CHOICES, + blank = True, null = True, default = default_promo()) + nickname = models.CharField( + max_length = 255, + blank = True, default = "") + password = models.CharField( + max_length = 255, + unique = True, + blank = True, null = True, default = None) + + def __str__(self): + return '%s (%s)' % (self.trigramme, self.name) + + # Propriétés pour accéder aux attributs de user et cofprofile et user + @property + def user(self): + return self.cofprofile.user + @property + def username(self): + return self.cofprofile.user.username + @property + def first_name(self): + return self.cofprofile.user.first_name + @property + def last_name(self): + return self.cofprofile.user.last_name + @property + def email(self): + return self.cofprofile.user.email + @property + def departement(self): + return self.cofprofile.departement + @property + def is_cof(self): + return self.cofprofile.is_cof + + # Propriétés supplémentaires + @property + def real_balance(self): + if (hasattr(self, 'negative')): + return self.balance - self.negative.balance_offset + return self.balance + + @property + def name(self): + return self.user.get_full_name() + + @property + def is_cash(self): + return self.trigramme == 'LIQ' + + @property + def need_comment(self): + return self.trigramme == '#13' + + @staticmethod + def is_validandfree(trigramme): + data = { 'is_valid' : False, 'is_free' : False } + pattern = re.compile("^[^a-z]{3}$") + data['is_valid'] = pattern.match(trigramme) and True or False + try: + account = Account.objects.get(trigramme=trigramme) + except Account.DoesNotExist: + data['is_free'] = True + return data + + def perms_to_perform_operation(self, amount): + overdraft_duration_max = Settings.OVERDRAFT_DURATION() + overdraft_amount_max = Settings.OVERDRAFT_AMOUNT() + perms = set() + stop_ope = False + # Checking is cash account + if self.is_cash: + # Yes, so no perms and no stop + return set(), False + if self.need_comment: + perms.add('kfet.perform_commented_operations') + # Checking is frozen account + if self.is_frozen: + perms.add('kfet.override_frozen_protection') + new_balance = self.balance + amount + if new_balance < 0 and amount < 0: + # Retrieving overdraft amount limit + if (hasattr(self, 'negative') + and self.negative.authz_overdraft_amount is not None): + overdraft_amount = - self.negative.authz_overdraft_amount + else: + overdraft_amount = - overdraft_amount_max + # Retrieving overdraft datetime limit + if (hasattr(self, 'negative') + and self.negative.authz_overdraft_until is not None): + overdraft_until = self.negative.authz_overdraft_until + elif hasattr(self, 'negative'): + overdraft_until = \ + self.negative.start + overdraft_duration_max + else: + overdraft_until = timezone.now() + overdraft_duration_max + # Checking it doesn't break 1 rule + if new_balance < overdraft_amount or timezone.now() > overdraft_until: + stop_ope = True + perms.add('kfet.perform_negative_operations') + return perms, stop_ope + + # Surcharge Méthode save() avec gestions de User et CofProfile + # Args: + # - data : datas pour User et CofProfile + # Action: + # - Enregistre User, CofProfile à partir de "data" + # - Enregistre Account + def save(self, data = {}, *args, **kwargs): + if self.pk and data: + # Account update + + # Updating User with data + user = self.user + user.first_name = data.get("first_name", user.first_name) + user.last_name = data.get("last_name", user.last_name) + user.email = data.get("email", user.email) + user.save() + # Updating CofProfile with data + cof = self.cofprofile + cof.departement = data.get("departement", cof.departement) + cof.save() + elif data: + # New account + + # Checking if user has already an account + username = data.get("username") + try: + user = User.objects.get(username=username) + if hasattr(user.profile, "account_kfet"): + trigramme = user.profile.account_kfet.trigramme + raise Account.UserHasAccount(trigramme) + except User.DoesNotExist: + pass + + # Creating or updating User instance + (user, _) = User.objects.get_or_create(username=username) + if "first_name" in data: + user.first_name = data['first_name'] + if "last_name" in data: + user.last_name = data['last_name'] + if "email" in data: + user.email = data['email'] + user.save() + # Creating or updating CofProfile instance + (cof, _) = CofProfile.objects.get_or_create(user=user) + if "login_clipper" in data: + cof.login_clipper = data['login_clipper'] + if "departement" in data: + cof.departement = data['departement'] + cof.save() + if data: + self.cofprofile = cof + super(Account, self).save(*args, **kwargs) + + # Surcharge de delete + # Pas de suppression possible + # Cas à régler plus tard + def delete(self, *args, **kwargs): + pass + + class UserHasAccount(Exception): + def __init__(self, trigramme): + self.trigramme = trigramme + +class AccountNegative(models.Model): + account = models.OneToOneField( + Account, on_delete = models.PROTECT, + related_name = "negative") + start = models.DateTimeField( + blank = True, null = True, default = None) + balance_offset = models.DecimalField( + max_digits = 6, decimal_places = 2, + blank = True, null = True, default = None) + authz_overdraft_amount = models.DecimalField( + max_digits = 6, decimal_places = 2, + blank = True, null = True, default = None) + authz_overdraft_until = models.DateTimeField( + blank = True, null = True, default = None) + comment = models.CharField(max_length = 255, blank = True) + +@python_2_unicode_compatible +class Checkout(models.Model): + created_by = models.ForeignKey( + Account, on_delete = models.PROTECT, + related_name = "+") + name = models.CharField(max_length = 45) + valid_from = models.DateTimeField() + valid_to = models.DateTimeField() + balance = models.DecimalField( + max_digits = 6, decimal_places = 2, + default = 0) + is_protected = models.BooleanField(default = False) + + def get_absolute_url(self): + return reverse('kfet.checkout.read', kwargs={'pk': self.pk}) + + class Meta: + ordering = ['-valid_to'] + + def __str__(self): + return self.name + +class CheckoutTransfer(models.Model): + from_checkout = models.ForeignKey( + Checkout, on_delete = models.PROTECT, + related_name = "transfers_from") + to_checkout = models.ForeignKey( + Checkout, on_delete = models.PROTECT, + related_name = "transfers_to") + amount = models.DecimalField( + max_digits = 6, decimal_places = 2) + +@python_2_unicode_compatible +class CheckoutStatement(models.Model): + by = models.ForeignKey( + Account, on_delete = models.PROTECT, + related_name = "+") + checkout = models.ForeignKey( + Checkout, on_delete = models.PROTECT, + related_name = "statements") + balance_old = models.DecimalField(max_digits = 6, decimal_places = 2) + balance_new = models.DecimalField(max_digits = 6, decimal_places = 2) + amount_taken = models.DecimalField(max_digits = 6, decimal_places = 2) + amount_error = models.DecimalField(max_digits = 6, decimal_places = 2) + at = models.DateTimeField(auto_now_add = True) + not_count = models.BooleanField(default=False) + + taken_001 = models.PositiveSmallIntegerField(default=0) + taken_002 = models.PositiveSmallIntegerField(default=0) + taken_005 = models.PositiveSmallIntegerField(default=0) + taken_01 = models.PositiveSmallIntegerField(default=0) + taken_02 = models.PositiveSmallIntegerField(default=0) + taken_05 = models.PositiveSmallIntegerField(default=0) + taken_1 = models.PositiveSmallIntegerField(default=0) + taken_2 = models.PositiveSmallIntegerField(default=0) + taken_5 = models.PositiveSmallIntegerField(default=0) + taken_10 = models.PositiveSmallIntegerField(default=0) + taken_20 = models.PositiveSmallIntegerField(default=0) + taken_50 = models.PositiveSmallIntegerField(default=0) + taken_100 = models.PositiveSmallIntegerField(default=0) + taken_200 = models.PositiveSmallIntegerField(default=0) + taken_500 = models.PositiveSmallIntegerField(default=0) + taken_cheque = models.DecimalField(default=0, max_digits=6, decimal_places=2) + + def __str__(self): + return '%s %s' % (self.checkout, self.at) + + def save(self, *args, **kwargs): + if not self.pk: + checkout_id = self.checkout_id + self.balance_old = (Checkout.objects + .values_list('balance', flat=True).get(pk=checkout_id)) + if self.not_count: + self.balance_new = self.balance_old - self.amount_taken + self.amount_error = ( + self.balance_new + self.amount_taken - self.balance_old) + with transaction.atomic(): + Checkout.objects.filter(pk=checkout_id).update(balance=self.balance_new) + super(CheckoutStatement, self).save(*args, **kwargs) + else: + self.amount_error = ( + self.balance_new + self.amount_taken - self.balance_old) + # Si on modifie le dernier relevé d'une caisse et que la nouvelle + # balance est modifiée alors on modifie la balance actuelle de la caisse + last_statement = (CheckoutStatement.objects + .filter(checkout=self.checkout) + .order_by('at') + .last()) + if (last_statement.pk == self.pk + and last_statement.balance_new != self.balance_new): + Checkout.objects.filter(pk=self.checkout_id).update( + balance=F('balance') - last_statement.balance_new + self.balance_new) + super(CheckoutStatement, self).save(*args, **kwargs) + +@python_2_unicode_compatible +class ArticleCategory(models.Model): + name = models.CharField(max_length = 45) + + def __str__(self): + return self.name + +@python_2_unicode_compatible +class Article(models.Model): + name = models.CharField(max_length = 45) + is_sold = models.BooleanField(default = True) + price = models.DecimalField( + max_digits = 6, decimal_places = 2, + default = 0) + stock = models.IntegerField(default = 0) + category = models.ForeignKey( + ArticleCategory, on_delete = models.PROTECT, + related_name = "articles") + BOX_TYPE_CHOICES = ( + ("caisse", "caisse"), + ("carton", "carton"), + ("palette", "palette"), + ("fût", "fût"), + ) + box_type = models.CharField( + choices = BOX_TYPE_CHOICES, + max_length = choices_length(BOX_TYPE_CHOICES), + blank = True, null = True, default = None) + box_capacity = models.PositiveSmallIntegerField( + blank = True, null = True, default = None) + + def __str__(self): + return '%s - %s' % (self.category.name, self.name) + + def get_absolute_url(self): + return reverse('kfet.article.read', kwargs={'pk': self.pk}) + +class ArticleRule(models.Model): + article_on = models.OneToOneField( + Article, on_delete = models.PROTECT, + related_name = "rule_on") + article_to = models.OneToOneField( + Article, on_delete = models.PROTECT, + related_name = "rule_to") + ratio = models.PositiveSmallIntegerField() + +class Inventory(models.Model): + articles = models.ManyToManyField( + Article, + through = 'InventoryArticle', + related_name = "inventories") + by = models.ForeignKey( + Account, on_delete = models.PROTECT, + related_name = "+") + at = models.DateTimeField(auto_now_add = True) + # Optional + order = models.OneToOneField( + 'Order', on_delete = models.PROTECT, + related_name = "inventory", + blank = True, null = True, default = None) + + class Meta: + ordering = ['-at'] + +class InventoryArticle(models.Model): + inventory = models.ForeignKey( + Inventory, on_delete = models.PROTECT) + article = models.ForeignKey( + Article, on_delete = models.PROTECT) + stock_old = models.IntegerField() + stock_new = models.IntegerField() + stock_error = models.IntegerField(default = 0) + + def save(self, *args, **kwargs): + # S'il s'agit d'un inventaire provenant d'une livraison, il n'y a pas + # d'erreur + if not self.inventory.order: + self.stock_error = self.stock_new - self.stock_old + super(InventoryArticle, self).save(*args, **kwargs) + +@python_2_unicode_compatible +class Supplier(models.Model): + articles = models.ManyToManyField( + Article, + through = 'SupplierArticle', + related_name = "suppliers") + name = models.CharField(max_length = 45) + address = models.TextField() + email = models.EmailField() + phone = models.CharField(max_length = 10) + comment = models.TextField() + + def __str__(self): + return self.name + +class SupplierArticle(models.Model): + supplier = models.ForeignKey( + Supplier, on_delete = models.PROTECT) + article = models.ForeignKey( + Article, on_delete = models.PROTECT) + at = models.DateTimeField(auto_now_add = True) + price_HT = models.DecimalField( + max_digits = 7, decimal_places = 4, + blank = True, null = True, default = None) + TVA = models.DecimalField( + max_digits = 4, decimal_places = 2, + blank = True, null = True, default = None) + rights = models.DecimalField( + max_digits = 7, decimal_places = 4, + blank = True, null = True, default = None) + +class Order(models.Model): + supplier = models.ForeignKey( + Supplier, on_delete = models.PROTECT, + related_name = "orders") + articles = models.ManyToManyField( + Article, + through = "OrderArticle", + related_name = "orders") + at = models.DateTimeField(auto_now_add = True) + amount = models.DecimalField( + max_digits = 6, decimal_places = 2, default = 0) + + class Meta: + ordering = ['-at'] + +class OrderArticle(models.Model): + order = models.ForeignKey( + Order, on_delete = models.PROTECT) + article = models.ForeignKey( + Article, on_delete = models.PROTECT) + quantity_ordered = models.IntegerField() + quantity_received = models.IntegerField(default = 0) + +class TransferGroup(models.Model): + at = models.DateTimeField(auto_now_add = True) + # Optional + comment = models.CharField( + max_length = 255, + blank = True, default = "") + valid_by = models.ForeignKey( + Account, on_delete = models.PROTECT, + related_name = "+", + blank = True, null = True, default = None) + +class Transfer(models.Model): + group = models.ForeignKey( + TransferGroup, on_delete = models.PROTECT, + related_name = "transfers") + from_acc = models.ForeignKey( + Account, on_delete = models.PROTECT, + related_name = "transfers_from") + to_acc = models.ForeignKey( + Account, on_delete = models.PROTECT, + related_name = "transfers_to") + amount = models.DecimalField(max_digits = 6, decimal_places = 2) + # Optional + canceled_by = models.ForeignKey( + Account, on_delete = models.PROTECT, + null = True, blank = True, default = None, + related_name = "+") + canceled_at = models.DateTimeField( + null = True, blank = True, default = None) + +class OperationGroup(models.Model): + on_acc = models.ForeignKey( + Account, on_delete = models.PROTECT, + related_name = "opesgroup") + checkout = models.ForeignKey( + Checkout, on_delete = models.PROTECT, + related_name = "opesgroup") + at = models.DateTimeField(auto_now_add = True) + amount = models.DecimalField( + max_digits = 6, decimal_places = 2, + default = 0) + is_cof = models.BooleanField(default = False) + # Optional + comment = models.CharField( + max_length = 255, + blank = True, default = "") + valid_by = models.ForeignKey( + Account, on_delete = models.PROTECT, + related_name = "+", + blank = True, null = True, default = None) + +class Operation(models.Model): + PURCHASE = 'purchase' + DEPOSIT = 'deposit' + WITHDRAW = 'withdraw' + INITIAL = 'initial' + + TYPE_ORDER_CHOICES = ( + (PURCHASE, 'Achat'), + (DEPOSIT, 'Charge'), + (WITHDRAW, 'Retrait'), + (INITIAL, 'Initial'), + ) + + group = models.ForeignKey( + OperationGroup, on_delete = models.PROTECT, + related_name = "opes") + type = models.CharField( + choices = TYPE_ORDER_CHOICES, + max_length = choices_length(TYPE_ORDER_CHOICES)) + amount = models.DecimalField( + max_digits = 6, decimal_places = 2, + blank = True, default = 0) + is_checkout = models.BooleanField(default = True) + # Optional + article = models.ForeignKey( + Article, on_delete = models.PROTECT, + related_name = "operations", + blank = True, null = True, default = None) + article_nb = models.PositiveSmallIntegerField( + blank = True, null = True, default = None) + canceled_by = models.ForeignKey( + Account, on_delete = models.PROTECT, + related_name = "+", + blank = True, null = True, default = None) + canceled_at = models.DateTimeField( + blank = True, null = True, default = None) + addcost_for = models.ForeignKey( + Account, on_delete = models.PROTECT, + related_name = "addcosts", + blank = True, null = True, default = None) + addcost_amount = models.DecimalField( + max_digits = 6, decimal_places = 2, + blank = True, null = True, default = None) + +class GlobalPermissions(models.Model): + class Meta: + managed = False + permissions = ( + ('is_team', 'Is part of the team'), + ('perform_deposit', 'Effectuer une charge'), + ('perform_negative_operations', + 'Enregistrer des commandes en négatif'), + ('override_frozen_protection', "Forcer le gel d'un compte"), + ('cancel_old_operations', 'Annuler des commandes non récentes'), + ('manage_perms', 'Gérer les permissions K-Fêt'), + ('manage_addcosts', 'Gérer les majorations'), + ('perform_commented_operations', 'Enregistrer des commandes avec commentaires'), + ('view_negs', 'Voir la liste des négatifs'), + ('order_to_inventory', "Générer un inventaire à partir d'une commande"), + ('edit_balance_account', "Modifier la balance d'un compte"), + ('change_account_password', "Modifier le mot de passe d'une personne de l'équipe"), + ('special_add_account', "Créer un compte avec une balance initiale") + ) + +class Settings(models.Model): + name = models.CharField( + max_length = 45, + unique = True, + db_index = True) + value_decimal = models.DecimalField( + max_digits = 6, decimal_places = 2, + blank = True, null = True, default = None) + value_account = models.ForeignKey( + Account, on_delete = models.PROTECT, + blank = True, null = True, default = None) + value_duration = models.DurationField( + blank = True, null = True, default = None) + + @staticmethod + def setting_inst(name): + return Settings.objects.get(name=name) + + @staticmethod + def SUBVENTION_COF(): + subvention_cof = cache.get('SUBVENTION_COF') + if subvention_cof: + return subvention_cof + try: + subvention_cof = Settings.setting_inst("SUBVENTION_COF").value_decimal + except Settings.DoesNotExist: + subvention_cof = 0 + cache.set('SUBVENTION_COF', subvention_cof) + return subvention_cof + + @staticmethod + def ADDCOST_AMOUNT(): + try: + return Settings.setting_inst("ADDCOST_AMOUNT").value_decimal + except Settings.DoesNotExist: + return 0 + + @staticmethod + def ADDCOST_FOR(): + try: + return Settings.setting_inst("ADDCOST_FOR").value_account + except Settings.DoesNotExist: + return None; + + @staticmethod + def OVERDRAFT_DURATION(): + overdraft_duration = cache.get('OVERDRAFT_DURATION') + if overdraft_duration: + return overdraft_duration + try: + overdraft_duration = Settings.setting_inst("OVERDRAFT_DURATION").value_duration + except Settings.DoesNotExist: + overdraft_duration = timedelta() + cache.set('OVERDRAFT_DURATION', overdraft_duration) + return overdraft_duration + + @staticmethod + def OVERDRAFT_AMOUNT(): + overdraft_amount = cache.get('OVERDRAFT_AMOUNT') + if overdraft_amount: + return overdraft_amount + try: + overdraft_amount = Settings.setting_inst("OVERDRAFT_AMOUNT").value_decimal + except Settings.DoesNotExist: + overdraft_amount = 0 + cache.set('OVERDRAFT_AMOUNT', overdraft_amount) + return overdraft_amount + + @staticmethod + def CANCEL_DURATION(): + cancel_duration = cache.get('CANCEL_DURATION') + if cancel_duration: + return cancel_duration + try: + cancel_duration = Settings.setting_inst("CANCEL_DURATION").value_duration + except Settings.DoesNotExist: + cancel_duration = timedelta() + cache.set('CANCEL_DURATION', cancel_duration) + return cancel_duration + + @staticmethod + def create_missing(): + s, created = Settings.objects.get_or_create(name='SUBVENTION_COF') + if created: + s.value_decimal = 25 + s.save() + s, created = Settings.objects.get_or_create(name='ADDCOST_AMOUNT') + if created: + s.value_decimal = 0.5 + s.save() + s, created = Settings.objects.get_or_create(name='ADDCOST_FOR') + s, created = Settings.objects.get_or_create(name='OVERDRAFT_DURATION') + if created: + s.value_duration = timedelta(days=1) # 24h + s.save() + s, created = Settings.objects.get_or_create(name='OVERDRAFT_AMOUNT') + if created: + s.value_decimal = 20 + s.save() + s, created = Settings.objects.get_or_create(name='CANCEL_DURATION') + if created: + s.value_duration = timedelta(minutes=5) # 5min + s.save() + + @staticmethod + def empty_cache(): + cache.delete_many([ + 'SUBVENTION_COF', 'OVERDRAFT_DURATION', 'OVERDRAFT_AMOUNT', + 'CANCEL_DURATION', 'ADDCOST_AMOUNT', 'ADDCOST_FOR', + ]) + +class GenericTeamToken(models.Model): + token = models.CharField(max_length = 50, unique = True) diff --git a/kfet/routing.py b/kfet/routing.py new file mode 100644 index 00000000..e7bcca55 --- /dev/null +++ b/kfet/routing.py @@ -0,0 +1,14 @@ +# -*- coding: utf-8 -*- + +from __future__ import (absolute_import, division, + print_function, unicode_literals) +from builtins import * + +from channels.routing import route, route_class +from kfet import consumers + +channel_routing = [ + route_class(consumers.KPsul, path=r"^/ws/k-fet/k-psul/$"), + #route("websocket.connect", ws_kpsul_history_connect), + #route('websocket.receive', ws_message) +] diff --git a/kfet/static/kfet/css/bootstrap-datetimepicker.min.css b/kfet/static/kfet/css/bootstrap-datetimepicker.min.css new file mode 100644 index 00000000..cac7b200 --- /dev/null +++ b/kfet/static/kfet/css/bootstrap-datetimepicker.min.css @@ -0,0 +1,5 @@ +/*! + * Datetimepicker for Bootstrap 3 + * version : 4.17.37 + * https://github.com/Eonasdan/bootstrap-datetimepicker/ + */.bootstrap-datetimepicker-widget{display:block;list-style:none}.bootstrap-datetimepicker-widget.dropdown-menu{margin:2px 0;padding:4px;width:19em}@media (min-width:768px){.bootstrap-datetimepicker-widget.dropdown-menu.timepicker-sbs{width:38em}}@media (min-width:992px){.bootstrap-datetimepicker-widget.dropdown-menu.timepicker-sbs{width:38em}}@media (min-width:1200px){.bootstrap-datetimepicker-widget.dropdown-menu.timepicker-sbs{width:38em}}.bootstrap-datetimepicker-widget.dropdown-menu:before,.bootstrap-datetimepicker-widget.dropdown-menu:after{content:'';display:inline-block;position:absolute}.bootstrap-datetimepicker-widget.dropdown-menu.bottom:before{border-left:7px solid transparent;border-right:7px solid transparent;border-bottom:7px solid #ccc;border-bottom-color:rgba(0,0,0,0.2);top:-7px;left:7px}.bootstrap-datetimepicker-widget.dropdown-menu.bottom:after{border-left:6px solid transparent;border-right:6px solid transparent;border-bottom:6px solid white;top:-6px;left:8px}.bootstrap-datetimepicker-widget.dropdown-menu.top:before{border-left:7px solid transparent;border-right:7px solid transparent;border-top:7px solid #ccc;border-top-color:rgba(0,0,0,0.2);bottom:-7px;left:6px}.bootstrap-datetimepicker-widget.dropdown-menu.top:after{border-left:6px solid transparent;border-right:6px solid transparent;border-top:6px solid white;bottom:-6px;left:7px}.bootstrap-datetimepicker-widget.dropdown-menu.pull-right:before{left:auto;right:6px}.bootstrap-datetimepicker-widget.dropdown-menu.pull-right:after{left:auto;right:7px}.bootstrap-datetimepicker-widget .list-unstyled{margin:0}.bootstrap-datetimepicker-widget a[data-action]{padding:6px 0}.bootstrap-datetimepicker-widget a[data-action]:active{box-shadow:none}.bootstrap-datetimepicker-widget .timepicker-hour,.bootstrap-datetimepicker-widget .timepicker-minute,.bootstrap-datetimepicker-widget .timepicker-second{width:54px;font-weight:bold;font-size:1.2em;margin:0}.bootstrap-datetimepicker-widget button[data-action]{padding:6px}.bootstrap-datetimepicker-widget .btn[data-action="incrementHours"]::after{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0, 0, 0, 0);border:0;content:"Increment Hours"}.bootstrap-datetimepicker-widget .btn[data-action="incrementMinutes"]::after{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0, 0, 0, 0);border:0;content:"Increment Minutes"}.bootstrap-datetimepicker-widget .btn[data-action="decrementHours"]::after{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0, 0, 0, 0);border:0;content:"Decrement Hours"}.bootstrap-datetimepicker-widget .btn[data-action="decrementMinutes"]::after{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0, 0, 0, 0);border:0;content:"Decrement Minutes"}.bootstrap-datetimepicker-widget .btn[data-action="showHours"]::after{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0, 0, 0, 0);border:0;content:"Show Hours"}.bootstrap-datetimepicker-widget .btn[data-action="showMinutes"]::after{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0, 0, 0, 0);border:0;content:"Show Minutes"}.bootstrap-datetimepicker-widget .btn[data-action="togglePeriod"]::after{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0, 0, 0, 0);border:0;content:"Toggle AM/PM"}.bootstrap-datetimepicker-widget .btn[data-action="clear"]::after{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0, 0, 0, 0);border:0;content:"Clear the picker"}.bootstrap-datetimepicker-widget .btn[data-action="today"]::after{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0, 0, 0, 0);border:0;content:"Set the date to today"}.bootstrap-datetimepicker-widget .picker-switch{text-align:center}.bootstrap-datetimepicker-widget .picker-switch::after{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0, 0, 0, 0);border:0;content:"Toggle Date and Time Screens"}.bootstrap-datetimepicker-widget .picker-switch td{padding:0;margin:0;height:auto;width:auto;line-height:inherit}.bootstrap-datetimepicker-widget .picker-switch td span{line-height:2.5;height:2.5em;width:100%}.bootstrap-datetimepicker-widget table{width:100%;margin:0}.bootstrap-datetimepicker-widget table td,.bootstrap-datetimepicker-widget table th{text-align:center;border-radius:4px}.bootstrap-datetimepicker-widget table th{height:20px;line-height:20px;width:20px}.bootstrap-datetimepicker-widget table th.picker-switch{width:145px}.bootstrap-datetimepicker-widget table th.disabled,.bootstrap-datetimepicker-widget table th.disabled:hover{background:none;color:#777;cursor:not-allowed}.bootstrap-datetimepicker-widget table th.prev::after{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0, 0, 0, 0);border:0;content:"Previous Month"}.bootstrap-datetimepicker-widget table th.next::after{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0, 0, 0, 0);border:0;content:"Next Month"}.bootstrap-datetimepicker-widget table thead tr:first-child th{cursor:pointer}.bootstrap-datetimepicker-widget table thead tr:first-child th:hover{background:#eee}.bootstrap-datetimepicker-widget table td{height:54px;line-height:54px;width:54px}.bootstrap-datetimepicker-widget table td.cw{font-size:.8em;height:20px;line-height:20px;color:#777}.bootstrap-datetimepicker-widget table td.day{height:20px;line-height:20px;width:20px}.bootstrap-datetimepicker-widget table td.day:hover,.bootstrap-datetimepicker-widget table td.hour:hover,.bootstrap-datetimepicker-widget table td.minute:hover,.bootstrap-datetimepicker-widget table td.second:hover{background:#eee;cursor:pointer}.bootstrap-datetimepicker-widget table td.old,.bootstrap-datetimepicker-widget table td.new{color:#777}.bootstrap-datetimepicker-widget table td.today{position:relative}.bootstrap-datetimepicker-widget table td.today:before{content:'';display:inline-block;border:solid transparent;border-width:0 0 7px 7px;border-bottom-color:#337ab7;border-top-color:rgba(0,0,0,0.2);position:absolute;bottom:4px;right:4px}.bootstrap-datetimepicker-widget table td.active,.bootstrap-datetimepicker-widget table td.active:hover{background-color:#337ab7;color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25)}.bootstrap-datetimepicker-widget table td.active.today:before{border-bottom-color:#fff}.bootstrap-datetimepicker-widget table td.disabled,.bootstrap-datetimepicker-widget table td.disabled:hover{background:none;color:#777;cursor:not-allowed}.bootstrap-datetimepicker-widget table td span{display:inline-block;width:54px;height:54px;line-height:54px;margin:2px 1.5px;cursor:pointer;border-radius:4px}.bootstrap-datetimepicker-widget table td span:hover{background:#eee}.bootstrap-datetimepicker-widget table td span.active{background-color:#337ab7;color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25)}.bootstrap-datetimepicker-widget table td span.old{color:#777}.bootstrap-datetimepicker-widget table td span.disabled,.bootstrap-datetimepicker-widget table td span.disabled:hover{background:none;color:#777;cursor:not-allowed}.bootstrap-datetimepicker-widget.usetwentyfour td.hour{height:27px;line-height:27px}.bootstrap-datetimepicker-widget.wider{width:21em}.bootstrap-datetimepicker-widget .datepicker-decades .decade{line-height:1.8em !important}.input-group.date .input-group-addon{cursor:pointer}.sr-only{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0, 0, 0, 0);border:0} diff --git a/kfet/static/kfet/css/bootstrap.min.css b/kfet/static/kfet/css/bootstrap.min.css new file mode 100644 index 00000000..57f0b8fe --- /dev/null +++ b/kfet/static/kfet/css/bootstrap.min.css @@ -0,0 +1,6 @@ +/*! + * Bootstrap v3.3.7 (http://getbootstrap.com) + * Copyright 2011-2016 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + *//*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{margin:.67em 0;font-size:2em}mark{color:#000;background:#ff0}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{height:0;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{margin:0;font:inherit;color:inherit}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}input{line-height:normal}input[type=checkbox],input[type=radio]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{padding:.35em .625em .75em;margin:0 2px;border:1px solid silver}legend{padding:0;border:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-spacing:0;border-collapse:collapse}td,th{padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,:after,:before{color:#000!important;text-shadow:none!important;background:0 0!important;-webkit-box-shadow:none!important;box-shadow:none!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="javascript:"]:after,a[href^="#"]:after{content:""}blockquote,pre{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}img{max-width:100%!important}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000!important}.label{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 #ddd!important}}@font-face{font-family:'Glyphicons Halflings';src:url(../fonts/glyphicons-halflings-regular.eot);src:url(../fonts/glyphicons-halflings-regular.eot?#iefix) format('embedded-opentype'),url(../fonts/glyphicons-halflings-regular.woff2) format('woff2'),url(../fonts/glyphicons-halflings-regular.woff) format('woff'),url(../fonts/glyphicons-halflings-regular.ttf) format('truetype'),url(../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular) format('svg')}.glyphicon{position:relative;top:1px;display:inline-block;font-family:'Glyphicons Halflings';font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\002a"}.glyphicon-plus:before{content:"\002b"}.glyphicon-eur:before,.glyphicon-euro:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-download-alt:before{content:"\e025"}.glyphicon-download:before{content:"\e026"}.glyphicon-upload:before{content:"\e027"}.glyphicon-inbox:before{content:"\e028"}.glyphicon-play-circle:before{content:"\e029"}.glyphicon-repeat:before{content:"\e030"}.glyphicon-refresh:before{content:"\e031"}.glyphicon-list-alt:before{content:"\e032"}.glyphicon-lock:before{content:"\e033"}.glyphicon-flag:before{content:"\e034"}.glyphicon-headphones:before{content:"\e035"}.glyphicon-volume-off:before{content:"\e036"}.glyphicon-volume-down:before{content:"\e037"}.glyphicon-volume-up:before{content:"\e038"}.glyphicon-qrcode:before{content:"\e039"}.glyphicon-barcode:before{content:"\e040"}.glyphicon-tag:before{content:"\e041"}.glyphicon-tags:before{content:"\e042"}.glyphicon-book:before{content:"\e043"}.glyphicon-bookmark:before{content:"\e044"}.glyphicon-print:before{content:"\e045"}.glyphicon-camera:before{content:"\e046"}.glyphicon-font:before{content:"\e047"}.glyphicon-bold:before{content:"\e048"}.glyphicon-italic:before{content:"\e049"}.glyphicon-text-height:before{content:"\e050"}.glyphicon-text-width:before{content:"\e051"}.glyphicon-align-left:before{content:"\e052"}.glyphicon-align-center:before{content:"\e053"}.glyphicon-align-right:before{content:"\e054"}.glyphicon-align-justify:before{content:"\e055"}.glyphicon-list:before{content:"\e056"}.glyphicon-indent-left:before{content:"\e057"}.glyphicon-indent-right:before{content:"\e058"}.glyphicon-facetime-video:before{content:"\e059"}.glyphicon-picture:before{content:"\e060"}.glyphicon-map-marker:before{content:"\e062"}.glyphicon-adjust:before{content:"\e063"}.glyphicon-tint:before{content:"\e064"}.glyphicon-edit:before{content:"\e065"}.glyphicon-share:before{content:"\e066"}.glyphicon-check:before{content:"\e067"}.glyphicon-move:before{content:"\e068"}.glyphicon-step-backward:before{content:"\e069"}.glyphicon-fast-backward:before{content:"\e070"}.glyphicon-backward:before{content:"\e071"}.glyphicon-play:before{content:"\e072"}.glyphicon-pause:before{content:"\e073"}.glyphicon-stop:before{content:"\e074"}.glyphicon-forward:before{content:"\e075"}.glyphicon-fast-forward:before{content:"\e076"}.glyphicon-step-forward:before{content:"\e077"}.glyphicon-eject:before{content:"\e078"}.glyphicon-chevron-left:before{content:"\e079"}.glyphicon-chevron-right:before{content:"\e080"}.glyphicon-plus-sign:before{content:"\e081"}.glyphicon-minus-sign:before{content:"\e082"}.glyphicon-remove-sign:before{content:"\e083"}.glyphicon-ok-sign:before{content:"\e084"}.glyphicon-question-sign:before{content:"\e085"}.glyphicon-info-sign:before{content:"\e086"}.glyphicon-screenshot:before{content:"\e087"}.glyphicon-remove-circle:before{content:"\e088"}.glyphicon-ok-circle:before{content:"\e089"}.glyphicon-ban-circle:before{content:"\e090"}.glyphicon-arrow-left:before{content:"\e091"}.glyphicon-arrow-right:before{content:"\e092"}.glyphicon-arrow-up:before{content:"\e093"}.glyphicon-arrow-down:before{content:"\e094"}.glyphicon-share-alt:before{content:"\e095"}.glyphicon-resize-full:before{content:"\e096"}.glyphicon-resize-small:before{content:"\e097"}.glyphicon-exclamation-sign:before{content:"\e101"}.glyphicon-gift:before{content:"\e102"}.glyphicon-leaf:before{content:"\e103"}.glyphicon-fire:before{content:"\e104"}.glyphicon-eye-open:before{content:"\e105"}.glyphicon-eye-close:before{content:"\e106"}.glyphicon-warning-sign:before{content:"\e107"}.glyphicon-plane:before{content:"\e108"}.glyphicon-calendar:before{content:"\e109"}.glyphicon-random:before{content:"\e110"}.glyphicon-comment:before{content:"\e111"}.glyphicon-magnet:before{content:"\e112"}.glyphicon-chevron-up:before{content:"\e113"}.glyphicon-chevron-down:before{content:"\e114"}.glyphicon-retweet:before{content:"\e115"}.glyphicon-shopping-cart:before{content:"\e116"}.glyphicon-folder-close:before{content:"\e117"}.glyphicon-folder-open:before{content:"\e118"}.glyphicon-resize-vertical:before{content:"\e119"}.glyphicon-resize-horizontal:before{content:"\e120"}.glyphicon-hdd:before{content:"\e121"}.glyphicon-bullhorn:before{content:"\e122"}.glyphicon-bell:before{content:"\e123"}.glyphicon-certificate:before{content:"\e124"}.glyphicon-thumbs-up:before{content:"\e125"}.glyphicon-thumbs-down:before{content:"\e126"}.glyphicon-hand-right:before{content:"\e127"}.glyphicon-hand-left:before{content:"\e128"}.glyphicon-hand-up:before{content:"\e129"}.glyphicon-hand-down:before{content:"\e130"}.glyphicon-circle-arrow-right:before{content:"\e131"}.glyphicon-circle-arrow-left:before{content:"\e132"}.glyphicon-circle-arrow-up:before{content:"\e133"}.glyphicon-circle-arrow-down:before{content:"\e134"}.glyphicon-globe:before{content:"\e135"}.glyphicon-wrench:before{content:"\e136"}.glyphicon-tasks:before{content:"\e137"}.glyphicon-filter:before{content:"\e138"}.glyphicon-briefcase:before{content:"\e139"}.glyphicon-fullscreen:before{content:"\e140"}.glyphicon-dashboard:before{content:"\e141"}.glyphicon-paperclip:before{content:"\e142"}.glyphicon-heart-empty:before{content:"\e143"}.glyphicon-link:before{content:"\e144"}.glyphicon-phone:before{content:"\e145"}.glyphicon-pushpin:before{content:"\e146"}.glyphicon-usd:before{content:"\e148"}.glyphicon-gbp:before{content:"\e149"}.glyphicon-sort:before{content:"\e150"}.glyphicon-sort-by-alphabet:before{content:"\e151"}.glyphicon-sort-by-alphabet-alt:before{content:"\e152"}.glyphicon-sort-by-order:before{content:"\e153"}.glyphicon-sort-by-order-alt:before{content:"\e154"}.glyphicon-sort-by-attributes:before{content:"\e155"}.glyphicon-sort-by-attributes-alt:before{content:"\e156"}.glyphicon-unchecked:before{content:"\e157"}.glyphicon-expand:before{content:"\e158"}.glyphicon-collapse-down:before{content:"\e159"}.glyphicon-collapse-up:before{content:"\e160"}.glyphicon-log-in:before{content:"\e161"}.glyphicon-flash:before{content:"\e162"}.glyphicon-log-out:before{content:"\e163"}.glyphicon-new-window:before{content:"\e164"}.glyphicon-record:before{content:"\e165"}.glyphicon-save:before{content:"\e166"}.glyphicon-open:before{content:"\e167"}.glyphicon-saved:before{content:"\e168"}.glyphicon-import:before{content:"\e169"}.glyphicon-export:before{content:"\e170"}.glyphicon-send:before{content:"\e171"}.glyphicon-floppy-disk:before{content:"\e172"}.glyphicon-floppy-saved:before{content:"\e173"}.glyphicon-floppy-remove:before{content:"\e174"}.glyphicon-floppy-save:before{content:"\e175"}.glyphicon-floppy-open:before{content:"\e176"}.glyphicon-credit-card:before{content:"\e177"}.glyphicon-transfer:before{content:"\e178"}.glyphicon-cutlery:before{content:"\e179"}.glyphicon-header:before{content:"\e180"}.glyphicon-compressed:before{content:"\e181"}.glyphicon-earphone:before{content:"\e182"}.glyphicon-phone-alt:before{content:"\e183"}.glyphicon-tower:before{content:"\e184"}.glyphicon-stats:before{content:"\e185"}.glyphicon-sd-video:before{content:"\e186"}.glyphicon-hd-video:before{content:"\e187"}.glyphicon-subtitles:before{content:"\e188"}.glyphicon-sound-stereo:before{content:"\e189"}.glyphicon-sound-dolby:before{content:"\e190"}.glyphicon-sound-5-1:before{content:"\e191"}.glyphicon-sound-6-1:before{content:"\e192"}.glyphicon-sound-7-1:before{content:"\e193"}.glyphicon-copyright-mark:before{content:"\e194"}.glyphicon-registration-mark:before{content:"\e195"}.glyphicon-cloud-download:before{content:"\e197"}.glyphicon-cloud-upload:before{content:"\e198"}.glyphicon-tree-conifer:before{content:"\e199"}.glyphicon-tree-deciduous:before{content:"\e200"}.glyphicon-cd:before{content:"\e201"}.glyphicon-save-file:before{content:"\e202"}.glyphicon-open-file:before{content:"\e203"}.glyphicon-level-up:before{content:"\e204"}.glyphicon-copy:before{content:"\e205"}.glyphicon-paste:before{content:"\e206"}.glyphicon-alert:before{content:"\e209"}.glyphicon-equalizer:before{content:"\e210"}.glyphicon-king:before{content:"\e211"}.glyphicon-queen:before{content:"\e212"}.glyphicon-pawn:before{content:"\e213"}.glyphicon-bishop:before{content:"\e214"}.glyphicon-knight:before{content:"\e215"}.glyphicon-baby-formula:before{content:"\e216"}.glyphicon-tent:before{content:"\26fa"}.glyphicon-blackboard:before{content:"\e218"}.glyphicon-bed:before{content:"\e219"}.glyphicon-apple:before{content:"\f8ff"}.glyphicon-erase:before{content:"\e221"}.glyphicon-hourglass:before{content:"\231b"}.glyphicon-lamp:before{content:"\e223"}.glyphicon-duplicate:before{content:"\e224"}.glyphicon-piggy-bank:before{content:"\e225"}.glyphicon-scissors:before{content:"\e226"}.glyphicon-bitcoin:before{content:"\e227"}.glyphicon-btc:before{content:"\e227"}.glyphicon-xbt:before{content:"\e227"}.glyphicon-yen:before{content:"\00a5"}.glyphicon-jpy:before{content:"\00a5"}.glyphicon-ruble:before{content:"\20bd"}.glyphicon-rub:before{content:"\20bd"}.glyphicon-scale:before{content:"\e230"}.glyphicon-ice-lolly:before{content:"\e231"}.glyphicon-ice-lolly-tasted:before{content:"\e232"}.glyphicon-education:before{content:"\e233"}.glyphicon-option-horizontal:before{content:"\e234"}.glyphicon-option-vertical:before{content:"\e235"}.glyphicon-menu-hamburger:before{content:"\e236"}.glyphicon-modal-window:before{content:"\e237"}.glyphicon-oil:before{content:"\e238"}.glyphicon-grain:before{content:"\e239"}.glyphicon-sunglasses:before{content:"\e240"}.glyphicon-text-size:before{content:"\e241"}.glyphicon-text-color:before{content:"\e242"}.glyphicon-text-background:before{content:"\e243"}.glyphicon-object-align-top:before{content:"\e244"}.glyphicon-object-align-bottom:before{content:"\e245"}.glyphicon-object-align-horizontal:before{content:"\e246"}.glyphicon-object-align-left:before{content:"\e247"}.glyphicon-object-align-vertical:before{content:"\e248"}.glyphicon-object-align-right:before{content:"\e249"}.glyphicon-triangle-right:before{content:"\e250"}.glyphicon-triangle-left:before{content:"\e251"}.glyphicon-triangle-bottom:before{content:"\e252"}.glyphicon-triangle-top:before{content:"\e253"}.glyphicon-console:before{content:"\e254"}.glyphicon-superscript:before{content:"\e255"}.glyphicon-subscript:before{content:"\e256"}.glyphicon-menu-left:before{content:"\e257"}.glyphicon-menu-right:before{content:"\e258"}.glyphicon-menu-down:before{content:"\e259"}.glyphicon-menu-up:before{content:"\e260"}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}:after,:before{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333;background-color:#fff}button,input,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#337ab7;text-decoration:none}a:focus,a:hover{color:#23527c;text-decoration:underline}a:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.carousel-inner>.item>a>img,.carousel-inner>.item>img,.img-responsive,.thumbnail a>img,.thumbnail>img{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{display:inline-block;max-width:100%;height:auto;padding:4px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}[role=button]{cursor:pointer}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{font-family:inherit;font-weight:500;line-height:1.1;color:inherit}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-weight:400;line-height:1;color:#777}.h1,.h2,.h3,h1,h2,h3{margin-top:20px;margin-bottom:10px}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small{font-size:65%}.h4,.h5,.h6,h4,h5,h6{margin-top:10px;margin-bottom:10px}.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-size:75%}.h1,h1{font-size:36px}.h2,h2{font-size:30px}.h3,h3{font-size:24px}.h4,h4{font-size:18px}.h5,h5{font-size:14px}.h6,h6{font-size:12px}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16px;font-weight:300;line-height:1.4}@media (min-width:768px){.lead{font-size:21px}}.small,small{font-size:85%}.mark,mark{padding:.2em;background-color:#fcf8e3}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#777}.text-primary{color:#337ab7}a.text-primary:focus,a.text-primary:hover{color:#286090}.text-success{color:#3c763d}a.text-success:focus,a.text-success:hover{color:#2b542c}.text-info{color:#31708f}a.text-info:focus,a.text-info:hover{color:#245269}.text-warning{color:#8a6d3b}a.text-warning:focus,a.text-warning:hover{color:#66512c}.text-danger{color:#a94442}a.text-danger:focus,a.text-danger:hover{color:#843534}.bg-primary{color:#fff;background-color:#337ab7}a.bg-primary:focus,a.bg-primary:hover{background-color:#286090}.bg-success{background-color:#dff0d8}a.bg-success:focus,a.bg-success:hover{background-color:#c1e2b3}.bg-info{background-color:#d9edf7}a.bg-info:focus,a.bg-info:hover{background-color:#afd9ee}.bg-warning{background-color:#fcf8e3}a.bg-warning:focus,a.bg-warning:hover{background-color:#f7ecb5}.bg-danger{background-color:#f2dede}a.bg-danger:focus,a.bg-danger:hover{background-color:#e4b9b9}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eee}ol,ul{margin-top:0;margin-bottom:10px}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;margin-left:-5px;list-style:none}.list-inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-top:0;margin-bottom:20px}dd,dt{line-height:1.42857143}dt{font-weight:700}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;overflow:hidden;clear:left;text-align:right;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[data-original-title],abbr[title]{cursor:help;border-bottom:1px dotted #777}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10px 20px;margin:0 0 20px;font-size:17.5px;border-left:5px solid #eee}blockquote ol:last-child,blockquote p:last-child,blockquote ul:last-child{margin-bottom:0}blockquote .small,blockquote footer,blockquote small{display:block;font-size:80%;line-height:1.42857143;color:#777}blockquote .small:before,blockquote footer:before,blockquote small:before{content:'\2014 \00A0'}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;text-align:right;border-right:5px solid #eee;border-left:0}.blockquote-reverse .small:before,.blockquote-reverse footer:before,.blockquote-reverse small:before,blockquote.pull-right .small:before,blockquote.pull-right footer:before,blockquote.pull-right small:before{content:''}.blockquote-reverse .small:after,.blockquote-reverse footer:after,.blockquote-reverse small:after,blockquote.pull-right .small:after,blockquote.pull-right footer:after,blockquote.pull-right small:after{content:'\00A0 \2014'}address{margin-bottom:20px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#fff;background-color:#333;border-radius:3px;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.25);box-shadow:inset 0 -1px 0 rgba(0,0,0,.25)}kbd kbd{padding:0;font-size:100%;font-weight:700;-webkit-box-shadow:none;box-shadow:none}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.42857143;color:#333;word-break:break-all;word-wrap:break-word;background-color:#f5f5f5;border:1px solid #ccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{margin-right:-15px;margin-left:-15px}.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.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-md-1,.col-md-10,.col-md-11,.col-md-12,.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-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.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-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{position:relative;min-height:1px;padding-right:15px;padding-left:15px}.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0}@media (min-width:768px){.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:auto}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:auto}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0}}@media (min-width:992px){.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:auto}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:auto}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0}}@media (min-width:1200px){.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:auto}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:auto}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0}}table{background-color:transparent}caption{padding-top:8px;padding-bottom:8px;color:#777;text-align:left}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:20px}.table>tbody>tr>td,.table>tbody>tr>th,.table>tfoot>tr>td,.table>tfoot>tr>th,.table>thead>tr>td,.table>thead>tr>th{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #ddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}.table>caption+thead>tr:first-child>td,.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>td,.table>thead:first-child>tr:first-child>th{border-top:0}.table>tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed>tbody>tr>td,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>td,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>thead>tr>th{padding:5px}.table-bordered{border:1px solid #ddd}.table-bordered>tbody>tr>td,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>td,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border:1px solid #ddd}.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border-bottom-width:2px}.table-striped>tbody>tr:nth-of-type(odd){background-color:#f9f9f9}.table-hover>tbody>tr:hover{background-color:#f5f5f5}table col[class*=col-]{position:static;display:table-column;float:none}table td[class*=col-],table th[class*=col-]{position:static;display:table-cell;float:none}.table>tbody>tr.active>td,.table>tbody>tr.active>th,.table>tbody>tr>td.active,.table>tbody>tr>th.active,.table>tfoot>tr.active>td,.table>tfoot>tr.active>th,.table>tfoot>tr>td.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>thead>tr.active>th,.table>thead>tr>td.active,.table>thead>tr>th.active{background-color:#f5f5f5}.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr.active:hover>th,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover{background-color:#e8e8e8}.table>tbody>tr.success>td,.table>tbody>tr.success>th,.table>tbody>tr>td.success,.table>tbody>tr>th.success,.table>tfoot>tr.success>td,.table>tfoot>tr.success>th,.table>tfoot>tr>td.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>thead>tr.success>th,.table>thead>tr>td.success,.table>thead>tr>th.success{background-color:#dff0d8}.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr.success:hover>th,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover{background-color:#d0e9c6}.table>tbody>tr.info>td,.table>tbody>tr.info>th,.table>tbody>tr>td.info,.table>tbody>tr>th.info,.table>tfoot>tr.info>td,.table>tfoot>tr.info>th,.table>tfoot>tr>td.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>thead>tr.info>th,.table>thead>tr>td.info,.table>thead>tr>th.info{background-color:#d9edf7}.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr.info:hover>th,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover{background-color:#c4e3f3}.table>tbody>tr.warning>td,.table>tbody>tr.warning>th,.table>tbody>tr>td.warning,.table>tbody>tr>th.warning,.table>tfoot>tr.warning>td,.table>tfoot>tr.warning>th,.table>tfoot>tr>td.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>thead>tr.warning>th,.table>thead>tr>td.warning,.table>thead>tr>th.warning{background-color:#fcf8e3}.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr.warning:hover>th,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover{background-color:#faf2cc}.table>tbody>tr.danger>td,.table>tbody>tr.danger>th,.table>tbody>tr>td.danger,.table>tbody>tr>th.danger,.table>tfoot>tr.danger>td,.table>tfoot>tr.danger>th,.table>tfoot>tr>td.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>thead>tr.danger>th,.table>thead>tr>td.danger,.table>thead>tr>th.danger{background-color:#f2dede}.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr.danger:hover>th,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover{background-color:#ebcccc}.table-responsive{min-height:.01%;overflow-x:auto}@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #ddd}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>td,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>thead>tr>th{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}}fieldset{min-width:0;padding:0;margin:0;border:0}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}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:700}input[type=search]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type=checkbox],input[type=radio]{margin:4px 0 0;margin-top:1px\9;line-height:normal}input[type=file]{display:block}input[type=range]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type=file]:focus,input[type=checkbox]:focus,input[type=radio]:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:7px;font-size:14px;line-height:1.42857143;color:#555}.form-control{display:block;width:100%;height:34px;padding:6px 12px;font-size:14px;line-height:1.42857143;color:#555;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075);-webkit-transition:border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s;-o-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)}.form-control::-moz-placeholder{color:#999;opacity:1}.form-control:-ms-input-placeholder{color:#999}.form-control::-webkit-input-placeholder{color:#999}.form-control::-ms-expand{background-color:transparent;border:0}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{background-color:#eee;opacity:1}.form-control[disabled],fieldset[disabled] .form-control{cursor:not-allowed}textarea.form-control{height:auto}input[type=search]{-webkit-appearance:none}@media screen and (-webkit-min-device-pixel-ratio:0){input[type=date].form-control,input[type=time].form-control,input[type=datetime-local].form-control,input[type=month].form-control{line-height:34px}.input-group-sm input[type=date],.input-group-sm input[type=time],.input-group-sm input[type=datetime-local],.input-group-sm input[type=month],input[type=date].input-sm,input[type=time].input-sm,input[type=datetime-local].input-sm,input[type=month].input-sm{line-height:30px}.input-group-lg input[type=date],.input-group-lg input[type=time],.input-group-lg input[type=datetime-local],.input-group-lg input[type=month],input[type=date].input-lg,input[type=time].input-lg,input[type=datetime-local].input-lg,input[type=month].input-lg{line-height:46px}}.form-group{margin-bottom:15px}.checkbox,.radio{position:relative;display:block;margin-top:10px;margin-bottom:10px}.checkbox label,.radio label{min-height:20px;padding-left:20px;margin-bottom:0;font-weight:400;cursor:pointer}.checkbox input[type=checkbox],.checkbox-inline input[type=checkbox],.radio input[type=radio],.radio-inline input[type=radio]{position:absolute;margin-top:4px\9;margin-left:-20px}.checkbox+.checkbox,.radio+.radio{margin-top:-5px}.checkbox-inline,.radio-inline{position:relative;display:inline-block;padding-left:20px;margin-bottom:0;font-weight:400;vertical-align:middle;cursor:pointer}.checkbox-inline+.checkbox-inline,.radio-inline+.radio-inline{margin-top:0;margin-left:10px}fieldset[disabled] input[type=checkbox],fieldset[disabled] input[type=radio],input[type=checkbox].disabled,input[type=checkbox][disabled],input[type=radio].disabled,input[type=radio][disabled]{cursor:not-allowed}.checkbox-inline.disabled,.radio-inline.disabled,fieldset[disabled] .checkbox-inline,fieldset[disabled] .radio-inline{cursor:not-allowed}.checkbox.disabled label,.radio.disabled label,fieldset[disabled] .checkbox label,fieldset[disabled] .radio label{cursor:not-allowed}.form-control-static{min-height:34px;padding-top:7px;padding-bottom:7px;margin-bottom:0}.form-control-static.input-lg,.form-control-static.input-sm{padding-right:0;padding-left:0}.input-sm{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-sm{height:30px;line-height:30px}select[multiple].input-sm,textarea.input-sm{height:auto}.form-group-sm .form-control{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.form-group-sm select.form-control{height:30px;line-height:30px}.form-group-sm select[multiple].form-control,.form-group-sm textarea.form-control{height:auto}.form-group-sm .form-control-static{height:30px;min-height:32px;padding:6px 10px;font-size:12px;line-height:1.5}.input-lg{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-lg{height:46px;line-height:46px}select[multiple].input-lg,textarea.input-lg{height:auto}.form-group-lg .form-control{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.form-group-lg select.form-control{height:46px;line-height:46px}.form-group-lg select[multiple].form-control,.form-group-lg textarea.form-control{height:auto}.form-group-lg .form-control-static{height:46px;min-height:38px;padding:11px 16px;font-size:18px;line-height:1.3333333}.has-feedback{position:relative}.has-feedback .form-control{padding-right:42.5px}.form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:34px;height:34px;line-height:34px;text-align:center;pointer-events:none}.form-group-lg .form-control+.form-control-feedback,.input-group-lg+.form-control-feedback,.input-lg+.form-control-feedback{width:46px;height:46px;line-height:46px}.form-group-sm .form-control+.form-control-feedback,.input-group-sm+.form-control-feedback,.input-sm+.form-control-feedback{width:30px;height:30px;line-height:30px}.has-success .checkbox,.has-success .checkbox-inline,.has-success .control-label,.has-success .help-block,.has-success .radio,.has-success .radio-inline,.has-success.checkbox label,.has-success.checkbox-inline label,.has-success.radio label,.has-success.radio-inline label{color:#3c763d}.has-success .form-control{border-color:#3c763d;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-success .form-control:focus{border-color:#2b542c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168}.has-success .input-group-addon{color:#3c763d;background-color:#dff0d8;border-color:#3c763d}.has-success .form-control-feedback{color:#3c763d}.has-warning .checkbox,.has-warning .checkbox-inline,.has-warning .control-label,.has-warning .help-block,.has-warning .radio,.has-warning .radio-inline,.has-warning.checkbox label,.has-warning.checkbox-inline label,.has-warning.radio label,.has-warning.radio-inline label{color:#8a6d3b}.has-warning .form-control{border-color:#8a6d3b;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-warning .form-control:focus{border-color:#66512c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b}.has-warning .input-group-addon{color:#8a6d3b;background-color:#fcf8e3;border-color:#8a6d3b}.has-warning .form-control-feedback{color:#8a6d3b}.has-error .checkbox,.has-error .checkbox-inline,.has-error .control-label,.has-error .help-block,.has-error .radio,.has-error .radio-inline,.has-error.checkbox label,.has-error.checkbox-inline label,.has-error.radio label,.has-error.radio-inline label{color:#a94442}.has-error .form-control{border-color:#a94442;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-error .form-control:focus{border-color:#843534;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483}.has-error .input-group-addon{color:#a94442;background-color:#f2dede;border-color:#a94442}.has-error .form-control-feedback{color:#a94442}.has-feedback label~.form-control-feedback{top:25px}.has-feedback label.sr-only~.form-control-feedback{top:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#737373}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-static{display:inline-block}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .form-control,.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .checkbox,.form-inline .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .checkbox label,.form-inline .radio label{padding-left:0}.form-inline .checkbox input[type=checkbox],.form-inline .radio input[type=radio]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .checkbox,.form-horizontal .checkbox-inline,.form-horizontal .radio,.form-horizontal .radio-inline{padding-top:7px;margin-top:0;margin-bottom:0}.form-horizontal .checkbox,.form-horizontal .radio{min-height:27px}.form-horizontal .form-group{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.form-horizontal .control-label{padding-top:7px;margin-bottom:0;text-align:right}}.form-horizontal .has-feedback .form-control-feedback{right:15px}@media (min-width:768px){.form-horizontal .form-group-lg .control-label{padding-top:11px;font-size:18px}}@media (min-width:768px){.form-horizontal .form-group-sm .control-label{padding-top:6px;font-size:12px}}.btn{display:inline-block;padding:6px 12px;margin-bottom:0;font-size:14px;font-weight:400;line-height:1.42857143;text-align:center;white-space:nowrap;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-image:none;border:1px solid transparent;border-radius:4px}.btn.active.focus,.btn.active:focus,.btn.focus,.btn:active.focus,.btn:active:focus,.btn:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn.focus,.btn:focus,.btn:hover{color:#333;text-decoration:none}.btn.active,.btn:active{background-image:none;outline:0;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;filter:alpha(opacity=65);-webkit-box-shadow:none;box-shadow:none;opacity:.65}a.btn.disabled,fieldset[disabled] a.btn{pointer-events:none}.btn-default{color:#333;background-color:#fff;border-color:#ccc}.btn-default.focus,.btn-default:focus{color:#333;background-color:#e6e6e6;border-color:#8c8c8c}.btn-default:hover{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active.focus,.btn-default.active:focus,.btn-default.active:hover,.btn-default:active.focus,.btn-default:active:focus,.btn-default:active:hover,.open>.dropdown-toggle.btn-default.focus,.open>.dropdown-toggle.btn-default:focus,.open>.dropdown-toggle.btn-default:hover{color:#333;background-color:#d4d4d4;border-color:#8c8c8c}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{background-image:none}.btn-default.disabled.focus,.btn-default.disabled:focus,.btn-default.disabled:hover,.btn-default[disabled].focus,.btn-default[disabled]:focus,.btn-default[disabled]:hover,fieldset[disabled] .btn-default.focus,fieldset[disabled] .btn-default:focus,fieldset[disabled] .btn-default:hover{background-color:#fff;border-color:#ccc}.btn-default .badge{color:#fff;background-color:#333}.btn-primary{color:#fff;background-color:#337ab7;border-color:#2e6da4}.btn-primary.focus,.btn-primary:focus{color:#fff;background-color:#286090;border-color:#122b40}.btn-primary:hover{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active.focus,.btn-primary.active:focus,.btn-primary.active:hover,.btn-primary:active.focus,.btn-primary:active:focus,.btn-primary:active:hover,.open>.dropdown-toggle.btn-primary.focus,.open>.dropdown-toggle.btn-primary:focus,.open>.dropdown-toggle.btn-primary:hover{color:#fff;background-color:#204d74;border-color:#122b40}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{background-image:none}.btn-primary.disabled.focus,.btn-primary.disabled:focus,.btn-primary.disabled:hover,.btn-primary[disabled].focus,.btn-primary[disabled]:focus,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary.focus,fieldset[disabled] .btn-primary:focus,fieldset[disabled] .btn-primary:hover{background-color:#337ab7;border-color:#2e6da4}.btn-primary .badge{color:#337ab7;background-color:#fff}.btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.btn-success.focus,.btn-success:focus{color:#fff;background-color:#449d44;border-color:#255625}.btn-success:hover{color:#fff;background-color:#449d44;border-color:#398439}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{color:#fff;background-color:#449d44;border-color:#398439}.btn-success.active.focus,.btn-success.active:focus,.btn-success.active:hover,.btn-success:active.focus,.btn-success:active:focus,.btn-success:active:hover,.open>.dropdown-toggle.btn-success.focus,.open>.dropdown-toggle.btn-success:focus,.open>.dropdown-toggle.btn-success:hover{color:#fff;background-color:#398439;border-color:#255625}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{background-image:none}.btn-success.disabled.focus,.btn-success.disabled:focus,.btn-success.disabled:hover,.btn-success[disabled].focus,.btn-success[disabled]:focus,.btn-success[disabled]:hover,fieldset[disabled] .btn-success.focus,fieldset[disabled] .btn-success:focus,fieldset[disabled] .btn-success:hover{background-color:#5cb85c;border-color:#4cae4c}.btn-success .badge{color:#5cb85c;background-color:#fff}.btn-info{color:#fff;background-color:#5bc0de;border-color:#46b8da}.btn-info.focus,.btn-info:focus{color:#fff;background-color:#31b0d5;border-color:#1b6d85}.btn-info:hover{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active.focus,.btn-info.active:focus,.btn-info.active:hover,.btn-info:active.focus,.btn-info:active:focus,.btn-info:active:hover,.open>.dropdown-toggle.btn-info.focus,.open>.dropdown-toggle.btn-info:focus,.open>.dropdown-toggle.btn-info:hover{color:#fff;background-color:#269abc;border-color:#1b6d85}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{background-image:none}.btn-info.disabled.focus,.btn-info.disabled:focus,.btn-info.disabled:hover,.btn-info[disabled].focus,.btn-info[disabled]:focus,.btn-info[disabled]:hover,fieldset[disabled] .btn-info.focus,fieldset[disabled] .btn-info:focus,fieldset[disabled] .btn-info:hover{background-color:#5bc0de;border-color:#46b8da}.btn-info .badge{color:#5bc0de;background-color:#fff}.btn-warning{color:#fff;background-color:#f0ad4e;border-color:#eea236}.btn-warning.focus,.btn-warning:focus{color:#fff;background-color:#ec971f;border-color:#985f0d}.btn-warning:hover{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active.focus,.btn-warning.active:focus,.btn-warning.active:hover,.btn-warning:active.focus,.btn-warning:active:focus,.btn-warning:active:hover,.open>.dropdown-toggle.btn-warning.focus,.open>.dropdown-toggle.btn-warning:focus,.open>.dropdown-toggle.btn-warning:hover{color:#fff;background-color:#d58512;border-color:#985f0d}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{background-image:none}.btn-warning.disabled.focus,.btn-warning.disabled:focus,.btn-warning.disabled:hover,.btn-warning[disabled].focus,.btn-warning[disabled]:focus,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning.focus,fieldset[disabled] .btn-warning:focus,fieldset[disabled] .btn-warning:hover{background-color:#f0ad4e;border-color:#eea236}.btn-warning .badge{color:#f0ad4e;background-color:#fff}.btn-danger{color:#fff;background-color:#d9534f;border-color:#d43f3a}.btn-danger.focus,.btn-danger:focus{color:#fff;background-color:#c9302c;border-color:#761c19}.btn-danger:hover{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active.focus,.btn-danger.active:focus,.btn-danger.active:hover,.btn-danger:active.focus,.btn-danger:active:focus,.btn-danger:active:hover,.open>.dropdown-toggle.btn-danger.focus,.open>.dropdown-toggle.btn-danger:focus,.open>.dropdown-toggle.btn-danger:hover{color:#fff;background-color:#ac2925;border-color:#761c19}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{background-image:none}.btn-danger.disabled.focus,.btn-danger.disabled:focus,.btn-danger.disabled:hover,.btn-danger[disabled].focus,.btn-danger[disabled]:focus,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger.focus,fieldset[disabled] .btn-danger:focus,fieldset[disabled] .btn-danger:hover{background-color:#d9534f;border-color:#d43f3a}.btn-danger .badge{color:#d9534f;background-color:#fff}.btn-link{font-weight:400;color:#337ab7;border-radius:0}.btn-link,.btn-link.active,.btn-link:active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link,.btn-link:active,.btn-link:focus,.btn-link:hover{border-color:transparent}.btn-link:focus,.btn-link:hover{color:#23527c;text-decoration:underline;background-color:transparent}.btn-link[disabled]:focus,.btn-link[disabled]:hover,fieldset[disabled] .btn-link:focus,fieldset[disabled] .btn-link:hover{color:#777;text-decoration:none}.btn-group-lg>.btn,.btn-lg{padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.btn-group-sm>.btn,.btn-sm{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-group-xs>.btn,.btn-xs{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{opacity:0;-webkit-transition:opacity .15s linear;-o-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}tr.collapse.in{display:table-row}tbody.collapse.in{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition-timing-function:ease;-o-transition-timing-function:ease;transition-timing-function:ease;-webkit-transition-duration:.35s;-o-transition-duration:.35s;transition-duration:.35s;-webkit-transition-property:height,visibility;-o-transition-property:height,visibility;transition-property:height,visibility}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px dashed;border-top:4px solid\9;border-right:4px solid transparent;border-left:4px solid transparent}.dropdown,.dropup{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;font-size:14px;text-align:left;list-style:none;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.15);border-radius:4px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,.175);box-shadow:0 6px 12px rgba(0,0,0,.175)}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.42857143;color:#333;white-space:nowrap}.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{color:#262626;text-decoration:none;background-color:#f5f5f5}.dropdown-menu>.active>a,.dropdown-menu>.active>a:focus,.dropdown-menu>.active>a:hover{color:#fff;text-decoration:none;background-color:#337ab7;outline:0}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{color:#777}.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{text-decoration:none;cursor:not-allowed;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{right:0;left:auto}.dropdown-menu-left{right:auto;left:0}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.42857143;color:#777;white-space:nowrap}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{content:"";border-top:0;border-bottom:4px dashed;border-bottom:4px solid\9}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}@media (min-width:768px){.navbar-right .dropdown-menu{right:0;left:auto}.navbar-right .dropdown-menu-left{right:auto;left:0}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;float:left}.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:2}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar .btn,.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-bottom-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-right:8px;padding-left:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-right:12px;padding-left:12px}.btn-group.open .dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-group.open .dropdown-toggle.btn-link{-webkit-box-shadow:none;box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-left-radius:0;border-top-right-radius:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-top-right-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{display:table-cell;float:none;width:1%}.btn-group-justified>.btn-group .btn{width:100%}.btn-group-justified>.btn-group .dropdown-menu{left:auto}[data-toggle=buttons]>.btn input[type=checkbox],[data-toggle=buttons]>.btn input[type=radio],[data-toggle=buttons]>.btn-group>.btn input[type=checkbox],[data-toggle=buttons]>.btn-group>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*=col-]{float:none;padding-right:0;padding-left:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group .form-control:focus{z-index:3}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:46px;line-height:46px}select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn,textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:30px;line-height:30px}select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn,textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn{height:auto}.input-group .form-control,.input-group-addon,.input-group-btn{display:table-cell}.input-group .form-control:not(:first-child):not(:last-child),.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:6px 12px;font-size:14px;font-weight:400;line-height:1;color:#555;text-align:center;background-color:#eee;border:1px solid #ccc;border-radius:4px}.input-group-addon.input-sm{padding:5px 10px;font-size:12px;border-radius:3px}.input-group-addon.input-lg{padding:10px 16px;font-size:18px;border-radius:6px}.input-group-addon input[type=checkbox],.input-group-addon input[type=radio]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn-group:not(:last-child)>.btn,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:first-child>.btn-group:not(:first-child)>.btn,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:active,.input-group-btn>.btn:focus,.input-group-btn>.btn:hover{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{z-index:2;margin-left:-1px}.nav{padding-left:0;margin-bottom:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:focus,.nav>li>a:hover{text-decoration:none;background-color:#eee}.nav>li.disabled>a{color:#777}.nav>li.disabled>a:focus,.nav>li.disabled>a:hover{color:#777;text-decoration:none;cursor:not-allowed;background-color:transparent}.nav .open>a,.nav .open>a:focus,.nav .open>a:hover{background-color: #000;/* border-color:#337ab7; */}.nav .nav-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>li.active>a,.nav-tabs>li.active>a:focus,.nav-tabs>li.active>a:hover{color:#555;cursor:default;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border-bottom-color:#fff}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:focus,.nav-pills>li.active>a:hover{color:#fff;background-color:#337ab7}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border-bottom-color:#fff}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.navbar{position:relative;min-height:50px;margin-bottom:20px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:4px}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{padding-right:15px;padding-left:15px;overflow-x:visible;-webkit-overflow-scrolling:touch;border-top:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1)}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;-webkit-box-shadow:none;box-shadow:none}.navbar-collapse.collapse{display:block!important;height:auto!important;padding-bottom:0;overflow:visible!important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse{padding-right:0;padding-left:0}}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:340px}@media (max-device-width:480px) and (orientation:landscape){.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:200px}}.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width:768px){.navbar-static-top{border-radius:0}}.navbar-fixed-bottom,.navbar-fixed-top{position:fixed;right:0;left:0;z-index:1030}@media (min-width:768px){.navbar-fixed-bottom,.navbar-fixed-top{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.navbar-brand{float:left;height:50px;padding:15px 15px;font-size:18px;line-height:20px}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-brand>img{display:block}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;padding:9px 10px;margin-top:8px;margin-right:15px;margin-bottom:8px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:7.5px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:20px}@media (max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;-webkit-box-shadow:none;box-shadow:none}.navbar-nav .open .dropdown-menu .dropdown-header,.navbar-nav .open .dropdown-menu>li>a{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:20px}.navbar-nav .open .dropdown-menu>li>a:focus,.navbar-nav .open .dropdown-menu>li>a:hover{background-image:none}}@media (min-width:768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:15px;padding-bottom:15px}}.navbar-form{padding:10px 15px;margin-top:8px;margin-right:-15px;margin-bottom:8px;margin-left:-15px;border-top:1px solid transparent;border-bottom:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1)}@media (min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .form-control-static{display:inline-block}.navbar-form .input-group{display:inline-table;vertical-align:middle}.navbar-form .input-group .form-control,.navbar-form .input-group .input-group-addon,.navbar-form .input-group .input-group-btn{width:auto}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .checkbox,.navbar-form .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.navbar-form .checkbox label,.navbar-form .radio label{padding-left:0}.navbar-form .checkbox input[type=checkbox],.navbar-form .radio input[type=radio]{position:relative;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}.navbar-form .form-group:last-child{margin-bottom:0}}@media (min-width:768px){.navbar-form{width:auto;padding-top:0;padding-bottom:0;margin-right:0;margin-left:0;border:0;-webkit-box-shadow:none;box-shadow:none}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-left-radius:0;border-top-right-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{margin-bottom:0;border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:8px;margin-bottom:8px}.navbar-btn.btn-sm{margin-top:10px;margin-bottom:10px}.navbar-btn.btn-xs{margin-top:14px;margin-bottom:14px}.navbar-text{margin-top:15px;margin-bottom:15px}@media (min-width:768px){.navbar-text{float:left;margin-right:15px;margin-left:15px}}@media (min-width:768px){.navbar-left{float:left!important}.navbar-right{float:right!important;margin-right:-15px}.navbar-right~.navbar-right{margin-right:0}}.navbar-default{background-color:#f8f8f8;border-color:#e7e7e7}.navbar-default .navbar-brand{color:#777}.navbar-default .navbar-brand:focus,.navbar-default .navbar-brand:hover{color:#5e5e5e;background-color:transparent}.navbar-default .navbar-text{color:#777}.navbar-default .navbar-nav>li>a{color:#777}.navbar-default .navbar-nav>li>a:focus,.navbar-default .navbar-nav>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:focus,.navbar-default .navbar-nav>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:focus,.navbar-default .navbar-nav>.disabled>a:hover{color:#ccc;background-color:transparent}.navbar-default .navbar-toggle{border-color:#ddd}.navbar-default .navbar-toggle:focus,.navbar-default .navbar-toggle:hover{background-color:#ddd}.navbar-default .navbar-toggle .icon-bar{background-color:#888}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#e7e7e7}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:focus,.navbar-default .navbar-nav>.open>a:hover{color:#555;background-color:#e7e7e7}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#777}.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#ccc;background-color:transparent}}.navbar-default .navbar-link{color:#777}.navbar-default .navbar-link:hover{color:#333}.navbar-default .btn-link{color:#777}.navbar-default .btn-link:focus,.navbar-default .btn-link:hover{color:#333}.navbar-default .btn-link[disabled]:focus,.navbar-default .btn-link[disabled]:hover,fieldset[disabled] .navbar-default .btn-link:focus,fieldset[disabled] .navbar-default .btn-link:hover{color:#ccc}.navbar-inverse{background-color:#222;border-color:#080808}.navbar-inverse .navbar-brand{color:#9d9d9d}.navbar-inverse .navbar-brand:focus,.navbar-inverse .navbar-brand:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-text{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a:focus,.navbar-inverse .navbar-nav>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:focus,.navbar-inverse .navbar-nav>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:focus,.navbar-inverse .navbar-nav>.disabled>a:hover{color:#444;background-color:transparent}.navbar-inverse .navbar-toggle{border-color:#333}.navbar-inverse .navbar-toggle:focus,.navbar-inverse .navbar-toggle:hover{background-color:#333}.navbar-inverse .navbar-toggle .icon-bar{background-color:#fff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#101010}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:focus,.navbar-inverse .navbar-nav>.open>a:hover{color:#fff;background-color:#080808}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#444;background-color:transparent}}.navbar-inverse .navbar-link{color:#9d9d9d}.navbar-inverse .navbar-link:hover{color:#fff}.navbar-inverse .btn-link{color:#9d9d9d}.navbar-inverse .btn-link:focus,.navbar-inverse .btn-link:hover{color:#fff}.navbar-inverse .btn-link[disabled]:focus,.navbar-inverse .btn-link[disabled]:hover,fieldset[disabled] .navbar-inverse .btn-link:focus,fieldset[disabled] .navbar-inverse .btn-link:hover{color:#444}.breadcrumb{padding:8px 15px;margin-bottom:20px;list-style:none;background-color:#f5f5f5;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{padding:0 5px;color:#ccc;content:"/\00a0"}.breadcrumb>.active{color:#777}.pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:6px 12px;margin-left:-1px;line-height:1.42857143;color:#337ab7;text-decoration:none;background-color:#fff;border:1px solid #ddd}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-top-left-radius:4px;border-bottom-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-top-right-radius:4px;border-bottom-right-radius:4px}.pagination>li>a:focus,.pagination>li>a:hover,.pagination>li>span:focus,.pagination>li>span:hover{z-index:2;color:#23527c;background-color:#eee;border-color:#ddd}.pagination>.active>a,.pagination>.active>a:focus,.pagination>.active>a:hover,.pagination>.active>span,.pagination>.active>span:focus,.pagination>.active>span:hover{z-index:3;color:#fff;cursor:default;background-color:#337ab7;border-color:#337ab7}.pagination>.disabled>a,.pagination>.disabled>a:focus,.pagination>.disabled>a:hover,.pagination>.disabled>span,.pagination>.disabled>span:focus,.pagination>.disabled>span:hover{color:#777;cursor:not-allowed;background-color:#fff;border-color:#ddd}.pagination-lg>li>a,.pagination-lg>li>span{padding:10px 16px;font-size:18px;line-height:1.3333333}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-top-left-radius:6px;border-bottom-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-top-right-radius:6px;border-bottom-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:12px;line-height:1.5}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-top-left-radius:3px;border-bottom-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-top-right-radius:3px;border-bottom-right-radius:3px}.pager{padding-left:0;margin:20px 0;text-align:center;list-style:none}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;border-radius:15px}.pager li>a:focus,.pager li>a:hover{text-decoration:none;background-color:#eee}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:focus,.pager .disabled>a:hover,.pager .disabled>span{color:#777;cursor:not-allowed;background-color:#fff}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}a.label:focus,a.label:hover{color:#fff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#777}.label-default[href]:focus,.label-default[href]:hover{background-color:#5e5e5e}.label-primary{background-color:#337ab7}.label-primary[href]:focus,.label-primary[href]:hover{background-color:#286090}.label-success{background-color:#5cb85c}.label-success[href]:focus,.label-success[href]:hover{background-color:#449d44}.label-info{background-color:#5bc0de}.label-info[href]:focus,.label-info[href]:hover{background-color:#31b0d5}.label-warning{background-color:#f0ad4e}.label-warning[href]:focus,.label-warning[href]:hover{background-color:#ec971f}.label-danger{background-color:#d9534f}.label-danger[href]:focus,.label-danger[href]:hover{background-color:#c9302c}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#777;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-group-xs>.btn .badge,.btn-xs .badge{top:0;padding:1px 5px}a.badge:focus,a.badge:hover{color:#fff;text-decoration:none;cursor:pointer}.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#337ab7;background-color:#fff}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding-top:30px;padding-bottom:30px;margin-bottom:30px;color:inherit;background-color:#eee}.jumbotron .h1,.jumbotron h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:21px;font-weight:200}.jumbotron>hr{border-top-color:#d5d5d5}.container .jumbotron,.container-fluid .jumbotron{padding-right:15px;padding-left:15px;border-radius:6px}.jumbotron .container{max-width:100%}@media screen and (min-width:768px){.jumbotron{padding-top:48px;padding-bottom:48px}.container .jumbotron,.container-fluid .jumbotron{padding-right:60px;padding-left:60px}.jumbotron .h1,.jumbotron h1{font-size:63px}}.thumbnail{display:block;padding:4px;margin-bottom:20px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:border .2s ease-in-out;-o-transition:border .2s ease-in-out;transition:border .2s ease-in-out}.thumbnail a>img,.thumbnail>img{margin-right:auto;margin-left:auto}a.thumbnail.active,a.thumbnail:focus,a.thumbnail:hover{border-color:#337ab7}.thumbnail .caption{padding:9px;color:#333}.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:700}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable,.alert-dismissible{padding-right:35px}.alert-dismissable .close,.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.alert-success hr{border-top-color:#c9e2b3}.alert-success .alert-link{color:#2b542c}.alert-info{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.alert-info hr{border-top-color:#a6e1ec}.alert-info .alert-link{color:#245269}.alert-warning{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.alert-warning hr{border-top-color:#f7e1b5}.alert-warning .alert-link{color:#66512c}.alert-danger{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.alert-danger hr{border-top-color:#e4b9c0}.alert-danger .alert-link{color:#843534}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#f5f5f5;border-radius:4px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.1);box-shadow:inset 0 1px 2px rgba(0,0,0,.1)}.progress-bar{float:left;width:0;height:100%;font-size:12px;line-height:20px;color:#fff;text-align:center;background-color:#337ab7;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);-webkit-transition:width .6s ease;-o-transition:width .6s ease;transition:width .6s ease}.progress-bar-striped,.progress-striped .progress-bar{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);-webkit-background-size:40px 40px;background-size:40px 40px}.progress-bar.active,.progress.active .progress-bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-success{background-color:#5cb85c}.progress-striped .progress-bar-success{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-info{background-color:#5bc0de}.progress-striped .progress-bar-info{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-warning{background-color:#f0ad4e}.progress-striped .progress-bar-warning{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-danger{background-color:#d9534f}.progress-striped .progress-bar-danger{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.media{margin-top:15px}.media:first-child{margin-top:0}.media,.media-body{overflow:hidden;zoom:1}.media-body{width:10000px}.media-object{display:block}.media-object.img-thumbnail{max-width:none}.media-right,.media>.pull-right{padding-left:10px}.media-left,.media>.pull-left{padding-right:10px}.media-body,.media-left,.media-right{display:table-cell;vertical-align:top}.media-middle{vertical-align:middle}.media-bottom{vertical-align:bottom}.media-heading{margin-top:0;margin-bottom:5px}.media-list{padding-left:0;list-style:none}.list-group{padding-left:0;margin-bottom:20px}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#fff;border:1px solid #ddd}.list-group-item:first-child{border-top-left-radius:4px;border-top-right-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}a.list-group-item,button.list-group-item{color:#555}a.list-group-item .list-group-item-heading,button.list-group-item .list-group-item-heading{color:#333}a.list-group-item:focus,a.list-group-item:hover,button.list-group-item:focus,button.list-group-item:hover{color:#555;text-decoration:none;background-color:#f5f5f5}button.list-group-item{width:100%;text-align:left}.list-group-item.disabled,.list-group-item.disabled:focus,.list-group-item.disabled:hover{color:#777;cursor:not-allowed;background-color:#eee}.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading{color:inherit}.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text{color:#777}.list-group-item.active,.list-group-item.active:focus,.list-group-item.active:hover{z-index:2;color:#fff;background-color:#337ab7;border-color:#337ab7}.list-group-item.active .list-group-item-heading,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading>small{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:focus .list-group-item-text,.list-group-item.active:hover .list-group-item-text{color:#c7ddef}.list-group-item-success{color:#3c763d;background-color:#dff0d8}a.list-group-item-success,button.list-group-item-success{color:#3c763d}a.list-group-item-success .list-group-item-heading,button.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:focus,a.list-group-item-success:hover,button.list-group-item-success:focus,button.list-group-item-success:hover{color:#3c763d;background-color:#d0e9c6}a.list-group-item-success.active,a.list-group-item-success.active:focus,a.list-group-item-success.active:hover,button.list-group-item-success.active,button.list-group-item-success.active:focus,button.list-group-item-success.active:hover{color:#fff;background-color:#3c763d;border-color:#3c763d}.list-group-item-info{color:#31708f;background-color:#d9edf7}a.list-group-item-info,button.list-group-item-info{color:#31708f}a.list-group-item-info .list-group-item-heading,button.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:focus,a.list-group-item-info:hover,button.list-group-item-info:focus,button.list-group-item-info:hover{color:#31708f;background-color:#c4e3f3}a.list-group-item-info.active,a.list-group-item-info.active:focus,a.list-group-item-info.active:hover,button.list-group-item-info.active,button.list-group-item-info.active:focus,button.list-group-item-info.active:hover{color:#fff;background-color:#31708f;border-color:#31708f}.list-group-item-warning{color:#8a6d3b;background-color:#fcf8e3}a.list-group-item-warning,button.list-group-item-warning{color:#8a6d3b}a.list-group-item-warning .list-group-item-heading,button.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:focus,a.list-group-item-warning:hover,button.list-group-item-warning:focus,button.list-group-item-warning:hover{color:#8a6d3b;background-color:#faf2cc}a.list-group-item-warning.active,a.list-group-item-warning.active:focus,a.list-group-item-warning.active:hover,button.list-group-item-warning.active,button.list-group-item-warning.active:focus,button.list-group-item-warning.active:hover{color:#fff;background-color:#8a6d3b;border-color:#8a6d3b}.list-group-item-danger{color:#a94442;background-color:#f2dede}a.list-group-item-danger,button.list-group-item-danger{color:#a94442}a.list-group-item-danger .list-group-item-heading,button.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:focus,a.list-group-item-danger:hover,button.list-group-item-danger:focus,button.list-group-item-danger:hover{color:#a94442;background-color:#ebcccc}a.list-group-item-danger.active,a.list-group-item-danger.active:focus,a.list-group-item-danger.active:hover,button.list-group-item-danger.active,button.list-group-item-danger.active:focus,button.list-group-item-danger.active:hover{color:#fff;background-color:#a94442;border-color:#a94442}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:20px;background-color:#fff;border:1px solid transparent;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,.05);box-shadow:0 1px 1px rgba(0,0,0,.05)}.panel-body{padding:15px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-left-radius:3px;border-top-right-radius:3px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:16px;color:inherit}.panel-title>.small,.panel-title>.small>a,.panel-title>a,.panel-title>small,.panel-title>small>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#f5f5f5;border-top:1px solid #ddd;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group,.panel>.panel-collapse>.list-group{margin-bottom:0}.panel>.list-group .list-group-item,.panel>.panel-collapse>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child,.panel>.panel-collapse>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-left-radius:3px;border-top-right-radius:3px}.panel>.list-group:last-child .list-group-item:last-child,.panel>.panel-collapse>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.panel-heading+.panel-collapse>.list-group .list-group-item:first-child{border-top-left-radius:0;border-top-right-radius:0}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.list-group+.panel-footer{border-top-width:0}.panel>.panel-collapse>.table,.panel>.table,.panel>.table-responsive>.table{margin-bottom:0}.panel>.panel-collapse>.table caption,.panel>.table caption,.panel>.table-responsive>.table caption{padding-right:15px;padding-left:15px}.panel>.table-responsive:first-child>.table:first-child,.panel>.table:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child,.panel>.table:first-child>thead:first-child>tr:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.panel>.table-responsive:last-child>.table:last-child,.panel>.table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive,.panel>.table+.panel-body,.panel>.table-responsive+.panel-body{border-top:1px solid #ddd}.panel>.table>tbody:first-child>tr:first-child td,.panel>.table>tbody:first-child>tr:first-child th{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{margin-bottom:0;border:0}.panel-group{margin-bottom:20px}.panel-group .panel{margin-bottom:0;border-radius:4px}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse>.list-group,.panel-group .panel-heading+.panel-collapse>.panel-body{border-top:1px solid #ddd}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #ddd}.panel-default{border-color:#ddd}.panel-default>.panel-heading{color:#333;background-color:#f5f5f5;border-color:#ddd}.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ddd}.panel-default>.panel-heading .badge{color:#f5f5f5;background-color:#333}.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ddd}.panel-primary{border-color:#337ab7}.panel-primary>.panel-heading{color:#fff;background-color:#337ab7;border-color:#337ab7}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:#337ab7}.panel-primary>.panel-heading .badge{color:#337ab7;background-color:#fff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#337ab7}.panel-success{border-color:#d6e9c6}.panel-success>.panel-heading{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:#d6e9c6}.panel-success>.panel-heading .badge{color:#dff0d8;background-color:#3c763d}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#d6e9c6}.panel-info{border-color:#bce8f1}.panel-info>.panel-heading{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:#bce8f1}.panel-info>.panel-heading .badge{color:#d9edf7;background-color:#31708f}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#bce8f1}.panel-warning{border-color:#faebcc}.panel-warning>.panel-heading{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:#faebcc}.panel-warning>.panel-heading .badge{color:#fcf8e3;background-color:#8a6d3b}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#faebcc}.panel-danger{border-color:#ebccd1}.panel-danger>.panel-heading{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ebccd1}.panel-danger>.panel-heading .badge{color:#f2dede;background-color:#a94442}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ebccd1}.embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}.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-16by9{padding-bottom:56.25%}.embed-responsive-4by3{padding-bottom:75%}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);box-shadow:inset 0 1px 1px rgba(0,0,0,.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,.15)}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:21px;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;filter:alpha(opacity=20);opacity:.2}.close:focus,.close:hover{color:#000;text-decoration:none;cursor:pointer;filter:alpha(opacity=50);opacity:.5}button.close{-webkit-appearance:none;padding:0;cursor:pointer;background:0 0;border:0}.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;display:none;overflow:hidden;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transition:-webkit-transform .3s ease-out;-o-transition:-o-transform .3s ease-out;transition:transform .3s ease-out;-webkit-transform:translate(0,-25%);-ms-transform:translate(0,-25%);-o-transform:translate(0,-25%);transform:translate(0,-25%)}.modal.in .modal-dialog{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);-o-transform:translate(0,0);transform:translate(0,0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #999;border:1px solid rgba(0,0,0,.2);border-radius:6px;outline:0;-webkit-box-shadow:0 3px 9px rgba(0,0,0,.5);box-shadow:0 3px 9px rgba(0,0,0,.5)}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{filter:alpha(opacity=0);opacity:0}.modal-backdrop.in{filter:alpha(opacity=50);opacity:.5}.modal-header{padding:15px;border-bottom:1px solid #e5e5e5}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:15px}.modal-footer{padding:15px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{-webkit-box-shadow:0 5px 15px rgba(0,0,0,.5);box-shadow:0 5px 15px rgba(0,0,0,.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1070;display:block;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:12px;font-style:normal;font-weight:400;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;filter:alpha(opacity=0);opacity:0;line-break:auto}.tooltip.in{filter:alpha(opacity=90);opacity:.9}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;background-color:#000;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-left .tooltip-arrow{right:5px;bottom:0;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-right .tooltip-arrow{bottom:0;left:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-left .tooltip-arrow{top:0;right:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-right .tooltip-arrow{top:0;left:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.popover{position:absolute;top:0;left:0;z-index:1060;display:none;max-width:276px;padding:1px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;font-style:normal;font-weight:400;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.2);border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,.2);box-shadow:0 5px 10px rgba(0,0,0,.2);line-break:auto}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{padding:8px 14px;margin:0;font-size:14px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow{border-width:11px}.popover>.arrow:after{content:"";border-width:10px}.popover.top>.arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#999;border-top-color:rgba(0,0,0,.25);border-bottom-width:0}.popover.top>.arrow:after{bottom:1px;margin-left:-10px;content:" ";border-top-color:#fff;border-bottom-width:0}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#999;border-right-color:rgba(0,0,0,.25);border-left-width:0}.popover.right>.arrow:after{bottom:-10px;left:1px;content:" ";border-right-color:#fff;border-left-width:0}.popover.bottom>.arrow{top:-11px;left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,.25)}.popover.bottom>.arrow:after{top:1px;margin-left:-10px;content:" ";border-top-width:0;border-bottom-color:#fff}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999;border-left-color:rgba(0,0,0,.25)}.popover.left>.arrow:after{right:1px;bottom:-10px;content:" ";border-right-width:0;border-left-color:#fff}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;-webkit-transition:.6s ease-in-out left;-o-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel-inner>.item>a>img,.carousel-inner>.item>img{line-height:1}@media all and (transform-3d),(-webkit-transform-3d){.carousel-inner>.item{-webkit-transition:-webkit-transform .6s ease-in-out;-o-transition:-o-transform .6s ease-in-out;transition:transform .6s ease-in-out;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000px;perspective:1000px}.carousel-inner>.item.active.right,.carousel-inner>.item.next{left:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}.carousel-inner>.item.active.left,.carousel-inner>.item.prev{left:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}.carousel-inner>.item.active,.carousel-inner>.item.next.left,.carousel-inner>.item.prev.right{left:0;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;bottom:0;left:0;width:15%;font-size:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6);background-color:rgba(0,0,0,0);filter:alpha(opacity=50);opacity:.5}.carousel-control.left{background-image:-webkit-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.5)),to(rgba(0,0,0,.0001)));background-image:linear-gradient(to right,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);background-repeat:repeat-x}.carousel-control.right{right:0;left:auto;background-image:-webkit-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.0001)),to(rgba(0,0,0,.5)));background-image:linear-gradient(to right,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);background-repeat:repeat-x}.carousel-control:focus,.carousel-control:hover{color:#fff;text-decoration:none;filter:alpha(opacity=90);outline:0;opacity:.9}.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{position:absolute;top:50%;z-index:5;display:inline-block;margin-top:-10px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{left:50%;margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{right:50%;margin-right:-10px}.carousel-control .icon-next,.carousel-control .icon-prev{width:20px;height:20px;font-family:serif;line-height:1}.carousel-control .icon-prev:before{content:'\2039'}.carousel-control .icon-next:before{content:'\203a'}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;padding-left:0;margin-left:-30%;text-align:center;list-style:none}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;cursor:pointer;background-color:#000\9;background-color:rgba(0,0,0,0);border:1px solid #fff;border-radius:10px}.carousel-indicators .active{width:12px;height:12px;margin:0;background-color:#fff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width:768px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{width:30px;height:30px;margin-top:-10px;font-size:30px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{margin-right:-10px}.carousel-caption{right:20%;left:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.btn-group-vertical>.btn-group:after,.btn-group-vertical>.btn-group:before,.btn-toolbar:after,.btn-toolbar:before,.clearfix:after,.clearfix:before,.container-fluid:after,.container-fluid:before,.container:after,.container:before,.dl-horizontal dd:after,.dl-horizontal dd:before,.form-horizontal .form-group:after,.form-horizontal .form-group:before,.modal-footer:after,.modal-footer:before,.modal-header:after,.modal-header:before,.nav:after,.nav:before,.navbar-collapse:after,.navbar-collapse:before,.navbar-header:after,.navbar-header:before,.navbar:after,.navbar:before,.pager:after,.pager:before,.panel-body:after,.panel-body:before,.row:after,.row:before{display:table;content:" "}.btn-group-vertical>.btn-group:after,.btn-toolbar:after,.clearfix:after,.container-fluid:after,.container:after,.dl-horizontal dd:after,.form-horizontal .form-group:after,.modal-footer:after,.modal-header:after,.nav:after,.navbar-collapse:after,.navbar-header:after,.navbar:after,.pager:after,.panel-body:after,.row:after{clear:both}.center-block{display:block;margin-right:auto;margin-left:auto}.pull-right{float:right!important}.pull-left{float:left!important}.hide{display:none!important}.show{display:block!important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none!important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-lg,.visible-md,.visible-sm,.visible-xs{display:none!important}.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block{display:none!important}@media (max-width:767px){.visible-xs{display:block!important}table.visible-xs{display:table!important}tr.visible-xs{display:table-row!important}td.visible-xs,th.visible-xs{display:table-cell!important}}@media (max-width:767px){.visible-xs-block{display:block!important}}@media (max-width:767px){.visible-xs-inline{display:inline!important}}@media (max-width:767px){.visible-xs-inline-block{display:inline-block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block!important}table.visible-sm{display:table!important}tr.visible-sm{display:table-row!important}td.visible-sm,th.visible-sm{display:table-cell!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-block{display:block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline{display:inline!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline-block{display:inline-block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block!important}table.visible-md{display:table!important}tr.visible-md{display:table-row!important}td.visible-md,th.visible-md{display:table-cell!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-block{display:block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline{display:inline!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline-block{display:inline-block!important}}@media (min-width:1200px){.visible-lg{display:block!important}table.visible-lg{display:table!important}tr.visible-lg{display:table-row!important}td.visible-lg,th.visible-lg{display:table-cell!important}}@media (min-width:1200px){.visible-lg-block{display:block!important}}@media (min-width:1200px){.visible-lg-inline{display:inline!important}}@media (min-width:1200px){.visible-lg-inline-block{display:inline-block!important}}@media (max-width:767px){.hidden-xs{display:none!important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none!important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none!important}}@media (min-width:1200px){.hidden-lg{display:none!important}}.visible-print{display:none!important}@media print{.visible-print{display:block!important}table.visible-print{display:table!important}tr.visible-print{display:table-row!important}td.visible-print,th.visible-print{display:table-cell!important}}.visible-print-block{display:none!important}@media print{.visible-print-block{display:block!important}}.visible-print-inline{display:none!important}@media print{.visible-print-inline{display:inline!important}}.visible-print-inline-block{display:none!important}@media print{.visible-print-inline-block{display:inline-block!important}}@media print{.hidden-print{display:none!important}} +/*# sourceMappingURL=bootstrap.min.css.map */ \ No newline at end of file diff --git a/kfet/static/kfet/css/history.css b/kfet/static/kfet/css/history.css new file mode 100644 index 00000000..976f5782 --- /dev/null +++ b/kfet/static/kfet/css/history.css @@ -0,0 +1,106 @@ +#history { + font-family:'Roboto Mono'; +} + +#history span { + display:inline-block; +} + +#history .day { + height:40px; + line-height:40px; + background-color:#c8102e; + color:#fff; + padding-left:20px; + font-size:16px; + font-weight:bold; +} + +#history .opegroup { + height:30px; + line-height:30px; + background-color:rgba(200,16,46,0.85); + color:#fff; + font-weight:bold; + padding-left:20px; + overflow:auto; +} + +#history .opegroup .time { + width:70px; +} + +#history .opegroup .trigramme { + width:55px; + text-align:right; +} + +#history .opegroup .amount { + text-align:right; + width:90px; +} + +#history .opegroup .valid_by { + padding-left:20px +} + +#history .opegroup .comment { + padding-left:20px; +} + +#history .ope { + position:relative; + height:25px; + line-height:24px; + font-size:12px; + padding-left:15px; + overflow:auto; +} + +#history .ope .amount { + width:50px; + text-align:right; +} + +#history .ope .infos1 { + width:80px; + text-align:right; +} + +#history .ope .infos2 { + padding-left:15px; +} + +#history .ope .addcost { + padding-left:20px; +} + +#history .ope .canceled { + padding-left:20px; +} + +#history div.ope.ui-selected, #history div.ope.ui-selecting { + background-color:rgba(200,16,46,0.6); + color:#FFF; +} + +#history .ope.canceled, #history .transfer.canceled { + color:#444; +} + +#history .ope.canceled::before, #history.transfer.canceled::before { + position: absolute; + content: ' '; + width:100%; + left:0; + top: 12px; + border-top: 1px solid rgba(200,16,46,0.5); +} + +#history .transfer .amount { + width:80px; +} + +#history .transfer .from_acc { + padding-left:10px; +} diff --git a/kfet/static/kfet/css/index.css b/kfet/static/kfet/css/index.css new file mode 100644 index 00000000..c6ee9ff6 --- /dev/null +++ b/kfet/static/kfet/css/index.css @@ -0,0 +1,265 @@ +@import url("nav.css"); +@import url("kpsul.css"); +@import url("jconfirm-kfet.css"); +@import url("history.css"); + +body { + margin-top:50px; + font-family:Roboto; + background:#ddd; +} + +h1,h2,h3,h4,h5,h6 { + font-family:Oswald; +} + +a { + color:#333; +} + +a:focus, a:hover { + color:#C8102E; +} + +:focus { + outline:none; +} + +textarea { + font-family:'Roboto Mono'; + border-radius:0 !important; +} + +.table { + margin-bottom:0; +} + +.table { + width:100%; +} + +.table td { + vertical-align:middle !important; +} + +.table td.no-padding { + padding:0; +} + +.table thead { + background:#c8102e; + color:#fff; + font-weight:bold; + font-size:16px; +} + +.table thead td { + padding:8px !important; +} + +.table tr.section { + background:rgba(200,16,46,0.9); + color:#fff; + font-weight:bold; +} + +.table tr.section td { + border-top:0; + font-size:16px; + padding:8px 30px; +} + +.btn, .btn-lg, .btn-group-lg>.btn { + border-radius:0; +} + +.btn-primary { + font-family:Oswald; + background-color:rgba(200,16,46,0.9); + color:#FFF; + border:0; +} + +.btn-primary:hover, .btn-primary.focus, .btn-primary:focus { + background-color:#000; + color:#FFF; +} + +.row-page-header { + background-color:rgba(200,16,46,1); + color:#FFF; + border-bottom:3px solid #000; +} + +.page-header { + border:0; + padding:0; + margin:15px 20px; + text-transform:uppercase; + font-weight:bold; +} + +.col-content-left, .col-content-right { + padding:0; +} + +.content-left-top { + background:#fff; + padding:10px 30px; +} + +.content-left .buttons { + background:#fff; +} + +.content-left .buttons .btn { + display:block; +} + +.content-left-top.frozen-account { + background:#000FBA; + color:#fff; +} + +.content-left .block { + padding-top:25px; +} + +.content-left .block .line { + font-size:16px; + line-height:30px; +} + +.content-left .line.line-big { + font-family:Oswald; + font-size:60px; + font-weight:bold; + text-align:center; +} + +.content-left .line.line-bigsub { + font-size:25px; + font-weight:bold; + text-align:center; +} + +.content-left .line.balance { + font-size:45px; + text-align:center; +} + +.content-right { + margin:0 15px; +} + +.content-right-block { + padding-bottom:5px; + position:relative; +} + +.content-right-block:last-child { + padding-bottom:15px; +} + +.content-right-block > div:not(.buttons-title) { + background:#fff; +} + +.content-right-block .buttons-title { + position:absolute; + top:8px; + right:20px; +} + +.content-right-block > div.row { + margin:0; +} + +.content-right-block h2 { + margin:20px 20px 15px; + padding-bottom:5px; + border-bottom:3px solid #c8102e; + font-size:40px; +} + +.content-right-block h3 { + border-bottom: 1px solid #c8102e; + margin: 20px 15px 15px; + padding-bottom: 10px; + padding-left: 20px; + font-size:25px; +} + +/* + * Pages formulaires seuls + */ + +.form-only .content-form { + margin:15px; + + background:#fff; + + padding:15px; +} + +.form-only .account_create #id_trigramme { + display:block; + width:200px; + height:80px; + margin:0 auto 15px; + border:1px solid #ccc; + + font-size:70px; + + text-align:center; + text-transform:uppercase; +} + +/* + * Specific account create + */ + +.highlight_autocomplete { + font-weight:bold; + text-decoration:underline; +} + +#search_autocomplete { + margin-bottom:15px; +} + +#search_results { + top:-15px !important; + left:0 !important; + z-index:100; +} + +#search_results ul { + list-style-type:none; + padding:0; + background:rgba(255,255,255,0.9); +} + +#search_results ul li.user_category { + font-weight:bold; + background:#c8102e; + color:#fff; +} + +#search_results ul li a { + display:block; + padding:5px 20px; + height:100%; + width:100%; +} + +#search_results ul li a:hover { + background:rgba(200,16,46,0.9); + color:#fff; + text-decoration:none; +} + +#search_results ul li span.text { + display:block; + padding:5px 20px; +} diff --git a/kfet/static/kfet/css/jconfirm-kfet.css b/kfet/static/kfet/css/jconfirm-kfet.css new file mode 100644 index 00000000..86c7c42b --- /dev/null +++ b/kfet/static/kfet/css/jconfirm-kfet.css @@ -0,0 +1,66 @@ +.jconfirm-bg.seen { + opacity:1 !important; +} + +.jconfirm .jconfirm-box { + padding:0; + border-radius:0 !important; + font-family:"Roboto Mono"; +} + +.jconfirm .jconfirm-box div.title-c .title { + display: block; + + padding:0 15px; + height:40px; + line-height:40px; + + font-size:20px; + font-weight:bold; + + color:#FFF; + background-color:#C8102E; +} + +.jconfirm .jconfirm-box .content-pane { + margin:0 !important; +} + +.jconfirm .jconfirm-box .content { + border-bottom:1px solid #ddd; +} + +.jconfirm .jconfirm-box input { + width:100%; + height:70px; + + border:0; + + font-size:40px; + + text-align:center; +} + +.jconfirm .jconfirm-box .buttons { + margin-top:-5px; /* j'arrive pas à voir pk y'a un espace au dessus sinon... */ + padding:0; + height:40px; +} + +.jconfirm .jconfirm-box .buttons button { + height:100%; + margin:0; + margin:0 !important; +} + +.jconfirm .jconfirm-box .buttons button:first-child:focus, +.jconfirm .jconfirm-box .buttons button:first-child:hover { + color:#FFF !important; + background:#009011 !important; +} + +.jconfirm .jconfirm-box .buttons button:nth-child(2):focus, +.jconfirm .jconfirm-box .buttons button:nth-child(2):hover { + color:#FFF !important; + background:#C8102E !important; +} diff --git a/kfet/static/kfet/css/jquery-confirm.css b/kfet/static/kfet/css/jquery-confirm.css new file mode 100644 index 00000000..e11388ef --- /dev/null +++ b/kfet/static/kfet/css/jquery-confirm.css @@ -0,0 +1,519 @@ +/*! + * jquery-confirm v2.5.1 (http://craftpip.github.io/jquery-confirm/) + * Author: boniface pereira + * Website: www.craftpip.com + * Contact: hey@craftpip.com + * + * Copyright 2013-2016 jquery-confirm + * Licensed under MIT (https://github.com/craftpip/jquery-confirm/blob/master/LICENSE) + */ +@-webkit-keyframes jconfirm-rotate { + from { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + to { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); + } +} +@keyframes jconfirm-rotate { + from { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + to { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); + } +} +body.jconfirm-noscroll { + overflow: hidden !important; +} +.jconfirm { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + z-index: 99999999; + font-family: inherit; + overflow: hidden; +} +.jconfirm .jconfirm-bg { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + opacity: 0; + -webkit-transition: all .4s; + transition: all .4s; +} +.jconfirm .jconfirm-bg.seen { + opacity: 1; +} +.jconfirm .jconfirm-scrollpane { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + overflow-y: auto; + -webkit-perspective: 500px; + perspective: 500px; + -webkit-perspective-origin: center; + perspective-origin: center; +} +.jconfirm .jconfirm-box { + background: white; + border-radius: 4px; + position: relative; + outline: none; + padding: 15px 15px 0; +} +.jconfirm .jconfirm-box div.closeIcon { + height: 20px; + width: 20px; + position: absolute; + top: 5px; + right: 5px; + cursor: pointer; + opacity: .6; + text-align: center; + -webkit-transition: opacity 0.1s ease-in; + transition: opacity 0.1s ease-in; + display: none; + font-size: 27px; + line-height: 14px; +} +.jconfirm .jconfirm-box div.closeIcon .fa { + font-size: 16px; +} +.jconfirm .jconfirm-box div.closeIcon .glyphicon { + font-size: 16px; +} +.jconfirm .jconfirm-box div.closeIcon .zmdi { + font-size: 16px; +} +.jconfirm .jconfirm-box div.closeIcon:hover { + opacity: 1; +} +.jconfirm .jconfirm-box div.title-c { + display: block; + font-size: 22px; + line-height: 20px; +} +.jconfirm .jconfirm-box div.title-c .icon-c { + font-size: inherit; + padding-bottom: 15px; + display: inline-block; + margin-right: 8px; + vertical-align: middle; +} +.jconfirm .jconfirm-box div.title-c .icon-c i { + vertical-align: middle; +} +.jconfirm .jconfirm-box div.title-c .icon-c:empty { + display: none; +} +.jconfirm .jconfirm-box div.title-c .title { + font-size: inherit; + font-family: inherit; + display: inline-block; + vertical-align: middle; + padding-bottom: 15px; +} +.jconfirm .jconfirm-box div.title-c .title:empty { + display: none; +} +.jconfirm .jconfirm-box div.content-pane { + margin-bottom: 15px; + height: auto; + -webkit-transition: height 0.4s ease-in; + transition: height 0.4s ease-in; + display: inline-block; + width: 100%; + position: relative; +} +.jconfirm .jconfirm-box div.content-pane .content { + position: absolute; + top: 0; + left: 0; + -webkit-transition: all 0.2s ease-in; + transition: all 0.2s ease-in; + right: 0; +} +.jconfirm .jconfirm-box div.content-pane .content img { + width: 100%; + height: auto; +} +.jconfirm .jconfirm-box div.content-pane .content:empty { + display: none; +} +.jconfirm .jconfirm-box div.content-pane .content:empty.loading { + height: 40px; + position: relative; + opacity: 0.6; + display: block; +} +.jconfirm .jconfirm-box div.content-pane .content:empty.loading:before { + content: ''; + height: 20px; + width: 20px; + border: solid 2px transparent; + position: absolute; + left: 50%; + margin-left: -10px; + border-radius: 50%; + -webkit-animation: jconfirm-rotate 1s infinite linear; + animation: jconfirm-rotate 1s infinite linear; + border-bottom-color: #aaa; + top: 50%; + margin-top: -10px; +} +.jconfirm .jconfirm-box div.content-pane .content:empty.loading:after { + content: ''; + position: absolute; + left: 50%; + margin-left: -15px; +} +.jconfirm .jconfirm-box .buttons { + padding-bottom: 15px; +} +.jconfirm .jconfirm-box .buttons button + button { + margin-left: 5px; +} +.jconfirm .jquery-clear { + clear: both; +} +.jconfirm.rtl { + direction: rtl; +} +.jconfirm.rtl div.closeIcon { + left: 12px; + right: auto; +} +.jconfirm.jconfirm-white .jconfirm-bg { + background-color: rgba(0, 0, 0, 0.2); +} +.jconfirm.jconfirm-white .jconfirm-box { + box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2); + border-radius: 5px; +} +.jconfirm.jconfirm-white .jconfirm-box .buttons { + float: right; +} +.jconfirm.jconfirm-white .jconfirm-box .buttons button { + border: none; + background-image: none; + text-transform: uppercase; + font-size: 14px; + font-weight: bold; + text-shadow: none; + -webkit-transition: background .1s; + transition: background .1s; + color: white; +} +.jconfirm.jconfirm-white .jconfirm-box .buttons button.btn-default { + box-shadow: none; + color: #333; +} +.jconfirm.jconfirm-white .jconfirm-box .buttons button.btn-default:hover { + background: #ddd; +} +.jconfirm.jconfirm-black .jconfirm-bg { + background-color: rgba(0, 0, 0, 0.5); +} +.jconfirm.jconfirm-black .jconfirm-box { + box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2); + background: #444; + border-radius: 5px; + color: white; +} +.jconfirm.jconfirm-black .jconfirm-box .buttons { + float: right; +} +.jconfirm.jconfirm-black .jconfirm-box .buttons button { + border: none; + background-image: none; + text-transform: uppercase; + font-size: 14px; + font-weight: bold; + text-shadow: none; + -webkit-transition: background .1s; + transition: background .1s; + color: white; +} +.jconfirm.jconfirm-black .jconfirm-box .buttons button.btn-default { + box-shadow: none; + color: #fff; + background: none; +} +.jconfirm.jconfirm-black .jconfirm-box .buttons button.btn-default:hover { + background: #666; +} +.jconfirm .jconfirm-box.hilight { + -webkit-animation: hilight 0.82s cubic-bezier(0.36, 0.07, 0.19, 0.97) both; + animation: hilight 0.82s cubic-bezier(0.36, 0.07, 0.19, 0.97) both; + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); +} +@-webkit-keyframes hilight { + 10%, + 90% { + -webkit-transform: translate3d(-2px, 0, 0); + transform: translate3d(-2px, 0, 0); + } + 20%, + 80% { + -webkit-transform: translate3d(4px, 0, 0); + transform: translate3d(4px, 0, 0); + } + 30%, + 50%, + 70% { + -webkit-transform: translate3d(-8px, 0, 0); + transform: translate3d(-8px, 0, 0); + } + 40%, + 60% { + -webkit-transform: translate3d(8px, 0, 0); + transform: translate3d(8px, 0, 0); + } +} +@keyframes hilight { + 10%, + 90% { + -webkit-transform: translate3d(-2px, 0, 0); + transform: translate3d(-2px, 0, 0); + } + 20%, + 80% { + -webkit-transform: translate3d(4px, 0, 0); + transform: translate3d(4px, 0, 0); + } + 30%, + 50%, + 70% { + -webkit-transform: translate3d(-8px, 0, 0); + transform: translate3d(-8px, 0, 0); + } + 40%, + 60% { + -webkit-transform: translate3d(8px, 0, 0); + transform: translate3d(8px, 0, 0); + } +} +/*Transition rules*/ +.jconfirm { + -webkit-perspective: 400px; + perspective: 400px; +} +.jconfirm .jconfirm-box { + opacity: 1; + -webkit-transition-property: -webkit-transform, opacity, box-shadow; + transition-property: transform, opacity, box-shadow; +} +.jconfirm .jconfirm-box.anim-top, +.jconfirm .jconfirm-box.anim-left, +.jconfirm .jconfirm-box.anim-right, +.jconfirm .jconfirm-box.anim-bottom, +.jconfirm .jconfirm-box.anim-opacity, +.jconfirm .jconfirm-box.anim-zoom, +.jconfirm .jconfirm-box.anim-scale, +.jconfirm .jconfirm-box.anim-none, +.jconfirm .jconfirm-box.anim-rotate, +.jconfirm .jconfirm-box.anim-rotatex, +.jconfirm .jconfirm-box.anim-rotatey, +.jconfirm .jconfirm-box.anim-scaley, +.jconfirm .jconfirm-box.anim-scalex { + opacity: 0; +} +.jconfirm .jconfirm-box.anim-rotate { + -webkit-transform: rotate(90deg); + -ms-transform: rotate(90deg); + transform: rotate(90deg); +} +.jconfirm .jconfirm-box.anim-rotatex { + -webkit-transform: rotateX(90deg); + transform: rotateX(90deg); + -webkit-transform-origin: center; + -ms-transform-origin: center; + transform-origin: center; +} +.jconfirm .jconfirm-box.anim-rotatexr { + -webkit-transform: rotateX(-90deg); + transform: rotateX(-90deg); + -webkit-transform-origin: center; + -ms-transform-origin: center; + transform-origin: center; +} +.jconfirm .jconfirm-box.anim-rotatey { + -webkit-transform: rotatey(90deg); + -ms-transform: rotatey(90deg); + transform: rotatey(90deg); + -webkit-transform-origin: center; + -ms-transform-origin: center; + transform-origin: center; +} +.jconfirm .jconfirm-box.anim-rotateyr { + -webkit-transform: rotatey(-90deg); + -ms-transform: rotatey(-90deg); + transform: rotatey(-90deg); + -webkit-transform-origin: center; + -ms-transform-origin: center; + transform-origin: center; +} +.jconfirm .jconfirm-box.anim-scaley { + -webkit-transform: scaley(1.5); + -ms-transform: scaley(1.5); + transform: scaley(1.5); + -webkit-transform-origin: center; + -ms-transform-origin: center; + transform-origin: center; +} +.jconfirm .jconfirm-box.anim-scalex { + -webkit-transform: scalex(1.5); + -ms-transform: scalex(1.5); + transform: scalex(1.5); + -webkit-transform-origin: center; + -ms-transform-origin: center; + transform-origin: center; +} +.jconfirm .jconfirm-box.anim-top { + -webkit-transform: translate(0px, -100px); + -ms-transform: translate(0px, -100px); + transform: translate(0px, -100px); +} +.jconfirm .jconfirm-box.anim-left { + -webkit-transform: translate(-100px, 0px); + -ms-transform: translate(-100px, 0px); + transform: translate(-100px, 0px); +} +.jconfirm .jconfirm-box.anim-right { + -webkit-transform: translate(100px, 0px); + -ms-transform: translate(100px, 0px); + transform: translate(100px, 0px); +} +.jconfirm .jconfirm-box.anim-bottom { + -webkit-transform: translate(0px, 100px); + -ms-transform: translate(0px, 100px); + transform: translate(0px, 100px); +} +.jconfirm .jconfirm-box.anim-zoom { + -webkit-transform: scale(1.2); + -ms-transform: scale(1.2); + transform: scale(1.2); +} +.jconfirm .jconfirm-box.anim-scale { + -webkit-transform: scale(0.5); + -ms-transform: scale(0.5); + transform: scale(0.5); +} +.jconfirm .jconfirm-box.anim-none { + display: none; +} +.jconfirm.jconfirm-supervan .jconfirm-bg { + background-color: rgba(54, 70, 93, 0.95); +} +.jconfirm.jconfirm-supervan .jconfirm-box { + background-color: transparent; +} +.jconfirm.jconfirm-supervan .jconfirm-box div.closeIcon { + color: white; +} +.jconfirm.jconfirm-supervan .jconfirm-box div.title-c { + text-align: center; + color: white; + font-size: 28px; + font-weight: normal; +} +.jconfirm.jconfirm-supervan .jconfirm-box div.title-c > * { + padding-bottom: 25px; +} +.jconfirm.jconfirm-supervan .jconfirm-box div.content-pane { + margin-bottom: 25px; +} +.jconfirm.jconfirm-supervan .jconfirm-box div.content { + text-align: center; + color: white; +} +.jconfirm.jconfirm-supervan .jconfirm-box .buttons { + text-align: center; +} +.jconfirm.jconfirm-supervan .jconfirm-box .buttons button { + font-size: 16px; + border-radius: 2px; + background: #303f53; + text-shadow: none; + border: none; + color: white; + padding: 10px; + min-width: 100px; +} +.jconfirm.jconfirm-material .jconfirm-bg { + background-color: rgba(0, 0, 0, 0.67); +} +.jconfirm.jconfirm-material .jconfirm-box { + background-color: white; + box-shadow: 0 7px 8px -4px rgba(0, 0, 0, 0.2), 0 13px 19px 2px rgba(0, 0, 0, 0.14), 0 5px 24px 4px rgba(0, 0, 0, 0.12); + padding: 30px 25px 10px 25px; +} +.jconfirm.jconfirm-material .jconfirm-box div.closeIcon { + color: rgba(0, 0, 0, 0.87); +} +.jconfirm.jconfirm-material .jconfirm-box div.title-c { + color: rgba(0, 0, 0, 0.87); + font-size: 22px; + font-weight: bold; +} +.jconfirm.jconfirm-material .jconfirm-box div.content { + text-align: left; + color: rgba(0, 0, 0, 0.87); +} +.jconfirm.jconfirm-material .jconfirm-box .buttons { + text-align: right; +} +.jconfirm.jconfirm-material .jconfirm-box .buttons button { + text-transform: uppercase; + font-weight: 500; +} +.jconfirm.jconfirm-bootstrap .jconfirm-bg { + background-color: rgba(0, 0, 0, 0.21); +} +.jconfirm.jconfirm-bootstrap .jconfirm-box { + background-color: white; + box-shadow: 0 3px 8px 0px rgba(0, 0, 0, 0.2); + border: solid 1px rgba(0, 0, 0, 0.4); + padding: 15px 0 0; +} +.jconfirm.jconfirm-bootstrap .jconfirm-box div.closeIcon { + color: rgba(0, 0, 0, 0.87); +} +.jconfirm.jconfirm-bootstrap .jconfirm-box div.title-c { + color: rgba(0, 0, 0, 0.87); + font-size: 22px; + font-weight: bold; + padding-left: 15px; + padding-right: 15px; +} +.jconfirm.jconfirm-bootstrap .jconfirm-box div.content { + text-align: left; + color: rgba(0, 0, 0, 0.87); + padding: 0px 15px; +} +.jconfirm.jconfirm-bootstrap .jconfirm-box .buttons { + text-align: right; + padding: 0px 0 0px; + margin: -5px 0 0px; + border-top: solid 1px #ddd; + overflow: hidden; + border-radius: 0 0 4px 4px; +} +.jconfirm.jconfirm-bootstrap .jconfirm-box .buttons button { + font-weight: 500; + border-radius: 0px; + margin: 0; + border-left: solid 1px #ddd; +} diff --git a/kfet/static/kfet/css/jquery-ui.min.css b/kfet/static/kfet/css/jquery-ui.min.css new file mode 100644 index 00000000..5e11b92a --- /dev/null +++ b/kfet/static/kfet/css/jquery-ui.min.css @@ -0,0 +1,6 @@ +/*! jQuery UI - v1.12.0 - 2016-08-17 +* http://jqueryui.com +* Includes: selectable.css +* Copyright jQuery Foundation and other contributors; Licensed MIT */ + +.ui-selectable{-ms-touch-action:none;touch-action:none}.ui-selectable-helper{position:absolute;z-index:100;border:1px dotted black} \ No newline at end of file diff --git a/kfet/static/kfet/css/kpsul.css b/kfet/static/kfet/css/kpsul.css new file mode 100644 index 00000000..b15f6de8 --- /dev/null +++ b/kfet/static/kfet/css/kpsul.css @@ -0,0 +1,385 @@ +input[type=number] { + -moz-appearance:textfield; +} + +input[type=number]::-webkit-inner-spin-button, +input[type=number]::-webkit-outer-spin-button { + -webkit-appearance: none; + margin: 0; +} + +#account, #checkout, input, #history, #basket, #basket_rel, #articles_data { + background:#fff; +} + +@keyframes colorchange { + 0% { background: yellow; } + 50% { background: #660066; } + 100% { background: yellow; } +} + +/* + * Top row + */ + +.row.kpsul_top { + padding:0 15px; + color:#000; +} + +.row.kpsul_top > div { + margin-top:15px; +} + +/* Account */ + +#account { + color:black; + height:120px; +} + +#account[data-balance="ok"] #account_form input { background:#009011; color:#FFF;} +#account[data-balance="low"] #account_form input { background:#EC6400; color:#FFF; } +#account[data-balance="neg"] #account_form input { background:#C8102E; color:#FFF; } +#account[data-balance="frozen"] #account_form input { background:#000FBA; color:#FFF; } + +#account_form { + padding:0; + height:100%; +} + +#account_form input { + width:100%; + height:100%; + + padding:0; + padding-bottom:10px; + + border:0; + border-radius:0; + + background:#f3f3f3; + + font-family:'Roboto Mono'; + font-size:50px; + font-weight:bold; + + text-align:center; + text-transform:uppercase; +} + +#account_data { + height:100%; +} + +#account_data .data_line { + line-height:16px; + + font-family:'Roboto Mono'; + font-size:12px; +} + +#account_data #account-balance { + height:40px; + line-height:40px; + + font-size:35px; + font-weight:bold; +} + +#account-name { + font-weight:bold; +} + +#account .buttons { + position:absolute; + bottom:0; + right:0; +} + +@media (min-width: 600px) { + #account_form input { font-size:60px; } + + @media (min-width: 768px) { + #account{ height:160px; } + #account_form input { font-size: 70px; } + #account_name { font-size:60px; } + #account_data .data_line { + font-size:14px; + line-height:24px; + } + #account_data #account-balance { + font-size:50px; + line-height:60px; + height:60px; + } + + #account { margin-right:0; } + #account_form input { font-size:85px; } + + @media (min-width: 992px) { + #account_form input { font-size:100px; } + } + } +} + +/* Checkout */ + +#checkout { + padding:0; + height:160px; + font-family:'Roboto Mono'; +} + +#checkout_form select { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + cursor:pointer; + outline:none; + border:0; + + width:100%; + height:50px; + padding:0 15px; + + background:#c8102e; + color:#fff; + + font-weight:bold; + font-size:18px; +} + +#checkout_form select option { + height:25px; + padding:0 15px; + line-height:25px; + font-weight:normal; + font-size:14px; + background-color:rgba(255,255,255,0.1); +} + +#checkout_data { + padding:0 15px; +} + +#checkout_data .buttons { + position:absolute; + bottom:0; + right:0; +} + +#addcost { + position:absolute; + bottom:0; + left:0; + + height:32px; + line-height:32px; + padding:0 10px; + + background:#c8102e; + color:#fff; + + font-weight:bold; + + animation: blink 1.5s steps(1) infinite; +} +@keyframes blink { 50% { visibility: hidden; } } + +/* + * Second part + */ + +.row.kpsul_middle { + margin:15px 0; + font-family:'Roboto Mono'; + color:#000; +} + +.row.kpsul_middle > div { + padding:0; + height:100%; +} + +.row.kpsul_middle > div:first-child > div { + margin-right:0; +} + +.row.kpsul_middle > div+div { + margin-top:15px; +} + +@media (min-width:768px) { + .row.kpsul_middle > div:first-child > div { + margin-right:15px + } + + .row.kpsul_middle > div { + margin-top:0 !important; + } +} + +/* Special operations */ + +#special_operations { + height:40px; +} + +#special_operations button { + height:100%; + width:25%; + + float:left; + + background:#c8102e; + color:#FFF; + + font-size:18px; + font-weight:bold; +} + +#special_operations button:focus, #special_operations button:hover { + outline:none; + background:#000; + color:#fff; +} + +/* Article autocomplete */ + +#article_selection { + height:40px; + width:100%; +} + +#article_selection input { + height:100%; + float:left; + border:0; + border-right:1px solid #c8102e; + border-bottom:1px solid #c8102e; + border-radius:0; + font-size:16px; + font-weight:bold; +} + +#article_selection input+input { + border-right:0; +} + +#article_autocomplete { + width:90%; + padding-left:10px; +} + +#article_number { + width:10%; + text-align:center; +} + +@media (min-width:1200px) { + #article_autocomplete { + width:92% + } + + #article_number { + width:8%; + } +} + +/* Article data */ + +#articles_data { + overflow:auto; + max-height:500px; +} + +#articles_data table { + width: 100%; +} + +#articles_data table tr.article { + height:25px; + font-size:14px; +} + +#articles_data table tr.article>td:first-child { + padding-left:10px; +} + +#articles_data table tr.category { + height:35px; + background-color:#c8102e; + font-size:16px; + color:#FFF; + font-weight:bold; +} + +#articles_data table tr.category>td:first-child { + padding-left:20px; +} + +/* Second part - Left - bottom */ + +.kpsul_middle_left_bottom { + margin:15px 0 0; +} + +.kpsul_middle_left_bottom > div { + height:160px; + padding:0; +} + +#basket, #basket_rel { + height:100%; +} + +#basket_rel { + border-top:1px solid #C8102E; +} + +#basket { + overflow:auto; +} + +@media (min-width:768px) { + #basket { + margin-right:7px; + } + #basket_rel { + border-top:0; + margin-left:7px; + } +} + +#basket table { + width:100%; +} + +#basket table tr { + height:25px; + font-size:14px; +} + +#basket tr .amount { + width:70px; + padding-right:15px; + text-align:right; +} + +#basket tr .number { + width:50px; + padding-right:15px; + text-align:right; +} + +#basket tr.ui-selected, #basket tr.ui-selecting { + background-color:rgba(200,16,46,0.6); + color:#FFF; +} + +/* History */ + +.kpsul_middle_right_col { + overflow:auto; +} diff --git a/kfet/static/kfet/css/kpsul_grid.css b/kfet/static/kfet/css/kpsul_grid.css new file mode 100644 index 00000000..c90924d2 --- /dev/null +++ b/kfet/static/kfet/css/kpsul_grid.css @@ -0,0 +1,9 @@ +@media (min-width:768px) { + html { height:100%; } + body { height:calc(100% - 50px); } + .container-fluid { height:100%; } + .kpsul_middle { height:calc(100% - 205px); } + .kpsul_middle_left_top { height:calc(100% - 175px); min-height:80px; } + .kpsul_middle_left { height:100%; } + #articles_data { height: calc(100% - 80px); } +} diff --git a/kfet/static/kfet/css/multiple-select.css b/kfet/static/kfet/css/multiple-select.css new file mode 100644 index 00000000..c5b8d183 --- /dev/null +++ b/kfet/static/kfet/css/multiple-select.css @@ -0,0 +1,191 @@ +/** + * @author zhixin wen + */ + +.ms-parent { + display: inline-block; + position: relative; + vertical-align: middle; +} + +.ms-choice { + display: block; + width: 100%; + height: 26px; + padding: 0; + overflow: hidden; + cursor: pointer; + border: 1px solid #aaa; + text-align: left; + white-space: nowrap; + line-height: 26px; + color: #444; + text-decoration: none; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + background-color: #fff; +} + +.ms-choice.disabled { + background-color: #f4f4f4; + background-image: none; + border: 1px solid #ddd; + cursor: default; +} + +.ms-choice > span { + position: absolute; + top: 0; + left: 0; + right: 20px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + display: block; + padding-left: 8px; +} + +.ms-choice > span.placeholder { + color: #999; +} + +.ms-choice > div { + position: absolute; + top: 0; + right: 0; + width: 20px; + height: 25px; + background: url('../img/multiple-select.png') left top no-repeat; +} + +.ms-choice > div.open { + background: url('../img/multiple-select.png') right top no-repeat; +} + +.ms-drop { + width: 100%; + overflow: hidden; + display: none; + margin-top: -1px; + padding: 0; + position: absolute; + z-index: 1000; + background: #fff; + color: #000; + border: 1px solid #aaa; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} + +.ms-drop.bottom { + top: 100%; + -webkit-box-shadow: 0 4px 5px rgba(0, 0, 0, .15); + -moz-box-shadow: 0 4px 5px rgba(0, 0, 0, .15); + box-shadow: 0 4px 5px rgba(0, 0, 0, .15); +} + +.ms-drop.top { + bottom: 100%; + -webkit-box-shadow: 0 -4px 5px rgba(0, 0, 0, .15); + -moz-box-shadow: 0 -4px 5px rgba(0, 0, 0, .15); + box-shadow: 0 -4px 5px rgba(0, 0, 0, .15); +} + +.ms-search { + display: inline-block; + margin: 0; + min-height: 26px; + padding: 4px; + position: relative; + white-space: nowrap; + width: 100%; + z-index: 10000; +} + +.ms-search input { + width: 100%; + height: auto !important; + min-height: 24px; + padding: 0 20px 0 5px; + margin: 0; + outline: 0; + font-family: sans-serif; + font-size: 1em; + border: 1px solid #aaa; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; + background: #fff url('../img/multiple-select.png') no-repeat 100% -22px; + background: url('../img/multiple-select.png') no-repeat 100% -22px, -webkit-gradient(linear, left bottom, left top, color-stop(0.85, white), color-stop(0.99, #eeeeee)); + background: url('../img/multiple-select.png') no-repeat 100% -22px, -webkit-linear-gradient(center bottom, white 85%, #eeeeee 99%); + background: url('../img/multiple-select.png') no-repeat 100% -22px, -moz-linear-gradient(center bottom, white 85%, #eeeeee 99%); + background: url('../img/multiple-select.png') no-repeat 100% -22px, -o-linear-gradient(bottom, white 85%, #eeeeee 99%); + background: url('../img/multiple-select.png') no-repeat 100% -22px, -ms-linear-gradient(top, #ffffff 85%, #eeeeee 99%); + background: url('../img/multiple-select.png') no-repeat 100% -22px, linear-gradient(top, #ffffff 85%, #eeeeee 99%); +} + +.ms-search, .ms-search input { + -webkit-box-sizing: border-box; + -khtml-box-sizing: border-box; + -moz-box-sizing: border-box; + -ms-box-sizing: border-box; + box-sizing: border-box; +} + +.ms-drop ul { + overflow: auto; + margin: 0; + padding: 5px 8px; +} + +.ms-drop ul > li { + list-style: none; + display: list-item; + background-image: none; + position: static; +} + +.ms-drop ul > li .disabled { + opacity: .35; + filter: Alpha(Opacity=35); +} + +.ms-drop ul > li.multiple { + display: block; + float: left; +} + +.ms-drop ul > li.group { + clear: both; +} + +.ms-drop ul > li.multiple label { + width: 100%; + display: block; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.ms-drop ul > li label { + font-weight: normal; + display: block; + white-space: nowrap; +} + +.ms-drop ul > li label.optgroup { + font-weight: bold; +} + +.ms-drop input[type="checkbox"] { + vertical-align: middle; +} + +.ms-drop .ms-no-results { + display: none; +} diff --git a/kfet/static/kfet/css/nav.css b/kfet/static/kfet/css/nav.css new file mode 100644 index 00000000..5ffc7b24 --- /dev/null +++ b/kfet/static/kfet/css/nav.css @@ -0,0 +1,67 @@ +nav { + background:#000; + color:#DDD; + font-family:Oswald; +} + +.navbar-nav > li > .dropdown-menu { + border:0; + border-radius:0; +} + +.navbar-fixed-top { + border:0; +} + +nav .navbar-brand { + padding:3px 25px; +} + +nav .navbar-brand img { + height:44px; +} + +nav .navbar-toggle .icon-bar { + background-color:#FFF; +} + +nav a { + color:#DDD; +} + +.navbar-nav { + font-weight:bold; + font-size:14px; + text-transform:uppercase; +} + +.nav>li>a:focus, .nav>li>a:hover { + background-color:#C8102E; + color:#FFF; +} + +.nav .open>a, .nav .open>a:focus, .nav .open>a:hover { + background-color:#C8102E; +} + +.dropdown-menu { + padding:0; +} + +.dropdown-menu>li>a { + padding:8px 20px; +} + +.dropdown-menu .divider { + margin:0; +} + +@media (max-width: 767px) { + .navbar-nav .open .dropdown-menu { + background-color:#FFF; + } + + .navbar-nav { + margin:0 -15px; + } +} diff --git a/kfet/static/kfet/css/transfers_form.css b/kfet/static/kfet/css/transfers_form.css new file mode 100644 index 00000000..231a0c89 --- /dev/null +++ b/kfet/static/kfet/css/transfers_form.css @@ -0,0 +1,73 @@ +.transfer_general { + margin:15px 0; + height:45px; +} + +.transfer_general input { + height:100%; + border:0; + padding:0 5px; + font-size:16px; +} + +.transfer_general button { + height:100%; + margin-top:-3px; +} + +.transfer_formset { + background:#FFF; +} + +.transfer_formset thead { + height:40px; + background:#c8102e; + color:#fff; + font-size:20px; + font-weight:bold; + text-align:center; +} + +.transfer_form td { + height:50px; + vertical-align:middle !important; + padding:0 !important; +} + +.transfer_form input { + border:0; + border-radius:0; + + width:100%; + height:100%; + + font-family:'Roboto Mono'; + font-size:25px; + font-weight:bold; + + text-align:center; + vertical-align:middle; + text-transform:uppercase; +} + +.transfer_form .from_acc_data, .transfer_form .to_acc_data { + width:30%; + text-align:center; + font-size:20px; +} + +.transfer_form .from_acc, .transfer_form .to_acc { + width:15%; +} + +.transfer_form .from_acc { + border-left:1px solid #ddd; +} + +.transfer_form .to_acc { + border-right:1px solid #ddd; +} + +.transfer_form .amount { + width:10%; +} diff --git a/kfet/static/kfet/img/logo3.png b/kfet/static/kfet/img/logo3.png new file mode 100644 index 00000000..b68418cd Binary files /dev/null and b/kfet/static/kfet/img/logo3.png differ diff --git a/kfet/static/kfet/img/multiple-select.png b/kfet/static/kfet/img/multiple-select.png new file mode 100644 index 00000000..b1282ce4 Binary files /dev/null and b/kfet/static/kfet/img/multiple-select.png differ diff --git a/kfet/static/kfet/js/bootstrap-datetimepicker.min.js b/kfet/static/kfet/js/bootstrap-datetimepicker.min.js new file mode 100644 index 00000000..db3d085d --- /dev/null +++ b/kfet/static/kfet/js/bootstrap-datetimepicker.min.js @@ -0,0 +1,9 @@ +/*! version : 4.17.37 + ========================================================= + bootstrap-datetimejs + https://github.com/Eonasdan/bootstrap-datetimepicker + Copyright (c) 2015 Jonathan Peterson + ========================================================= + */ +!function(a){"use strict";if("function"==typeof define&&define.amd)define(["jquery","moment"],a);else if("object"==typeof exports)a(require("jquery"),require("moment"));else{if("undefined"==typeof jQuery)throw"bootstrap-datetimepicker requires jQuery to be loaded first";if("undefined"==typeof moment)throw"bootstrap-datetimepicker requires Moment.js to be loaded first";a(jQuery,moment)}}(function(a,b){"use strict";if(!b)throw new Error("bootstrap-datetimepicker requires Moment.js to be loaded first");var c=function(c,d){var e,f,g,h,i,j,k,l={},m=!0,n=!1,o=!1,p=0,q=[{clsName:"days",navFnc:"M",navStep:1},{clsName:"months",navFnc:"y",navStep:1},{clsName:"years",navFnc:"y",navStep:10},{clsName:"decades",navFnc:"y",navStep:100}],r=["days","months","years","decades"],s=["top","bottom","auto"],t=["left","right","auto"],u=["default","top","bottom"],v={up:38,38:"up",down:40,40:"down",left:37,37:"left",right:39,39:"right",tab:9,9:"tab",escape:27,27:"escape",enter:13,13:"enter",pageUp:33,33:"pageUp",pageDown:34,34:"pageDown",shift:16,16:"shift",control:17,17:"control",space:32,32:"space",t:84,84:"t","delete":46,46:"delete"},w={},x=function(a){var c,e,f,g,h,i=!1;return void 0!==b.tz&&void 0!==d.timeZone&&null!==d.timeZone&&""!==d.timeZone&&(i=!0),void 0===a||null===a?c=i?b().tz(d.timeZone).startOf("d"):b().startOf("d"):i?(e=b().tz(d.timeZone).utcOffset(),f=b(a,j,d.useStrict).utcOffset(),f!==e?(g=b().tz(d.timeZone).format("Z"),h=b(a,j,d.useStrict).format("YYYY-MM-DD[T]HH:mm:ss")+g,c=b(h,j,d.useStrict).tz(d.timeZone)):c=b(a,j,d.useStrict).tz(d.timeZone)):c=b(a,j,d.useStrict),c},y=function(a){if("string"!=typeof a||a.length>1)throw new TypeError("isEnabled expects a single character string parameter");switch(a){case"y":return-1!==i.indexOf("Y");case"M":return-1!==i.indexOf("M");case"d":return-1!==i.toLowerCase().indexOf("d");case"h":case"H":return-1!==i.toLowerCase().indexOf("h");case"m":return-1!==i.indexOf("m");case"s":return-1!==i.indexOf("s");default:return!1}},z=function(){return y("h")||y("m")||y("s")},A=function(){return y("y")||y("M")||y("d")},B=function(){var b=a("").append(a("").append(a("").addClass("prev").attr("data-action","previous").append(a("").addClass(d.icons.previous))).append(a("").addClass("picker-switch").attr("data-action","pickerSwitch").attr("colspan",d.calendarWeeks?"6":"5")).append(a("").addClass("next").attr("data-action","next").append(a("").addClass(d.icons.next)))),c=a("").append(a("").append(a("").attr("colspan",d.calendarWeeks?"8":"7")));return[a("
").addClass("datepicker-days").append(a("").addClass("table-condensed").append(b).append(a(""))),a("
").addClass("datepicker-months").append(a("
").addClass("table-condensed").append(b.clone()).append(c.clone())),a("
").addClass("datepicker-years").append(a("
").addClass("table-condensed").append(b.clone()).append(c.clone())),a("
").addClass("datepicker-decades").append(a("
").addClass("table-condensed").append(b.clone()).append(c.clone()))]},C=function(){var b=a(""),c=a(""),e=a("");return y("h")&&(b.append(a("
").append(a("").attr({href:"#",tabindex:"-1",title:d.tooltips.incrementHour}).addClass("btn").attr("data-action","incrementHours").append(a("").addClass(d.icons.up)))),c.append(a("").append(a("").addClass("timepicker-hour").attr({"data-time-component":"hours",title:d.tooltips.pickHour}).attr("data-action","showHours"))),e.append(a("").append(a("").attr({href:"#",tabindex:"-1",title:d.tooltips.decrementHour}).addClass("btn").attr("data-action","decrementHours").append(a("").addClass(d.icons.down))))),y("m")&&(y("h")&&(b.append(a("").addClass("separator")),c.append(a("").addClass("separator").html(":")),e.append(a("").addClass("separator"))),b.append(a("").append(a("").attr({href:"#",tabindex:"-1",title:d.tooltips.incrementMinute}).addClass("btn").attr("data-action","incrementMinutes").append(a("").addClass(d.icons.up)))),c.append(a("").append(a("").addClass("timepicker-minute").attr({"data-time-component":"minutes",title:d.tooltips.pickMinute}).attr("data-action","showMinutes"))),e.append(a("").append(a("").attr({href:"#",tabindex:"-1",title:d.tooltips.decrementMinute}).addClass("btn").attr("data-action","decrementMinutes").append(a("").addClass(d.icons.down))))),y("s")&&(y("m")&&(b.append(a("").addClass("separator")),c.append(a("").addClass("separator").html(":")),e.append(a("").addClass("separator"))),b.append(a("").append(a("").attr({href:"#",tabindex:"-1",title:d.tooltips.incrementSecond}).addClass("btn").attr("data-action","incrementSeconds").append(a("").addClass(d.icons.up)))),c.append(a("").append(a("").addClass("timepicker-second").attr({"data-time-component":"seconds",title:d.tooltips.pickSecond}).attr("data-action","showSeconds"))),e.append(a("").append(a("").attr({href:"#",tabindex:"-1",title:d.tooltips.decrementSecond}).addClass("btn").attr("data-action","decrementSeconds").append(a("").addClass(d.icons.down))))),h||(b.append(a("").addClass("separator")),c.append(a("").append(a("").addClass("separator"))),a("
").addClass("timepicker-picker").append(a("").addClass("table-condensed").append([b,c,e]))},D=function(){var b=a("
").addClass("timepicker-hours").append(a("
").addClass("table-condensed")),c=a("
").addClass("timepicker-minutes").append(a("
").addClass("table-condensed")),d=a("
").addClass("timepicker-seconds").append(a("
").addClass("table-condensed")),e=[C()];return y("h")&&e.push(b),y("m")&&e.push(c),y("s")&&e.push(d),e},E=function(){var b=[];return d.showTodayButton&&b.push(a("
").append(a("").attr({"data-action":"today",title:d.tooltips.today}).append(a("").addClass(d.icons.today)))),!d.sideBySide&&A()&&z()&&b.push(a("").append(a("").attr({"data-action":"togglePicker",title:d.tooltips.selectTime}).append(a("").addClass(d.icons.time)))),d.showClear&&b.push(a("").append(a("").attr({"data-action":"clear",title:d.tooltips.clear}).append(a("").addClass(d.icons.clear)))),d.showClose&&b.push(a("").append(a("").attr({"data-action":"close",title:d.tooltips.close}).append(a("").addClass(d.icons.close)))),a("").addClass("table-condensed").append(a("").append(a("").append(b)))},F=function(){var b=a("
").addClass("bootstrap-datetimepicker-widget dropdown-menu"),c=a("
").addClass("datepicker").append(B()),e=a("
").addClass("timepicker").append(D()),f=a("
    ").addClass("list-unstyled"),g=a("
  • ").addClass("picker-switch"+(d.collapse?" accordion-toggle":"")).append(E());return d.inline&&b.removeClass("dropdown-menu"),h&&b.addClass("usetwentyfour"),y("s")&&!h&&b.addClass("wider"),d.sideBySide&&A()&&z()?(b.addClass("timepicker-sbs"),"top"===d.toolbarPlacement&&b.append(g),b.append(a("
    ").addClass("row").append(c.addClass("col-md-6")).append(e.addClass("col-md-6"))),"bottom"===d.toolbarPlacement&&b.append(g),b):("top"===d.toolbarPlacement&&f.append(g),A()&&f.append(a("
  • ").addClass(d.collapse&&z()?"collapse in":"").append(c)),"default"===d.toolbarPlacement&&f.append(g),z()&&f.append(a("
  • ").addClass(d.collapse&&A()?"collapse":"").append(e)),"bottom"===d.toolbarPlacement&&f.append(g),b.append(f))},G=function(){var b,e={};return b=c.is("input")||d.inline?c.data():c.find("input").data(),b.dateOptions&&b.dateOptions instanceof Object&&(e=a.extend(!0,e,b.dateOptions)),a.each(d,function(a){var c="date"+a.charAt(0).toUpperCase()+a.slice(1);void 0!==b[c]&&(e[a]=b[c])}),e},H=function(){var b,e=(n||c).position(),f=(n||c).offset(),g=d.widgetPositioning.vertical,h=d.widgetPositioning.horizontal;if(d.widgetParent)b=d.widgetParent.append(o);else if(c.is("input"))b=c.after(o).parent();else{if(d.inline)return void(b=c.append(o));b=c,c.children().first().after(o)}if("auto"===g&&(g=f.top+1.5*o.height()>=a(window).height()+a(window).scrollTop()&&o.height()+c.outerHeight()a(window).width()?"right":"left"),"top"===g?o.addClass("top").removeClass("bottom"):o.addClass("bottom").removeClass("top"),"right"===h?o.addClass("pull-right"):o.removeClass("pull-right"),"relative"!==b.css("position")&&(b=b.parents().filter(function(){return"relative"===a(this).css("position")}).first()),0===b.length)throw new Error("datetimepicker component should be placed within a relative positioned container");o.css({top:"top"===g?"auto":e.top+c.outerHeight(),bottom:"top"===g?e.top+c.outerHeight():"auto",left:"left"===h?b===c?0:e.left:"auto",right:"left"===h?"auto":b.outerWidth()-c.outerWidth()-(b===c?0:e.left)})},I=function(a){"dp.change"===a.type&&(a.date&&a.date.isSame(a.oldDate)||!a.date&&!a.oldDate)||c.trigger(a)},J=function(a){"y"===a&&(a="YYYY"),I({type:"dp.update",change:a,viewDate:f.clone()})},K=function(a){o&&(a&&(k=Math.max(p,Math.min(3,k+a))),o.find(".datepicker > div").hide().filter(".datepicker-"+q[k].clsName).show())},L=function(){var b=a("
"),c=f.clone().startOf("w").startOf("d");for(d.calendarWeeks===!0&&b.append(a(""),d.calendarWeeks&&c.append('"),k.push(c)),g="",b.isBefore(f,"M")&&(g+=" old"),b.isAfter(f,"M")&&(g+=" new"),b.isSame(e,"d")&&!m&&(g+=" active"),Q(b,"d")||(g+=" disabled"),b.isSame(x(),"d")&&(g+=" today"),(0===b.day()||6===b.day())&&(g+=" weekend"),c.append('"),b.add(1,"d");i.find("tbody").empty().append(k),S(),T(),U()}},W=function(){var b=o.find(".timepicker-hours table"),c=f.clone().startOf("d"),d=[],e=a("");for(f.hour()>11&&!h&&c.hour(12);c.isSame(f,"d")&&(h||f.hour()<12&&c.hour()<12||f.hour()>11);)c.hour()%4===0&&(e=a(""),d.push(e)),e.append('"),c.add(1,"h");b.empty().append(d)},X=function(){for(var b=o.find(".timepicker-minutes table"),c=f.clone().startOf("h"),e=[],g=a(""),h=1===d.stepping?5:d.stepping;f.isSame(c,"h");)c.minute()%(4*h)===0&&(g=a(""),e.push(g)),g.append('"),c.add(h,"m");b.empty().append(e)},Y=function(){for(var b=o.find(".timepicker-seconds table"),c=f.clone().startOf("m"),d=[],e=a("");f.isSame(c,"m");)c.second()%20===0&&(e=a(""),d.push(e)),e.append('"),c.add(5,"s");b.empty().append(d)},Z=function(){var a,b,c=o.find(".timepicker span[data-time-component]");h||(a=o.find(".timepicker [data-action=togglePeriod]"),b=e.clone().add(e.hours()>=12?-12:12,"h"),a.text(e.format("A")),Q(b,"h")?a.removeClass("disabled"):a.addClass("disabled")),c.filter("[data-time-component=hours]").text(e.format(h?"HH":"hh")),c.filter("[data-time-component=minutes]").text(e.format("mm")),c.filter("[data-time-component=seconds]").text(e.format("ss")),W(),X(),Y()},$=function(){o&&(V(),Z())},_=function(a){var b=m?null:e;return a?(a=a.clone().locale(d.locale),1!==d.stepping&&a.minutes(Math.round(a.minutes()/d.stepping)*d.stepping%60).seconds(0),void(Q(a)?(e=a,f=e.clone(),g.val(e.format(i)),c.data("date",e.format(i)),m=!1,$(),I({type:"dp.change",date:e.clone(),oldDate:b})):(d.keepInvalid||g.val(m?"":e.format(i)),I({type:"dp.error",date:a})))):(m=!0,g.val(""),c.data("date",""),I({type:"dp.change",date:!1,oldDate:b}),void $())},aa=function(){var b=!1;return o?(o.find(".collapse").each(function(){var c=a(this).data("collapse");return c&&c.transitioning?(b=!0,!1):!0}),b?l:(n&&n.hasClass("btn")&&n.toggleClass("active"),o.hide(),a(window).off("resize",H),o.off("click","[data-action]"),o.off("mousedown",!1),o.remove(),o=!1,I({type:"dp.hide",date:e.clone()}),g.blur(),l)):l},ba=function(){_(null)},ca={next:function(){var a=q[k].navFnc;f.add(q[k].navStep,a),V(),J(a)},previous:function(){var a=q[k].navFnc;f.subtract(q[k].navStep,a),V(),J(a)},pickerSwitch:function(){K(1)},selectMonth:function(b){var c=a(b.target).closest("tbody").find("span").index(a(b.target));f.month(c),k===p?(_(e.clone().year(f.year()).month(f.month())),d.inline||aa()):(K(-1),V()),J("M")},selectYear:function(b){var c=parseInt(a(b.target).text(),10)||0;f.year(c),k===p?(_(e.clone().year(f.year())),d.inline||aa()):(K(-1),V()),J("YYYY")},selectDecade:function(b){var c=parseInt(a(b.target).data("selection"),10)||0;f.year(c),k===p?(_(e.clone().year(f.year())),d.inline||aa()):(K(-1),V()),J("YYYY")},selectDay:function(b){var c=f.clone();a(b.target).is(".old")&&c.subtract(1,"M"),a(b.target).is(".new")&&c.add(1,"M"),_(c.date(parseInt(a(b.target).text(),10))),z()||d.keepOpen||d.inline||aa()},incrementHours:function(){var a=e.clone().add(1,"h");Q(a,"h")&&_(a)},incrementMinutes:function(){var a=e.clone().add(d.stepping,"m");Q(a,"m")&&_(a)},incrementSeconds:function(){var a=e.clone().add(1,"s");Q(a,"s")&&_(a)},decrementHours:function(){var a=e.clone().subtract(1,"h");Q(a,"h")&&_(a)},decrementMinutes:function(){var a=e.clone().subtract(d.stepping,"m");Q(a,"m")&&_(a)},decrementSeconds:function(){var a=e.clone().subtract(1,"s");Q(a,"s")&&_(a)},togglePeriod:function(){_(e.clone().add(e.hours()>=12?-12:12,"h"))},togglePicker:function(b){var c,e=a(b.target),f=e.closest("ul"),g=f.find(".in"),h=f.find(".collapse:not(.in)");if(g&&g.length){if(c=g.data("collapse"),c&&c.transitioning)return;g.collapse?(g.collapse("hide"),h.collapse("show")):(g.removeClass("in"),h.addClass("in")),e.is("span")?e.toggleClass(d.icons.time+" "+d.icons.date):e.find("span").toggleClass(d.icons.time+" "+d.icons.date)}},showPicker:function(){o.find(".timepicker > div:not(.timepicker-picker)").hide(),o.find(".timepicker .timepicker-picker").show()},showHours:function(){o.find(".timepicker .timepicker-picker").hide(),o.find(".timepicker .timepicker-hours").show()},showMinutes:function(){o.find(".timepicker .timepicker-picker").hide(),o.find(".timepicker .timepicker-minutes").show()},showSeconds:function(){o.find(".timepicker .timepicker-picker").hide(),o.find(".timepicker .timepicker-seconds").show()},selectHour:function(b){var c=parseInt(a(b.target).text(),10);h||(e.hours()>=12?12!==c&&(c+=12):12===c&&(c=0)),_(e.clone().hours(c)),ca.showPicker.call(l)},selectMinute:function(b){_(e.clone().minutes(parseInt(a(b.target).text(),10))),ca.showPicker.call(l)},selectSecond:function(b){_(e.clone().seconds(parseInt(a(b.target).text(),10))),ca.showPicker.call(l)},clear:ba,today:function(){var a=x();Q(a,"d")&&_(a)},close:aa},da=function(b){return a(b.currentTarget).is(".disabled")?!1:(ca[a(b.currentTarget).data("action")].apply(l,arguments),!1)},ea=function(){var b,c={year:function(a){return a.month(0).date(1).hours(0).seconds(0).minutes(0)},month:function(a){return a.date(1).hours(0).seconds(0).minutes(0)},day:function(a){return a.hours(0).seconds(0).minutes(0)},hour:function(a){return a.seconds(0).minutes(0)},minute:function(a){return a.seconds(0)}};return g.prop("disabled")||!d.ignoreReadonly&&g.prop("readonly")||o?l:(void 0!==g.val()&&0!==g.val().trim().length?_(ga(g.val().trim())):d.useCurrent&&m&&(g.is("input")&&0===g.val().trim().length||d.inline)&&(b=x(),"string"==typeof d.useCurrent&&(b=c[d.useCurrent](b)),_(b)),o=F(),L(),R(),o.find(".timepicker-hours").hide(),o.find(".timepicker-minutes").hide(),o.find(".timepicker-seconds").hide(),$(),K(),a(window).on("resize",H),o.on("click","[data-action]",da),o.on("mousedown",!1),n&&n.hasClass("btn")&&n.toggleClass("active"),o.show(),H(),d.focusOnShow&&!g.is(":focus")&&g.focus(),I({type:"dp.show"}),l)},fa=function(){return o?aa():ea()},ga=function(a){return a=void 0===d.parseInputDate?b.isMoment(a)||a instanceof Date?b(a):x(a):d.parseInputDate(a),a.locale(d.locale),a},ha=function(a){var b,c,e,f,g=null,h=[],i={},j=a.which,k="p";w[j]=k;for(b in w)w.hasOwnProperty(b)&&w[b]===k&&(h.push(b),parseInt(b,10)!==j&&(i[b]=!0));for(b in d.keyBinds)if(d.keyBinds.hasOwnProperty(b)&&"function"==typeof d.keyBinds[b]&&(e=b.split(" "),e.length===h.length&&v[j]===e[e.length-1])){for(f=!0,c=e.length-2;c>=0;c--)if(!(v[e[c]]in i)){f=!1;break}if(f){g=d.keyBinds[b];break}}g&&(g.call(l,o),a.stopPropagation(),a.preventDefault())},ia=function(a){w[a.which]="r",a.stopPropagation(),a.preventDefault()},ja=function(b){var c=a(b.target).val().trim(),d=c?ga(c):null;return _(d),b.stopImmediatePropagation(),!1},ka=function(){g.on({change:ja,blur:d.debug?"":aa,keydown:ha,keyup:ia,focus:d.allowInputToggle?ea:""}),c.is("input")?g.on({focus:ea}):n&&(n.on("click",fa),n.on("mousedown",!1))},la=function(){g.off({change:ja,blur:blur,keydown:ha,keyup:ia,focus:d.allowInputToggle?aa:""}),c.is("input")?g.off({focus:ea}):n&&(n.off("click",fa),n.off("mousedown",!1))},ma=function(b){var c={};return a.each(b,function(){var a=ga(this);a.isValid()&&(c[a.format("YYYY-MM-DD")]=!0)}),Object.keys(c).length?c:!1},na=function(b){var c={};return a.each(b,function(){c[this]=!0}),Object.keys(c).length?c:!1},oa=function(){var a=d.format||"L LT";i=a.replace(/(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g,function(a){var b=e.localeData().longDateFormat(a)||a;return b.replace(/(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g,function(a){return e.localeData().longDateFormat(a)||a})}),j=d.extraFormats?d.extraFormats.slice():[],j.indexOf(a)<0&&j.indexOf(i)<0&&j.push(i),h=i.toLowerCase().indexOf("a")<1&&i.replace(/\[.*?\]/g,"").indexOf("h")<1,y("y")&&(p=2),y("M")&&(p=1),y("d")&&(p=0),k=Math.max(p,k),m||_(e)};if(l.destroy=function(){aa(),la(),c.removeData("DateTimePicker"),c.removeData("date")},l.toggle=fa,l.show=ea,l.hide=aa,l.disable=function(){return aa(),n&&n.hasClass("btn")&&n.addClass("disabled"),g.prop("disabled",!0),l},l.enable=function(){return n&&n.hasClass("btn")&&n.removeClass("disabled"),g.prop("disabled",!1),l},l.ignoreReadonly=function(a){if(0===arguments.length)return d.ignoreReadonly;if("boolean"!=typeof a)throw new TypeError("ignoreReadonly () expects a boolean parameter");return d.ignoreReadonly=a,l},l.options=function(b){if(0===arguments.length)return a.extend(!0,{},d);if(!(b instanceof Object))throw new TypeError("options() options parameter should be an object");return a.extend(!0,d,b),a.each(d,function(a,b){if(void 0===l[a])throw new TypeError("option "+a+" is not recognized!");l[a](b)}),l},l.date=function(a){if(0===arguments.length)return m?null:e.clone();if(!(null===a||"string"==typeof a||b.isMoment(a)||a instanceof Date))throw new TypeError("date() parameter must be one of [null, string, moment or Date]");return _(null===a?null:ga(a)),l},l.format=function(a){if(0===arguments.length)return d.format;if("string"!=typeof a&&("boolean"!=typeof a||a!==!1))throw new TypeError("format() expects a sting or boolean:false parameter "+a);return d.format=a,i&&oa(),l},l.timeZone=function(a){return 0===arguments.length?d.timeZone:(d.timeZone=a,l)},l.dayViewHeaderFormat=function(a){if(0===arguments.length)return d.dayViewHeaderFormat;if("string"!=typeof a)throw new TypeError("dayViewHeaderFormat() expects a string parameter");return d.dayViewHeaderFormat=a,l},l.extraFormats=function(a){if(0===arguments.length)return d.extraFormats;if(a!==!1&&!(a instanceof Array))throw new TypeError("extraFormats() expects an array or false parameter");return d.extraFormats=a,j&&oa(),l},l.disabledDates=function(b){if(0===arguments.length)return d.disabledDates?a.extend({},d.disabledDates):d.disabledDates;if(!b)return d.disabledDates=!1,$(),l;if(!(b instanceof Array))throw new TypeError("disabledDates() expects an array parameter");return d.disabledDates=ma(b),d.enabledDates=!1,$(),l},l.enabledDates=function(b){if(0===arguments.length)return d.enabledDates?a.extend({},d.enabledDates):d.enabledDates;if(!b)return d.enabledDates=!1,$(),l;if(!(b instanceof Array))throw new TypeError("enabledDates() expects an array parameter");return d.enabledDates=ma(b),d.disabledDates=!1,$(),l},l.daysOfWeekDisabled=function(a){if(0===arguments.length)return d.daysOfWeekDisabled.splice(0);if("boolean"==typeof a&&!a)return d.daysOfWeekDisabled=!1,$(),l;if(!(a instanceof Array))throw new TypeError("daysOfWeekDisabled() expects an array parameter");if(d.daysOfWeekDisabled=a.reduce(function(a,b){return b=parseInt(b,10),b>6||0>b||isNaN(b)?a:(-1===a.indexOf(b)&&a.push(b),a)},[]).sort(),d.useCurrent&&!d.keepInvalid){for(var b=0;!Q(e,"d");){if(e.add(1,"d"),7===b)throw"Tried 7 times to find a valid date";b++}_(e)}return $(),l},l.maxDate=function(a){if(0===arguments.length)return d.maxDate?d.maxDate.clone():d.maxDate;if("boolean"==typeof a&&a===!1)return d.maxDate=!1,$(),l;"string"==typeof a&&("now"===a||"moment"===a)&&(a=x());var b=ga(a);if(!b.isValid())throw new TypeError("maxDate() Could not parse date parameter: "+a);if(d.minDate&&b.isBefore(d.minDate))throw new TypeError("maxDate() date parameter is before options.minDate: "+b.format(i));return d.maxDate=b,d.useCurrent&&!d.keepInvalid&&e.isAfter(a)&&_(d.maxDate),f.isAfter(b)&&(f=b.clone().subtract(d.stepping,"m")),$(),l},l.minDate=function(a){if(0===arguments.length)return d.minDate?d.minDate.clone():d.minDate;if("boolean"==typeof a&&a===!1)return d.minDate=!1,$(),l;"string"==typeof a&&("now"===a||"moment"===a)&&(a=x());var b=ga(a);if(!b.isValid())throw new TypeError("minDate() Could not parse date parameter: "+a);if(d.maxDate&&b.isAfter(d.maxDate))throw new TypeError("minDate() date parameter is after options.maxDate: "+b.format(i));return d.minDate=b,d.useCurrent&&!d.keepInvalid&&e.isBefore(a)&&_(d.minDate),f.isBefore(b)&&(f=b.clone().add(d.stepping,"m")),$(),l},l.defaultDate=function(a){if(0===arguments.length)return d.defaultDate?d.defaultDate.clone():d.defaultDate;if(!a)return d.defaultDate=!1,l;"string"==typeof a&&("now"===a||"moment"===a)&&(a=x());var b=ga(a);if(!b.isValid())throw new TypeError("defaultDate() Could not parse date parameter: "+a);if(!Q(b))throw new TypeError("defaultDate() date passed is invalid according to component setup validations");return d.defaultDate=b,(d.defaultDate&&d.inline||""===g.val().trim())&&_(d.defaultDate),l},l.locale=function(a){if(0===arguments.length)return d.locale;if(!b.localeData(a))throw new TypeError("locale() locale "+a+" is not loaded from moment locales!");return d.locale=a,e.locale(d.locale),f.locale(d.locale),i&&oa(),o&&(aa(),ea()),l},l.stepping=function(a){return 0===arguments.length?d.stepping:(a=parseInt(a,10),(isNaN(a)||1>a)&&(a=1),d.stepping=a,l)},l.useCurrent=function(a){var b=["year","month","day","hour","minute"];if(0===arguments.length)return d.useCurrent;if("boolean"!=typeof a&&"string"!=typeof a)throw new TypeError("useCurrent() expects a boolean or string parameter");if("string"==typeof a&&-1===b.indexOf(a.toLowerCase()))throw new TypeError("useCurrent() expects a string parameter of "+b.join(", "));return d.useCurrent=a,l},l.collapse=function(a){if(0===arguments.length)return d.collapse;if("boolean"!=typeof a)throw new TypeError("collapse() expects a boolean parameter");return d.collapse===a?l:(d.collapse=a,o&&(aa(),ea()),l)},l.icons=function(b){if(0===arguments.length)return a.extend({},d.icons);if(!(b instanceof Object))throw new TypeError("icons() expects parameter to be an Object");return a.extend(d.icons,b),o&&(aa(),ea()),l},l.tooltips=function(b){if(0===arguments.length)return a.extend({},d.tooltips);if(!(b instanceof Object))throw new TypeError("tooltips() expects parameter to be an Object");return a.extend(d.tooltips,b),o&&(aa(),ea()),l},l.useStrict=function(a){if(0===arguments.length)return d.useStrict;if("boolean"!=typeof a)throw new TypeError("useStrict() expects a boolean parameter");return d.useStrict=a,l},l.sideBySide=function(a){if(0===arguments.length)return d.sideBySide;if("boolean"!=typeof a)throw new TypeError("sideBySide() expects a boolean parameter");return d.sideBySide=a,o&&(aa(),ea()),l},l.viewMode=function(a){if(0===arguments.length)return d.viewMode;if("string"!=typeof a)throw new TypeError("viewMode() expects a string parameter");if(-1===r.indexOf(a))throw new TypeError("viewMode() parameter must be one of ("+r.join(", ")+") value");return d.viewMode=a,k=Math.max(r.indexOf(a),p),K(),l},l.toolbarPlacement=function(a){if(0===arguments.length)return d.toolbarPlacement;if("string"!=typeof a)throw new TypeError("toolbarPlacement() expects a string parameter");if(-1===u.indexOf(a))throw new TypeError("toolbarPlacement() parameter must be one of ("+u.join(", ")+") value");return d.toolbarPlacement=a,o&&(aa(),ea()),l},l.widgetPositioning=function(b){if(0===arguments.length)return a.extend({},d.widgetPositioning);if("[object Object]"!=={}.toString.call(b))throw new TypeError("widgetPositioning() expects an object variable");if(b.horizontal){if("string"!=typeof b.horizontal)throw new TypeError("widgetPositioning() horizontal variable must be a string");if(b.horizontal=b.horizontal.toLowerCase(),-1===t.indexOf(b.horizontal))throw new TypeError("widgetPositioning() expects horizontal parameter to be one of ("+t.join(", ")+")");d.widgetPositioning.horizontal=b.horizontal}if(b.vertical){if("string"!=typeof b.vertical)throw new TypeError("widgetPositioning() vertical variable must be a string");if(b.vertical=b.vertical.toLowerCase(),-1===s.indexOf(b.vertical))throw new TypeError("widgetPositioning() expects vertical parameter to be one of ("+s.join(", ")+")");d.widgetPositioning.vertical=b.vertical}return $(),l},l.calendarWeeks=function(a){if(0===arguments.length)return d.calendarWeeks;if("boolean"!=typeof a)throw new TypeError("calendarWeeks() expects parameter to be a boolean value");return d.calendarWeeks=a,$(),l},l.showTodayButton=function(a){if(0===arguments.length)return d.showTodayButton;if("boolean"!=typeof a)throw new TypeError("showTodayButton() expects a boolean parameter");return d.showTodayButton=a,o&&(aa(),ea()),l},l.showClear=function(a){if(0===arguments.length)return d.showClear;if("boolean"!=typeof a)throw new TypeError("showClear() expects a boolean parameter");return d.showClear=a,o&&(aa(),ea()),l},l.widgetParent=function(b){if(0===arguments.length)return d.widgetParent;if("string"==typeof b&&(b=a(b)),null!==b&&"string"!=typeof b&&!(b instanceof a))throw new TypeError("widgetParent() expects a string or a jQuery object parameter");return d.widgetParent=b,o&&(aa(),ea()),l},l.keepOpen=function(a){if(0===arguments.length)return d.keepOpen;if("boolean"!=typeof a)throw new TypeError("keepOpen() expects a boolean parameter");return d.keepOpen=a,l},l.focusOnShow=function(a){if(0===arguments.length)return d.focusOnShow;if("boolean"!=typeof a)throw new TypeError("focusOnShow() expects a boolean parameter");return d.focusOnShow=a,l},l.inline=function(a){if(0===arguments.length)return d.inline;if("boolean"!=typeof a)throw new TypeError("inline() expects a boolean parameter");return d.inline=a,l},l.clear=function(){return ba(),l},l.keyBinds=function(a){return d.keyBinds=a,l},l.getMoment=function(a){return x(a)},l.debug=function(a){if("boolean"!=typeof a)throw new TypeError("debug() expects a boolean parameter");return d.debug=a,l},l.allowInputToggle=function(a){if(0===arguments.length)return d.allowInputToggle;if("boolean"!=typeof a)throw new TypeError("allowInputToggle() expects a boolean parameter");return d.allowInputToggle=a,l},l.showClose=function(a){if(0===arguments.length)return d.showClose;if("boolean"!=typeof a)throw new TypeError("showClose() expects a boolean parameter");return d.showClose=a,l},l.keepInvalid=function(a){if(0===arguments.length)return d.keepInvalid;if("boolean"!=typeof a)throw new TypeError("keepInvalid() expects a boolean parameter");return d.keepInvalid=a,l},l.datepickerInput=function(a){if(0===arguments.length)return d.datepickerInput;if("string"!=typeof a)throw new TypeError("datepickerInput() expects a string parameter");return d.datepickerInput=a,l},l.parseInputDate=function(a){if(0===arguments.length)return d.parseInputDate; +if("function"!=typeof a)throw new TypeError("parseInputDate() sholud be as function");return d.parseInputDate=a,l},l.disabledTimeIntervals=function(b){if(0===arguments.length)return d.disabledTimeIntervals?a.extend({},d.disabledTimeIntervals):d.disabledTimeIntervals;if(!b)return d.disabledTimeIntervals=!1,$(),l;if(!(b instanceof Array))throw new TypeError("disabledTimeIntervals() expects an array parameter");return d.disabledTimeIntervals=b,$(),l},l.disabledHours=function(b){if(0===arguments.length)return d.disabledHours?a.extend({},d.disabledHours):d.disabledHours;if(!b)return d.disabledHours=!1,$(),l;if(!(b instanceof Array))throw new TypeError("disabledHours() expects an array parameter");if(d.disabledHours=na(b),d.enabledHours=!1,d.useCurrent&&!d.keepInvalid){for(var c=0;!Q(e,"h");){if(e.add(1,"h"),24===c)throw"Tried 24 times to find a valid date";c++}_(e)}return $(),l},l.enabledHours=function(b){if(0===arguments.length)return d.enabledHours?a.extend({},d.enabledHours):d.enabledHours;if(!b)return d.enabledHours=!1,$(),l;if(!(b instanceof Array))throw new TypeError("enabledHours() expects an array parameter");if(d.enabledHours=na(b),d.disabledHours=!1,d.useCurrent&&!d.keepInvalid){for(var c=0;!Q(e,"h");){if(e.add(1,"h"),24===c)throw"Tried 24 times to find a valid date";c++}_(e)}return $(),l},l.viewDate=function(a){if(0===arguments.length)return f.clone();if(!a)return f=e.clone(),l;if(!("string"==typeof a||b.isMoment(a)||a instanceof Date))throw new TypeError("viewDate() parameter must be one of [string, moment or Date]");return f=ga(a),J(),l},c.is("input"))g=c;else if(g=c.find(d.datepickerInput),0===g.size())g=c.find("input");else if(!g.is("input"))throw new Error('CSS class "'+d.datepickerInput+'" cannot be applied to non input element');if(c.hasClass("input-group")&&(n=0===c.find(".datepickerbutton").size()?c.find(".input-group-addon"):c.find(".datepickerbutton")),!d.inline&&!g.is("input"))throw new Error("Could not initialize DateTimePicker without an input element");return e=x(),f=e.clone(),a.extend(!0,d,G()),l.options(d),oa(),ka(),g.prop("disabled")&&l.disable(),g.is("input")&&0!==g.val().trim().length?_(ga(g.val().trim())):d.defaultDate&&void 0===g.attr("placeholder")&&_(d.defaultDate),d.inline&&ea(),l};a.fn.datetimepicker=function(b){return this.each(function(){var d=a(this);d.data("DateTimePicker")||(b=a.extend(!0,{},a.fn.datetimepicker.defaults,b),d.data("DateTimePicker",c(d,b)))})},a.fn.datetimepicker.defaults={timeZone:"Etc/UTC",format:!1,dayViewHeaderFormat:"MMMM YYYY",extraFormats:!1,stepping:1,minDate:!1,maxDate:!1,useCurrent:!0,collapse:!0,locale:b.locale(),defaultDate:!1,disabledDates:!1,enabledDates:!1,icons:{time:"glyphicon glyphicon-time",date:"glyphicon glyphicon-calendar",up:"glyphicon glyphicon-chevron-up",down:"glyphicon glyphicon-chevron-down",previous:"glyphicon glyphicon-chevron-left",next:"glyphicon glyphicon-chevron-right",today:"glyphicon glyphicon-screenshot",clear:"glyphicon glyphicon-trash",close:"glyphicon glyphicon-remove"},tooltips:{today:"Go to today",clear:"Clear selection",close:"Close the picker",selectMonth:"Select Month",prevMonth:"Previous Month",nextMonth:"Next Month",selectYear:"Select Year",prevYear:"Previous Year",nextYear:"Next Year",selectDecade:"Select Decade",prevDecade:"Previous Decade",nextDecade:"Next Decade",prevCentury:"Previous Century",nextCentury:"Next Century",pickHour:"Pick Hour",incrementHour:"Increment Hour",decrementHour:"Decrement Hour",pickMinute:"Pick Minute",incrementMinute:"Increment Minute",decrementMinute:"Decrement Minute",pickSecond:"Pick Second",incrementSecond:"Increment Second",decrementSecond:"Decrement Second",togglePeriod:"Toggle Period",selectTime:"Select Time"},useStrict:!1,sideBySide:!1,daysOfWeekDisabled:!1,calendarWeeks:!1,viewMode:"days",toolbarPlacement:"default",showTodayButton:!1,showClear:!1,showClose:!1,widgetPositioning:{horizontal:"auto",vertical:"auto"},widgetParent:null,ignoreReadonly:!1,keepOpen:!1,focusOnShow:!0,inline:!1,keepInvalid:!1,datepickerInput:".datepickerinput",keyBinds:{up:function(a){if(a){var b=this.date()||this.getMoment();a.find(".datepicker").is(":visible")?this.date(b.clone().subtract(7,"d")):this.date(b.clone().add(this.stepping(),"m"))}},down:function(a){if(!a)return void this.show();var b=this.date()||this.getMoment();a.find(".datepicker").is(":visible")?this.date(b.clone().add(7,"d")):this.date(b.clone().subtract(this.stepping(),"m"))},"control up":function(a){if(a){var b=this.date()||this.getMoment();a.find(".datepicker").is(":visible")?this.date(b.clone().subtract(1,"y")):this.date(b.clone().add(1,"h"))}},"control down":function(a){if(a){var b=this.date()||this.getMoment();a.find(".datepicker").is(":visible")?this.date(b.clone().add(1,"y")):this.date(b.clone().subtract(1,"h"))}},left:function(a){if(a){var b=this.date()||this.getMoment();a.find(".datepicker").is(":visible")&&this.date(b.clone().subtract(1,"d"))}},right:function(a){if(a){var b=this.date()||this.getMoment();a.find(".datepicker").is(":visible")&&this.date(b.clone().add(1,"d"))}},pageUp:function(a){if(a){var b=this.date()||this.getMoment();a.find(".datepicker").is(":visible")&&this.date(b.clone().subtract(1,"M"))}},pageDown:function(a){if(a){var b=this.date()||this.getMoment();a.find(".datepicker").is(":visible")&&this.date(b.clone().add(1,"M"))}},enter:function(){this.hide()},escape:function(){this.hide()},"control space":function(a){a.find(".timepicker").is(":visible")&&a.find('.btn[data-action="togglePeriod"]').click()},t:function(){this.date(this.getMoment())},"delete":function(){this.clear()}},debug:!1,allowInputToggle:!1,disabledTimeIntervals:!1,disabledHours:!1,enabledHours:!1,viewDate:!1}}); \ No newline at end of file diff --git a/kfet/static/kfet/js/history.js b/kfet/static/kfet/js/history.js new file mode 100644 index 00000000..291c106d --- /dev/null +++ b/kfet/static/kfet/js/history.js @@ -0,0 +1,139 @@ +function KHistory(options={}) { + $.extend(this, KHistory.default_options, options); + + this.$container = $(this.container); + + this.reset = function() { + this.$container.html(''); + }; + + this.addOpeGroup = function(opegroup) { + var $day = this._getOrCreateDay(opegroup['at']); + var $opegroup = this._opeGroupHtml(opegroup); + + $day.after($opegroup); + + var trigramme = opegroup['on_acc_trigramme']; + var is_cof = opegroup['is_cof']; + for (var i=0; i', + template_opegroup: '
', + template_ope: '
', + display_trigramme: true, +} diff --git a/kfet/static/kfet/js/jquery-confirm.js b/kfet/static/kfet/js/jquery-confirm.js new file mode 100644 index 00000000..6f645ccd --- /dev/null +++ b/kfet/static/kfet/js/jquery-confirm.js @@ -0,0 +1,607 @@ +/*! + * jquery-confirm v2.5.1 (http://craftpip.github.io/jquery-confirm/) + * Author: Boniface Pereira + * Website: www.craftpip.com + * Contact: hey@craftpip.com + * + * Copyright 2013-2015 jquery-confirm + * Licensed under MIT (https://github.com/craftpip/jquery-confirm/blob/master/LICENSE) + * + * Modifié par: Aurélien Delobelle + * Contact: aure.delo@gmail.com + * Contenu des modifications: + * Cas où il y a un input avec autofocus dans 'content' -> Fix + */ + +if (typeof jQuery === 'undefined') { + throw new Error('jquery-confirm requires jQuery'); +} + +var jconfirm, Jconfirm; +(function ($) { + "use strict"; + + $.fn.confirm = function (options, option2) { + if (typeof options === 'undefined') options = {}; + if (typeof options === 'string') + options = { + content: options, + title: (option2) ? option2 : false + + }; + /* + * Alias of $.confirm to emulate native confirm() + */ + $(this).each(function () { + var $this = $(this); + $this.on('click', function (e) { + e.preventDefault(); + var jcOption = $.extend({}, options); + if ($this.attr('data-title')) + jcOption['title'] = $this.attr('data-title'); + if ($this.attr('data-content')) + jcOption['content'] = $this.attr('data-content'); + + jcOption['$target'] = $this; + if ($this.attr('href') && !options['confirm']) + jcOption['confirm'] = function () { + location.href = $this.attr('href'); + }; + $.confirm(jcOption); + }); + }); + return $(this); + }; + $.confirm = function (options, option2) { + if (typeof options === 'undefined') options = {}; + if (typeof options === 'string') + options = { + content: options, + title: (option2) ? option2 : false + }; + /* + * Alias of jconfirm + */ + return jconfirm(options); + }; + $.alert = function (options, option2) { + if (typeof options === 'undefined') options = {}; + if (typeof options === 'string') + options = { + content: options, + title: (option2) ? option2 : false + }; + /* + * Alias of jconfirm + */ + options.cancelButton = false; + return jconfirm(options); + }; + $.dialog = function (options, option2) { + if (typeof options === 'undefined') options = {}; + if (typeof options === 'string') + options = { + content: options, + title: (option2) ? option2 : false + }; + /* + * Alias of jconfirm + */ + options.cancelButton = false; + options.confirmButton = false; + options.confirmKeys = [13]; + return jconfirm(options); + }; + jconfirm = function (options) { + if (typeof options === 'undefined') options = {}; + /* + * initial function for calling. + */ + if (jconfirm.defaults) { + /* + * Merge global defaults with plugin defaults + */ + $.extend(jconfirm.pluginDefaults, jconfirm.defaults); + } + /* + * merge options with plugin defaults. + */ + var options = $.extend({}, jconfirm.pluginDefaults, options); + return new Jconfirm(options); + }; + Jconfirm = function (options) { + /* + * constructor function Jconfirm, + * options = user options. + */ + $.extend(this, options); + this._init(); + }; + Jconfirm.prototype = { + _init: function () { + var that = this; + this._rand = Math.round(Math.random() * 99999); + this._buildHTML(); + this._bindEvents(); + setTimeout(function () { + that.open(); + that._watchContent(); + }, 0); + }, + _buildHTML: function () { + var that = this; + /* + * Prefixing animations. + */ + this.animation = 'anim-' + this.animation.toLowerCase(); + this.closeAnimation = 'anim-' + this.closeAnimation.toLowerCase(); + this.theme = 'jconfirm-' + this.theme.toLowerCase(); + if (this.animation == 'anim-none') + this.animationSpeed = 0; + + this._lastFocused = $('body').find(':focus'); + + /* + * Append html. + */ + this.$el = $(this.template).appendTo(this.container).addClass(this.theme); + this.$el.find('.jconfirm-box-container').addClass(this.columnClass); + this.$el.find('.jconfirm-bg').css(this._getCSS(this.animationSpeed, 1)); + this.$el.find('.jconfirm-bg').css('opacity', this.opacity); + this.$b = this.$el.find('.jconfirm-box').css(this._getCSS(this.animationSpeed, this.animationBounce)).addClass(this.animation); + this.$body = this.$b; // alias + + /* + * Add rtl class if rtl option has selected + */ + if (this.rtl) + this.$el.addClass("rtl"); + + this._contentReady = $.Deferred(); + this._modalReady = $.Deferred(); + + /* + * Setup title contents + */ + this.$title = this.$el.find('.title'); + this.contentDiv = this.$el.find('div.content'); + this.$content = this.contentDiv; // alias + this.$contentPane = this.$el.find('.content-pane'); + this.$icon = this.$el.find('.icon-c'); + this.$closeIcon = this.$el.find('.closeIcon'); + this.$contentPane.css(this._getCSS(this.animationSpeed, 1)); + this.setTitle(); + this.setIcon(); + this._setButtons(); + + if (this.closeIconClass) + this.$closeIcon.html(''); + + that._contentHash = this._hash(that.$content.html()); + $.when(this._contentReady, this._modalReady).then(function () { + that.setContent(); + that.setTitle(); + that.setIcon(); + }); + + this._getContent(); + this._imagesLoaded(); + + if (this.autoClose) + this._startCountDown(); + }, + _unwatchContent: function () { + clearInterval(this._timer); + }, + _hash: function () { + return btoa((encodeURIComponent(this.$content.html()))); + }, + _watchContent: function () { + var that = this; + this._timer = setInterval(function () { + var now = that._hash(that.$content.html()); + if (that._contentHash != now) { + that._contentHash = now; + that.setDialogCenter(); + that._imagesLoaded(); + } + }, this.watchInterval); + }, + _bindEvents: function () { + var that = this; + var boxClicked = false; + + this.$el.find('.jconfirm-scrollpane').click(function (e) { + // ignore propagated clicks + if (!boxClicked) { + // background clicked + if (that.backgroundDismiss) { + that.cancel(); + that.close(); + } else { + that.$b.addClass('hilight'); + setTimeout(function () { + that.$b.removeClass('hilight'); + }, 800); + } + } + boxClicked = false; + }); + + this.$el.find('.jconfirm-box').click(function (e) { + boxClicked = true; + }); + + if (this.$confirmButton) { + this.$confirmButton.click(function (e) { + e.preventDefault(); + var r = that.confirm(that.$b); + that._stopCountDown(); + that.onAction('confirm'); + if (typeof r === 'undefined' || r) + that.close(); + }); + } + if (this.$cancelButton) { + this.$cancelButton.click(function (e) { + e.preventDefault(); + var r = that.cancel(that.$b); + that._stopCountDown(); + that.onAction('cancel'); + + if (typeof r === 'undefined' || r) + that.close(); + }); + } + if (this.$closeButton) { + this.$closeButton.click(function (e) { + e.preventDefault(); + that._stopCountDown(); + that.cancel(); + that.onAction('close'); + that.close(); + }); + } + if (this.keyboardEnabled) { + setTimeout(function () { + $(window).on('keyup.' + this._rand, function (e) { + that.reactOnKey(e); + }); + }, 500); + } + + $(window).on('resize.' + this._rand, function () { + that.setDialogCenter(true); + }); + }, + _getCSS: function (speed, bounce) { + return { + '-webkit-transition-duration': speed / 1000 + 's', + 'transition-duration': speed / 1000 + 's', + '-webkit-transition-timing-function': 'cubic-bezier(.36,1.1,.2, ' + bounce + ')', + 'transition-timing-function': 'cubic-bezier(.36,1.1,.2, ' + bounce + ')' + }; + }, + _imagesLoaded: function () { + var that = this; + $.each(this.$content.find('img:not(.loaded)'), function (i, a) { + var interval = setInterval(function () { + var h = $(a).css('height'); + if (h !== '0px') { + $(a).addClass('loaded'); + that.setDialogCenter(); + clearInterval(interval); + } + }, 40); + }) + }, + _setButtons: function () { + /* + * Settings up buttons + */ + this.$btnc = this.$el.find('.buttons'); + if (this.confirmButton && $.trim(this.confirmButton) !== '') { + this.$confirmButton = $('').appendTo(this.$btnc).addClass(this.confirmButtonClass); + } + if (this.cancelButton && $.trim(this.cancelButton) !== '') { + this.$cancelButton = $('').appendTo(this.$btnc).addClass(this.cancelButtonClass); + } + if (!this.confirmButton && !this.cancelButton) { + this.$btnc.hide(); + } + if (!this.confirmButton && !this.cancelButton && this.closeIcon === null) { + this.$closeButton = this.$b.find('.closeIcon').show(); + } + if (this.closeIcon === true) { + this.$closeButton = this.$b.find('.closeIcon').show(); + } + }, + setTitle: function (string) { + this.title = (typeof string !== 'undefined') ? string : this.title; + this.$title.html(this.title || ''); + }, + setIcon: function (iconClass) { + this.title = (typeof string !== 'undefined') ? iconClass : this.title; + this.$icon.html(this.icon ? '' : ''); + }, + setContent: function (string) { + // only set the content on the modal. + var that = this; + this.content = (typeof string == 'undefined') ? this.content : string; + + if (this.content == '') { + this.$content.html(this.content); + this.$contentPane.hide(); + } else { + this.$content.html(this.content); + this.$contentPane.show(); + } + if (this.$content.hasClass('loading')) { + this.$content.removeClass('loading');// it was loading via ajax. + this.$btnc.find('button').prop('disabled', false); + } + }, + _getContent: function (string) { + // get content from remote & stuff. + var that = this; + string = (string) ? string : this.content; + this._isAjax = false; + /* + * Set content. + */ + if (!this.content) { // if the content is falsy + this.content = ''; + this.setContent(this.content); + this._contentReady.reject(); + } else if (typeof this.content === 'string') { + if (this.content.substr(0, 4).toLowerCase() === 'url:') { + this._isAjax = true; + this.$content.addClass('loading'); + this.$btnc.find('button').prop('disabled', true); + var url = this.content.substring(4, this.content.length); + $.get(url).done(function (html) { + that.content = html; + that._contentReady.resolve(); + }).always(function (data, status, xhr) { + if (typeof that.contentLoaded === 'function') + that.contentLoaded(data, status, xhr); + }); + } else { + this.setContent(this.content); + this._contentReady.reject(); + } + } else if (typeof this.content === 'function') { + this.$content.addClass('loading'); + this.$btnc.find('button').attr('disabled', 'disabled'); + var promise = this.content(this); + if (typeof promise !== 'object') { + console.error('The content function must return jquery promise.'); + } else if (typeof promise.always !== 'function') { + console.error('The object returned is not a jquery promise.'); + } else { + this._isAjax = true; + promise.always(function (data, status) { + that._contentReady.resolve(); + }); + } + } else { + console.error('Invalid option for property content, passed: ' + typeof this.content); + } + this.setDialogCenter(); + }, + _stopCountDown: function () { + clearInterval(this.timerInterval); + if (this.$cd) + this.$cd.remove(); + }, + _startCountDown: function () { + var opt = this.autoClose.split('|'); + if (/cancel/.test(opt[0]) && this.type === 'alert') { + return false; + } else if (/confirm|cancel/.test(opt[0])) { + this.$cd = $('').appendTo(this['$' + opt[0] + 'Button']); + var that = this; + that.$cd.parent().click(); + var time = opt[1] / 1000; + this.timerInterval = setInterval(function () { + that.$cd.html(' (' + (time -= 1) + ')'); + if (time === 0) { + that.$cd.html(''); + that.$cd.parent().trigger('click'); + clearInterval(that.timerInterval); + } + }, 1000); + } else { + console.error('Invalid option ' + opt[0] + ', must be confirm/cancel'); + } + }, + reactOnKey: function key(e) { + + /* + * prevent keyup event if the dialog is not last! + */ + var a = $('.jconfirm'); + if (a.eq(a.length - 1)[0] !== this.$el[0]) + return false; + + var key = e.which; + // Do not react if Enter/Space is pressed on input elements + if (this.contentDiv.find(':input').is(':focus') && /13|32/.test(key)) + return false; + + if ($.inArray(key, this.cancelKeys) !== -1) { + /* + * Cancel key pressed. + */ + if (this.$cancelButton) { + this.$cancelButton.click(); + } else { + this.close(); + } + }/* + if ($.inArray(key, this.confirmKeys) !== -1) { + */ + /* + * Confirm key pressed. + */ + /* + if (this.$confirmButton) { + this.$confirmButton.click(); + } + }*/ + }, + setDialogCenter: function () { + if (this.$contentPane.css('display') == 'none') { + var contentHeight = 0; + var paneHeight = 0; + } else { + var contentHeight = this.$content.outerHeight(); + var paneHeight = this.$contentPane.height(); + if (paneHeight == 0) + paneHeight = contentHeight; + } + var off = 100; + var w = this.$content.outerWidth(); + + //var s = '-clip-path: inset(0px 0px '+contentHeight+'px 0px);' + + // 'clip-path: inset(0px 0px '+contentHeight+'px 0px)'; + + this.$content.css({ + 'clip': 'rect(0px ' + (off + w) + 'px ' + contentHeight + 'px -' + off + 'px)' + }); + + this.$contentPane.css({ + 'height': contentHeight + }); + + var windowHeight = $(window).height(); + var boxHeight = this.$b.outerHeight() - paneHeight + contentHeight; + var topMargin = (windowHeight - boxHeight) / 2; + var minMargin = 100; + if (boxHeight > (windowHeight - minMargin)) { + var style = { + 'margin-top': minMargin / 2, + 'margin-bottom': minMargin / 2 + } + $('body').addClass('jconfirm-noscroll'); + } else { + var style = { + 'margin-top': topMargin + } + $('body').removeClass('jconfirm-noscroll'); + } + this.$b.css(style); + }, + close: function () { + var that = this; + + if (this.isClosed()) + return false; + + if (typeof this.onClose === 'function') + this.onClose(); + + this._unwatchContent(); + that._lastFocused.focus(); + + //this.observer.disconnect(); + /* + unbind the window resize & keyup event. + */ + $(window).unbind('resize.' + this._rand); + if (this.keyboardEnabled) + $(window).unbind('keyup.' + this._rand); + + that.$el.find('.jconfirm-bg').removeClass('seen'); + $('body').removeClass('jconfirm-noscroll'); + + this.$b.addClass(this.closeAnimation); + var closeTimer = (this.closeAnimation == 'anim-none') ? 0 : this.animationSpeed; + setTimeout(function () { + that.$el.remove(); + }, closeTimer * 25 / 100); + + jconfirm.record.closed += 1; + jconfirm.record.currentlyOpen -= 1; + + return true; + }, + open: function () { + var that = this; + if (this.isClosed()) + return false; + + that.$el.find('.jconfirm-bg').addClass('seen'); + this.$b.removeClass(this.animation); + jconfirm.record.opened += 1; + jconfirm.record.currentlyOpen += 1; + if (typeof this.onOpen === 'function') + this.onOpen(); + + var jcr = 'jconfirm-box' + this._rand; + this.$b.attr('aria-labelledby', jcr).attr('tabindex', -1).focus(); + this.$b.find('input[autofocus]:visible:first').focus(); + if (this.$title) + this.$title.attr('id', jcr); else if (this.$content) + this.$content.attr('id', jcr); + + setTimeout(function () { + that.$b.css({ + 'transition-property': that.$b.css('transition-property') + ', margin' + }); + that._modalReady.resolve(); + }, this.animationSpeed); + + return true; + }, + isClosed: function () { + return this.$el.css('display') === ''; + } + }; + + jconfirm.pluginDefaults = { + template: '
', + title: 'Hello', + content: 'Are you sure to continue?', + contentLoaded: function () { + }, + icon: '', + opacity: 0.2, + confirmButton: 'Okay', + cancelButton: 'Close', + confirmButtonClass: 'btn-default', + cancelButtonClass: 'btn-default', + theme: 'white', + animation: 'zoom', + closeAnimation: 'scale', + animationSpeed: 500, + animationBounce: 1.2, + keyboardEnabled: false, + rtl: false, + confirmKeys: [13], // ENTER key + cancelKeys: [27], // ESC key + container: 'body', + confirm: function () { + }, + cancel: function () { + }, + backgroundDismiss: false, + autoClose: false, + closeIcon: null, + closeIconClass: false, + watchInterval: 100, + columnClass: 'col-md-4 col-md-offset-4 col-sm-6 col-sm-offset-3 col-xs-10 col-xs-offset-1', + onOpen: function () { + }, + onClose: function () { + }, + onAction: function () { + } + }; + + jconfirm.record = { + opened: 0, + closed: 0, + currentlyOpen: 0 + }; +})(jQuery); diff --git a/kfet/static/kfet/js/jquery-ui.min.js b/kfet/static/kfet/js/jquery-ui.min.js new file mode 100644 index 00000000..03fffa75 --- /dev/null +++ b/kfet/static/kfet/js/jquery-ui.min.js @@ -0,0 +1,6 @@ +/*! jQuery UI - v1.12.0 - 2016-08-17 +* http://jqueryui.com +* Includes: widget.js, widgets/selectable.js, widgets/mouse.js +* Copyright jQuery Foundation and other contributors; Licensed MIT */ + +(function(t){"function"==typeof define&&define.amd?define(["jquery"],t):t(jQuery)})(function(t){t.ui=t.ui||{},t.ui.version="1.12.0";var e=0,i=Array.prototype.slice;t.cleanData=function(e){return function(i){var s,n,o;for(o=0;null!=(n=i[o]);o++)try{s=t._data(n,"events"),s&&s.remove&&t(n).triggerHandler("remove")}catch(a){}e(i)}}(t.cleanData),t.widget=function(e,i,s){var n,o,a,r={},l=e.split(".")[0];e=e.split(".")[1];var h=l+"-"+e;return s||(s=i,i=t.Widget),t.isArray(s)&&(s=t.extend.apply(null,[{}].concat(s))),t.expr[":"][h.toLowerCase()]=function(e){return!!t.data(e,h)},t[l]=t[l]||{},n=t[l][e],o=t[l][e]=function(t,e){return this._createWidget?(arguments.length&&this._createWidget(t,e),void 0):new o(t,e)},t.extend(o,n,{version:s.version,_proto:t.extend({},s),_childConstructors:[]}),a=new i,a.options=t.widget.extend({},a.options),t.each(s,function(e,s){return t.isFunction(s)?(r[e]=function(){function t(){return i.prototype[e].apply(this,arguments)}function n(t){return i.prototype[e].apply(this,t)}return function(){var e,i=this._super,o=this._superApply;return this._super=t,this._superApply=n,e=s.apply(this,arguments),this._super=i,this._superApply=o,e}}(),void 0):(r[e]=s,void 0)}),o.prototype=t.widget.extend(a,{widgetEventPrefix:n?a.widgetEventPrefix||e:e},r,{constructor:o,namespace:l,widgetName:e,widgetFullName:h}),n?(t.each(n._childConstructors,function(e,i){var s=i.prototype;t.widget(s.namespace+"."+s.widgetName,o,i._proto)}),delete n._childConstructors):i._childConstructors.push(o),t.widget.bridge(e,o),o},t.widget.extend=function(e){for(var s,n,o=i.call(arguments,1),a=0,r=o.length;r>a;a++)for(s in o[a])n=o[a][s],o[a].hasOwnProperty(s)&&void 0!==n&&(e[s]=t.isPlainObject(n)?t.isPlainObject(e[s])?t.widget.extend({},e[s],n):t.widget.extend({},n):n);return e},t.widget.bridge=function(e,s){var n=s.prototype.widgetFullName||e;t.fn[e]=function(o){var a="string"==typeof o,r=i.call(arguments,1),l=this;return a?this.each(function(){var i,s=t.data(this,n);return"instance"===o?(l=s,!1):s?t.isFunction(s[o])&&"_"!==o.charAt(0)?(i=s[o].apply(s,r),i!==s&&void 0!==i?(l=i&&i.jquery?l.pushStack(i.get()):i,!1):void 0):t.error("no such method '"+o+"' for "+e+" widget instance"):t.error("cannot call methods on "+e+" prior to initialization; "+"attempted to call method '"+o+"'")}):(r.length&&(o=t.widget.extend.apply(null,[o].concat(r))),this.each(function(){var e=t.data(this,n);e?(e.option(o||{}),e._init&&e._init()):t.data(this,n,new s(o,this))})),l}},t.Widget=function(){},t.Widget._childConstructors=[],t.Widget.prototype={widgetName:"widget",widgetEventPrefix:"",defaultElement:"
",options:{classes:{},disabled:!1,create:null},_createWidget:function(i,s){s=t(s||this.defaultElement||this)[0],this.element=t(s),this.uuid=e++,this.eventNamespace="."+this.widgetName+this.uuid,this.bindings=t(),this.hoverable=t(),this.focusable=t(),this.classesElementLookup={},s!==this&&(t.data(s,this.widgetFullName,this),this._on(!0,this.element,{remove:function(t){t.target===s&&this.destroy()}}),this.document=t(s.style?s.ownerDocument:s.document||s),this.window=t(this.document[0].defaultView||this.document[0].parentWindow)),this.options=t.widget.extend({},this.options,this._getCreateOptions(),i),this._create(),this.options.disabled&&this._setOptionDisabled(this.options.disabled),this._trigger("create",null,this._getCreateEventData()),this._init()},_getCreateOptions:function(){return{}},_getCreateEventData:t.noop,_create:t.noop,_init:t.noop,destroy:function(){var e=this;this._destroy(),t.each(this.classesElementLookup,function(t,i){e._removeClass(i,t)}),this.element.off(this.eventNamespace).removeData(this.widgetFullName),this.widget().off(this.eventNamespace).removeAttr("aria-disabled"),this.bindings.off(this.eventNamespace)},_destroy:t.noop,widget:function(){return this.element},option:function(e,i){var s,n,o,a=e;if(0===arguments.length)return t.widget.extend({},this.options);if("string"==typeof e)if(a={},s=e.split("."),e=s.shift(),s.length){for(n=a[e]=t.widget.extend({},this.options[e]),o=0;s.length-1>o;o++)n[s[o]]=n[s[o]]||{},n=n[s[o]];if(e=s.pop(),1===arguments.length)return void 0===n[e]?null:n[e];n[e]=i}else{if(1===arguments.length)return void 0===this.options[e]?null:this.options[e];a[e]=i}return this._setOptions(a),this},_setOptions:function(t){var e;for(e in t)this._setOption(e,t[e]);return this},_setOption:function(t,e){return"classes"===t&&this._setOptionClasses(e),this.options[t]=e,"disabled"===t&&this._setOptionDisabled(e),this},_setOptionClasses:function(e){var i,s,n;for(i in e)n=this.classesElementLookup[i],e[i]!==this.options.classes[i]&&n&&n.length&&(s=t(n.get()),this._removeClass(n,i),s.addClass(this._classes({element:s,keys:i,classes:e,add:!0})))},_setOptionDisabled:function(t){this._toggleClass(this.widget(),this.widgetFullName+"-disabled",null,!!t),t&&(this._removeClass(this.hoverable,null,"ui-state-hover"),this._removeClass(this.focusable,null,"ui-state-focus"))},enable:function(){return this._setOptions({disabled:!1})},disable:function(){return this._setOptions({disabled:!0})},_classes:function(e){function i(i,o){var a,r;for(r=0;i.length>r;r++)a=n.classesElementLookup[i[r]]||t(),a=e.add?t(t.unique(a.get().concat(e.element.get()))):t(a.not(e.element).get()),n.classesElementLookup[i[r]]=a,s.push(i[r]),o&&e.classes[i[r]]&&s.push(e.classes[i[r]])}var s=[],n=this;return e=t.extend({element:this.element,classes:this.options.classes||{}},e),e.keys&&i(e.keys.match(/\S+/g)||[],!0),e.extra&&i(e.extra.match(/\S+/g)||[]),s.join(" ")},_removeClass:function(t,e,i){return this._toggleClass(t,e,i,!1)},_addClass:function(t,e,i){return this._toggleClass(t,e,i,!0)},_toggleClass:function(t,e,i,s){s="boolean"==typeof s?s:i;var n="string"==typeof t||null===t,o={extra:n?e:i,keys:n?t:e,element:n?this.element:t,add:s};return o.element.toggleClass(this._classes(o),s),this},_on:function(e,i,s){var n,o=this;"boolean"!=typeof e&&(s=i,i=e,e=!1),s?(i=n=t(i),this.bindings=this.bindings.add(i)):(s=i,i=this.element,n=this.widget()),t.each(s,function(s,a){function r(){return e||o.options.disabled!==!0&&!t(this).hasClass("ui-state-disabled")?("string"==typeof a?o[a]:a).apply(o,arguments):void 0}"string"!=typeof a&&(r.guid=a.guid=a.guid||r.guid||t.guid++);var l=s.match(/^([\w:-]*)\s*(.*)$/),h=l[1]+o.eventNamespace,c=l[2];c?n.on(h,c,r):i.on(h,r)})},_off:function(e,i){i=(i||"").split(" ").join(this.eventNamespace+" ")+this.eventNamespace,e.off(i).off(i),this.bindings=t(this.bindings.not(e).get()),this.focusable=t(this.focusable.not(e).get()),this.hoverable=t(this.hoverable.not(e).get())},_delay:function(t,e){function i(){return("string"==typeof t?s[t]:t).apply(s,arguments)}var s=this;return setTimeout(i,e||0)},_hoverable:function(e){this.hoverable=this.hoverable.add(e),this._on(e,{mouseenter:function(e){this._addClass(t(e.currentTarget),null,"ui-state-hover")},mouseleave:function(e){this._removeClass(t(e.currentTarget),null,"ui-state-hover")}})},_focusable:function(e){this.focusable=this.focusable.add(e),this._on(e,{focusin:function(e){this._addClass(t(e.currentTarget),null,"ui-state-focus")},focusout:function(e){this._removeClass(t(e.currentTarget),null,"ui-state-focus")}})},_trigger:function(e,i,s){var n,o,a=this.options[e];if(s=s||{},i=t.Event(i),i.type=(e===this.widgetEventPrefix?e:this.widgetEventPrefix+e).toLowerCase(),i.target=this.element[0],o=i.originalEvent)for(n in o)n in i||(i[n]=o[n]);return this.element.trigger(i,s),!(t.isFunction(a)&&a.apply(this.element[0],[i].concat(s))===!1||i.isDefaultPrevented())}},t.each({show:"fadeIn",hide:"fadeOut"},function(e,i){t.Widget.prototype["_"+e]=function(s,n,o){"string"==typeof n&&(n={effect:n});var a,r=n?n===!0||"number"==typeof n?i:n.effect||i:e;n=n||{},"number"==typeof n&&(n={duration:n}),a=!t.isEmptyObject(n),n.complete=o,n.delay&&s.delay(n.delay),a&&t.effects&&t.effects.effect[r]?s[e](n):r!==e&&s[r]?s[r](n.duration,n.easing,o):s.queue(function(i){t(this)[e](),o&&o.call(s[0]),i()})}}),t.widget,t.ui.ie=!!/msie [\w.]+/.exec(navigator.userAgent.toLowerCase());var s=!1;t(document).on("mouseup",function(){s=!1}),t.widget("ui.mouse",{version:"1.12.0",options:{cancel:"input, textarea, button, select, option",distance:1,delay:0},_mouseInit:function(){var e=this;this.element.on("mousedown."+this.widgetName,function(t){return e._mouseDown(t)}).on("click."+this.widgetName,function(i){return!0===t.data(i.target,e.widgetName+".preventClickEvent")?(t.removeData(i.target,e.widgetName+".preventClickEvent"),i.stopImmediatePropagation(),!1):void 0}),this.started=!1},_mouseDestroy:function(){this.element.off("."+this.widgetName),this._mouseMoveDelegate&&this.document.off("mousemove."+this.widgetName,this._mouseMoveDelegate).off("mouseup."+this.widgetName,this._mouseUpDelegate)},_mouseDown:function(e){if(!s){this._mouseMoved=!1,this._mouseStarted&&this._mouseUp(e),this._mouseDownEvent=e;var i=this,n=1===e.which,o="string"==typeof this.options.cancel&&e.target.nodeName?t(e.target).closest(this.options.cancel).length:!1;return n&&!o&&this._mouseCapture(e)?(this.mouseDelayMet=!this.options.delay,this.mouseDelayMet||(this._mouseDelayTimer=setTimeout(function(){i.mouseDelayMet=!0},this.options.delay)),this._mouseDistanceMet(e)&&this._mouseDelayMet(e)&&(this._mouseStarted=this._mouseStart(e)!==!1,!this._mouseStarted)?(e.preventDefault(),!0):(!0===t.data(e.target,this.widgetName+".preventClickEvent")&&t.removeData(e.target,this.widgetName+".preventClickEvent"),this._mouseMoveDelegate=function(t){return i._mouseMove(t)},this._mouseUpDelegate=function(t){return i._mouseUp(t)},this.document.on("mousemove."+this.widgetName,this._mouseMoveDelegate).on("mouseup."+this.widgetName,this._mouseUpDelegate),e.preventDefault(),s=!0,!0)):!0}},_mouseMove:function(e){if(this._mouseMoved){if(t.ui.ie&&(!document.documentMode||9>document.documentMode)&&!e.button)return this._mouseUp(e);if(!e.which)if(e.originalEvent.altKey||e.originalEvent.ctrlKey||e.originalEvent.metaKey||e.originalEvent.shiftKey)this.ignoreMissingWhich=!0;else if(!this.ignoreMissingWhich)return this._mouseUp(e)}return(e.which||e.button)&&(this._mouseMoved=!0),this._mouseStarted?(this._mouseDrag(e),e.preventDefault()):(this._mouseDistanceMet(e)&&this._mouseDelayMet(e)&&(this._mouseStarted=this._mouseStart(this._mouseDownEvent,e)!==!1,this._mouseStarted?this._mouseDrag(e):this._mouseUp(e)),!this._mouseStarted)},_mouseUp:function(e){this.document.off("mousemove."+this.widgetName,this._mouseMoveDelegate).off("mouseup."+this.widgetName,this._mouseUpDelegate),this._mouseStarted&&(this._mouseStarted=!1,e.target===this._mouseDownEvent.target&&t.data(e.target,this.widgetName+".preventClickEvent",!0),this._mouseStop(e)),this._mouseDelayTimer&&(clearTimeout(this._mouseDelayTimer),delete this._mouseDelayTimer),this.ignoreMissingWhich=!1,s=!1,e.preventDefault()},_mouseDistanceMet:function(t){return Math.max(Math.abs(this._mouseDownEvent.pageX-t.pageX),Math.abs(this._mouseDownEvent.pageY-t.pageY))>=this.options.distance},_mouseDelayMet:function(){return this.mouseDelayMet},_mouseStart:function(){},_mouseDrag:function(){},_mouseStop:function(){},_mouseCapture:function(){return!0}}),t.widget("ui.selectable",t.ui.mouse,{version:"1.12.0",options:{appendTo:"body",autoRefresh:!0,distance:0,filter:"*",tolerance:"touch",selected:null,selecting:null,start:null,stop:null,unselected:null,unselecting:null},_create:function(){var e=this;this._addClass("ui-selectable"),this.dragged=!1,this.refresh=function(){e.elementPos=t(e.element[0]).offset(),e.selectees=t(e.options.filter,e.element[0]),e._addClass(e.selectees,"ui-selectee"),e.selectees.each(function(){var i=t(this),s=i.offset(),n={left:s.left-e.elementPos.left,top:s.top-e.elementPos.top};t.data(this,"selectable-item",{element:this,$element:i,left:n.left,top:n.top,right:n.left+i.outerWidth(),bottom:n.top+i.outerHeight(),startselected:!1,selected:i.hasClass("ui-selected"),selecting:i.hasClass("ui-selecting"),unselecting:i.hasClass("ui-unselecting")})})},this.refresh(),this._mouseInit(),this.helper=t("
"),this._addClass(this.helper,"ui-selectable-helper")},_destroy:function(){this.selectees.removeData("selectable-item"),this._mouseDestroy()},_mouseStart:function(e){var i=this,s=this.options;this.opos=[e.pageX,e.pageY],this.elementPos=t(this.element[0]).offset(),this.options.disabled||(this.selectees=t(s.filter,this.element[0]),this._trigger("start",e),t(s.appendTo).append(this.helper),this.helper.css({left:e.pageX,top:e.pageY,width:0,height:0}),s.autoRefresh&&this.refresh(),this.selectees.filter(".ui-selected").each(function(){var s=t.data(this,"selectable-item");s.startselected=!0,e.metaKey||e.ctrlKey||(i._removeClass(s.$element,"ui-selected"),s.selected=!1,i._addClass(s.$element,"ui-unselecting"),s.unselecting=!0,i._trigger("unselecting",e,{unselecting:s.element}))}),t(e.target).parents().addBack().each(function(){var s,n=t.data(this,"selectable-item");return n?(s=!e.metaKey&&!e.ctrlKey||!n.$element.hasClass("ui-selected"),i._removeClass(n.$element,s?"ui-unselecting":"ui-selected")._addClass(n.$element,s?"ui-selecting":"ui-unselecting"),n.unselecting=!s,n.selecting=s,n.selected=s,s?i._trigger("selecting",e,{selecting:n.element}):i._trigger("unselecting",e,{unselecting:n.element}),!1):void 0}))},_mouseDrag:function(e){if(this.dragged=!0,!this.options.disabled){var i,s=this,n=this.options,o=this.opos[0],a=this.opos[1],r=e.pageX,l=e.pageY;return o>r&&(i=r,r=o,o=i),a>l&&(i=l,l=a,a=i),this.helper.css({left:o,top:a,width:r-o,height:l-a}),this.selectees.each(function(){var i=t.data(this,"selectable-item"),h=!1,c={};i&&i.element!==s.element[0]&&(c.left=i.left+s.elementPos.left,c.right=i.right+s.elementPos.left,c.top=i.top+s.elementPos.top,c.bottom=i.bottom+s.elementPos.top,"touch"===n.tolerance?h=!(c.left>r||o>c.right||c.top>l||a>c.bottom):"fit"===n.tolerance&&(h=c.left>o&&r>c.right&&c.top>a&&l>c.bottom),h?(i.selected&&(s._removeClass(i.$element,"ui-selected"),i.selected=!1),i.unselecting&&(s._removeClass(i.$element,"ui-unselecting"),i.unselecting=!1),i.selecting||(s._addClass(i.$element,"ui-selecting"),i.selecting=!0,s._trigger("selecting",e,{selecting:i.element}))):(i.selecting&&((e.metaKey||e.ctrlKey)&&i.startselected?(s._removeClass(i.$element,"ui-selecting"),i.selecting=!1,s._addClass(i.$element,"ui-selected"),i.selected=!0):(s._removeClass(i.$element,"ui-selecting"),i.selecting=!1,i.startselected&&(s._addClass(i.$element,"ui-unselecting"),i.unselecting=!0),s._trigger("unselecting",e,{unselecting:i.element}))),i.selected&&(e.metaKey||e.ctrlKey||i.startselected||(s._removeClass(i.$element,"ui-selected"),i.selected=!1,s._addClass(i.$element,"ui-unselecting"),i.unselecting=!0,s._trigger("unselecting",e,{unselecting:i.element})))))}),!1}},_mouseStop:function(e){var i=this;return this.dragged=!1,t(".ui-unselecting",this.element[0]).each(function(){var s=t.data(this,"selectable-item");i._removeClass(s.$element,"ui-unselecting"),s.unselecting=!1,s.startselected=!1,i._trigger("unselected",e,{unselected:s.element})}),t(".ui-selecting",this.element[0]).each(function(){var s=t.data(this,"selectable-item");i._removeClass(s.$element,"ui-selecting")._addClass(s.$element,"ui-selected"),s.selecting=!1,s.selected=!0,s.startselected=!0,i._trigger("selected",e,{selected:s.element})}),this._trigger("stop",e),this.helper.remove(),!1}})}); \ No newline at end of file diff --git a/kfet/static/kfet/js/js.cookie.js b/kfet/static/kfet/js/js.cookie.js new file mode 100644 index 00000000..7f3dffde --- /dev/null +++ b/kfet/static/kfet/js/js.cookie.js @@ -0,0 +1,151 @@ +/*! + * JavaScript Cookie v2.1.2 + * https://github.com/js-cookie/js-cookie + * + * Copyright 2006, 2015 Klaus Hartl & Fagner Brack + * Released under the MIT license + */ +;(function (factory) { + if (typeof define === 'function' && define.amd) { + define(factory); + } else if (typeof exports === 'object') { + module.exports = factory(); + } else { + var OldCookies = window.Cookies; + var api = window.Cookies = factory(); + api.noConflict = function () { + window.Cookies = OldCookies; + return api; + }; + } +}(function () { + function extend () { + var i = 0; + var result = {}; + for (; i < arguments.length; i++) { + var attributes = arguments[ i ]; + for (var key in attributes) { + result[key] = attributes[key]; + } + } + return result; + } + + function init (converter) { + function api (key, value, attributes) { + var result; + if (typeof document === 'undefined') { + return; + } + + // Write + + if (arguments.length > 1) { + attributes = extend({ + path: '/' + }, api.defaults, attributes); + + if (typeof attributes.expires === 'number') { + var expires = new Date(); + expires.setMilliseconds(expires.getMilliseconds() + attributes.expires * 864e+5); + attributes.expires = expires; + } + + try { + result = JSON.stringify(value); + if (/^[\{\[]/.test(result)) { + value = result; + } + } catch (e) {} + + if (!converter.write) { + value = encodeURIComponent(String(value)) + .replace(/%(23|24|26|2B|3A|3C|3E|3D|2F|3F|40|5B|5D|5E|60|7B|7D|7C)/g, decodeURIComponent); + } else { + value = converter.write(value, key); + } + + key = encodeURIComponent(String(key)); + key = key.replace(/%(23|24|26|2B|5E|60|7C)/g, decodeURIComponent); + key = key.replace(/[\(\)]/g, escape); + + return (document.cookie = [ + key, '=', value, + attributes.expires && '; expires=' + attributes.expires.toUTCString(), // use expires attribute, max-age is not supported by IE + attributes.path && '; path=' + attributes.path, + attributes.domain && '; domain=' + attributes.domain, + attributes.secure ? '; secure' : '' + ].join('')); + } + + // Read + + if (!key) { + result = {}; + } + + // To prevent the for loop in the first place assign an empty array + // in case there are no cookies at all. Also prevents odd result when + // calling "get()" + var cookies = document.cookie ? document.cookie.split('; ') : []; + var rdecode = /(%[0-9A-Z]{2})+/g; + var i = 0; + + for (; i < cookies.length; i++) { + var parts = cookies[i].split('='); + var cookie = parts.slice(1).join('='); + + if (cookie.charAt(0) === '"') { + cookie = cookie.slice(1, -1); + } + + try { + var name = parts[0].replace(rdecode, decodeURIComponent); + cookie = converter.read ? + converter.read(cookie, name) : converter(cookie, name) || + cookie.replace(rdecode, decodeURIComponent); + + if (this.json) { + try { + cookie = JSON.parse(cookie); + } catch (e) {} + } + + if (key === name) { + result = cookie; + break; + } + + if (!key) { + result[name] = cookie; + } + } catch (e) {} + } + + return result; + } + + api.set = api; + api.get = function (key) { + return api(key); + }; + api.getJSON = function () { + return api.apply({ + json: true + }, [].slice.call(arguments)); + }; + api.defaults = {}; + + api.remove = function (key, attributes) { + api(key, '', extend(attributes, { + expires: -1 + })); + }; + + api.withConverter = init; + + return api; + } + + return init(function () {}); +})); diff --git a/kfet/static/kfet/js/kfet.js b/kfet/static/kfet/js/kfet.js new file mode 100644 index 00000000..7aa1d963 --- /dev/null +++ b/kfet/static/kfet/js/kfet.js @@ -0,0 +1,113 @@ +$(document).ready(function() { + $(window).scroll(function() { + if ($(window).width() >= 768 && $(this).scrollTop() > 72.6) { + $('.col-content-left').css({'position':'fixed', 'top':'50px'}); + $('.col-content-right').addClass('col-sm-offset-4 col-md-offset-3'); + } else { + $('.col-content-left').css({'position':'relative', 'top':'0'}); + $('.col-content-right').removeClass('col-sm-offset-4 col-md-offset-3'); + } + }); + + if (typeof Cookies !== 'undefined') { + // Retrieving csrf token + csrftoken = Cookies.get('csrftoken'); + // Appending csrf token to ajax post requests + function csrfSafeMethod(method) { + // these HTTP methods do not require CSRF protection + return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method)); + } + $.ajaxSetup({ + beforeSend: function(xhr, settings) { + if (!csrfSafeMethod(settings.type) && !this.crossDomain) { + xhr.setRequestHeader("X-CSRFToken", csrftoken); + } + } + }); + } +}); + +function dateUTCToParis(date) { + return moment.tz(date, 'UTC').tz('Europe/Paris'); +} + +function amountDisplay(amount, is_cof=false, tri='') { + if (tri == 'LIQ') + return (- amount).toFixed(2) +'€'; + return amountToUKF(amount, is_cof); +} + +function amountToUKF(amount, is_cof=false) { + var coef_cof = is_cof ? 1 + settings['subvention_cof'] / 100 : 1; + return Math.round(amount * coef_cof * 10); +} + +function isValidTrigramme(trigramme) { + var pattern = /^[^a-z]{3}$/; + return trigramme.match(pattern); +} + +function getErrorsHtml(data) { + var content = ''; + if ('operation_group' in data['errors']) { + content += 'Général'; + content += '
    '; + if (data['errors']['operation_group'].indexOf('on_acc') != -1) + content += '
  • Pas de compte sélectionné
  • '; + if (data['errors']['operation_group'].indexOf('checkout') != -1) + content += '
  • Pas de caisse sélectionnée
  • '; + content += '
'; + } + if ('missing_perms' in data['errors']) { + content += 'Permissions manquantes'; + content += '
    '; + for (var i=0; i'; + content += '
'; + } + if ('negative' in data['errors']) { + var url_base = "{% url 'kfet.account.update' LIQ}"; + url_base = base_url(0, url_base.length-8); + for (var i=0; iAutorisation de négatif requise pour '+data['errors']['negative'][i]+''; + } + } + if ('addcost' in data['errors']) { + content += '
    '; + if (data['errors']['addcost'].indexOf('__all__') != -1) + content += '
  • Compte invalide
  • '; + if (data['errors']['addcost'].indexOf('amount') != -1) + content += '
  • Montant invalide
  • '; + content += '
'; + } + return content; +} + +function requestAuth(data, callback, focus_next = null) { + var content = getErrorsHtml(data); + content += '', + $.confirm({ + title: 'Authentification requise', + content: content, + backgroundDismiss: true, + animation:'top', + closeAnimation:'bottom', + keyboardEnabled: true, + confirm: function() { + var password = this.$content.find('input').val(); + callback(password); + }, + onOpen: function() { + var that = this; + this.$content.find('input').on('keypress', function(e) { + if (e.keyCode == 13) + that.$confirmButton.click(); + }); + }, + onClose: function() { + if (focus_next) + this._lastFocused = focus_next; + } + }); +} + diff --git a/kfet/static/kfet/js/moment-fr.js b/kfet/static/kfet/js/moment-fr.js new file mode 100644 index 00000000..143fca16 --- /dev/null +++ b/kfet/static/kfet/js/moment-fr.js @@ -0,0 +1,64 @@ +//! moment.js locale configuration +//! locale : French [fr] +//! author : John Fischer : https://github.com/jfroffice + +;(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' + && typeof require === 'function' ? factory(require('../moment')) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : + factory(global.moment) +}(this, function (moment) { 'use strict'; + + + var fr = moment.defineLocale('fr', { + months : 'Janvier_Février_Mars_Avril_Mai_Juin_Juillet_Août_Septembre_Octobre_Novembre_Décembre'.split('_'), + monthsShort : 'janv._févr._mars_avr._mai_juin_juil._août_sept._oct._nov._déc.'.split('_'), + monthsParseExact : true, + weekdays : 'Dimanche_Lundi_Mardi_Mercredi_Jeudi_Vendredi_Samedi'.split('_'), + weekdaysShort : 'dim._lun._mar._mer._jeu._ven._sam.'.split('_'), + weekdaysMin : 'Di_Lu_Ma_Me_Je_Ve_Sa'.split('_'), + weekdaysParseExact : true, + longDateFormat : { + LT : 'HH:mm', + LTS : 'HH:mm:ss', + L : 'DD/MM/YYYY', + LL : 'D MMMM YYYY', + LLL : 'D MMMM YYYY HH:mm', + LLLL : 'dddd D MMMM YYYY HH:mm' + }, + calendar : { + sameDay: '[Aujourd\'hui à] LT', + nextDay: '[Demain à] LT', + nextWeek: 'dddd [à] LT', + lastDay: '[Hier à] LT', + lastWeek: 'dddd [dernier à] LT', + sameElse: 'L' + }, + relativeTime : { + future : 'dans %s', + past : 'il y a %s', + s : 'quelques secondes', + m : 'une minute', + mm : '%d minutes', + h : 'une heure', + hh : '%d heures', + d : 'un jour', + dd : '%d jours', + M : 'un mois', + MM : '%d mois', + y : 'un an', + yy : '%d ans' + }, + ordinalParse: /\d{1,2}(er|)/, + ordinal : function (number) { + return number + (number === 1 ? 'er' : ''); + }, + week : { + dow : 1, // Monday is the first day of the week. + doy : 4 // The week that contains Jan 4th is the first week of the year. + } + }); + + return fr; + +})); diff --git a/kfet/static/kfet/js/moment-timezone-with-data-2010-2020.js b/kfet/static/kfet/js/moment-timezone-with-data-2010-2020.js new file mode 100644 index 00000000..7e58bb50 --- /dev/null +++ b/kfet/static/kfet/js/moment-timezone-with-data-2010-2020.js @@ -0,0 +1,1196 @@ +//! moment-timezone.js +//! version : 0.5.5 +//! author : Tim Wood +//! license : MIT +//! github.com/moment/moment-timezone + +(function (root, factory) { + "use strict"; + + /*global define*/ + if (typeof define === 'function' && define.amd) { + define(['moment'], factory); // AMD + } else if (typeof module === 'object' && module.exports) { + module.exports = factory(require('moment')); // Node + } else { + factory(root.moment); // Browser + } +}(this, function (moment) { + "use strict"; + + // Do not load moment-timezone a second time. + if (moment.tz !== undefined) { + logError('Moment Timezone ' + moment.tz.version + ' was already loaded ' + (moment.tz.dataVersion ? 'with data from ' : 'without any data') + moment.tz.dataVersion); + return moment; + } + + var VERSION = "0.5.5", + zones = {}, + links = {}, + names = {}, + guesses = {}, + cachedGuess, + + momentVersion = moment.version.split('.'), + major = +momentVersion[0], + minor = +momentVersion[1]; + + // Moment.js version check + if (major < 2 || (major === 2 && minor < 6)) { + logError('Moment Timezone requires Moment.js >= 2.6.0. You are using Moment.js ' + moment.version + '. See momentjs.com'); + } + + /************************************ + Unpacking + ************************************/ + + function charCodeToInt(charCode) { + if (charCode > 96) { + return charCode - 87; + } else if (charCode > 64) { + return charCode - 29; + } + return charCode - 48; + } + + function unpackBase60(string) { + var i = 0, + parts = string.split('.'), + whole = parts[0], + fractional = parts[1] || '', + multiplier = 1, + num, + out = 0, + sign = 1; + + // handle negative numbers + if (string.charCodeAt(0) === 45) { + i = 1; + sign = -1; + } + + // handle digits before the decimal + for (i; i < whole.length; i++) { + num = charCodeToInt(whole.charCodeAt(i)); + out = 60 * out + num; + } + + // handle digits after the decimal + for (i = 0; i < fractional.length; i++) { + multiplier = multiplier / 60; + num = charCodeToInt(fractional.charCodeAt(i)); + out += num * multiplier; + } + + return out * sign; + } + + function arrayToInt (array) { + for (var i = 0; i < array.length; i++) { + array[i] = unpackBase60(array[i]); + } + } + + function intToUntil (array, length) { + for (var i = 0; i < length; i++) { + array[i] = Math.round((array[i - 1] || 0) + (array[i] * 60000)); // minutes to milliseconds + } + + array[length - 1] = Infinity; + } + + function mapIndices (source, indices) { + var out = [], i; + + for (i = 0; i < indices.length; i++) { + out[i] = source[indices[i]]; + } + + return out; + } + + function unpack (string) { + var data = string.split('|'), + offsets = data[2].split(' '), + indices = data[3].split(''), + untils = data[4].split(' '); + + arrayToInt(offsets); + arrayToInt(indices); + arrayToInt(untils); + + intToUntil(untils, indices.length); + + return { + name : data[0], + abbrs : mapIndices(data[1].split(' '), indices), + offsets : mapIndices(offsets, indices), + untils : untils, + population : data[5] | 0 + }; + } + + /************************************ + Zone object + ************************************/ + + function Zone (packedString) { + if (packedString) { + this._set(unpack(packedString)); + } + } + + Zone.prototype = { + _set : function (unpacked) { + this.name = unpacked.name; + this.abbrs = unpacked.abbrs; + this.untils = unpacked.untils; + this.offsets = unpacked.offsets; + this.population = unpacked.population; + }, + + _index : function (timestamp) { + var target = +timestamp, + untils = this.untils, + i; + + for (i = 0; i < untils.length; i++) { + if (target < untils[i]) { + return i; + } + } + }, + + parse : function (timestamp) { + var target = +timestamp, + offsets = this.offsets, + untils = this.untils, + max = untils.length - 1, + offset, offsetNext, offsetPrev, i; + + for (i = 0; i < max; i++) { + offset = offsets[i]; + offsetNext = offsets[i + 1]; + offsetPrev = offsets[i ? i - 1 : i]; + + if (offset < offsetNext && tz.moveAmbiguousForward) { + offset = offsetNext; + } else if (offset > offsetPrev && tz.moveInvalidForward) { + offset = offsetPrev; + } + + if (target < untils[i] - (offset * 60000)) { + return offsets[i]; + } + } + + return offsets[max]; + }, + + abbr : function (mom) { + return this.abbrs[this._index(mom)]; + }, + + offset : function (mom) { + return this.offsets[this._index(mom)]; + } + }; + + /************************************ + Current Timezone + ************************************/ + + function OffsetAt(at) { + var timeString = at.toTimeString(); + var abbr = timeString.match(/\([a-z ]+\)/i); + if (abbr && abbr[0]) { + // 17:56:31 GMT-0600 (CST) + // 17:56:31 GMT-0600 (Central Standard Time) + abbr = abbr[0].match(/[A-Z]/g); + abbr = abbr ? abbr.join('') : undefined; + } else { + // 17:56:31 CST + // 17:56:31 GMT+0800 (台北標準時間) + abbr = timeString.match(/[A-Z]{3,5}/g); + abbr = abbr ? abbr[0] : undefined; + } + + if (abbr === 'GMT') { + abbr = undefined; + } + + this.at = +at; + this.abbr = abbr; + this.offset = at.getTimezoneOffset(); + } + + function ZoneScore(zone) { + this.zone = zone; + this.offsetScore = 0; + this.abbrScore = 0; + } + + ZoneScore.prototype.scoreOffsetAt = function (offsetAt) { + this.offsetScore += Math.abs(this.zone.offset(offsetAt.at) - offsetAt.offset); + if (this.zone.abbr(offsetAt.at).replace(/[^A-Z]/g, '') !== offsetAt.abbr) { + this.abbrScore++; + } + }; + + function findChange(low, high) { + var mid, diff; + + while ((diff = ((high.at - low.at) / 12e4 | 0) * 6e4)) { + mid = new OffsetAt(new Date(low.at + diff)); + if (mid.offset === low.offset) { + low = mid; + } else { + high = mid; + } + } + + return low; + } + + function userOffsets() { + var startYear = new Date().getFullYear() - 2, + last = new OffsetAt(new Date(startYear, 0, 1)), + offsets = [last], + change, next, i; + + for (i = 1; i < 48; i++) { + next = new OffsetAt(new Date(startYear, i, 1)); + if (next.offset !== last.offset) { + change = findChange(last, next); + offsets.push(change); + offsets.push(new OffsetAt(new Date(change.at + 6e4))); + } + last = next; + } + + for (i = 0; i < 4; i++) { + offsets.push(new OffsetAt(new Date(startYear + i, 0, 1))); + offsets.push(new OffsetAt(new Date(startYear + i, 6, 1))); + } + + return offsets; + } + + function sortZoneScores (a, b) { + if (a.offsetScore !== b.offsetScore) { + return a.offsetScore - b.offsetScore; + } + if (a.abbrScore !== b.abbrScore) { + return a.abbrScore - b.abbrScore; + } + return b.zone.population - a.zone.population; + } + + function addToGuesses (name, offsets) { + var i, offset; + arrayToInt(offsets); + for (i = 0; i < offsets.length; i++) { + offset = offsets[i]; + guesses[offset] = guesses[offset] || {}; + guesses[offset][name] = true; + } + } + + function guessesForUserOffsets (offsets) { + var offsetsLength = offsets.length, + filteredGuesses = {}, + out = [], + i, j, guessesOffset; + + for (i = 0; i < offsetsLength; i++) { + guessesOffset = guesses[offsets[i].offset] || {}; + for (j in guessesOffset) { + if (guessesOffset.hasOwnProperty(j)) { + filteredGuesses[j] = true; + } + } + } + + for (i in filteredGuesses) { + if (filteredGuesses.hasOwnProperty(i)) { + out.push(names[i]); + } + } + + return out; + } + + function rebuildGuess () { + + // use Intl API when available and returning valid time zone + try { + var intlName = Intl.DateTimeFormat().resolvedOptions().timeZone; + if (intlName){ + var name = names[normalizeName(intlName)]; + if (name) { + return name; + } + logError("Moment Timezone found " + intlName + " from the Intl api, but did not have that data loaded."); + } + } catch (e) { + // Intl unavailable, fall back to manual guessing. + } + + var offsets = userOffsets(), + offsetsLength = offsets.length, + guesses = guessesForUserOffsets(offsets), + zoneScores = [], + zoneScore, i, j; + + for (i = 0; i < guesses.length; i++) { + zoneScore = new ZoneScore(getZone(guesses[i]), offsetsLength); + for (j = 0; j < offsetsLength; j++) { + zoneScore.scoreOffsetAt(offsets[j]); + } + zoneScores.push(zoneScore); + } + + zoneScores.sort(sortZoneScores); + + return zoneScores.length > 0 ? zoneScores[0].zone.name : undefined; + } + + function guess (ignoreCache) { + if (!cachedGuess || ignoreCache) { + cachedGuess = rebuildGuess(); + } + return cachedGuess; + } + + /************************************ + Global Methods + ************************************/ + + function normalizeName (name) { + return (name || '').toLowerCase().replace(/\//g, '_'); + } + + function addZone (packed) { + var i, name, split, normalized; + + if (typeof packed === "string") { + packed = [packed]; + } + + for (i = 0; i < packed.length; i++) { + split = packed[i].split('|'); + name = split[0]; + normalized = normalizeName(name); + zones[normalized] = packed[i]; + names[normalized] = name; + if (split[5]) { + addToGuesses(normalized, split[2].split(' ')); + } + } + } + + function getZone (name, caller) { + name = normalizeName(name); + + var zone = zones[name]; + var link; + + if (zone instanceof Zone) { + return zone; + } + + if (typeof zone === 'string') { + zone = new Zone(zone); + zones[name] = zone; + return zone; + } + + // Pass getZone to prevent recursion more than 1 level deep + if (links[name] && caller !== getZone && (link = getZone(links[name], getZone))) { + zone = zones[name] = new Zone(); + zone._set(link); + zone.name = names[name]; + return zone; + } + + return null; + } + + function getNames () { + var i, out = []; + + for (i in names) { + if (names.hasOwnProperty(i) && (zones[i] || zones[links[i]]) && names[i]) { + out.push(names[i]); + } + } + + return out.sort(); + } + + function addLink (aliases) { + var i, alias, normal0, normal1; + + if (typeof aliases === "string") { + aliases = [aliases]; + } + + for (i = 0; i < aliases.length; i++) { + alias = aliases[i].split('|'); + + normal0 = normalizeName(alias[0]); + normal1 = normalizeName(alias[1]); + + links[normal0] = normal1; + names[normal0] = alias[0]; + + links[normal1] = normal0; + names[normal1] = alias[1]; + } + } + + function loadData (data) { + addZone(data.zones); + addLink(data.links); + tz.dataVersion = data.version; + } + + function zoneExists (name) { + if (!zoneExists.didShowError) { + zoneExists.didShowError = true; + logError("moment.tz.zoneExists('" + name + "') has been deprecated in favor of !moment.tz.zone('" + name + "')"); + } + return !!getZone(name); + } + + function needsOffset (m) { + return !!(m._a && (m._tzm === undefined)); + } + + function logError (message) { + if (typeof console !== 'undefined' && typeof console.error === 'function') { + console.error(message); + } + } + + /************************************ + moment.tz namespace + ************************************/ + + function tz (input) { + var args = Array.prototype.slice.call(arguments, 0, -1), + name = arguments[arguments.length - 1], + zone = getZone(name), + out = moment.utc.apply(null, args); + + if (zone && !moment.isMoment(input) && needsOffset(out)) { + out.add(zone.parse(out), 'minutes'); + } + + out.tz(name); + + return out; + } + + tz.version = VERSION; + tz.dataVersion = ''; + tz._zones = zones; + tz._links = links; + tz._names = names; + tz.add = addZone; + tz.link = addLink; + tz.load = loadData; + tz.zone = getZone; + tz.zoneExists = zoneExists; // deprecated in 0.1.0 + tz.guess = guess; + tz.names = getNames; + tz.Zone = Zone; + tz.unpack = unpack; + tz.unpackBase60 = unpackBase60; + tz.needsOffset = needsOffset; + tz.moveInvalidForward = true; + tz.moveAmbiguousForward = false; + + /************************************ + Interface with Moment.js + ************************************/ + + var fn = moment.fn; + + moment.tz = tz; + + moment.defaultZone = null; + + moment.updateOffset = function (mom, keepTime) { + var zone = moment.defaultZone, + offset; + + if (mom._z === undefined) { + if (zone && needsOffset(mom) && !mom._isUTC) { + mom._d = moment.utc(mom._a)._d; + mom.utc().add(zone.parse(mom), 'minutes'); + } + mom._z = zone; + } + if (mom._z) { + offset = mom._z.offset(mom); + if (Math.abs(offset) < 16) { + offset = offset / 60; + } + if (mom.utcOffset !== undefined) { + mom.utcOffset(-offset, keepTime); + } else { + mom.zone(offset, keepTime); + } + } + }; + + fn.tz = function (name) { + if (name) { + this._z = getZone(name); + if (this._z) { + moment.updateOffset(this); + } else { + logError("Moment Timezone has no data for " + name + ". See http://momentjs.com/timezone/docs/#/data-loading/."); + } + return this; + } + if (this._z) { return this._z.name; } + }; + + function abbrWrap (old) { + return function () { + if (this._z) { return this._z.abbr(this); } + return old.call(this); + }; + } + + function resetZoneWrap (old) { + return function () { + this._z = null; + return old.apply(this, arguments); + }; + } + + fn.zoneName = abbrWrap(fn.zoneName); + fn.zoneAbbr = abbrWrap(fn.zoneAbbr); + fn.utc = resetZoneWrap(fn.utc); + + moment.tz.setDefault = function(name) { + if (major < 2 || (major === 2 && minor < 9)) { + logError('Moment Timezone setDefault() requires Moment.js >= 2.9.0. You are using Moment.js ' + moment.version + '.'); + } + moment.defaultZone = name ? getZone(name) : null; + return moment; + }; + + // Cloning a moment should include the _z property. + var momentProperties = moment.momentProperties; + if (Object.prototype.toString.call(momentProperties) === '[object Array]') { + // moment 2.8.1+ + momentProperties.push('_z'); + momentProperties.push('_a'); + } else if (momentProperties) { + // moment 2.7.0 + momentProperties._z = null; + } + + loadData({ + "version": "2016f", + "zones": [ + "Africa/Abidjan|GMT|0|0||48e5", + "Africa/Khartoum|EAT|-30|0||51e5", + "Africa/Algiers|CET|-10|0||26e5", + "Africa/Lagos|WAT|-10|0||17e6", + "Africa/Maputo|CAT|-20|0||26e5", + "Africa/Cairo|EET EEST|-20 -30|010101010|1Cby0 Fb0 c10 8n0 8Nd0 gL0 e10 mn0|15e6", + "Africa/Casablanca|WET WEST|0 -10|01010101010101010101010101010101010101010|1Cco0 Db0 1zd0 Lz0 1Nf0 wM0 co0 go0 1o00 s00 dA0 vc0 11A0 A00 e00 y00 11A0 uM0 e00 Dc0 11A0 s00 e00 IM0 WM0 mo0 gM0 LA0 WM0 jA0 e00 Rc0 11A0 e00 e00 U00 11A0 8o0 e00 11A0|32e5", + "Europe/Paris|CET CEST|-10 -20|01010101010101010101010|1BWp0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00|11e6", + "Africa/Johannesburg|SAST|-20|0||84e5", + "Africa/Tripoli|EET CET CEST|-20 -10 -20|0120|1IlA0 TA0 1o00|11e5", + "Africa/Windhoek|WAST WAT|-20 -10|01010101010101010101010|1C1c0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0|32e4", + "America/Adak|HST HDT|a0 90|01010101010101010101010|1BR00 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|326", + "America/Anchorage|AKST AKDT|90 80|01010101010101010101010|1BQX0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|30e4", + "America/Santo_Domingo|AST|40|0||29e5", + "America/Araguaina|BRT BRST|30 20|010|1IdD0 Lz0|14e4", + "America/Argentina/Buenos_Aires|ART|30|0|", + "America/Asuncion|PYST PYT|30 40|01010101010101010101010|1C430 1a10 1fz0 1a10 1fz0 1cN0 17b0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0|28e5", + "America/Panama|EST|50|0||15e5", + "America/Bahia|BRT BRST|30 20|010|1FJf0 Rb0|27e5", + "America/Bahia_Banderas|MST CDT CST|70 50 60|01212121212121212121212|1C1l0 1nW0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0|84e3", + "America/Fortaleza|BRT|30|0||34e5", + "America/Managua|CST|60|0||22e5", + "America/Manaus|AMT|40|0||19e5", + "America/Bogota|COT|50|0||90e5", + "America/Denver|MST MDT|70 60|01010101010101010101010|1BQV0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|26e5", + "America/Campo_Grande|AMST AMT|30 40|01010101010101010101010|1BIr0 1zd0 On0 1zd0 Rb0 1zd0 Lz0 1C10 Lz0 1C10 On0 1zd0 On0 1zd0 On0 1zd0 On0 1C10 Lz0 1C10 Lz0 1C10|77e4", + "America/Cancun|CST CDT EST|60 50 50|010101010102|1C1k0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 Dd0|63e4", + "America/Caracas|VET VET|4u 40|01|1QMT0|29e5", + "America/Cayenne|GFT|30|0||58e3", + "America/Chicago|CST CDT|60 50|01010101010101010101010|1BQU0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|92e5", + "America/Chihuahua|MST MDT|70 60|01010101010101010101010|1C1l0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0|81e4", + "America/Phoenix|MST|70|0||42e5", + "America/Los_Angeles|PST PDT|80 70|01010101010101010101010|1BQW0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|15e6", + "America/New_York|EST EDT|50 40|01010101010101010101010|1BQT0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|21e6", + "America/Rio_Branco|AMT ACT|40 50|01|1KLE0|31e4", + "America/Fort_Nelson|PST PDT MST|80 70 70|010101010102|1BQW0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0|39e2", + "America/Halifax|AST ADT|40 30|01010101010101010101010|1BQS0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|39e4", + "America/Godthab|WGT WGST|30 20|01010101010101010101010|1BWp0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00|17e3", + "America/Goose_Bay|AST ADT|40 30|01010101010101010101010|1BQQ1 1zb0 Op0 1zcX Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|76e2", + "America/Grand_Turk|EST EDT AST|50 40 40|0101010101012|1BQT0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|37e2", + "America/Guayaquil|ECT|50|0||27e5", + "America/Guyana|GYT|40|0||80e4", + "America/Havana|CST CDT|50 40|01010101010101010101010|1BQR0 1wo0 U00 1zc0 U00 1qM0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0|21e5", + "America/La_Paz|BOT|40|0||19e5", + "America/Lima|PET|50|0||11e6", + "America/Mexico_City|CST CDT|60 50|01010101010101010101010|1C1k0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0|20e6", + "America/Metlakatla|PST AKST AKDT|80 90 80|012121212121|1PAa0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|14e2", + "America/Miquelon|PMST PMDT|30 20|01010101010101010101010|1BQR0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|61e2", + "America/Montevideo|UYST UYT|20 30|010101010101|1BQQ0 1ld0 14n0 1ld0 14n0 1o10 11z0 1o10 11z0 1o10 11z0|17e5", + "America/Noronha|FNT|20|0||30e2", + "America/North_Dakota/Beulah|MST MDT CST CDT|70 60 60 50|01232323232323232323232|1BQV0 1zb0 Oo0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0", + "America/Paramaribo|SRT|30|0||24e4", + "America/Port-au-Prince|EST EDT|50 40|010101010|1GI70 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|23e5", + "America/Santiago|CLST CLT|30 40|010101010101010101010|1C1f0 1fB0 1nX0 G10 1EL0 Op0 1zb0 Rd0 1wn0 Rd0 46n0 Ap0 1Nb0 Ap0 1Nb0 Ap0 1Nb0 Ap0 1Nb0 Ap0|62e5", + "America/Sao_Paulo|BRST BRT|20 30|01010101010101010101010|1BIq0 1zd0 On0 1zd0 Rb0 1zd0 Lz0 1C10 Lz0 1C10 On0 1zd0 On0 1zd0 On0 1zd0 On0 1C10 Lz0 1C10 Lz0 1C10|20e6", + "America/Scoresbysund|EGT EGST|10 0|01010101010101010101010|1BWp0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00|452", + "America/St_Johns|NST NDT|3u 2u|01010101010101010101010|1BQPv 1zb0 Op0 1zcX Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0|11e4", + "Antarctica/Casey|CAST AWST|-b0 -80|0101|1BN30 40P0 KL0|10", + "Antarctica/Davis|DAVT DAVT|-50 -70|0101|1BPw0 3Wn0 KN0|70", + "Antarctica/DumontDUrville|DDUT|-a0|0||80", + "Antarctica/Macquarie|AEDT MIST|-b0 -b0|01|1C140|1", + "Antarctica/Mawson|MAWT|-50|0||60", + "Pacific/Auckland|NZDT NZST|-d0 -c0|01010101010101010101010|1C120 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00|14e5", + "Antarctica/Rothera|ROTT|30|0||130", + "Antarctica/Syowa|SYOT|-30|0||20", + "Antarctica/Troll|UTC CEST|0 -20|01010101010101010101010|1BWp0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00|40", + "Antarctica/Vostok|VOST|-60|0||25", + "Asia/Baghdad|AST|-30|0||66e5", + "Asia/Almaty|+06|-60|0||15e5", + "Asia/Amman|EET EEST|-20 -30|010101010101010101010|1BVy0 1qM0 11A0 1o00 11A0 4bX0 Dd0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0|25e5", + "Asia/Anadyr|ANAT ANAST ANAT|-c0 -c0 -b0|0120|1BWe0 1qN0 WM0|13e3", + "Asia/Aqtobe|+05|-50|0||27e4", + "Asia/Ashgabat|TMT|-50|0||41e4", + "Asia/Baku|AZT AZST|-40 -50|0101010101010|1BWo0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00|27e5", + "Asia/Bangkok|ICT|-70|0||15e6", + "Asia/Barnaul|+06 +07|-60 -70|010101|1BWk0 1qM0 WM0 8Hz0 3rd0", + "Asia/Beirut|EET EEST|-20 -30|01010101010101010101010|1BWm0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0|22e5", + "Asia/Bishkek|KGT|-60|0||87e4", + "Asia/Brunei|BNT|-80|0||42e4", + "Asia/Kolkata|IST|-5u|0||15e6", + "Asia/Chita|YAKT YAKST YAKT IRKT|-90 -a0 -a0 -80|010230|1BWh0 1qM0 WM0 8Hz0 3re0|33e4", + "Asia/Choibalsan|CHOT CHOST|-80 -90|0101010101010|1O8G0 1cJ0 1cP0 1cJ0 1cP0 1fx0 1cP0 1cJ0 1cP0 1cJ0 1cP0 1cJ0|38e3", + "Asia/Shanghai|CST|-80|0||23e6", + "Asia/Dhaka|BDT|-60|0||16e6", + "Asia/Damascus|EET EEST|-20 -30|01010101010101010101010|1C0m0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0|26e5", + "Asia/Dili|TLT|-90|0||19e4", + "Asia/Dubai|GST|-40|0||39e5", + "Asia/Dushanbe|TJT|-50|0||76e4", + "Asia/Gaza|EET EEST|-20 -30|01010101010101010101010|1BVW1 SKX 1xd1 MKX 1AN0 1a00 1fA0 1cL0 1cN0 1nX0 1210 1nz0 1220 1ny0 1220 1qm0 1220 1ny0 1220 1ny0 1220 1ny0|18e5", + "Asia/Hebron|EET EEST|-20 -30|0101010101010101010101010|1BVy0 Tb0 1xd1 MKX bB0 cn0 1cN0 1a00 1fA0 1cL0 1cN0 1nX0 1210 1nz0 1220 1ny0 1220 1qm0 1220 1ny0 1220 1ny0 1220 1ny0|25e4", + "Asia/Hong_Kong|HKT|-80|0||73e5", + "Asia/Hovd|HOVT HOVST|-70 -80|0101010101010|1O8H0 1cJ0 1cP0 1cJ0 1cP0 1fx0 1cP0 1cJ0 1cP0 1cJ0 1cP0 1cJ0|81e3", + "Asia/Irkutsk|IRKT IRKST IRKT|-80 -90 -90|01020|1BWi0 1qM0 WM0 8Hz0|60e4", + "Europe/Istanbul|EET EEST|-20 -30|01010101010101010101010|1BWp0 1qM0 Xc0 1qo0 WM0 1qM0 11A0 1o00 1200 1nA0 11A0 1tA0 U00 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00|13e6", + "Asia/Jakarta|WIB|-70|0||31e6", + "Asia/Jayapura|WIT|-90|0||26e4", + "Asia/Jerusalem|IST IDT|-20 -30|01010101010101010101010|1BVA0 17X0 1kp0 1dz0 1c10 1aL0 1eN0 1oL0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0|81e4", + "Asia/Kabul|AFT|-4u|0||46e5", + "Asia/Kamchatka|PETT PETST PETT|-c0 -c0 -b0|0120|1BWe0 1qN0 WM0|18e4", + "Asia/Karachi|PKT|-50|0||24e6", + "Asia/Urumqi|XJT|-60|0||32e5", + "Asia/Kathmandu|NPT|-5J|0||12e5", + "Asia/Khandyga|VLAT VLAST VLAT YAKT YAKT|-a0 -b0 -b0 -a0 -90|010234|1BWg0 1qM0 WM0 17V0 7zD0|66e2", + "Asia/Krasnoyarsk|KRAT KRAST KRAT|-70 -80 -80|01020|1BWj0 1qM0 WM0 8Hz0|10e5", + "Asia/Kuala_Lumpur|MYT|-80|0||71e5", + "Asia/Magadan|MAGT MAGST MAGT MAGT|-b0 -c0 -c0 -a0|010230|1BWf0 1qM0 WM0 8Hz0 3Cq0|95e3", + "Asia/Makassar|WITA|-80|0||15e5", + "Asia/Manila|PHT|-80|0||24e6", + "Europe/Athens|EET EEST|-20 -30|01010101010101010101010|1BWp0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00|35e5", + "Asia/Novokuznetsk|+07 +06|-70 -60|010|1Dp80 WM0|55e4", + "Asia/Novosibirsk|+06 +07|-60 -70|010101|1BWk0 1qM0 WM0 8Hz0 4eN0|15e5", + "Asia/Omsk|OMST OMSST OMST|-60 -70 -70|01020|1BWk0 1qM0 WM0 8Hz0|12e5", + "Asia/Pyongyang|KST KST|-90 -8u|01|1P4D0|29e5", + "Asia/Rangoon|MMT|-6u|0||48e5", + "Asia/Sakhalin|SAKT SAKST SAKT|-a0 -b0 -b0|010202|1BWg0 1qM0 WM0 8Hz0 3rd0|58e4", + "Asia/Tashkent|UZT|-50|0||23e5", + "Asia/Seoul|KST|-90|0||23e6", + "Asia/Singapore|SGT|-80|0||56e5", + "Asia/Srednekolymsk|MAGT MAGST MAGT SRET|-b0 -c0 -c0 -b0|01023|1BWf0 1qM0 WM0 8Hz0|35e2", + "Asia/Tbilisi|GET|-40|0||11e5", + "Asia/Tehran|IRST IRDT|-3u -4u|01010101010101010101010|1BTUu 1dz0 1cp0 1dz0 1cp0 1dz0 1cN0 1dz0 1cp0 1dz0 1cp0 1dz0 1cp0 1dz0 1cN0 1dz0 1cp0 1dz0 1cp0 1dz0 1cp0 1dz0|14e6", + "Asia/Thimphu|BTT|-60|0||79e3", + "Asia/Tokyo|JST|-90|0||38e6", + "Asia/Tomsk|+06 +07|-60 -70|010101|1BWk0 1qM0 WM0 8Hz0 3Qp0|10e5", + "Asia/Ulaanbaatar|ULAT ULAST|-80 -90|0101010101010|1O8G0 1cJ0 1cP0 1cJ0 1cP0 1fx0 1cP0 1cJ0 1cP0 1cJ0 1cP0 1cJ0|12e5", + "Asia/Ust-Nera|MAGT MAGST MAGT VLAT VLAT|-b0 -c0 -c0 -b0 -a0|010234|1BWf0 1qM0 WM0 17V0 7zD0|65e2", + "Asia/Vladivostok|VLAT VLAST VLAT|-a0 -b0 -b0|01020|1BWg0 1qM0 WM0 8Hz0|60e4", + "Asia/Yakutsk|YAKT YAKST YAKT|-90 -a0 -a0|01020|1BWh0 1qM0 WM0 8Hz0|28e4", + "Asia/Yekaterinburg|YEKT YEKST YEKT|-50 -60 -60|01020|1BWl0 1qM0 WM0 8Hz0|14e5", + "Asia/Yerevan|AMT AMST|-40 -50|01010|1BWm0 1qM0 WM0 1qM0|13e5", + "Atlantic/Azores|AZOT AZOST|10 0|01010101010101010101010|1BWp0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00|25e4", + "Europe/Lisbon|WET WEST|0 -10|01010101010101010101010|1BWp0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00|27e5", + "Atlantic/Cape_Verde|CVT|10|0||50e4", + "Atlantic/South_Georgia|GST|20|0||30", + "Atlantic/Stanley|FKST FKT|30 40|010|1C6R0 U10|21e2", + "Australia/Sydney|AEDT AEST|-b0 -a0|01010101010101010101010|1C140 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0|40e5", + "Australia/Adelaide|ACDT ACST|-au -9u|01010101010101010101010|1C14u 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0|11e5", + "Australia/Brisbane|AEST|-a0|0||20e5", + "Australia/Darwin|ACST|-9u|0||12e4", + "Australia/Eucla|ACWST|-8J|0||368", + "Australia/Lord_Howe|LHDT LHST|-b0 -au|01010101010101010101010|1C130 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu|347", + "Australia/Perth|AWST|-80|0||18e5", + "Pacific/Easter|EASST EAST|50 60|010101010101010101010|1C1f0 1fB0 1nX0 G10 1EL0 Op0 1zb0 Rd0 1wn0 Rd0 46n0 Ap0 1Nb0 Ap0 1Nb0 Ap0 1Nb0 Ap0 1Nb0 Ap0|30e2", + "Europe/Dublin|GMT IST|0 -10|01010101010101010101010|1BWp0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00|12e5", + "Etc/GMT+1|GMT+1|10|0|", + "Etc/GMT+10|GMT+10|a0|0|", + "Etc/GMT+11|GMT+11|b0|0|", + "Etc/GMT+12|GMT+12|c0|0|", + "Etc/GMT+2|GMT+2|20|0|", + "Etc/GMT+3|GMT+3|30|0|", + "Etc/GMT+4|GMT+4|40|0|", + "Etc/GMT+5|GMT+5|50|0|", + "Etc/GMT+6|GMT+6|60|0|", + "Etc/GMT+7|GMT+7|70|0|", + "Etc/GMT+8|GMT+8|80|0|", + "Etc/GMT+9|GMT+9|90|0|", + "Etc/GMT-1|GMT-1|-10|0|", + "Etc/GMT-10|GMT-10|-a0|0|", + "Etc/GMT-11|GMT-11|-b0|0|", + "Etc/GMT-12|GMT-12|-c0|0|", + "Etc/GMT-13|GMT-13|-d0|0|", + "Etc/GMT-14|GMT-14|-e0|0|", + "Etc/GMT-2|GMT-2|-20|0|", + "Etc/GMT-3|GMT-3|-30|0|", + "Etc/GMT-4|GMT-4|-40|0|", + "Etc/GMT-5|GMT-5|-50|0|", + "Etc/GMT-6|GMT-6|-60|0|", + "Etc/GMT-7|GMT-7|-70|0|", + "Etc/GMT-8|GMT-8|-80|0|", + "Etc/GMT-9|GMT-9|-90|0|", + "Etc/UCT|UCT|0|0|", + "Etc/UTC|UTC|0|0|", + "Europe/Astrakhan|+03 +04|-30 -40|010101|1BWn0 1qM0 WM0 8Hz0 3rd0", + "Europe/London|GMT BST|0 -10|01010101010101010101010|1BWp0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00|10e6", + "Europe/Chisinau|EET EEST|-20 -30|01010101010101010101010|1BWo0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00|67e4", + "Europe/Kaliningrad|EET EEST FET|-20 -30 -30|01020|1BWo0 1qM0 WM0 8Hz0|44e4", + "Europe/Kirov|+03 +04|-30 -40|01010|1BWn0 1qM0 WM0 8Hz0|48e4", + "Europe/Minsk|EET EEST FET MSK|-20 -30 -30 -30|01023|1BWo0 1qM0 WM0 8Hy0|19e5", + "Europe/Moscow|MSK MSD MSK|-30 -40 -40|01020|1BWn0 1qM0 WM0 8Hz0|16e6", + "Europe/Samara|SAMT SAMST SAMT|-40 -40 -30|0120|1BWm0 1qN0 WM0|12e5", + "Europe/Simferopol|EET EEST MSK MSK|-20 -30 -40 -30|01010101023|1BWp0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11z0 1nW0|33e4", + "Pacific/Honolulu|HST|a0|0||37e4", + "Indian/Chagos|IOT|-60|0||30e2", + "Indian/Christmas|CXT|-70|0||21e2", + "Indian/Cocos|CCT|-6u|0||596", + "Indian/Kerguelen|TFT|-50|0||130", + "Indian/Mahe|SCT|-40|0||79e3", + "Indian/Maldives|MVT|-50|0||35e4", + "Indian/Mauritius|MUT|-40|0||15e4", + "Indian/Reunion|RET|-40|0||84e4", + "Pacific/Majuro|MHT|-c0|0||28e3", + "MET|MET MEST|-10 -20|01010101010101010101010|1BWp0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00", + "Pacific/Chatham|CHADT CHAST|-dJ -cJ|01010101010101010101010|1C120 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00|600", + "Pacific/Apia|SST SDT WSDT WSST|b0 a0 -e0 -d0|01012323232323232323232|1Dbn0 1ff0 1a00 CI0 AQ0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00|37e3", + "Pacific/Bougainville|PGT BST|-a0 -b0|01|1NwE0|18e4", + "Pacific/Chuuk|CHUT|-a0|0||49e3", + "Pacific/Efate|VUT|-b0|0||66e3", + "Pacific/Enderbury|PHOT|-d0|0||1", + "Pacific/Fakaofo|TKT TKT|b0 -d0|01|1Gfn0|483", + "Pacific/Fiji|FJST FJT|-d0 -c0|01010101010101010101010|1BWe0 1o00 Rc0 1wo0 Ao0 1Nc0 Ao0 1Q00 xz0 1SN0 uM0 1SM0 uM0 1VA0 s00 1VA0 uM0 1SM0 uM0 1SM0 uM0 1SM0|88e4", + "Pacific/Funafuti|TVT|-c0|0||45e2", + "Pacific/Galapagos|GALT|60|0||25e3", + "Pacific/Gambier|GAMT|90|0||125", + "Pacific/Guadalcanal|SBT|-b0|0||11e4", + "Pacific/Guam|ChST|-a0|0||17e4", + "Pacific/Kiritimati|LINT|-e0|0||51e2", + "Pacific/Kosrae|KOST|-b0|0||66e2", + "Pacific/Marquesas|MART|9u|0||86e2", + "Pacific/Pago_Pago|SST|b0|0||37e2", + "Pacific/Nauru|NRT|-c0|0||10e3", + "Pacific/Niue|NUT|b0|0||12e2", + "Pacific/Norfolk|NFT NFT|-bu -b0|01|1PoCu|25e4", + "Pacific/Noumea|NCT|-b0|0||98e3", + "Pacific/Palau|PWT|-90|0||21e3", + "Pacific/Pitcairn|PST|80|0||56", + "Pacific/Pohnpei|PONT|-b0|0||34e3", + "Pacific/Port_Moresby|PGT|-a0|0||25e4", + "Pacific/Rarotonga|CKT|a0|0||13e3", + "Pacific/Tahiti|TAHT|a0|0||18e4", + "Pacific/Tarawa|GILT|-c0|0||29e3", + "Pacific/Tongatapu|TOT|-d0|0||75e3", + "Pacific/Wake|WAKT|-c0|0||16e3", + "Pacific/Wallis|WFT|-c0|0||94" + ], + "links": [ + "Africa/Abidjan|Africa/Accra", + "Africa/Abidjan|Africa/Bamako", + "Africa/Abidjan|Africa/Banjul", + "Africa/Abidjan|Africa/Bissau", + "Africa/Abidjan|Africa/Conakry", + "Africa/Abidjan|Africa/Dakar", + "Africa/Abidjan|Africa/Freetown", + "Africa/Abidjan|Africa/Lome", + "Africa/Abidjan|Africa/Monrovia", + "Africa/Abidjan|Africa/Nouakchott", + "Africa/Abidjan|Africa/Ouagadougou", + "Africa/Abidjan|Africa/Sao_Tome", + "Africa/Abidjan|Africa/Timbuktu", + "Africa/Abidjan|America/Danmarkshavn", + "Africa/Abidjan|Atlantic/Reykjavik", + "Africa/Abidjan|Atlantic/St_Helena", + "Africa/Abidjan|Etc/GMT", + "Africa/Abidjan|Etc/GMT+0", + "Africa/Abidjan|Etc/GMT-0", + "Africa/Abidjan|Etc/GMT0", + "Africa/Abidjan|Etc/Greenwich", + "Africa/Abidjan|GMT", + "Africa/Abidjan|GMT+0", + "Africa/Abidjan|GMT-0", + "Africa/Abidjan|GMT0", + "Africa/Abidjan|Greenwich", + "Africa/Abidjan|Iceland", + "Africa/Algiers|Africa/Tunis", + "Africa/Cairo|Egypt", + "Africa/Casablanca|Africa/El_Aaiun", + "Africa/Johannesburg|Africa/Maseru", + "Africa/Johannesburg|Africa/Mbabane", + "Africa/Khartoum|Africa/Addis_Ababa", + "Africa/Khartoum|Africa/Asmara", + "Africa/Khartoum|Africa/Asmera", + "Africa/Khartoum|Africa/Dar_es_Salaam", + "Africa/Khartoum|Africa/Djibouti", + "Africa/Khartoum|Africa/Juba", + "Africa/Khartoum|Africa/Kampala", + "Africa/Khartoum|Africa/Mogadishu", + "Africa/Khartoum|Africa/Nairobi", + "Africa/Khartoum|Indian/Antananarivo", + "Africa/Khartoum|Indian/Comoro", + "Africa/Khartoum|Indian/Mayotte", + "Africa/Lagos|Africa/Bangui", + "Africa/Lagos|Africa/Brazzaville", + "Africa/Lagos|Africa/Douala", + "Africa/Lagos|Africa/Kinshasa", + "Africa/Lagos|Africa/Libreville", + "Africa/Lagos|Africa/Luanda", + "Africa/Lagos|Africa/Malabo", + "Africa/Lagos|Africa/Ndjamena", + "Africa/Lagos|Africa/Niamey", + "Africa/Lagos|Africa/Porto-Novo", + "Africa/Maputo|Africa/Blantyre", + "Africa/Maputo|Africa/Bujumbura", + "Africa/Maputo|Africa/Gaborone", + "Africa/Maputo|Africa/Harare", + "Africa/Maputo|Africa/Kigali", + "Africa/Maputo|Africa/Lubumbashi", + "Africa/Maputo|Africa/Lusaka", + "Africa/Tripoli|Libya", + "America/Adak|America/Atka", + "America/Adak|US/Aleutian", + "America/Anchorage|America/Juneau", + "America/Anchorage|America/Nome", + "America/Anchorage|America/Sitka", + "America/Anchorage|America/Yakutat", + "America/Anchorage|US/Alaska", + "America/Argentina/Buenos_Aires|America/Argentina/Catamarca", + "America/Argentina/Buenos_Aires|America/Argentina/ComodRivadavia", + "America/Argentina/Buenos_Aires|America/Argentina/Cordoba", + "America/Argentina/Buenos_Aires|America/Argentina/Jujuy", + "America/Argentina/Buenos_Aires|America/Argentina/La_Rioja", + "America/Argentina/Buenos_Aires|America/Argentina/Mendoza", + "America/Argentina/Buenos_Aires|America/Argentina/Rio_Gallegos", + "America/Argentina/Buenos_Aires|America/Argentina/Salta", + "America/Argentina/Buenos_Aires|America/Argentina/San_Juan", + "America/Argentina/Buenos_Aires|America/Argentina/San_Luis", + "America/Argentina/Buenos_Aires|America/Argentina/Tucuman", + "America/Argentina/Buenos_Aires|America/Argentina/Ushuaia", + "America/Argentina/Buenos_Aires|America/Buenos_Aires", + "America/Argentina/Buenos_Aires|America/Catamarca", + "America/Argentina/Buenos_Aires|America/Cordoba", + "America/Argentina/Buenos_Aires|America/Jujuy", + "America/Argentina/Buenos_Aires|America/Mendoza", + "America/Argentina/Buenos_Aires|America/Rosario", + "America/Campo_Grande|America/Cuiaba", + "America/Chicago|America/Indiana/Knox", + "America/Chicago|America/Indiana/Tell_City", + "America/Chicago|America/Knox_IN", + "America/Chicago|America/Matamoros", + "America/Chicago|America/Menominee", + "America/Chicago|America/North_Dakota/Center", + "America/Chicago|America/North_Dakota/New_Salem", + "America/Chicago|America/Rainy_River", + "America/Chicago|America/Rankin_Inlet", + "America/Chicago|America/Resolute", + "America/Chicago|America/Winnipeg", + "America/Chicago|CST6CDT", + "America/Chicago|Canada/Central", + "America/Chicago|US/Central", + "America/Chicago|US/Indiana-Starke", + "America/Chihuahua|America/Mazatlan", + "America/Chihuahua|Mexico/BajaSur", + "America/Denver|America/Boise", + "America/Denver|America/Cambridge_Bay", + "America/Denver|America/Edmonton", + "America/Denver|America/Inuvik", + "America/Denver|America/Ojinaga", + "America/Denver|America/Shiprock", + "America/Denver|America/Yellowknife", + "America/Denver|Canada/Mountain", + "America/Denver|MST7MDT", + "America/Denver|Navajo", + "America/Denver|US/Mountain", + "America/Fortaleza|America/Belem", + "America/Fortaleza|America/Maceio", + "America/Fortaleza|America/Recife", + "America/Fortaleza|America/Santarem", + "America/Halifax|America/Glace_Bay", + "America/Halifax|America/Moncton", + "America/Halifax|America/Thule", + "America/Halifax|Atlantic/Bermuda", + "America/Halifax|Canada/Atlantic", + "America/Havana|Cuba", + "America/Los_Angeles|America/Dawson", + "America/Los_Angeles|America/Ensenada", + "America/Los_Angeles|America/Santa_Isabel", + "America/Los_Angeles|America/Tijuana", + "America/Los_Angeles|America/Vancouver", + "America/Los_Angeles|America/Whitehorse", + "America/Los_Angeles|Canada/Pacific", + "America/Los_Angeles|Canada/Yukon", + "America/Los_Angeles|Mexico/BajaNorte", + "America/Los_Angeles|PST8PDT", + "America/Los_Angeles|US/Pacific", + "America/Los_Angeles|US/Pacific-New", + "America/Managua|America/Belize", + "America/Managua|America/Costa_Rica", + "America/Managua|America/El_Salvador", + "America/Managua|America/Guatemala", + "America/Managua|America/Regina", + "America/Managua|America/Swift_Current", + "America/Managua|America/Tegucigalpa", + "America/Managua|Canada/East-Saskatchewan", + "America/Managua|Canada/Saskatchewan", + "America/Manaus|America/Boa_Vista", + "America/Manaus|America/Porto_Velho", + "America/Manaus|Brazil/West", + "America/Mexico_City|America/Merida", + "America/Mexico_City|America/Monterrey", + "America/Mexico_City|Mexico/General", + "America/New_York|America/Detroit", + "America/New_York|America/Fort_Wayne", + "America/New_York|America/Indiana/Indianapolis", + "America/New_York|America/Indiana/Marengo", + "America/New_York|America/Indiana/Petersburg", + "America/New_York|America/Indiana/Vevay", + "America/New_York|America/Indiana/Vincennes", + "America/New_York|America/Indiana/Winamac", + "America/New_York|America/Indianapolis", + "America/New_York|America/Iqaluit", + "America/New_York|America/Kentucky/Louisville", + "America/New_York|America/Kentucky/Monticello", + "America/New_York|America/Louisville", + "America/New_York|America/Montreal", + "America/New_York|America/Nassau", + "America/New_York|America/Nipigon", + "America/New_York|America/Pangnirtung", + "America/New_York|America/Thunder_Bay", + "America/New_York|America/Toronto", + "America/New_York|Canada/Eastern", + "America/New_York|EST5EDT", + "America/New_York|US/East-Indiana", + "America/New_York|US/Eastern", + "America/New_York|US/Michigan", + "America/Noronha|Brazil/DeNoronha", + "America/Panama|America/Atikokan", + "America/Panama|America/Cayman", + "America/Panama|America/Coral_Harbour", + "America/Panama|America/Jamaica", + "America/Panama|EST", + "America/Panama|Jamaica", + "America/Phoenix|America/Creston", + "America/Phoenix|America/Dawson_Creek", + "America/Phoenix|America/Hermosillo", + "America/Phoenix|MST", + "America/Phoenix|US/Arizona", + "America/Rio_Branco|America/Eirunepe", + "America/Rio_Branco|America/Porto_Acre", + "America/Rio_Branco|Brazil/Acre", + "America/Santiago|Antarctica/Palmer", + "America/Santiago|Chile/Continental", + "America/Santo_Domingo|America/Anguilla", + "America/Santo_Domingo|America/Antigua", + "America/Santo_Domingo|America/Aruba", + "America/Santo_Domingo|America/Barbados", + "America/Santo_Domingo|America/Blanc-Sablon", + "America/Santo_Domingo|America/Curacao", + "America/Santo_Domingo|America/Dominica", + "America/Santo_Domingo|America/Grenada", + "America/Santo_Domingo|America/Guadeloupe", + "America/Santo_Domingo|America/Kralendijk", + "America/Santo_Domingo|America/Lower_Princes", + "America/Santo_Domingo|America/Marigot", + "America/Santo_Domingo|America/Martinique", + "America/Santo_Domingo|America/Montserrat", + "America/Santo_Domingo|America/Port_of_Spain", + "America/Santo_Domingo|America/Puerto_Rico", + "America/Santo_Domingo|America/St_Barthelemy", + "America/Santo_Domingo|America/St_Kitts", + "America/Santo_Domingo|America/St_Lucia", + "America/Santo_Domingo|America/St_Thomas", + "America/Santo_Domingo|America/St_Vincent", + "America/Santo_Domingo|America/Tortola", + "America/Santo_Domingo|America/Virgin", + "America/Sao_Paulo|Brazil/East", + "America/St_Johns|Canada/Newfoundland", + "Asia/Almaty|Asia/Qyzylorda", + "Asia/Aqtobe|Asia/Aqtau", + "Asia/Aqtobe|Asia/Oral", + "Asia/Ashgabat|Asia/Ashkhabad", + "Asia/Baghdad|Asia/Aden", + "Asia/Baghdad|Asia/Bahrain", + "Asia/Baghdad|Asia/Kuwait", + "Asia/Baghdad|Asia/Qatar", + "Asia/Baghdad|Asia/Riyadh", + "Asia/Bangkok|Asia/Ho_Chi_Minh", + "Asia/Bangkok|Asia/Phnom_Penh", + "Asia/Bangkok|Asia/Saigon", + "Asia/Bangkok|Asia/Vientiane", + "Asia/Dhaka|Asia/Dacca", + "Asia/Dubai|Asia/Muscat", + "Asia/Hong_Kong|Hongkong", + "Asia/Jakarta|Asia/Pontianak", + "Asia/Jerusalem|Asia/Tel_Aviv", + "Asia/Jerusalem|Israel", + "Asia/Kathmandu|Asia/Katmandu", + "Asia/Kolkata|Asia/Calcutta", + "Asia/Kolkata|Asia/Colombo", + "Asia/Kuala_Lumpur|Asia/Kuching", + "Asia/Makassar|Asia/Ujung_Pandang", + "Asia/Seoul|ROK", + "Asia/Shanghai|Asia/Chongqing", + "Asia/Shanghai|Asia/Chungking", + "Asia/Shanghai|Asia/Harbin", + "Asia/Shanghai|Asia/Macao", + "Asia/Shanghai|Asia/Macau", + "Asia/Shanghai|Asia/Taipei", + "Asia/Shanghai|PRC", + "Asia/Shanghai|ROC", + "Asia/Singapore|Singapore", + "Asia/Tashkent|Asia/Samarkand", + "Asia/Tehran|Iran", + "Asia/Thimphu|Asia/Thimbu", + "Asia/Tokyo|Japan", + "Asia/Ulaanbaatar|Asia/Ulan_Bator", + "Asia/Urumqi|Asia/Kashgar", + "Australia/Adelaide|Australia/Broken_Hill", + "Australia/Adelaide|Australia/South", + "Australia/Adelaide|Australia/Yancowinna", + "Australia/Brisbane|Australia/Lindeman", + "Australia/Brisbane|Australia/Queensland", + "Australia/Darwin|Australia/North", + "Australia/Lord_Howe|Australia/LHI", + "Australia/Perth|Australia/West", + "Australia/Sydney|Australia/ACT", + "Australia/Sydney|Australia/Canberra", + "Australia/Sydney|Australia/Currie", + "Australia/Sydney|Australia/Hobart", + "Australia/Sydney|Australia/Melbourne", + "Australia/Sydney|Australia/NSW", + "Australia/Sydney|Australia/Tasmania", + "Australia/Sydney|Australia/Victoria", + "Etc/UCT|UCT", + "Etc/UTC|Etc/Universal", + "Etc/UTC|Etc/Zulu", + "Etc/UTC|UTC", + "Etc/UTC|Universal", + "Etc/UTC|Zulu", + "Europe/Astrakhan|Europe/Ulyanovsk", + "Europe/Athens|Asia/Nicosia", + "Europe/Athens|EET", + "Europe/Athens|Europe/Bucharest", + "Europe/Athens|Europe/Helsinki", + "Europe/Athens|Europe/Kiev", + "Europe/Athens|Europe/Mariehamn", + "Europe/Athens|Europe/Nicosia", + "Europe/Athens|Europe/Riga", + "Europe/Athens|Europe/Sofia", + "Europe/Athens|Europe/Tallinn", + "Europe/Athens|Europe/Uzhgorod", + "Europe/Athens|Europe/Vilnius", + "Europe/Athens|Europe/Zaporozhye", + "Europe/Chisinau|Europe/Tiraspol", + "Europe/Dublin|Eire", + "Europe/Istanbul|Asia/Istanbul", + "Europe/Istanbul|Turkey", + "Europe/Lisbon|Atlantic/Canary", + "Europe/Lisbon|Atlantic/Faeroe", + "Europe/Lisbon|Atlantic/Faroe", + "Europe/Lisbon|Atlantic/Madeira", + "Europe/Lisbon|Portugal", + "Europe/Lisbon|WET", + "Europe/London|Europe/Belfast", + "Europe/London|Europe/Guernsey", + "Europe/London|Europe/Isle_of_Man", + "Europe/London|Europe/Jersey", + "Europe/London|GB", + "Europe/London|GB-Eire", + "Europe/Moscow|Europe/Volgograd", + "Europe/Moscow|W-SU", + "Europe/Paris|Africa/Ceuta", + "Europe/Paris|Arctic/Longyearbyen", + "Europe/Paris|Atlantic/Jan_Mayen", + "Europe/Paris|CET", + "Europe/Paris|Europe/Amsterdam", + "Europe/Paris|Europe/Andorra", + "Europe/Paris|Europe/Belgrade", + "Europe/Paris|Europe/Berlin", + "Europe/Paris|Europe/Bratislava", + "Europe/Paris|Europe/Brussels", + "Europe/Paris|Europe/Budapest", + "Europe/Paris|Europe/Busingen", + "Europe/Paris|Europe/Copenhagen", + "Europe/Paris|Europe/Gibraltar", + "Europe/Paris|Europe/Ljubljana", + "Europe/Paris|Europe/Luxembourg", + "Europe/Paris|Europe/Madrid", + "Europe/Paris|Europe/Malta", + "Europe/Paris|Europe/Monaco", + "Europe/Paris|Europe/Oslo", + "Europe/Paris|Europe/Podgorica", + "Europe/Paris|Europe/Prague", + "Europe/Paris|Europe/Rome", + "Europe/Paris|Europe/San_Marino", + "Europe/Paris|Europe/Sarajevo", + "Europe/Paris|Europe/Skopje", + "Europe/Paris|Europe/Stockholm", + "Europe/Paris|Europe/Tirane", + "Europe/Paris|Europe/Vaduz", + "Europe/Paris|Europe/Vatican", + "Europe/Paris|Europe/Vienna", + "Europe/Paris|Europe/Warsaw", + "Europe/Paris|Europe/Zagreb", + "Europe/Paris|Europe/Zurich", + "Europe/Paris|Poland", + "Pacific/Auckland|Antarctica/McMurdo", + "Pacific/Auckland|Antarctica/South_Pole", + "Pacific/Auckland|NZ", + "Pacific/Chatham|NZ-CHAT", + "Pacific/Chuuk|Pacific/Truk", + "Pacific/Chuuk|Pacific/Yap", + "Pacific/Easter|Chile/EasterIsland", + "Pacific/Guam|Pacific/Saipan", + "Pacific/Honolulu|HST", + "Pacific/Honolulu|Pacific/Johnston", + "Pacific/Honolulu|US/Hawaii", + "Pacific/Majuro|Kwajalein", + "Pacific/Majuro|Pacific/Kwajalein", + "Pacific/Pago_Pago|Pacific/Midway", + "Pacific/Pago_Pago|Pacific/Samoa", + "Pacific/Pago_Pago|US/Samoa", + "Pacific/Pohnpei|Pacific/Ponape" + ] + }); + + + return moment; +})); diff --git a/kfet/static/kfet/js/moment.js b/kfet/static/kfet/js/moment.js new file mode 100644 index 00000000..714b4c44 --- /dev/null +++ b/kfet/static/kfet/js/moment.js @@ -0,0 +1,4195 @@ +//! moment.js +//! version : 2.14.1 +//! authors : Tim Wood, Iskren Chernev, Moment.js contributors +//! license : MIT +//! momentjs.com + +;(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + global.moment = factory() +}(this, function () { 'use strict'; + + var hookCallback; + + function utils_hooks__hooks () { + return hookCallback.apply(null, arguments); + } + + // This is done to register the method called with moment() + // without creating circular dependencies. + function setHookCallback (callback) { + hookCallback = callback; + } + + function isArray(input) { + return input instanceof Array || Object.prototype.toString.call(input) === '[object Array]'; + } + + function isObject(input) { + return Object.prototype.toString.call(input) === '[object Object]'; + } + + function isObjectEmpty(obj) { + var k; + for (k in obj) { + // even if its not own property I'd still call it non-empty + return false; + } + return true; + } + + function isDate(input) { + return input instanceof Date || Object.prototype.toString.call(input) === '[object Date]'; + } + + function map(arr, fn) { + var res = [], i; + for (i = 0; i < arr.length; ++i) { + res.push(fn(arr[i], i)); + } + return res; + } + + function hasOwnProp(a, b) { + return Object.prototype.hasOwnProperty.call(a, b); + } + + function extend(a, b) { + for (var i in b) { + if (hasOwnProp(b, i)) { + a[i] = b[i]; + } + } + + if (hasOwnProp(b, 'toString')) { + a.toString = b.toString; + } + + if (hasOwnProp(b, 'valueOf')) { + a.valueOf = b.valueOf; + } + + return a; + } + + function create_utc__createUTC (input, format, locale, strict) { + return createLocalOrUTC(input, format, locale, strict, true).utc(); + } + + function defaultParsingFlags() { + // We need to deep clone this object. + return { + empty : false, + unusedTokens : [], + unusedInput : [], + overflow : -2, + charsLeftOver : 0, + nullInput : false, + invalidMonth : null, + invalidFormat : false, + userInvalidated : false, + iso : false, + parsedDateParts : [], + meridiem : null + }; + } + + function getParsingFlags(m) { + if (m._pf == null) { + m._pf = defaultParsingFlags(); + } + return m._pf; + } + + var some; + if (Array.prototype.some) { + some = Array.prototype.some; + } else { + some = function (fun) { + var t = Object(this); + var len = t.length >>> 0; + + for (var i = 0; i < len; i++) { + if (i in t && fun.call(this, t[i], i, t)) { + return true; + } + } + + return false; + }; + } + + function valid__isValid(m) { + if (m._isValid == null) { + var flags = getParsingFlags(m); + var parsedParts = some.call(flags.parsedDateParts, function (i) { + return i != null; + }); + m._isValid = !isNaN(m._d.getTime()) && + flags.overflow < 0 && + !flags.empty && + !flags.invalidMonth && + !flags.invalidWeekday && + !flags.nullInput && + !flags.invalidFormat && + !flags.userInvalidated && + (!flags.meridiem || (flags.meridiem && parsedParts)); + + if (m._strict) { + m._isValid = m._isValid && + flags.charsLeftOver === 0 && + flags.unusedTokens.length === 0 && + flags.bigHour === undefined; + } + } + return m._isValid; + } + + function valid__createInvalid (flags) { + var m = create_utc__createUTC(NaN); + if (flags != null) { + extend(getParsingFlags(m), flags); + } + else { + getParsingFlags(m).userInvalidated = true; + } + + return m; + } + + function isUndefined(input) { + return input === void 0; + } + + // Plugins that add properties should also add the key here (null value), + // so we can properly clone ourselves. + var momentProperties = utils_hooks__hooks.momentProperties = []; + + function copyConfig(to, from) { + var i, prop, val; + + if (!isUndefined(from._isAMomentObject)) { + to._isAMomentObject = from._isAMomentObject; + } + if (!isUndefined(from._i)) { + to._i = from._i; + } + if (!isUndefined(from._f)) { + to._f = from._f; + } + if (!isUndefined(from._l)) { + to._l = from._l; + } + if (!isUndefined(from._strict)) { + to._strict = from._strict; + } + if (!isUndefined(from._tzm)) { + to._tzm = from._tzm; + } + if (!isUndefined(from._isUTC)) { + to._isUTC = from._isUTC; + } + if (!isUndefined(from._offset)) { + to._offset = from._offset; + } + if (!isUndefined(from._pf)) { + to._pf = getParsingFlags(from); + } + if (!isUndefined(from._locale)) { + to._locale = from._locale; + } + + if (momentProperties.length > 0) { + for (i in momentProperties) { + prop = momentProperties[i]; + val = from[prop]; + if (!isUndefined(val)) { + to[prop] = val; + } + } + } + + return to; + } + + var updateInProgress = false; + + // Moment prototype object + function Moment(config) { + copyConfig(this, config); + this._d = new Date(config._d != null ? config._d.getTime() : NaN); + // Prevent infinite loop in case updateOffset creates new moment + // objects. + if (updateInProgress === false) { + updateInProgress = true; + utils_hooks__hooks.updateOffset(this); + updateInProgress = false; + } + } + + function isMoment (obj) { + return obj instanceof Moment || (obj != null && obj._isAMomentObject != null); + } + + function absFloor (number) { + if (number < 0) { + // -0 -> 0 + return Math.ceil(number) || 0; + } else { + return Math.floor(number); + } + } + + function toInt(argumentForCoercion) { + var coercedNumber = +argumentForCoercion, + value = 0; + + if (coercedNumber !== 0 && isFinite(coercedNumber)) { + value = absFloor(coercedNumber); + } + + return value; + } + + // compare two arrays, return the number of differences + function compareArrays(array1, array2, dontConvert) { + var len = Math.min(array1.length, array2.length), + lengthDiff = Math.abs(array1.length - array2.length), + diffs = 0, + i; + for (i = 0; i < len; i++) { + if ((dontConvert && array1[i] !== array2[i]) || + (!dontConvert && toInt(array1[i]) !== toInt(array2[i]))) { + diffs++; + } + } + return diffs + lengthDiff; + } + + function warn(msg) { + if (utils_hooks__hooks.suppressDeprecationWarnings === false && + (typeof console !== 'undefined') && console.warn) { + console.warn('Deprecation warning: ' + msg); + } + } + + function deprecate(msg, fn) { + var firstTime = true; + + return extend(function () { + if (utils_hooks__hooks.deprecationHandler != null) { + utils_hooks__hooks.deprecationHandler(null, msg); + } + if (firstTime) { + warn(msg + '\nArguments: ' + Array.prototype.slice.call(arguments).join(', ') + '\n' + (new Error()).stack); + firstTime = false; + } + return fn.apply(this, arguments); + }, fn); + } + + var deprecations = {}; + + function deprecateSimple(name, msg) { + if (utils_hooks__hooks.deprecationHandler != null) { + utils_hooks__hooks.deprecationHandler(name, msg); + } + if (!deprecations[name]) { + warn(msg); + deprecations[name] = true; + } + } + + utils_hooks__hooks.suppressDeprecationWarnings = false; + utils_hooks__hooks.deprecationHandler = null; + + function isFunction(input) { + return input instanceof Function || Object.prototype.toString.call(input) === '[object Function]'; + } + + function locale_set__set (config) { + var prop, i; + for (i in config) { + prop = config[i]; + if (isFunction(prop)) { + this[i] = prop; + } else { + this['_' + i] = prop; + } + } + this._config = config; + // Lenient ordinal parsing accepts just a number in addition to + // number + (possibly) stuff coming from _ordinalParseLenient. + this._ordinalParseLenient = new RegExp(this._ordinalParse.source + '|' + (/\d{1,2}/).source); + } + + function mergeConfigs(parentConfig, childConfig) { + var res = extend({}, parentConfig), prop; + for (prop in childConfig) { + if (hasOwnProp(childConfig, prop)) { + if (isObject(parentConfig[prop]) && isObject(childConfig[prop])) { + res[prop] = {}; + extend(res[prop], parentConfig[prop]); + extend(res[prop], childConfig[prop]); + } else if (childConfig[prop] != null) { + res[prop] = childConfig[prop]; + } else { + delete res[prop]; + } + } + } + for (prop in parentConfig) { + if (hasOwnProp(parentConfig, prop) && + !hasOwnProp(childConfig, prop) && + isObject(parentConfig[prop])) { + // make sure changes to properties don't modify parent config + res[prop] = extend({}, res[prop]); + } + } + return res; + } + + function Locale(config) { + if (config != null) { + this.set(config); + } + } + + var keys; + + if (Object.keys) { + keys = Object.keys; + } else { + keys = function (obj) { + var i, res = []; + for (i in obj) { + if (hasOwnProp(obj, i)) { + res.push(i); + } + } + return res; + }; + } + + var defaultCalendar = { + sameDay : '[Today at] LT', + nextDay : '[Tomorrow at] LT', + nextWeek : 'dddd [at] LT', + lastDay : '[Yesterday at] LT', + lastWeek : '[Last] dddd [at] LT', + sameElse : 'L' + }; + + function locale_calendar__calendar (key, mom, now) { + var output = this._calendar[key] || this._calendar['sameElse']; + return isFunction(output) ? output.call(mom, now) : output; + } + + var defaultLongDateFormat = { + LTS : 'h:mm:ss A', + LT : 'h:mm A', + L : 'MM/DD/YYYY', + LL : 'MMMM D, YYYY', + LLL : 'MMMM D, YYYY h:mm A', + LLLL : 'dddd, MMMM D, YYYY h:mm A' + }; + + function longDateFormat (key) { + var format = this._longDateFormat[key], + formatUpper = this._longDateFormat[key.toUpperCase()]; + + if (format || !formatUpper) { + return format; + } + + this._longDateFormat[key] = formatUpper.replace(/MMMM|MM|DD|dddd/g, function (val) { + return val.slice(1); + }); + + return this._longDateFormat[key]; + } + + var defaultInvalidDate = 'Invalid date'; + + function invalidDate () { + return this._invalidDate; + } + + var defaultOrdinal = '%d'; + var defaultOrdinalParse = /\d{1,2}/; + + function ordinal (number) { + return this._ordinal.replace('%d', number); + } + + var defaultRelativeTime = { + future : 'in %s', + past : '%s ago', + s : 'a few seconds', + m : 'a minute', + mm : '%d minutes', + h : 'an hour', + hh : '%d hours', + d : 'a day', + dd : '%d days', + M : 'a month', + MM : '%d months', + y : 'a year', + yy : '%d years' + }; + + function relative__relativeTime (number, withoutSuffix, string, isFuture) { + var output = this._relativeTime[string]; + return (isFunction(output)) ? + output(number, withoutSuffix, string, isFuture) : + output.replace(/%d/i, number); + } + + function pastFuture (diff, output) { + var format = this._relativeTime[diff > 0 ? 'future' : 'past']; + return isFunction(format) ? format(output) : format.replace(/%s/i, output); + } + + var aliases = {}; + + function addUnitAlias (unit, shorthand) { + var lowerCase = unit.toLowerCase(); + aliases[lowerCase] = aliases[lowerCase + 's'] = aliases[shorthand] = unit; + } + + function normalizeUnits(units) { + return typeof units === 'string' ? aliases[units] || aliases[units.toLowerCase()] : undefined; + } + + function normalizeObjectUnits(inputObject) { + var normalizedInput = {}, + normalizedProp, + prop; + + for (prop in inputObject) { + if (hasOwnProp(inputObject, prop)) { + normalizedProp = normalizeUnits(prop); + if (normalizedProp) { + normalizedInput[normalizedProp] = inputObject[prop]; + } + } + } + + return normalizedInput; + } + + var priorities = {}; + + function addUnitPriority(unit, priority) { + priorities[unit] = priority; + } + + function getPrioritizedUnits(unitsObj) { + var units = []; + for (var u in unitsObj) { + units.push({unit: u, priority: priorities[u]}); + } + units.sort(function (a, b) { + return a.priority - b.priority; + }); + return units; + } + + function makeGetSet (unit, keepTime) { + return function (value) { + if (value != null) { + get_set__set(this, unit, value); + utils_hooks__hooks.updateOffset(this, keepTime); + return this; + } else { + return get_set__get(this, unit); + } + }; + } + + function get_set__get (mom, unit) { + return mom.isValid() ? + mom._d['get' + (mom._isUTC ? 'UTC' : '') + unit]() : NaN; + } + + function get_set__set (mom, unit, value) { + if (mom.isValid()) { + mom._d['set' + (mom._isUTC ? 'UTC' : '') + unit](value); + } + } + + // MOMENTS + + function stringGet (units) { + units = normalizeUnits(units); + if (isFunction(this[units])) { + return this[units](); + } + return this; + } + + + function stringSet (units, value) { + if (typeof units === 'object') { + units = normalizeObjectUnits(units); + var prioritized = getPrioritizedUnits(units); + for (var i = 0; i < prioritized.length; i++) { + this[prioritized[i].unit](units[prioritized[i].unit]); + } + } else { + units = normalizeUnits(units); + if (isFunction(this[units])) { + return this[units](value); + } + } + return this; + } + + function zeroFill(number, targetLength, forceSign) { + var absNumber = '' + Math.abs(number), + zerosToFill = targetLength - absNumber.length, + sign = number >= 0; + return (sign ? (forceSign ? '+' : '') : '-') + + Math.pow(10, Math.max(0, zerosToFill)).toString().substr(1) + absNumber; + } + + var formattingTokens = /(\[[^\[]*\])|(\\)?([Hh]mm(ss)?|Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Qo?|YYYYYY|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|kk?|mm?|ss?|S{1,9}|x|X|zz?|ZZ?|.)/g; + + var localFormattingTokens = /(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g; + + var formatFunctions = {}; + + var formatTokenFunctions = {}; + + // token: 'M' + // padded: ['MM', 2] + // ordinal: 'Mo' + // callback: function () { this.month() + 1 } + function addFormatToken (token, padded, ordinal, callback) { + var func = callback; + if (typeof callback === 'string') { + func = function () { + return this[callback](); + }; + } + if (token) { + formatTokenFunctions[token] = func; + } + if (padded) { + formatTokenFunctions[padded[0]] = function () { + return zeroFill(func.apply(this, arguments), padded[1], padded[2]); + }; + } + if (ordinal) { + formatTokenFunctions[ordinal] = function () { + return this.localeData().ordinal(func.apply(this, arguments), token); + }; + } + } + + function removeFormattingTokens(input) { + if (input.match(/\[[\s\S]/)) { + return input.replace(/^\[|\]$/g, ''); + } + return input.replace(/\\/g, ''); + } + + function makeFormatFunction(format) { + var array = format.match(formattingTokens), i, length; + + for (i = 0, length = array.length; i < length; i++) { + if (formatTokenFunctions[array[i]]) { + array[i] = formatTokenFunctions[array[i]]; + } else { + array[i] = removeFormattingTokens(array[i]); + } + } + + return function (mom) { + var output = '', i; + for (i = 0; i < length; i++) { + output += array[i] instanceof Function ? array[i].call(mom, format) : array[i]; + } + return output; + }; + } + + // format date using native date object + function formatMoment(m, format) { + if (!m.isValid()) { + return m.localeData().invalidDate(); + } + + format = expandFormat(format, m.localeData()); + formatFunctions[format] = formatFunctions[format] || makeFormatFunction(format); + + return formatFunctions[format](m); + } + + function expandFormat(format, locale) { + var i = 5; + + function replaceLongDateFormatTokens(input) { + return locale.longDateFormat(input) || input; + } + + localFormattingTokens.lastIndex = 0; + while (i >= 0 && localFormattingTokens.test(format)) { + format = format.replace(localFormattingTokens, replaceLongDateFormatTokens); + localFormattingTokens.lastIndex = 0; + i -= 1; + } + + return format; + } + + var match1 = /\d/; // 0 - 9 + var match2 = /\d\d/; // 00 - 99 + var match3 = /\d{3}/; // 000 - 999 + var match4 = /\d{4}/; // 0000 - 9999 + var match6 = /[+-]?\d{6}/; // -999999 - 999999 + var match1to2 = /\d\d?/; // 0 - 99 + var match3to4 = /\d\d\d\d?/; // 999 - 9999 + var match5to6 = /\d\d\d\d\d\d?/; // 99999 - 999999 + var match1to3 = /\d{1,3}/; // 0 - 999 + var match1to4 = /\d{1,4}/; // 0 - 9999 + var match1to6 = /[+-]?\d{1,6}/; // -999999 - 999999 + + var matchUnsigned = /\d+/; // 0 - inf + var matchSigned = /[+-]?\d+/; // -inf - inf + + var matchOffset = /Z|[+-]\d\d:?\d\d/gi; // +00:00 -00:00 +0000 -0000 or Z + var matchShortOffset = /Z|[+-]\d\d(?::?\d\d)?/gi; // +00 -00 +00:00 -00:00 +0000 -0000 or Z + + var matchTimestamp = /[+-]?\d+(\.\d{1,3})?/; // 123456789 123456789.123 + + // any word (or two) characters or numbers including two/three word month in arabic. + // includes scottish gaelic two word and hyphenated months + var matchWord = /[0-9]*['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+|[\u0600-\u06FF\/]+(\s*?[\u0600-\u06FF]+){1,2}/i; + + + var regexes = {}; + + function addRegexToken (token, regex, strictRegex) { + regexes[token] = isFunction(regex) ? regex : function (isStrict, localeData) { + return (isStrict && strictRegex) ? strictRegex : regex; + }; + } + + function getParseRegexForToken (token, config) { + if (!hasOwnProp(regexes, token)) { + return new RegExp(unescapeFormat(token)); + } + + return regexes[token](config._strict, config._locale); + } + + // Code from http://stackoverflow.com/questions/3561493/is-there-a-regexp-escape-function-in-javascript + function unescapeFormat(s) { + return regexEscape(s.replace('\\', '').replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g, function (matched, p1, p2, p3, p4) { + return p1 || p2 || p3 || p4; + })); + } + + function regexEscape(s) { + return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'); + } + + var tokens = {}; + + function addParseToken (token, callback) { + var i, func = callback; + if (typeof token === 'string') { + token = [token]; + } + if (typeof callback === 'number') { + func = function (input, array) { + array[callback] = toInt(input); + }; + } + for (i = 0; i < token.length; i++) { + tokens[token[i]] = func; + } + } + + function addWeekParseToken (token, callback) { + addParseToken(token, function (input, array, config, token) { + config._w = config._w || {}; + callback(input, config._w, config, token); + }); + } + + function addTimeToArrayFromToken(token, input, config) { + if (input != null && hasOwnProp(tokens, token)) { + tokens[token](input, config._a, config, token); + } + } + + var YEAR = 0; + var MONTH = 1; + var DATE = 2; + var HOUR = 3; + var MINUTE = 4; + var SECOND = 5; + var MILLISECOND = 6; + var WEEK = 7; + var WEEKDAY = 8; + + var indexOf; + + if (Array.prototype.indexOf) { + indexOf = Array.prototype.indexOf; + } else { + indexOf = function (o) { + // I know + var i; + for (i = 0; i < this.length; ++i) { + if (this[i] === o) { + return i; + } + } + return -1; + }; + } + + function daysInMonth(year, month) { + return new Date(Date.UTC(year, month + 1, 0)).getUTCDate(); + } + + // FORMATTING + + addFormatToken('M', ['MM', 2], 'Mo', function () { + return this.month() + 1; + }); + + addFormatToken('MMM', 0, 0, function (format) { + return this.localeData().monthsShort(this, format); + }); + + addFormatToken('MMMM', 0, 0, function (format) { + return this.localeData().months(this, format); + }); + + // ALIASES + + addUnitAlias('month', 'M'); + + // PRIORITY + + addUnitPriority('month', 8); + + // PARSING + + addRegexToken('M', match1to2); + addRegexToken('MM', match1to2, match2); + addRegexToken('MMM', function (isStrict, locale) { + return locale.monthsShortRegex(isStrict); + }); + addRegexToken('MMMM', function (isStrict, locale) { + return locale.monthsRegex(isStrict); + }); + + addParseToken(['M', 'MM'], function (input, array) { + array[MONTH] = toInt(input) - 1; + }); + + addParseToken(['MMM', 'MMMM'], function (input, array, config, token) { + var month = config._locale.monthsParse(input, token, config._strict); + // if we didn't find a month name, mark the date as invalid. + if (month != null) { + array[MONTH] = month; + } else { + getParsingFlags(config).invalidMonth = input; + } + }); + + // LOCALES + + var MONTHS_IN_FORMAT = /D[oD]?(\[[^\[\]]*\]|\s+)+MMMM?/; + var defaultLocaleMonths = 'January_February_March_April_May_June_July_August_September_October_November_December'.split('_'); + function localeMonths (m, format) { + return isArray(this._months) ? this._months[m.month()] : + this._months[(this._months.isFormat || MONTHS_IN_FORMAT).test(format) ? 'format' : 'standalone'][m.month()]; + } + + var defaultLocaleMonthsShort = 'Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec'.split('_'); + function localeMonthsShort (m, format) { + return isArray(this._monthsShort) ? this._monthsShort[m.month()] : + this._monthsShort[MONTHS_IN_FORMAT.test(format) ? 'format' : 'standalone'][m.month()]; + } + + function units_month__handleStrictParse(monthName, format, strict) { + var i, ii, mom, llc = monthName.toLocaleLowerCase(); + if (!this._monthsParse) { + // this is not used + this._monthsParse = []; + this._longMonthsParse = []; + this._shortMonthsParse = []; + for (i = 0; i < 12; ++i) { + mom = create_utc__createUTC([2000, i]); + this._shortMonthsParse[i] = this.monthsShort(mom, '').toLocaleLowerCase(); + this._longMonthsParse[i] = this.months(mom, '').toLocaleLowerCase(); + } + } + + if (strict) { + if (format === 'MMM') { + ii = indexOf.call(this._shortMonthsParse, llc); + return ii !== -1 ? ii : null; + } else { + ii = indexOf.call(this._longMonthsParse, llc); + return ii !== -1 ? ii : null; + } + } else { + if (format === 'MMM') { + ii = indexOf.call(this._shortMonthsParse, llc); + if (ii !== -1) { + return ii; + } + ii = indexOf.call(this._longMonthsParse, llc); + return ii !== -1 ? ii : null; + } else { + ii = indexOf.call(this._longMonthsParse, llc); + if (ii !== -1) { + return ii; + } + ii = indexOf.call(this._shortMonthsParse, llc); + return ii !== -1 ? ii : null; + } + } + } + + function localeMonthsParse (monthName, format, strict) { + var i, mom, regex; + + if (this._monthsParseExact) { + return units_month__handleStrictParse.call(this, monthName, format, strict); + } + + if (!this._monthsParse) { + this._monthsParse = []; + this._longMonthsParse = []; + this._shortMonthsParse = []; + } + + // TODO: add sorting + // Sorting makes sure if one month (or abbr) is a prefix of another + // see sorting in computeMonthsParse + for (i = 0; i < 12; i++) { + // make the regex if we don't have it already + mom = create_utc__createUTC([2000, i]); + if (strict && !this._longMonthsParse[i]) { + this._longMonthsParse[i] = new RegExp('^' + this.months(mom, '').replace('.', '') + '$', 'i'); + this._shortMonthsParse[i] = new RegExp('^' + this.monthsShort(mom, '').replace('.', '') + '$', 'i'); + } + if (!strict && !this._monthsParse[i]) { + regex = '^' + this.months(mom, '') + '|^' + this.monthsShort(mom, ''); + this._monthsParse[i] = new RegExp(regex.replace('.', ''), 'i'); + } + // test the regex + if (strict && format === 'MMMM' && this._longMonthsParse[i].test(monthName)) { + return i; + } else if (strict && format === 'MMM' && this._shortMonthsParse[i].test(monthName)) { + return i; + } else if (!strict && this._monthsParse[i].test(monthName)) { + return i; + } + } + } + + // MOMENTS + + function setMonth (mom, value) { + var dayOfMonth; + + if (!mom.isValid()) { + // No op + return mom; + } + + if (typeof value === 'string') { + if (/^\d+$/.test(value)) { + value = toInt(value); + } else { + value = mom.localeData().monthsParse(value); + // TODO: Another silent failure? + if (typeof value !== 'number') { + return mom; + } + } + } + + dayOfMonth = Math.min(mom.date(), daysInMonth(mom.year(), value)); + mom._d['set' + (mom._isUTC ? 'UTC' : '') + 'Month'](value, dayOfMonth); + return mom; + } + + function getSetMonth (value) { + if (value != null) { + setMonth(this, value); + utils_hooks__hooks.updateOffset(this, true); + return this; + } else { + return get_set__get(this, 'Month'); + } + } + + function getDaysInMonth () { + return daysInMonth(this.year(), this.month()); + } + + var defaultMonthsShortRegex = matchWord; + function monthsShortRegex (isStrict) { + if (this._monthsParseExact) { + if (!hasOwnProp(this, '_monthsRegex')) { + computeMonthsParse.call(this); + } + if (isStrict) { + return this._monthsShortStrictRegex; + } else { + return this._monthsShortRegex; + } + } else { + if (!hasOwnProp(this, '_monthsShortRegex')) { + this._monthsShortRegex = defaultMonthsShortRegex; + } + return this._monthsShortStrictRegex && isStrict ? + this._monthsShortStrictRegex : this._monthsShortRegex; + } + } + + var defaultMonthsRegex = matchWord; + function monthsRegex (isStrict) { + if (this._monthsParseExact) { + if (!hasOwnProp(this, '_monthsRegex')) { + computeMonthsParse.call(this); + } + if (isStrict) { + return this._monthsStrictRegex; + } else { + return this._monthsRegex; + } + } else { + if (!hasOwnProp(this, '_monthsRegex')) { + this._monthsRegex = defaultMonthsRegex; + } + return this._monthsStrictRegex && isStrict ? + this._monthsStrictRegex : this._monthsRegex; + } + } + + function computeMonthsParse () { + function cmpLenRev(a, b) { + return b.length - a.length; + } + + var shortPieces = [], longPieces = [], mixedPieces = [], + i, mom; + for (i = 0; i < 12; i++) { + // make the regex if we don't have it already + mom = create_utc__createUTC([2000, i]); + shortPieces.push(this.monthsShort(mom, '')); + longPieces.push(this.months(mom, '')); + mixedPieces.push(this.months(mom, '')); + mixedPieces.push(this.monthsShort(mom, '')); + } + // Sorting makes sure if one month (or abbr) is a prefix of another it + // will match the longer piece. + shortPieces.sort(cmpLenRev); + longPieces.sort(cmpLenRev); + mixedPieces.sort(cmpLenRev); + for (i = 0; i < 12; i++) { + shortPieces[i] = regexEscape(shortPieces[i]); + longPieces[i] = regexEscape(longPieces[i]); + } + for (i = 0; i < 24; i++) { + mixedPieces[i] = regexEscape(mixedPieces[i]); + } + + this._monthsRegex = new RegExp('^(' + mixedPieces.join('|') + ')', 'i'); + this._monthsShortRegex = this._monthsRegex; + this._monthsStrictRegex = new RegExp('^(' + longPieces.join('|') + ')', 'i'); + this._monthsShortStrictRegex = new RegExp('^(' + shortPieces.join('|') + ')', 'i'); + } + + // FORMATTING + + addFormatToken('Y', 0, 0, function () { + var y = this.year(); + return y <= 9999 ? '' + y : '+' + y; + }); + + addFormatToken(0, ['YY', 2], 0, function () { + return this.year() % 100; + }); + + addFormatToken(0, ['YYYY', 4], 0, 'year'); + addFormatToken(0, ['YYYYY', 5], 0, 'year'); + addFormatToken(0, ['YYYYYY', 6, true], 0, 'year'); + + // ALIASES + + addUnitAlias('year', 'y'); + + // PRIORITIES + + addUnitPriority('year', 1); + + // PARSING + + addRegexToken('Y', matchSigned); + addRegexToken('YY', match1to2, match2); + addRegexToken('YYYY', match1to4, match4); + addRegexToken('YYYYY', match1to6, match6); + addRegexToken('YYYYYY', match1to6, match6); + + addParseToken(['YYYYY', 'YYYYYY'], YEAR); + addParseToken('YYYY', function (input, array) { + array[YEAR] = input.length === 2 ? utils_hooks__hooks.parseTwoDigitYear(input) : toInt(input); + }); + addParseToken('YY', function (input, array) { + array[YEAR] = utils_hooks__hooks.parseTwoDigitYear(input); + }); + addParseToken('Y', function (input, array) { + array[YEAR] = parseInt(input, 10); + }); + + // HELPERS + + function daysInYear(year) { + return isLeapYear(year) ? 366 : 365; + } + + function isLeapYear(year) { + return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0; + } + + // HOOKS + + utils_hooks__hooks.parseTwoDigitYear = function (input) { + return toInt(input) + (toInt(input) > 68 ? 1900 : 2000); + }; + + // MOMENTS + + var getSetYear = makeGetSet('FullYear', true); + + function getIsLeapYear () { + return isLeapYear(this.year()); + } + + function createDate (y, m, d, h, M, s, ms) { + //can't just apply() to create a date: + //http://stackoverflow.com/questions/181348/instantiating-a-javascript-object-by-calling-prototype-constructor-apply + var date = new Date(y, m, d, h, M, s, ms); + + //the date constructor remaps years 0-99 to 1900-1999 + if (y < 100 && y >= 0 && isFinite(date.getFullYear())) { + date.setFullYear(y); + } + return date; + } + + function createUTCDate (y) { + var date = new Date(Date.UTC.apply(null, arguments)); + + //the Date.UTC function remaps years 0-99 to 1900-1999 + if (y < 100 && y >= 0 && isFinite(date.getUTCFullYear())) { + date.setUTCFullYear(y); + } + return date; + } + + // start-of-first-week - start-of-year + function firstWeekOffset(year, dow, doy) { + var // first-week day -- which january is always in the first week (4 for iso, 1 for other) + fwd = 7 + dow - doy, + // first-week day local weekday -- which local weekday is fwd + fwdlw = (7 + createUTCDate(year, 0, fwd).getUTCDay() - dow) % 7; + + return -fwdlw + fwd - 1; + } + + //http://en.wikipedia.org/wiki/ISO_week_date#Calculating_a_date_given_the_year.2C_week_number_and_weekday + function dayOfYearFromWeeks(year, week, weekday, dow, doy) { + var localWeekday = (7 + weekday - dow) % 7, + weekOffset = firstWeekOffset(year, dow, doy), + dayOfYear = 1 + 7 * (week - 1) + localWeekday + weekOffset, + resYear, resDayOfYear; + + if (dayOfYear <= 0) { + resYear = year - 1; + resDayOfYear = daysInYear(resYear) + dayOfYear; + } else if (dayOfYear > daysInYear(year)) { + resYear = year + 1; + resDayOfYear = dayOfYear - daysInYear(year); + } else { + resYear = year; + resDayOfYear = dayOfYear; + } + + return { + year: resYear, + dayOfYear: resDayOfYear + }; + } + + function weekOfYear(mom, dow, doy) { + var weekOffset = firstWeekOffset(mom.year(), dow, doy), + week = Math.floor((mom.dayOfYear() - weekOffset - 1) / 7) + 1, + resWeek, resYear; + + if (week < 1) { + resYear = mom.year() - 1; + resWeek = week + weeksInYear(resYear, dow, doy); + } else if (week > weeksInYear(mom.year(), dow, doy)) { + resWeek = week - weeksInYear(mom.year(), dow, doy); + resYear = mom.year() + 1; + } else { + resYear = mom.year(); + resWeek = week; + } + + return { + week: resWeek, + year: resYear + }; + } + + function weeksInYear(year, dow, doy) { + var weekOffset = firstWeekOffset(year, dow, doy), + weekOffsetNext = firstWeekOffset(year + 1, dow, doy); + return (daysInYear(year) - weekOffset + weekOffsetNext) / 7; + } + + // FORMATTING + + addFormatToken('w', ['ww', 2], 'wo', 'week'); + addFormatToken('W', ['WW', 2], 'Wo', 'isoWeek'); + + // ALIASES + + addUnitAlias('week', 'w'); + addUnitAlias('isoWeek', 'W'); + + // PRIORITIES + + addUnitPriority('week', 5); + addUnitPriority('isoWeek', 5); + + // PARSING + + addRegexToken('w', match1to2); + addRegexToken('ww', match1to2, match2); + addRegexToken('W', match1to2); + addRegexToken('WW', match1to2, match2); + + addWeekParseToken(['w', 'ww', 'W', 'WW'], function (input, week, config, token) { + week[token.substr(0, 1)] = toInt(input); + }); + + // HELPERS + + // LOCALES + + function localeWeek (mom) { + return weekOfYear(mom, this._week.dow, this._week.doy).week; + } + + var defaultLocaleWeek = { + dow : 0, // Sunday is the first day of the week. + doy : 6 // The week that contains Jan 1st is the first week of the year. + }; + + function localeFirstDayOfWeek () { + return this._week.dow; + } + + function localeFirstDayOfYear () { + return this._week.doy; + } + + // MOMENTS + + function getSetWeek (input) { + var week = this.localeData().week(this); + return input == null ? week : this.add((input - week) * 7, 'd'); + } + + function getSetISOWeek (input) { + var week = weekOfYear(this, 1, 4).week; + return input == null ? week : this.add((input - week) * 7, 'd'); + } + + // FORMATTING + + addFormatToken('d', 0, 'do', 'day'); + + addFormatToken('dd', 0, 0, function (format) { + return this.localeData().weekdaysMin(this, format); + }); + + addFormatToken('ddd', 0, 0, function (format) { + return this.localeData().weekdaysShort(this, format); + }); + + addFormatToken('dddd', 0, 0, function (format) { + return this.localeData().weekdays(this, format); + }); + + addFormatToken('e', 0, 0, 'weekday'); + addFormatToken('E', 0, 0, 'isoWeekday'); + + // ALIASES + + addUnitAlias('day', 'd'); + addUnitAlias('weekday', 'e'); + addUnitAlias('isoWeekday', 'E'); + + // PRIORITY + addUnitPriority('day', 11); + addUnitPriority('weekday', 11); + addUnitPriority('isoWeekday', 11); + + // PARSING + + addRegexToken('d', match1to2); + addRegexToken('e', match1to2); + addRegexToken('E', match1to2); + addRegexToken('dd', function (isStrict, locale) { + return locale.weekdaysMinRegex(isStrict); + }); + addRegexToken('ddd', function (isStrict, locale) { + return locale.weekdaysShortRegex(isStrict); + }); + addRegexToken('dddd', function (isStrict, locale) { + return locale.weekdaysRegex(isStrict); + }); + + addWeekParseToken(['dd', 'ddd', 'dddd'], function (input, week, config, token) { + var weekday = config._locale.weekdaysParse(input, token, config._strict); + // if we didn't get a weekday name, mark the date as invalid + if (weekday != null) { + week.d = weekday; + } else { + getParsingFlags(config).invalidWeekday = input; + } + }); + + addWeekParseToken(['d', 'e', 'E'], function (input, week, config, token) { + week[token] = toInt(input); + }); + + // HELPERS + + function parseWeekday(input, locale) { + if (typeof input !== 'string') { + return input; + } + + if (!isNaN(input)) { + return parseInt(input, 10); + } + + input = locale.weekdaysParse(input); + if (typeof input === 'number') { + return input; + } + + return null; + } + + function parseIsoWeekday(input, locale) { + if (typeof input === 'string') { + return locale.weekdaysParse(input) % 7 || 7; + } + return isNaN(input) ? null : input; + } + + // LOCALES + + var defaultLocaleWeekdays = 'Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split('_'); + function localeWeekdays (m, format) { + return isArray(this._weekdays) ? this._weekdays[m.day()] : + this._weekdays[this._weekdays.isFormat.test(format) ? 'format' : 'standalone'][m.day()]; + } + + var defaultLocaleWeekdaysShort = 'Sun_Mon_Tue_Wed_Thu_Fri_Sat'.split('_'); + function localeWeekdaysShort (m) { + return this._weekdaysShort[m.day()]; + } + + var defaultLocaleWeekdaysMin = 'Su_Mo_Tu_We_Th_Fr_Sa'.split('_'); + function localeWeekdaysMin (m) { + return this._weekdaysMin[m.day()]; + } + + function day_of_week__handleStrictParse(weekdayName, format, strict) { + var i, ii, mom, llc = weekdayName.toLocaleLowerCase(); + if (!this._weekdaysParse) { + this._weekdaysParse = []; + this._shortWeekdaysParse = []; + this._minWeekdaysParse = []; + + for (i = 0; i < 7; ++i) { + mom = create_utc__createUTC([2000, 1]).day(i); + this._minWeekdaysParse[i] = this.weekdaysMin(mom, '').toLocaleLowerCase(); + this._shortWeekdaysParse[i] = this.weekdaysShort(mom, '').toLocaleLowerCase(); + this._weekdaysParse[i] = this.weekdays(mom, '').toLocaleLowerCase(); + } + } + + if (strict) { + if (format === 'dddd') { + ii = indexOf.call(this._weekdaysParse, llc); + return ii !== -1 ? ii : null; + } else if (format === 'ddd') { + ii = indexOf.call(this._shortWeekdaysParse, llc); + return ii !== -1 ? ii : null; + } else { + ii = indexOf.call(this._minWeekdaysParse, llc); + return ii !== -1 ? ii : null; + } + } else { + if (format === 'dddd') { + ii = indexOf.call(this._weekdaysParse, llc); + if (ii !== -1) { + return ii; + } + ii = indexOf.call(this._shortWeekdaysParse, llc); + if (ii !== -1) { + return ii; + } + ii = indexOf.call(this._minWeekdaysParse, llc); + return ii !== -1 ? ii : null; + } else if (format === 'ddd') { + ii = indexOf.call(this._shortWeekdaysParse, llc); + if (ii !== -1) { + return ii; + } + ii = indexOf.call(this._weekdaysParse, llc); + if (ii !== -1) { + return ii; + } + ii = indexOf.call(this._minWeekdaysParse, llc); + return ii !== -1 ? ii : null; + } else { + ii = indexOf.call(this._minWeekdaysParse, llc); + if (ii !== -1) { + return ii; + } + ii = indexOf.call(this._weekdaysParse, llc); + if (ii !== -1) { + return ii; + } + ii = indexOf.call(this._shortWeekdaysParse, llc); + return ii !== -1 ? ii : null; + } + } + } + + function localeWeekdaysParse (weekdayName, format, strict) { + var i, mom, regex; + + if (this._weekdaysParseExact) { + return day_of_week__handleStrictParse.call(this, weekdayName, format, strict); + } + + if (!this._weekdaysParse) { + this._weekdaysParse = []; + this._minWeekdaysParse = []; + this._shortWeekdaysParse = []; + this._fullWeekdaysParse = []; + } + + for (i = 0; i < 7; i++) { + // make the regex if we don't have it already + + mom = create_utc__createUTC([2000, 1]).day(i); + if (strict && !this._fullWeekdaysParse[i]) { + this._fullWeekdaysParse[i] = new RegExp('^' + this.weekdays(mom, '').replace('.', '\.?') + '$', 'i'); + this._shortWeekdaysParse[i] = new RegExp('^' + this.weekdaysShort(mom, '').replace('.', '\.?') + '$', 'i'); + this._minWeekdaysParse[i] = new RegExp('^' + this.weekdaysMin(mom, '').replace('.', '\.?') + '$', 'i'); + } + if (!this._weekdaysParse[i]) { + regex = '^' + this.weekdays(mom, '') + '|^' + this.weekdaysShort(mom, '') + '|^' + this.weekdaysMin(mom, ''); + this._weekdaysParse[i] = new RegExp(regex.replace('.', ''), 'i'); + } + // test the regex + if (strict && format === 'dddd' && this._fullWeekdaysParse[i].test(weekdayName)) { + return i; + } else if (strict && format === 'ddd' && this._shortWeekdaysParse[i].test(weekdayName)) { + return i; + } else if (strict && format === 'dd' && this._minWeekdaysParse[i].test(weekdayName)) { + return i; + } else if (!strict && this._weekdaysParse[i].test(weekdayName)) { + return i; + } + } + } + + // MOMENTS + + function getSetDayOfWeek (input) { + if (!this.isValid()) { + return input != null ? this : NaN; + } + var day = this._isUTC ? this._d.getUTCDay() : this._d.getDay(); + if (input != null) { + input = parseWeekday(input, this.localeData()); + return this.add(input - day, 'd'); + } else { + return day; + } + } + + function getSetLocaleDayOfWeek (input) { + if (!this.isValid()) { + return input != null ? this : NaN; + } + var weekday = (this.day() + 7 - this.localeData()._week.dow) % 7; + return input == null ? weekday : this.add(input - weekday, 'd'); + } + + function getSetISODayOfWeek (input) { + if (!this.isValid()) { + return input != null ? this : NaN; + } + + // behaves the same as moment#day except + // as a getter, returns 7 instead of 0 (1-7 range instead of 0-6) + // as a setter, sunday should belong to the previous week. + + if (input != null) { + var weekday = parseIsoWeekday(input, this.localeData()); + return this.day(this.day() % 7 ? weekday : weekday - 7); + } else { + return this.day() || 7; + } + } + + var defaultWeekdaysRegex = matchWord; + function weekdaysRegex (isStrict) { + if (this._weekdaysParseExact) { + if (!hasOwnProp(this, '_weekdaysRegex')) { + computeWeekdaysParse.call(this); + } + if (isStrict) { + return this._weekdaysStrictRegex; + } else { + return this._weekdaysRegex; + } + } else { + if (!hasOwnProp(this, '_weekdaysRegex')) { + this._weekdaysRegex = defaultWeekdaysRegex; + } + return this._weekdaysStrictRegex && isStrict ? + this._weekdaysStrictRegex : this._weekdaysRegex; + } + } + + var defaultWeekdaysShortRegex = matchWord; + function weekdaysShortRegex (isStrict) { + if (this._weekdaysParseExact) { + if (!hasOwnProp(this, '_weekdaysRegex')) { + computeWeekdaysParse.call(this); + } + if (isStrict) { + return this._weekdaysShortStrictRegex; + } else { + return this._weekdaysShortRegex; + } + } else { + if (!hasOwnProp(this, '_weekdaysShortRegex')) { + this._weekdaysShortRegex = defaultWeekdaysShortRegex; + } + return this._weekdaysShortStrictRegex && isStrict ? + this._weekdaysShortStrictRegex : this._weekdaysShortRegex; + } + } + + var defaultWeekdaysMinRegex = matchWord; + function weekdaysMinRegex (isStrict) { + if (this._weekdaysParseExact) { + if (!hasOwnProp(this, '_weekdaysRegex')) { + computeWeekdaysParse.call(this); + } + if (isStrict) { + return this._weekdaysMinStrictRegex; + } else { + return this._weekdaysMinRegex; + } + } else { + if (!hasOwnProp(this, '_weekdaysMinRegex')) { + this._weekdaysMinRegex = defaultWeekdaysMinRegex; + } + return this._weekdaysMinStrictRegex && isStrict ? + this._weekdaysMinStrictRegex : this._weekdaysMinRegex; + } + } + + + function computeWeekdaysParse () { + function cmpLenRev(a, b) { + return b.length - a.length; + } + + var minPieces = [], shortPieces = [], longPieces = [], mixedPieces = [], + i, mom, minp, shortp, longp; + for (i = 0; i < 7; i++) { + // make the regex if we don't have it already + mom = create_utc__createUTC([2000, 1]).day(i); + minp = this.weekdaysMin(mom, ''); + shortp = this.weekdaysShort(mom, ''); + longp = this.weekdays(mom, ''); + minPieces.push(minp); + shortPieces.push(shortp); + longPieces.push(longp); + mixedPieces.push(minp); + mixedPieces.push(shortp); + mixedPieces.push(longp); + } + // Sorting makes sure if one weekday (or abbr) is a prefix of another it + // will match the longer piece. + minPieces.sort(cmpLenRev); + shortPieces.sort(cmpLenRev); + longPieces.sort(cmpLenRev); + mixedPieces.sort(cmpLenRev); + for (i = 0; i < 7; i++) { + shortPieces[i] = regexEscape(shortPieces[i]); + longPieces[i] = regexEscape(longPieces[i]); + mixedPieces[i] = regexEscape(mixedPieces[i]); + } + + this._weekdaysRegex = new RegExp('^(' + mixedPieces.join('|') + ')', 'i'); + this._weekdaysShortRegex = this._weekdaysRegex; + this._weekdaysMinRegex = this._weekdaysRegex; + + this._weekdaysStrictRegex = new RegExp('^(' + longPieces.join('|') + ')', 'i'); + this._weekdaysShortStrictRegex = new RegExp('^(' + shortPieces.join('|') + ')', 'i'); + this._weekdaysMinStrictRegex = new RegExp('^(' + minPieces.join('|') + ')', 'i'); + } + + // FORMATTING + + function hFormat() { + return this.hours() % 12 || 12; + } + + function kFormat() { + return this.hours() || 24; + } + + addFormatToken('H', ['HH', 2], 0, 'hour'); + addFormatToken('h', ['hh', 2], 0, hFormat); + addFormatToken('k', ['kk', 2], 0, kFormat); + + addFormatToken('hmm', 0, 0, function () { + return '' + hFormat.apply(this) + zeroFill(this.minutes(), 2); + }); + + addFormatToken('hmmss', 0, 0, function () { + return '' + hFormat.apply(this) + zeroFill(this.minutes(), 2) + + zeroFill(this.seconds(), 2); + }); + + addFormatToken('Hmm', 0, 0, function () { + return '' + this.hours() + zeroFill(this.minutes(), 2); + }); + + addFormatToken('Hmmss', 0, 0, function () { + return '' + this.hours() + zeroFill(this.minutes(), 2) + + zeroFill(this.seconds(), 2); + }); + + function meridiem (token, lowercase) { + addFormatToken(token, 0, 0, function () { + return this.localeData().meridiem(this.hours(), this.minutes(), lowercase); + }); + } + + meridiem('a', true); + meridiem('A', false); + + // ALIASES + + addUnitAlias('hour', 'h'); + + // PRIORITY + addUnitPriority('hour', 13); + + // PARSING + + function matchMeridiem (isStrict, locale) { + return locale._meridiemParse; + } + + addRegexToken('a', matchMeridiem); + addRegexToken('A', matchMeridiem); + addRegexToken('H', match1to2); + addRegexToken('h', match1to2); + addRegexToken('HH', match1to2, match2); + addRegexToken('hh', match1to2, match2); + + addRegexToken('hmm', match3to4); + addRegexToken('hmmss', match5to6); + addRegexToken('Hmm', match3to4); + addRegexToken('Hmmss', match5to6); + + addParseToken(['H', 'HH'], HOUR); + addParseToken(['a', 'A'], function (input, array, config) { + config._isPm = config._locale.isPM(input); + config._meridiem = input; + }); + addParseToken(['h', 'hh'], function (input, array, config) { + array[HOUR] = toInt(input); + getParsingFlags(config).bigHour = true; + }); + addParseToken('hmm', function (input, array, config) { + var pos = input.length - 2; + array[HOUR] = toInt(input.substr(0, pos)); + array[MINUTE] = toInt(input.substr(pos)); + getParsingFlags(config).bigHour = true; + }); + addParseToken('hmmss', function (input, array, config) { + var pos1 = input.length - 4; + var pos2 = input.length - 2; + array[HOUR] = toInt(input.substr(0, pos1)); + array[MINUTE] = toInt(input.substr(pos1, 2)); + array[SECOND] = toInt(input.substr(pos2)); + getParsingFlags(config).bigHour = true; + }); + addParseToken('Hmm', function (input, array, config) { + var pos = input.length - 2; + array[HOUR] = toInt(input.substr(0, pos)); + array[MINUTE] = toInt(input.substr(pos)); + }); + addParseToken('Hmmss', function (input, array, config) { + var pos1 = input.length - 4; + var pos2 = input.length - 2; + array[HOUR] = toInt(input.substr(0, pos1)); + array[MINUTE] = toInt(input.substr(pos1, 2)); + array[SECOND] = toInt(input.substr(pos2)); + }); + + // LOCALES + + function localeIsPM (input) { + // IE8 Quirks Mode & IE7 Standards Mode do not allow accessing strings like arrays + // Using charAt should be more compatible. + return ((input + '').toLowerCase().charAt(0) === 'p'); + } + + var defaultLocaleMeridiemParse = /[ap]\.?m?\.?/i; + function localeMeridiem (hours, minutes, isLower) { + if (hours > 11) { + return isLower ? 'pm' : 'PM'; + } else { + return isLower ? 'am' : 'AM'; + } + } + + + // MOMENTS + + // Setting the hour should keep the time, because the user explicitly + // specified which hour he wants. So trying to maintain the same hour (in + // a new timezone) makes sense. Adding/subtracting hours does not follow + // this rule. + var getSetHour = makeGetSet('Hours', true); + + var baseConfig = { + calendar: defaultCalendar, + longDateFormat: defaultLongDateFormat, + invalidDate: defaultInvalidDate, + ordinal: defaultOrdinal, + ordinalParse: defaultOrdinalParse, + relativeTime: defaultRelativeTime, + + months: defaultLocaleMonths, + monthsShort: defaultLocaleMonthsShort, + + week: defaultLocaleWeek, + + weekdays: defaultLocaleWeekdays, + weekdaysMin: defaultLocaleWeekdaysMin, + weekdaysShort: defaultLocaleWeekdaysShort, + + meridiemParse: defaultLocaleMeridiemParse + }; + + // internal storage for locale config files + var locales = {}; + var globalLocale; + + function normalizeLocale(key) { + return key ? key.toLowerCase().replace('_', '-') : key; + } + + // pick the locale from the array + // try ['en-au', 'en-gb'] as 'en-au', 'en-gb', 'en', as in move through the list trying each + // substring from most specific to least, but move to the next array item if it's a more specific variant than the current root + function chooseLocale(names) { + var i = 0, j, next, locale, split; + + while (i < names.length) { + split = normalizeLocale(names[i]).split('-'); + j = split.length; + next = normalizeLocale(names[i + 1]); + next = next ? next.split('-') : null; + while (j > 0) { + locale = loadLocale(split.slice(0, j).join('-')); + if (locale) { + return locale; + } + if (next && next.length >= j && compareArrays(split, next, true) >= j - 1) { + //the next array item is better than a shallower substring of this one + break; + } + j--; + } + i++; + } + return null; + } + + function loadLocale(name) { + var oldLocale = null; + // TODO: Find a better way to register and load all the locales in Node + if (!locales[name] && (typeof module !== 'undefined') && + module && module.exports) { + try { + oldLocale = globalLocale._abbr; + require('./locale/' + name); + // because defineLocale currently also sets the global locale, we + // want to undo that for lazy loaded locales + locale_locales__getSetGlobalLocale(oldLocale); + } catch (e) { } + } + return locales[name]; + } + + // This function will load locale and then set the global locale. If + // no arguments are passed in, it will simply return the current global + // locale key. + function locale_locales__getSetGlobalLocale (key, values) { + var data; + if (key) { + if (isUndefined(values)) { + data = locale_locales__getLocale(key); + } + else { + data = defineLocale(key, values); + } + + if (data) { + // moment.duration._locale = moment._locale = data; + globalLocale = data; + } + } + + return globalLocale._abbr; + } + + function defineLocale (name, config) { + if (config !== null) { + var parentConfig = baseConfig; + config.abbr = name; + if (locales[name] != null) { + deprecateSimple('defineLocaleOverride', + 'use moment.updateLocale(localeName, config) to change ' + + 'an existing locale. moment.defineLocale(localeName, ' + + 'config) should only be used for creating a new locale ' + + 'See http://momentjs.com/guides/#/warnings/define-locale/ for more info.'); + parentConfig = locales[name]._config; + } else if (config.parentLocale != null) { + if (locales[config.parentLocale] != null) { + parentConfig = locales[config.parentLocale]._config; + } else { + // treat as if there is no base config + deprecateSimple('parentLocaleUndefined', + 'specified parentLocale is not defined yet. See http://momentjs.com/guides/#/warnings/parent-locale/'); + } + } + locales[name] = new Locale(mergeConfigs(parentConfig, config)); + + // backwards compat for now: also set the locale + locale_locales__getSetGlobalLocale(name); + + return locales[name]; + } else { + // useful for testing + delete locales[name]; + return null; + } + } + + function updateLocale(name, config) { + if (config != null) { + var locale, parentConfig = baseConfig; + // MERGE + if (locales[name] != null) { + parentConfig = locales[name]._config; + } + config = mergeConfigs(parentConfig, config); + locale = new Locale(config); + locale.parentLocale = locales[name]; + locales[name] = locale; + + // backwards compat for now: also set the locale + locale_locales__getSetGlobalLocale(name); + } else { + // pass null for config to unupdate, useful for tests + if (locales[name] != null) { + if (locales[name].parentLocale != null) { + locales[name] = locales[name].parentLocale; + } else if (locales[name] != null) { + delete locales[name]; + } + } + } + return locales[name]; + } + + // returns locale data + function locale_locales__getLocale (key) { + var locale; + + if (key && key._locale && key._locale._abbr) { + key = key._locale._abbr; + } + + if (!key) { + return globalLocale; + } + + if (!isArray(key)) { + //short-circuit everything else + locale = loadLocale(key); + if (locale) { + return locale; + } + key = [key]; + } + + return chooseLocale(key); + } + + function locale_locales__listLocales() { + return keys(locales); + } + + function checkOverflow (m) { + var overflow; + var a = m._a; + + if (a && getParsingFlags(m).overflow === -2) { + overflow = + a[MONTH] < 0 || a[MONTH] > 11 ? MONTH : + a[DATE] < 1 || a[DATE] > daysInMonth(a[YEAR], a[MONTH]) ? DATE : + a[HOUR] < 0 || a[HOUR] > 24 || (a[HOUR] === 24 && (a[MINUTE] !== 0 || a[SECOND] !== 0 || a[MILLISECOND] !== 0)) ? HOUR : + a[MINUTE] < 0 || a[MINUTE] > 59 ? MINUTE : + a[SECOND] < 0 || a[SECOND] > 59 ? SECOND : + a[MILLISECOND] < 0 || a[MILLISECOND] > 999 ? MILLISECOND : + -1; + + if (getParsingFlags(m)._overflowDayOfYear && (overflow < YEAR || overflow > DATE)) { + overflow = DATE; + } + if (getParsingFlags(m)._overflowWeeks && overflow === -1) { + overflow = WEEK; + } + if (getParsingFlags(m)._overflowWeekday && overflow === -1) { + overflow = WEEKDAY; + } + + getParsingFlags(m).overflow = overflow; + } + + return m; + } + + // iso 8601 regex + // 0000-00-00 0000-W00 or 0000-W00-0 + T + 00 or 00:00 or 00:00:00 or 00:00:00.000 + +00:00 or +0000 or +00) + var extendedIsoRegex = /^\s*((?:[+-]\d{6}|\d{4})-(?:\d\d-\d\d|W\d\d-\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?::\d\d(?::\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?/; + var basicIsoRegex = /^\s*((?:[+-]\d{6}|\d{4})(?:\d\d\d\d|W\d\d\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?:\d\d(?:\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?/; + + var tzRegex = /Z|[+-]\d\d(?::?\d\d)?/; + + var isoDates = [ + ['YYYYYY-MM-DD', /[+-]\d{6}-\d\d-\d\d/], + ['YYYY-MM-DD', /\d{4}-\d\d-\d\d/], + ['GGGG-[W]WW-E', /\d{4}-W\d\d-\d/], + ['GGGG-[W]WW', /\d{4}-W\d\d/, false], + ['YYYY-DDD', /\d{4}-\d{3}/], + ['YYYY-MM', /\d{4}-\d\d/, false], + ['YYYYYYMMDD', /[+-]\d{10}/], + ['YYYYMMDD', /\d{8}/], + // YYYYMM is NOT allowed by the standard + ['GGGG[W]WWE', /\d{4}W\d{3}/], + ['GGGG[W]WW', /\d{4}W\d{2}/, false], + ['YYYYDDD', /\d{7}/] + ]; + + // iso time formats and regexes + var isoTimes = [ + ['HH:mm:ss.SSSS', /\d\d:\d\d:\d\d\.\d+/], + ['HH:mm:ss,SSSS', /\d\d:\d\d:\d\d,\d+/], + ['HH:mm:ss', /\d\d:\d\d:\d\d/], + ['HH:mm', /\d\d:\d\d/], + ['HHmmss.SSSS', /\d\d\d\d\d\d\.\d+/], + ['HHmmss,SSSS', /\d\d\d\d\d\d,\d+/], + ['HHmmss', /\d\d\d\d\d\d/], + ['HHmm', /\d\d\d\d/], + ['HH', /\d\d/] + ]; + + var aspNetJsonRegex = /^\/?Date\((\-?\d+)/i; + + // date from iso format + function configFromISO(config) { + var i, l, + string = config._i, + match = extendedIsoRegex.exec(string) || basicIsoRegex.exec(string), + allowTime, dateFormat, timeFormat, tzFormat; + + if (match) { + getParsingFlags(config).iso = true; + + for (i = 0, l = isoDates.length; i < l; i++) { + if (isoDates[i][1].exec(match[1])) { + dateFormat = isoDates[i][0]; + allowTime = isoDates[i][2] !== false; + break; + } + } + if (dateFormat == null) { + config._isValid = false; + return; + } + if (match[3]) { + for (i = 0, l = isoTimes.length; i < l; i++) { + if (isoTimes[i][1].exec(match[3])) { + // match[2] should be 'T' or space + timeFormat = (match[2] || ' ') + isoTimes[i][0]; + break; + } + } + if (timeFormat == null) { + config._isValid = false; + return; + } + } + if (!allowTime && timeFormat != null) { + config._isValid = false; + return; + } + if (match[4]) { + if (tzRegex.exec(match[4])) { + tzFormat = 'Z'; + } else { + config._isValid = false; + return; + } + } + config._f = dateFormat + (timeFormat || '') + (tzFormat || ''); + configFromStringAndFormat(config); + } else { + config._isValid = false; + } + } + + // date from iso format or fallback + function configFromString(config) { + var matched = aspNetJsonRegex.exec(config._i); + + if (matched !== null) { + config._d = new Date(+matched[1]); + return; + } + + configFromISO(config); + if (config._isValid === false) { + delete config._isValid; + utils_hooks__hooks.createFromInputFallback(config); + } + } + + utils_hooks__hooks.createFromInputFallback = deprecate( + 'moment construction falls back to js Date. This is ' + + 'discouraged and will be removed in upcoming major ' + + 'release. Please refer to ' + + 'http://momentjs.com/guides/#/warnings/js-date/ for more info.', + function (config) { + config._d = new Date(config._i + (config._useUTC ? ' UTC' : '')); + } + ); + + // Pick the first defined of two or three arguments. + function defaults(a, b, c) { + if (a != null) { + return a; + } + if (b != null) { + return b; + } + return c; + } + + function currentDateArray(config) { + // hooks is actually the exported moment object + var nowValue = new Date(utils_hooks__hooks.now()); + if (config._useUTC) { + return [nowValue.getUTCFullYear(), nowValue.getUTCMonth(), nowValue.getUTCDate()]; + } + return [nowValue.getFullYear(), nowValue.getMonth(), nowValue.getDate()]; + } + + // convert an array to a date. + // the array should mirror the parameters below + // note: all values past the year are optional and will default to the lowest possible value. + // [year, month, day , hour, minute, second, millisecond] + function configFromArray (config) { + var i, date, input = [], currentDate, yearToUse; + + if (config._d) { + return; + } + + currentDate = currentDateArray(config); + + //compute day of the year from weeks and weekdays + if (config._w && config._a[DATE] == null && config._a[MONTH] == null) { + dayOfYearFromWeekInfo(config); + } + + //if the day of the year is set, figure out what it is + if (config._dayOfYear) { + yearToUse = defaults(config._a[YEAR], currentDate[YEAR]); + + if (config._dayOfYear > daysInYear(yearToUse)) { + getParsingFlags(config)._overflowDayOfYear = true; + } + + date = createUTCDate(yearToUse, 0, config._dayOfYear); + config._a[MONTH] = date.getUTCMonth(); + config._a[DATE] = date.getUTCDate(); + } + + // Default to current date. + // * if no year, month, day of month are given, default to today + // * if day of month is given, default month and year + // * if month is given, default only year + // * if year is given, don't default anything + for (i = 0; i < 3 && config._a[i] == null; ++i) { + config._a[i] = input[i] = currentDate[i]; + } + + // Zero out whatever was not defaulted, including time + for (; i < 7; i++) { + config._a[i] = input[i] = (config._a[i] == null) ? (i === 2 ? 1 : 0) : config._a[i]; + } + + // Check for 24:00:00.000 + if (config._a[HOUR] === 24 && + config._a[MINUTE] === 0 && + config._a[SECOND] === 0 && + config._a[MILLISECOND] === 0) { + config._nextDay = true; + config._a[HOUR] = 0; + } + + config._d = (config._useUTC ? createUTCDate : createDate).apply(null, input); + // Apply timezone offset from input. The actual utcOffset can be changed + // with parseZone. + if (config._tzm != null) { + config._d.setUTCMinutes(config._d.getUTCMinutes() - config._tzm); + } + + if (config._nextDay) { + config._a[HOUR] = 24; + } + } + + function dayOfYearFromWeekInfo(config) { + var w, weekYear, week, weekday, dow, doy, temp, weekdayOverflow; + + w = config._w; + if (w.GG != null || w.W != null || w.E != null) { + dow = 1; + doy = 4; + + // TODO: We need to take the current isoWeekYear, but that depends on + // how we interpret now (local, utc, fixed offset). So create + // a now version of current config (take local/utc/offset flags, and + // create now). + weekYear = defaults(w.GG, config._a[YEAR], weekOfYear(local__createLocal(), 1, 4).year); + week = defaults(w.W, 1); + weekday = defaults(w.E, 1); + if (weekday < 1 || weekday > 7) { + weekdayOverflow = true; + } + } else { + dow = config._locale._week.dow; + doy = config._locale._week.doy; + + weekYear = defaults(w.gg, config._a[YEAR], weekOfYear(local__createLocal(), dow, doy).year); + week = defaults(w.w, 1); + + if (w.d != null) { + // weekday -- low day numbers are considered next week + weekday = w.d; + if (weekday < 0 || weekday > 6) { + weekdayOverflow = true; + } + } else if (w.e != null) { + // local weekday -- counting starts from begining of week + weekday = w.e + dow; + if (w.e < 0 || w.e > 6) { + weekdayOverflow = true; + } + } else { + // default to begining of week + weekday = dow; + } + } + if (week < 1 || week > weeksInYear(weekYear, dow, doy)) { + getParsingFlags(config)._overflowWeeks = true; + } else if (weekdayOverflow != null) { + getParsingFlags(config)._overflowWeekday = true; + } else { + temp = dayOfYearFromWeeks(weekYear, week, weekday, dow, doy); + config._a[YEAR] = temp.year; + config._dayOfYear = temp.dayOfYear; + } + } + + // constant that refers to the ISO standard + utils_hooks__hooks.ISO_8601 = function () {}; + + // date from string and format string + function configFromStringAndFormat(config) { + // TODO: Move this to another part of the creation flow to prevent circular deps + if (config._f === utils_hooks__hooks.ISO_8601) { + configFromISO(config); + return; + } + + config._a = []; + getParsingFlags(config).empty = true; + + // This array is used to make a Date, either with `new Date` or `Date.UTC` + var string = '' + config._i, + i, parsedInput, tokens, token, skipped, + stringLength = string.length, + totalParsedInputLength = 0; + + tokens = expandFormat(config._f, config._locale).match(formattingTokens) || []; + + for (i = 0; i < tokens.length; i++) { + token = tokens[i]; + parsedInput = (string.match(getParseRegexForToken(token, config)) || [])[0]; + // console.log('token', token, 'parsedInput', parsedInput, + // 'regex', getParseRegexForToken(token, config)); + if (parsedInput) { + skipped = string.substr(0, string.indexOf(parsedInput)); + if (skipped.length > 0) { + getParsingFlags(config).unusedInput.push(skipped); + } + string = string.slice(string.indexOf(parsedInput) + parsedInput.length); + totalParsedInputLength += parsedInput.length; + } + // don't parse if it's not a known token + if (formatTokenFunctions[token]) { + if (parsedInput) { + getParsingFlags(config).empty = false; + } + else { + getParsingFlags(config).unusedTokens.push(token); + } + addTimeToArrayFromToken(token, parsedInput, config); + } + else if (config._strict && !parsedInput) { + getParsingFlags(config).unusedTokens.push(token); + } + } + + // add remaining unparsed input length to the string + getParsingFlags(config).charsLeftOver = stringLength - totalParsedInputLength; + if (string.length > 0) { + getParsingFlags(config).unusedInput.push(string); + } + + // clear _12h flag if hour is <= 12 + if (config._a[HOUR] <= 12 && + getParsingFlags(config).bigHour === true && + config._a[HOUR] > 0) { + getParsingFlags(config).bigHour = undefined; + } + + getParsingFlags(config).parsedDateParts = config._a.slice(0); + getParsingFlags(config).meridiem = config._meridiem; + // handle meridiem + config._a[HOUR] = meridiemFixWrap(config._locale, config._a[HOUR], config._meridiem); + + configFromArray(config); + checkOverflow(config); + } + + + function meridiemFixWrap (locale, hour, meridiem) { + var isPm; + + if (meridiem == null) { + // nothing to do + return hour; + } + if (locale.meridiemHour != null) { + return locale.meridiemHour(hour, meridiem); + } else if (locale.isPM != null) { + // Fallback + isPm = locale.isPM(meridiem); + if (isPm && hour < 12) { + hour += 12; + } + if (!isPm && hour === 12) { + hour = 0; + } + return hour; + } else { + // this is not supposed to happen + return hour; + } + } + + // date from string and array of format strings + function configFromStringAndArray(config) { + var tempConfig, + bestMoment, + + scoreToBeat, + i, + currentScore; + + if (config._f.length === 0) { + getParsingFlags(config).invalidFormat = true; + config._d = new Date(NaN); + return; + } + + for (i = 0; i < config._f.length; i++) { + currentScore = 0; + tempConfig = copyConfig({}, config); + if (config._useUTC != null) { + tempConfig._useUTC = config._useUTC; + } + tempConfig._f = config._f[i]; + configFromStringAndFormat(tempConfig); + + if (!valid__isValid(tempConfig)) { + continue; + } + + // if there is any input that was not parsed add a penalty for that format + currentScore += getParsingFlags(tempConfig).charsLeftOver; + + //or tokens + currentScore += getParsingFlags(tempConfig).unusedTokens.length * 10; + + getParsingFlags(tempConfig).score = currentScore; + + if (scoreToBeat == null || currentScore < scoreToBeat) { + scoreToBeat = currentScore; + bestMoment = tempConfig; + } + } + + extend(config, bestMoment || tempConfig); + } + + function configFromObject(config) { + if (config._d) { + return; + } + + var i = normalizeObjectUnits(config._i); + config._a = map([i.year, i.month, i.day || i.date, i.hour, i.minute, i.second, i.millisecond], function (obj) { + return obj && parseInt(obj, 10); + }); + + configFromArray(config); + } + + function createFromConfig (config) { + var res = new Moment(checkOverflow(prepareConfig(config))); + if (res._nextDay) { + // Adding is smart enough around DST + res.add(1, 'd'); + res._nextDay = undefined; + } + + return res; + } + + function prepareConfig (config) { + var input = config._i, + format = config._f; + + config._locale = config._locale || locale_locales__getLocale(config._l); + + if (input === null || (format === undefined && input === '')) { + return valid__createInvalid({nullInput: true}); + } + + if (typeof input === 'string') { + config._i = input = config._locale.preparse(input); + } + + if (isMoment(input)) { + return new Moment(checkOverflow(input)); + } else if (isArray(format)) { + configFromStringAndArray(config); + } else if (isDate(input)) { + config._d = input; + } else if (format) { + configFromStringAndFormat(config); + } else { + configFromInput(config); + } + + if (!valid__isValid(config)) { + config._d = null; + } + + return config; + } + + function configFromInput(config) { + var input = config._i; + if (input === undefined) { + config._d = new Date(utils_hooks__hooks.now()); + } else if (isDate(input)) { + config._d = new Date(input.valueOf()); + } else if (typeof input === 'string') { + configFromString(config); + } else if (isArray(input)) { + config._a = map(input.slice(0), function (obj) { + return parseInt(obj, 10); + }); + configFromArray(config); + } else if (typeof(input) === 'object') { + configFromObject(config); + } else if (typeof(input) === 'number') { + // from milliseconds + config._d = new Date(input); + } else { + utils_hooks__hooks.createFromInputFallback(config); + } + } + + function createLocalOrUTC (input, format, locale, strict, isUTC) { + var c = {}; + + if (typeof(locale) === 'boolean') { + strict = locale; + locale = undefined; + } + + if ((isObject(input) && isObjectEmpty(input)) || + (isArray(input) && input.length === 0)) { + input = undefined; + } + // object construction must be done this way. + // https://github.com/moment/moment/issues/1423 + c._isAMomentObject = true; + c._useUTC = c._isUTC = isUTC; + c._l = locale; + c._i = input; + c._f = format; + c._strict = strict; + + return createFromConfig(c); + } + + function local__createLocal (input, format, locale, strict) { + return createLocalOrUTC(input, format, locale, strict, false); + } + + var prototypeMin = deprecate( + 'moment().min is deprecated, use moment.max instead. http://momentjs.com/guides/#/warnings/min-max/', + function () { + var other = local__createLocal.apply(null, arguments); + if (this.isValid() && other.isValid()) { + return other < this ? this : other; + } else { + return valid__createInvalid(); + } + } + ); + + var prototypeMax = deprecate( + 'moment().max is deprecated, use moment.min instead. http://momentjs.com/guides/#/warnings/min-max/', + function () { + var other = local__createLocal.apply(null, arguments); + if (this.isValid() && other.isValid()) { + return other > this ? this : other; + } else { + return valid__createInvalid(); + } + } + ); + + // Pick a moment m from moments so that m[fn](other) is true for all + // other. This relies on the function fn to be transitive. + // + // moments should either be an array of moment objects or an array, whose + // first element is an array of moment objects. + function pickBy(fn, moments) { + var res, i; + if (moments.length === 1 && isArray(moments[0])) { + moments = moments[0]; + } + if (!moments.length) { + return local__createLocal(); + } + res = moments[0]; + for (i = 1; i < moments.length; ++i) { + if (!moments[i].isValid() || moments[i][fn](res)) { + res = moments[i]; + } + } + return res; + } + + // TODO: Use [].sort instead? + function min () { + var args = [].slice.call(arguments, 0); + + return pickBy('isBefore', args); + } + + function max () { + var args = [].slice.call(arguments, 0); + + return pickBy('isAfter', args); + } + + var now = function () { + return Date.now ? Date.now() : +(new Date()); + }; + + function Duration (duration) { + var normalizedInput = normalizeObjectUnits(duration), + years = normalizedInput.year || 0, + quarters = normalizedInput.quarter || 0, + months = normalizedInput.month || 0, + weeks = normalizedInput.week || 0, + days = normalizedInput.day || 0, + hours = normalizedInput.hour || 0, + minutes = normalizedInput.minute || 0, + seconds = normalizedInput.second || 0, + milliseconds = normalizedInput.millisecond || 0; + + // representation for dateAddRemove + this._milliseconds = +milliseconds + + seconds * 1e3 + // 1000 + minutes * 6e4 + // 1000 * 60 + hours * 1000 * 60 * 60; //using 1000 * 60 * 60 instead of 36e5 to avoid floating point rounding errors https://github.com/moment/moment/issues/2978 + // Because of dateAddRemove treats 24 hours as different from a + // day when working around DST, we need to store them separately + this._days = +days + + weeks * 7; + // It is impossible translate months into days without knowing + // which months you are are talking about, so we have to store + // it separately. + this._months = +months + + quarters * 3 + + years * 12; + + this._data = {}; + + this._locale = locale_locales__getLocale(); + + this._bubble(); + } + + function isDuration (obj) { + return obj instanceof Duration; + } + + // FORMATTING + + function offset (token, separator) { + addFormatToken(token, 0, 0, function () { + var offset = this.utcOffset(); + var sign = '+'; + if (offset < 0) { + offset = -offset; + sign = '-'; + } + return sign + zeroFill(~~(offset / 60), 2) + separator + zeroFill(~~(offset) % 60, 2); + }); + } + + offset('Z', ':'); + offset('ZZ', ''); + + // PARSING + + addRegexToken('Z', matchShortOffset); + addRegexToken('ZZ', matchShortOffset); + addParseToken(['Z', 'ZZ'], function (input, array, config) { + config._useUTC = true; + config._tzm = offsetFromString(matchShortOffset, input); + }); + + // HELPERS + + // timezone chunker + // '+10:00' > ['10', '00'] + // '-1530' > ['-15', '30'] + var chunkOffset = /([\+\-]|\d\d)/gi; + + function offsetFromString(matcher, string) { + var matches = ((string || '').match(matcher) || []); + var chunk = matches[matches.length - 1] || []; + var parts = (chunk + '').match(chunkOffset) || ['-', 0, 0]; + var minutes = +(parts[1] * 60) + toInt(parts[2]); + + return parts[0] === '+' ? minutes : -minutes; + } + + // Return a moment from input, that is local/utc/zone equivalent to model. + function cloneWithOffset(input, model) { + var res, diff; + if (model._isUTC) { + res = model.clone(); + diff = (isMoment(input) || isDate(input) ? input.valueOf() : local__createLocal(input).valueOf()) - res.valueOf(); + // Use low-level api, because this fn is low-level api. + res._d.setTime(res._d.valueOf() + diff); + utils_hooks__hooks.updateOffset(res, false); + return res; + } else { + return local__createLocal(input).local(); + } + } + + function getDateOffset (m) { + // On Firefox.24 Date#getTimezoneOffset returns a floating point. + // https://github.com/moment/moment/pull/1871 + return -Math.round(m._d.getTimezoneOffset() / 15) * 15; + } + + // HOOKS + + // This function will be called whenever a moment is mutated. + // It is intended to keep the offset in sync with the timezone. + utils_hooks__hooks.updateOffset = function () {}; + + // MOMENTS + + // keepLocalTime = true means only change the timezone, without + // affecting the local hour. So 5:31:26 +0300 --[utcOffset(2, true)]--> + // 5:31:26 +0200 It is possible that 5:31:26 doesn't exist with offset + // +0200, so we adjust the time as needed, to be valid. + // + // Keeping the time actually adds/subtracts (one hour) + // from the actual represented time. That is why we call updateOffset + // a second time. In case it wants us to change the offset again + // _changeInProgress == true case, then we have to adjust, because + // there is no such time in the given timezone. + function getSetOffset (input, keepLocalTime) { + var offset = this._offset || 0, + localAdjust; + if (!this.isValid()) { + return input != null ? this : NaN; + } + if (input != null) { + if (typeof input === 'string') { + input = offsetFromString(matchShortOffset, input); + } else if (Math.abs(input) < 16) { + input = input * 60; + } + if (!this._isUTC && keepLocalTime) { + localAdjust = getDateOffset(this); + } + this._offset = input; + this._isUTC = true; + if (localAdjust != null) { + this.add(localAdjust, 'm'); + } + if (offset !== input) { + if (!keepLocalTime || this._changeInProgress) { + add_subtract__addSubtract(this, create__createDuration(input - offset, 'm'), 1, false); + } else if (!this._changeInProgress) { + this._changeInProgress = true; + utils_hooks__hooks.updateOffset(this, true); + this._changeInProgress = null; + } + } + return this; + } else { + return this._isUTC ? offset : getDateOffset(this); + } + } + + function getSetZone (input, keepLocalTime) { + if (input != null) { + if (typeof input !== 'string') { + input = -input; + } + + this.utcOffset(input, keepLocalTime); + + return this; + } else { + return -this.utcOffset(); + } + } + + function setOffsetToUTC (keepLocalTime) { + return this.utcOffset(0, keepLocalTime); + } + + function setOffsetToLocal (keepLocalTime) { + if (this._isUTC) { + this.utcOffset(0, keepLocalTime); + this._isUTC = false; + + if (keepLocalTime) { + this.subtract(getDateOffset(this), 'm'); + } + } + return this; + } + + function setOffsetToParsedOffset () { + if (this._tzm) { + this.utcOffset(this._tzm); + } else if (typeof this._i === 'string') { + this.utcOffset(offsetFromString(matchOffset, this._i)); + } + return this; + } + + function hasAlignedHourOffset (input) { + if (!this.isValid()) { + return false; + } + input = input ? local__createLocal(input).utcOffset() : 0; + + return (this.utcOffset() - input) % 60 === 0; + } + + function isDaylightSavingTime () { + return ( + this.utcOffset() > this.clone().month(0).utcOffset() || + this.utcOffset() > this.clone().month(5).utcOffset() + ); + } + + function isDaylightSavingTimeShifted () { + if (!isUndefined(this._isDSTShifted)) { + return this._isDSTShifted; + } + + var c = {}; + + copyConfig(c, this); + c = prepareConfig(c); + + if (c._a) { + var other = c._isUTC ? create_utc__createUTC(c._a) : local__createLocal(c._a); + this._isDSTShifted = this.isValid() && + compareArrays(c._a, other.toArray()) > 0; + } else { + this._isDSTShifted = false; + } + + return this._isDSTShifted; + } + + function isLocal () { + return this.isValid() ? !this._isUTC : false; + } + + function isUtcOffset () { + return this.isValid() ? this._isUTC : false; + } + + function isUtc () { + return this.isValid() ? this._isUTC && this._offset === 0 : false; + } + + // ASP.NET json date format regex + var aspNetRegex = /^(\-)?(?:(\d*)[. ])?(\d+)\:(\d+)(?:\:(\d+)\.?(\d{3})?\d*)?$/; + + // from http://docs.closure-library.googlecode.com/git/closure_goog_date_date.js.source.html + // somewhat more in line with 4.4.3.2 2004 spec, but allows decimal anywhere + // and further modified to allow for strings containing both week and day + var isoRegex = /^(-)?P(?:(-?[0-9,.]*)Y)?(?:(-?[0-9,.]*)M)?(?:(-?[0-9,.]*)W)?(?:(-?[0-9,.]*)D)?(?:T(?:(-?[0-9,.]*)H)?(?:(-?[0-9,.]*)M)?(?:(-?[0-9,.]*)S)?)?$/; + + function create__createDuration (input, key) { + var duration = input, + // matching against regexp is expensive, do it on demand + match = null, + sign, + ret, + diffRes; + + if (isDuration(input)) { + duration = { + ms : input._milliseconds, + d : input._days, + M : input._months + }; + } else if (typeof input === 'number') { + duration = {}; + if (key) { + duration[key] = input; + } else { + duration.milliseconds = input; + } + } else if (!!(match = aspNetRegex.exec(input))) { + sign = (match[1] === '-') ? -1 : 1; + duration = { + y : 0, + d : toInt(match[DATE]) * sign, + h : toInt(match[HOUR]) * sign, + m : toInt(match[MINUTE]) * sign, + s : toInt(match[SECOND]) * sign, + ms : toInt(match[MILLISECOND]) * sign + }; + } else if (!!(match = isoRegex.exec(input))) { + sign = (match[1] === '-') ? -1 : 1; + duration = { + y : parseIso(match[2], sign), + M : parseIso(match[3], sign), + w : parseIso(match[4], sign), + d : parseIso(match[5], sign), + h : parseIso(match[6], sign), + m : parseIso(match[7], sign), + s : parseIso(match[8], sign) + }; + } else if (duration == null) {// checks for null or undefined + duration = {}; + } else if (typeof duration === 'object' && ('from' in duration || 'to' in duration)) { + diffRes = momentsDifference(local__createLocal(duration.from), local__createLocal(duration.to)); + + duration = {}; + duration.ms = diffRes.milliseconds; + duration.M = diffRes.months; + } + + ret = new Duration(duration); + + if (isDuration(input) && hasOwnProp(input, '_locale')) { + ret._locale = input._locale; + } + + return ret; + } + + create__createDuration.fn = Duration.prototype; + + function parseIso (inp, sign) { + // We'd normally use ~~inp for this, but unfortunately it also + // converts floats to ints. + // inp may be undefined, so careful calling replace on it. + var res = inp && parseFloat(inp.replace(',', '.')); + // apply sign while we're at it + return (isNaN(res) ? 0 : res) * sign; + } + + function positiveMomentsDifference(base, other) { + var res = {milliseconds: 0, months: 0}; + + res.months = other.month() - base.month() + + (other.year() - base.year()) * 12; + if (base.clone().add(res.months, 'M').isAfter(other)) { + --res.months; + } + + res.milliseconds = +other - +(base.clone().add(res.months, 'M')); + + return res; + } + + function momentsDifference(base, other) { + var res; + if (!(base.isValid() && other.isValid())) { + return {milliseconds: 0, months: 0}; + } + + other = cloneWithOffset(other, base); + if (base.isBefore(other)) { + res = positiveMomentsDifference(base, other); + } else { + res = positiveMomentsDifference(other, base); + res.milliseconds = -res.milliseconds; + res.months = -res.months; + } + + return res; + } + + function absRound (number) { + if (number < 0) { + return Math.round(-1 * number) * -1; + } else { + return Math.round(number); + } + } + + // TODO: remove 'name' arg after deprecation is removed + function createAdder(direction, name) { + return function (val, period) { + var dur, tmp; + //invert the arguments, but complain about it + if (period !== null && !isNaN(+period)) { + deprecateSimple(name, 'moment().' + name + '(period, number) is deprecated. Please use moment().' + name + '(number, period). ' + + 'See http://momentjs.com/guides/#/warnings/add-inverted-param/ for more info.'); + tmp = val; val = period; period = tmp; + } + + val = typeof val === 'string' ? +val : val; + dur = create__createDuration(val, period); + add_subtract__addSubtract(this, dur, direction); + return this; + }; + } + + function add_subtract__addSubtract (mom, duration, isAdding, updateOffset) { + var milliseconds = duration._milliseconds, + days = absRound(duration._days), + months = absRound(duration._months); + + if (!mom.isValid()) { + // No op + return; + } + + updateOffset = updateOffset == null ? true : updateOffset; + + if (milliseconds) { + mom._d.setTime(mom._d.valueOf() + milliseconds * isAdding); + } + if (days) { + get_set__set(mom, 'Date', get_set__get(mom, 'Date') + days * isAdding); + } + if (months) { + setMonth(mom, get_set__get(mom, 'Month') + months * isAdding); + } + if (updateOffset) { + utils_hooks__hooks.updateOffset(mom, days || months); + } + } + + var add_subtract__add = createAdder(1, 'add'); + var add_subtract__subtract = createAdder(-1, 'subtract'); + + function getCalendarFormat(myMoment, now) { + var diff = myMoment.diff(now, 'days', true); + return diff < -6 ? 'sameElse' : + diff < -1 ? 'lastWeek' : + diff < 0 ? 'lastDay' : + diff < 1 ? 'sameDay' : + diff < 2 ? 'nextDay' : + diff < 7 ? 'nextWeek' : 'sameElse'; + } + + function moment_calendar__calendar (time, formats) { + // We want to compare the start of today, vs this. + // Getting start-of-today depends on whether we're local/utc/offset or not. + var now = time || local__createLocal(), + sod = cloneWithOffset(now, this).startOf('day'), + format = utils_hooks__hooks.calendarFormat(this, sod) || 'sameElse'; + + var output = formats && (isFunction(formats[format]) ? formats[format].call(this, now) : formats[format]); + + return this.format(output || this.localeData().calendar(format, this, local__createLocal(now))); + } + + function clone () { + return new Moment(this); + } + + function isAfter (input, units) { + var localInput = isMoment(input) ? input : local__createLocal(input); + if (!(this.isValid() && localInput.isValid())) { + return false; + } + units = normalizeUnits(!isUndefined(units) ? units : 'millisecond'); + if (units === 'millisecond') { + return this.valueOf() > localInput.valueOf(); + } else { + return localInput.valueOf() < this.clone().startOf(units).valueOf(); + } + } + + function isBefore (input, units) { + var localInput = isMoment(input) ? input : local__createLocal(input); + if (!(this.isValid() && localInput.isValid())) { + return false; + } + units = normalizeUnits(!isUndefined(units) ? units : 'millisecond'); + if (units === 'millisecond') { + return this.valueOf() < localInput.valueOf(); + } else { + return this.clone().endOf(units).valueOf() < localInput.valueOf(); + } + } + + function isBetween (from, to, units, inclusivity) { + inclusivity = inclusivity || '()'; + return (inclusivity[0] === '(' ? this.isAfter(from, units) : !this.isBefore(from, units)) && + (inclusivity[1] === ')' ? this.isBefore(to, units) : !this.isAfter(to, units)); + } + + function isSame (input, units) { + var localInput = isMoment(input) ? input : local__createLocal(input), + inputMs; + if (!(this.isValid() && localInput.isValid())) { + return false; + } + units = normalizeUnits(units || 'millisecond'); + if (units === 'millisecond') { + return this.valueOf() === localInput.valueOf(); + } else { + inputMs = localInput.valueOf(); + return this.clone().startOf(units).valueOf() <= inputMs && inputMs <= this.clone().endOf(units).valueOf(); + } + } + + function isSameOrAfter (input, units) { + return this.isSame(input, units) || this.isAfter(input,units); + } + + function isSameOrBefore (input, units) { + return this.isSame(input, units) || this.isBefore(input,units); + } + + function diff (input, units, asFloat) { + var that, + zoneDelta, + delta, output; + + if (!this.isValid()) { + return NaN; + } + + that = cloneWithOffset(input, this); + + if (!that.isValid()) { + return NaN; + } + + zoneDelta = (that.utcOffset() - this.utcOffset()) * 6e4; + + units = normalizeUnits(units); + + if (units === 'year' || units === 'month' || units === 'quarter') { + output = monthDiff(this, that); + if (units === 'quarter') { + output = output / 3; + } else if (units === 'year') { + output = output / 12; + } + } else { + delta = this - that; + output = units === 'second' ? delta / 1e3 : // 1000 + units === 'minute' ? delta / 6e4 : // 1000 * 60 + units === 'hour' ? delta / 36e5 : // 1000 * 60 * 60 + units === 'day' ? (delta - zoneDelta) / 864e5 : // 1000 * 60 * 60 * 24, negate dst + units === 'week' ? (delta - zoneDelta) / 6048e5 : // 1000 * 60 * 60 * 24 * 7, negate dst + delta; + } + return asFloat ? output : absFloor(output); + } + + function monthDiff (a, b) { + // difference in months + var wholeMonthDiff = ((b.year() - a.year()) * 12) + (b.month() - a.month()), + // b is in (anchor - 1 month, anchor + 1 month) + anchor = a.clone().add(wholeMonthDiff, 'months'), + anchor2, adjust; + + if (b - anchor < 0) { + anchor2 = a.clone().add(wholeMonthDiff - 1, 'months'); + // linear across the month + adjust = (b - anchor) / (anchor - anchor2); + } else { + anchor2 = a.clone().add(wholeMonthDiff + 1, 'months'); + // linear across the month + adjust = (b - anchor) / (anchor2 - anchor); + } + + //check for negative zero, return zero if negative zero + return -(wholeMonthDiff + adjust) || 0; + } + + utils_hooks__hooks.defaultFormat = 'YYYY-MM-DDTHH:mm:ssZ'; + utils_hooks__hooks.defaultFormatUtc = 'YYYY-MM-DDTHH:mm:ss[Z]'; + + function toString () { + return this.clone().locale('en').format('ddd MMM DD YYYY HH:mm:ss [GMT]ZZ'); + } + + function moment_format__toISOString () { + var m = this.clone().utc(); + if (0 < m.year() && m.year() <= 9999) { + if (isFunction(Date.prototype.toISOString)) { + // native implementation is ~50x faster, use it when we can + return this.toDate().toISOString(); + } else { + return formatMoment(m, 'YYYY-MM-DD[T]HH:mm:ss.SSS[Z]'); + } + } else { + return formatMoment(m, 'YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]'); + } + } + + function format (inputString) { + if (!inputString) { + inputString = this.isUtc() ? utils_hooks__hooks.defaultFormatUtc : utils_hooks__hooks.defaultFormat; + } + var output = formatMoment(this, inputString); + return this.localeData().postformat(output); + } + + function from (time, withoutSuffix) { + if (this.isValid() && + ((isMoment(time) && time.isValid()) || + local__createLocal(time).isValid())) { + return create__createDuration({to: this, from: time}).locale(this.locale()).humanize(!withoutSuffix); + } else { + return this.localeData().invalidDate(); + } + } + + function fromNow (withoutSuffix) { + return this.from(local__createLocal(), withoutSuffix); + } + + function to (time, withoutSuffix) { + if (this.isValid() && + ((isMoment(time) && time.isValid()) || + local__createLocal(time).isValid())) { + return create__createDuration({from: this, to: time}).locale(this.locale()).humanize(!withoutSuffix); + } else { + return this.localeData().invalidDate(); + } + } + + function toNow (withoutSuffix) { + return this.to(local__createLocal(), withoutSuffix); + } + + // If passed a locale key, it will set the locale for this + // instance. Otherwise, it will return the locale configuration + // variables for this instance. + function locale (key) { + var newLocaleData; + + if (key === undefined) { + return this._locale._abbr; + } else { + newLocaleData = locale_locales__getLocale(key); + if (newLocaleData != null) { + this._locale = newLocaleData; + } + return this; + } + } + + var lang = deprecate( + 'moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.', + function (key) { + if (key === undefined) { + return this.localeData(); + } else { + return this.locale(key); + } + } + ); + + function localeData () { + return this._locale; + } + + function startOf (units) { + units = normalizeUnits(units); + // the following switch intentionally omits break keywords + // to utilize falling through the cases. + switch (units) { + case 'year': + this.month(0); + /* falls through */ + case 'quarter': + case 'month': + this.date(1); + /* falls through */ + case 'week': + case 'isoWeek': + case 'day': + case 'date': + this.hours(0); + /* falls through */ + case 'hour': + this.minutes(0); + /* falls through */ + case 'minute': + this.seconds(0); + /* falls through */ + case 'second': + this.milliseconds(0); + } + + // weeks are a special case + if (units === 'week') { + this.weekday(0); + } + if (units === 'isoWeek') { + this.isoWeekday(1); + } + + // quarters are also special + if (units === 'quarter') { + this.month(Math.floor(this.month() / 3) * 3); + } + + return this; + } + + function endOf (units) { + units = normalizeUnits(units); + if (units === undefined || units === 'millisecond') { + return this; + } + + // 'date' is an alias for 'day', so it should be considered as such. + if (units === 'date') { + units = 'day'; + } + + return this.startOf(units).add(1, (units === 'isoWeek' ? 'week' : units)).subtract(1, 'ms'); + } + + function to_type__valueOf () { + return this._d.valueOf() - ((this._offset || 0) * 60000); + } + + function unix () { + return Math.floor(this.valueOf() / 1000); + } + + function toDate () { + return new Date(this.valueOf()); + } + + function toArray () { + var m = this; + return [m.year(), m.month(), m.date(), m.hour(), m.minute(), m.second(), m.millisecond()]; + } + + function toObject () { + var m = this; + return { + years: m.year(), + months: m.month(), + date: m.date(), + hours: m.hours(), + minutes: m.minutes(), + seconds: m.seconds(), + milliseconds: m.milliseconds() + }; + } + + function toJSON () { + // new Date(NaN).toJSON() === null + return this.isValid() ? this.toISOString() : null; + } + + function moment_valid__isValid () { + return valid__isValid(this); + } + + function parsingFlags () { + return extend({}, getParsingFlags(this)); + } + + function invalidAt () { + return getParsingFlags(this).overflow; + } + + function creationData() { + return { + input: this._i, + format: this._f, + locale: this._locale, + isUTC: this._isUTC, + strict: this._strict + }; + } + + // FORMATTING + + addFormatToken(0, ['gg', 2], 0, function () { + return this.weekYear() % 100; + }); + + addFormatToken(0, ['GG', 2], 0, function () { + return this.isoWeekYear() % 100; + }); + + function addWeekYearFormatToken (token, getter) { + addFormatToken(0, [token, token.length], 0, getter); + } + + addWeekYearFormatToken('gggg', 'weekYear'); + addWeekYearFormatToken('ggggg', 'weekYear'); + addWeekYearFormatToken('GGGG', 'isoWeekYear'); + addWeekYearFormatToken('GGGGG', 'isoWeekYear'); + + // ALIASES + + addUnitAlias('weekYear', 'gg'); + addUnitAlias('isoWeekYear', 'GG'); + + // PRIORITY + + addUnitPriority('weekYear', 1); + addUnitPriority('isoWeekYear', 1); + + + // PARSING + + addRegexToken('G', matchSigned); + addRegexToken('g', matchSigned); + addRegexToken('GG', match1to2, match2); + addRegexToken('gg', match1to2, match2); + addRegexToken('GGGG', match1to4, match4); + addRegexToken('gggg', match1to4, match4); + addRegexToken('GGGGG', match1to6, match6); + addRegexToken('ggggg', match1to6, match6); + + addWeekParseToken(['gggg', 'ggggg', 'GGGG', 'GGGGG'], function (input, week, config, token) { + week[token.substr(0, 2)] = toInt(input); + }); + + addWeekParseToken(['gg', 'GG'], function (input, week, config, token) { + week[token] = utils_hooks__hooks.parseTwoDigitYear(input); + }); + + // MOMENTS + + function getSetWeekYear (input) { + return getSetWeekYearHelper.call(this, + input, + this.week(), + this.weekday(), + this.localeData()._week.dow, + this.localeData()._week.doy); + } + + function getSetISOWeekYear (input) { + return getSetWeekYearHelper.call(this, + input, this.isoWeek(), this.isoWeekday(), 1, 4); + } + + function getISOWeeksInYear () { + return weeksInYear(this.year(), 1, 4); + } + + function getWeeksInYear () { + var weekInfo = this.localeData()._week; + return weeksInYear(this.year(), weekInfo.dow, weekInfo.doy); + } + + function getSetWeekYearHelper(input, week, weekday, dow, doy) { + var weeksTarget; + if (input == null) { + return weekOfYear(this, dow, doy).year; + } else { + weeksTarget = weeksInYear(input, dow, doy); + if (week > weeksTarget) { + week = weeksTarget; + } + return setWeekAll.call(this, input, week, weekday, dow, doy); + } + } + + function setWeekAll(weekYear, week, weekday, dow, doy) { + var dayOfYearData = dayOfYearFromWeeks(weekYear, week, weekday, dow, doy), + date = createUTCDate(dayOfYearData.year, 0, dayOfYearData.dayOfYear); + + this.year(date.getUTCFullYear()); + this.month(date.getUTCMonth()); + this.date(date.getUTCDate()); + return this; + } + + // FORMATTING + + addFormatToken('Q', 0, 'Qo', 'quarter'); + + // ALIASES + + addUnitAlias('quarter', 'Q'); + + // PRIORITY + + addUnitPriority('quarter', 7); + + // PARSING + + addRegexToken('Q', match1); + addParseToken('Q', function (input, array) { + array[MONTH] = (toInt(input) - 1) * 3; + }); + + // MOMENTS + + function getSetQuarter (input) { + return input == null ? Math.ceil((this.month() + 1) / 3) : this.month((input - 1) * 3 + this.month() % 3); + } + + // FORMATTING + + addFormatToken('D', ['DD', 2], 'Do', 'date'); + + // ALIASES + + addUnitAlias('date', 'D'); + + // PRIOROITY + addUnitPriority('date', 9); + + // PARSING + + addRegexToken('D', match1to2); + addRegexToken('DD', match1to2, match2); + addRegexToken('Do', function (isStrict, locale) { + return isStrict ? locale._ordinalParse : locale._ordinalParseLenient; + }); + + addParseToken(['D', 'DD'], DATE); + addParseToken('Do', function (input, array) { + array[DATE] = toInt(input.match(match1to2)[0], 10); + }); + + // MOMENTS + + var getSetDayOfMonth = makeGetSet('Date', true); + + // FORMATTING + + addFormatToken('DDD', ['DDDD', 3], 'DDDo', 'dayOfYear'); + + // ALIASES + + addUnitAlias('dayOfYear', 'DDD'); + + // PRIORITY + addUnitPriority('dayOfYear', 4); + + // PARSING + + addRegexToken('DDD', match1to3); + addRegexToken('DDDD', match3); + addParseToken(['DDD', 'DDDD'], function (input, array, config) { + config._dayOfYear = toInt(input); + }); + + // HELPERS + + // MOMENTS + + function getSetDayOfYear (input) { + var dayOfYear = Math.round((this.clone().startOf('day') - this.clone().startOf('year')) / 864e5) + 1; + return input == null ? dayOfYear : this.add((input - dayOfYear), 'd'); + } + + // FORMATTING + + addFormatToken('m', ['mm', 2], 0, 'minute'); + + // ALIASES + + addUnitAlias('minute', 'm'); + + // PRIORITY + + addUnitPriority('minute', 14); + + // PARSING + + addRegexToken('m', match1to2); + addRegexToken('mm', match1to2, match2); + addParseToken(['m', 'mm'], MINUTE); + + // MOMENTS + + var getSetMinute = makeGetSet('Minutes', false); + + // FORMATTING + + addFormatToken('s', ['ss', 2], 0, 'second'); + + // ALIASES + + addUnitAlias('second', 's'); + + // PRIORITY + + addUnitPriority('second', 15); + + // PARSING + + addRegexToken('s', match1to2); + addRegexToken('ss', match1to2, match2); + addParseToken(['s', 'ss'], SECOND); + + // MOMENTS + + var getSetSecond = makeGetSet('Seconds', false); + + // FORMATTING + + addFormatToken('S', 0, 0, function () { + return ~~(this.millisecond() / 100); + }); + + addFormatToken(0, ['SS', 2], 0, function () { + return ~~(this.millisecond() / 10); + }); + + addFormatToken(0, ['SSS', 3], 0, 'millisecond'); + addFormatToken(0, ['SSSS', 4], 0, function () { + return this.millisecond() * 10; + }); + addFormatToken(0, ['SSSSS', 5], 0, function () { + return this.millisecond() * 100; + }); + addFormatToken(0, ['SSSSSS', 6], 0, function () { + return this.millisecond() * 1000; + }); + addFormatToken(0, ['SSSSSSS', 7], 0, function () { + return this.millisecond() * 10000; + }); + addFormatToken(0, ['SSSSSSSS', 8], 0, function () { + return this.millisecond() * 100000; + }); + addFormatToken(0, ['SSSSSSSSS', 9], 0, function () { + return this.millisecond() * 1000000; + }); + + + // ALIASES + + addUnitAlias('millisecond', 'ms'); + + // PRIORITY + + addUnitPriority('millisecond', 16); + + // PARSING + + addRegexToken('S', match1to3, match1); + addRegexToken('SS', match1to3, match2); + addRegexToken('SSS', match1to3, match3); + + var token; + for (token = 'SSSS'; token.length <= 9; token += 'S') { + addRegexToken(token, matchUnsigned); + } + + function parseMs(input, array) { + array[MILLISECOND] = toInt(('0.' + input) * 1000); + } + + for (token = 'S'; token.length <= 9; token += 'S') { + addParseToken(token, parseMs); + } + // MOMENTS + + var getSetMillisecond = makeGetSet('Milliseconds', false); + + // FORMATTING + + addFormatToken('z', 0, 0, 'zoneAbbr'); + addFormatToken('zz', 0, 0, 'zoneName'); + + // MOMENTS + + function getZoneAbbr () { + return this._isUTC ? 'UTC' : ''; + } + + function getZoneName () { + return this._isUTC ? 'Coordinated Universal Time' : ''; + } + + var momentPrototype__proto = Moment.prototype; + + momentPrototype__proto.add = add_subtract__add; + momentPrototype__proto.calendar = moment_calendar__calendar; + momentPrototype__proto.clone = clone; + momentPrototype__proto.diff = diff; + momentPrototype__proto.endOf = endOf; + momentPrototype__proto.format = format; + momentPrototype__proto.from = from; + momentPrototype__proto.fromNow = fromNow; + momentPrototype__proto.to = to; + momentPrototype__proto.toNow = toNow; + momentPrototype__proto.get = stringGet; + momentPrototype__proto.invalidAt = invalidAt; + momentPrototype__proto.isAfter = isAfter; + momentPrototype__proto.isBefore = isBefore; + momentPrototype__proto.isBetween = isBetween; + momentPrototype__proto.isSame = isSame; + momentPrototype__proto.isSameOrAfter = isSameOrAfter; + momentPrototype__proto.isSameOrBefore = isSameOrBefore; + momentPrototype__proto.isValid = moment_valid__isValid; + momentPrototype__proto.lang = lang; + momentPrototype__proto.locale = locale; + momentPrototype__proto.localeData = localeData; + momentPrototype__proto.max = prototypeMax; + momentPrototype__proto.min = prototypeMin; + momentPrototype__proto.parsingFlags = parsingFlags; + momentPrototype__proto.set = stringSet; + momentPrototype__proto.startOf = startOf; + momentPrototype__proto.subtract = add_subtract__subtract; + momentPrototype__proto.toArray = toArray; + momentPrototype__proto.toObject = toObject; + momentPrototype__proto.toDate = toDate; + momentPrototype__proto.toISOString = moment_format__toISOString; + momentPrototype__proto.toJSON = toJSON; + momentPrototype__proto.toString = toString; + momentPrototype__proto.unix = unix; + momentPrototype__proto.valueOf = to_type__valueOf; + momentPrototype__proto.creationData = creationData; + + // Year + momentPrototype__proto.year = getSetYear; + momentPrototype__proto.isLeapYear = getIsLeapYear; + + // Week Year + momentPrototype__proto.weekYear = getSetWeekYear; + momentPrototype__proto.isoWeekYear = getSetISOWeekYear; + + // Quarter + momentPrototype__proto.quarter = momentPrototype__proto.quarters = getSetQuarter; + + // Month + momentPrototype__proto.month = getSetMonth; + momentPrototype__proto.daysInMonth = getDaysInMonth; + + // Week + momentPrototype__proto.week = momentPrototype__proto.weeks = getSetWeek; + momentPrototype__proto.isoWeek = momentPrototype__proto.isoWeeks = getSetISOWeek; + momentPrototype__proto.weeksInYear = getWeeksInYear; + momentPrototype__proto.isoWeeksInYear = getISOWeeksInYear; + + // Day + momentPrototype__proto.date = getSetDayOfMonth; + momentPrototype__proto.day = momentPrototype__proto.days = getSetDayOfWeek; + momentPrototype__proto.weekday = getSetLocaleDayOfWeek; + momentPrototype__proto.isoWeekday = getSetISODayOfWeek; + momentPrototype__proto.dayOfYear = getSetDayOfYear; + + // Hour + momentPrototype__proto.hour = momentPrototype__proto.hours = getSetHour; + + // Minute + momentPrototype__proto.minute = momentPrototype__proto.minutes = getSetMinute; + + // Second + momentPrototype__proto.second = momentPrototype__proto.seconds = getSetSecond; + + // Millisecond + momentPrototype__proto.millisecond = momentPrototype__proto.milliseconds = getSetMillisecond; + + // Offset + momentPrototype__proto.utcOffset = getSetOffset; + momentPrototype__proto.utc = setOffsetToUTC; + momentPrototype__proto.local = setOffsetToLocal; + momentPrototype__proto.parseZone = setOffsetToParsedOffset; + momentPrototype__proto.hasAlignedHourOffset = hasAlignedHourOffset; + momentPrototype__proto.isDST = isDaylightSavingTime; + momentPrototype__proto.isLocal = isLocal; + momentPrototype__proto.isUtcOffset = isUtcOffset; + momentPrototype__proto.isUtc = isUtc; + momentPrototype__proto.isUTC = isUtc; + + // Timezone + momentPrototype__proto.zoneAbbr = getZoneAbbr; + momentPrototype__proto.zoneName = getZoneName; + + // Deprecations + momentPrototype__proto.dates = deprecate('dates accessor is deprecated. Use date instead.', getSetDayOfMonth); + momentPrototype__proto.months = deprecate('months accessor is deprecated. Use month instead', getSetMonth); + momentPrototype__proto.years = deprecate('years accessor is deprecated. Use year instead', getSetYear); + momentPrototype__proto.zone = deprecate('moment().zone is deprecated, use moment().utcOffset instead. http://momentjs.com/guides/#/warnings/zone/', getSetZone); + momentPrototype__proto.isDSTShifted = deprecate('isDSTShifted is deprecated. See http://momentjs.com/guides/#/warnings/dst-shifted/ for more information', isDaylightSavingTimeShifted); + + var momentPrototype = momentPrototype__proto; + + function moment__createUnix (input) { + return local__createLocal(input * 1000); + } + + function moment__createInZone () { + return local__createLocal.apply(null, arguments).parseZone(); + } + + function preParsePostFormat (string) { + return string; + } + + var prototype__proto = Locale.prototype; + + prototype__proto.calendar = locale_calendar__calendar; + prototype__proto.longDateFormat = longDateFormat; + prototype__proto.invalidDate = invalidDate; + prototype__proto.ordinal = ordinal; + prototype__proto.preparse = preParsePostFormat; + prototype__proto.postformat = preParsePostFormat; + prototype__proto.relativeTime = relative__relativeTime; + prototype__proto.pastFuture = pastFuture; + prototype__proto.set = locale_set__set; + + // Month + prototype__proto.months = localeMonths; + prototype__proto.monthsShort = localeMonthsShort; + prototype__proto.monthsParse = localeMonthsParse; + prototype__proto.monthsRegex = monthsRegex; + prototype__proto.monthsShortRegex = monthsShortRegex; + + // Week + prototype__proto.week = localeWeek; + prototype__proto.firstDayOfYear = localeFirstDayOfYear; + prototype__proto.firstDayOfWeek = localeFirstDayOfWeek; + + // Day of Week + prototype__proto.weekdays = localeWeekdays; + prototype__proto.weekdaysMin = localeWeekdaysMin; + prototype__proto.weekdaysShort = localeWeekdaysShort; + prototype__proto.weekdaysParse = localeWeekdaysParse; + + prototype__proto.weekdaysRegex = weekdaysRegex; + prototype__proto.weekdaysShortRegex = weekdaysShortRegex; + prototype__proto.weekdaysMinRegex = weekdaysMinRegex; + + // Hours + prototype__proto.isPM = localeIsPM; + prototype__proto.meridiem = localeMeridiem; + + function lists__get (format, index, field, setter) { + var locale = locale_locales__getLocale(); + var utc = create_utc__createUTC().set(setter, index); + return locale[field](utc, format); + } + + function listMonthsImpl (format, index, field) { + if (typeof format === 'number') { + index = format; + format = undefined; + } + + format = format || ''; + + if (index != null) { + return lists__get(format, index, field, 'month'); + } + + var i; + var out = []; + for (i = 0; i < 12; i++) { + out[i] = lists__get(format, i, field, 'month'); + } + return out; + } + + // () + // (5) + // (fmt, 5) + // (fmt) + // (true) + // (true, 5) + // (true, fmt, 5) + // (true, fmt) + function listWeekdaysImpl (localeSorted, format, index, field) { + if (typeof localeSorted === 'boolean') { + if (typeof format === 'number') { + index = format; + format = undefined; + } + + format = format || ''; + } else { + format = localeSorted; + index = format; + localeSorted = false; + + if (typeof format === 'number') { + index = format; + format = undefined; + } + + format = format || ''; + } + + var locale = locale_locales__getLocale(), + shift = localeSorted ? locale._week.dow : 0; + + if (index != null) { + return lists__get(format, (index + shift) % 7, field, 'day'); + } + + var i; + var out = []; + for (i = 0; i < 7; i++) { + out[i] = lists__get(format, (i + shift) % 7, field, 'day'); + } + return out; + } + + function lists__listMonths (format, index) { + return listMonthsImpl(format, index, 'months'); + } + + function lists__listMonthsShort (format, index) { + return listMonthsImpl(format, index, 'monthsShort'); + } + + function lists__listWeekdays (localeSorted, format, index) { + return listWeekdaysImpl(localeSorted, format, index, 'weekdays'); + } + + function lists__listWeekdaysShort (localeSorted, format, index) { + return listWeekdaysImpl(localeSorted, format, index, 'weekdaysShort'); + } + + function lists__listWeekdaysMin (localeSorted, format, index) { + return listWeekdaysImpl(localeSorted, format, index, 'weekdaysMin'); + } + + locale_locales__getSetGlobalLocale('en', { + ordinalParse: /\d{1,2}(th|st|nd|rd)/, + ordinal : function (number) { + var b = number % 10, + output = (toInt(number % 100 / 10) === 1) ? 'th' : + (b === 1) ? 'st' : + (b === 2) ? 'nd' : + (b === 3) ? 'rd' : 'th'; + return number + output; + } + }); + + // Side effect imports + utils_hooks__hooks.lang = deprecate('moment.lang is deprecated. Use moment.locale instead.', locale_locales__getSetGlobalLocale); + utils_hooks__hooks.langData = deprecate('moment.langData is deprecated. Use moment.localeData instead.', locale_locales__getLocale); + + var mathAbs = Math.abs; + + function duration_abs__abs () { + var data = this._data; + + this._milliseconds = mathAbs(this._milliseconds); + this._days = mathAbs(this._days); + this._months = mathAbs(this._months); + + data.milliseconds = mathAbs(data.milliseconds); + data.seconds = mathAbs(data.seconds); + data.minutes = mathAbs(data.minutes); + data.hours = mathAbs(data.hours); + data.months = mathAbs(data.months); + data.years = mathAbs(data.years); + + return this; + } + + function duration_add_subtract__addSubtract (duration, input, value, direction) { + var other = create__createDuration(input, value); + + duration._milliseconds += direction * other._milliseconds; + duration._days += direction * other._days; + duration._months += direction * other._months; + + return duration._bubble(); + } + + // supports only 2.0-style add(1, 's') or add(duration) + function duration_add_subtract__add (input, value) { + return duration_add_subtract__addSubtract(this, input, value, 1); + } + + // supports only 2.0-style subtract(1, 's') or subtract(duration) + function duration_add_subtract__subtract (input, value) { + return duration_add_subtract__addSubtract(this, input, value, -1); + } + + function absCeil (number) { + if (number < 0) { + return Math.floor(number); + } else { + return Math.ceil(number); + } + } + + function bubble () { + var milliseconds = this._milliseconds; + var days = this._days; + var months = this._months; + var data = this._data; + var seconds, minutes, hours, years, monthsFromDays; + + // if we have a mix of positive and negative values, bubble down first + // check: https://github.com/moment/moment/issues/2166 + if (!((milliseconds >= 0 && days >= 0 && months >= 0) || + (milliseconds <= 0 && days <= 0 && months <= 0))) { + milliseconds += absCeil(monthsToDays(months) + days) * 864e5; + days = 0; + months = 0; + } + + // The following code bubbles up values, see the tests for + // examples of what that means. + data.milliseconds = milliseconds % 1000; + + seconds = absFloor(milliseconds / 1000); + data.seconds = seconds % 60; + + minutes = absFloor(seconds / 60); + data.minutes = minutes % 60; + + hours = absFloor(minutes / 60); + data.hours = hours % 24; + + days += absFloor(hours / 24); + + // convert days to months + monthsFromDays = absFloor(daysToMonths(days)); + months += monthsFromDays; + days -= absCeil(monthsToDays(monthsFromDays)); + + // 12 months -> 1 year + years = absFloor(months / 12); + months %= 12; + + data.days = days; + data.months = months; + data.years = years; + + return this; + } + + function daysToMonths (days) { + // 400 years have 146097 days (taking into account leap year rules) + // 400 years have 12 months === 4800 + return days * 4800 / 146097; + } + + function monthsToDays (months) { + // the reverse of daysToMonths + return months * 146097 / 4800; + } + + function as (units) { + var days; + var months; + var milliseconds = this._milliseconds; + + units = normalizeUnits(units); + + if (units === 'month' || units === 'year') { + days = this._days + milliseconds / 864e5; + months = this._months + daysToMonths(days); + return units === 'month' ? months : months / 12; + } else { + // handle milliseconds separately because of floating point math errors (issue #1867) + days = this._days + Math.round(monthsToDays(this._months)); + switch (units) { + case 'week' : return days / 7 + milliseconds / 6048e5; + case 'day' : return days + milliseconds / 864e5; + case 'hour' : return days * 24 + milliseconds / 36e5; + case 'minute' : return days * 1440 + milliseconds / 6e4; + case 'second' : return days * 86400 + milliseconds / 1000; + // Math.floor prevents floating point math errors here + case 'millisecond': return Math.floor(days * 864e5) + milliseconds; + default: throw new Error('Unknown unit ' + units); + } + } + } + + // TODO: Use this.as('ms')? + function duration_as__valueOf () { + return ( + this._milliseconds + + this._days * 864e5 + + (this._months % 12) * 2592e6 + + toInt(this._months / 12) * 31536e6 + ); + } + + function makeAs (alias) { + return function () { + return this.as(alias); + }; + } + + var asMilliseconds = makeAs('ms'); + var asSeconds = makeAs('s'); + var asMinutes = makeAs('m'); + var asHours = makeAs('h'); + var asDays = makeAs('d'); + var asWeeks = makeAs('w'); + var asMonths = makeAs('M'); + var asYears = makeAs('y'); + + function duration_get__get (units) { + units = normalizeUnits(units); + return this[units + 's'](); + } + + function makeGetter(name) { + return function () { + return this._data[name]; + }; + } + + var milliseconds = makeGetter('milliseconds'); + var seconds = makeGetter('seconds'); + var minutes = makeGetter('minutes'); + var hours = makeGetter('hours'); + var days = makeGetter('days'); + var months = makeGetter('months'); + var years = makeGetter('years'); + + function weeks () { + return absFloor(this.days() / 7); + } + + var round = Math.round; + var thresholds = { + s: 45, // seconds to minute + m: 45, // minutes to hour + h: 22, // hours to day + d: 26, // days to month + M: 11 // months to year + }; + + // helper function for moment.fn.from, moment.fn.fromNow, and moment.duration.fn.humanize + function substituteTimeAgo(string, number, withoutSuffix, isFuture, locale) { + return locale.relativeTime(number || 1, !!withoutSuffix, string, isFuture); + } + + function duration_humanize__relativeTime (posNegDuration, withoutSuffix, locale) { + var duration = create__createDuration(posNegDuration).abs(); + var seconds = round(duration.as('s')); + var minutes = round(duration.as('m')); + var hours = round(duration.as('h')); + var days = round(duration.as('d')); + var months = round(duration.as('M')); + var years = round(duration.as('y')); + + var a = seconds < thresholds.s && ['s', seconds] || + minutes <= 1 && ['m'] || + minutes < thresholds.m && ['mm', minutes] || + hours <= 1 && ['h'] || + hours < thresholds.h && ['hh', hours] || + days <= 1 && ['d'] || + days < thresholds.d && ['dd', days] || + months <= 1 && ['M'] || + months < thresholds.M && ['MM', months] || + years <= 1 && ['y'] || ['yy', years]; + + a[2] = withoutSuffix; + a[3] = +posNegDuration > 0; + a[4] = locale; + return substituteTimeAgo.apply(null, a); + } + + // This function allows you to set the rounding function for relative time strings + function duration_humanize__getSetRelativeTimeRounding (roundingFunction) { + if (roundingFunction === undefined) { + return round; + } + if (typeof(roundingFunction) === 'function') { + round = roundingFunction; + return true; + } + return false; + } + + // This function allows you to set a threshold for relative time strings + function duration_humanize__getSetRelativeTimeThreshold (threshold, limit) { + if (thresholds[threshold] === undefined) { + return false; + } + if (limit === undefined) { + return thresholds[threshold]; + } + thresholds[threshold] = limit; + return true; + } + + function humanize (withSuffix) { + var locale = this.localeData(); + var output = duration_humanize__relativeTime(this, !withSuffix, locale); + + if (withSuffix) { + output = locale.pastFuture(+this, output); + } + + return locale.postformat(output); + } + + var iso_string__abs = Math.abs; + + function iso_string__toISOString() { + // for ISO strings we do not use the normal bubbling rules: + // * milliseconds bubble up until they become hours + // * days do not bubble at all + // * months bubble up until they become years + // This is because there is no context-free conversion between hours and days + // (think of clock changes) + // and also not between days and months (28-31 days per month) + var seconds = iso_string__abs(this._milliseconds) / 1000; + var days = iso_string__abs(this._days); + var months = iso_string__abs(this._months); + var minutes, hours, years; + + // 3600 seconds -> 60 minutes -> 1 hour + minutes = absFloor(seconds / 60); + hours = absFloor(minutes / 60); + seconds %= 60; + minutes %= 60; + + // 12 months -> 1 year + years = absFloor(months / 12); + months %= 12; + + + // inspired by https://github.com/dordille/moment-isoduration/blob/master/moment.isoduration.js + var Y = years; + var M = months; + var D = days; + var h = hours; + var m = minutes; + var s = seconds; + var total = this.asSeconds(); + + if (!total) { + // this is the same as C#'s (Noda) and python (isodate)... + // but not other JS (goog.date) + return 'P0D'; + } + + return (total < 0 ? '-' : '') + + 'P' + + (Y ? Y + 'Y' : '') + + (M ? M + 'M' : '') + + (D ? D + 'D' : '') + + ((h || m || s) ? 'T' : '') + + (h ? h + 'H' : '') + + (m ? m + 'M' : '') + + (s ? s + 'S' : ''); + } + + var duration_prototype__proto = Duration.prototype; + + duration_prototype__proto.abs = duration_abs__abs; + duration_prototype__proto.add = duration_add_subtract__add; + duration_prototype__proto.subtract = duration_add_subtract__subtract; + duration_prototype__proto.as = as; + duration_prototype__proto.asMilliseconds = asMilliseconds; + duration_prototype__proto.asSeconds = asSeconds; + duration_prototype__proto.asMinutes = asMinutes; + duration_prototype__proto.asHours = asHours; + duration_prototype__proto.asDays = asDays; + duration_prototype__proto.asWeeks = asWeeks; + duration_prototype__proto.asMonths = asMonths; + duration_prototype__proto.asYears = asYears; + duration_prototype__proto.valueOf = duration_as__valueOf; + duration_prototype__proto._bubble = bubble; + duration_prototype__proto.get = duration_get__get; + duration_prototype__proto.milliseconds = milliseconds; + duration_prototype__proto.seconds = seconds; + duration_prototype__proto.minutes = minutes; + duration_prototype__proto.hours = hours; + duration_prototype__proto.days = days; + duration_prototype__proto.weeks = weeks; + duration_prototype__proto.months = months; + duration_prototype__proto.years = years; + duration_prototype__proto.humanize = humanize; + duration_prototype__proto.toISOString = iso_string__toISOString; + duration_prototype__proto.toString = iso_string__toISOString; + duration_prototype__proto.toJSON = iso_string__toISOString; + duration_prototype__proto.locale = locale; + duration_prototype__proto.localeData = localeData; + + // Deprecations + duration_prototype__proto.toIsoString = deprecate('toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)', iso_string__toISOString); + duration_prototype__proto.lang = lang; + + // Side effect imports + + // FORMATTING + + addFormatToken('X', 0, 0, 'unix'); + addFormatToken('x', 0, 0, 'valueOf'); + + // PARSING + + addRegexToken('x', matchSigned); + addRegexToken('X', matchTimestamp); + addParseToken('X', function (input, array, config) { + config._d = new Date(parseFloat(input, 10) * 1000); + }); + addParseToken('x', function (input, array, config) { + config._d = new Date(toInt(input)); + }); + + // Side effect imports + + + utils_hooks__hooks.version = '2.14.1'; + + setHookCallback(local__createLocal); + + utils_hooks__hooks.fn = momentPrototype; + utils_hooks__hooks.min = min; + utils_hooks__hooks.max = max; + utils_hooks__hooks.now = now; + utils_hooks__hooks.utc = create_utc__createUTC; + utils_hooks__hooks.unix = moment__createUnix; + utils_hooks__hooks.months = lists__listMonths; + utils_hooks__hooks.isDate = isDate; + utils_hooks__hooks.locale = locale_locales__getSetGlobalLocale; + utils_hooks__hooks.invalid = valid__createInvalid; + utils_hooks__hooks.duration = create__createDuration; + utils_hooks__hooks.isMoment = isMoment; + utils_hooks__hooks.weekdays = lists__listWeekdays; + utils_hooks__hooks.parseZone = moment__createInZone; + utils_hooks__hooks.localeData = locale_locales__getLocale; + utils_hooks__hooks.isDuration = isDuration; + utils_hooks__hooks.monthsShort = lists__listMonthsShort; + utils_hooks__hooks.weekdaysMin = lists__listWeekdaysMin; + utils_hooks__hooks.defineLocale = defineLocale; + utils_hooks__hooks.updateLocale = updateLocale; + utils_hooks__hooks.locales = locale_locales__listLocales; + utils_hooks__hooks.weekdaysShort = lists__listWeekdaysShort; + utils_hooks__hooks.normalizeUnits = normalizeUnits; + utils_hooks__hooks.relativeTimeRounding = duration_humanize__getSetRelativeTimeRounding; + utils_hooks__hooks.relativeTimeThreshold = duration_humanize__getSetRelativeTimeThreshold; + utils_hooks__hooks.calendarFormat = getCalendarFormat; + utils_hooks__hooks.prototype = momentPrototype; + + var _moment = utils_hooks__hooks; + + return _moment; + +})); \ No newline at end of file diff --git a/kfet/static/kfet/js/multiple-select.js b/kfet/static/kfet/js/multiple-select.js new file mode 100644 index 00000000..ea997187 --- /dev/null +++ b/kfet/static/kfet/js/multiple-select.js @@ -0,0 +1,782 @@ +/** + * @author zhixin wen + * @version 1.2.1 + * + * http://wenzhixin.net.cn/p/multiple-select/ + */ + +(function ($) { + + 'use strict'; + + // it only does '%s', and return '' when arguments are undefined + var sprintf = function (str) { + var args = arguments, + flag = true, + i = 1; + + str = str.replace(/%s/g, function () { + var arg = args[i++]; + + if (typeof arg === 'undefined') { + flag = false; + return ''; + } + return arg; + }); + return flag ? str : ''; + }; + + var removeDiacritics = function (str) { + var defaultDiacriticsRemovalMap = [ + {'base':'A', 'letters':/[\u0041\u24B6\uFF21\u00C0\u00C1\u00C2\u1EA6\u1EA4\u1EAA\u1EA8\u00C3\u0100\u0102\u1EB0\u1EAE\u1EB4\u1EB2\u0226\u01E0\u00C4\u01DE\u1EA2\u00C5\u01FA\u01CD\u0200\u0202\u1EA0\u1EAC\u1EB6\u1E00\u0104\u023A\u2C6F]/g}, + {'base':'AA','letters':/[\uA732]/g}, + {'base':'AE','letters':/[\u00C6\u01FC\u01E2]/g}, + {'base':'AO','letters':/[\uA734]/g}, + {'base':'AU','letters':/[\uA736]/g}, + {'base':'AV','letters':/[\uA738\uA73A]/g}, + {'base':'AY','letters':/[\uA73C]/g}, + {'base':'B', 'letters':/[\u0042\u24B7\uFF22\u1E02\u1E04\u1E06\u0243\u0182\u0181]/g}, + {'base':'C', 'letters':/[\u0043\u24B8\uFF23\u0106\u0108\u010A\u010C\u00C7\u1E08\u0187\u023B\uA73E]/g}, + {'base':'D', 'letters':/[\u0044\u24B9\uFF24\u1E0A\u010E\u1E0C\u1E10\u1E12\u1E0E\u0110\u018B\u018A\u0189\uA779]/g}, + {'base':'DZ','letters':/[\u01F1\u01C4]/g}, + {'base':'Dz','letters':/[\u01F2\u01C5]/g}, + {'base':'E', 'letters':/[\u0045\u24BA\uFF25\u00C8\u00C9\u00CA\u1EC0\u1EBE\u1EC4\u1EC2\u1EBC\u0112\u1E14\u1E16\u0114\u0116\u00CB\u1EBA\u011A\u0204\u0206\u1EB8\u1EC6\u0228\u1E1C\u0118\u1E18\u1E1A\u0190\u018E]/g}, + {'base':'F', 'letters':/[\u0046\u24BB\uFF26\u1E1E\u0191\uA77B]/g}, + {'base':'G', 'letters':/[\u0047\u24BC\uFF27\u01F4\u011C\u1E20\u011E\u0120\u01E6\u0122\u01E4\u0193\uA7A0\uA77D\uA77E]/g}, + {'base':'H', 'letters':/[\u0048\u24BD\uFF28\u0124\u1E22\u1E26\u021E\u1E24\u1E28\u1E2A\u0126\u2C67\u2C75\uA78D]/g}, + {'base':'I', 'letters':/[\u0049\u24BE\uFF29\u00CC\u00CD\u00CE\u0128\u012A\u012C\u0130\u00CF\u1E2E\u1EC8\u01CF\u0208\u020A\u1ECA\u012E\u1E2C\u0197]/g}, + {'base':'J', 'letters':/[\u004A\u24BF\uFF2A\u0134\u0248]/g}, + {'base':'K', 'letters':/[\u004B\u24C0\uFF2B\u1E30\u01E8\u1E32\u0136\u1E34\u0198\u2C69\uA740\uA742\uA744\uA7A2]/g}, + {'base':'L', 'letters':/[\u004C\u24C1\uFF2C\u013F\u0139\u013D\u1E36\u1E38\u013B\u1E3C\u1E3A\u0141\u023D\u2C62\u2C60\uA748\uA746\uA780]/g}, + {'base':'LJ','letters':/[\u01C7]/g}, + {'base':'Lj','letters':/[\u01C8]/g}, + {'base':'M', 'letters':/[\u004D\u24C2\uFF2D\u1E3E\u1E40\u1E42\u2C6E\u019C]/g}, + {'base':'N', 'letters':/[\u004E\u24C3\uFF2E\u01F8\u0143\u00D1\u1E44\u0147\u1E46\u0145\u1E4A\u1E48\u0220\u019D\uA790\uA7A4]/g}, + {'base':'NJ','letters':/[\u01CA]/g}, + {'base':'Nj','letters':/[\u01CB]/g}, + {'base':'O', 'letters':/[\u004F\u24C4\uFF2F\u00D2\u00D3\u00D4\u1ED2\u1ED0\u1ED6\u1ED4\u00D5\u1E4C\u022C\u1E4E\u014C\u1E50\u1E52\u014E\u022E\u0230\u00D6\u022A\u1ECE\u0150\u01D1\u020C\u020E\u01A0\u1EDC\u1EDA\u1EE0\u1EDE\u1EE2\u1ECC\u1ED8\u01EA\u01EC\u00D8\u01FE\u0186\u019F\uA74A\uA74C]/g}, + {'base':'OI','letters':/[\u01A2]/g}, + {'base':'OO','letters':/[\uA74E]/g}, + {'base':'OU','letters':/[\u0222]/g}, + {'base':'P', 'letters':/[\u0050\u24C5\uFF30\u1E54\u1E56\u01A4\u2C63\uA750\uA752\uA754]/g}, + {'base':'Q', 'letters':/[\u0051\u24C6\uFF31\uA756\uA758\u024A]/g}, + {'base':'R', 'letters':/[\u0052\u24C7\uFF32\u0154\u1E58\u0158\u0210\u0212\u1E5A\u1E5C\u0156\u1E5E\u024C\u2C64\uA75A\uA7A6\uA782]/g}, + {'base':'S', 'letters':/[\u0053\u24C8\uFF33\u1E9E\u015A\u1E64\u015C\u1E60\u0160\u1E66\u1E62\u1E68\u0218\u015E\u2C7E\uA7A8\uA784]/g}, + {'base':'T', 'letters':/[\u0054\u24C9\uFF34\u1E6A\u0164\u1E6C\u021A\u0162\u1E70\u1E6E\u0166\u01AC\u01AE\u023E\uA786]/g}, + {'base':'TZ','letters':/[\uA728]/g}, + {'base':'U', 'letters':/[\u0055\u24CA\uFF35\u00D9\u00DA\u00DB\u0168\u1E78\u016A\u1E7A\u016C\u00DC\u01DB\u01D7\u01D5\u01D9\u1EE6\u016E\u0170\u01D3\u0214\u0216\u01AF\u1EEA\u1EE8\u1EEE\u1EEC\u1EF0\u1EE4\u1E72\u0172\u1E76\u1E74\u0244]/g}, + {'base':'V', 'letters':/[\u0056\u24CB\uFF36\u1E7C\u1E7E\u01B2\uA75E\u0245]/g}, + {'base':'VY','letters':/[\uA760]/g}, + {'base':'W', 'letters':/[\u0057\u24CC\uFF37\u1E80\u1E82\u0174\u1E86\u1E84\u1E88\u2C72]/g}, + {'base':'X', 'letters':/[\u0058\u24CD\uFF38\u1E8A\u1E8C]/g}, + {'base':'Y', 'letters':/[\u0059\u24CE\uFF39\u1EF2\u00DD\u0176\u1EF8\u0232\u1E8E\u0178\u1EF6\u1EF4\u01B3\u024E\u1EFE]/g}, + {'base':'Z', 'letters':/[\u005A\u24CF\uFF3A\u0179\u1E90\u017B\u017D\u1E92\u1E94\u01B5\u0224\u2C7F\u2C6B\uA762]/g}, + {'base':'a', 'letters':/[\u0061\u24D0\uFF41\u1E9A\u00E0\u00E1\u00E2\u1EA7\u1EA5\u1EAB\u1EA9\u00E3\u0101\u0103\u1EB1\u1EAF\u1EB5\u1EB3\u0227\u01E1\u00E4\u01DF\u1EA3\u00E5\u01FB\u01CE\u0201\u0203\u1EA1\u1EAD\u1EB7\u1E01\u0105\u2C65\u0250]/g}, + {'base':'aa','letters':/[\uA733]/g}, + {'base':'ae','letters':/[\u00E6\u01FD\u01E3]/g}, + {'base':'ao','letters':/[\uA735]/g}, + {'base':'au','letters':/[\uA737]/g}, + {'base':'av','letters':/[\uA739\uA73B]/g}, + {'base':'ay','letters':/[\uA73D]/g}, + {'base':'b', 'letters':/[\u0062\u24D1\uFF42\u1E03\u1E05\u1E07\u0180\u0183\u0253]/g}, + {'base':'c', 'letters':/[\u0063\u24D2\uFF43\u0107\u0109\u010B\u010D\u00E7\u1E09\u0188\u023C\uA73F\u2184]/g}, + {'base':'d', 'letters':/[\u0064\u24D3\uFF44\u1E0B\u010F\u1E0D\u1E11\u1E13\u1E0F\u0111\u018C\u0256\u0257\uA77A]/g}, + {'base':'dz','letters':/[\u01F3\u01C6]/g}, + {'base':'e', 'letters':/[\u0065\u24D4\uFF45\u00E8\u00E9\u00EA\u1EC1\u1EBF\u1EC5\u1EC3\u1EBD\u0113\u1E15\u1E17\u0115\u0117\u00EB\u1EBB\u011B\u0205\u0207\u1EB9\u1EC7\u0229\u1E1D\u0119\u1E19\u1E1B\u0247\u025B\u01DD]/g}, + {'base':'f', 'letters':/[\u0066\u24D5\uFF46\u1E1F\u0192\uA77C]/g}, + {'base':'g', 'letters':/[\u0067\u24D6\uFF47\u01F5\u011D\u1E21\u011F\u0121\u01E7\u0123\u01E5\u0260\uA7A1\u1D79\uA77F]/g}, + {'base':'h', 'letters':/[\u0068\u24D7\uFF48\u0125\u1E23\u1E27\u021F\u1E25\u1E29\u1E2B\u1E96\u0127\u2C68\u2C76\u0265]/g}, + {'base':'hv','letters':/[\u0195]/g}, + {'base':'i', 'letters':/[\u0069\u24D8\uFF49\u00EC\u00ED\u00EE\u0129\u012B\u012D\u00EF\u1E2F\u1EC9\u01D0\u0209\u020B\u1ECB\u012F\u1E2D\u0268\u0131]/g}, + {'base':'j', 'letters':/[\u006A\u24D9\uFF4A\u0135\u01F0\u0249]/g}, + {'base':'k', 'letters':/[\u006B\u24DA\uFF4B\u1E31\u01E9\u1E33\u0137\u1E35\u0199\u2C6A\uA741\uA743\uA745\uA7A3]/g}, + {'base':'l', 'letters':/[\u006C\u24DB\uFF4C\u0140\u013A\u013E\u1E37\u1E39\u013C\u1E3D\u1E3B\u017F\u0142\u019A\u026B\u2C61\uA749\uA781\uA747]/g}, + {'base':'lj','letters':/[\u01C9]/g}, + {'base':'m', 'letters':/[\u006D\u24DC\uFF4D\u1E3F\u1E41\u1E43\u0271\u026F]/g}, + {'base':'n', 'letters':/[\u006E\u24DD\uFF4E\u01F9\u0144\u00F1\u1E45\u0148\u1E47\u0146\u1E4B\u1E49\u019E\u0272\u0149\uA791\uA7A5]/g}, + {'base':'nj','letters':/[\u01CC]/g}, + {'base':'o', 'letters':/[\u006F\u24DE\uFF4F\u00F2\u00F3\u00F4\u1ED3\u1ED1\u1ED7\u1ED5\u00F5\u1E4D\u022D\u1E4F\u014D\u1E51\u1E53\u014F\u022F\u0231\u00F6\u022B\u1ECF\u0151\u01D2\u020D\u020F\u01A1\u1EDD\u1EDB\u1EE1\u1EDF\u1EE3\u1ECD\u1ED9\u01EB\u01ED\u00F8\u01FF\u0254\uA74B\uA74D\u0275]/g}, + {'base':'oi','letters':/[\u01A3]/g}, + {'base':'ou','letters':/[\u0223]/g}, + {'base':'oo','letters':/[\uA74F]/g}, + {'base':'p','letters':/[\u0070\u24DF\uFF50\u1E55\u1E57\u01A5\u1D7D\uA751\uA753\uA755]/g}, + {'base':'q','letters':/[\u0071\u24E0\uFF51\u024B\uA757\uA759]/g}, + {'base':'r','letters':/[\u0072\u24E1\uFF52\u0155\u1E59\u0159\u0211\u0213\u1E5B\u1E5D\u0157\u1E5F\u024D\u027D\uA75B\uA7A7\uA783]/g}, + {'base':'s','letters':/[\u0073\u24E2\uFF53\u00DF\u015B\u1E65\u015D\u1E61\u0161\u1E67\u1E63\u1E69\u0219\u015F\u023F\uA7A9\uA785\u1E9B]/g}, + {'base':'t','letters':/[\u0074\u24E3\uFF54\u1E6B\u1E97\u0165\u1E6D\u021B\u0163\u1E71\u1E6F\u0167\u01AD\u0288\u2C66\uA787]/g}, + {'base':'tz','letters':/[\uA729]/g}, + {'base':'u','letters':/[\u0075\u24E4\uFF55\u00F9\u00FA\u00FB\u0169\u1E79\u016B\u1E7B\u016D\u00FC\u01DC\u01D8\u01D6\u01DA\u1EE7\u016F\u0171\u01D4\u0215\u0217\u01B0\u1EEB\u1EE9\u1EEF\u1EED\u1EF1\u1EE5\u1E73\u0173\u1E77\u1E75\u0289]/g}, + {'base':'v','letters':/[\u0076\u24E5\uFF56\u1E7D\u1E7F\u028B\uA75F\u028C]/g}, + {'base':'vy','letters':/[\uA761]/g}, + {'base':'w','letters':/[\u0077\u24E6\uFF57\u1E81\u1E83\u0175\u1E87\u1E85\u1E98\u1E89\u2C73]/g}, + {'base':'x','letters':/[\u0078\u24E7\uFF58\u1E8B\u1E8D]/g}, + {'base':'y','letters':/[\u0079\u24E8\uFF59\u1EF3\u00FD\u0177\u1EF9\u0233\u1E8F\u00FF\u1EF7\u1E99\u1EF5\u01B4\u024F\u1EFF]/g}, + {'base':'z','letters':/[\u007A\u24E9\uFF5A\u017A\u1E91\u017C\u017E\u1E93\u1E95\u01B6\u0225\u0240\u2C6C\uA763]/g} + ]; + + for (var i = 0; i < defaultDiacriticsRemovalMap.length; i++) { + str = str.replace(defaultDiacriticsRemovalMap[i].letters, defaultDiacriticsRemovalMap[i].base); + } + + return str; + + }; + + function MultipleSelect($el, options) { + var that = this, + name = $el.attr('name') || options.name || ''; + + this.options = options; + + // hide select element + this.$el = $el.hide(); + + // label element + this.$label = this.$el.closest('label'); + if (this.$label.length === 0 && this.$el.attr('id')) { + this.$label = $(sprintf('label[for="%s"]', this.$el.attr('id').replace(/:/g, '\\:'))); + } + + // restore class and title from select element + this.$parent = $(sprintf( + '
', + $el.attr('class') || '', + sprintf('title="%s"', $el.attr('title')))); + + // add placeholder to choice button + this.$choice = $(sprintf([ + '' + ].join(''), + this.options.placeholder)); + + // default position is bottom + this.$drop = $(sprintf('
', + this.options.position, + sprintf(' style="width: %s"', this.options.dropWidth))); + + this.$el.after(this.$parent); + this.$parent.append(this.$choice); + this.$parent.append(this.$drop); + + if (this.$el.prop('disabled')) { + this.$choice.addClass('disabled'); + } + this.$parent.css('width', + this.options.width || + this.$el.css('width') || + this.$el.outerWidth() + 20); + + this.selectAllName = 'data-name="selectAll' + name + '"'; + this.selectGroupName = 'data-name="selectGroup' + name + '"'; + this.selectItemName = 'data-name="selectItem' + name + '"'; + + if (!this.options.keepOpen) { + $(document).click(function (e) { + if ($(e.target)[0] === that.$choice[0] || + $(e.target).parents('.ms-choice')[0] === that.$choice[0]) { + return; + } + if (($(e.target)[0] === that.$drop[0] || + $(e.target).parents('.ms-drop')[0] !== that.$drop[0] && e.target !== $el[0]) && + that.options.isOpen) { + that.close(); + } + }); + } + } + + MultipleSelect.prototype = { + constructor: MultipleSelect, + + init: function () { + var that = this, + $ul = $('
    '); + + this.$drop.html(''); + + if (this.options.filter) { + this.$drop.append([ + ''].join('') + ); + } + + if (this.options.selectAll && !this.options.single) { + $ul.append([ + '
  • ', + '', + '
  • ' + ].join('')); + } + + $.each(this.$el.children(), function (i, elm) { + $ul.append(that.optionToHtml(i, elm)); + }); + $ul.append(sprintf('
  • %s
  • ', this.options.noMatchesFound)); + this.$drop.append($ul); + + this.$drop.find('ul').css('max-height', this.options.maxHeight + 'px'); + this.$drop.find('.multiple').css('width', this.options.multipleWidth + 'px'); + + this.$searchInput = this.$drop.find('.ms-search input'); + this.$selectAll = this.$drop.find('input[' + this.selectAllName + ']'); + this.$selectGroups = this.$drop.find('input[' + this.selectGroupName + ']'); + this.$selectItems = this.$drop.find('input[' + this.selectItemName + ']:enabled'); + this.$disableItems = this.$drop.find('input[' + this.selectItemName + ']:disabled'); + this.$noResults = this.$drop.find('.ms-no-results'); + + this.events(); + this.updateSelectAll(true); + this.update(true); + + if (this.options.isOpen) { + this.open(); + } + }, + + optionToHtml: function (i, elm, group, groupDisabled) { + var that = this, + $elm = $(elm), + classes = $elm.attr('class') || '', + title = sprintf('title="%s"', $elm.attr('title')), + multiple = this.options.multiple ? 'multiple' : '', + disabled, + type = this.options.single ? 'radio' : 'checkbox'; + + if ($elm.is('option')) { + var value = $elm.val(), + text = that.options.textTemplate($elm), + selected = $elm.prop('selected'), + style = sprintf('style="%s"', this.options.styler(value)), + $el; + + disabled = groupDisabled || $elm.prop('disabled'); + + $el = $([ + sprintf('
  • ', multiple, classes, title, style), + sprintf('', + '
  • ' + ].join('')); + $el.find('input').val(value); + return $el; + } + if ($elm.is('optgroup')) { + var label = that.options.labelTemplate($elm), + $group = $('
    '); + + group = 'group_' + i; + disabled = $elm.prop('disabled'); + + $group.append([ + '
  • ', + sprintf('', + '
  • ' + ].join('')); + + $.each($elm.children(), function (i, elm) { + $group.append(that.optionToHtml(i, elm, group, disabled)); + }); + return $group.html(); + } + }, + + events: function () { + var that = this, + toggleOpen = function (e) { + e.preventDefault(); + that[that.options.isOpen ? 'close' : 'open'](); + }; + + if (this.$label) { + this.$label.off('click').on('click', function (e) { + if (e.target.nodeName.toLowerCase() !== 'label' || e.target !== this) { + return; + } + toggleOpen(e); + if (!that.options.filter || !that.options.isOpen) { + that.focus(); + } + e.stopPropagation(); // Causes lost focus otherwise + }); + } + + this.$choice.off('click').on('click', toggleOpen) + .off('focus').on('focus', this.options.onFocus) + .off('blur').on('blur', this.options.onBlur); + + this.$parent.off('keydown').on('keydown', function (e) { + switch (e.which) { + case 27: // esc key + that.close(); + that.$choice.focus(); + break; + } + }); + + this.$searchInput.off('keydown').on('keydown',function (e) { + // Ensure shift-tab causes lost focus from filter as with clicking away + if (e.keyCode === 9 && e.shiftKey) { + that.close(); + } + }).off('keyup').on('keyup', function (e) { + // enter or space + // Avoid selecting/deselecting if no choices made + if (that.options.filterAcceptOnEnter && (e.which === 13 || e.which == 32) && that.$searchInput.val()) { + that.$selectAll.click(); + that.close(); + that.focus(); + return; + } + that.filter(); + }); + + this.$selectAll.off('click').on('click', function () { + var checked = $(this).prop('checked'), + $items = that.$selectItems.filter(':visible'); + + if ($items.length === that.$selectItems.length) { + that[checked ? 'checkAll' : 'uncheckAll'](); + } else { // when the filter option is true + that.$selectGroups.prop('checked', checked); + $items.prop('checked', checked); + that.options[checked ? 'onCheckAll' : 'onUncheckAll'](); + that.update(); + } + }); + this.$selectGroups.off('click').on('click', function () { + var group = $(this).parent().attr('data-group'), + $items = that.$selectItems.filter(':visible'), + $children = $items.filter(sprintf('[data-group="%s"]', group)), + checked = $children.length !== $children.filter(':checked').length; + + $children.prop('checked', checked); + that.updateSelectAll(); + that.update(); + that.options.onOptgroupClick({ + label: $(this).parent().text(), + checked: checked, + children: $children.get(), + instance: that + }); + }); + this.$selectItems.off('click').on('click', function () { + that.updateSelectAll(); + that.update(); + that.updateOptGroupSelect(); + that.options.onClick({ + label: $(this).parent().text(), + value: $(this).val(), + checked: $(this).prop('checked'), + instance: that + }); + + if (that.options.single && that.options.isOpen && !that.options.keepOpen) { + that.close(); + } + + if (that.options.single) { + var clickedVal = $(this).val(); + that.$selectItems.filter(function() { + return $(this).val() !== clickedVal; + }).each(function() { + $(this).prop('checked', false); + }); + that.update(); + } + }); + }, + + open: function () { + if (this.$choice.hasClass('disabled')) { + return; + } + this.options.isOpen = true; + this.$choice.find('>div').addClass('open'); + this.$drop[this.animateMethod('show')](); + + // fix filter bug: no results show + this.$selectAll.parent().show(); + this.$noResults.hide(); + + // Fix #77: 'All selected' when no options + if (!this.$el.children().length) { + this.$selectAll.parent().hide(); + this.$noResults.show(); + } + + if (this.options.container) { + var offset = this.$drop.offset(); + this.$drop.appendTo($(this.options.container)); + this.$drop.offset({ + top: offset.top, + left: offset.left + }); + } + + if (this.options.filter) { + this.$searchInput.val(''); + this.$searchInput.focus(); + this.filter(); + } + this.options.onOpen(); + }, + + close: function () { + this.options.isOpen = false; + this.$choice.find('>div').removeClass('open'); + this.$drop[this.animateMethod('hide')](); + if (this.options.container) { + this.$parent.append(this.$drop); + this.$drop.css({ + 'top': 'auto', + 'left': 'auto' + }); + } + this.options.onClose(); + }, + + animateMethod: function (method) { + var methods = { + show: { + fade: 'fadeIn', + slide: 'slideDown' + }, + hide: { + fade: 'fadeOut', + slide: 'slideUp' + } + }; + + return methods[method][this.options.animate] || method; + }, + + update: function (isInit) { + var selects = this.options.displayValues ? this.getSelects() : this.getSelects('text'), + $span = this.$choice.find('>span'), + sl = selects.length; + + if (sl === 0) { + $span.addClass('placeholder').html(this.options.placeholder); + } else if (this.options.allSelected && sl === this.$selectItems.length + this.$disableItems.length) { + $span.removeClass('placeholder').html(this.options.allSelected); + } else if (this.options.ellipsis && sl > this.options.minimumCountSelected) { + $span.removeClass('placeholder').text(selects.slice(0, this.options.minimumCountSelected) + .join(this.options.delimiter) + '...'); + } else if (this.options.countSelected && sl > this.options.minimumCountSelected) { + $span.removeClass('placeholder').html(this.options.countSelected + .replace('#', selects.length) + .replace('%', this.$selectItems.length + this.$disableItems.length)); + } else { + $span.removeClass('placeholder').text(selects.join(this.options.delimiter)); + } + + if (this.options.addTitle) { + $span.prop('title', this.getSelects('text')); + } + + // set selects to select + this.$el.val(this.getSelects()).trigger('change'); + + // add selected class to selected li + this.$drop.find('li').removeClass('selected'); + this.$drop.find('input:checked').each(function () { + $(this).parents('li').first().addClass('selected'); + }); + + // trigger
    ").addClass("cw").text("#"));c.isBefore(f.clone().endOf("w"));)b.append(a("").addClass("dow").text(c.format("dd"))),c.add(1,"d");o.find(".datepicker-days thead").append(b)},M=function(a){return d.disabledDates[a.format("YYYY-MM-DD")]===!0},N=function(a){return d.enabledDates[a.format("YYYY-MM-DD")]===!0},O=function(a){return d.disabledHours[a.format("H")]===!0},P=function(a){return d.enabledHours[a.format("H")]===!0},Q=function(b,c){if(!b.isValid())return!1;if(d.disabledDates&&"d"===c&&M(b))return!1;if(d.enabledDates&&"d"===c&&!N(b))return!1;if(d.minDate&&b.isBefore(d.minDate,c))return!1;if(d.maxDate&&b.isAfter(d.maxDate,c))return!1;if(d.daysOfWeekDisabled&&"d"===c&&-1!==d.daysOfWeekDisabled.indexOf(b.day()))return!1;if(d.disabledHours&&("h"===c||"m"===c||"s"===c)&&O(b))return!1;if(d.enabledHours&&("h"===c||"m"===c||"s"===c)&&!P(b))return!1;if(d.disabledTimeIntervals&&("h"===c||"m"===c||"s"===c)){var e=!1;if(a.each(d.disabledTimeIntervals,function(){return b.isBetween(this[0],this[1])?(e=!0,!1):void 0}),e)return!1}return!0},R=function(){for(var b=[],c=f.clone().startOf("y").startOf("d");c.isSame(f,"y");)b.push(a("").attr("data-action","selectMonth").addClass("month").text(c.format("MMM"))),c.add(1,"M");o.find(".datepicker-months td").empty().append(b)},S=function(){var b=o.find(".datepicker-months"),c=b.find("th"),g=b.find("tbody").find("span");c.eq(0).find("span").attr("title",d.tooltips.prevYear),c.eq(1).attr("title",d.tooltips.selectYear),c.eq(2).find("span").attr("title",d.tooltips.nextYear),b.find(".disabled").removeClass("disabled"),Q(f.clone().subtract(1,"y"),"y")||c.eq(0).addClass("disabled"),c.eq(1).text(f.year()),Q(f.clone().add(1,"y"),"y")||c.eq(2).addClass("disabled"),g.removeClass("active"),e.isSame(f,"y")&&!m&&g.eq(e.month()).addClass("active"),g.each(function(b){Q(f.clone().month(b),"M")||a(this).addClass("disabled")})},T=function(){var a=o.find(".datepicker-years"),b=a.find("th"),c=f.clone().subtract(5,"y"),g=f.clone().add(6,"y"),h="";for(b.eq(0).find("span").attr("title",d.tooltips.prevDecade),b.eq(1).attr("title",d.tooltips.selectDecade),b.eq(2).find("span").attr("title",d.tooltips.nextDecade),a.find(".disabled").removeClass("disabled"),d.minDate&&d.minDate.isAfter(c,"y")&&b.eq(0).addClass("disabled"),b.eq(1).text(c.year()+"-"+g.year()),d.maxDate&&d.maxDate.isBefore(g,"y")&&b.eq(2).addClass("disabled");!c.isAfter(g,"y");)h+=''+c.year()+"",c.add(1,"y");a.find("td").html(h)},U=function(){var a=o.find(".datepicker-decades"),c=a.find("th"),g=b({y:f.year()-f.year()%100-1}),h=g.clone().add(100,"y"),i=g.clone(),j="";for(c.eq(0).find("span").attr("title",d.tooltips.prevCentury),c.eq(2).find("span").attr("title",d.tooltips.nextCentury),a.find(".disabled").removeClass("disabled"),(g.isSame(b({y:1900}))||d.minDate&&d.minDate.isAfter(g,"y"))&&c.eq(0).addClass("disabled"),c.eq(1).text(g.year()+"-"+h.year()),(g.isSame(b({y:2e3}))||d.maxDate&&d.maxDate.isBefore(h,"y"))&&c.eq(2).addClass("disabled");!g.isAfter(h,"y");)j+=''+(g.year()+1)+" - "+(g.year()+12)+"",g.add(12,"y");j+="",a.find("td").html(j),c.eq(1).text(i.year()+1+"-"+g.year())},V=function(){var b,c,g,h,i=o.find(".datepicker-days"),j=i.find("th"),k=[];if(A()){for(j.eq(0).find("span").attr("title",d.tooltips.prevMonth),j.eq(1).attr("title",d.tooltips.selectMonth),j.eq(2).find("span").attr("title",d.tooltips.nextMonth),i.find(".disabled").removeClass("disabled"),j.eq(1).text(f.format(d.dayViewHeaderFormat)),Q(f.clone().subtract(1,"M"),"M")||j.eq(0).addClass("disabled"),Q(f.clone().add(1,"M"),"M")||j.eq(2).addClass("disabled"),b=f.clone().startOf("M").startOf("w").startOf("d"),h=0;42>h;h++)0===b.weekday()&&(c=a("
    '+b.week()+"'+b.date()+"
    '+c.format(h?"HH":"hh")+"
    '+c.format("mm")+"
    '+c.format("ss")+"
    + + + + + + + + + + + + + {% for account in accounts %} + + + + + + + + + + {% endfor %} + +
    TrigrammeNomBalanceCOFDptPromo
    + + + + {{ account.trigramme }}{{ account.name }}{{ account.balance }}€{{ account.is_cof }}{{ account.departement }}{{ account.promo|default_if_none:'' }}
    + + + + + + +{% endblock %} diff --git a/kfet/templates/kfet/account_create.html b/kfet/templates/kfet/account_create.html new file mode 100644 index 00000000..ac6c5daa --- /dev/null +++ b/kfet/templates/kfet/account_create.html @@ -0,0 +1,95 @@ +{% extends "kfet/base.html" %} +{% load staticfiles %} + +{% block title %}Nouveau compte{% endblock %} + +{% block extra_head %} + +{% endblock %} + +{% block content-header-title %}Création d'un compte{% endblock %} + +{% block content %} + +{% include 'kfet/base_messages.html' %} + +
    +
    +
    + +
    +
    +
    + + +{% endblock %} diff --git a/kfet/templates/kfet/account_create_autocomplete.html b/kfet/templates/kfet/account_create_autocomplete.html new file mode 100644 index 00000000..b99abecf --- /dev/null +++ b/kfet/templates/kfet/account_create_autocomplete.html @@ -0,0 +1,49 @@ +{% load kfet_tags %} + +
    diff --git a/kfet/templates/kfet/account_create_form.html b/kfet/templates/kfet/account_create_form.html new file mode 100644 index 00000000..aa040058 --- /dev/null +++ b/kfet/templates/kfet/account_create_form.html @@ -0,0 +1,9 @@ +{% load widget_tweaks %} + +{% include 'kfet/form_snippet.html' with form=user_form %} +{% include 'kfet/form_snippet.html' with form=cof_form %} +{% include 'kfet/form_snippet.html' with form=account_form %} + +{% if user_form %} + {% include 'kfet/form_submit_snippet.html' with value="Enregistrer" %} +{% endif %} diff --git a/kfet/templates/kfet/account_create_special.html b/kfet/templates/kfet/account_create_special.html new file mode 100644 index 00000000..a002d40e --- /dev/null +++ b/kfet/templates/kfet/account_create_special.html @@ -0,0 +1,96 @@ +{% extends "kfet/base.html" %} +{% load staticfiles %} + +{% block title %}Nouveau compte{% endblock %} + +{% block extra_head %} + +{% endblock %} + +{% block content-header-title %}Création d'un compte{% endblock %} + +{% block content %} + +{% include 'kfet/base_messages.html' %} + +
    +
    +
    + +
    +
    +
    + + +{% endblock %} diff --git a/kfet/templates/kfet/account_group.html b/kfet/templates/kfet/account_group.html new file mode 100644 index 00000000..2154fed4 --- /dev/null +++ b/kfet/templates/kfet/account_group.html @@ -0,0 +1,54 @@ +{% extends 'kfet/base.html' %} + +{% block title %}Groupes de comptes{% endblock %} +{% block content-header-title %}Groupes de comptes{% endblock %} + +{% block content %} + +
    + +
    + {% include 'kfet/base_messages.html' %} +
    + {% for group in groups %} +
    +
    + + + +
    +

    {{ group.name }}

    +
    +
    +

    Permissions

    +
      + {% for perm in group.permissions.all %} +
    • {{ perm.name }}
    • + {% endfor %} +
    +
    +
    +

    Comptes

    +
      + {% for user in group.user_set.all %} +
    • {{ user.profile.account_kfet }}
    • + {% endfor %} +
    +
    +
    +
    + {% endfor %} +
    +
    +
    + +{% endblock %} diff --git a/kfet/templates/kfet/account_group_form.html b/kfet/templates/kfet/account_group_form.html new file mode 100644 index 00000000..e37bdd89 --- /dev/null +++ b/kfet/templates/kfet/account_group_form.html @@ -0,0 +1,38 @@ +{% extends 'kfet/base.html' %} +{% load staticfiles %} + +{% block extra_head %} + + +{% endblock %} + +{% block content %} + +
    + {% csrf_token %} +
    + {{ form.name.errors }} + {{ form.name.label_tag }} +
    + K-Fêt + {{ form.name }} +
    +
    +
    + {{ form.permissions.errors }} + {{ form.permissions.label_tag }} + {{ form.permissions }} +
    + +
    + + + +{% endblock %} diff --git a/kfet/templates/kfet/account_negative.html b/kfet/templates/kfet/account_negative.html new file mode 100644 index 00000000..77fdf118 --- /dev/null +++ b/kfet/templates/kfet/account_negative.html @@ -0,0 +1,80 @@ +{% extends 'kfet/base.html' %} + +{% block title %}Comptes en négatifs{% endblock %} +{% block content-header-title %}Comptes - Négatifs{% endblock %} + +{% block content %} + +
    +
    +
    +
    +
    {{ negatives|length }}
    +
    compte{{ negatives|length|pluralize }} en négatif
    +
    +
    Total: {{ negatives_sum|floatformat:2 }}€
    +
    +
    +
    Découvert autorisé par défaut
    +
    Montant: {{ settings.overdraft_amount }}€
    +
    Pendant: {{ settings.overdraft_duration }}
    +
    +
    + {% if perms.kfet.change_settings %} + + {% endif %} +
    +
    +
    + {% include 'kfet/base_messages.html' %} +
    +
    +

    Liste des comptes en négatifs

    +
    + + + + + + + + + + + + + + + + {% for neg in negatives %} + + + + + + + + + + + + {% endfor %} + +
    TriNomBalanceRéelleDébutDécouvert autoriséJusqu'auBalance offset
    + + + + {{ neg.account.trigramme }}{{ neg.account.name }}{{ neg.account.balance|floatformat:2 }}€ + {% if neg.account.balance_offset %} + {{ neg.account.real_balance|floatformat:2 }}€ + {% endif %} + {{ neg.start|date:'d/m/Y H:i:s'}}{{ neg.authz_overdraft_amount|default_if_none:'' }}{{ neg.authz_overdrafy_until|default_if_none:'' }}{{ neg.balance_offset|default_if_none:'' }}
    +
    +
    +
    +
    +
    + +{% endblock %} diff --git a/kfet/templates/kfet/account_read.html b/kfet/templates/kfet/account_read.html new file mode 100644 index 00000000..2d91197b --- /dev/null +++ b/kfet/templates/kfet/account_read.html @@ -0,0 +1,95 @@ +{% extends "kfet/base.html" %} +{% load staticfiles %} +{% load kfet_tags %} +{% load l10n %} + +{% block extra_head %} + + + + + + +{% endblock %} + +{% block title %} +{% if account.user == request.user %} + Mon compte +{% else %} + Informations du compte {{ account.trigramme }} +{% endif %} +{% endblock %} + +{% block content-header-title %} +{% if account.user == request.user %} + Mon compte +{% else %} + Compte - {{ account.trigramme }} +{% endif %} +{% endblock %} + +{% block content %} + +
    +
    +
    + {% include 'kfet/left_account.html' %} +
    +
    +
    + {% include "kfet/base_messages.html" %} +
    + {% if addcosts %} +
    +

    Gagné des majorations

    +
    +
      + {% for addcost in addcosts %} +
    • {{ addcost.date|date:'l j F' }}: +{{ addcost.sum_addcosts }}€
    • + {% endfor %} +
    +
    +
    + {% endif %} +
    +

    Historique

    +
    +
    +
    +
    +
    +
    + + + +{% endblock %} diff --git a/kfet/templates/kfet/account_read_title.html b/kfet/templates/kfet/account_read_title.html new file mode 100644 index 00000000..6712ed77 --- /dev/null +++ b/kfet/templates/kfet/account_read_title.html @@ -0,0 +1,5 @@ +{% if account.user == request.user %} + Mon compte +{% else %} + Informations du compte {{ account.trigramme }} +{% endif %} diff --git a/kfet/templates/kfet/account_update.html b/kfet/templates/kfet/account_update.html new file mode 100644 index 00000000..faacfe4b --- /dev/null +++ b/kfet/templates/kfet/account_update.html @@ -0,0 +1,61 @@ +{% extends "kfet/base.html" %} +{% load widget_tweaks %} +{% load staticfiles %} + +{% block extra_head %} +{{ negative_form.media }} +{% endblock %} + +{% block title %} +{% if account.user == request.user %} + Modification de mes informations +{% else %} + {{ account.trigramme }} - Édition +{% endif %} +{% endblock %} + +{% block content-header-title %} +{% if account.user == request.user %} + Modification de mes informations +{% else %} + Édition du compte {{ account.trigramme }} +{% endif %} +{% endblock %} + +{% block content %} + +{% include "kfet/base_messages.html" %} + +
    +
    +
    +
    + {% csrf_token %} + {% include 'kfet/form_snippet.html' with form=user_form %} + {% include 'kfet/form_snippet.html' with form=cof_form %} + {% include 'kfet/form_snippet.html' with form=account_form %} + {% include 'kfet/form_snippet.html' with form=group_form %} + {% include 'kfet/form_snippet.html' with form=pwd_form %} + {% include 'kfet/form_snippet.html' with form=negative_form %} + {% if perms.kfet.is_team %} + {% include 'kfet/form_authentication_snippet.html' %} + {% endif %} + {% include 'kfet/form_submit_snippet.html' with value="Mettre à jour" %} +
    +
    +
    +
    + + + +{% endblock %} diff --git a/kfet/templates/kfet/article.html b/kfet/templates/kfet/article.html new file mode 100644 index 00000000..1d0a86fd --- /dev/null +++ b/kfet/templates/kfet/article.html @@ -0,0 +1,67 @@ +{% extends 'kfet/base.html' %} + +{% block title %}Articles{% endblock %} +{% block content-header-title %}Articles{% endblock %} + +{% block content %} + +
    +
    +
    +
    +
    {{ articles|length }}
    +
    article{{ articles|length|pluralize }}
    +
    + +
    +
    +
    + {% include 'kfet/base_messages.html' %} +
    +
    +

    Liste des articles

    +
    + + + + + + + + + + + + + {% for article in articles %} + {% ifchanged article.category %} + + + + {% endifchanged %} + + + + + + + + + {% endfor %} + +
    NomPrixStockEn venteDernier inventaire
    {{ article.category.name }}
    + + + + {{ article.name }}{{ article.price }}€{{ article.stock }}{{ article.is_sold }}{{ article.inventory.0.at }}
    +
    +
    +
    +
    +
    + +{% endblock %} diff --git a/kfet/templates/kfet/article_create.html b/kfet/templates/kfet/article_create.html new file mode 100644 index 00000000..742756b2 --- /dev/null +++ b/kfet/templates/kfet/article_create.html @@ -0,0 +1,17 @@ +{% extends 'kfet/base.html' %} + +{% block title %}Nouvel article{% endblock %} +{% block content-header-title %}Création d'un article{% endblock %} + +{% block content %} + +
    + {% csrf_token %} + {{ form.as_p }} + {% if not perms.kfet.add_article %} + + {% endif %} + +
    + +{% endblock %} diff --git a/kfet/templates/kfet/article_read.html b/kfet/templates/kfet/article_read.html new file mode 100644 index 00000000..ea820a09 --- /dev/null +++ b/kfet/templates/kfet/article_read.html @@ -0,0 +1,85 @@ +{% extends 'kfet/base.html' %} + +{% block title %}Informations sur l'article {{ article }}{% endblock %} +{% block content-header-title %}Article - {{ article.name }}{% endblock %} + +{% block content %} + +
    +
    +
    +
    +
    {{ article.name }}
    +
    {{ article.category }}
    +
    +
    Prix (hors réduc.): {{ article.price }}€
    +
    Stock: {{ article.stock }}
    +
    En vente: {{ article.is_sold }}
    +
    +
    + +
    +
    +
    + {% include 'kfet/base_messages.html' %} +
    +
    +

    Historique

    +
    +
    +

    Inventaires

    + + + + + + + + + + {% for inventoryart in inventoryarts %} + + + + + + {% endfor %} + +
    DateStockErreur
    {{ inventoryart.inventory.at }}{{ inventoryart.stock_new }}{{ inventoryart.stock_error }}
    +
    +
    +

    Prix fournisseurs

    + + + + + + + + + + + + {% for supplierart in supplierarts %} + + + + + + + + {% endfor %} + +
    DateFournisseurHTTVADroits
    {{ supplierart.at }}{{ supplierart.supplier.name }}{{ supplierart.price_HT }}{{ supplierart.TVA }}{{ supplierart.rights }}
    +
    +
    +
    +
    +
    +
    + +{% endblock %} diff --git a/kfet/templates/kfet/article_update.html b/kfet/templates/kfet/article_update.html new file mode 100644 index 00000000..db0107cc --- /dev/null +++ b/kfet/templates/kfet/article_update.html @@ -0,0 +1,17 @@ +{% extends 'kfet/base.html' %} + +{% block title %}Édition de l'article {{ article.name }}{% endblock %} +{% block content-header-title %}Article {{ article.name }} - Édition{% endblock %} + +{% block content %} + +
    + {% csrf_token %} + {{ form.as_p }} + {% if not perms.kfet.change_article %} + + {% endif %} + +
    + +{% endblock %} diff --git a/kfet/templates/kfet/base.html b/kfet/templates/kfet/base.html new file mode 100644 index 00000000..c1db0c26 --- /dev/null +++ b/kfet/templates/kfet/base.html @@ -0,0 +1,44 @@ +{% load staticfiles %} + + + + + + + {% block title %}{% endblock %} | K-Fêt - ENS Ulm + + {# CSS #} + + + + + + {# JS #} + + + + + + {% block extra_head %}{% endblock %} + + {# Vieux IE pas comprendre HTML5 et Media Queries #} + + + + {% include "kfet/base_nav.html" %} +
    + {% block content-header %} +
    +
    +

    {% block content-header-title %}{% endblock %}

    +
    +
    + {% endblock %} + {% block content %}{% endblock %} + {% include "kfet/base_footer.html" %} +
    + + diff --git a/kfet/templates/kfet/base_footer.html b/kfet/templates/kfet/base_footer.html new file mode 100644 index 00000000..e69de29b diff --git a/kfet/templates/kfet/base_messages.html b/kfet/templates/kfet/base_messages.html new file mode 100644 index 00000000..d2bb5061 --- /dev/null +++ b/kfet/templates/kfet/base_messages.html @@ -0,0 +1,7 @@ +{% if messages %} +
      + {% for message in messages %} + {{ message }} + {% endfor %} +
    +{% endif %} diff --git a/kfet/templates/kfet/base_nav.html b/kfet/templates/kfet/base_nav.html new file mode 100644 index 00000000..b5c98375 --- /dev/null +++ b/kfet/templates/kfet/base_nav.html @@ -0,0 +1,68 @@ +{% load staticfiles %} + + + + diff --git a/kfet/templates/kfet/checkout.html b/kfet/templates/kfet/checkout.html new file mode 100644 index 00000000..fb2d10a7 --- /dev/null +++ b/kfet/templates/kfet/checkout.html @@ -0,0 +1,60 @@ +{% extends "kfet/base.html" %} + +{% block title %}Liste des caisses{% endblock %} +{% block content-header-title %}Caisses{% endblock %} + +{% block content %} + +
    +
    +
    +
    +
    {{ checkouts|length }}
    +
    caisse{{ checkouts|length|pluralize }}
    +
    + +
    +
    +
    + {% include 'kfet/base_messages.html' %} +
    +
    +

    Liste des caisses

    +
    + + + + + + + + + + + + + {% for checkout in checkouts %} + + + + + + + + + {% endfor %} + +
    NomBalanceDéb. valid.Fin valid.Protégée
    + + + + {{ checkout.name }}{{ checkout.balance}}€{{ checkout.valid_from }}{{ checkout.valid_to }}{{ checkout.is_protected }}
    +
    +
    +
    +
    +
    + +{% endblock %} diff --git a/kfet/templates/kfet/checkout_create.html b/kfet/templates/kfet/checkout_create.html new file mode 100644 index 00000000..0f254f65 --- /dev/null +++ b/kfet/templates/kfet/checkout_create.html @@ -0,0 +1,53 @@ +{% extends "kfet/base.html" %} +{% block extra_head %}{{ form.media }}{% endblock %} + +{% block title %}Nouvelle caisse{% endblock %} +{% block content-header-title %}Création d'une caisse{% endblock %} + +{% block content %} + +{% include 'kfet/base_messages.html' %} +
    + {% csrf_token %} + {{ form.non_field_errors}} + {% for field in form %} + {{ field.errors }} + {{ field.label_tag }} +
    {{ field }}
    + {% if field.help_text %} +

    {{ field.help_text|safe }}

    + {% endif %} + {% endfor %} + {% if not perms.kfet.add_checkout %} + + {% endif %} + +
    + + + +{% endblock %} diff --git a/kfet/templates/kfet/checkout_read.html b/kfet/templates/kfet/checkout_read.html new file mode 100644 index 00000000..14c9ddfd --- /dev/null +++ b/kfet/templates/kfet/checkout_read.html @@ -0,0 +1,51 @@ +{% extends 'kfet/base.html' %} + +{% block title %}Informations sur la caisse {{ checkout.name }}{% endblock %} +{% block content-header-title %}Caisse - {{ checkout.name }}{% endblock %} + +{% block content %} + +
    +
    +
    + {% include 'kfet/left_checkout.html' %} +
    +
    +
    + {% include "kfet/base_messages.html" %} +
    +
    +

    Relevés

    +
    + {% if not statements %} + Pas de relevé + {% else %} + + + + + + + + + + + {% for statement in statements %} + + + + + + + + {% endfor %} + +
    Date/heureMontant prisMontant laisséErreur
    {{ statement.at }}{{ statement.amount_taken }}{{ statement.balance_new }}{{ statement.amount_error }}
    + {% endif %} +
    +
    +
    +
    +
    + +{% endblock %} diff --git a/kfet/templates/kfet/checkout_update.html b/kfet/templates/kfet/checkout_update.html new file mode 100644 index 00000000..0a4be1ac --- /dev/null +++ b/kfet/templates/kfet/checkout_update.html @@ -0,0 +1,67 @@ +{% extends 'kfet/base.html' %} +{% block extra_head %}{{ form.media }}{% endblock %} + +{% block title %}Édition de la caisse {{ checkout.name }}{% endblock %} +{% block content-header-title %}Caisse {{ checkout.name }} - Édition{% endblock %} + +{% block content %} + +
    +
    +
    + {% include 'kfet/left_checkout.html' %} +
    +
    +
    + {% include 'kfet/base_messages.html' %} +
    +
    +
    + {% csrf_token %} + {{ form.non_field_errors }} + {% for field in form %} + {{ field.errors }} + {{ field.label_tag }} +
    {{ field }}
    + {% if field.help_text %} +

    {{ field.help_text|safe }}

    + {% endif %} + {% endfor %} + {% if not perms.kfet.add_checkout %} + + {% endif %} + +
    +
    +
    +
    +
    + + + +{% endblock %} diff --git a/kfet/templates/kfet/checkoutstatement.html b/kfet/templates/kfet/checkoutstatement.html new file mode 100644 index 00000000..ac7cf9f8 --- /dev/null +++ b/kfet/templates/kfet/checkoutstatement.html @@ -0,0 +1,13 @@ +{% extends "kfet/base.html" %} + +{% block title %}Relevés{% endblock %} + +{% block content %} + +
      +{% for statement in checkoutstatements %} +
    • {{ statement }}
    • +{% endfor %} +
    + +{% endblock %} diff --git a/kfet/templates/kfet/checkoutstatement_create.html b/kfet/templates/kfet/checkoutstatement_create.html new file mode 100644 index 00000000..0edb66ad --- /dev/null +++ b/kfet/templates/kfet/checkoutstatement_create.html @@ -0,0 +1,178 @@ +{% extends "kfet/base.html" %} +{% load l10n %} + +{% block title %}Nouveau relevé{% endblock %} +{% block content-header-title %}Caisse {{ checkout.name }} - Nouveau relevé{% endblock %} + +{% block content %} + +
    +
    +
    + {% include 'kfet/left_checkout.html' %} +
    +
    +
    + {% include "kfet/base_messages.html" %} +
    +
    + {% csrf_token %} +
    +

    Général

    +
    +
    + Ancienne balance : {{ checkout.balance|unlocalize }}
    + Nouvelle balance : 0
    + Pris : 0
    + Erreur : 0
    + {% if not perms.kfet.add_checkoutstatement %} + + + {% endif %} + +
    +
    +
    +

    Pris

    +
    + + + + + + + + + + + + + + + + + + + +
    5€10€20€50€100€200€500€
    + + + + + + + + + + + + + + + + + + + + + +
    2€1€0.50€0.20€0.10€0.05€0.02€0.01€
    + Chèque: +
    +
    +
    +

    En caisse

    +
    + + + + + + + + + + + + + + + + + + + +
    5€10€20€50€100€200€500€
    + + + + + + + + + + + + + + + + + + + + + +
    2€1€0.50€0.20€0.10€0.05€0.02€0.01€
    +
    +
    +
    +
    +
    +
    + + + +{% endblock %} diff --git a/kfet/templates/kfet/checkoutstatement_update.html b/kfet/templates/kfet/checkoutstatement_update.html new file mode 100644 index 00000000..18d8938f --- /dev/null +++ b/kfet/templates/kfet/checkoutstatement_update.html @@ -0,0 +1,33 @@ +{% extends 'kfet/base.html' %} + +{% block title %}Modification d'un relevé{% endblock %} +{% block content-header-title %} +Caisse {{ checkout.name }} - Modification relevé {{ checkoutstatement.at }} +{% endblock %} + +{% block content %} + +
    +
    +
    + {% include 'kfet/left_checkout.html' %} +
    +
    +
    + {% include 'kfet/base_messages.html' %} +
    +
    +
    + {% csrf_token %} + {{ form.as_p }} + {% if not perms.kfet.change_checkoutstatement %} + + {% endif %} + +
    +
    +
    +
    +
    + +{% endblock %} diff --git a/kfet/templates/kfet/form_authentication_snippet.html b/kfet/templates/kfet/form_authentication_snippet.html new file mode 100644 index 00000000..d3eb2496 --- /dev/null +++ b/kfet/templates/kfet/form_authentication_snippet.html @@ -0,0 +1,6 @@ +
    + +
    + +
    +
    diff --git a/kfet/templates/kfet/form_field_snippet.html b/kfet/templates/kfet/form_field_snippet.html new file mode 100644 index 00000000..92ec385d --- /dev/null +++ b/kfet/templates/kfet/form_field_snippet.html @@ -0,0 +1,9 @@ +{% load widget_tweaks %} + +
    + +
    + {{ field|add_class:'form-control' }} + {{ field.errors }} +
    +
    diff --git a/kfet/templates/kfet/form_snippet.html b/kfet/templates/kfet/form_snippet.html new file mode 100644 index 00000000..2f6d9c7c --- /dev/null +++ b/kfet/templates/kfet/form_snippet.html @@ -0,0 +1,3 @@ +{% for field in form %} + {% include 'kfet/form_field_snippet.html' with field=field %} +{% endfor %} diff --git a/kfet/templates/kfet/form_submit_snippet.html b/kfet/templates/kfet/form_submit_snippet.html new file mode 100644 index 00000000..fba168da --- /dev/null +++ b/kfet/templates/kfet/form_submit_snippet.html @@ -0,0 +1,5 @@ +
    +
    + +
    +
    diff --git a/kfet/templates/kfet/history.html b/kfet/templates/kfet/history.html new file mode 100644 index 00000000..091b4f2f --- /dev/null +++ b/kfet/templates/kfet/history.html @@ -0,0 +1,263 @@ +{% extends 'kfet/base.html' %} +{% load staticfiles %} +{% load l10n %} + +{% block extra_head %} + + + + + + + + + + + + + +{% endblock %} + +{% block title %}Historique{% endblock %} +{% block content-header-title %}Historique{% endblock %} + +{% block content %} + +
    +
    +
    +
    +
    +
    opérations
    +
    +

    Filtres

    +
    De
    +
    à
    +
    Caisses {{ filter_form.checkouts }}
    +
    Comptes {{ filter_form.accounts }}
    +
    +
    +
    +
    +
    + {% include 'kfet/base_messages.html' %} +
    +
    +

    Général

    +
    +
    +
    +
    +

    Opérations

    +
    + +
    +
    +
    +
    +
    +
    + + + +{% endblock %} diff --git a/kfet/templates/kfet/inventory.html b/kfet/templates/kfet/inventory.html new file mode 100644 index 00000000..5d8a28f4 --- /dev/null +++ b/kfet/templates/kfet/inventory.html @@ -0,0 +1,64 @@ +{% extends 'kfet/base.html' %} + +{% block title %}Inventaires{% endblock %} +{% block content-header-title %}Inventaires{% endblock %} + +{% block content %} + +
    +
    +
    +
    +
    + +
    +
    +
    + {% include 'kfet/base_messages.html' %} +
    +
    +

    Liste des inventaires

    +
    + + + + + + + + + + + + {% for inventory in inventories %} + + + + + + + + {% endfor %} + +
    DateParNb articlesCommande
    + + + + {{ inventory.at }}{{ inventory.by.trigramme }}{{ inventory.nb_articles }} + {% if inventory.order %} + + #{{ inventory.order.pk }} + + {% endif %} +
    +
    +
    +
    +
    +
    + +{% endblock %} diff --git a/kfet/templates/kfet/inventory_create.html b/kfet/templates/kfet/inventory_create.html new file mode 100644 index 00000000..d4f53c3c --- /dev/null +++ b/kfet/templates/kfet/inventory_create.html @@ -0,0 +1,43 @@ +{% extends 'kfet/base.html' %} + +{% block title %}Nouvel inventaire{% endblock %} +{% block content-header-title %}Nouvel inventaire{% endblock %} + +{% block content %} + +{% include 'kfet/base_messages.html' %} + +
    + + + + + + + + + + {% for form in formset %} + {% ifchanged form.category %} + + + + {% endifchanged %} + + {{ form.article }} + + + + + {% endfor %} + +
    ArticleThéo.Réel
    {{ form.category_name }}
    {{ form.name }}{{ form.stock_old }}{{ form.stock_new }}
    + {% if not perms.kfet.add_inventory %} + + {% endif %} + {% csrf_token %} + {{ formset.management_form }} + +
    + +{% endblock %} diff --git a/kfet/templates/kfet/inventory_read.html b/kfet/templates/kfet/inventory_read.html new file mode 100644 index 00000000..aef6b326 --- /dev/null +++ b/kfet/templates/kfet/inventory_read.html @@ -0,0 +1,62 @@ +{% extends 'kfet/base.html' %} + +{% block title %}Inventaire #{{ inventory.pk }}{% endblock %} +{% block content-header-title %}Inventaire #{{ inventory.pk }}{% endblock %} + +{% block content %} + +
    +
    +
    +
    +
    Date: {{ inventory.at }}
    +
    Par: {{ inventory.by.trigramme }}
    + {% if inventory.order %} +
    + Commande relative:  + + #{{ inventory.order.pk }} + +
    + {% endif %} +
    +
    +
    +
    + {% include 'kfet/base_messages.html' %} +
    +
    +

    Détails

    +
    + + + + + + + + + + + {% for inventoryart in inventoryarts %} + {% ifchanged inventoryart.article.category %} + + + + {% endifchanged %} + + + + + + + {% endfor %} + +
    ArticleStock avantStock aprèsErreur
    {{ inventoryart.article.category.name }}
    {{ inventoryart.article.name }}{{ inventoryart.stock_old }}{{ inventoryart.stock_new }}{{ inventoryart.stock_error }}
    +
    +
    +
    +
    +
    + +{% endblock %} diff --git a/kfet/templates/kfet/kpsul.html b/kfet/templates/kfet/kpsul.html new file mode 100644 index 00000000..1694d57f --- /dev/null +++ b/kfet/templates/kfet/kpsul.html @@ -0,0 +1,1214 @@ +{% extends 'kfet/base.html' %} +{% load staticfiles %} + +{% block extra_head %} + + + + + + + + + + + +{% endblock %} + +{% block title %}K-Psul{% endblock %} + +{% block content-header %}{% endblock %} + +{% block content %} + +
    +
    +
    +
    + {{ trigramme_form.trigramme }} +
    +
    +
    +
    +
    +
    + + + +
    +
    +
    +
    +
    +
    +
    +
    +
    + {{ checkout_form.checkout }} +
    +
    +
    + En caisse: € +
    +
    +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    + + + + +
    +
    + + + +
    +
    + + + +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +
    + + + + + + +{% csrf_token %} + + + +{% endblock %} diff --git a/kfet/templates/kfet/left_account.html b/kfet/templates/kfet/left_account.html new file mode 100644 index 00000000..19352728 --- /dev/null +++ b/kfet/templates/kfet/left_account.html @@ -0,0 +1,43 @@ +{% load kfet_tags %} + +
    +
    {{ account.trigramme }}
    +
    {{ account.balance|ukf:account.is_cof }} UKF
    +
    +
    {{ account.name }}
    + {% if perms.kfet.is_team %} +
    {{ account.nickname }}
    + {% endif %} +
    + {% if account.email %} + {{ account.email }} + {% else %} + Pas d'email ! + {% endif %} +
    +
    + {{ account.departement }} {{ account.promo }} +
    +
    Statut COF: {{ account.is_cof }}
    +
    +
    + {% if account.negative.start %} +
    En négatif depuis {{ account.negative.start }}
    + {% endif %} + {% if account.negative.balance_offset %} +
    Solde réel: {{ account.real_balance }} €
    + {% endif %} + {% if account.negative.authz_overdraft_amount %} +
    Découvert autorisé: {{ account.negative.authz_overdraft_amount }} €
    + {% endif %} + {% if account.negative.authz_overdraft_until %} +
    Découvert autorisé jusqu'à : {{ account.negative.authz_overdraft_until }}
    + {% endif %} +
    +
    + diff --git a/kfet/templates/kfet/left_checkout.html b/kfet/templates/kfet/left_checkout.html new file mode 100644 index 00000000..e00eede8 --- /dev/null +++ b/kfet/templates/kfet/left_checkout.html @@ -0,0 +1,13 @@ +
    +
    {{ checkout.name }}
    +
    {{ checkout.balance|floatformat:2 }} €
    +
    +
    Valide du {{ checkout.valid_from|date:'l j F Y, G:i' }}
    +
    au {{ checkout.valid_to|date:'l j F Y, G:i' }}
    +
    Créée par {{ checkout.created_by }}
    +
    +
    + diff --git a/kfet/templates/kfet/login_genericteam.html b/kfet/templates/kfet/login_genericteam.html new file mode 100644 index 00000000..f5f8c863 --- /dev/null +++ b/kfet/templates/kfet/login_genericteam.html @@ -0,0 +1,7 @@ +{% extends 'kfet/base.html' %} + +{% block content %} + +Connexion utilisateur K-Fêt générique réussie + +{% endblock %} diff --git a/kfet/templates/kfet/order.html b/kfet/templates/kfet/order.html new file mode 100644 index 00000000..d1fb0442 --- /dev/null +++ b/kfet/templates/kfet/order.html @@ -0,0 +1,106 @@ +{% extends 'kfet/base.html' %} + +{% block title %}Commandes{% endblock %} +{% block content-header-title %}Commandes{% endblock %} + +{% block content %} + +
    +
    +
    +
    +
    {{ orders|length }}
    +
    commande{{ orders|length|pluralize }}
    +
    +
    +
    +
    +
    +
    + {% include 'kfet/base_messages.html' %} +
    +
    +

    Fournisseurs

    +
    + + + + + + + + + + + + {% for supplier in suppliers %} + + + + + + + + + + {% endfor %} + +
    NomMailTél.AdresseCommentaire
    + + Passer une commande + + + + + + {{ supplier.name }}{{ supplier.email }}{{ supplier.phone }}{{ supplier.address }}{{ supplier.comment }}
    +
    +
    +
    +

    Liste des commandes

    +
    + + + + + + + + + + + + {% for order in orders %} + + + + + + + + {% endfor %} + +
    DateFournisseurInventaire
    + {% if not order.inventory %} + + Générer inventaire + + {% endif %} + + + + + {{ order.at }}{{ order.supplier }} + {% if order.inventory %} + + #{{ order.inventory.pk }} + + {% endif %} +
    +
    +
    +
    +
    +
    + +{% endblock %} diff --git a/kfet/templates/kfet/order_create.html b/kfet/templates/kfet/order_create.html new file mode 100644 index 00000000..efd316a6 --- /dev/null +++ b/kfet/templates/kfet/order_create.html @@ -0,0 +1,62 @@ +{% extends 'kfet/base.html' %} + +{% block title %}Nouvelle commande{% endblock %} +{% block content-header-title %}Nouvelle commande {{ supplier.name }}{% endblock %} + +{% block content %} + +
    + {% csrf_token %} + + + + + + + + + + + + + + + + + + + + + + {% for form in formset %} + {% ifchanged form.category %} + + + + {% endifchanged %} + + {{ form.article }} + + + + + + + + + + + + + + {% endfor %} + +
    ArticleVentesV. moy.E.T.Prév.StockRec.Com.
    S1S2S3S4S5
    {{ form.category_name }}
    {{ form.name }}{{ form.v_s1 }}{{ form.v_s2 }}{{ form.v_s3 }}{{ form.v_s4 }}{{ form.v_s5 }}{{ form.v_moy }}{{ form.v_et }}{{ form.v_prev }}{{ form.stock }}{{ form.c_rec }}{{ form.quantity_ordered }}
    + {% if not perms.kfet.add_order %} + + {% endif %} + {{ formset.management_form }} + +
    + +{% endblock %} diff --git a/kfet/templates/kfet/order_read.html b/kfet/templates/kfet/order_read.html new file mode 100644 index 00000000..f7436a22 --- /dev/null +++ b/kfet/templates/kfet/order_read.html @@ -0,0 +1,67 @@ +{% extends 'kfet/base.html' %} + +{% block title %}Commande #{{ order.pk }}{% endblock %} +{% block content-header-title %}Commande #{{ order.pk }}{% endblock %} + +{% block content %} + +
    +
    +
    +
    +
    Créée le: {{ order.at }}
    +
    Fournisseur: {{ order.supplier.name }}
    +
    +
    +
    +
    + {% include 'kfet/base_messages.html' %} +
    +
    +

    Détails

    +
    + + + + + + + + + + + {% for orderart in orderarts %} + {% ifchanged orderart.article.category %} + + + + {% endifchanged %} + + + + + + + {% endfor %} + +
    ArticleCommandéCommandé (#box)Reçu
    {{ orderart.article.category.name }}
    {{ orderart.article.name }}{{ orderart.quantity_ordered }} + {% if orderart.article.box_capacity %} + {# c'est une division ! #} + {% widthratio orderart.quantity_ordered orderart.article.box_capacity 1 %} + {% endif %} + + {{ orderart.quantity_received|default_if_none:'' }} +
    +
    +
    +
    +

    Mail

    +
    + +
    +
    +
    +
    +
    + +{% endblock %} diff --git a/kfet/templates/kfet/order_to_inventory.html b/kfet/templates/kfet/order_to_inventory.html new file mode 100644 index 00000000..4345cf2b --- /dev/null +++ b/kfet/templates/kfet/order_to_inventory.html @@ -0,0 +1,48 @@ +{% extends 'kfet/base.html' %} + +{% block title %}{% endblock %} +{% block content-header-title %}{% endblock %} + +{% block content %} + +{% include 'kfet/base_messages.html' %} + +
    + {% csrf_token %} + + + + + + + + + + + + + {% for form in formset %} + {% ifchanged form.category %} + + + + {% endifchanged %} + + {{ form.article }} + + + + + + + {% endfor %} + +
    ArticleHTTVADroitsCom.Reçu
    {{ form.category_name }}
    {{ form.name }}{{ form.price_HT }}{{ form.TVA }}{{ form.rights }}{{ form.quantity_ordered }}{{ form.quantity_received }}
    + {% if not perms.kfet.order_to_inventory %} + + {% endif %} + {{ formset.management_form }} + +
    + +{% endblock %} diff --git a/kfet/templates/kfet/settings.html b/kfet/templates/kfet/settings.html new file mode 100644 index 00000000..bf48f3bb --- /dev/null +++ b/kfet/templates/kfet/settings.html @@ -0,0 +1,24 @@ +{% extends 'kfet/base.html' %} + +{% block title %}Paramètres{% endblock %} +{% block content-header-title %}Paramètres{% endblock %} + +{% block content %} + +{% include 'kfet/base_messages.html' %} + + + + + + + {% for setting in settings %} + + + + + + {% endfor %} +
    NomValeur
    Modifier{{ setting.name }}{% firstof setting.value_decimal setting.value_duration setting.value_account %}
    + +{% endblock %} diff --git a/kfet/templates/kfet/settings_update.html b/kfet/templates/kfet/settings_update.html new file mode 100644 index 00000000..3d0596c2 --- /dev/null +++ b/kfet/templates/kfet/settings_update.html @@ -0,0 +1,14 @@ +{% extends 'kfet/base.html' %} + +{% block title %}Modification de {{ settings.name }}{% endblock %} +{% block content-header-title %}Modification de {{ settings.name }}{% endblock %} + +{% block content %} + +
    + {% csrf_token %} + {{ form.as_p }} + +
    + +{% endblock %} diff --git a/kfet/templates/kfet/supplier_form.html b/kfet/templates/kfet/supplier_form.html new file mode 100644 index 00000000..d1e10a5c --- /dev/null +++ b/kfet/templates/kfet/supplier_form.html @@ -0,0 +1,19 @@ +{% extends 'kfet/base.html' %} + +{% block title %}Fournisseur - Modification{% endblock %} +{% block content-header-title %}Fournisseur - Modification{% endblock %} + +{% block content %} + +{% include 'kfet/base_messages.html' %} + +
    + {% csrf_token %} + {{ form.as_p }} + {% if not perms.kfet.change_supplier %} + + {% endif %} + +
    + +{% endblock %} diff --git a/kfet/templates/kfet/transfers.html b/kfet/templates/kfet/transfers.html new file mode 100644 index 00000000..1875a21e --- /dev/null +++ b/kfet/templates/kfet/transfers.html @@ -0,0 +1,47 @@ +{% extends 'kfet/base.html' %} + +{% block title %}Transferts{% endblock %} +{% block content-header-title %}Transferts{% endblock %} + +{% block content %} + +
    +
    +
    +
    +
    + +
    +
    +
    + {% include 'kfet/base_messages.html' %} +
    +
    +

    Liste des transferts

    +
    + {% for transfergroup in transfergroups %} +
    + {{ transfergroup.at }} + {{ transfergroup.valid_by.trigramme }} + {{ transfergroup.comment }} +
    + {% for transfer in transfergroup.transfers.all %} +
    + {{ transfer.amount }} € + {{ transfer.from_acc.trigramme }} + + {{ transfer.to_acc.trigramme }} +
    + {% endfor %} + {% endfor %} +
    +
    + + + + +{% endblock %} diff --git a/kfet/templates/kfet/transfers_create.html b/kfet/templates/kfet/transfers_create.html new file mode 100644 index 00000000..294db1e5 --- /dev/null +++ b/kfet/templates/kfet/transfers_create.html @@ -0,0 +1,121 @@ +{% extends 'kfet/base.html' %} +{% load staticfiles %} + +{% block extra_head %} + + +{% endblock %} + +{% block title %}Nouveaux transferts{% endblock %} +{% block content-header-title %}Nouveaux transferts{% endblock %} + +{% block content %} + +{% csrf_token %} + +
    +
    + + {{ transfer_formset.management_form }} +
    + + + + + + + + + + + + {% for form in transfer_formset %} + + + + + + + + {% endfor %} + +
    DeVers
    + + {{ form.from_acc }} + {{ form.amount }} + + {{ form.to_acc }} +
    +
    + + + +{% endblock %} diff --git a/kfet/templatetags/__init__.py b/kfet/templatetags/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/kfet/templatetags/kfet_tags.py b/kfet/templatetags/kfet_tags.py new file mode 100644 index 00000000..bb8bce9f --- /dev/null +++ b/kfet/templatetags/kfet_tags.py @@ -0,0 +1,41 @@ +# -*- coding: utf-8 -*- + +from __future__ import (absolute_import, division, + print_function, unicode_literals) +from builtins import * + +from django import template +from django.utils.html import escape +from django.utils.safestring import mark_safe +from django.core.cache import cache +from kfet.models import Settings +import re + +register = template.Library() + +def highlight_text(text, q): + q2 = "|".join(q.split()) + pattern = re.compile(r"(?P%s)" % q2, re.IGNORECASE) + return mark_safe(re.sub(pattern, r"\g", text)) + +@register.filter(is_safe=True) +def highlight_user(user, q): + if user.first_name and user.last_name: + text = "%s %s (%s)" % (user.first_name, user.last_name, user.username) + else: + text = user.username + return highlight_text(escape(text), q) + +@register.filter(is_safe=True) +def highlight_clipper(clipper, q): + if clipper.fullname: + text = "%s (%s)" % (clipper.fullname, clipper.username) + else: + text = clipper.username + return highlight_text(escape(text), q) + +@register.filter() +def ukf(balance, is_cof): + grant = is_cof and (1 + Settings.SUBVENTION_COF() / 100) or 1 + # float nécessaire car sinon problème avec le round de future.builtins + return round(float(balance * 10 * grant)) diff --git a/kfet/tests.py b/kfet/tests.py new file mode 100644 index 00000000..03ee6fc6 --- /dev/null +++ b/kfet/tests.py @@ -0,0 +1,9 @@ +# -*- coding: utf-8 -*- + +from __future__ import (absolute_import, division, + print_function, unicode_literals) +from builtins import * + +from django.test import TestCase + +# Create your tests here. diff --git a/kfet/urls.py b/kfet/urls.py new file mode 100644 index 00000000..e0bacf9a --- /dev/null +++ b/kfet/urls.py @@ -0,0 +1,204 @@ +# -*- coding: utf-8 -*- + +from __future__ import (absolute_import, division, + print_function, unicode_literals) +from builtins import * + +from django.conf.urls import url +from django.contrib.auth.decorators import permission_required +from kfet import views +from kfet import autocomplete +from kfet.decorators import teamkfet_required + +urlpatterns = [ + url(r'^$', views.home, + name = 'kfet.home'), + url(r'^login/genericteam$', views.login_genericteam, + name = 'kfet.login.genericteam'), + url(r'^history$', views.history, + name = 'kfet.history'), + + # ----- + # Account urls + # ----- + + # Account - General + url(r'^accounts/$', views.account, + name = 'kfet.account'), + url(r'^accounts/is_validandfree$', views.account_is_validandfree_ajax, + name = 'kfet.account.is_validandfree.ajax'), + + # Account - Create + url(r'^accounts/new$', views.account_create, + name = 'kfet.account.create'), + url(r'^accounts/new_special$', views.account_create_special, + name = 'kfet.account.create_special'), + url(r'^accounts/new/user/(?P.+)$', views.account_create_ajax, + name = 'kfet.account.create.fromuser'), + url(r'^accounts/new/clipper/(?P.+)$', views.account_create_ajax, + name = 'kfet.account.create.fromclipper'), + url(r'^accounts/new/empty$', views.account_create_ajax, + name = 'kfet.account.create.empty'), + url(r'^autocomplete/account_new$', autocomplete.account_create, + name = 'kfet.account.create.autocomplete'), + + # Account - Read + url(r'^accounts/(?P.{3})$', views.account_read, + name = 'kfet.account.read'), + + # Account - Update + url(r'^accounts/(?P.{3})/edit$', views.account_update, + name = 'kfet.account.update'), + + # Account - Groups + url(r'^accounts/groups$', views.account_group, + name = 'kfet.account.group'), + url(r'^accounts/groups/new$', + permission_required('kfet.manage_perms')(views.AccountGroupCreate.as_view()), + name = 'kfet.account.group.create'), + url(r'^accounts/groups/(?P\d+)/edit$', + permission_required('kfet.manage_perms')(views.AccountGroupUpdate.as_view()), + name = 'kfet.account.group.update'), + + url(r'^accounts/negatives$', + permission_required('kfet.view_negs')(views.AccountNegativeList.as_view()), + name = 'kfet.account.negative'), + + # ----- + # Checkout urls + # ----- + + # Checkout - General + url('^checkouts/$', + teamkfet_required(views.CheckoutList.as_view()), + name = 'kfet.checkout'), + # Checkout - Create + url('^checkouts/new$', + teamkfet_required(views.CheckoutCreate.as_view()), + name = 'kfet.checkout.create'), + # Checkout - Read + url('^checkouts/(?P\d+)$', + teamkfet_required(views.CheckoutRead.as_view()), + name = 'kfet.checkout.read'), + # Checkout - Update + url('^checkouts/(?P\d+)/edit$', + teamkfet_required(views.CheckoutUpdate.as_view()), + name = 'kfet.checkout.update'), + + ### Checkout Statements urls + + # Checkout Statement - General + url('^checkouts/statements/$', + teamkfet_required(views.CheckoutStatementList.as_view()), + name = 'kfet.checkoutstatement'), + # Checkout Statement - Create + url('^checkouts/(?P\d+)/statements/add', + teamkfet_required(views.CheckoutStatementCreate.as_view()), + name = 'kfet.checkoutstatement.create'), + # Checkout Statement - Update + url('^checkouts/(?P\d+)/statements/(?P\d+)/edit', + teamkfet_required(views.CheckoutStatementUpdate.as_view()), + name = 'kfet.checkoutstatement.update'), + + # ----- + # Article urls + # ----- + + # Article - General + url('^articles/$', + teamkfet_required(views.ArticleList.as_view()), + name = 'kfet.article'), + # Article - Create + url('^articles/new$', + teamkfet_required(views.ArticleCreate.as_view()), + name = 'kfet.article.create'), + # Article - Read + url('^articles/(?P\d+)$', + teamkfet_required(views.ArticleRead.as_view()), + name = 'kfet.article.read'), + # Article - Update + url('^articles/(?P\d+)/edit$', + teamkfet_required(views.ArticleUpdate.as_view()), + name = 'kfet.article.update'), + + # ----- + # K-Psul urls + # ----- + + url('^k-psul/$', views.kpsul, name = 'kfet.kpsul'), + url('^k-psul/checkout_data$', views.kpsul_checkout_data, + name = 'kfet.kpsul.checkout_data'), + url('^k-psul/perform_operations$', views.kpsul_perform_operations, + name = 'kfet.kpsul.perform_operations'), + url('^k-psul/cancel_operations$', views.kpsul_cancel_operations, + name = 'kfet.kpsul.cancel_operations'), + url('^k-psul/articles_data', views.kpsul_articles_data, + name = 'kfet.kpsul.articles_data'), + url('^k-psul/update_addcost$', views.kpsul_update_addcost, + name = 'kfet.kpsul.update_addcost'), + url('^k-psul/get_settings$', views.kpsul_get_settings, + name = 'kfet.kpsul.get_settings'), + + # ----- + # JSON urls + # ----- + + url(r'^history.json$', views.history_json, + name = 'kfet.history.json'), + url(r'^accounts/read.json$', views.account_read_json, + name = 'kfet.account.read.json'), + + + # ----- + # Settings urls + # ----- + + url(r'^settings/$', + permission_required('kfet.change_settings')(views.SettingsList.as_view()), + name = 'kfet.settings'), + url(r'^settings/(?P\d+)/edit$', + permission_required('kfet.change_settings')(views.SettingsUpdate.as_view()), + name = 'kfet.settings.update'), + + # ----- + # Transfers urls + # ----- + + url(r'^transfers/$', views.transfers, + name = 'kfet.transfers'), + url(r'^transfers/new$', views.transfers_create, + name = 'kfet.transfers.create'), + url(r'^transfers/perform$', views.perform_transfers, + name = 'kfet.transfers.perform'), + + # ----- + # Inventories urls + # ----- + + url(r'^inventaires/$', + teamkfet_required(views.InventoryList.as_view()), + name = 'kfet.inventory'), + url(r'^inventaires/new$', views.inventory_create, + name = 'kfet.inventory.create'), + url(r'^inventaires/(?P\d+)$', + teamkfet_required(views.InventoryRead.as_view()), + name = 'kfet.inventory.read'), + + # ----- + # Order urls + # ----- + + url(r'^orders/$', + teamkfet_required(views.OrderList.as_view()), + name = 'kfet.order'), + url(r'^orders/(?P\d+)$', + teamkfet_required(views.OrderRead.as_view()), + name = 'kfet.order.read'), + url(r'^orders/suppliers/(?P\d+)/edit$', + teamkfet_required(views.SupplierUpdate.as_view()), + name = 'kfet.order.supplier.update'), + url(r'^orders/suppliers/(?P\d+)/new-order$', views.order_create, + name = 'kfet.order.new'), + url(r'^orders/(?P\d+)/to_inventory$', views.order_to_inventory, + name = 'kfet.order.to_inventory'), +] diff --git a/kfet/views.py b/kfet/views.py new file mode 100644 index 00000000..736dbe8a --- /dev/null +++ b/kfet/views.py @@ -0,0 +1,1831 @@ +# -*- coding: utf-8 -*- + +from __future__ import (absolute_import, division, + print_function, unicode_literals) +from builtins import * + +from django.shortcuts import render, get_object_or_404, redirect +from django.core.exceptions import PermissionDenied, ValidationError +from django.core.cache import cache +from django.views.generic import ListView, DetailView +from django.views.generic.edit import CreateView, UpdateView, DeleteView, FormView +from django.core.urlresolvers import reverse_lazy +from django.contrib import messages +from django.contrib.messages.views import SuccessMessageMixin +from django.contrib.auth import authenticate, login +from django.contrib.auth.decorators import login_required, permission_required +from django.contrib.auth.models import User, Permission, Group +from django.http import HttpResponse, JsonResponse, Http404 +from django.forms import modelformset_factory, formset_factory +from django.db import IntegrityError, transaction +from django.db.models import F, Sum, Prefetch, Count, Func +from django.db.models.functions import Coalesce +from django.utils import timezone +from django.utils.crypto import get_random_string +from gestioncof.models import CofProfile, Clipper +from kfet.decorators import teamkfet_required +from kfet.models import (Account, Checkout, Article, Settings, AccountNegative, + CheckoutStatement, GenericTeamToken, Supplier, SupplierArticle, Inventory, + InventoryArticle, Order, OrderArticle) +from kfet.forms import * +from collections import defaultdict +from kfet import consumers +from datetime import timedelta +import django_cas_ng +import hashlib +import heapq +import statistics + +@login_required +def home(request): + return render(request, "kfet/base.html") + +@teamkfet_required +def login_genericteam(request): + profile, _ = CofProfile.objects.get_or_create(user=request.user) + logout_cas = '' + if profile.login_clipper: + logout_cas = django_cas_ng.views.logout(request) + + token = GenericTeamToken.objects.create(token=get_random_string(50)) + user = authenticate(username="kfet_genericteam", token=token.token) + login(request, user) + + if logout_cas: + return logout_cas + + return render(request, "kfet/login_genericteam.html") + +def put_cleaned_data_in_dict(dict, form): + for field in form.cleaned_data: + dict[field] = form.cleaned_data[field] + +# ----- +# Account views +# ----- + +# Account - General + +@login_required +@teamkfet_required +def account(request): + accounts = Account.objects.select_related('cofprofile__user').order_by('trigramme') + return render(request, "kfet/account.html", { 'accounts' : accounts }) + +@login_required +@teamkfet_required +def account_is_validandfree_ajax(request): + if not request.GET.get("trigramme", ''): + raise Http404 + trigramme = request.GET.get("trigramme") + data = Account.is_validandfree(trigramme) + return JsonResponse(data) + +# Account - Create + +@login_required +@teamkfet_required +def account_create_special(request): + + # Enregistrement + if request.method == "POST": + trigramme_form = AccountTriForm(request.POST, initial={'balance':0}) + balance_form = AccountBalanceForm(request.POST) + + # Peuplement des forms + username = request.POST.get('username') + login_clipper = request.POST.get('login_clipper') + + forms = get_account_create_forms( + request, username=username, login_clipper=login_clipper) + + account_form = forms['account_form'] + cof_form = forms['cof_form'] + user_form = forms['user_form'] + + if all((user_form.is_valid(), cof_form.is_valid(), + trigramme_form.is_valid(), account_form.is_valid(), + balance_form.is_valid())): + # Checking permission + if not request.user.has_perm('kfet.special_add_account'): + messages.error(request, 'Permission refusée') + else: + data = {} + # Fill data for Account.save() + put_cleaned_data_in_dict(data, user_form) + put_cleaned_data_in_dict(data, cof_form) + + try: + account = trigramme_form.save(data = data) + account_form = AccountNoTriForm(request.POST, instance=account) + account_form.save() + balance_form = AccountBalanceForm(request.POST, instance=account) + balance_form.save() + amount = balance_form.cleaned_data['balance'] + checkout = Checkout.objects.get(name='Initial') + is_cof = account.is_cof + opegroup = OperationGroup.objects.create( + on_acc=account, + checkout=checkout, + amount = amount, + is_cof = account.is_cof) + ope = Operation.objects.create( + group = opegroup, + type = Operation.INITIAL, + amount = amount, + is_checkout = False) + messages.success(request, 'Compte créé : %s' % account.trigramme) + return redirect('kfet.account.create') + except Account.UserHasAccount as e: + messages.error(request, \ + "Cet utilisateur a déjà un compte K-Fêt : %s" % e.trigramme) + else: + initial = { 'trigramme': request.GET.get('trigramme', '') } + trigramme_form = AccountTriForm(initial = initial) + balance_form = AccountBalanceForm(initial = {'balance': 0}) + account_form = None + cof_form = None + user_form = None + + return render(request, "kfet/account_create_special.html", { + 'trigramme_form': trigramme_form, + 'account_form': account_form, + 'cof_form': cof_form, + 'user_form': user_form, + 'balance_form': balance_form, + }) + + +# Account - Create + +@login_required +@teamkfet_required +def account_create(request): + + # Enregistrement + if request.method == "POST": + trigramme_form = AccountTriForm(request.POST) + + # Peuplement des forms + username = request.POST.get('username') + login_clipper = request.POST.get('login_clipper') + + forms = get_account_create_forms( + request, username=username, login_clipper=login_clipper) + + account_form = forms['account_form'] + cof_form = forms['cof_form'] + user_form = forms['user_form'] + + if all((user_form.is_valid(), cof_form.is_valid(), + trigramme_form.is_valid(), account_form.is_valid())): + # Checking permission + if not request.user.has_perm('kfet.add_account'): + messages.error(request, 'Permission refusée') + else: + data = {} + # Fill data for Account.save() + put_cleaned_data_in_dict(data, user_form) + put_cleaned_data_in_dict(data, cof_form) + + try: + account = trigramme_form.save(data = data) + account_form = AccountNoTriForm(request.POST, instance=account) + account_form.save() + messages.success(request, 'Compte créé : %s' % account.trigramme) + return redirect('kfet.account.create') + except Account.UserHasAccount as e: + messages.error(request, \ + "Cet utilisateur a déjà un compte K-Fêt : %s" % e.trigramme) + else: + initial = { 'trigramme': request.GET.get('trigramme', '') } + trigramme_form = AccountTriForm(initial = initial) + account_form = None + cof_form = None + user_form = None + + return render(request, "kfet/account_create.html", { + 'trigramme_form': trigramme_form, + 'account_form': account_form, + 'cof_form': cof_form, + 'user_form': user_form, + }) + +def account_form_set_readonly_fields(user_form, cof_form): + user_form.fields['username'].widget.attrs['readonly'] = True + cof_form.fields['login_clipper'].widget.attrs['readonly'] = True + cof_form.fields['is_cof'].widget.attrs['disabled'] = True + +def get_account_create_forms(request=None, username=None, login_clipper=None): + user = None + clipper = None + if login_clipper and not username: + # à partir d'un clipper + # le user associé à ce clipper ne devrait pas encore exister + clipper = get_object_or_404(Clipper, username = login_clipper) + try: + # Vérification que clipper ne soit pas déjà dans User + user = User.objects.get(username=login_clipper) + # Ici, on nous a menti, le user existe déjà + username = user.username + clipper = None + except User.DoesNotExist: + # Clipper (sans user déjà existant) + + # UserForm - Prefill + user_initial = { + 'username' : login_clipper, + 'email' : "%s@clipper.ens.fr" % login_clipper} + if clipper.fullname: + # Prefill du nom et prénom + names = clipper.fullname.split() + # Le premier, c'est le prénom + user_initial['first_name'] = names[0] + if len(names) > 1: + # Si d'autres noms -> tous dans le nom de famille + user_initial['last_name'] = " ".join(names[1:]) + # CofForm - Prefill + cof_initial = { 'login_clipper': login_clipper } + + # Form créations + if request: + user_form = UserForm(request.POST, initial=user_initial, from_clipper=True) + cof_form = CofForm(request.POST, initial=cof_initial) + else: + user_form = UserForm(initial=user_initial, from_clipper=True) + cof_form = CofForm(initial=cof_initial) + + # Protection (read-only) des champs username et login_clipper + account_form_set_readonly_fields(user_form, cof_form) + if username and not clipper: + try: + user = User.objects.get(username=username) + # le user existe déjà + # récupération du profil cof + (cof, _) = CofProfile.objects.get_or_create(user=user) + # UserForm + CofForm - Création à partir des instances existantes + if request: + user_form = UserForm(request.POST, instance = user) + cof_form = CofForm(request.POST, instance = cof) + else: + user_form = UserForm(instance=user) + cof_form = CofForm(instance=cof) + # Protection (read-only) des champs username, login_clipper et is_cof + account_form_set_readonly_fields(user_form, cof_form) + except User.DoesNotExist: + # le username donnée n'existe pas -> Création depuis rien + # (éventuellement en cours avec erreurs précédemment) + pass + if not user and not clipper: + # connaît pas du tout, faut tout remplir + if request: + user_form = UserForm(request.POST) + cof_form = CofForm(request.POST) + else: + user_form = UserForm() + cof_form = CofForm() + # mais on laisse le username en écriture + cof_form.fields['login_clipper'].widget.attrs['readonly'] = True + cof_form.fields['is_cof'].widget.attrs['disabled'] = True + + if request: + account_form = AccountNoTriForm(request.POST) + else: + account_form = AccountNoTriForm() + + return { + 'account_form': account_form, + 'cof_form': cof_form, + 'user_form': user_form, + } + + +@login_required +@teamkfet_required +def account_create_ajax(request, username=None, login_clipper=None): + forms = get_account_create_forms(request=None, username=username, login_clipper=login_clipper) + return render(request, "kfet/account_create_form.html", { + 'account_form' : forms['account_form'], + 'cof_form' : forms['cof_form'], + 'user_form' : forms['user_form'], + }) + +# Account - Read + +@login_required +def account_read(request, trigramme): + try: + account = Account.objects.select_related('negative').get(trigramme=trigramme) + except Account.DoesNotExist: + raise Http404 + + # Checking permissions + if not request.user.has_perm('kfet.is_team') \ + and request.user != account.user: + raise PermissionDenied + + addcosts = (OperationGroup.objects + .filter(opes__addcost_for=account,opes__canceled_at=None) + .extra({'date':"date(at)"}) + .values('date') + .annotate(sum_addcosts=Sum('opes__addcost_amount')) + .order_by('-date')) + + return render(request, "kfet/account_read.html", { + 'account' : account, + 'addcosts': addcosts, + 'settings': { 'subvention_cof': Settings.SUBVENTION_COF() }, + }) + +# Account - Update + +@login_required +def account_update(request, trigramme): + account = get_object_or_404(Account, trigramme=trigramme) + + # Checking permissions + if not request.user.has_perm('kfet.is_team') \ + and request.user != account.user: + raise PermissionDenied + + if request.user.has_perm('kfet.is_team'): + user_form = UserRestrictTeamForm(instance=account.user) + group_form = UserGroupForm(instance=account.user) + account_form = AccountForm(instance=account) + cof_form = CofRestrictForm(instance=account.cofprofile) + pwd_form = AccountPwdForm() + if account.balance < 0 and not hasattr(account, 'negative'): + AccountNegative.objects.create(account=account, start=timezone.now()) + account.refresh_from_db() + if hasattr(account, 'negative'): + negative_form = AccountNegativeForm(instance=account.negative) + else: + negative_form = None + else: + user_form = UserRestrictForm(instance=account.user) + account_form = AccountRestrictForm(instance=account) + cof_form = None + group_form = None + negative_form = None + pwd_form = None + + if request.method == "POST": + # Update attempt + success = False + missing_perm = True + + if request.user.has_perm('kfet.is_team'): + account_form = AccountForm(request.POST, instance=account) + cof_form = CofRestrictForm(request.POST, instance=account.cofprofile) + user_form = UserRestrictTeamForm(request.POST, instance=account.user) + group_form = UserGroupForm(request.POST, instance=account.user) + pwd_form = AccountPwdForm(request.POST) + if hasattr(account, 'negative'): + negative_form = AccountNegativeForm(request.POST, instance=account.negative) + + if (request.user.has_perm('kfet.change_account') + and account_form.is_valid() and cof_form.is_valid() + and user_form.is_valid()): + missing_perm = False + data = {} + # Fill data for Account.save() + put_cleaned_data_in_dict(data, user_form) + put_cleaned_data_in_dict(data, cof_form) + + # Updating + account_form.save(data = data) + + # Checking perm to update password + if (request.user.has_perm('kfet.change_account_password') + and pwd_form.is_valid()): + pwd = pwd_form.cleaned_data['pwd1'] + pwd_sha256 = hashlib.sha256(pwd.encode()).hexdigest() + Account.objects.filter(pk=account.pk).update( + password = pwd_sha256) + messages.success(request, 'Mot de passe mis à jour') + + # Checking perm to manage perms + if (request.user.has_perm('kfet.manage_perms') + and group_form.is_valid()): + group_form.save() + + # Checking perm to manage negative + if hasattr(account, 'negative'): + balance_offset_old = 0 + if account.negative.balance_offset: + balance_offset_old = account.negative.balance_offset + if (hasattr(account, 'negative') + and request.user.has_perm('kfet.change_accountnegative') + and negative_form.is_valid()): + balance_offset_new = negative_form.cleaned_data['balance_offset'] + if not balance_offset_new: + balance_offset_new = 0 + balance_offset_diff = balance_offset_new - balance_offset_old + Account.objects.filter(pk=account.pk).update( + balance = F('balance') + balance_offset_diff) + negative_form.save() + if not balance_offset_new and Account.objects.get(pk=account.pk).balance >= 0: + AccountNegative.objects.get(account=account).delete() + + success = True + messages.success(request, + 'Informations du compte %s mises à jour' % account.trigramme) + + if request.user == account.user: + missing_perm = False + account.refresh_from_db() + user_form = UserRestrictForm(request.POST, instance=account.user) + account_form = AccountRestrictForm(request.POST, instance=account) + + if user_form.is_valid() and account_form.is_valid(): + user_form.save() + account_form.save() + success = True + messages.success(request, 'Vos informations ont été mises à jour') + + if missing_perm: + messages.error(request, 'Permission refusée') + if success: + return redirect('kfet.account.read', account.trigramme) + else: + messages.error(request, 'Informations non mises à jour. Corrigez les erreurs') + + return render(request, "kfet/account_update.html", { + 'account' : account, + 'account_form' : account_form, + 'cof_form' : cof_form, + 'user_form' : user_form, + 'group_form' : group_form, + 'negative_form': negative_form, + 'pwd_form' : pwd_form, + }) + +@permission_required('kfet.manage_perms') +def account_group(request): + groups = (Group.objects + .filter(name__icontains='K-Fêt') + .prefetch_related('permissions', 'user_set__profile__account_kfet') + ) + return render(request, 'kfet/account_group.html', { 'groups': groups }) + +class AccountGroupCreate(SuccessMessageMixin, CreateView): + model = Group + template_name = 'kfet/account_group_form.html' + form_class = GroupForm + success_message = 'Nouveau groupe : %(name)s' + success_url = reverse_lazy('kfet.account.group') + +class AccountGroupUpdate(UpdateView): + queryset = Group.objects.filter(name__icontains='K-Fêt') + template_name = 'kfet/account_group_form.html' + form_class = GroupForm + success_message = 'Groupe modifié : %(name)s' + success_url = reverse_lazy('kfet.account.group') + +class AccountNegativeList(ListView): + queryset = (AccountNegative.objects + .select_related('account', 'account__cofprofile__user')) + template_name = 'kfet/account_negative.html' + context_object_name = 'negatives' + + def get_context_data(self, **kwargs): + context = super(AccountNegativeList, self).get_context_data(**kwargs) + context['settings'] = { + 'overdraft_amount': Settings.OVERDRAFT_AMOUNT(), + 'overdraft_duration': Settings.OVERDRAFT_DURATION(), + } + negs_sum = (AccountNegative.objects + .aggregate( + bal = Coalesce(Sum('account__balance'),0), + offset = Coalesce(Sum('balance_offset'),0), + ) + ) + context['negatives_sum'] = negs_sum['bal'] + negs_sum['offset'] + return context + +# ----- +# Checkout views +# ----- + +# Checkout - General + +class CheckoutList(ListView): + model = Checkout + template_name = 'kfet/checkout.html' + context_object_name = 'checkouts' + +# Checkout - Create + +class CheckoutCreate(SuccessMessageMixin, CreateView): + model = Checkout + template_name = 'kfet/checkout_create.html' + form_class = CheckoutForm + success_message = 'Nouvelle caisse : %(name)s' + + # Surcharge de la validation + def form_valid(self, form): + # Checking permission + if not self.request.user.has_perm('kfet.add_checkout'): + form.add_error(None, 'Permission refusée') + return self.form_invalid(form) + + # Creating + form.instance.created_by = self.request.user.profile.account_kfet + checkout = form.save() + + # Création d'un relevé avec balance initiale + CheckoutStatement.objects.create( + checkout = checkout, + by = self.request.user.profile.account_kfet, + balance_old = checkout.balance, + balance_new = checkout.balance, + amount_taken = 0) + + return super(CheckoutCreate, self).form_valid(form) + +# Checkout - Read + +class CheckoutRead(DetailView): + model = Checkout + template_name = 'kfet/checkout_read.html' + context_object_name = 'checkout' + + def get_context_data(self, **kwargs): + context = super(CheckoutRead, self).get_context_data(**kwargs) + context['statements'] = context['checkout'].statements.order_by('-at') + return context + +# Checkout - Update + +class CheckoutUpdate(SuccessMessageMixin, UpdateView): + model = Checkout + template_name = 'kfet/checkout_update.html' + form_class = CheckoutRestrictForm + success_message = 'Informations mises à jour pour la caisse : %(name)s' + + # Surcharge de la validation + def form_valid(self, form): + # Checking permission + if not self.request.user.has_perm('kfet.change_checkout'): + form.add_error(None, 'Permission refusée') + return self.form_invalid(form) + # Updating + return super(CheckoutUpdate, self).form_valid(form) + +# ----- +# Checkout Statement views +# ----- + +# Checkout Statement - General + +class CheckoutStatementList(ListView): + model = CheckoutStatement + queryset = CheckoutStatement.objects.order_by('-at') + template_name = 'kfet/checkoutstatement.html' + context_object_name= 'checkoutstatements' + +# Checkout Statement - Create + +def getAmountTaken(data): + return Decimal(data.taken_001 * 0.01 + data.taken_002 * 0.02 + + data.taken_005 * 0.05 + data.taken_01 * 0.1 + + data.taken_02 * 0.2 + data.taken_05 * 0.5 + + data.taken_1 * 1 + data.taken_2 * 2 + + data.taken_5 * 5 + data.taken_10 * 10 + + data.taken_20 * 20 + data.taken_50 * 50 + + data.taken_100 * 100 + data.taken_200 * 200 + + data.taken_500 * 500 + float(data.taken_cheque)) + +def getAmountBalance(data): + return Decimal(data['balance_001'] * 0.01 + data['balance_002'] * 0.02 + + data['balance_005'] * 0.05 + data['balance_01'] * 0.1 + + data['balance_02'] * 0.2 + data['balance_05'] * 0.5 + + data['balance_1'] * 1 + data['balance_2'] * 2 + + data['balance_5'] * 5 + data['balance_10'] * 10 + + data['balance_20'] * 20 + data['balance_50'] * 50 + + data['balance_100'] * 100 + data['balance_200'] * 200 + + data['balance_500'] * 500) + +class CheckoutStatementCreate(SuccessMessageMixin, CreateView): + model = CheckoutStatement + template_name = 'kfet/checkoutstatement_create.html' + form_class = CheckoutStatementCreateForm + success_message = 'Nouveau relevé : %(checkout)s - %(at)s' + + def get_success_url(self): + return reverse_lazy('kfet.checkout.read', kwargs={'pk':self.kwargs['pk_checkout']}) + + def get_success_message(self, cleaned_data): + return self.success_message % dict( + cleaned_data, + checkout = self.object.checkout.name, + at = self.object.at) + + def get_context_data(self, **kwargs): + context = super(CheckoutStatementCreate, self).get_context_data(**kwargs) + checkout = Checkout.objects.get(pk=self.kwargs['pk_checkout']) + context['checkout'] = checkout + return context + + def form_valid(self, form): + # Checking permission + if not self.request.user.has_perm('kfet.add_checkoutstatement'): + form.add_error(None, 'Permission refusée') + return self.form_invalid(form) + # Creating + form.instance.amount_taken = getAmountTaken(form.instance) + if not form.instance.not_count: + form.instance.balance_new = getAmountBalance(form.cleaned_data) + form.instance.checkout_id = self.kwargs['pk_checkout'] + form.instance.by = self.request.user.profile.account_kfet + return super(CheckoutStatementCreate, self).form_valid(form) + +class CheckoutStatementUpdate(SuccessMessageMixin, UpdateView): + model = CheckoutStatement + template_name = 'kfet/checkoutstatement_update.html' + form_class = CheckoutStatementUpdateForm + success_message = 'Relevé modifié' + + def get_success_url(self): + return reverse_lazy('kfet.checkout.read', kwargs={'pk':self.kwargs['pk_checkout']}) + + def get_context_data(self, **kwargs): + context = super(CheckoutStatementUpdate, self).get_context_data(**kwargs) + checkout = Checkout.objects.get(pk=self.kwargs['pk_checkout']) + context['checkout'] = checkout + return context + + def form_valid(self, form): + # Checking permission + if not self.request.user.has_perm('kfet.change_checkoutstatement'): + form.add_error(None, 'Permission refusée') + return self.form_invalid(form) + # Updating + form.instance.amount_taken = getAmountTaken(form.instance) + return super(CheckoutStatementUpdate, self).form_valid(form) + +# ----- +# Article views +# ----- + +# Article - General + +class ArticleList(ListView): + queryset = (Article.objects + .select_related('category') + .prefetch_related(Prefetch('inventories', + queryset = Inventory.objects.order_by('-at'), + to_attr = 'inventory')) + .order_by('category', '-is_sold', 'name')) + template_name = 'kfet/article.html' + context_object_name = 'articles' + +# Article - Create + +class ArticleCreate(SuccessMessageMixin, CreateView): + model = Article + template_name = 'kfet/article_create.html' + form_class = ArticleForm + success_message = 'Nouvel item : %(category)s - %(name)s' + + # Surcharge de la validation + def form_valid(self, form): + # Checking permission + if not self.request.user.has_perm('kfet.add_article'): + form.add_error(None, 'Permission refusée') + return self.form_invalid(form) + + # Save ici pour save le manytomany suppliers + article = form.save() + # Save des suppliers déjà existant + for supplier in form.cleaned_data['suppliers']: + SupplierArticle.objects.create( + article = article, supplier = supplier) + + # Nouveau supplier + supplier_new = form.cleaned_data['supplier_new'].strip() + if supplier_new: + supplier, created = Supplier.objects.get_or_create( + name=supplier_new) + if created: + SupplierArticle.objects.create( + article = article, supplier = supplier) + + # Inventaire avec stock initial + inventory = Inventory() + inventory.by = self.request.user.profile.account_kfet + inventory.save() + InventoryArticle.objects.create( + inventory = inventory, + article = article, + stock_old = article.stock, + stock_new = article.stock, + ) + + # Creating + return super(ArticleCreate, self).form_valid(form) + +# Article - Read + +class ArticleRead(DetailView): + model = Article + template_name = 'kfet/article_read.html' + context_object_name = 'article' + + def get_context_data(self, **kwargs): + context = super(ArticleRead, self).get_context_data(**kwargs) + inventoryarts = (InventoryArticle.objects + .filter(article = self.object) + .select_related('inventory') + .order_by('-inventory__at')) + context['inventoryarts'] = inventoryarts + supplierarts = (SupplierArticle.objects + .filter(article = self.object) + .select_related('supplier') + .order_by('-at')) + context['supplierarts'] = supplierarts + return context + +# Article - Update + +class ArticleUpdate(SuccessMessageMixin, UpdateView): + model = Article + template_name = 'kfet/article_update.html' + form_class = ArticleRestrictForm + success_message = "Informations mises à jour pour l'article : %(name)s" + + # Surcharge de la validation + def form_valid(self, form): + # Checking permission + if not self.request.user.has_perm('kfet.change_article'): + form.add_error(None, 'Permission refusée') + return self.form_invalid(form) + + # Save ici pour save le manytomany suppliers + article = form.save() + # Save des suppliers déjà existant + for supplier in form.cleaned_data['suppliers']: + if supplier not in article.suppliers.all(): + SupplierArticle.objects.create( + article = article, supplier = supplier) + + # On vire les suppliers désélectionnés + for supplier in article.suppliers.all(): + if supplier not in form.cleaned_data['suppliers']: + SupplierArticle.objects.filter( + article = article, supplier = supplier).delete() + + # Nouveau supplier + supplier_new = form.cleaned_data['supplier_new'].strip() + if supplier_new: + supplier, created = Supplier.objects.get_or_create( + name=supplier_new) + if created: + SupplierArticle.objects.create( + article = article, supplier = supplier) + + # Updating + return super(ArticleUpdate, self).form_valid(form) + +# ----- +# K-Psul +# ----- + +@teamkfet_required +def kpsul(request): + data = {} + data['operationgroup_form'] = KPsulOperationGroupForm() + data['trigramme_form'] = KPsulAccountForm() + data['checkout_form'] = KPsulCheckoutForm() + operation_formset = KPsulOperationFormSet(queryset=Operation.objects.none()) + data['operation_formset'] = operation_formset + return render(request, 'kfet/kpsul.html', data) + +@teamkfet_required +def kpsul_get_settings(request): + addcost_for = Settings.ADDCOST_FOR() + data = { + 'subvention_cof': Settings.SUBVENTION_COF(), + 'addcost_for' : addcost_for and addcost_for.trigramme or '', + 'addcost_amount': Settings.ADDCOST_AMOUNT(), + } + return JsonResponse(data) + +@teamkfet_required +def account_read_json(request): + trigramme = request.POST.get('trigramme', '') + account = get_object_or_404(Account, trigramme=trigramme) + data = { 'id': account.pk, 'name': account.name, 'email': account.email, + 'is_cof': account.is_cof, 'promo': account.promo, + 'balance': account.balance, 'is_frozen': account.is_frozen, + 'departement': account.departement, 'nickname': account.nickname, + 'trigramme': account.trigramme } + return JsonResponse(data) + +@teamkfet_required +def kpsul_checkout_data(request): + pk = request.POST.get('pk', 0) + if not pk: + pk = 0 + try: + data = (Checkout.objects + .annotate( + last_statement_by_first_name=F('statements__by__cofprofile__user__first_name'), + last_statement_by_last_name=F('statements__by__cofprofile__user__last_name'), + last_statement_by_trigramme=F('statements__by__trigramme'), + last_statement_balance=F('statements__balance_new'), + last_statement_at=F('statements__at')) + .values( + 'id', 'name', 'balance', 'valid_from', 'valid_to', + 'last_statement_balance', 'last_statement_at', + 'last_statement_by_trigramme', 'last_statement_by_last_name', + 'last_statement_by_first_name') + .select_related( + 'statements' + 'statements__by', + 'statements__by__cofprofile__user') + .filter(pk=pk) + .order_by('statements__at') + .last()) + except Checkout.DoesNotExist: + raise http404 + return JsonResponse(data) + +@teamkfet_required +def kpsul_update_addcost(request): + addcost_form = AddcostForm(request.POST) + + if not addcost_form.is_valid(): + data = { 'errors': { 'addcost': list(addcost_form.errors) } } + return JsonResponse(data, status=400) + required_perms = ['kfet.manage_addcosts'] + if not request.user.has_perms(required_perms): + data = { + 'errors': { + 'missing_perms': get_missing_perms(required_perms, request.user) + } + } + return JsonResponse(data, status=403) + + trigramme = addcost_form.cleaned_data['trigramme'] + account = trigramme and Account.objects.get(trigramme=trigramme) or None + Settings.objects.filter(name='ADDCOST_FOR').update(value_account=account) + Settings.objects.filter(name='ADDCOST_AMOUNT').update(value_decimal=addcost_form.cleaned_data['amount']) + cache.delete('ADDCOST_FOR') + cache.delete('ADDCOST_AMOUNT') + data = { + 'addcost': { + 'for': trigramme and account.trigramme or None, + 'amount': addcost_form.cleaned_data['amount'], + } + } + consumers.KPsul.group_send('kfet.kpsul', data) + return JsonResponse(data) + +def get_missing_perms(required_perms, user): + missing_perms_codenames = [ (perm.split('.'))[1] + for perm in required_perms if not user.has_perm(perm)] + missing_perms = list( + Permission.objects + .filter(codename__in=missing_perms_codenames) + .values_list('name', flat=True)) + return missing_perms + +@teamkfet_required +def kpsul_perform_operations(request): + # Initializing response data + data = { 'operationgroup': 0, 'operations': [], + 'warnings': {}, 'errors': {} } + + # Checking operationgroup + operationgroup_form = KPsulOperationGroupForm(request.POST) + if not operationgroup_form.is_valid(): + data['errors']['operation_group'] = list(operationgroup_form.errors) + + # Checking operation_formset + operation_formset = KPsulOperationFormSet(request.POST) + if not operation_formset.is_valid(): + data['errors']['operations'] = list(operation_formset.errors) + + # Returning BAD REQUEST if errors + if data['errors']: + return JsonResponse(data, status=400) + + # Pre-saving (no commit) + operationgroup = operationgroup_form.save(commit = False) + operations = operation_formset.save(commit = False) + + # Retrieving COF grant + cof_grant = Settings.SUBVENTION_COF() + # Retrieving addcosts data + addcost_amount = Settings.ADDCOST_AMOUNT() + addcost_for = Settings.ADDCOST_FOR() + + # Initializing vars + required_perms = set() # Required perms to perform all operations + cof_grant_divisor = 1 + cof_grant / 100 + to_addcost_for_balance = 0 # For balance of addcost_for + to_checkout_balance = 0 # For balance of selected checkout + to_articles_stocks = defaultdict(lambda:0) # For stocks articles + is_addcost = (addcost_for and addcost_amount + and addcost_for != operationgroup.on_acc) + need_comment = operationgroup.on_acc.need_comment + + # Filling data of each operations + operationgroup + calculating other stuffs + for operation in operations: + if operation.type == Operation.PURCHASE: + operation.amount = - operation.article.price * operation.article_nb + if is_addcost: + operation.addcost_for = addcost_for + operation.addcost_amount = addcost_amount * operation.article_nb + operation.amount -= operation.addcost_amount + to_addcost_for_balance += operation.addcost_amount + if operationgroup.on_acc.is_cash: + operation.is_checkout = True + to_checkout_balance += -operation.amount + else: + operation.is_checkout = False + if operationgroup.on_acc.is_cof: + if is_addcost: + operation.addcost_amount = operation.addcost_amount / cof_grant_divisor + operation.amount = operation.amount / cof_grant_divisor + to_articles_stocks[operation.article] -= operation.article_nb + else: + if operationgroup.on_acc.is_cash: + data['errors']['account'] = 'Charge et retrait impossible sur LIQ' + to_checkout_balance += operation.amount + operationgroup.amount += operation.amount + if operation.type == Operation.DEPOSIT: + required_perms.add('kfet.perform_deposit') + if (not operation.is_checkout + and operation.type in [Operation.DEPOSIT, Operation.WITHDRAW]): + required_perms.add('kfet.edit_balance_account') + need_comment = True + if operationgroup.on_acc.is_cof: + to_addcost_for_balance = to_addcost_for_balance / cof_grant_divisor + + (perms, stop) = operationgroup.on_acc.perms_to_perform_operation( + amount = operationgroup.amount) + required_perms |= perms + + if need_comment: + operationgroup.comment = operationgroup.comment.strip() + if not operationgroup.comment: + data['errors']['need_comment'] = True + return JsonResponse(data, status=400) + + if stop or not request.user.has_perms(required_perms): + missing_perms = get_missing_perms(required_perms, request.user) + if missing_perms: + data['errors']['missing_perms'] = missing_perms + if stop: + data['errors']['negative'] = [operationgroup.on_acc.trigramme] + return JsonResponse(data, status=403) + + # If 1 perm is required, filling who perform the operations + if required_perms: + operationgroup.valid_by = request.user.profile.account_kfet + # Filling cof status for statistics + operationgroup.is_cof = operationgroup.on_acc.is_cof + + # Starting transaction to ensure data consistency + with transaction.atomic(): + # If not cash account, + # saving account's balance and adding to Negative if not in + if not operationgroup.on_acc.is_cash: + Account.objects.filter(pk=operationgroup.on_acc.pk).update( + balance = F('balance') + operationgroup.amount) + operationgroup.on_acc.refresh_from_db() + if operationgroup.on_acc.balance < 0: + if hasattr(operationgroup.on_acc, 'negative'): + if not operationgroup.on_acc.negative.start: + operationgroup.on_acc.negative.start = timezone.now() + operationgroup.on_acc.negative.save() + else: + negative = AccountNegative( + account = operationgroup.on_acc, start = timezone.now()) + negative.save() + elif (hasattr(operationgroup.on_acc, 'negative') + and not operationgroup.on_acc.negative.balance_offset): + operationgroup.on_acc.negative.delete() + + # Updating checkout's balance + if to_checkout_balance: + Checkout.objects.filter(pk=operationgroup.checkout.pk).update( + balance = F('balance') + to_checkout_balance) + + # Saving addcost_for with new balance if there is one + if is_addcost and to_addcost_for_balance: + Account.objects.filter(pk=addcost_for.pk).update( + balance = F('balance') + to_addcost_for_balance) + + # Saving operation group + operationgroup.save() + data['operationgroup'] = operationgroup.pk + + # Filling operationgroup id for each operations and saving + for operation in operations: + operation.group = operationgroup + operation.save() + data['operations'].append(operation.pk) + + # Updating articles stock + for article in to_articles_stocks: + Article.objects.filter(pk=article.pk).update( + stock = F('stock') + to_articles_stocks[article]) + + # Websocket data + websocket_data = {} + websocket_data['opegroups'] = [{ + 'add': True, + 'id': operationgroup.pk, + 'amount': operationgroup.amount, + 'checkout__name': operationgroup.checkout.name, + 'at': operationgroup.at, + 'is_cof': operationgroup.is_cof, + 'comment': operationgroup.comment, + 'valid_by__trigramme': ( operationgroup.valid_by and + operationgroup.valid_by.trigramme or None), + 'on_acc__trigramme': operationgroup.on_acc.trigramme, + 'opes': [], + }] + for operation in operations: + ope_data = { + 'id': operation.pk, 'type': operation.type, 'amount': operation.amount, + 'addcost_amount': operation.addcost_amount, + 'addcost_for__trigramme': is_addcost and addcost_for.trigramme or None, + 'is_checkout': operation.is_checkout, + 'article__name': operation.article and operation.article.name or None, + 'article_nb': operation.article_nb, + 'group_id': operationgroup.pk, + 'canceled_by__trigramme': None, 'canceled_at': None, + } + websocket_data['opegroups'][0]['opes'].append(ope_data) + # Need refresh from db cause we used update on queryset + operationgroup.checkout.refresh_from_db() + websocket_data['checkouts'] = [{ + 'id': operationgroup.checkout.pk, + 'balance': operationgroup.checkout.balance, + }] + websocket_data['articles'] = [] + # Need refresh from db cause we used update on querysets + articles_pk = [ article.pk for article in to_articles_stocks] + articles = Article.objects.values('id', 'stock').filter(pk__in=articles_pk) + for article in articles: + websocket_data['articles'].append({ + 'id': article['id'], + 'stock': article['stock'] + }) + consumers.KPsul.group_send('kfet.kpsul', websocket_data) + return JsonResponse(data) + +@teamkfet_required +def kpsul_cancel_operations(request): + # Pour la réponse + data = { 'canceled': [], 'warnings': {}, 'errors': {}} + + # Checking if BAD REQUEST (opes_pk not int or not existing) + try: + # Set pour virer les doublons + opes_post = set(map(int, filter(None, request.POST.getlist('operations[]', [])))) + except ValueError: + return JsonResponse(data, status=400) + opes_all = ( + Operation.objects + .select_related('group', 'group__on_acc', 'group__on_acc__negative') + .filter(pk__in=opes_post)) + opes_pk = [ ope.pk for ope in opes_all ] + opes_notexisting = [ ope for ope in opes_post if ope not in opes_pk ] + if opes_notexisting: + data['errors']['opes_notexisting'] = opes_notexisting + return JsonResponse(data, status=400) + + opes_already_canceled = [] # Déjà annulée + opes = [] # Pas déjà annulée + required_perms = set() + stop_all = False + cancel_duration = Settings.CANCEL_DURATION() + to_accounts_balances = defaultdict(lambda:0) # Modifs à faire sur les balances des comptes + to_groups_amounts = defaultdict(lambda:0) # ------ sur les montants des groupes d'opé + to_checkouts_balances = defaultdict(lambda:0) # ------ sur les balances de caisses + to_articles_stocks = defaultdict(lambda:0) # ------ sur les stocks d'articles + for ope in opes_all: + if ope.canceled_at: + # Opération déjà annulée, va pour un warning en Response + opes_already_canceled.append(ope.pk) + else: + opes.append(ope.pk) + # Si opé il y a plus de CANCEL_DURATION, permission requise + if ope.group.at + cancel_duration < timezone.now(): + required_perms.add('kfet.cancel_old_operations') + + # Calcul de toutes modifs à faire en cas de validation + + # Pour les balances de comptes + if not ope.group.on_acc.is_cash: + to_accounts_balances[ope.group.on_acc] -= ope.amount + if ope.addcost_for and ope.addcost_amount: + to_accounts_balances[ope.addcost_for] -= ope.addcost_amount + # Pour les groupes d'opés + to_groups_amounts[ope.group] -= ope.amount + + # Pour les balances de caisses + # Les balances de caisses dont il y a eu un relevé depuis la date + # de la commande ne doivent pas être modifiées + # TODO : Prendre en compte le dernier relevé où la caisse a été + # comptée et donc modifier les balance_old (et amount_error) + # des relevés suivants. + # Note : Dans le cas où un CheckoutStatement est mis à jour + # par `.save()`, amount_error est recalculé automatiquement, + # ce qui n'est pas le cas en faisant un update sur queryset + # TODO ? : Maj les balance_old de relevés pour modifier l'erreur + last_statement = (CheckoutStatement.objects + .filter(checkout=ope.group.checkout) + .order_by('at') + .last()) + if not last_statement or last_statement.at < ope.group.at: + if ope.type == Operation.PURCHASE: + if ope.group.on_acc.is_cash: + to_checkouts_balances[ope.group.checkout] -= - ope.amount + else: + to_checkouts_balances[ope.group.checkout] -= ope.amount + + # Pour les stocks d'articles + # Les stocks d'articles dont il y a eu un inventaire depuis la date + # de la commande ne doivent pas être modifiés + # TODO : Prendre en compte le dernier inventaire où le stock a bien + # été compté (pas dans le cas d'une livraison). + # Note : si InventoryArticle est maj par .save(), stock_error + # est recalculé automatiquement + if ope.article and ope.article_nb: + last_stock = (InventoryArticle.objects + .select_related('inventory') + .filter(article=ope.article) + .order_by('inventory__at') + .last()) + if not last_stock or last_stock.inventory.at < ope.group.at: + to_articles_stocks[ope.article] += ope.article_nb + + if not opes: + data['warnings']['already_canceled'] = opes_already_canceled + return JsonResponse(data) + + negative_accounts = [] + # Checking permissions or stop + for account in to_accounts_balances: + (perms, stop) = account.perms_to_perform_operation( + amount = to_accounts_balances[account]) + required_perms |= perms + stop_all = stop_all or stop + if stop: + negative_accounts.append(account.trigramme) + + if stop_all or not request.user.has_perms(required_perms): + missing_perms = get_missing_perms(required_perms, request.user) + if missing_perms: + data['errors']['missing_perms'] = missing_perms + if stop_all: + data['errors']['negative'] = negative_accounts + return JsonResponse(data, status=403) + + canceled_by = required_perms and request.user.profile.account_kfet or None + canceled_at = timezone.now() + + with transaction.atomic(): + (Operation.objects.filter(pk__in=opes) + .update(canceled_by=canceled_by, canceled_at=canceled_at)) + for account in to_accounts_balances: + Account.objects.filter(pk=account.pk).update( + balance = F('balance') + to_accounts_balances[account]) + for checkout in to_checkouts_balances: + Checkout.objects.filter(pk=checkout.pk).update( + balance = F('balance') + to_checkouts_balances[checkout]) + for group in to_groups_amounts: + OperationGroup.objects.filter(pk=group.pk).update( + amount = F('amount') + to_groups_amounts[group]) + for article in to_articles_stocks: + Article.objects.filter(pk=article.pk).update( + stock = F('stock') + to_articles_stocks[article]) + + # Websocket data + websocket_data = { 'opegroups': [], 'opes': [], 'checkouts': [], 'articles': [] } + # Need refresh from db cause we used update on querysets + opegroups_pk = [ opegroup.pk for opegroup in to_groups_amounts ] + opegroups = (OperationGroup.objects + .values('id','amount','is_cof').filter(pk__in=opegroups_pk)) + for opegroup in opegroups: + websocket_data['opegroups'].append({ + 'cancellation': True, + 'id': opegroup['id'], + 'amount': opegroup['amount'], + 'is_cof': opegroup['is_cof'], + }) + canceled_by__trigramme = canceled_by and canceled_by.trigramme or None + for ope in opes: + websocket_data['opes'].append({ + 'cancellation': True, + 'id': ope, + 'canceled_by__trigramme': canceled_by__trigramme, + 'canceled_at': canceled_at, + }) + # Need refresh from db cause we used update on querysets + checkouts_pk = [ checkout.pk for checkout in to_checkouts_balances] + checkouts = (Checkout.objects + .values('id', 'balance').filter(pk__in=checkouts_pk)) + for checkout in checkouts: + websocket_data['checkouts'].append({ + 'id': checkout['id'], + 'balance': checkout['balance']}) + # Need refresh from db cause we used update on querysets + articles_pk = [ article.pk for articles in to_articles_stocks] + articles = Article.objects.values('id', 'stock').filter(pk__in=articles_pk) + for article in articles: + websocket_data['articles'].append({ + 'id': article['id'], + 'stock': article['stock']}) + consumers.KPsul.group_send('kfet.kpsul', websocket_data) + + data['canceled'] = opes + if opes_already_canceled: + data['warnings']['already_canceled'] = opes_already_canceled + return JsonResponse(data) + +@login_required +def history_json(request): + # Récupération des paramètres + from_date = request.POST.get('from', None) + to_date = request.POST.get('to', None) + limit = request.POST.get('limit', None); + checkouts = request.POST.getlist('checkouts[]', None) + accounts = request.POST.getlist('accounts[]', None) + + # Construction de la requête (sur les opérations) pour le prefetch + queryset_prefetch = Operation.objects.select_related( + 'canceled_by__trigramme', 'addcost_for__trigramme', + 'article__name') + + # Construction de la requête principale + opegroups = (OperationGroup.objects + .prefetch_related(Prefetch('opes', queryset = queryset_prefetch)) + .select_related('on_acc__trigramme', 'valid_by__trigramme') + .order_by('at') + ) + # Application des filtres + if from_date: + opegroups = opegroups.filter(at__gte=from_date) + if to_date: + opegroups = opegroups.filter(at__lt=to_date) + if checkouts: + opegroups = opegroups.filter(checkout_id__in=checkouts) + if accounts: + opegroups = opegroups.filter(on_acc_id__in=accounts) + # Un non-membre de l'équipe n'a que accès à son historique + if not request.user.has_perm('kfet.is_team'): + opegroups = opegroups.filter(on_acc=request.user.profile.account_kfet) + if limit: + opegroups = opegroups[:limit] + + + # Construction de la réponse + opegroups_list = [] + for opegroup in opegroups: + opegroup_dict = { + 'id' : opegroup.id, + 'amount' : opegroup.amount, + 'at' : opegroup.at, + 'checkout_id': opegroup.checkout_id, + 'is_cof' : opegroup.is_cof, + 'comment' : opegroup.comment, + 'opes' : [], + 'on_acc__trigramme': + opegroup.on_acc and opegroup.on_acc.trigramme or None, + } + if request.user.has_perm('kfet.is_team'): + opegroup_dict['valid_by__trigramme'] = ( + opegroup.valid_by and opegroup.valid_by.trigramme or None) + for ope in opegroup.opes.all(): + ope_dict = { + 'id' : ope.id, + 'type' : ope.type, + 'amount' : ope.amount, + 'article_nb' : ope.article_nb, + 'is_checkout' : ope.is_checkout, + 'addcost_amount': ope.addcost_amount, + 'canceled_at' : ope.canceled_at, + 'article__name': + ope.article and ope.article.name or None, + 'addcost_for__trigramme': + ope.addcost_for and ope.addcost_for.trigramme or None, + } + if request.user.has_perm('kfet.is_team'): + ope_dict['canceled_by__trigramme'] = ( + ope.canceled_by and ope.canceled_by.trigramme or None) + opegroup_dict['opes'].append(ope_dict) + opegroups_list.append(opegroup_dict) + return JsonResponse({ 'opegroups': opegroups_list }) + +@teamkfet_required +def kpsul_articles_data(request): + articles = ( + Article.objects + .values('id', 'name', 'price', 'stock', 'category_id', 'category__name') + .filter(is_sold=True)) + return JsonResponse({ 'articles': list(articles) }) + +@teamkfet_required +def history(request): + data = { + 'filter_form': FilterHistoryForm(), + 'settings': { + 'subvention_cof': Settings.SUBVENTION_COF(), + } + } + return render(request, 'kfet/history.html', data) + +# ----- +# Settings views +# ----- + +class SettingsList(ListView): + model = Settings + context_object_name = 'settings' + template_name = 'kfet/settings.html' + + def get_context_data(self, **kwargs): + Settings.create_missing() + return super(SettingsList, self).get_context_data(**kwargs) + +class SettingsUpdate(SuccessMessageMixin, UpdateView): + model = Settings + form_class = SettingsForm + template_name = 'kfet/settings_update.html' + success_message = 'Paramètre %(name)s mis à jour' + success_url = reverse_lazy('kfet.settings') + + def form_valid(self, form): + # Checking permission + if not self.request.user.has_perm('kfet.change_settings'): + form.add_error(None, 'Permission refusée') + return self.form_invalid(form) + # Creating + Settings.empty_cache() + return super(SettingsUpdate, self).form_valid(form) + +# ----- +# Transfer views +# ----- + +@teamkfet_required +def transfers(request): + transfergroups = (TransferGroup.objects + .prefetch_related('transfers') + .order_by('-at')) + return render(request, 'kfet/transfers.html', { + 'transfergroups': transfergroups, + }) + +@teamkfet_required +def transfers_create(request): + transfer_formset = TransferFormSet(queryset=Transfer.objects.none()) + return render(request, 'kfet/transfers_create.html', + { 'transfer_formset': transfer_formset }) + +@teamkfet_required +def perform_transfers(request): + data = { 'errors': {}, 'transfers': [], 'transfergroup': 0 } + + # Checking transfer_formset + transfer_formset = TransferFormSet(request.POST) + if not transfer_formset.is_valid(): + return JsonResponse({ 'errors': list(transfer_formset.errors)}, status=400) + + transfers = transfer_formset.save(commit = False) + + # Initializing vars + required_perms = set('kfet.add_transfer') # Required perms to perform all transfers + to_accounts_balances = defaultdict(lambda:0) # For balances of accounts + + for transfer in transfers: + to_accounts_balances[transfer.from_acc] -= transfer.amount + to_accounts_balances[transfer.to_acc] += transfer.amount + + stop_all = False + + negative_accounts = [] + # Checking if ok on all accounts + for account in to_accounts_balances: + (perms, stop) = account.perms_to_perform_operation( + amount = to_accounts_balances[account]) + required_perms |= perms + stop_all = stop_all or stop + if stop: + negative_accounts.append(account.trigramme) + + if stop_all or not request.user.has_perms(required_perms): + missing_perms = get_missing_perms(required_perms, request.user) + if missing_perms: + data['errors']['missing_perms'] = missing_perms + if stop_all: + data['errors']['negative'] = negative_accounts + return JsonResponse(data, status=403) + + # Creating transfer group + transfergroup = TransferGroup() + if required_perms: + transfergroup.valid_by = request.user.profile.account_kfet + + comment = request.POST.get('comment', '') + transfergroup.comment = comment.strip() + + with transaction.atomic(): + # Updating balances accounts + for account in to_accounts_balances: + Account.objects.filter(pk=account.pk).update( + balance = F('balance') + to_accounts_balances[account]) + account.refresh_from_db() + if account.balance < 0: + if hasattr(account, 'negative'): + if not account.negative.start: + account.negative.start = timezone.now() + account.negative.save() + else: + negative = AccountNegative( + account = account, start = timezone.now()) + negative.save() + elif (hasattr(account, 'negative') + and not account.negative.balance_offset): + account.negative.delete() + + # Saving transfer group + transfergroup.save() + data['transfergroup'] = transfergroup.pk + + # Saving all transfers with group + for transfer in transfers: + transfer.group = transfergroup + transfer.save() + data['transfers'].append(transfer.pk) + + return JsonResponse(data) + +class InventoryList(ListView): + queryset = (Inventory.objects + .select_related('by', 'order') + .annotate(nb_articles=Count('articles')) + .order_by('-at')) + template_name = 'kfet/inventory.html' + context_object_name = 'inventories' + +@teamkfet_required +def inventory_create(request): + + articles = (Article.objects + .select_related('category') + .order_by('category__name', 'name') + ) + initial = [] + for article in articles: + initial.append({ + 'article' : article.pk, + 'stock_old': article.stock, + 'name' : article.name, + 'category' : article.category_id, + 'category__name': article.category.name + }) + + cls_formset = formset_factory( + form = InventoryArticleForm, + extra = 0, + ) + + if request.POST: + formset = cls_formset(request.POST, initial=initial) + + if not request.user.has_perm('kfet.add_inventory'): + messages.error(request, 'Permission refusée') + elif formset.is_valid(): + with transaction.atomic(): + + articles = Article.objects.select_for_update() + inventory = Inventory() + inventory.by = request.user.profile.account_kfet + saved = False + for form in formset: + if form.cleaned_data['stock_new'] is not None: + if not saved: + inventory.save() + saved = True + + article = articles.get(pk=form.cleaned_data['article'].pk) + stock_old = article.stock + stock_new = form.cleaned_data['stock_new'] + InventoryArticle.objects.create( + inventory = inventory, + article = article, + stock_old = stock_old, + stock_new = stock_new) + article.stock = stock_new + article.save() + if saved: + messages.success(request, 'Inventaire créé') + return redirect('kfet.inventory') + messages.warning(request, 'Bah alors ? On a rien compté ?') + else: + messages.error(request, 'Pas marché') + else: + formset = cls_formset(initial = initial) + + return render(request, 'kfet/inventory_create.html', { + 'formset': formset, + }) + +class InventoryRead(DetailView): + model = Inventory + template_name = 'kfet/inventory_read.html' + context_object_name = 'inventory' + + def get_context_data(self, **kwargs): + context = super(InventoryRead, self).get_context_data(**kwargs) + inventoryarticles = (InventoryArticle.objects + .select_related('article', 'article__category') + .filter(inventory = self.object) + .order_by('article__category__name', 'article__name')) + context['inventoryarts'] = inventoryarticles + return context + +# ----- +# Order views +# ----- + +class OrderList(ListView): + queryset = Order.objects.select_related('supplier', 'inventory') + template_name = 'kfet/order.html' + context_object_name = 'orders' + + def get_context_data(self, **kwargs): + context = super(OrderList, self).get_context_data(**kwargs) + context['suppliers'] = Supplier.objects.order_by('name') + return context + +@teamkfet_required +def order_create(request, pk): + supplier = get_object_or_404(Supplier, pk=pk) + + articles = (Article.objects + .filter(suppliers=supplier.pk) + .select_related('category') + .order_by('category__name', 'name')) + + initial = [] + today = timezone.now().date() + sales_q = (Operation.objects + .select_related('group') + .filter(article__in=articles, canceled_at=None) + .values('article')) + sales_s1 = (sales_q + .filter( + group__at__gte = today-timedelta(weeks=5), + group__at__lt = today-timedelta(weeks=4)) + .annotate(nb=Sum('article_nb')) + ) + sales_s1 = { d['article']:d['nb'] for d in sales_s1 } + sales_s2 = (sales_q + .filter( + group__at__gte = today-timedelta(weeks=4), + group__at__lt = today-timedelta(weeks=3)) + .annotate(nb=Sum('article_nb')) + ) + sales_s2 = { d['article']:d['nb'] for d in sales_s2 } + sales_s3 = (sales_q + .filter( + group__at__gte = today-timedelta(weeks=3), + group__at__lt = today-timedelta(weeks=2)) + .annotate(nb=Sum('article_nb')) + ) + sales_s3 = { d['article']:d['nb'] for d in sales_s3 } + sales_s4 = (sales_q + .filter( + group__at__gte = today-timedelta(weeks=2), + group__at__lt = today-timedelta(weeks=1)) + .annotate(nb=Sum('article_nb')) + ) + sales_s4 = { d['article']:d['nb'] for d in sales_s4 } + sales_s5 = (sales_q + .filter(group__at__gte = today-timedelta(weeks=1)) + .annotate(nb=Sum('article_nb')) + ) + sales_s5 = { d['article']:d['nb'] for d in sales_s5 } + + for article in articles: + v_s1 = sales_s1.get(article.pk, 0) + v_s2 = sales_s2.get(article.pk, 0) + v_s3 = sales_s3.get(article.pk, 0) + v_s4 = sales_s4.get(article.pk, 0) + v_s5 = sales_s5.get(article.pk, 0) + v_all = [v_s1, v_s2, v_s3, v_s4, v_s5] + v_3max = heapq.nlargest(3, v_all) + v_moy = statistics.mean(v_3max) + v_et = statistics.pstdev(v_3max, v_moy) + v_prev = v_moy + v_et + c_rec_tot = max(v_prev * 1.5 - article.stock, 0) + if article.box_capacity: + c_rec_temp = c_rec_tot / article.box_capacity + if c_rec_temp >= 10: + c_rec = round(c_rec_temp) + elif c_rec_temp > 5: + c_rec = 10 + elif c_rec_temp > 2: + c_rec = 5 + else: + c_rec = round(c_rec_temp) + initial.append({ + 'article': article.pk, + 'name': article.name, + 'category': article.category_id, + 'category__name': article.category.name, + 'stock': article.stock, + 'box_capacity': article.box_capacity, + 'v_s1': v_s1, + 'v_s2': v_s2, + 'v_s3': v_s3, + 'v_s4': v_s4, + 'v_s5': v_s5, + 'v_moy': round(v_moy), + 'v_et': round(v_et), + 'v_prev': round(v_prev), + 'c_rec': article.box_capacity and c_rec or round(c_rec_tot), + }) + + cls_formset = formset_factory( + form = OrderArticleForm, + extra = 0) + + if request.POST: + formset = cls_formset(request.POST, initial=initial) + + if not request.user.has_perm('kfet.add_order'): + messages.error(request, 'Permission refusée') + elif formset.is_valid(): + order = Order() + order.supplier = supplier + saved = False + for form in formset: + if form.cleaned_data['quantity_ordered'] is not None: + if not saved: + order.save() + saved = True + + article = articles.get(pk=form.cleaned_data['article'].pk) + q_ordered = form.cleaned_data['quantity_ordered'] + if article.box_capacity: + q_ordered *= article.box_capacity + OrderArticle.objects.create( + order = order, + article = article, + quantity_ordered = q_ordered) + if saved: + messages.success(request, 'Commande créée') + return redirect('kfet.order.read', order.pk) + messages.warning(request, 'Rien commandé => Pas de commande') + else: + messages.error(request, 'Corrigez les erreurs') + else: + formset = cls_formset(initial=initial) + + return render(request, 'kfet/order_create.html', { + 'supplier': supplier, + 'formset' : formset, + }) + +class OrderRead(DetailView): + model = Order + template_name = 'kfet/order_read.html' + context_object_name = 'order' + + def get_context_data(self, **kwargs): + context = super(OrderRead, self).get_context_data(**kwargs) + orderarticles = (OrderArticle.objects + .select_related('article', 'article__category') + .filter(order=self.object) + .order_by('article__category__name', 'article__name') + ) + context['orderarts'] = orderarticles + mail = ("Bonjour,\n\nNous voudrions pour le ##DATE## à la K-Fêt de " + "l'ENS Ulm :") + category = 0 + for orderarticle in orderarticles: + if category != orderarticle.article.category: + category = orderarticle.article.category + mail += '\n' + nb = orderarticle.quantity_ordered + box = '' + if orderarticle.article.box_capacity: + nb /= orderarticle.article.box_capacity + if nb >= 2: + box = ' %ss de' % orderarticle.article.box_type + else: + box = ' %s de' % orderarticle.article.box_type + name = orderarticle.article.name.capitalize() + mail += "\n- %s%s %s" % (round(nb), box, name) + + mail += ("\n\nMerci d'appeler le numéro suivant lorsque les livreurs " + "sont là : ##TELEPHONE##\nCordialement,\n##PRENOM## ##NOM## " + ", pour la K-Fêt de l'ENS Ulm") + + context['mail'] = mail + return context + +@teamkfet_required +def order_to_inventory(request, pk): + order = get_object_or_404(Order, pk=pk) + + if hasattr(order, 'inventory'): + raise Http404 + + articles = (Article.objects + .filter(orders=order.pk) + .select_related('category') + .prefetch_related(Prefetch('orderarticle_set', + queryset = OrderArticle.objects.filter(order=order), + to_attr = 'order')) + .prefetch_related(Prefetch('supplierarticle_set', + queryset = (SupplierArticle.objects + .filter(supplier=order.supplier) + .order_by('-at')), + to_attr = 'supplier')) + .order_by('category__name', 'name')) + + initial = [] + for article in articles: + initial.append({ + 'article': article.pk, + 'name': article.name, + 'category': article.category_id, + 'category__name': article.category.name, + 'quantity_ordered': article.order[0].quantity_ordered, + 'quantity_received': article.order[0].quantity_ordered, + 'price_HT': article.supplier[0].price_HT, + 'TVA': article.supplier[0].TVA, + 'rights': article.supplier[0].rights, + }) + + cls_formset = formset_factory(OrderArticleToInventoryForm, extra=0) + + if request.method == 'POST': + formset = cls_formset(request.POST, initial=initial) + + if not request.user.has_perm('kfet.order_to_inventory'): + messages.error(request, 'Permission refusée') + elif formset.is_valid(): + with transaction.atomic(): + inventory = Inventory() + inventory.order = order + inventory.by = request.user.profile.account_kfet + inventory.save() + for form in formset: + q_received = form.cleaned_data['quantity_received'] + article = form.cleaned_data['article'] + SupplierArticle.objects.create( + supplier = order.supplier, + article = article, + price_HT = form.cleaned_data['price_HT'], + TVA = form.cleaned_data['TVA'], + rights = form.cleaned_data['rights']) + (OrderArticle.objects + .filter(order=order, article=article) + .update(quantity_received = q_received)) + InventoryArticle.objects.create( + inventory = inventory, + article = article, + stock_old = article.stock, + stock_new = article.stock + q_received) + article.stock += q_received + if q_received > 0: + article.is_sold = True + article.save() + messages.success(request, "C'est tout bon !") + return redirect('kfet.order') + else: + messages.error(request, "Corrigez les erreurs") + else: + formset = cls_formset(initial=initial) + + return render(request, 'kfet/order_to_inventory.html', { + 'formset': formset, + }) + +class SupplierUpdate(SuccessMessageMixin, UpdateView): + model = Supplier + template_name = 'kfet/supplier_form.html' + fields = ['name', 'address', 'email', 'phone', 'comment'] + success_url = reverse_lazy('kfet.order') + sucess_message = 'Données fournisseur mis à jour' + + # Surcharge de la validation + def form_valid(self, form): + # Checking permission + if not self.request.user.has_perm('kfet.change_supplier'): + form.add_error(None, 'Permission refusée') + return self.form_invalid(form) + # Updating + return super(SupplierUpdate, self).form_valid(form) diff --git a/provisioning/bootstrap.sh b/provisioning/bootstrap.sh index 99b3db66..304d8cd0 100644 --- a/provisioning/bootstrap.sh +++ b/provisioning/bootstrap.sh @@ -9,7 +9,7 @@ DBPASSWD="4KZt3nGPLVeWSvtBZPSM3fSzXpzEU4" # Installation de paquets utiles apt-get update && apt-get install -y mercurial python-pip python-dev \ - libmysqlclient-dev libjpeg-dev + libmysqlclient-dev libjpeg-dev git # Configuration et installation de mysql. Le mot de passe root est le même que # le mot de passe pour l'utilisateur local - pour rappel, ceci est une instance @@ -21,6 +21,9 @@ apt-get install -y mysql-server mysql -uroot -p$DBPASSWD -e "CREATE DATABASE $DBNAME; GRANT ALL PRIVILEGES ON $DBNAME.* TO '$DBUSER'@'localhost' IDENTIFIED BY '$DBPASSWD'" +# Installation de redis-server. Todo: lui mettre un mot de passe +apt-get install -y redis-server + # Mise en place du .bash_profile pour tout configurer lors du `vagrant ssh` cat > ~vagrant/.bash_profile <