master cf31d957b9cf cached
155 files
861.0 KB
252.0k tokens
279 symbols
1 requests
Download .txt
Showing preview only (916K chars total). Download the full file or copy to clipboard to get everything.
Repository: mitchtabian/CodingWithMitchBlog-REST-API
Branch: master
Commit: cf31d957b9cf
Files: 155
Total size: 861.0 KB

Directory structure:
gitextract_9q6h212s/

├── README.md
└── src/
    ├── account/
    │   ├── __init__.py
    │   ├── admin.py
    │   ├── api/
    │   │   ├── __init__.py
    │   │   ├── serializers.py
    │   │   ├── urls.py
    │   │   └── views.py
    │   ├── apps.py
    │   ├── backends.py
    │   ├── forms.py
    │   ├── migrations/
    │   │   ├── 0001_initial.py
    │   │   └── __init__.py
    │   ├── models.py
    │   ├── templates/
    │   │   └── account/
    │   │       ├── account.html
    │   │       ├── login.html
    │   │       ├── must_authenticate.html
    │   │       └── register.html
    │   ├── tests.py
    │   └── views.py
    ├── blog/
    │   ├── __init__.py
    │   ├── admin.py
    │   ├── api/
    │   │   ├── __init__.py
    │   │   ├── serializers.py
    │   │   ├── urls.py
    │   │   └── views.py
    │   ├── apps.py
    │   ├── forms.py
    │   ├── migrations/
    │   │   ├── 0001_initial.py
    │   │   └── __init__.py
    │   ├── models.py
    │   ├── templates/
    │   │   └── blog/
    │   │       ├── create_blog.html
    │   │       ├── detail_blog.html
    │   │       ├── edit_blog.html
    │   │       └── snippets/
    │   │           ├── blog_post_pagination.html
    │   │           └── blog_post_snippet.html
    │   ├── tests.py
    │   ├── urls.py
    │   ├── utils.py
    │   └── views.py
    ├── db.sqlite3
    ├── manage.py
    ├── mysite/
    │   ├── __init__.py
    │   ├── settings.py
    │   ├── urls.py
    │   └── wsgi.py
    ├── personal/
    │   ├── __init__.py
    │   ├── admin.py
    │   ├── apps.py
    │   ├── migrations/
    │   │   ├── 0001_initial.py
    │   │   ├── 0002_delete_question.py
    │   │   └── __init__.py
    │   ├── models.py
    │   ├── templates/
    │   │   └── personal/
    │   │       └── home.html
    │   ├── tests.py
    │   └── views.py
    ├── requirements.txt
    ├── static_cdn/
    │   └── admin/
    │       ├── css/
    │       │   ├── autocomplete.css
    │       │   ├── base.css
    │       │   ├── changelists.css
    │       │   ├── dashboard.css
    │       │   ├── fonts.css
    │       │   ├── forms.css
    │       │   ├── login.css
    │       │   ├── responsive.css
    │       │   ├── responsive_rtl.css
    │       │   ├── rtl.css
    │       │   ├── vendor/
    │       │   │   └── select2/
    │       │   │       ├── LICENSE-SELECT2.md
    │       │   │       └── select2.css
    │       │   └── widgets.css
    │       ├── fonts/
    │       │   ├── LICENSE.txt
    │       │   └── README.txt
    │       ├── img/
    │       │   ├── LICENSE
    │       │   └── README.txt
    │       └── js/
    │           ├── SelectBox.js
    │           ├── SelectFilter2.js
    │           ├── actions.js
    │           ├── admin/
    │           │   ├── DateTimeShortcuts.js
    │           │   └── RelatedObjectLookups.js
    │           ├── autocomplete.js
    │           ├── calendar.js
    │           ├── cancel.js
    │           ├── change_form.js
    │           ├── collapse.js
    │           ├── core.js
    │           ├── inlines.js
    │           ├── jquery.init.js
    │           ├── popup_response.js
    │           ├── prepopulate.js
    │           ├── prepopulate_init.js
    │           ├── timeparse.js
    │           ├── urlify.js
    │           └── vendor/
    │               ├── jquery/
    │               │   ├── LICENSE.txt
    │               │   └── jquery.js
    │               ├── select2/
    │               │   ├── LICENSE.md
    │               │   ├── i18n/
    │               │   │   ├── ar.js
    │               │   │   ├── az.js
    │               │   │   ├── bg.js
    │               │   │   ├── ca.js
    │               │   │   ├── cs.js
    │               │   │   ├── da.js
    │               │   │   ├── de.js
    │               │   │   ├── el.js
    │               │   │   ├── en.js
    │               │   │   ├── es.js
    │               │   │   ├── et.js
    │               │   │   ├── eu.js
    │               │   │   ├── fa.js
    │               │   │   ├── fi.js
    │               │   │   ├── fr.js
    │               │   │   ├── gl.js
    │               │   │   ├── he.js
    │               │   │   ├── hi.js
    │               │   │   ├── hr.js
    │               │   │   ├── hu.js
    │               │   │   ├── id.js
    │               │   │   ├── is.js
    │               │   │   ├── it.js
    │               │   │   ├── ja.js
    │               │   │   ├── km.js
    │               │   │   ├── ko.js
    │               │   │   ├── lt.js
    │               │   │   ├── lv.js
    │               │   │   ├── mk.js
    │               │   │   ├── ms.js
    │               │   │   ├── nb.js
    │               │   │   ├── nl.js
    │               │   │   ├── pl.js
    │               │   │   ├── pt-BR.js
    │               │   │   ├── pt.js
    │               │   │   ├── ro.js
    │               │   │   ├── ru.js
    │               │   │   ├── sk.js
    │               │   │   ├── sr-Cyrl.js
    │               │   │   ├── sr.js
    │               │   │   ├── sv.js
    │               │   │   ├── th.js
    │               │   │   ├── tr.js
    │               │   │   ├── uk.js
    │               │   │   ├── vi.js
    │               │   │   ├── zh-CN.js
    │               │   │   └── zh-TW.js
    │               │   └── select2.full.js
    │               └── xregexp/
    │                   ├── LICENSE.txt
    │                   └── xregexp.js
    └── templates/
        ├── base.html
        ├── registration/
        │   ├── password_change.html
        │   ├── password_change_done.html
        │   ├── password_reset_complete.html
        │   ├── password_reset_done.html
        │   ├── password_reset_email.html
        │   ├── password_reset_form.html
        │   └── password_reset_subject.txt
        └── snippets/
            ├── base_css.html
            ├── footer.html
            └── header.html

================================================
FILE CONTENTS
================================================

================================================
FILE: README.md
================================================
<a href="https://codingwithmitch.com/courses/build-a-rest-api/"><img class='header-img' src='https://codingwithmitch.s3.amazonaws.com/static/build-a-rest-api-with-the-django-rest-framework/images/Django_REST-framework_Setup.png' /></a>
<a href="https://codingwithmitch.com/courses/build-a-rest-api/"><h1>Build a REST API</h1></a>

<p>In this course you'll learn to build a REST API for a website so other technologies can interact with it.</p>
<p>This course is a continuation of the <a href="https://codingwithmitch.com/courses/building-a-website-django-python/" target="_blank">"Building a website with Django (Python)"</a> course. Which is a free course where I show you how to build a website like  <a href="https://open-api.xyz" target="_blank" rel="nofollow">open-api.xyz</a>.</p>
<p><strong>What you'll learn:</strong></p>
<ul>
<li>How to build and customize a Rest API</li>
<li>User authentication via Django "TokenAuthentication"</li>
<li>Generating Auth Tokens from a mobile app</li>
<li>
CRUD functionality on a live website:<br>
<ol>
<li>Create blog posts</li>
<li>Retrieve blog posts</li>
<li>Update blog posts</li>
<li>Delete blog posts</li>
</ol>
</li>
<li>Pagination (very important for mobile apps)</li>
<li>Serialization of data</li>
<li>JSON</li>
</ul>



================================================
FILE: src/account/__init__.py
================================================


================================================
FILE: src/account/admin.py
================================================
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from account.models import Account


class AccountAdmin(UserAdmin):
	list_display = ('pk', 'email','username','date_joined', 'last_login', 'is_admin','is_staff')
	search_fields = ('pk', 'email','username',)
	readonly_fields=('pk', 'date_joined', 'last_login')

	filter_horizontal = ()
	list_filter = ()
	fieldsets = ()


admin.site.register(Account, AccountAdmin)





================================================
FILE: src/account/api/__init__.py
================================================


================================================
FILE: src/account/api/serializers.py
================================================
from rest_framework import serializers

from account.models import Account


class RegistrationSerializer(serializers.ModelSerializer):

	password2 				= serializers.CharField(style={'input_type': 'password'}, write_only=True)

	class Meta:
		model = Account
		fields = ['email', 'username', 'password', 'password2']
		extra_kwargs = {
				'password': {'write_only': True},
		}	


	def	save(self):

		account = Account(
					email=self.validated_data['email'],
					username=self.validated_data['username']
				)
		password = self.validated_data['password']
		password2 = self.validated_data['password2']
		if password != password2:
			raise serializers.ValidationError({'password': 'Passwords must match.'})
		account.set_password(password)
		account.save()
		return account


class AccountPropertiesSerializer(serializers.ModelSerializer):

	class Meta:
		model = Account
		fields = ['pk', 'email', 'username', ]




class ChangePasswordSerializer(serializers.Serializer):

	old_password 				= serializers.CharField(required=True)
	new_password 				= serializers.CharField(required=True)
	confirm_new_password 		= serializers.CharField(required=True)







================================================
FILE: src/account/api/urls.py
================================================
from django.urls import path
from account.api.views import(
	registration_view,
	ObtainAuthTokenView,
	account_properties_view,
	update_account_view,
	does_account_exist_view,
	ChangePasswordView,
)
from rest_framework.authtoken.views import obtain_auth_token

app_name = 'account'

urlpatterns = [
	path('check_if_account_exists/', does_account_exist_view, name="check_if_account_exists"),
	path('change_password/', ChangePasswordView.as_view(), name="change_password"),
	path('properties', account_properties_view, name="properties"),
	path('properties/update', update_account_view, name="update"),
 	path('login', ObtainAuthTokenView.as_view(), name="login"), 
	path('register', registration_view, name="register"),

]

================================================
FILE: src/account/api/views.py
================================================
from rest_framework import status
from rest_framework.response import Response
from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import IsAuthenticated
from rest_framework.views import APIView
from rest_framework.generics import UpdateAPIView
from django.contrib.auth import authenticate
from rest_framework.authentication import TokenAuthentication
from rest_framework.decorators import api_view, authentication_classes, permission_classes

from account.api.serializers import RegistrationSerializer, AccountPropertiesSerializer, ChangePasswordSerializer
from account.models import Account
from rest_framework.authtoken.models import Token

# Register
# Response: https://gist.github.com/mitchtabian/c13c41fa0f51b304d7638b7bac7cb694
# Url: https://<your-domain>/api/account/register
@api_view(['POST', ])
@permission_classes([])
@authentication_classes([])
def registration_view(request):

	if request.method == 'POST':
		data = {}
		email = request.data.get('email', '0').lower()
		if validate_email(email) != None:
			data['error_message'] = 'That email is already in use.'
			data['response'] = 'Error'
			return Response(data)

		username = request.data.get('username', '0')
		if validate_username(username) != None:
			data['error_message'] = 'That username is already in use.'
			data['response'] = 'Error'
			return Response(data)

		serializer = RegistrationSerializer(data=request.data)
		
		if serializer.is_valid():
			account = serializer.save()
			data['response'] = 'successfully registered new user.'
			data['email'] = account.email
			data['username'] = account.username
			data['pk'] = account.pk
			token = Token.objects.get(user=account).key
			data['token'] = token
		else:
			data = serializer.errors
		return Response(data)

def validate_email(email):
	account = None
	try:
		account = Account.objects.get(email=email)
	except Account.DoesNotExist:
		return None
	if account != None:
		return email

def validate_username(username):
	account = None
	try:
		account = Account.objects.get(username=username)
	except Account.DoesNotExist:
		return None
	if account != None:
		return username


# Account properties
# Response: https://gist.github.com/mitchtabian/4adaaaabc767df73c5001a44b4828ca5
# Url: https://<your-domain>/api/account/
# Headers: Authorization: Token <token>
@api_view(['GET', ])
@permission_classes((IsAuthenticated, ))
def account_properties_view(request):

	try:
		account = request.user
	except Account.DoesNotExist:
		return Response(status=status.HTTP_404_NOT_FOUND)

	if request.method == 'GET':
		serializer = AccountPropertiesSerializer(account)
		return Response(serializer.data)


# Account update properties
# Response: https://gist.github.com/mitchtabian/72bb4c4811199b1d303eb2d71ec932b2
# Url: https://<your-domain>/api/account/properties/update
# Headers: Authorization: Token <token>
@api_view(['PUT',])
@permission_classes((IsAuthenticated, ))
def update_account_view(request):

	try:
		account = request.user
	except Account.DoesNotExist:
		return Response(status=status.HTTP_404_NOT_FOUND)
		
	if request.method == 'PUT':
		serializer = AccountPropertiesSerializer(account, data=request.data)
		data = {}
		if serializer.is_valid():
			serializer.save()
			data['response'] = 'Account update success'
			return Response(data=data)
		return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)



# LOGIN
# Response: https://gist.github.com/mitchtabian/8e1bde81b3be342853ddfcc45ec0df8a
# URL: http://127.0.0.1:8000/api/account/login
class ObtainAuthTokenView(APIView):

	authentication_classes = []
	permission_classes = []

	def post(self, request):
		context = {}

		email = request.POST.get('username')
		password = request.POST.get('password')
		account = authenticate(email=email, password=password)
		if account:
			try:
				token = Token.objects.get(user=account)
			except Token.DoesNotExist:
				token = Token.objects.create(user=account)
			context['response'] = 'Successfully authenticated.'
			context['pk'] = account.pk
			context['email'] = email.lower()
			context['token'] = token.key
		else:
			context['response'] = 'Error'
			context['error_message'] = 'Invalid credentials'

		return Response(context)




@api_view(['GET', ])
@permission_classes([])
@authentication_classes([])
def does_account_exist_view(request):

	if request.method == 'GET':
		email = request.GET['email'].lower()
		data = {}
		try:
			account = Account.objects.get(email=email)
			data['response'] = email
		except Account.DoesNotExist:
			data['response'] = "Account does not exist"
		return Response(data)



class ChangePasswordView(UpdateAPIView):

	serializer_class = ChangePasswordSerializer
	model = Account
	permission_classes = (IsAuthenticated,)
	authentication_classes = (TokenAuthentication,)

	def get_object(self, queryset=None):
		obj = self.request.user
		return obj

	def update(self, request, *args, **kwargs):
		self.object = self.get_object()
		serializer = self.get_serializer(data=request.data)

		if serializer.is_valid():
			# Check old password
			if not self.object.check_password(serializer.data.get("old_password")):
				return Response({"old_password": ["Wrong password."]}, status=status.HTTP_400_BAD_REQUEST)

			# confirm the new passwords match
			new_password = serializer.data.get("new_password")
			confirm_new_password = serializer.data.get("confirm_new_password")
			if new_password != confirm_new_password:
				return Response({"new_password": ["New passwords must match"]}, status=status.HTTP_400_BAD_REQUEST)

			# set_password also hashes the password that the user will get
			self.object.set_password(serializer.data.get("new_password"))
			self.object.save()
			return Response({"response":"successfully changed password"}, status=status.HTTP_200_OK)

		return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)























================================================
FILE: src/account/apps.py
================================================
from django.apps import AppConfig


class AccountConfig(AppConfig):
    name = 'account'


================================================
FILE: src/account/backends.py
================================================
from django.contrib.auth import get_user_model
from django.contrib.auth.backends import ModelBackend


class CaseInsensitiveModelBackend(ModelBackend):
    def authenticate(self, request, username=None, password=None, **kwargs):
        UserModel = get_user_model()
        if username is None:
            username = kwargs.get(UserModel.USERNAME_FIELD)
        try:
            case_insensitive_username_field = '{}__iexact'.format(UserModel.USERNAME_FIELD)
            user = UserModel._default_manager.get(**{case_insensitive_username_field: username})
        except UserModel.DoesNotExist:
            # Run the default password hasher once to reduce the timing
            # difference between an existing and a non-existing user (#20760).
            UserModel().set_password(password)
        else:
            if user.check_password(password) and self.user_can_authenticate(user):
                return user




================================================
FILE: src/account/forms.py
================================================
from django import forms
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth import authenticate

from account.models import Account


class RegistrationForm(UserCreationForm):
	email = forms.EmailField(max_length=254, help_text='Required. Add a valid email address.')

	class Meta:
		model = Account
		fields = ('email', 'username', 'password1', 'password2', )

	def clean_email(self):
		email = self.cleaned_data['email'].lower()
		try:
			account = Account.objects.exclude(pk=self.instance.pk).get(email=email)
		except Account.DoesNotExist:
			return email
		raise forms.ValidationError('Email "%s" is already in use.' % account)

	def clean_username(self):
		username = self.cleaned_data['username']
		try:
			account = Account.objects.exclude(pk=self.instance.pk).get(username=username)
		except Account.DoesNotExist:
			return username
		raise forms.ValidationError('Username "%s" is already in use.' % username)


class AccountAuthenticationForm(forms.ModelForm):

	password = forms.CharField(label='Password', widget=forms.PasswordInput)

	class Meta:
		model = Account
		fields = ('email', 'password')

	def clean(self):
		if self.is_valid():
			email = self.cleaned_data['email']
			password = self.cleaned_data['password']
			if not authenticate(email=email, password=password):
				raise forms.ValidationError("Invalid login")


class AccountUpdateForm(forms.ModelForm):

	class Meta:
		model = Account
		fields = ('email', 'username', )

	def clean_email(self):
		email = self.cleaned_data['email'].lower()
		try:
			account = Account.objects.exclude(pk=self.instance.pk).get(email=email)
		except Account.DoesNotExist:
			return email
		raise forms.ValidationError('Email "%s" is already in use.' % account)

	def clean_username(self):
		username = self.cleaned_data['username']
		try:
			account = Account.objects.exclude(pk=self.instance.pk).get(username=username)
		except Account.DoesNotExist:
			return username
		raise forms.ValidationError('Username "%s" is already in use.' % username)


















================================================
FILE: src/account/migrations/0001_initial.py
================================================
# Generated by Django 2.2.2 on 2019-06-24 20:53

from django.db import migrations, models


class Migration(migrations.Migration):

    initial = True

    dependencies = [
    ]

    operations = [
        migrations.CreateModel(
            name='Account',
            fields=[
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('password', models.CharField(max_length=128, verbose_name='password')),
                ('email', models.EmailField(max_length=60, unique=True, verbose_name='email')),
                ('username', models.CharField(max_length=30, unique=True)),
                ('date_joined', models.DateTimeField(auto_now_add=True, verbose_name='date joined')),
                ('last_login', models.DateTimeField(auto_now=True, verbose_name='last login')),
                ('is_admin', models.BooleanField(default=False)),
                ('is_active', models.BooleanField(default=True)),
                ('is_staff', models.BooleanField(default=False)),
                ('is_superuser', models.BooleanField(default=False)),
            ],
            options={
                'abstract': False,
            },
        ),
    ]


================================================
FILE: src/account/migrations/__init__.py
================================================


================================================
FILE: src/account/models.py
================================================
from django.db import models
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager

from django.conf import settings
from django.db.models.signals import post_save
from django.dispatch import receiver
from rest_framework.authtoken.models import Token


class MyAccountManager(BaseUserManager):
	def create_user(self, email, username, password=None):
		if not email:
			raise ValueError('Users must have an email address')
		if not username:
			raise ValueError('Users must have a username')

		user = self.model(
			email=self.normalize_email(email),
			username=username,
		)

		user.set_password(password)
		user.save(using=self._db)
		return user

	def create_superuser(self, email, username, password):
		user = self.create_user(
			email=self.normalize_email(email),
			password=password,
			username=username,
		)
		user.is_admin = True
		user.is_staff = True
		user.is_superuser = True
		user.save(using=self._db)
		return user


class Account(AbstractBaseUser):
	email 					= models.EmailField(verbose_name="email", max_length=60, unique=True)
	username 				= models.CharField(max_length=30, unique=True)
	date_joined				= models.DateTimeField(verbose_name='date joined', auto_now_add=True)
	last_login				= models.DateTimeField(verbose_name='last login', auto_now=True)
	is_admin				= models.BooleanField(default=False)
	is_active				= models.BooleanField(default=True)
	is_staff				= models.BooleanField(default=False)
	is_superuser			= models.BooleanField(default=False)


	USERNAME_FIELD = 'email'
	REQUIRED_FIELDS = ['username']

	objects = MyAccountManager()

	def __str__(self):
		return self.email

	# For checking permissions. to keep it simple all admin have ALL permissons
	def has_perm(self, perm, obj=None):
		return self.is_admin

	# Does this user have permission to view this app? (ALWAYS YES FOR SIMPLICITY)
	def has_module_perms(self, app_label):
		return True


@receiver(post_save, sender=settings.AUTH_USER_MODEL)
def create_auth_token(sender, instance=None, created=False, **kwargs):
    if created:
        Token.objects.create(user=instance)














================================================
FILE: src/account/templates/account/account.html
================================================
{% extends 'base.html' %}


{% block content %}

<style type="text/css">
  .form-signin {
  width: 100%;
  max-width: 330px;
  padding: 15px;
  margin: auto;
  }

  .form-signin .form-control {
    position: relative;
    box-sizing: border-box;
    height: auto;
    padding: 10px;
    font-size: 16px;
  }
  .form-signin .form-control:focus {
    z-index: 2;
  }
  .form-signin input[type="email"] {
    margin-bottom: 10px;
    border-bottom-right-radius: 0;
    border-bottom-left-radius: 0;
  }
  .form-signin input[type="username"] {
    margin-bottom: 10px;
    border-top-left-radius: 0;
    border-top-right-radius: 0;
  }
  .h3{
    text-align: center;
  }
  .blog-posts{
    max-width: 500px;
    width:100%;
    margin: auto;
  }
</style>


<form class="form-signin" method="post">{% csrf_token %}

  <h1 class="h3 mb-3 font-weight-normal">Account</h1>

  <input type="email" name="email" id="inputEmail" class="form-control" placeholder="Email address" required autofocus value={{account_form.initial.email}}>
  
  <input type="text" name="username" id="inputUsername" class="form-control" placeholder="Username" required
  value="{{account_form.initial.username}}">

  {% for field in account_form %}
      <p>
        {% for error in field.errors %}
          <p style="color: red">{{ error }}</p>
        {% endfor %}
      </p>
  {% endfor %}
  {% if account_form.non_field_errors %}
    <div style="color: red">
      <p>{{account_form.non_field_errors}}</p>
    </div>
      
  {% endif %}

  {% if success_message %}
    <p style="color:green; text-align: center;">{{success_message}}</p>
  {% endif  %}

  <button class="btn btn-lg btn-primary btn-block" type="submit">Save changes</button>

</form>

<div class="d-flex flex-column">
  <a class="m-auto" href="{% url 'password_change' %}">Change password</a>
</div>


<div class="container mt-4 p-4">
  <div class="row">
    <div class="blog-posts">
      <h3 class="mb-3 font-weight-normal">Blog posts:</h3>
      {% if blog_posts %}
      <ul>
        {% for post in blog_posts %}
          <a href="{% url 'blog:detail' post.slug %}" style="text-decoration: none;">
            <li class="list-group-item">{{post}}</li>
          </a>
        {% endfor %}
      </ul>
      {% else %}
        <p>You have no blog posts. Create a post <a href="{% url 'blog:create' %}">here</a>.</p>
      {% endif %}

    </div>
  </div>
</div>


{% endblock content %}




================================================
FILE: src/account/templates/account/login.html
================================================
{% extends 'base.html' %}
{% load static %}

{% block content %}

<style type="text/css">
  .form-signin {
  width: 100%;
  max-width: 330px;
  padding: 15px;
  margin: auto;
  }

  .form-signin .form-control {
    position: relative;
    box-sizing: border-box;
    height: auto;
    padding: 10px;
    font-size: 16px;
  }
  .form-signin .form-control:focus {
    z-index: 2;
  }
  .form-signin input[type="email"] {
    margin-bottom: -1px;
    border-bottom-right-radius: 0;
    border-bottom-left-radius: 0;
  }
  .form-signin input[type="password"] {
    margin-bottom: 10px;
    border-top-left-radius: 0;
    border-top-right-radius: 0;
  }
  .h3{
    text-align: center;
  }
</style>


<form class="form-signin" method="post">{% csrf_token %}
<div class="d-flex flex-column pb-3">
  <img class="img-fluid mx-auto d-block" src="{% static 'codingwithmitch_logo.png' %}" alt="codingwithmitch logo" width="72" height="72">
</div>
  <h1 class="h3 mb-3 font-weight-normal">Login</h1>

  <input type="email" name="email" id="inputEmail" class="form-control" placeholder="Email address" required autofocus>
  
  <input type="password" name="password" id="inputPassword" class="form-control" placeholder="Password" required>

  {% for field in login_form %}
      <p>
        {% for error in field.errors %}
          <p style="color: red">{{ error }}</p>
        {% endfor %}
      </p>
  {% endfor %}
  {% if login_form.non_field_errors %}
    <div style="color: red">
      <p>{{login_form.non_field_errors}}</p>
    </div>
      
  {% endif %}

  <button class="btn btn-lg btn-primary btn-block" type="submit">Log in</button>

</form>

<div class="d-flex flex-column">
  <a class="m-auto" href="{% url 'password_reset' %}">Reset password</a>
</div>



{% endblock content %}




================================================
FILE: src/account/templates/account/must_authenticate.html
================================================
{% extends 'base.html' %}


{% block content %}

<div class="container">
	<div class="d-flex flex-column">
		<p class="m-auto">Must authenticate to view this page.</p>
		<p class="m-auto">
			<a href="{% url 'register' %}">Register</a> or
			<a href="{% url 'login' %}">Login</a>
		</p>
	</div>
</div>


{% endblock content %}

================================================
FILE: src/account/templates/account/register.html
================================================
{% extends 'base.html' %}
{% load static %}

{% block content %}


<style type="text/css">
  .form-signin {
  width: 100%;
  max-width: 330px;
  padding: 15px;
  margin: auto;
  }

  .form-signin .form-control {
    position: relative;
    box-sizing: border-box;
    height: auto;
    padding: 10px;
    font-size: 16px;
  }
  .form-signin .form-control:focus {
    z-index: 2;
  }
  .form-signin input[type="email"] {
    margin-bottom: 10px;
    border-bottom-right-radius: 0;
    border-bottom-left-radius: 0;
  }
  .form-signin input[type="text"] {
    margin-bottom: 10px;
    border-bottom-right-radius: 0;
    border-bottom-left-radius: 0;
  }
  .form-signin input[type="password"] {
    margin-bottom: -1px;
    border-top-left-radius: 0;
    border-top-right-radius: 0;
  }

  .h3{
    text-align: center;
  }

</style>


<form class="form-signin" method="post">{% csrf_token %}
<div class="d-flex flex-column pb-3">
  <img class="img-fluid mx-auto d-block" src="{% static 'codingwithmitch_logo.png' %}" alt="codingwithmitch logo" width="72" height="72">
</div>
  <h1 class="h3 mb-3 font-weight-normal">Register an account</h1>

  <input type="email" name="email" id="inputEmail" class="form-control" placeholder="Email address" required autofocus>

  <input type="text" name="username" id="inputUsername" class="form-control" placeholder="Username" required>
  
  <input type="password" name="password1" id="inputPassword1" class="form-control" placeholder="Password" required>

  <input type="password" name="password2" id="inputPassword2" class="form-control" placeholder="Confirm password" required>

  {% for field in registration_form %}
      <p>
        {% for error in field.errors %}
          <p style="color: red">{{ error }}</p>
        {% endfor %}
      </p>
  {% endfor %}
  {% if registration_form.non_field_errors %}
    <div style="color: red">
      <p>{{registration_form.non_field_errors}}</p>
    </div>
      
  {% endif %}

  <button class="btn btn-lg btn-primary btn-block" type="submit">Register</button>

</form>


{% endblock content %}





================================================
FILE: src/account/tests.py
================================================
from django.test import TestCase

# Create your tests here.


================================================
FILE: src/account/views.py
================================================
from django.shortcuts import render, redirect
from django.contrib.auth import login, authenticate, logout
from account.forms import RegistrationForm, AccountAuthenticationForm, AccountUpdateForm

from blog.models import BlogPost

def registration_view(request):
	context = {}
	if request.POST:
		form = RegistrationForm(request.POST)
		if form.is_valid():
			form.save()
			email = form.cleaned_data.get('email').lower()
			raw_password = form.cleaned_data.get('password1')
			account = authenticate(email=email, password=raw_password)
			login(request, account)
			return redirect('home')
		else:
			context['registration_form'] = form

	else:
		form = RegistrationForm()
		context['registration_form'] = form
	return render(request, 'account/register.html', context)


def logout_view(request):
	logout(request)
	return redirect('/')


def login_view(request):

	context = {}

	user = request.user
	if user.is_authenticated: 
		return redirect("home")

	if request.POST:
		form = AccountAuthenticationForm(request.POST)
		if form.is_valid():
			email = request.POST['email']
			password = request.POST['password']
			user = authenticate(email=email, password=password)

			if user:
				login(request, user)
				return redirect("home")

	else:
		form = AccountAuthenticationForm()

	context['login_form'] = form

	# print(form)
	return render(request, "account/login.html", context)


def account_view(request):

	if not request.user.is_authenticated:
			return redirect("login")

	context = {}
	if request.POST:
		form = AccountUpdateForm(request.POST, instance=request.user)
		if form.is_valid():
			form.initial = {
					"email": request.POST['email'],
					"username": request.POST['username'],
			}
			form.save()
			context['success_message'] = "Updated"
	else:
		form = AccountUpdateForm(

			initial={
					"email": request.user.email, 
					"username": request.user.username,
				}
			)

	context['account_form'] = form

	blog_posts = BlogPost.objects.filter(author=request.user)
	context['blog_posts'] = blog_posts

	return render(request, "account/account.html", context)


def must_authenticate_view(request):
	return render(request, 'account/must_authenticate.html', {})




================================================
FILE: src/blog/__init__.py
================================================


================================================
FILE: src/blog/admin.py
================================================
from django.contrib import admin

from blog.models import BlogPost

admin.site.register(BlogPost)


================================================
FILE: src/blog/api/__init__.py
================================================


================================================
FILE: src/blog/api/serializers.py
================================================
from rest_framework import serializers
from blog.models import BlogPost

import os
from django.conf import settings
from django.core.files.storage import default_storage
from django.core.files.storage import FileSystemStorage
IMAGE_SIZE_MAX_BYTES = 1024 * 1024 * 2 # 2MB
MIN_TITLE_LENGTH = 5
MIN_BODY_LENGTH = 50

from blog.utils import is_image_aspect_ratio_valid, is_image_size_valid


class BlogPostSerializer(serializers.ModelSerializer):

	username = serializers.SerializerMethodField('get_username_from_author')
	image 	 = serializers.SerializerMethodField('validate_image_url')

	class Meta:
		model = BlogPost
		fields = ['pk', 'title', 'slug', 'body', 'image', 'date_updated', 'username']


	def get_username_from_author(self, blog_post):
		username = blog_post.author.username
		return username

	def validate_image_url(self, blog_post):
		image = blog_post.image
		new_url = image.url
		if "?" in new_url:
			new_url = image.url[:image.url.rfind("?")]
		return new_url




class BlogPostUpdateSerializer(serializers.ModelSerializer):

	class Meta:
		model = BlogPost
		fields = ['title', 'body', 'image']

	def validate(self, blog_post):
		try:
			title = blog_post['title']
			if len(title) < MIN_TITLE_LENGTH:
				raise serializers.ValidationError({"response": "Enter a title longer than " + str(MIN_TITLE_LENGTH) + " characters."})
			
			body = blog_post['body']
			if len(body) < MIN_BODY_LENGTH:
				raise serializers.ValidationError({"response": "Enter a body longer than " + str(MIN_BODY_LENGTH) + " characters."})
			
			image = blog_post['image']
			url = os.path.join(settings.TEMP , str(image))
			storage = FileSystemStorage(location=url)

			with storage.open('', 'wb+') as destination:
				for chunk in image.chunks():
					destination.write(chunk)
				destination.close()

			# Check image size
			if not is_image_size_valid(url, IMAGE_SIZE_MAX_BYTES):
				os.remove(url)
				raise serializers.ValidationError({"response": "That image is too large. Images must be less than 2 MB. Try a different image."})

			# Check image aspect ratio
			if not is_image_aspect_ratio_valid(url):
				os.remove(url)
				raise serializers.ValidationError({"response": "Image height must not exceed image width. Try a different image."})

			os.remove(url)
		except KeyError:
			pass
		return blog_post


class BlogPostCreateSerializer(serializers.ModelSerializer):


	class Meta:
		model = BlogPost
		fields = ['title', 'body', 'image', 'date_updated', 'author']


	def save(self):
		
		try:
			image = self.validated_data['image']
			title = self.validated_data['title']
			if len(title) < MIN_TITLE_LENGTH:
				raise serializers.ValidationError({"response": "Enter a title longer than " + str(MIN_TITLE_LENGTH) + " characters."})
			
			body = self.validated_data['body']
			if len(body) < MIN_BODY_LENGTH:
				raise serializers.ValidationError({"response": "Enter a body longer than " + str(MIN_BODY_LENGTH) + " characters."})
			
			blog_post = BlogPost(
								author=self.validated_data['author'],
								title=title,
								body=body,
								image=image,
								)

			url = os.path.join(settings.TEMP , str(image))
			storage = FileSystemStorage(location=url)

			with storage.open('', 'wb+') as destination:
				for chunk in image.chunks():
					destination.write(chunk)
				destination.close()

			# Check image size
			if not is_image_size_valid(url, IMAGE_SIZE_MAX_BYTES):
				os.remove(url)
				raise serializers.ValidationError({"response": "That image is too large. Images must be less than 2 MB. Try a different image."})

			# Check image aspect ratio
			if not is_image_aspect_ratio_valid(url):
				os.remove(url)
				raise serializers.ValidationError({"response": "Image height must not exceed image width. Try a different image."})

			os.remove(url)
			blog_post.save()
			return blog_post
		except KeyError:
			raise serializers.ValidationError({"response": "You must have a title, some content, and an image."})











================================================
FILE: src/blog/api/urls.py
================================================
from django.urls import path
from blog.api.views import(
	api_detail_blog_view,
	api_update_blog_view,
	api_delete_blog_view,
	api_create_blog_view,
	api_is_author_of_blogpost,
	ApiBlogListView
)

app_name = 'blog'

urlpatterns = [
	path('<slug>/', api_detail_blog_view, name="detail"),
	path('<slug>/update', api_update_blog_view, name="update"),
	path('<slug>/delete', api_delete_blog_view, name="delete"),
	path('create', api_create_blog_view, name="create"),
	path('list', ApiBlogListView.as_view(), name="list"),
	path('<slug>/is_author', api_is_author_of_blogpost, name="is_author"),
]

================================================
FILE: src/blog/api/views.py
================================================
from rest_framework import status
from rest_framework.response import Response
from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import IsAuthenticated
from rest_framework.authentication import TokenAuthentication
from rest_framework.pagination import PageNumberPagination
from rest_framework.generics import ListAPIView
from rest_framework.filters import SearchFilter, OrderingFilter

from account.models import Account
from blog.models import BlogPost
from blog.api.serializers import BlogPostSerializer, BlogPostUpdateSerializer, BlogPostCreateSerializer

SUCCESS = 'success'
ERROR = 'error'
DELETE_SUCCESS = 'deleted'
UPDATE_SUCCESS = 'updated'
CREATE_SUCCESS = 'created'

# Response: https://gist.github.com/mitchtabian/93f287bd1370e7a1ad3c9588b0b22e3d
# Url: https://<your-domain>/api/blog/<slug>/
# Headers: Authorization: Token <token>
@api_view(['GET', ])
@permission_classes((IsAuthenticated, ))
def api_detail_blog_view(request, slug):

	try:
		blog_post = BlogPost.objects.get(slug=slug)
	except BlogPost.DoesNotExist:
		return Response(status=status.HTTP_404_NOT_FOUND)

	if request.method == 'GET':
		serializer = BlogPostSerializer(blog_post)
		return Response(serializer.data)


# Response: https://gist.github.com/mitchtabian/32507e93c530aa5949bc08d795ba66df
# Url: https://<your-domain>/api/blog/<slug>/update
# Headers: Authorization: Token <token>
@api_view(['PUT',])
@permission_classes((IsAuthenticated,))
def api_update_blog_view(request, slug):

	try:
		blog_post = BlogPost.objects.get(slug=slug)
	except BlogPost.DoesNotExist:
		return Response(status=status.HTTP_404_NOT_FOUND)

	user = request.user
	if blog_post.author != user:
		return Response({'response':"You don't have permission to edit that."}) 
		
	if request.method == 'PUT':
		serializer = BlogPostUpdateSerializer(blog_post, data=request.data, partial=True)
		data = {}
		if serializer.is_valid():
			serializer.save()
			data['response'] = UPDATE_SUCCESS
			data['pk'] = blog_post.pk
			data['title'] = blog_post.title
			data['body'] = blog_post.body
			data['slug'] = blog_post.slug
			data['date_updated'] = blog_post.date_updated
			image_url = str(request.build_absolute_uri(blog_post.image.url))
			if "?" in image_url:
				image_url = image_url[:image_url.rfind("?")]
			data['image'] = image_url
			data['username'] = blog_post.author.username
			return Response(data=data)
		return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)



@api_view(['GET',])
@permission_classes((IsAuthenticated,))
def api_is_author_of_blogpost(request, slug):
	try:
		blog_post = BlogPost.objects.get(slug=slug)
	except BlogPost.DoesNotExist:
		return Response(status=status.HTTP_404_NOT_FOUND)

	data = {}
	user = request.user
	if blog_post.author != user:
		data['response'] = "You don't have permission to edit that."
		return Response(data=data)
	data['response'] = "You have permission to edit that."
	return Response(data=data)


# Response: https://gist.github.com/mitchtabian/a97be3f8b71c75d588e23b414898ae5c
# Url: https://<your-domain>/api/blog/<slug>/delete
# Headers: Authorization: Token <token>
@api_view(['DELETE',])
@permission_classes((IsAuthenticated, ))
def api_delete_blog_view(request, slug):

	try:
		blog_post = BlogPost.objects.get(slug=slug)
	except BlogPost.DoesNotExist:
		return Response(status=status.HTTP_404_NOT_FOUND)

	user = request.user
	if blog_post.author != user:
		return Response({'response':"You don't have permission to delete that."}) 

	if request.method == 'DELETE':
		operation = blog_post.delete()
		data = {}
		if operation:
			data['response'] = DELETE_SUCCESS
		return Response(data=data)


# Response: https://gist.github.com/mitchtabian/78d7dcbeab4135c055ff6422238a31f9
# Url: https://<your-domain>/api/blog/create
# Headers: Authorization: Token <token>
@api_view(['POST'])
@permission_classes((IsAuthenticated,))
def api_create_blog_view(request):

	if request.method == 'POST':

		data = request.data
		data['author'] = request.user.pk
		serializer = BlogPostCreateSerializer(data=data)

		data = {}
		if serializer.is_valid():
			blog_post = serializer.save()
			data['response'] = CREATE_SUCCESS
			data['pk'] = blog_post.pk
			data['title'] = blog_post.title
			data['body'] = blog_post.body
			data['slug'] = blog_post.slug
			data['date_updated'] = blog_post.date_updated
			image_url = str(request.build_absolute_uri(blog_post.image.url))
			if "?" in image_url:
				image_url = image_url[:image_url.rfind("?")]
			data['image'] = image_url
			data['username'] = blog_post.author.username
			return Response(data=data)
		return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)


# Response: https://gist.github.com/mitchtabian/ae03573737067c9269701ea662460205
# Url: 
#		1) list: https://<your-domain>/api/blog/list
#		2) pagination: http://<your-domain>/api/blog/list?page=2
#		3) search: http://<your-domain>/api/blog/list?search=mitch
#		4) ordering: http://<your-domain>/api/blog/list?ordering=-date_updated
#		4) search + pagination + ordering: <your-domain>/api/blog/list?search=mitch&page=2&ordering=-date_updated
# Headers: Authorization: Token <token>
class ApiBlogListView(ListAPIView):
	queryset = BlogPost.objects.all()
	serializer_class = BlogPostSerializer
	authentication_classes = (TokenAuthentication,)
	permission_classes = (IsAuthenticated,)
	pagination_class = PageNumberPagination
	filter_backends = (SearchFilter, OrderingFilter)
	search_fields = ('title', 'body', 'author__username')

================================================
FILE: src/blog/apps.py
================================================
from django.apps import AppConfig


class BlogConfig(AppConfig):
    name = 'blog'


================================================
FILE: src/blog/forms.py
================================================
from django import forms

from blog.models import BlogPost


class CreateBlogPostForm(forms.ModelForm):

	class Meta:
		model = BlogPost
		fields = ['title', 'body', 'image']



class UpdateBlogPostForm(forms.ModelForm):

	class Meta:
		model = BlogPost
		fields = ['title', 'body', 'image']

	def save(self, commit=True):
		blog_post = self.instance
		blog_post.title = self.cleaned_data['title']
		blog_post.body = self.cleaned_data['body']

		if self.cleaned_data['image']:
			blog_post.image = self.cleaned_data['image']

		if commit:
			blog_post.save()
		return blog_post

================================================
FILE: src/blog/migrations/0001_initial.py
================================================
# Generated by Django 2.2.2 on 2019-06-24 20:55

import blog.models
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

    initial = True

    dependencies = [
        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
    ]

    operations = [
        migrations.CreateModel(
            name='BlogPost',
            fields=[
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('title', models.CharField(max_length=50)),
                ('body', models.TextField(max_length=5000)),
                ('image', models.ImageField(upload_to=blog.models.upload_location)),
                ('date_published', models.DateTimeField(auto_now_add=True, verbose_name='date published')),
                ('date_updated', models.DateTimeField(auto_now=True, verbose_name='date updated')),
                ('slug', models.SlugField(blank=True, unique=True)),
                ('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
            ],
        ),
    ]


================================================
FILE: src/blog/migrations/__init__.py
================================================


================================================
FILE: src/blog/models.py
================================================
from django.db import models

from django.utils.text import slugify
from django.conf import settings
from django.db.models.signals import post_delete, pre_save
from django.dispatch import receiver


def upload_location(instance, filename, **kwargs):
	file_path = 'blog/{author_id}/{title}-{filename}'.format(
			author_id=str(instance.author.id), title=str(instance.title), filename=filename
		) 
	return file_path


class BlogPost(models.Model):
	title 				= models.CharField(max_length=50, null=False, blank=True)
	body 				= models.TextField(max_length=5000, null=False, blank=True)
	image 				= models.ImageField(upload_to=upload_location, null=False, blank=True)
	date_published 		= models.DateTimeField(auto_now_add=True, verbose_name="date published")
	date_updated 		= models.DateTimeField(auto_now=True, verbose_name="date updated")
	author 				= models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
	slug 				= models.SlugField(blank=True, unique=True)

	def __str__(self):
		return self.title

@receiver(post_delete, sender=BlogPost)
def submission_delete(sender, instance, **kwargs):
	instance.image.delete(False)

def pre_save_blog_post_receiever(sender, instance, *args, **kwargs):
	if not instance.slug:
		instance.slug = slugify(instance.author.username + "-" + instance.title)

pre_save.connect(pre_save_blog_post_receiever, sender=BlogPost)



================================================
FILE: src/blog/templates/blog/create_blog.html
================================================
{% extends 'base.html' %}


{% block content %}

<style type="text/css">
  .create-form {
	    width: 100%;
	    max-width: 100%;
	    padding: 15px;
	    margin: auto;
  	}
  	.submit-button{
  		max-width: 200px;
  	}
</style>

<div class="container">
	<div class="row">
		<div class="col-lg-7 offset-lg-1">
			<form class="create-form" method="post" enctype="multipart/form-data">{% csrf_token %}

				<!-- title -->
				<div class="form-group">
					<label for="id_title">Title</label>
					<input class="form-control" type="text" name="title" id="id_title" placeholder="Title" required autofocus>
				</div>

				<!-- Body -->
				<div class="form-group">
					<label for="id_body">Content</label>
					<textarea class="form-control" rows="10" type="text" name="body" id="id_body" placeholder="This blog is about..." required></textarea>
				</div>

				<!-- Image -->
				<div class="form-group">
					<label for="id_image">Image</label>
					<input  type="file" name="image" id="id_image" accept="image/*" required>
				</div>

				<!-- Submit btn -->
				<button class="submit-button btn btn-lg btn-primary btn-block" type="submit">POST</button>
			</form>	
		</div>
	</div>
</div>

{% endblock content %}

================================================
FILE: src/blog/templates/blog/detail_blog.html
================================================
{% extends 'base.html' %}


{% block content %}
<style type="text/css">
	
	.card {
		max-width: 700px;
	}

	.container{
		padding: 20px;
	}
</style>


<div class="container">
	<div class="row">
		
		<!-- Blog post -->
		<div class="card m-auto">
			<img class="card-img-top" src="{{blog_post.image.url}}">
			<div class="card-body my-2">
				<h2 class="card-title">{{blog_post.title}}</h2>
				<p class="card-text">{{blog_post.body|linebreaksbr}}</p>
				<p>
					{% if blog_post.author == request.user %}
						<a href="{% url 'blog:edit' blog_post.slug %}" class="btn btn-primary">Update</a>
					{% endif %}
				</p>
				
			</div>
			<div class="card-footer text-muted">
				Updated on {{blog_post.date_updated}} by {{blog_post.author}}
			</div>
		</div>
		<!-- end Blog post -->

	</div>
</div>

{% endblock content %}

================================================
FILE: src/blog/templates/blog/edit_blog.html
================================================
{% extends 'base.html' %}


{% block content %}
<style type="text/css">
   .create-form {
	    width: 100%;
	    max-width: 100%;
	    padding: 15px;
	    margin: auto;
  	}
  	.submit-button{
  		max-width: 200px;
  	}
	.image {
	  opacity: 1;
	  transition: .5s ease;
	  backface-visibility: hidden;
	}
	.middle {
	  transition: .5s ease;
	  opacity: 0;
	  position: absolute;
	  text-align: center;
	  top: 50%;
	  left: 50%;
	  transform: translate(-50%, -50%);
	
	}
	.image-group{
		position: relative;
  		text-align: center;
  		cursor:pointer;
	}
	.image-group:hover .image {
	  opacity: 0.3;
	}
	.image-group:hover .middle {
	  opacity: 1;
	}
	.text {
	  margin: auto;
	  background-color: #4CAF50;
	  color: white;
	  font-size: 15px;
	  padding: 15px;
	}
</style>

<div class="container">
	<div class="row">
		<div class="col-lg-7 offset-lg-1">

			{% if success_message %}
				<h3 style="color:green; text-align: center;">{{success_message}}</h3>
			{% endif %}

			<form class="create-form" method="post" enctype="multipart/form-data">{% csrf_token %}

				<!-- title -->
				<div class="form-group">
					<label for="id_title">Title</label>
					<input class="form-control" type="text" name="title" id="id_title" placeholder="Title" value="{{form.initial.title}}" required autofocus>
				</div>

				<!-- Body -->
				<div class="form-group">
					<label for="id_body">Content</label>
					<textarea class="form-control" rows="10" type="text" name="body" id="id_body" placeholder="This blog is about..." 
					required>{{form.initial.body}}</textarea>
				</div>

				<!-- Image -->
				<div class="form-group image-group" id="id_image_group">
					<img class="img-fluid image" src="{{form.initial.image.url}}" id="id_image_display">
					  <div class="middle">
					    <div class="text">Change image</div>
					  </div>
				</div>
				<input type="file" name="image" id="id_image_file" accept="image/*" onchange="readURL(this)">
				

				{% for field in form %}
					<p>
						{% for error in field.errors %}
							<p style="color: red">{{ error }}</p>
						{% endfor %}
					</p>
				{% endfor %}
				{% if form.non_field_errors %}
					<div style="color: red">
						<p>{{form.non_field_errors}}</p>
					</div>
				{% endif %}

				<!-- Submit btn -->
				<button class="submit-button btn btn-lg btn-primary btn-block" type="submit">Update</button>
			</form>	
		</div>
	</div>
</div>

<script type="text/javascript">
	document.getElementById('id_image_group').onclick = function(event){
    	document.getElementById('id_image_file').click();
    };
	function readURL(input){
		var reader = new FileReader();
		reader.onload = function (e) {
            $('#id_image_display')
                .attr('src', e.target.result)
        };
		reader.readAsDataURL(input.files[0]);
	}
</script>

{% endblock content %}

================================================
FILE: src/blog/templates/blog/snippets/blog_post_pagination.html
================================================
<!-- FILTERED Pagination -->
<div class="d-flex flex-column m-auto pagination mt-5">
<nav aria-label="Page navigation">
<ul class="pagination pg-grey justify-content-center">
	{% if blog_posts.has_previous %}
        <li class="page-item">
		  <a class="page-link" href="?{% if query %}q={{query}}&{% endif %}page={{ blog_posts.previous_page_number }}">&laquo; Previous</a>
		</li>
    {% endif %}

	{% for i in blog_posts.paginator.page_range %}
		{% if blog_posts.number == i %}
			<li class="page-item active"><a class="page-link">{{ i }}</a></li>
		{% else %}
			<li class="page-item" ><a class="page-link" href="?{% if query %}q={{query}}&{% endif %}page={{ i }}">{{ i }}</a></li>
		{% endif %}
	{% endfor %}
    
    {% if blog_posts.has_next %}
    	<li class="page-item">
		  <a class="page-link" href="?{% if query %}q={{query}}&{% endif %}page={{ blog_posts.next_page_number }}">Next</a>
		</li>
		<li class="page-item">
		  <a class="page-link"  href="?{% if query %}q={{query}}&{% endif %}page={{ blog_posts.paginator.num_pages }}">Last &raquo;</a>
		</li>
    {% endif %}
</ul>
</nav>
</div>
<!-- end pagination-->

================================================
FILE: src/blog/templates/blog/snippets/blog_post_snippet.html
================================================
<style type="text/css">
	.card{
		max-width: 700px;
		width: 100%;
	}
	.card-body{
		padding: 20px;

	}
</style>

{% if blog_post %}
<div class="container">
	<div class="row">

		
		<!-- Blog Post -->
		<div class="card m-auto">
			<a href="{% url 'blog:detail' post.slug %}">
				<img class="card-img-top" src="{{blog_post.image.url}}">
			</a>
			
			<div class="card-body mt-2 mb-2">
				<a href="{% url 'blog:detail' post.slug %}">
					<h2 class="card-title">{{blog_post.title}}</h2>
				</a>
				<p class="card-text">{{blog_post.body|linebreaksbr|truncatechars:250}}</p>
				{% if blog_post.author == request.user %}
					<a href="{% url 'blog:edit' blog_post.slug %}" class="btn btn-primary">Update</a>
				{% endif %}
			</div>
			<div class="card-footer text-muted">
			  Updated on {{blog_post.date_updated}} by {{blog_post.author}}
			</div>
		</div>
		
	</div>
</div>

{% else %}

<div class="container"> 
	<div class="row">
		<div class="card m-auto">
			<div class="card-body mt-2 mb-2">
				<h2 class="card-title">
					No results
				</h2>
				<p class="card-text">There were no results matching your search: "{{query}}".</p>
			</div>
		</div>
	</div>
</div>
{% endif %}

================================================
FILE: src/blog/tests.py
================================================
from django.test import TestCase

# Create your tests here.


================================================
FILE: src/blog/urls.py
================================================
from django.urls import path
from blog.views import(

	create_blog_view,
	detail_blog_view,
	edit_blog_view,

)

app_name = 'blog'

urlpatterns = [
	path('create/', create_blog_view, name="create"),
	path('<slug>/', detail_blog_view, name="detail"),
	path('<slug>/edit', edit_blog_view, name="edit"),
]

================================================
FILE: src/blog/utils.py
================================================
import cv2
import os

def is_image_aspect_ratio_valid(img_url):
	img = cv2.imread(img_url)
	dimensions = tuple(img.shape[1::-1]) # gives: (width, height)
	# print("dimensions: " + str(dimensions))
	aspect_ratio = dimensions[0] / dimensions[1] # divide w / h
	# print("aspect_ratio: " + str(aspect_ratio))
	if aspect_ratio < 1:
		return False
	return True


def is_image_size_valid(img_url, mb_limit):
	image_size = os.path.getsize(img_url)
	# print("image size: " + str(image_size))
	if image_size > mb_limit:
		return False
	return True

================================================
FILE: src/blog/views.py
================================================
from django.shortcuts import render, redirect, get_object_or_404
from django.db.models import Q
from django.http import HttpResponse

from blog.models import BlogPost
from blog.forms import CreateBlogPostForm, UpdateBlogPostForm

from account.models import Account


def create_blog_view(request):

	context = {}

	user = request.user
	if not user.is_authenticated:
		return redirect('must_authenticate')

	form = CreateBlogPostForm(request.POST or None, request.FILES or None)
	if form.is_valid():
		obj = form.save(commit=False)
		author = Account.objects.filter(email=user.email).first()
		obj.author = author
		obj.save()
		form = CreateBlogPostForm()

	context['form'] = form

	return render(request, "blog/create_blog.html", context)


def detail_blog_view(request, slug):

	context = {}

	blog_post = get_object_or_404(BlogPost, slug=slug)
	context['blog_post'] = blog_post

	return render(request, 'blog/detail_blog.html', context)



def edit_blog_view(request, slug):

	context = {}

	user = request.user
	if not user.is_authenticated:
		return redirect("must_authenticate")

	blog_post = get_object_or_404(BlogPost, slug=slug)

	if blog_post.author != user:
		return HttpResponse("You are not the author of that post.")

	if request.POST:
		form = UpdateBlogPostForm(request.POST or None, request.FILES or None, instance=blog_post)
		if form.is_valid():
			obj = form.save(commit=False)
			obj.save()
			context['success_message'] = "Updated"
			blog_post = obj

	form = UpdateBlogPostForm(
			initial = {
					"title": blog_post.title,
					"body": blog_post.body,
					"image": blog_post.image,
			}
		)

	context['form'] = form
	return render(request, 'blog/edit_blog.html', context)


def get_blog_queryset(query=None):
	queryset = []
	queries = query.split(" ") # python install 2019 = [python, install, 2019]
	for q in queries:
		posts = BlogPost.objects.filter(
				Q(title__icontains=q) | 
				Q(body__icontains=q)
			).distinct()

		for post in posts:
			queryset.append(post)

	return list(set(queryset))	


================================================
FILE: src/manage.py
================================================
#!/usr/bin/env python
"""Django's command-line utility for administrative tasks."""
import os
import sys


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


if __name__ == '__main__':
    main()


================================================
FILE: src/mysite/__init__.py
================================================


================================================
FILE: src/mysite/settings.py
================================================
"""
Django settings for mysite project.

Generated by 'django-admin startproject' using Django 2.2.2.

For more information on this file, see
https://docs.djangoproject.com/en/2.2/topics/settings/

For the full list of settings and their values, see
https://docs.djangoproject.com/en/2.2/ref/settings/
"""

import os
from decouple import config

# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))


# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/2.2/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = '!p1ai_b373g3bmz-**m@%h9+0_8xm7*41etdbi+t266-mogm08'

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True

ALLOWED_HOSTS = []

if DEBUG:
    EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend' # During development only


# Application definition

INSTALLED_APPS = [

    # My apps
    'personal',
    'account',
    'blog',


    # django apps
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'rest_framework',
    'rest_framework.authtoken',
]

AUTH_USER_MODEL = 'account.Account'
AUTHENTICATION_BACKENDS = ( 
    'django.contrib.auth.backends.AllowAllUsersModelBackend', 
    'account.backends.CaseInsensitiveModelBackend',
    )

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework.authentication.TokenAuthentication',
    ),
    'DEFAULT_PERMISSION_CLASSES': (
        'rest_framework.permissions.IsAuthenticated',
    ),
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
    'PAGE_SIZE': 10,
}

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

ROOT_URLCONF = 'mysite.urls'

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, 'templates')],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]


WSGI_APPLICATION = 'mysite.wsgi.application'


# Database
# https://docs.djangoproject.com/en/2.2/ref/settings/#databases

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}


# Password validation
# https://docs.djangoproject.com/en/2.2/ref/settings/#auth-password-validators

AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
    },
]


# Internationalization
# https://docs.djangoproject.com/en/2.2/topics/i18n/

LANGUAGE_CODE = 'en-us'

TIME_ZONE = 'UTC'

USE_I18N = True

USE_L10N = True

USE_TZ = True


# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/2.2/howto/static-files/
STATICFILES_DIRS = [
    os.path.join(BASE_DIR, 'static'),
    os.path.join(BASE_DIR, 'media'),
]
STATIC_URL = '/static/'
MEDIA_URL = '/media/'
STATIC_ROOT = os.path.join(BASE_DIR, 'static_cdn')
MEDIA_ROOT = os.path.join(BASE_DIR, 'media_cdn')
TEMP = os.path.join(BASE_DIR, 'temp')

================================================
FILE: src/mysite/urls.py
================================================
"""mysite URL Configuration

The `urlpatterns` list routes URLs to views. For more information please see:
    https://docs.djangoproject.com/en/2.2/topics/http/urls/
Examples:
Function views
    1. Add an import:  from my_app import views
    2. Add a URL to urlpatterns:  path('', views.home, name='home')
Class-based views
    1. Add an import:  from other_app.views import Home
    2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
Including another URLconf
    1. Import the include() function: from django.urls import include, path
    2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path, include
from django.contrib.auth import views as auth_views
from django.conf.urls.static import static
from django.conf import settings

from personal.views import (
	home_screen_view,
)

from account.views import (
    registration_view,
    logout_view,
    login_view,
    account_view,
    must_authenticate_view,
)

urlpatterns = [
    path('', home_screen_view, name="home"),
    path('admin/', admin.site.urls),
    path('account/', account_view, name="account"),
    path('blog/', include('blog.urls', 'blog')),
    path('login/', login_view, name="login"),
    path('logout/', logout_view, name="logout"),
    path('must_authenticate/', must_authenticate_view, name="must_authenticate"),
    path('register/', registration_view, name="register"),
	
	# REST-framework
    path('api/blog/', include('blog.api.urls', 'blog_api')),
    path('api/account/', include('account.api.urls', 'account_api')),

    # Password reset links (ref: https://github.com/django/django/blob/master/django/contrib/auth/views.py)
    path('password_change/done/', auth_views.PasswordChangeDoneView.as_view(template_name='registration/password_change_done.html'), 
        name='password_change_done'),

    path('password_change/', auth_views.PasswordChangeView.as_view(template_name='registration/password_change.html'), 
        name='password_change'),

    path('password_reset/done/', auth_views.PasswordResetCompleteView.as_view(template_name='registration/password_reset_done.html'),
     name='password_reset_done'),

    path('reset/<uidb64>/<token>/', auth_views.PasswordResetConfirmView.as_view(), name='password_reset_confirm'),
    path('password_reset/', auth_views.PasswordResetView.as_view(), name='password_reset'),
    
    path('reset/done/', auth_views.PasswordResetCompleteView.as_view(template_name='registration/password_reset_complete.html'),
     name='password_reset_complete'),
]

if settings.DEBUG:
    urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
    urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)


================================================
FILE: src/mysite/wsgi.py
================================================
"""
WSGI config for mysite project.

It exposes the WSGI callable as a module-level variable named ``application``.

For more information on this file, see
https://docs.djangoproject.com/en/2.2/howto/deployment/wsgi/
"""

import os

from django.core.wsgi import get_wsgi_application

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mysite.settings')

application = get_wsgi_application()


================================================
FILE: src/personal/__init__.py
================================================


================================================
FILE: src/personal/admin.py
================================================
from django.contrib import admin



================================================
FILE: src/personal/apps.py
================================================
from django.apps import AppConfig


class PersonalConfig(AppConfig):
    name = 'personal'


================================================
FILE: src/personal/migrations/0001_initial.py
================================================
# Generated by Django 2.2.2 on 2019-06-19 18:01

from django.db import migrations, models


class Migration(migrations.Migration):

    initial = True

    dependencies = [
    ]

    operations = [
        migrations.CreateModel(
            name='Question',
            fields=[
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('title', models.CharField(max_length=60)),
                ('question', models.TextField(max_length=400)),
                ('priority', models.CharField(choices=[('L', 'Low'), ('M', 'Medium'), ('H', 'High')], max_length=1)),
            ],
            options={
                'verbose_name': 'The Question',
                'verbose_name_plural': 'Questions from People',
            },
        ),
    ]


================================================
FILE: src/personal/migrations/0002_delete_question.py
================================================
# Generated by Django 2.2.2 on 2019-06-19 18:24

from django.db import migrations


class Migration(migrations.Migration):

    dependencies = [
        ('personal', '0001_initial'),
    ]

    operations = [
        migrations.DeleteModel(
            name='Question',
        ),
    ]


================================================
FILE: src/personal/migrations/__init__.py
================================================


================================================
FILE: src/personal/models.py
================================================
from django.db import models

# #Two tuple structure
# #The first element in each tuple is the value that will be stored in the database. The second element is displayed by the field’s form widget.
# #Tuple
# PRIORITY = [
# 	('L', 'Low'), #Tuple1
# 	('M', 'Medium'), #Tuple2
# 	('H', 'High'), #Tuple3
# ]

# class Question(models.Model):
# 	title		 			= models.CharField(max_length=60)
# 	question 				= models.TextField(max_length=400)
# 	priority 				= models.CharField(max_length=1, choices=PRIORITY)

# 	def __str__(self):
# 		return self.title


# 	class Meta:
# 		verbose_name = 'The Question'
# 		verbose_name_plural = 'Questions from People'

================================================
FILE: src/personal/templates/personal/home.html
================================================
{% extends 'base.html' %}
{% load static %}

{% block content %}
<style type="text/css">
	@media (max-width: 768px) { 
		.right-column{
			margin-left: 0px;
		}
	}
	@media (min-width: 768px) { 
		.right-column{
			margin-left: 20px;
		}
	}
	.blog-post-container{
		margin-bottom: 20px;
		width: 100%;
	}
	.create-post-bar{
		background-color: #fff;
		margin-bottom:20px;
	}
	.left-column{
		padding:0px;
	}
	.right-column{
		padding:0px;
	}
	.lead{
		font-size: 17px;
	}
</style>
<div class="container">
	<div class="row">

		<!-- blog feed -->
		<div class="left-column col-lg-7 offset-lg-1">

			<!-- Top 'create post' bar -->
			<div class="d-lg-none mb-3">
				<div class="card m-auto d-flex flex-column p-3">
					<img class="img-fluid d-block m-auto pb-2" src="{% static 'codingwithmitch_logo.png' %}" width="72" height="72">
					<p class="lead">In this course you'll learn how to build a simple blog with user registration and blog CRUD functionality. Django is a powerful 
	  					framework and you'll see why in this course.</p>
					<p class="m-auto"><a class="btn btn-primary" href="{% url 'blog:create' %}">Create post</a></p>
				</div>
			</div>
			<!-- end Top 'create post' bar -->

			<!-- Blog posts-->
			{% if blog_posts %}
				{% for post in blog_posts %}
					<div class="blog-post-container">
						{% include 'blog/snippets/blog_post_snippet.html' with blog_post=post %}
					</div>
				{% endfor %}
			{% else %}
				<div class="blog-post-container">
					{% include 'blog/snippets/blog_post_snippet.html' with query=query %}
				</div>
			{% endif %}
			<!-- End Blog posts-->
			
			<!-- Pagination -->
			{% include 'blog/snippets/blog_post_pagination.html' with blog_posts=blog_posts %}

		</div>
		<!-- end blog feed -->



		<!-- Right 'create post' column  -->
		<div class="right-column col-lg-3 d-lg-flex d-none flex-column">

			<div class="card create-post-bar d-flex flex-column p-3">
				<img class="img-fluid d-block m-auto pb-2" src="{% static 'codingwithmitch_logo.png' %}" width="72" height="72">
				<p class="lead">In this course you'll learn how to build a simple blog with user registration and blog CRUD functionality. Django is a powerful 
  					framework and you'll see why in this course.</p>
				<p class="m-auto"><a class="btn btn-primary" href="{% url 'blog:create' %}">Create post</a></p>
			</div>
		
		</div>
		<!-- end Right 'create post' column  -->

	</div>
</div>

{% endblock content %}

================================================
FILE: src/personal/tests.py
================================================
from django.test import TestCase

# Create your tests here.


================================================
FILE: src/personal/views.py
================================================
from django.shortcuts import render
from operator import attrgetter
from django.core.paginator import EmptyPage, PageNotAnInteger, Paginator

from blog.views import get_blog_queryset
from blog.models import BlogPost

BLOG_POSTS_PER_PAGE = 10

def home_screen_view(request):
	
	context = {}

	query = ""
	query = request.GET.get('q', '')
	context['query'] = str(query)
	print("home_screen_view: " + str(query))

	blog_posts = sorted(get_blog_queryset(query), key=attrgetter('date_updated'), reverse=True)
	
	# Pagination
	page = request.GET.get('page', 1)
	blog_posts_paginator = Paginator(blog_posts, BLOG_POSTS_PER_PAGE)

	try:
		blog_posts = blog_posts_paginator.page(page)
	except PageNotAnInteger:
		blog_posts = blog_posts_paginator.page(BLOG_POSTS_PER_PAGE)
	except EmptyPage:
		blog_posts = blog_posts_paginator.page(blog_posts_paginator.num_pages)

	context['blog_posts'] = blog_posts

	return render(request, "personal/home.html", context)


================================================
FILE: src/requirements.txt
================================================
amqp==2.5.0
anyjson==0.3.3
billiard==3.6.0.0
celery==4.3.0
certifi==2019.6.16
chardet==3.0.4
Django==2.2.2
django-celery-beat==1.5.0
django-celery-results==1.1.2
django-timezone-field==3.0
djangorestframework==3.9.4
idna==2.8
kombu==4.6.3
numpy==1.16.4
opencv-python==4.1.0.25
Pillow==6.0.0
python-crontab==2.3.8
python-dateutil==2.8.0
python-decouple==3.1
pytz==2019.1
redis==3.2.1
requests==2.22.0
six==1.12.0
sqlparse==0.3.0
urllib3==1.25.3
vine==1.3.0
virtualenv==16.6.0


================================================
FILE: src/static_cdn/admin/css/autocomplete.css
================================================
select.admin-autocomplete {
    width: 20em;
}

.select2-container--admin-autocomplete.select2-container {
    min-height: 30px;
}

.select2-container--admin-autocomplete .select2-selection--single,
.select2-container--admin-autocomplete .select2-selection--multiple {
    min-height: 30px;
    padding: 0;
}

.select2-container--admin-autocomplete.select2-container--focus .select2-selection,
.select2-container--admin-autocomplete.select2-container--open .select2-selection {
    border-color: #999;
    min-height: 30px;
}

.select2-container--admin-autocomplete.select2-container--focus .select2-selection.select2-selection--single,
.select2-container--admin-autocomplete.select2-container--open .select2-selection.select2-selection--single {
    padding: 0;
}

.select2-container--admin-autocomplete.select2-container--focus .select2-selection.select2-selection--multiple,
.select2-container--admin-autocomplete.select2-container--open .select2-selection.select2-selection--multiple {
    padding: 0;
}

.select2-container--admin-autocomplete .select2-selection--single {
    background-color: #fff;
    border: 1px solid #ccc;
    border-radius: 4px;
}

.select2-container--admin-autocomplete .select2-selection--single .select2-selection__rendered {
    color: #444;
    line-height: 30px;
}

.select2-container--admin-autocomplete .select2-selection--single .select2-selection__clear {
    cursor: pointer;
    float: right;
    font-weight: bold;
}

.select2-container--admin-autocomplete .select2-selection--single .select2-selection__placeholder {
    color: #999;
}

.select2-container--admin-autocomplete .select2-selection--single .select2-selection__arrow {
    height: 26px;
    position: absolute;
    top: 1px;
    right: 1px;
    width: 20px;
}

.select2-container--admin-autocomplete .select2-selection--single .select2-selection__arrow b {
    border-color: #888 transparent transparent transparent;
    border-style: solid;
    border-width: 5px 4px 0 4px;
    height: 0;
    left: 50%;
    margin-left: -4px;
    margin-top: -2px;
    position: absolute;
    top: 50%;
    width: 0;
}

.select2-container--admin-autocomplete[dir="rtl"] .select2-selection--single .select2-selection__clear {
    float: left;
}

.select2-container--admin-autocomplete[dir="rtl"] .select2-selection--single .select2-selection__arrow {
    left: 1px;
    right: auto;
}

.select2-container--admin-autocomplete.select2-container--disabled .select2-selection--single {
    background-color: #eee;
    cursor: default;
}

.select2-container--admin-autocomplete.select2-container--disabled .select2-selection--single .select2-selection__clear {
    display: none;
}

.select2-container--admin-autocomplete.select2-container--open .select2-selection--single .select2-selection__arrow b {
    border-color: transparent transparent #888 transparent;
    border-width: 0 4px 5px 4px;
}

.select2-container--admin-autocomplete .select2-selection--multiple {
    background-color: white;
    border: 1px solid #ccc;
    border-radius: 4px;
    cursor: text;
}

.select2-container--admin-autocomplete .select2-selection--multiple .select2-selection__rendered {
    box-sizing: border-box;
    list-style: none;
    margin: 0;
    padding: 0 5px;
    width: 100%;
}

.select2-container--admin-autocomplete .select2-selection--multiple .select2-selection__rendered li {
    list-style: none;
}

.select2-container--admin-autocomplete .select2-selection--multiple .select2-selection__placeholder {
    color: #999;
    margin-top: 5px;
    float: left;
}

.select2-container--admin-autocomplete .select2-selection--multiple .select2-selection__clear {
    cursor: pointer;
    float: right;
    font-weight: bold;
    margin: 5px;
}

.select2-container--admin-autocomplete .select2-selection--multiple .select2-selection__choice {
    background-color: #e4e4e4;
    border: 1px solid #ccc;
    border-radius: 4px;
    cursor: default;
    float: left;
    margin-right: 5px;
    margin-top: 5px;
    padding: 0 5px;
}

.select2-container--admin-autocomplete .select2-selection--multiple .select2-selection__choice__remove {
    color: #999;
    cursor: pointer;
    display: inline-block;
    font-weight: bold;
    margin-right: 2px;
}

.select2-container--admin-autocomplete .select2-selection--multiple .select2-selection__choice__remove:hover {
    color: #333;
}

.select2-container--admin-autocomplete[dir="rtl"] .select2-selection--multiple .select2-selection__choice, .select2-container--admin-autocomplete[dir="rtl"] .select2-selection--multiple .select2-selection__placeholder, .select2-container--admin-autocomplete[dir="rtl"] .select2-selection--multiple .select2-search--inline {
    float: right;
}

.select2-container--admin-autocomplete[dir="rtl"] .select2-selection--multiple .select2-selection__choice {
    margin-left: 5px;
    margin-right: auto;
}

.select2-container--admin-autocomplete[dir="rtl"] .select2-selection--multiple .select2-selection__choice__remove {
    margin-left: 2px;
    margin-right: auto;
}

.select2-container--admin-autocomplete.select2-container--focus .select2-selection--multiple {
    border: solid #999 1px;
    outline: 0;
}

.select2-container--admin-autocomplete.select2-container--disabled .select2-selection--multiple {
    background-color: #eee;
    cursor: default;
}

.select2-container--admin-autocomplete.select2-container--disabled .select2-selection__choice__remove {
    display: none;
}

.select2-container--admin-autocomplete.select2-container--open.select2-container--above .select2-selection--single, .select2-container--admin-autocomplete.select2-container--open.select2-container--above .select2-selection--multiple {
    border-top-left-radius: 0;
    border-top-right-radius: 0;
}

.select2-container--admin-autocomplete.select2-container--open.select2-container--below .select2-selection--single, .select2-container--admin-autocomplete.select2-container--open.select2-container--below .select2-selection--multiple {
    border-bottom-left-radius: 0;
    border-bottom-right-radius: 0;
}

.select2-container--admin-autocomplete .select2-search--dropdown .select2-search__field {
    border: 1px solid #ccc;
}

.select2-container--admin-autocomplete .select2-search--inline .select2-search__field {
    background: transparent;
    border: none;
    outline: 0;
    box-shadow: none;
    -webkit-appearance: textfield;
}

.select2-container--admin-autocomplete .select2-results > .select2-results__options {
    max-height: 200px;
    overflow-y: auto;
}

.select2-container--admin-autocomplete .select2-results__option[role=group] {
    padding: 0;
}

.select2-container--admin-autocomplete .select2-results__option[aria-disabled=true] {
    color: #999;
}

.select2-container--admin-autocomplete .select2-results__option[aria-selected=true] {
    background-color: #ddd;
}

.select2-container--admin-autocomplete .select2-results__option .select2-results__option {
    padding-left: 1em;
}

.select2-container--admin-autocomplete .select2-results__option .select2-results__option .select2-results__group {
    padding-left: 0;
}

.select2-container--admin-autocomplete .select2-results__option .select2-results__option .select2-results__option {
    margin-left: -1em;
    padding-left: 2em;
}

.select2-container--admin-autocomplete .select2-results__option .select2-results__option .select2-results__option .select2-results__option {
    margin-left: -2em;
    padding-left: 3em;
}

.select2-container--admin-autocomplete .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option {
    margin-left: -3em;
    padding-left: 4em;
}

.select2-container--admin-autocomplete .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option {
    margin-left: -4em;
    padding-left: 5em;
}

.select2-container--admin-autocomplete .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option {
    margin-left: -5em;
    padding-left: 6em;
}

.select2-container--admin-autocomplete .select2-results__option--highlighted[aria-selected] {
    background-color: #79aec8;
    color: white;
}

.select2-container--admin-autocomplete .select2-results__group {
    cursor: default;
    display: block;
    padding: 6px;
}


================================================
FILE: src/static_cdn/admin/css/base.css
================================================
/*
    DJANGO Admin styles
*/

@import url(fonts.css);

body {
    margin: 0;
    padding: 0;
    font-size: 14px;
    font-family: "Roboto","Lucida Grande","DejaVu Sans","Bitstream Vera Sans",Verdana,Arial,sans-serif;
    color: #333;
    background: #fff;
}

/* LINKS */

a:link, a:visited {
    color: #447e9b;
    text-decoration: none;
}

a:focus, a:hover {
    color: #036;
}

a:focus {
    text-decoration: underline;
}

a img {
    border: none;
}

a.section:link, a.section:visited {
    color: #fff;
    text-decoration: none;
}

a.section:focus, a.section:hover {
    text-decoration: underline;
}

/* GLOBAL DEFAULTS */

p, ol, ul, dl {
    margin: .2em 0 .8em 0;
}

p {
    padding: 0;
    line-height: 140%;
}

h1,h2,h3,h4,h5 {
    font-weight: bold;
}

h1 {
    margin: 0 0 20px;
    font-weight: 300;
    font-size: 20px;
    color: #666;
}

h2 {
    font-size: 16px;
    margin: 1em 0 .5em 0;
}

h2.subhead {
    font-weight: normal;
    margin-top: 0;
}

h3 {
    font-size: 14px;
    margin: .8em 0 .3em 0;
    color: #666;
    font-weight: bold;
}

h4 {
    font-size: 12px;
    margin: 1em 0 .8em 0;
    padding-bottom: 3px;
}

h5 {
    font-size: 10px;
    margin: 1.5em 0 .5em 0;
    color: #666;
    text-transform: uppercase;
    letter-spacing: 1px;
}

ul li {
    list-style-type: square;
    padding: 1px 0;
}

li ul {
    margin-bottom: 0;
}

li, dt, dd {
    font-size: 13px;
    line-height: 20px;
}

dt {
    font-weight: bold;
    margin-top: 4px;
}

dd {
    margin-left: 0;
}

form {
    margin: 0;
    padding: 0;
}

fieldset {
    margin: 0;
    padding: 0;
    border: none;
    border-top: 1px solid #eee;
}

blockquote {
    font-size: 11px;
    color: #777;
    margin-left: 2px;
    padding-left: 10px;
    border-left: 5px solid #ddd;
}

code, pre {
    font-family: "Bitstream Vera Sans Mono", Monaco, "Courier New", Courier, monospace;
    color: #666;
    font-size: 12px;
}

pre.literal-block {
    margin: 10px;
    background: #eee;
    padding: 6px 8px;
}

code strong {
    color: #930;
}

hr {
    clear: both;
    color: #eee;
    background-color: #eee;
    height: 1px;
    border: none;
    margin: 0;
    padding: 0;
    font-size: 1px;
    line-height: 1px;
}

/* TEXT STYLES & MODIFIERS */

.small {
    font-size: 11px;
}

.tiny {
    font-size: 10px;
}

p.tiny {
    margin-top: -2px;
}

.mini {
    font-size: 10px;
}

p.mini {
    margin-top: -3px;
}

.help, p.help, form p.help, div.help, form div.help, div.help li {
    font-size: 11px;
    color: #999;
}

div.help ul {
     margin-bottom: 0;
}

.help-tooltip {
    cursor: help;
}

p img, h1 img, h2 img, h3 img, h4 img, td img {
    vertical-align: middle;
}

.quiet, a.quiet:link, a.quiet:visited {
    color: #999;
    font-weight: normal;
}

.float-right {
    float: right;
}

.float-left {
    float: left;
}

.clear {
    clear: both;
}

.align-left {
    text-align: left;
}

.align-right {
    text-align: right;
}

.example {
    margin: 10px 0;
    padding: 5px 10px;
    background: #efefef;
}

.nowrap {
    white-space: nowrap;
}

/* TABLES */

table {
    border-collapse: collapse;
    border-color: #ccc;
}

td, th {
    font-size: 13px;
    line-height: 16px;
    border-bottom: 1px solid #eee;
    vertical-align: top;
    padding: 8px;
    font-family: "Roboto", "Lucida Grande", Verdana, Arial, sans-serif;
}

th {
    font-weight: 600;
    text-align: left;
}

thead th,
tfoot td {
    color: #666;
    padding: 5px 10px;
    font-size: 11px;
    background: #fff;
    border: none;
    border-top: 1px solid #eee;
    border-bottom: 1px solid #eee;
}

tfoot td {
    border-bottom: none;
    border-top: 1px solid #eee;
}

thead th.required {
    color: #000;
}

tr.alt {
    background: #f6f6f6;
}

.row1 {
    background: #fff;
}

.row2 {
    background: #f9f9f9;
}

/* SORTABLE TABLES */

thead th {
    padding: 5px 10px;
    line-height: normal;
    text-transform: uppercase;
    background: #f6f6f6;
}

thead th a:link, thead th a:visited {
    color: #666;
}

thead th.sorted {
    background: #eee;
}

thead th.sorted .text {
    padding-right: 42px;
}

table thead th .text span {
    padding: 8px 10px;
    display: block;
}

table thead th .text a {
    display: block;
    cursor: pointer;
    padding: 8px 10px;
}

table thead th .text a:focus, table thead th .text a:hover {
    background: #eee;
}

thead th.sorted a.sortremove {
    visibility: hidden;
}

table thead th.sorted:hover a.sortremove {
    visibility: visible;
}

table thead th.sorted .sortoptions {
    display: block;
    padding: 9px 5px 0 5px;
    float: right;
    text-align: right;
}

table thead th.sorted .sortpriority {
    font-size: .8em;
    min-width: 12px;
    text-align: center;
    vertical-align: 3px;
    margin-left: 2px;
    margin-right: 2px;
}

table thead th.sorted .sortoptions a {
    position: relative;
    width: 14px;
    height: 14px;
    display: inline-block;
    background: url(../img/sorting-icons.svg) 0 0 no-repeat;
    background-size: 14px auto;
}

table thead th.sorted .sortoptions a.sortremove {
    background-position: 0 0;
}

table thead th.sorted .sortoptions a.sortremove:after {
    content: '\\';
    position: absolute;
    top: -6px;
    left: 3px;
    font-weight: 200;
    font-size: 18px;
    color: #999;
}

table thead th.sorted .sortoptions a.sortremove:focus:after,
table thead th.sorted .sortoptions a.sortremove:hover:after {
    color: #447e9b;
}

table thead th.sorted .sortoptions a.sortremove:focus,
table thead th.sorted .sortoptions a.sortremove:hover {
    background-position: 0 -14px;
}

table thead th.sorted .sortoptions a.ascending {
    background-position: 0 -28px;
}

table thead th.sorted .sortoptions a.ascending:focus,
table thead th.sorted .sortoptions a.ascending:hover {
    background-position: 0 -42px;
}

table thead th.sorted .sortoptions a.descending {
    top: 1px;
    background-position: 0 -56px;
}

table thead th.sorted .sortoptions a.descending:focus,
table thead th.sorted .sortoptions a.descending:hover {
    background-position: 0 -70px;
}

/* FORM DEFAULTS */

input, textarea, select, .form-row p, form .button {
    margin: 2px 0;
    padding: 2px 3px;
    vertical-align: middle;
    font-family: "Roboto", "Lucida Grande", Verdana, Arial, sans-serif;
    font-weight: normal;
    font-size: 13px;
}
.form-row div.help {
    padding: 2px 3px;
}

textarea {
    vertical-align: top;
}

input[type=text], input[type=password], input[type=email], input[type=url],
input[type=number], input[type=tel], textarea, select, .vTextField {
    border: 1px solid #ccc;
    border-radius: 4px;
    padding: 5px 6px;
    margin-top: 0;
}

input[type=text]:focus, input[type=password]:focus, input[type=email]:focus,
input[type=url]:focus, input[type=number]:focus, input[type=tel]:focus,
textarea:focus, select:focus, .vTextField:focus {
    border-color: #999;
}

select {
    height: 30px;
}

select[multiple] {
    /* Allow HTML size attribute to override the height in the rule above. */
    height: auto;
    min-height: 150px;
}

/* FORM BUTTONS */

.button, input[type=submit], input[type=button], .submit-row input, a.button {
    background: #79aec8;
    padding: 10px 15px;
    border: none;
    border-radius: 4px;
    color: #fff;
    cursor: pointer;
}

a.button {
    padding: 4px 5px;
}

.button:active, input[type=submit]:active, input[type=button]:active,
.button:focus, input[type=submit]:focus, input[type=button]:focus,
.button:hover, input[type=submit]:hover, input[type=button]:hover {
    background: #609ab6;
}

.button[disabled], input[type=submit][disabled], input[type=button][disabled] {
    opacity: 0.4;
}

.button.default, input[type=submit].default, .submit-row input.default {
    float: right;
    border: none;
    font-weight: 400;
    background: #417690;
}

.button.default:active, input[type=submit].default:active,
.button.default:focus, input[type=submit].default:focus,
.button.default:hover, input[type=submit].default:hover {
    background: #205067;
}

.button[disabled].default,
input[type=submit][disabled].default,
input[type=button][disabled].default {
    opacity: 0.4;
}


/* MODULES */

.module {
    border: none;
    margin-bottom: 30px;
    background: #fff;
}

.module p, .module ul, .module h3, .module h4, .module dl, .module pre {
    padding-left: 10px;
    padding-right: 10px;
}

.module blockquote {
    margin-left: 12px;
}

.module ul, .module ol {
    margin-left: 1.5em;
}

.module h3 {
    margin-top: .6em;
}

.module h2, .module caption, .inline-group h2 {
    margin: 0;
    padding: 8px;
    font-weight: 400;
    font-size: 13px;
    text-align: left;
    background: #79aec8;
    color: #fff;
}

.module caption,
.inline-group h2 {
    font-size: 12px;
    letter-spacing: 0.5px;
    text-transform: uppercase;
}

.module table {
    border-collapse: collapse;
}

/* MESSAGES & ERRORS */

ul.messagelist {
    padding: 0;
    margin: 0;
}

ul.messagelist li {
    display: block;
    font-weight: 400;
    font-size: 13px;
    padding: 10px 10px 10px 65px;
    margin: 0 0 10px 0;
    background: #dfd url(../img/icon-yes.svg) 40px 12px no-repeat;
    background-size: 16px auto;
    color: #333;
}

ul.messagelist li.warning {
    background: #ffc url(../img/icon-alert.svg) 40px 14px no-repeat;
    background-size: 14px auto;
}

ul.messagelist li.error {
    background: #ffefef url(../img/icon-no.svg) 40px 12px no-repeat;
    background-size: 16px auto;
}

.errornote {
    font-size: 14px;
    font-weight: 700;
    display: block;
    padding: 10px 12px;
    margin: 0 0 10px 0;
    color: #ba2121;
    border: 1px solid #ba2121;
    border-radius: 4px;
    background-color: #fff;
    background-position: 5px 12px;
}

ul.errorlist {
    margin: 0 0 4px;
    padding: 0;
    color: #ba2121;
    background: #fff;
}

ul.errorlist li {
    font-size: 13px;
    display: block;
    margin-bottom: 4px;
}

ul.errorlist li:first-child {
    margin-top: 0;
}

ul.errorlist li a {
    color: inherit;
    text-decoration: underline;
}

td ul.errorlist {
    margin: 0;
    padding: 0;
}

td ul.errorlist li {
    margin: 0;
}

.form-row.errors {
    margin: 0;
    border: none;
    border-bottom: 1px solid #eee;
    background: none;
}

.form-row.errors ul.errorlist li {
    padding-left: 0;
}

.errors input, .errors select, .errors textarea {
    border: 1px solid #ba2121;
}

div.system-message {
    background: #ffc;
    margin: 10px;
    padding: 6px 8px;
    font-size: .8em;
}

div.system-message p.system-message-title {
    padding: 4px 5px 4px 25px;
    margin: 0;
    color: #c11;
    background: #ffefef url(../img/icon-no.svg) 5px 5px no-repeat;
}

.description {
    font-size: 12px;
    padding: 5px 0 0 12px;
}

/* BREADCRUMBS */

div.breadcrumbs {
    background: #79aec8;
    padding: 10px 40px;
    border: none;
    font-size: 14px;
    color: #c4dce8;
    text-align: left;
}

div.breadcrumbs a {
    color: #fff;
}

div.breadcrumbs a:focus, div.breadcrumbs a:hover {
    color: #c4dce8;
}

/* ACTION ICONS */

.viewlink, .inlineviewlink {
    padding-left: 16px;
    background: url(../img/icon-viewlink.svg) 0 1px no-repeat;
}

.addlink {
    padding-left: 16px;
    background: url(../img/icon-addlink.svg) 0 1px no-repeat;
}

.changelink, .inlinechangelink {
    padding-left: 16px;
    background: url(../img/icon-changelink.svg) 0 1px no-repeat;
}

.deletelink {
    padding-left: 16px;
    background: url(../img/icon-deletelink.svg) 0 1px no-repeat;
}

a.deletelink:link, a.deletelink:visited {
    color: #CC3434;
}

a.deletelink:focus, a.deletelink:hover {
    color: #993333;
    text-decoration: none;
}

/* OBJECT TOOLS */

.object-tools {
    font-size: 10px;
    font-weight: bold;
    padding-left: 0;
    float: right;
    position: relative;
    margin-top: -48px;
}

.form-row .object-tools {
    margin-top: 5px;
    margin-bottom: 5px;
    float: none;
    height: 2em;
    padding-left: 3.5em;
}

.object-tools li {
    display: block;
    float: left;
    margin-left: 5px;
    height: 16px;
}

.object-tools a {
    border-radius: 15px;
}

.object-tools a:link, .object-tools a:visited {
    display: block;
    float: left;
    padding: 3px 12px;
    background: #999;
    font-weight: 400;
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    color: #fff;
}

.object-tools a:focus, .object-tools a:hover {
    background-color: #417690;
}

.object-tools a:focus{
    text-decoration: none;
}

.object-tools a.viewsitelink, .object-tools a.golink,.object-tools a.addlink {
    background-repeat: no-repeat;
    background-position: right 7px center;
    padding-right: 26px;
}

.object-tools a.viewsitelink, .object-tools a.golink {
    background-image: url(../img/tooltag-arrowright.svg);
}

.object-tools a.addlink {
    background-image: url(../img/tooltag-add.svg);
}

/* OBJECT HISTORY */

table#change-history {
    width: 100%;
}

table#change-history tbody th {
    width: 16em;
}

/* PAGE STRUCTURE */

#container {
    position: relative;
    width: 100%;
    min-width: 980px;
    padding: 0;
}

#content {
    padding: 20px 40px;
}

.dashboard #content {
    width: 600px;
}

#content-main {
    float: left;
    width: 100%;
}

#content-related {
    float: right;
    width: 260px;
    position: relative;
    margin-right: -300px;
}

#footer {
    clear: both;
    padding: 10px;
}

/* COLUMN TYPES */

.colMS {
    margin-right: 300px;
}

.colSM {
    margin-left: 300px;
}

.colSM #content-related {
    float: left;
    margin-right: 0;
    margin-left: -300px;
}

.colSM #content-main {
    float: right;
}

.popup .colM {
    width: auto;
}

/* HEADER */

#header {
    width: auto;
    height: auto;
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 10px 40px;
    background: #417690;
    color: #ffc;
    overflow: hidden;
}

#header a:link, #header a:visited {
    color: #fff;
}

#header a:focus , #header a:hover {
    text-decoration: underline;
}

#branding {
    float: left;
}

#branding h1 {
    padding: 0;
    margin: 0 20px 0 0;
    font-weight: 300;
    font-size: 24px;
    color: #f5dd5d;
}

#branding h1, #branding h1 a:link, #branding h1 a:visited {
    color: #f5dd5d;
}

#branding h2 {
    padding: 0 10px;
    font-size: 14px;
    margin: -8px 0 8px 0;
    font-weight: normal;
    color: #ffc;
}

#branding a:hover {
    text-decoration: none;
}

#user-tools {
    float: right;
    padding: 0;
    margin: 0 0 0 20px;
    font-weight: 300;
    font-size: 11px;
    letter-spacing: 0.5px;
    text-transform: uppercase;
    text-align: right;
}

#user-tools a {
    border-bottom: 1px solid rgba(255, 255, 255, 0.25);
}

#user-tools a:focus, #user-tools a:hover {
    text-decoration: none;
    border-bottom-color: #79aec8;
    color: #79aec8;
}

/* SIDEBAR */

#content-related {
    background: #f8f8f8;
}

#content-related .module {
    background: none;
}

#content-related h3 {
    font-size: 14px;
    color: #666;
    padding: 0 16px;
    margin: 0 0 16px;
}

#content-related h4 {
    font-size: 13px;
}

#content-related p {
    padding-left: 16px;
    padding-right: 16px;
}

#content-related .actionlist {
    padding: 0;
    margin: 16px;
}

#content-related .actionlist li {
    line-height: 1.2;
    margin-bottom: 10px;
    padding-left: 18px;
}

#content-related .module h2 {
    background: none;
    padding: 16px;
    margin-bottom: 16px;
    border-bottom: 1px solid #eaeaea;
    font-size: 18px;
    color: #333;
}

.delete-confirmation form input[type="submit"] {
    background: #ba2121;
    border-radius: 4px;
    padding: 10px 15px;
    color: #fff;
}

.delete-confirmation form input[type="submit"]:active,
.delete-confirmation form input[type="submit"]:focus,
.delete-confirmation form input[type="submit"]:hover {
    background: #a41515;
}

.delete-confirmation form .cancel-link {
    display: inline-block;
    vertical-align: middle;
    height: 15px;
    line-height: 15px;
    background: #ddd;
    border-radius: 4px;
    padding: 10px 15px;
    color: #333;
    margin: 0 0 0 10px;
}

.delete-confirmation form .cancel-link:active,
.delete-confirmation form .cancel-link:focus,
.delete-confirmation form .cancel-link:hover {
    background: #ccc;
}

/* POPUP */
.popup #content {
    padding: 20px;
}

.popup #container {
    min-width: 0;
}

.popup #header {
    padding: 10px 20px;
}


================================================
FILE: src/static_cdn/admin/css/changelists.css
================================================
/* CHANGELISTS */

#changelist {
    position: relative;
    width: 100%;
}

#changelist table {
    width: 100%;
}

.change-list .hiddenfields { display:none; }

.change-list .filtered table {
    border-right: none;
}

.change-list .filtered {
    min-height: 400px;
}

.change-list .filtered .results, .change-list .filtered .paginator,
.filtered #toolbar, .filtered div.xfull {
    margin-right: 280px;
    width: auto;
}

.change-list .filtered table tbody th {
    padding-right: 1em;
}

#changelist-form .results {
  overflow-x: auto;
}

#changelist .toplinks {
    border-bottom: 1px solid #ddd;
}

#changelist .paginator {
    color: #666;
    border-bottom: 1px solid #eee;
    background: #fff;
    overflow: hidden;
}

/* CHANGELIST TABLES */

#changelist table thead th {
    padding: 0;
    white-space: nowrap;
    vertical-align: middle;
}

#changelist table thead th.action-checkbox-column {
    width: 1.5em;
    text-align: center;
}

#changelist table tbody td.action-checkbox {
    text-align: center;
}

#changelist table tfoot {
    color: #666;
}

/* TOOLBAR */

#changelist #toolbar {
    padding: 8px 10px;
    margin-bottom: 15px;
    border-top: 1px solid #eee;
    border-bottom: 1px solid #eee;
    background: #f8f8f8;
    color: #666;
}

#changelist #toolbar form input {
    border-radius: 4px;
    font-size: 14px;
    padding: 5px;
    color: #333;
}

#changelist #toolbar form #searchbar {
    height: 19px;
    border: 1px solid #ccc;
    padding: 2px 5px;
    margin: 0;
    vertical-align: top;
    font-size: 13px;
}

#changelist #toolbar form #searchbar:focus {
    border-color: #999;
}

#changelist #toolbar form input[type="submit"] {
    border: 1px solid #ccc;
    padding: 2px 10px;
    margin: 0;
    vertical-align: middle;
    background: #fff;
    box-shadow: 0 -15px 20px -10px rgba(0, 0, 0, 0.15) inset;
    cursor: pointer;
    color: #333;
}

#changelist #toolbar form input[type="submit"]:focus,
#changelist #toolbar form input[type="submit"]:hover {
    border-color: #999;
}

#changelist #changelist-search img {
    vertical-align: middle;
    margin-right: 4px;
}

/* FILTER COLUMN */

#changelist-filter {
    position: absolute;
    top: 0;
    right: 0;
    z-index: 1000;
    width: 240px;
    background: #f8f8f8;
    border-left: none;
    margin: 0;
}

#changelist-filter h2 {
    font-size: 14px;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    padding: 5px 15px;
    margin-bottom: 12px;
    border-bottom: none;
}

#changelist-filter h3 {
    font-weight: 400;
    font-size: 14px;
    padding: 0 15px;
    margin-bottom: 10px;
}

#changelist-filter ul {
    margin: 5px 0;
    padding: 0 15px 15px;
    border-bottom: 1px solid #eaeaea;
}

#changelist-filter ul:last-child {
    border-bottom: none;
    padding-bottom: none;
}

#changelist-filter li {
    list-style-type: none;
    margin-left: 0;
    padding-left: 0;
}

#changelist-filter a {
    display: block;
    color: #999;
    text-overflow: ellipsis;
    overflow-x: hidden;
}

#changelist-filter li.selected {
    border-left: 5px solid #eaeaea;
    padding-left: 10px;
    margin-left: -15px;
}

#changelist-filter li.selected a {
    color: #5b80b2;
}

#changelist-filter a:focus, #changelist-filter a:hover,
#changelist-filter li.selected a:focus,
#changelist-filter li.selected a:hover {
    color: #036;
}

/* DATE DRILLDOWN */

.change-list ul.toplinks {
    display: block;
    float: left;
    padding: 0;
    margin: 0;
    width: 100%;
}

.change-list ul.toplinks li {
    padding: 3px 6px;
    font-weight: bold;
    list-style-type: none;
    display: inline-block;
}

.change-list ul.toplinks .date-back a {
    color: #999;
}

.change-list ul.toplinks .date-back a:focus,
.change-list ul.toplinks .date-back a:hover {
    color: #036;
}

/* PAGINATOR */

.paginator {
    font-size: 13px;
    padding-top: 10px;
    padding-bottom: 10px;
    line-height: 22px;
    margin: 0;
    border-top: 1px solid #ddd;
}

.paginator a:link, .paginator a:visited {
    padding: 2px 6px;
    background: #79aec8;
    text-decoration: none;
    color: #fff;
}

.paginator a.showall {
    padding: 0;
    border: none;
    background: none;
    color: #5b80b2;
}

.paginator a.showall:focus, .paginator a.showall:hover {
    background: none;
    color: #036;
}

.paginator .end {
    margin-right: 6px;
}

.paginator .this-page {
    padding: 2px 6px;
    font-weight: bold;
    font-size: 13px;
    vertical-align: top;
}

.paginator a:focus, .paginator a:hover {
    color: white;
    background: #036;
}

/* ACTIONS */

.filtered .actions {
    margin-right: 280px;
    border-right: none;
}

#changelist table input {
    margin: 0;
    vertical-align: baseline;
}

#changelist table tbody tr.selected {
    background-color: #FFFFCC;
}

#changelist .actions {
    padding: 10px;
    background: #fff;
    border-top: none;
    border-bottom: none;
    line-height: 24px;
    color: #999;
}

#changelist .actions.selected {
    background: #fffccf;
    border-top: 1px solid #fffee8;
    border-bottom: 1px solid #edecd6;
}

#changelist .actions span.all,
#changelist .actions span.action-counter,
#changelist .actions span.clear,
#changelist .actions span.question {
    font-size: 13px;
    margin: 0 0.5em;
    display: none;
}

#changelist .actions:last-child {
    border-bottom: none;
}

#changelist .actions select {
    vertical-align: top;
    height: 24px;
    background: none;
    color: #000;
    border: 1px solid #ccc;
    border-radius: 4px;
    font-size: 14px;
    padding: 0 0 0 4px;
    margin: 0;
    margin-left: 10px;
}

#changelist .actions select:focus {
    border-color: #999;
}

#changelist .actions label {
    display: inline-block;
    vertical-align: middle;
    font-size: 13px;
}

#changelist .actions .button {
    font-size: 13px;
    border: 1px solid #ccc;
    border-radius: 4px;
    background: #fff;
    box-shadow: 0 -15px 20px -10px rgba(0, 0, 0, 0.15) inset;
    cursor: pointer;
    height: 24px;
    line-height: 1;
    padding: 4px 8px;
    margin: 0;
    color: #333;
}

#changelist .actions .button:focus, #changelist .actions .button:hover {
    border-color: #999;
}


================================================
FILE: src/static_cdn/admin/css/dashboard.css
================================================
/* DASHBOARD */

.dashboard .module table th {
    width: 100%;
}

.dashboard .module table td {
    white-space: nowrap;
}

.dashboard .module table td a {
    display: block;
    padding-right: .6em;
}

/* RECENT ACTIONS MODULE */

.module ul.actionlist {
    margin-left: 0;
}

ul.actionlist li {
    list-style-type: none;
    overflow: hidden;
    text-overflow: ellipsis;
    -o-text-overflow: ellipsis;
}


================================================
FILE: src/static_cdn/admin/css/fonts.css
================================================
@font-face {
    font-family: 'Roboto';
    src: url('../fonts/Roboto-Bold-webfont.woff');
    font-weight: 700;
    font-style: normal;
}

@font-face {
    font-family: 'Roboto';
    src: url('../fonts/Roboto-Regular-webfont.woff');
    font-weight: 400;
    font-style: normal;
}

@font-face {
    font-family: 'Roboto';
    src: url('../fonts/Roboto-Light-webfont.woff');
    font-weight: 300;
    font-style: normal;
}


================================================
FILE: src/static_cdn/admin/css/forms.css
================================================
@import url('widgets.css');

/* FORM ROWS */

.form-row {
    overflow: hidden;
    padding: 10px;
    font-size: 13px;
    border-bottom: 1px solid #eee;
}

.form-row img, .form-row input {
    vertical-align: middle;
}

.form-row label input[type="checkbox"] {
    margin-top: 0;
    vertical-align: 0;
}

form .form-row p {
    padding-left: 0;
}

.hidden {
    display: none;
}

/* FORM LABELS */

label {
    font-weight: normal;
    color: #666;
    font-size: 13px;
}

.required label, label.required {
    font-weight: bold;
    color: #333;
}

/* RADIO BUTTONS */

form ul.radiolist li {
    list-style-type: none;
}

form ul.radiolist label {
    float: none;
    display: inline;
}

form ul.radiolist input[type="radio"] {
    margin: -2px 4px 0 0;
    padding: 0;
}

form ul.inline {
    margin-left: 0;
    padding: 0;
}

form ul.inline li {
    float: left;
    padding-right: 7px;
}

/* ALIGNED FIELDSETS */

.aligned label {
    display: block;
    padding: 4px 10px 0 0;
    float: left;
    width: 160px;
    word-wrap: break-word;
    line-height: 1;
}

.aligned label:not(.vCheckboxLabel):after {
    content: '';
    display: inline-block;
    vertical-align: middle;
    height: 26px;
}

.aligned label + p, .aligned label + div.help, .aligned label + div.readonly {
    padding: 6px 0;
    margin-top: 0;
    margin-bottom: 0;
    margin-left: 170px;
}

.aligned ul label {
    display: inline;
    float: none;
    width: auto;
}

.aligned .form-row input {
    margin-bottom: 0;
}

.colMS .aligned .vLargeTextField, .colMS .aligned .vXMLLargeTextField {
    width: 350px;
}

form .aligned ul {
    margin-left: 160px;
    padding-left: 10px;
}

form .aligned ul.radiolist {
    display: inline-block;
    margin: 0;
    padding: 0;
}

form .aligned p.help,
form .aligned div.help {
    clear: left;
    margin-top: 0;
    margin-left: 160px;
    padding-left: 10px;
}

form .aligned label + p.help,
form .aligned label + div.help {
    margin-left: 0;
    padding-left: 0;
}

form .aligned p.help:last-child,
form .aligned div.help:last-child {
    margin-bottom: 0;
    padding-bottom: 0;
}

form .aligned input + p.help,
form .aligned textarea + p.help,
form .aligned select + p.help,
form .aligned input + div.help,
form .aligned textarea + div.help,
form .aligned select + div.help {
    margin-left: 160px;
    padding-left: 10px;
}

form .aligned ul li {
    list-style: none;
}

form .aligned table p {
    margin-left: 0;
    padding-left: 0;
}

.aligned .vCheckboxLabel {
    float: none;
    width: auto;
    display: inline-block;
    vertical-align: -3px;
    padding: 0 0 5px 5px;
}

.aligned .vCheckboxLabel + p.help,
.aligned .vCheckboxLabel + div.help {
    margin-top: -4px;
}

.colM .aligned .vLargeTextField, .colM .aligned .vXMLLargeTextField {
    width: 610px;
}

.checkbox-row p.help,
.checkbox-row div.help {
    margin-left: 0;
    padding-left: 0;
}

fieldset .fieldBox {
    float: left;
    margin-right: 20px;
}

/* WIDE FIELDSETS */

.wide label {
    width: 200px;
}

form .wide p,
form .wide input + p.help,
form .wide input + div.help {
    margin-left: 200px;
}

form .wide p.help,
form .wide div.help {
    padding-left: 38px;
}

form div.help ul {
    padding-left: 0;
    margin-left: 0;
}

.colM fieldset.wide .vLargeTextField, .colM fieldset.wide .vXMLLargeTextField {
    width: 450px;
}

/* COLLAPSED FIELDSETS */

fieldset.collapsed * {
    display: none;
}

fieldset.collapsed h2, fieldset.collapsed {
    display: block;
}

fieldset.collapsed {
    border: 1px solid #eee;
    border-radius: 4px;
    overflow: hidden;
}

fieldset.collapsed h2 {
    background: #f8f8f8;
    color: #666;
}

fieldset .collapse-toggle {
    color: #fff;
}

fieldset.collapsed .collapse-toggle {
    background: transparent;
    display: inline;
    color: #447e9b;
}

/* MONOSPACE TEXTAREAS */

fieldset.monospace textarea {
    font-family: "Bitstream Vera Sans Mono", Monaco, "Courier New", Courier, monospace;
}

/* SUBMIT ROW */

.submit-row {
    padding: 12px 14px;
    margin: 0 0 20px;
    background: #f8f8f8;
    border: 1px solid #eee;
    border-radius: 4px;
    text-align: right;
    overflow: hidden;
}

body.popup .submit-row {
    overflow: auto;
}

.submit-row input {
    height: 35px;
    line-height: 15px;
    margin: 0 0 0 5px;
}

.submit-row input.default {
    margin: 0 0 0 8px;
    text-transform: uppercase;
}

.submit-row p {
    margin: 0.3em;
}

.submit-row p.deletelink-box {
    float: left;
    margin: 0;
}

.submit-row a.deletelink {
    display: block;
    background: #ba2121;
    border-radius: 4px;
    padding: 10px 15px;
    height: 15px;
    line-height: 15px;
    color: #fff;
}

.submit-row a.closelink {
    display: inline-block;
    background: #bbbbbb;
    border-radius: 4px;
    padding: 10px 15px;
    height: 15px;
    line-height: 15px;
    margin: 0 0 0 5px;
    color: #fff;
}

.submit-row a.deletelink:focus,
.submit-row a.deletelink:hover,
.submit-row a.deletelink:active {
    background: #a41515;
}

.submit-row a.closelink:focus,
.submit-row a.closelink:hover,
.submit-row a.closelink:active {
    background: #aaaaaa;
}

/* CUSTOM FORM FIELDS */

.vSelectMultipleField {
    vertical-align: top;
}

.vCheckboxField {
    border: none;
}

.vDateField, .vTimeField {
    margin-right: 2px;
    margin-bottom: 4px;
}

.vDateField {
    min-width: 6.85em;
}

.vTimeField {
    min-width: 4.7em;
}

.vURLField {
    width: 30em;
}

.vLargeTextField, .vXMLLargeTextField {
    width: 48em;
}

.flatpages-flatpage #id_content {
    height: 40.2em;
}

.module table .vPositiveSmallIntegerField {
    width: 2.2em;
}

.vTextField, .vUUIDField {
    width: 20em;
}

.vIntegerField {
    width: 5em;
}

.vBigIntegerField {
    width: 10em;
}

.vForeignKeyRawIdAdminField {
    width: 5em;
}

/* INLINES */

.inline-group {
    padding: 0;
    margin: 0 0 30px;
}

.inline-group thead th {
    padding: 8px 10px;
}

.inline-group .aligned label {
    width: 160px;
}

.inline-related {
    position: relative;
}

.inline-related h3 {
    margin: 0;
    color: #666;
    padding: 5px;
    font-size: 13px;
    background: #f8f8f8;
    border-top: 1px solid #eee;
    border-bottom: 1px solid #eee;
}

.inline-related h3 span.delete {
    float: right;
}

.inline-related h3 span.delete label {
    margin-left: 2px;
    font-size: 11px;
}

.inline-related fieldset {
    margin: 0;
    background: #fff;
    border: none;
    width: 100%;
}

.inline-related fieldset.module h3 {
    margin: 0;
    padding: 2px 5px 3px 5px;
    font-size: 11px;
    text-align: left;
    font-weight: bold;
    background: #bcd;
    color: #fff;
}

.inline-group .tabular fieldset.module {
    border: none;
}

.inline-related.tabular fieldset.module table {
    width: 100%;
}

.last-related fieldset {
    border: none;
}

.inline-group .tabular tr.has_original td {
    padding-top: 2em;
}

.inline-group .tabular tr td.original {
    padding: 2px 0 0 0;
    width: 0;
    _position: relative;
}

.inline-group .tabular th.original {
    width: 0px;
    padding: 0;
}

.inline-group .tabular td.original p {
    position: absolute;
    left: 0;
    height: 1.1em;
    padding: 2px 9px;
    overflow: hidden;
    font-size: 9px;
    font-weight: bold;
    color: #666;
    _width: 700px;
}

.inline-group ul.tools {
    padding: 0;
    margin: 0;
    list-style: none;
}

.inline-group ul.tools li {
    display: inline;
    padding: 0 5px;
}

.inline-group div.add-row,
.inline-group .tabular tr.add-row td {
    color: #666;
    background: #f8f8f8;
    padding: 8px 10px;
    border-bottom: 1px solid #eee;
}

.inline-group .tabular tr.add-row td {
    padding: 8px 10px;
    border-bottom: 1px solid #eee;
}

.inline-group ul.tools a.add,
.inline-group div.add-row a,
.inline-group .tabular tr.add-row td a {
    background: url(../img/icon-addlink.svg) 0 1px no-repeat;
    padding-left: 16px;
    font-size: 12px;
}

.empty-form {
    display: none;
}

/* RELATED FIELD ADD ONE / LOOKUP */

.add-another, .related-lookup {
    margin-left: 5px;
    display: inline-block;
    vertical-align: middle;
    background-repeat: no-repeat;
    background-size: 14px;
}

.add-another {
    width: 16px;
    height: 16px;
    background-image: url(../img/icon-addlink.svg);
}

.related-lookup {
    width: 16px;
    height: 16px;
    background-image: url(../img/search.svg);
}

form .related-widget-wrapper ul {
    display: inline-block;
    margin-left: 0;
    padding-left: 0;
}

.clearable-file-input input {
    margin-top: 0;
}


================================================
FILE: src/static_cdn/admin/css/login.css
================================================
/* LOGIN FORM */

body.login {
    background: #f8f8f8;
}

.login #header {
    height: auto;
    padding: 15px 16px;
    justify-content: center;
}

.login #header h1 {
    font-size: 18px;
}

.login #header h1 a {
    color: #fff;
}

.login #content {
    padding: 20px 20px 0;
}

.login #container {
    background: #fff;
    border: 1px solid #eaeaea;
    border-radius: 4px;
    overflow: hidden;
    width: 28em;
    min-width: 300px;
    margin: 100px auto;
}

.login #content-main {
    width: 100%;
}

.login .form-row {
    padding: 4px 0;
    float: left;
    width: 100%;
    border-bottom: none;
}

.login .form-row label {
    padding-right: 0.5em;
    line-height: 2em;
    font-size: 1em;
    clear: both;
    color: #333;
}

.login .form-row #id_username, .login .form-row #id_password {
    clear: both;
    padding: 8px;
    width: 100%;
    -webkit-box-sizing: border-box;
       -moz-box-sizing: border-box;
            box-sizing: border-box;
}

.login span.help {
    font-size: 10px;
    display: block;
}

.login .submit-row {
    clear: both;
    padding: 1em 0 0 9.4em;
    margin: 0;
    border: none;
    background: none;
    text-align: left;
}

.login .password-reset-link {
    text-align: center;
}


================================================
FILE: src/static_cdn/admin/css/responsive.css
================================================
/* Tablets */

input[type="submit"], button {
    -webkit-appearance: none;
    appearance: none;
}

@media (max-width: 1024px) {
    /* Basic */

    html {
        -webkit-text-size-adjust: 100%;
    }

    td, th {
        padding: 10px;
        font-size: 14px;
    }

    .small {
        font-size: 12px;
    }

    /* Layout */

    #container {
        min-width: 0;
    }

    #content {
        padding: 20px 30px 30px;
    }

    div.breadcrumbs {
        padding: 10px 30px;
    }

    /* Header */

    #header {
        flex-direction: column;
        padding: 15px 30px;
        justify-content: flex-start;
    }

    #branding h1 {
        margin: 0 0 8px;
        font-size: 20px;
        line-height: 1.2;
    }

    #user-tools {
        margin: 0;
        font-weight: 400;
        line-height: 1.85;
        text-align: left;
    }

    #user-tools a {
        display: inline-block;
        line-height: 1.4;
    }

    /* Dashboard */

    .dashboard #content {
        width: auto;
    }

    #content-related {
        margin-right: -290px;
    }

    .colSM #content-related {
        margin-left: -290px;
    }

    .colMS {
        margin-right: 290px;
    }

    .colSM {
        margin-left: 290px;
    }

    .dashboard .module table td a {
        padding-right: 0;
    }

    td .changelink, td .addlink {
        font-size: 13px;
    }

    /* Changelist */

    #changelist #toolbar {
        border: none;
        padding: 15px;
    }

    #changelist-search > div {
        display: -webkit-flex;
        display: flex;
        -webkit-flex-wrap: wrap;
        flex-wrap: wrap;
        max-width: 480px;
    }

    #changelist-search label {
        line-height: 22px;
    }

    #changelist #toolbar form #searchbar {
        -webkit-flex: 1 0 auto;
        flex: 1 0 auto;
        width: 0;
        height: 22px;
        margin: 0 10px 0 6px;
    }

    #changelist-search .quiet {
        width: 100%;
        margin: 5px 0 0 25px;
    }

    #changelist .actions {
        display: flex;
        flex-wrap: wrap;
        padding: 15px 0;
    }

    #changelist .actions.selected {
        border: none;
    }

    #changelist .actions label {
        display: flex;
    }

    #changelist .actions select {
        background: #fff;
    }

    #changelist .actions .button {
        min-width: 48px;
        margin: 0 10px;
    }

    #changelist .actions span.all,
    #changelist .actions span.clear,
    #changelist .actions span.question,
    #changelist .actions span.action-counter {
        font-size: 11px;
        margin: 0 10px 0 0;
    }

    #changelist-filter {
        width: 200px;
    }

    .change-list .filtered .results,
    .change-list .filtered .paginator,
    .filtered #toolbar,
    .filtered .actions,
    .filtered div.xfull {
        margin-right: 230px;
    }

    #changelist .paginator {
        border-top-color: #eee;
    }

    #changelist .results + .paginator {
        border-top: none;
    }

    /* Forms */

    label {
        font-size: 14px;
    }

    .form-row input[type=text],
    .form-row input[type=password],
    .form-row input[type=email],
    .form-row input[type=url],
    .form-row input[type=tel],
    .form-row input[type=number],
    .form-row textarea,
    .form-row select,
    .form-row .vTextField {
        box-sizing: border-box;
        margin: 0;
        padding: 6px 8px;
        min-height: 36px;
        font-size: 14px;
    }

    .form-row select {
        height: 36px;
    }

    .form-row select[multiple] {
        height: auto;
        min-height: 0;
    }

    fieldset .fieldBox {
        float: none;
        margin: 0 -10px;
        padding: 0 10px;
    }

    fieldset .fieldBox + .fieldBox {
        margin-top: 10px;
        padding-top: 10px;
        border-top: 1px solid #eee;
    }

    textarea {
        max-width: 518px;
        max-height: 120px;
    }

    .aligned label {
        padding-top: 6px;
    }

    .aligned .add-another,
    .aligned .related-lookup,
    .aligned .datetimeshortcuts,
    .aligned .related-lookup + strong {
        align-self: center;
        margin-left: 15px;
    }

    form .aligned ul.radiolist {
        margin-left: 2px;
    }

    /* Related widget */

    .related-widget-wrapper {
        float: none;
    }

    .related-widget-wrapper-link + .selector {
        max-width: calc(100% - 30px);
        margin-right: 15px;
    }

    select + .related-widget-wrapper-link,
    .related-widget-wrapper-link + .related-widget-wrapper-link {
        margin-left: 10px;
    }

    /* Selector */

    .selector {
        display: flex;
        width: 100%;
    }

    .selector .selector-filter {
        display: flex;
        align-items: center;
    }

    .selector .selector-filter label {
        margin: 0 8px 0 0;
    }

    .selector .selector-filter input {
        width: auto;
        min-height: 0;
        flex: 1 1;
    }

    .selector-available, .selector-chosen {
        width: auto;
        flex: 1 1;
        display: flex;
        flex-direction: column;
    }

    .selector select {
        width: 100%;
        flex: 1 0 auto;
        margin-bottom: 5px;
    }

    .selector ul.selector-chooser {
        width: 26px;
        height: 52px;
        padding: 2px 0;
        margin: auto 15px;
        border-radius: 20px;
        transform: translateY(-10px);
    }

    .selector-add, .selector-remove {
        width: 20px;
        height: 20px;
        background-size: 20px auto;
    }

    .selector-add {
        background-position: 0 -120px;
    }

    .selector-remove {
        background-position: 0 -80px;
    }

    a.selector-chooseall, a.selector-clearall {
        align-self: center;
    }

    .stacked {
        flex-direction: column;
        max-width: 480px;
    }

    .stacked > * {
        flex: 0 1 auto;
    }

    .stacked select {
        margin-bottom: 0;
    }

    .stacked .selector-available, .stacked .selector-chosen {
        width: auto;
    }

    .stacked ul.selector-chooser {
        width: 52px;
        height: 26px;
        padding: 0 2px;
        margin: 15px auto;
        transform: none;
    }

    .stacked .selector-chooser li {
        padding: 3px;
    }

    .stacked .selector-add, .stacked .selector-remove {
        background-size: 20px auto;
    }

    .stacked .selector-add {
        background-position: 0 -40px;
    }

    .stacked .active.selector-add {
        background-position: 0 -60px;
    }

    .stacked .selector-remove {
        background-position: 0 0;
    }

    .stacked .active.selector-remove {
        background-position: 0 -20px;
    }

    .help-tooltip, .selector .help-icon {
        display: none;
    }

    form .form-row p.datetime {
        width: 100%;
    }

    .datetime input {
        width: 50%;
        max-width: 120px;
    }

    .datetime span {
        font-size: 13px;
    }

    .datetime .timezonewarning {
        display: block;
        font-size: 11px;
        color: #999;
    }

    .datetimeshortcuts {
        color: #ccc;
    }

    .inline-group {
        overflow: auto;
    }

    /* Messages */

    ul.messagelist li {
        padding-left: 55px;
        background-position: 30px 12px;
    }

    ul.messagelist li.error {
        background-position: 30px 12px;
    }

    ul.messagelist li.warning {
        background-position: 30px 14px;
    }

    /* Login */

    .login #header {
        padding: 15px 20px;
    }

    .login #branding h1 {
        margin: 0;
    }

    /* GIS */

    div.olMap {
        max-width: calc(100vw - 30px);
        max-height: 300px;
    }

    .olMap + .clear_features {
        display: block;
        margin-top: 10px;
    }

    /* Docs */

    .module table.xfull {
        width: 100%;
    }

    pre.literal-block {
        overflow: auto;
    }
}

/* Mobile */

@media (max-width: 767px) {
    /* Layout */

    #header, #content, #footer {
        padding: 15px;
    }

    #footer:empty {
        padding: 0;
    }

    div.breadcrumbs {
        padding: 10px 15px;
    }

    /* Dashboard */

    .colMS, .colSM {
        margin: 0;
    }

    #content-related, .colSM #content-related {
        width: 100%;
        margin: 0;
    }

    #content-related .module {
        margin-bottom: 0;
    }

    #content-related .module h2 {
        padding: 10px 15px;
        font-size: 16px;
    }

    /* Changelist */

    #changelist {
        display: flex;
        flex-direction: column;
    }

    #changelist #toolbar {
        order: 1;
        padding: 10px;
    }

    #changelist .xfull {
        order: 2;
    }

    #changelist-form {
        order: 3;
    }

    #changelist-filter {
        order: 4;
    }

    #changelist .actions label {
        flex: 1 1;
    }

    #changelist .actions select {
        flex: 1 0;
        width: 100%;
    }

    #changelist .actions span {
        flex: 1 0 100%;
    }

    .change-list .filtered .results, .change-list .filtered .paginator,
    .filtered #toolbar, .filtered .actions, .filtered div.xfull {
        margin-right: 0;
    }

    #changelist-filter {
        position: static;
        width: auto;
        margin-top: 30px;
    }

    .object-tools {
        float: none;
        margin: 0 0 15px;
        padding: 0;
        overflow: hidden;
    }

    .object-tools li {
        height: auto;
        margin-left: 0;
    }

    .object-tools li + li {
        margin-left: 15px;
    }

    /* Forms */

    .form-row {
        padding: 15px 0;
    }

    .aligned .form-row,
    .aligned .form-row > div {
        display: flex;
        flex-wrap: wrap;
        max-width: 100vw;
    }

    .aligned .form-row > div {
        width: calc(100vw - 30px);
    }

    textarea {
        max-width: none;
    }

    .vURLField {
        width: auto;
    }

    fieldset .fieldBox + .fieldBox {
        margin-top: 15px;
        padding-top: 15px;
    }

    fieldset.collapsed .form-row {
        display: none;
    }

    .aligned label {
        width: 100%;
        padding: 0 0 10px;
    }

    .aligned label:after {
        max-height: 0;
    }

    .aligned .form-row input,
    .aligned .form-row select,
    .aligned .form-row textarea {
        flex: 1 1 auto;
        max-width: 100%;
    }

    .aligned .checkbox-row {
        align-items: center;
    }

    .aligned .checkbox-row input {
        flex: 0 1 auto;
        margin: 0;
    }

    .aligned .vCheckboxLabel {
        flex: 1 0;
        padding: 1px 0 0 5px;
    }

    .aligned label + p,
    .aligned label + div.help,
    .aligned label + div.readonly {
        padding: 0;
        margin-left: 0;
    }

    .aligned p.file-upload {
        margin-left: 0;
        font-size: 13px;
    }

    span.clearable-file-input {
        margin-left: 15px;
    }

    span.clearable-file-input label {
        font-size: 13px;
        padding-bottom: 0;
    }

    .aligned .timezonewarning {
        flex: 1 0 100%;
        margin-top: 5px;
    }

    form .aligned .form-row div.help {
        width: 100%;
        margin: 5px 0 0;
        padding: 0;
    }

    form .aligned ul {
        margin-left: 0;
        padding-left: 0;
    }

    form .aligned ul.radiolist {
        margin-right: 15px;
        margin-bottom: -3px;
    }

    form .aligned ul.radiolist li + li {
        margin-top: 5px;
    }

    /* Related widget */

    .related-widget-wrapper {
        width: 100%;
        display: flex;
        align-items: flex-start;
    }

    .related-widget-wrapper .selector {
        order: 1;
    }

    .related-widget-wrapper > a {
        order: 2;
    }

    .related-widget-wrapper .radiolist ~ a {
        align-self: flex-end;
    }

    .related-widget-wrapper > select ~ a {
        align-self: center;
    }

    select + .related-widget-wrapper-link,
    .related-widget-wrapper-link + .related-widget-wrapper-link {
        margin-left: 15px;
    }

    /* Selector */

    .selector {
        flex-direction: column;
    }

    .selector > * {
        float: none;
    }

    .selector-available, .selector-chosen {
        margin-bottom: 0;
        flex: 1 1 auto;
    }

    .selector select {
        max-height: 96px;
    }

    .selector ul.selector-chooser {
        display: block;
        float: none;
        width: 52px;
        height: 26px;
        padding: 0 2px;
        margin: 15px auto 20px;
        transform: none;
    }

    .selector ul.selector-chooser li {
        float: left;
    }

    .selector-remove {
        background-position: 0 0;
    }

    .selector-add  {
        background-position: 0 -40px;
    }

    /* Inlines */

    .inline-group[data-inline-type="stacked"] .inline-related {
        border: 2px solid #eee;
        border-radius: 4px;
        margin-top: 15px;
        overflow: auto;
    }

    .inline-group[data-inline-type="stacked"] .inline-related > * {
        box-sizing: border-box;
    }

    .inline-group[data-inline-type="stacked"] .inline-related + .inline-related {
        margin-top: 30px;
    }

    .inline-group[data-inline-type="stacked"] .inline-related .module {
        padding: 0 10px;
    }

    .inline-group[data-inline-type="stacked"] .inline-related .module .form-row:last-child {
        border-bottom: none;
    }

    .inline-group[data-inline-type="stacked"] .inline-related h3 {
        padding: 10px;
        border-top-width: 0;
        border-bottom-width: 2px;
        display: flex;
        flex-wrap: wrap;
        align-items: center;
    }

    .inline-group[data-inline-type="stacked"] .inline-related h3 .inline_label {
        margin-right: auto;
    }

    .inline-group[data-inline-type="stacked"] .inline-related h3 span.delete {
        float: none;
        flex: 1 1 100%;
        margin-top: 5px;
    }

    .inline-group[data-inline-type="stacked"] .aligned .form-row > div:not([class]) {
        width: 100%;
    }

    .inline-group[data-inline-type="stacked"] .aligned label {
        width: 100%;
    }

    .inline-group[data-inline-type="stacked"] div.add-row {
        margin-top: 15px;
        border: 1px solid #eee;
        border-radius: 4px;
    }

    .inline-group div.add-row,
    .inline-group .tabular tr.add-row td {
        padding: 0;
    }

    .inline-group div.add-row a,
    .inline-group .tabular tr.add-row td a {
        display: block;
        padding: 8px 10px 8px 26px;
        background-position: 8px 9px;
    }

    /* Submit row */

    .submit-row {
        padding: 10px 10px 0;
        margin: 0 0 15px;
        display: flex;
        flex-direction: column;
    }

    .submit-row > * {
        width: 100%;
    }

    .submit-row input, .submit-row input.default, .submit-row a, .submit-row a.closelink {
        float: none;
        margin: 0 0 10px;
        text-align: center;
    }

    .submit-row a.closelink {
        padding: 10px 0;
    }

    .submit-row p.deletelink-box {
        order: 4;
    }

    /* Messages */

    ul.messagelist li {
        padding-left: 40px;
        background-position: 15px 12px;
    }

    ul.messagelist li.error {
        background-position: 15px 12px;
    }

    ul.messagelist li.warning {
        background-position: 15px 14px;
    }

    /* Paginator */

    .paginator .this-page, .paginator a:link, .paginator a:visited {
        padding: 4px 10px;
    }

    /* Login */

    body.login {
        padding: 0 15px;
    }

    .login #container {
        width: auto;
        max-width: 480px;
        margin: 50px auto;
    }

    .login #header,
    .login #content {
        padding: 15px;
    }

    .login #content-main {
        float: none;
    }

    .login .form-row {
        padding: 0;
    }

    .login .form-row + .form-row {
        margin-top: 15px;
    }

    .login .form-row label {
        display: block;
        margin: 0 0 5px;
        padding: 0;
        line-height: 1.2;
    }

    .login .submit-row {
        padding: 15px 0 0;
    }

    .login br, .login .submit-row label {
        display: none;
    }

    .login .submit-row input {
        margin: 0;
        text-transform: uppercase;
    }

    .errornote {
        margin: 0 0 20px;
        padding: 8px 12px;
        font-size: 13px;
    }

    /* Calendar and clock */

    .calendarbox, .clockbox {
        position: fixed !important;
        top: 50% !important;
        left: 50% !important;
        transform: translate(-50%, -50%);
        margin: 0;
        border: none;
        overflow: visible;
    }

    .calendarbox:before, .clockbox:before {
        content: '';
        position: fixed;
        top: 50%;
        left: 50%;
        width: 100vw;
        height: 100vh;
        background: rgba(0, 0, 0, 0.75);
        transform: translate(-50%, -50%);
    }

    .calendarbox > *, .clockbox > * {
        position: relative;
        z-index: 1;
    }

    .calendarbox > div:first-child {
        z-index: 2;
    }

    .calendarbox .calendar, .clockbox h2 {
        border-radius: 4px 4px 0 0;
        overflow: hidden;
    }

    .calendarbox .calendar-cancel, .clockbox .calendar-cancel {
        border-radius: 0 0 4px 4px;
        overflow: hidden;
    }

    .calendar-shortcuts {
        padding: 10px 0;
        font-size: 12px;
        line-height: 12px;
    }

    .calendar-shortcuts a {
        margin: 0 4px;
    }

    .timelist a {
        background: #fff;
        padding: 4px;
    }

    .calendar-cancel {
        padding: 8px 10px;
    }

    .clockbox h2 {
        padding: 8px 15px;
    }

    .calendar caption {
        padding: 10px;
    }

    .calendarbox .calendarnav-previous, .calendarbox .calendarnav-next {
        z-index: 1;
        top: 10px;
    }

    /* History */

    table#change-history tbody th, table#change-history tbody td {
        font-size: 13px;
        word-break: break-word;
    }

    table#change-history tbody th {
        width: auto;
    }

    /* Docs */

    table.model tbody th, table.model tbody td {
        font-size: 13px;
        word-break: break-word;
    }
}


================================================
FILE: src/static_cdn/admin/css/responsive_rtl.css
================================================
/* TABLETS */

@media (max-width: 1024px) {
    [dir="rtl"] .colMS {
        margin-right: 0;
    }

    [dir="rtl"] #user-tools {
        text-align: right;
    }

    [dir="rtl"] #changelist .actions label {
        padding-left: 10px;
        padding-right: 0;
    }

    [dir="rtl"] #changelist .actions select {
        margin-left: 0;
        margin-right: 15px;
    }

    [dir="rtl"] .change-list .filtered .results,
    [dir="rtl"] .change-list .filtered .paginator,
    [dir="rtl"] .filtered #toolbar,
    [dir="rtl"] .filtered div.xfull,
    [dir="rtl"] .filtered .actions {
        margin-right: 0;
        margin-left: 230px;
    }

    [dir="rtl"] .inline-group ul.tools a.add,
    [dir="rtl"] .inline-group div.add-row a,
    [dir="rtl"] .inline-group .tabular tr.add-row td a {
        padding: 8px 26px 8px 10px;
        background-position: calc(100% - 8px) 9px;
    }

    [dir="rtl"] .related-widget-wrapper-link + .selector {
        margin-right: 0;
        margin-left: 15px;
    }

    [dir="rtl"] .selector .selector-filter label {
        margin-right: 0;
        margin-left: 8px;
    }

    [dir="rtl"] .object-tools li {
        float: right;
    }

    [dir="rtl"] .object-tools li + li {
        margin-left: 0;
        margin-right: 15px;
    }

    [dir="rtl"] .dashboard .module table td a {
        padding-left: 0;
        padding-right: 16px;
    }
}

/* MOBILE */

@media (max-width: 767px) {
    [dir="rtl"] .change-list .filtered .results,
    [dir="rtl"] .change-list .filtered .paginator,
    [dir="rtl"] .filtered #toolbar,
    [dir="rtl"] .filtered div.xfull,
    [dir="rtl"] .filtered .actions {
        margin-left: 0;
    }

    [dir="rtl"] .aligned .add-another,
    [dir="rtl"] .aligned .related-lookup,
    [dir="rtl"] .aligned .datetimeshortcuts {
        margin-left: 0;
        margin-right: 15px;
    }

    [dir="rtl"] .aligned ul {
        margin-right: 0;
    }
}


================================================
FILE: src/static_cdn/admin/css/rtl.css
================================================
body {
    direction: rtl;
}

/* LOGIN */

.login .form-row {
    float: right;
}

.login .form-row label {
    float: right;
    padding-left: 0.5em;
    padding-right: 0;
    text-align: left;
}

.login .submit-row {
    clear: both;
    padding: 1em 9.4em 0 0;
}

/* GLOBAL */

th {
    text-align: right;
}

.module h2, .module caption {
    text-align: right;
}

.module ul, .module ol {
    margin-left: 0;
    margin-right: 1.5em;
}

.viewlink, .addlink, .changelink {
    padding-left: 0;
    padding-right: 16px;
    background-position: 100% 1px;
}

.deletelink {
    padding-left: 0;
    padding-right: 16px;
    background-position: 100% 1px;
}

.object-tools {
    float: left;
}

thead th:first-child,
tfoot td:first-child {
    border-left: none;
}

/* LAYOUT */

#user-tools {
    right: auto;
    left: 0;
    text-align: left;
}

div.breadcrumbs {
    text-align: right;
}

#content-main {
    float: right;
}

#content-related {
    float: left;
    margin-left: -300px;
    margin-right: auto;
}

.colMS {
    margin-left: 300px;
    margin-right: 0;
}

/* SORTABLE TABLES */

table thead th.sorted .sortoptions {
   float: left;
}

thead th.sorted .text {
    padding-right: 0;
    padding-left: 42px;
}

/* dashboard styles */

.dashboard .module table td a {
    padding-left: .6em;
    padding-right: 16px;
}

/* changelists styles */

.change-list .filtered table {
    border-left: none;
    border-right: 0px none;
}

#changelist-filter {
    right: auto;
    left: 0;
    border-left: none;
    border-right: none;
}

.change-list .filtered .results, .change-list .filtered .paginator, .filtered #toolbar, .filtered div.xfull {
    margin-right: 0;
    margin-left: 280px;
}

#changelist-filter li.selected {
    border-left: none;
    padding-left: 10px;
    margin-left: 0;
    border-right: 5px solid #eaeaea;
    padding-right: 10px;
    margin-right: -15px;
}

.filtered .actions {
    margin-left: 280px;
    margin-right: 0;
}

#changelist table tbody td:first-child, #changelist table tbody th:first-child {
    border-right: none;
    border-left: none;
}

/* FORMS */

.aligned label {
    padding: 0 0 3px 1em;
    float: right;
}

.submit-row {
    text-align: left
}

.submit-row p.deletelink-box {
    float: right;
}

.submit-row input.default {
    margin-left: 0;
}

.vDateField, .vTimeField {
    margin-left: 2px;
}

.aligned .form-row input {
    margin-left: 5px;
}

form .aligned p.help, form .aligned div.help {
    clear: right;
}

form .aligned ul {
    margin-right: 163px;
    margin-left: 0;
}

form ul.inline li {
    float: right;
    padding-right: 0;
    padding-left: 7px;
}

input[type=submit].default, .submit-row input.default {
    float: left;
}

fieldset .fieldBox {
    float: right;
    margin-left: 20px;
    margin-right: 0;
}

.errorlist li {
    background-position: 100% 12px;
    padding: 0;
}

.errornote {
    background-position: 100% 12px;
    padding: 10px 12px;
}

/* WIDGETS */

.calendarnav-previous {
    top: 0;
    left: auto;
    right: 10px;
}

.calendarnav-next {
    top: 0;
    right: auto;
    left: 10px;
}

.calendar caption, .calendarbox h2 {
    text-align: center;
}

.selector {
    float: right;
}

.selector .selector-filter {
    text-align: right;
}

.inline-deletelink {
    float: left;
}

form .form-row p.datetime {
    overflow: hidden;
}

.related-widget-wrapper {
    float: right;
}

/* MISC */

.inline-related h2, .inline-group h2 {
    text-align: right
}

.inline-related h3 span.delete {
    padding-right: 20px;
    padding-left: inherit;
    left: 10px;
    right: inherit;
    float:left;
}

.inline-related h3 span.delete label {
    margin-left: inherit;
    margin-right: 2px;
}

/* IE7 specific bug fixes */

div.colM {
    position: relative;
}

.submit-row input {
    float: left;
}


================================================
FILE: src/static_cdn/admin/css/vendor/select2/LICENSE-SELECT2.md
================================================
The MIT License (MIT)

Copyright (c) 2012-2015 Kevin Brown, Igor Vaynberg, and Select2 contributors

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.


================================================
FILE: src/static_cdn/admin/css/vendor/select2/select2.css
================================================
.select2-container {
  box-sizing: border-box;
  display: inline-block;
  margin: 0;
  position: relative;
  vertical-align: middle; }
  .select2-container .select2-selection--single {
    box-sizing: border-box;
    cursor: pointer;
    display: block;
    height: 28px;
    user-select: none;
    -webkit-user-select: none; }
    .select2-container .select2-selection--single .select2-selection__rendered {
      display: block;
      padding-left: 8px;
      padding-right: 20px;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap; }
    .select2-container .select2-selection--single .select2-selection__clear {
      position: relative; }
  .select2-container[dir="rtl"] .select2-selection--single .select2-selection__rendered {
    padding-right: 8px;
    padding-left: 20px; }
  .select2-container .select2-selection--multiple {
    box-sizing: border-box;
    cursor: pointer;
    display: block;
    min-height: 32px;
    user-select: none;
    -webkit-user-select: none; }
    .select2-container .select2-selection--multiple .select2-selection__rendered {
      display: inline-block;
      overflow: hidden;
      padding-left: 8px;
      text-overflow: ellipsis;
      white-space: nowrap; }
  .select2-container .select2-search--inline {
    float: left; }
    .select2-container .select2-search--inline .select2-search__field {
      box-sizing: border-box;
      border: none;
      font-size: 100%;
      margin-top: 5px;
      padding: 0; }
      .select2-container .select2-search--inline .select2-search__field::-webkit-search-cancel-button {
        -webkit-appearance: none; }

.select2-dropdown {
  background-color: white;
  border: 1px solid #aaa;
  border-radius: 4px;
  box-sizing: border-box;
  display: block;
  position: absolute;
  left: -100000px;
  width: 100%;
  z-index: 1051; }

.select2-results {
  display: block; }

.select2-results__options {
  list-style: none;
  margin: 0;
  padding: 0; }

.select2-results__option {
  padding: 6px;
  user-select: none;
  -webkit-user-select: none; }
  .select2-results__option[aria-selected] {
    cursor: pointer; }

.select2-container--open .select2-dropdown {
  left: 0; }

.select2-container--open .select2-dropdown--above {
  border-bottom: none;
  border-bottom-left-radius: 0;
  border-bottom-right-radius: 0; }

.select2-container--open .select2-dropdown--below {
  border-top: none;
  border-top-left-radius: 0;
  border-top-right-radius: 0; }

.select2-search--dropdown {
  display: block;
  padding: 4px; }
  .select2-search--dropdown .select2-search__field {
    padding: 4px;
    width: 100%;
    box-sizing: border-box; }
    .select2-search--dropdown .select2-search__field::-webkit-search-cancel-button {
      -webkit-appearance: none; }
  .select2-search--dropdown.select2-search--hide {
    display: none; }

.select2-close-mask {
  border: 0;
  margin: 0;
  padding: 0;
  display: block;
  position: fixed;
  left: 0;
  top: 0;
  min-height: 100%;
  min-width: 100%;
  height: auto;
  width: auto;
  opacity: 0;
  z-index: 99;
  background-color: #fff;
  filter: alpha(opacity=0); }

.select2-hidden-accessible {
  border: 0 !important;
  clip: rect(0 0 0 0) !important;
  height: 1px !important;
  margin: -1px !important;
  overflow: hidden !important;
  padding: 0 !important;
  position: absolute !important;
  width: 1px !important; }

.select2-container--default .select2-selection--single {
  background-color: #fff;
  border: 1px solid #aaa;
  border-radius: 4px; }
  .select2-container--default .select2-selection--single .select2-selection__rendered {
    color: #444;
    line-height: 28px; }
  .select2-container--default .select2-selection--single .select2-selection__clear {
    cursor: pointer;
    float: right;
    font-weight: bold; }
  .select2-container--default .select2-selection--single .select2-selection__placeholder {
    color: #999; }
  .select2-container--default .select2-selection--single .select2-selection__arrow {
    height: 26px;
    position: absolute;
    top: 1px;
    right: 1px;
    width: 20px; }
    .select2-container--default .select2-selection--single .select2-selection__arrow b {
      border-color: #888 transparent transparent transparent;
      border-style: solid;
      border-width: 5px 4px 0 4px;
      height: 0;
      left: 50%;
      margin-left: -4px;
      margin-top: -2px;
      position: absolute;
      top: 50%;
      width: 0; }

.select2-container--default[dir="rtl"] .select2-selection--single .select2-selection__clear {
  float: left; }

.select2-container--default[dir="rtl"] .select2-selection--single .select2-selection__arrow {
  left: 1px;
  right: auto; }

.select2-container--default.select2-container--disabled .select2-selection--single {
  background-color: #eee;
  cursor: default; }
  .select2-container--default.select2-container--disabled .select2-selection--single .select2-selection__clear {
    display: none; }

.select2-container--default.select2-container--open .select2-selection--single .select2-selection__arrow b {
  border-color: transparent transparent #888 transparent;
  border-width: 0 4px 5px 4px; }

.select2-container--default .select2-selection--multiple {
  background-color: white;
  border: 1px solid #aaa;
  border-radius: 4px;
  cursor: text; }
  .select2-container--default .select2-selection--multiple .select2-selection__rendered {
    box-sizing: border-box;
    list-style: none;
    margin: 0;
    padding: 0 5px;
    width: 100%; }
    .select2-container--default .select2-selection--multiple .select2-selection__rendered li {
      list-style: none; }
  .select2-container--default .select2-selection--multiple .select2-selection__placeholder {
    color: #999;
    margin-top: 5px;
    float: left; }
  .select2-container--default .select2-selection--multiple .select2-selection__clear {
    cursor: pointer;
    float: right;
    font-weight: bold;
    margin-top: 5px;
    margin-right: 10px; }
  .select2-container--default .select2-selection--multiple .select2-selection__choice {
    background-color: #e4e4e4;
    border: 1px solid #aaa;
    border-radius: 4px;
    cursor: default;
    float: left;
    margin-right: 5px;
    margin-top: 5px;
    padding: 0 5px; }
  .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {
    color: #999;
    cursor: pointer;
    display: inline-block;
    font-weight: bold;
    margin-right: 2px; }
    .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {
      color: #333; }

.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice, .select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__placeholder, .select2-container--default[dir="rtl"] .select2-selection--multiple .select2-search--inline {
  float: right; }

.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice {
  margin-left: 5px;
  margin-right: auto; }

.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice__remove {
  margin-left: 2px;
  margin-right: auto; }

.select2-container--default.select2-container--focus .select2-selection--multiple {
  border: solid black 1px;
  outline: 0; }

.select2-container--default.select2-container--disabled .select2-selection--multiple {
  background-color: #eee;
  cursor: default; }

.select2-container--default.select2-container--disabled .select2-selection__choice__remove {
  display: none; }

.select2-container--default.select2-container--open.select2-container--above .select2-selection--single, .select2-container--default.select2-container--open.select2-container--above .select2-selection--multiple {
  border-top-left-radius: 0;
  border-top-right-radius: 0; }

.select2-container--default.select2-container--open.select2-container--below .select2-selection--single, .select2-container--default.select2-container--open.select2-container--below .select2-selection--multiple {
  border-bottom-left-radius: 0;
  border-bottom-right-radius: 0; }

.select2-container--default .select2-search--dropdown .select2-search__field {
  border: 1px solid #aaa; }

.select2-container--default .select2-search--inline .select2-search__field {
  background: transparent;
  border: none;
  outline: 0;
  box-shadow: none;
  -webkit-appearance: textfield; }

.select2-container--default .select2-results > .select2-results__options {
  max-height: 200px;
  overflow-y: auto; }

.select2-container--default .select2-results__option[role=group] {
  padding: 0; }

.select2-container--default .select2-results__option[aria-disabled=true] {
  color: #999; }

.select2-container--default .select2-results__option[aria-selected=true] {
  background-color: #ddd; }

.select2-container--default .select2-results__option .select2-results__option {
  padding-left: 1em; }
  .select2-container--default .select2-results__option .select2-results__option .select2-results__group {
    padding-left: 0; }
  .select2-container--default .select2-results__option .select2-results__option .select2-results__option {
    margin-left: -1em;
    padding-left: 2em; }
    .select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option {
      margin-left: -2em;
      padding-left: 3em; }
      .select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option {
        margin-left: -3em;
        padding-left: 4em; }
        .select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option {
          margin-left: -4em;
          padding-left: 5em; }
          .select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option {
            margin-left: -5em;
            padding-left: 6em; }

.select2-container--default .select2-results__option--highlighted[aria-selected] {
  background-color: #5897fb;
  color: white; }

.select2-container--default .select2-results__group {
  cursor: default;
  display: block;
  padding: 6px; }

.select2-container--classic .select2-selection--single {
  background-color: #f7f7f7;
  border: 1px solid #aaa;
  border-radius: 4px;
  outline: 0;
  background-image: -webkit-linear-gradient(top, white 50%, #eeeeee 100%);
  background-image: -o-linear-gradient(top, white 50%, #eeeeee 100%);
  background-image: linear-gradient(to bottom, white 50%, #eeeeee 100%);
  background-repeat: repeat-x;
  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFFFFFFF', endColorstr='#FFEEEEEE', GradientType=0); }
  .select2-container--classic .select2-selection--single:focus {
    border: 1px solid #5897fb; }
  .select2-container--classic .select2-selection--single .select2-selection__rendered {
    color: #444;
    line-height: 28px; }
  .select2-container--classic .select2-selection--single .select2-selection__clear {
    cursor: pointer;
    float: right;
    font-weight: bold;
    margin-right: 10px; }
  .select2-container--classic .select2-selection--single .select2-selection__placeholder {
    color: #999; }
  .select2-container--classic .select2-selection--single .select2-selection__arrow {
    background-color: #ddd;
    border: none;
    border-left: 1px solid #aaa;
    border-top-right-radius: 4px;
    border-bottom-right-radius: 4px;
    height: 26px;
    position: absolute;
    top: 1px;
    right: 1px;
    width: 20px;
    background-image: -webkit-linear-gradient(top, #eeeeee 50%, #cccccc 100%);
    background-image: -o-linear-gradient(top, #eeeeee 50%, #cccccc 100%);
    background-image: linear-gradient(to bottom, #eeeeee 50%, #cccccc 100%);
    background-repeat: repeat-x;
    filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFEEEEEE', endColorstr='#FFCCCCCC', GradientType=0); }
    .select2-container--classic .select2-selection--single .select2-selection__arrow b {
      border-color: #888 transparent transparent transparent;
      border-style: solid;
      border-width: 5px 4px 0 4px;
      height: 0;
      left: 50%;
      margin-left: -4px;
      margin-top: -2px;
      position: absolute;
      top: 50%;
      width: 0; }

.select2-container--classic[dir="rtl"] .select2-selection--single .select2-selection__clear {
  float: left; }

.select2-container--classic[dir="rtl"] .select2-selection--single .select2-selection__arrow {
  border: none;
  border-right: 1px solid #aaa;
  border-radius: 0;
  border-top-left-radius: 4px;
  border-bottom-left-radius: 4px;
  left: 1px;
  right: auto; }

.select2-container--classic.select2-container--open .select2-selection--single {
  border: 1px solid #5897fb; }
  .select2-container--classic.select2-container--open .select2-selection--single .select2-selection__arrow {
    background: transparent;
    border: none; }
    .select2-container--classic.select2-container--open .select2-selection--single .select2-selection__arrow b {
      border-color: transparent transparent #888 transparent;
      border-width: 0 4px 5px 4px; }

.select2-container--classic.select2-container--open.select2-container--above .select2-selection--single {
  border-top: none;
  border-top-left-radius: 0;
  border-top-right-radius: 0;
  background-image: -webkit-linear-gradient(top, white 0%, #eeeeee 50%);
  background-image: -o-linear-gradient(top, white 0%, #eeeeee 50%);
  background-image: linear-gradient(to bottom, white 0%, #eeeeee 50%);
  background-repeat: repeat-x;
  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFFFFFFF', endColorstr='#FFEEEEEE', GradientType=0); }

.select2-container--classic.select2-container--open.select2-container--below .select2-selection--single {
  border-bottom: none;
  border-bottom-left-radius: 0;
  border-bottom-right-radius: 0;
  background-image: -webkit-linear-gradient(top, #eeeeee 50%, white 100%);
  background-image: -o-linear-gradient(top, #eeeeee 50%, white 100%);
  background-image: linear-gradient(to bottom, #eeeeee 50%, white 100%);
  background-repeat: repeat-x;
  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFEEEEEE', endColorstr='#FFFFFFFF', GradientType=0); }

.select2-container--classic .select2-selection--multiple {
  background-color: white;
  border: 1px solid #aaa;
  border-radius: 4px;
  cursor: text;
  outline: 0; }
  .select2-container--classic .select2-selection--multiple:focus {
    border: 1px solid #5897fb; }
  .select2-container--classic .select2-selection--multiple .select2-selection__rendered {
    list-style: none;
    margin: 0;
    padding: 0 5px; }
  .select2-container--classic .select2-selection--multiple .select2-selection__clear {
    display: none; }
  .select2-container--classic .select2-selection--multiple .select2-selection__choice {
    background-color: #e4e4e4;
    border: 1px solid #aaa;
    border-radius: 4px;
    cursor: default;
    float: left;
    margin-right: 5px;
    margin-top: 5px;
    padding: 0 5px; }
  .select2-container--classic .select2-selection--multiple .select2-selection__choice__remove {
    color: #888;
    cursor: pointer;
    display: inline-block;
    font-weight: bold;
    margin-right: 2px; }
    .select2-container--classic .select2-selection--multiple .select2-selection__choice__remove:hover {
      color: #555; }

.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice {
  float: right; }

.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice {
  margin-left: 5px;
  margin-right: auto; }

.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice__remove {
  margin-left: 2px;
  margin-right: auto; }

.select2-container--classic.select2-container--open .select2-selection--multiple {
  border: 1px solid #5897fb; }

.select2-container--classic.select2-container--open.select2-container--above .select2-selection--multiple {
  border-top: none;
  border-top-left-radius: 0;
  border-top-right-radius: 0; }

.select2-container--classic.select2-container--open.select2-container--below .select2-selection--multiple {
  border-bottom: none;
  border-bottom-left-radius: 0;
  border-bottom-right-radius: 0; }

.select2-container--classic .select2-search--dropdown .select2-search__field {
  border: 1px solid #aaa;
  outline: 0; }

.select2-container--classic .select2-search--inline .select2-search__field {
  outline: 0;
  box-shadow: none; }

.select2-container--classic .select2-dropdown {
  background-color: white;
  border: 1px solid transparent; }

.select2-container--classic .select2-dropdown--above {
  border-bottom: none; }

.select2-container--classic .select2-dropdown--below {
  border-top: none; }

.select2-container--classic .select2-results > .select2-results__options {
  max-height: 200px;
  overflow-y: auto; }

.select2-container--classic .select2-results__option[role=group] {
  padding: 0; }

.select2-container--classic .select2-results__option[aria-disabled=true] {
  color: grey; }

.select2-container--classic .select2-results__option--highlighted[aria-selected] {
  background-color: #3875d7;
  color: white; }

.select2-container--classic .select2-results__group {
  cursor: default;
  display: block;
  padding: 6px; }

.select2-container--classic.select2-container--open .select2-dropdown {
  border-color: #5897fb; }


================================================
FILE: src/static_cdn/admin/css/widgets.css
================================================
/* SELECTOR (FILTER INTERFACE) */

.selector {
    width: 800px;
    float: left;
}

.selector select {
    width: 380px;
    height: 17.2em;
}

.selector-available, .selector-chosen {
    float: left;
    width: 380px;
    text-align: center;
    margin-bottom: 5px;
}

.selector-chosen select {
    border-top: none;
}

.selector-available h2, .selector-chosen h2 {
    border: 1px solid #ccc;
    border-radius: 4px 4px 0 0;
}

.selector-chosen h2 {
    background: #79aec8;
    color: #fff;
}

.selector .selector-available h2 {
    background: #f8f8f8;
    color: #666;
}

.selector .selector-filter {
    background: white;
    border: 1px solid #ccc;
    border-width: 0 1px;
    padding: 8px;
    color: #999;
    font-size: 10px;
    margin: 0;
    text-align: left;
}

.selector .selector-filter label,
.inline-group .aligned .selector .selector-filter label {
    float: left;
    margin: 7px 0 0;
    width: 18px;
    height: 18px;
    padding: 0;
    overflow: hidden;
    line-height: 1;
}

.selector .selector-available input {
    width: 320px;
    margin-left: 8px;
}

.selector ul.selector-chooser {
    float: left;
    width: 22px;
    background-color: #eee;
    border-radius: 10px;
    margin: 10em 5px 0 5px;
    padding: 0;
}

.selector-chooser li {
    margin: 0;
    padding: 3px;
    list-style-type: none;
}

.selector select {
    padding: 0 10px;
    margin: 0 0 10px;
    border-radius: 0 0 4px 4px;
}

.selector-add, .selector-remove {
    width: 16px;
    height: 16px;
    display: block;
    text-indent: -3000px;
    overflow: hidden;
    cursor: default;
    opacity: 0.3;
}

.active.selector-add, .active.selector-remove {
    opacity: 1;
}

.active.selector-add:hover, .active.selector-remove:hover {
    cursor: pointer;
}

.selector-add {
    background: url(../img/selector-icons.svg) 0 -96px no-repeat;
}

.active.selector-add:focus, .active.selector-add:hover {
    background-position: 0 -112px;
}

.selector-remove {
    background: url(../img/selector-icons.svg) 0 -64px no-repeat;
}

.active.selector-remove:focus, .active.selector-remove:hover {
    background-position: 0 -80px;
}

a.selector-chooseall, a.selector-clearall {
    display: inline-block;
    height: 16px;
    text-align: left;
    margin: 1px auto 3px;
    overflow: hidden;
    font-weight: bold;
    line-height: 16px;
    color: #666;
    text-decoration: none;
    opacity: 0.3;
}

a.active.selector-chooseall:focus, a.active.selector-clearall:focus,
a.active.selector-chooseall:hover, a.active.selector-clearall:hover {
    color: #447e9b;
}

a.active.selector-chooseall, a.active.selector-clearall {
    opacity: 1;
}

a.active.selector-chooseall:hover, a.active.selector-clearall:hover {
    cursor: pointer;
}

a.selector-chooseall {
    padding: 0 18px 0 0;
    background: url(../img/selector-icons.svg) right -160px no-repeat;
    cursor: default;
}

a.active.selector-chooseall:focus, a.active.selector-chooseall:hover {
    background-position: 100% -176px;
}

a.selector-clearall {
    padding: 0 0 0 18px;
    background: url(../img/selector-icons.svg) 0 -128px no-repeat;
    cursor: default;
}

a.active.selector-clearall:focus, a.active.selector-clearall:hover {
    background-position: 0 -144px;
}

/* STACKED SELECTORS */

.stacked {
    float: left;
    width: 490px;
}

.stacked select {
    width: 480px;
    height: 10.1em;
}

.stacked .selector-available, .stacked .selector-chosen {
    width: 480px;
}

.stacked .selector-available {
    margin-bottom: 0;
}

.stacked .selector-available input {
    width: 422px;
}

.stacked ul.selector-chooser {
    height: 22px;
    width: 50px;
    margin: 0 0 10px 40%;
    background-color: #eee;
    border-radius: 10px;
}

.stacked .selector-chooser li {
    float: left;
    padding: 3px 3px 3px 5px;
}

.stacked .selector-chooseall, .stacked .selector-clearall {
    display: none;
}

.stacked .selector-add {
    background: url(../img/selector-icons.svg) 0 -32px no-repeat;
    cursor: default;
}

.stacked .active.selector-add {
    background-position: 0 -48px;
    cursor: pointer;
}

.stacked .selector-remove {
    background: url(../img/selector-icons.svg) 0 0 no-repeat;
    cursor: default;
}

.stacked .active.selector-remove {
    background-position: 0 -16px;
    cursor: pointer;
}

.selector .help-icon {
    background: url(../img/icon-unknown.svg) 0 0 no-repeat;
    display: inline-block;
    vertical-align: middle;
    margin: -2px 0 0 2px;
    width: 13px;
    height: 13px;
}

.selector .selector-chosen .help-icon {
    background: url(../img/icon-unknown-alt.svg) 0 0 no-repeat;
}

.selector .search-label-icon {
    background: url(../img/search.svg) 0 0 no-repeat;
    display: inline-block;
    height: 18px;
    width: 18px;
}

/* DATE AND TIME */

p.datetime {
    line-height: 20px;
    margin: 0;
    padding: 0;
    color: #666;
    font-weight: bold;
}

.datetime span {
    white-space: nowrap;
    font-weight: normal;
    font-size: 11px;
    color: #ccc;
}

.datetime input, .form-row .datetime input.vDateField, .form-row .datetime input.vTimeField {
    min-width: 0;
    margin-left: 5px;
    margin-bottom: 4px;
}

table p.datetime {
    font-size: 11px;
    margin-left: 0;
    padding-left: 0;
}

.datetimeshortcuts .clock-icon, .datetimeshortcuts .date-icon {
    position: relative;
    display: inline-block;
    vertical-align: middle;
    height: 16px;
    width: 16px;
    overflow: hidden;
}

.datetimeshortcuts .clock-icon {
    background: url(../img/icon-clock.svg) 0 0 no-repeat;
}

.datetimeshortcuts a:focus .clock-icon,
.datetimeshortcuts a:hover .clock-icon {
    background-position: 0 -16px;
}

.datetimeshortcuts .date-icon {
    background: url(../img/icon-calendar.svg) 0 0 no-repeat;
    top: -1px;
}

.datetimeshortcuts a:focus .date-icon,
.datetimeshortcuts a:hover .date-icon {
    background-position: 0 -16px;
}

.timezonewarning {
    font-size: 11px;
    color: #999;
}

/* URL */

p.url {
    line-height: 20px;
    margin: 0;
    padding: 0;
    color: #666;
    font-size: 11px;
    font-weight: bold;
}

.url a {
    font-weight: normal;
}

/* FILE UPLOADS */

p.file-upload {
    line-height: 20px;
    margin: 0;
    padding: 0;
    color: #666;
    font-size: 11px;
    font-weight: bold;
}

.aligned p.file-upload {
    margin-left: 170px;
}

.file-upload a {
    font-weight: normal;
}

.file-upload .deletelink {
    margin-left: 5px;
}

span.clearable-file-input label {
    color: #333;
    font-size: 11px;
    display: inline;
    float: none;
}

/* CALENDARS & CLOCKS */

.calendarbox, .clockbox {
    margin: 5px auto;
    font-size: 12px;
    width: 19em;
    text-align: center;
    background: white;
    border: 1px solid #ddd;
    border-radius: 4px;
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.15);
    overflow: hidden;
    position: relative;
}

.clockbox {
    width: auto;
}

.calendar {
    margin: 0;
    padding: 0;
}

.calendar table {
    margin: 0;
    padding: 0;
    border-collapse: collapse;
    background: white;
    width: 100%;
}

.calendar caption, .calendarbox h2 {
    margin: 0;
    text-align: center;
    border-top: none;
    background: #f5dd5d;
    font-weight: 700;
    font-size: 12px;
    color: #333;
}

.calendar th {
    padding: 8px 5px;
    background: #f8f8f8;
    border-bottom: 1px solid #ddd;
    font-weight: 400;
    font-size: 12px;
    text-align: center;
    color: #666;
}

.calendar td {
    font-weight: 400;
    font-size: 12px;
    text-align: center;
    padding: 0;
    border-top: 1px solid #eee;
    border-bottom: none;
}

.calendar td.selected a {
    background: #79aec8;
    color: #fff;
}

.calendar td.nonday {
    background: #f8f8f8;
}

.calendar td.today a {
    font-weight: 700;
}

.calendar td a, .timelist a {
    display: block;
    font-weight: 400;
    padding: 6px;
    text-decoration: none;
    color: #444;
}

.calendar td a:focus, .timelist a:focus,
.calendar td a:hover, .timelist a:hover {
    background: #79aec8;
    color: white;
}

.calendar td a:active, .timelist a:active {
    background: #417690;
    color: white;
}

.calendarnav {
    font-size: 10px;
    text-align: center;
    color: #ccc;
    margin: 0;
    padding: 1px 3px;
}

.calendarnav a:link, #calendarnav a:visited,
#calendarnav a:focus, #calendarnav a:hover {
    color: #999;
}

.calendar-shortcuts {
    background: white;
    font-size: 11px;
    line-height: 11px;
    border-top: 1px solid #eee;
    padding: 8px 0;
    color: #ccc;
}

.calendarbox .calendarnav-previous, .calendarbox .calendarnav-next {
    display: block;
    position: absolute;
    top: 8px;
    width: 15px;
    height: 15px;
    text-indent: -9999px;
    padding: 0;
}

.calendarnav-previous {
    left: 10px;
    background: url(../img/calendar-icons.svg) 0 0 no-repeat;
}

.calendarbox .calendarnav-previous:focus,
.calendarbox .calendarnav-previous:hover {
    background-position: 0 -15px;
}

.calendarnav-next {
    right: 10px;
    background: url(../img/calendar-icons.svg) 0 -30px no-repeat;
}

.calendarbox .calendarnav-next:focus,
.calendarbox .calendarnav-next:hover {
    background-position: 0 -45px;
}

.calendar-cancel {
    margin: 0;
    padding: 4px 0;
    font-size: 12px;
    background: #eee;
    border-top: 1px solid #ddd;
    color: #333;
}

.calendar-cancel:focus, .calendar-cancel:hover {
    background: #ddd;
}

.calendar-cancel a {
    color: black;
    display: block;
}

ul.timelist, .timelist li {
    list-style-type: none;
    margin: 0;
    padding: 0;
}

.timelist a {
    padding: 2px;
}

/* EDIT INLINE */

.inline-deletelink {
    float: right;
    text-indent: -9999px;
    background: url(../img/inline-delete.svg) 0 0 no-repeat;
    width: 16px;
    height: 16px;
    border: 0px none;
}

.inline-deletelink:focus, .inline-deletelink:hover {
    cursor: pointer;
}

/* RELATED WIDGET WRAPPER */
.related-widget-wrapper {
    float: left;       /* display properly in form rows with multiple fields */
    overflow: hidden;  /* clear floated contents */
}

.related-widget-wrapper-link {
    opacity: 0.3;
}

.related-widget-wrapper-link:link {
    opacity: .8;
}

.related-widget-wrapper-link:link:focus,
.related-widget-wrapper-link:link:hover {
    opacity: 1;
}

select + .related-widget-wrapper-link,
.related-widget-wrapper-link + .related-widget-wrapper-link {
    margin-left: 7px;
}


================================================
FILE: src/static_cdn/admin/fonts/LICENSE.txt
================================================

                                 Apache License
                           Version 2.0, January 2004
                        http://www.apache.org/licenses/

   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

   1. Definitions.

      "License" shall mean the terms and conditions for use, reproduction,
      and distribution as defined by Sections 1 through 9 of this document.

      "Licensor" shall mean the copyright owner or entity authorized by
      the copyright owner that is granting the License.

      "Legal Entity" shall mean the union of the acting entity and all
      other entities that control, are controlled by, or are under common
      control with that entity. For the purposes of this definition,
      "control" means (i) the power, direct or indirect, to cause the
      direction or management of such entity, whether by contract or
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
      outstanding shares, or (iii) beneficial ownership of such entity.

      "You" (or "Your") shall mean an individual or Legal Entity
      exercising permissions granted by this License.

      "Source" form shall mean the preferred form for making modifications,
      including but not limited to software source code, documentation
      source, and configuration files.

      "Object" form shall mean any form resulting from mechanical
      transformation or translation of a Source form, including but
      not limited to compiled object code, generated documentation,
      and conversions to other media types.

      "Work" shall mean the work of authorship, whether in Source or
      Object form, made available under the License, as indicated by a
      copyright notice that is included in or attached to the work
      (an example is provided in the Appendix below).

      "Derivative Works" shall mean any work, whether in Source or Object
      form, that is based on (or derived from) the Work and for which the
      editorial revisions, annotations, elaborations, or other modifications
      represent, as a whole, an original work of authorship. For the purposes
      of this License, Derivative Works shall not include works that remain
      separable from, or merely link (or bind by name) to the interfaces of,
      the Work and Derivative Works thereof.

      "Contribution" shall mean any work of authorship, including
      the original version of the Work and any modifications or additions
      to that Work or Derivative Works thereof, that is intentionally
      submitted to Licensor for inclusion in the Work by the copyright owner
      or by an individual or Legal Entity authorized to submit on behalf of
      the copyright owner. For the purposes of this definition, "submitted"
      means any form of electronic, verbal, or written communication sent
      to the Licensor or its representatives, including but not limited to
      communication on electronic mailing lists, source code control systems,
      and issue tracking systems that are managed by, or on behalf of, the
      Licensor for the purpose of discussing and improving the Work, but
      excluding communication that is conspicuously marked or otherwise
      designated in writing by the copyright owner as "Not a Contribution."

      "Contributor" shall mean Licensor and any individual or Legal Entity
      on behalf of whom a Contribution has been received by Licensor and
      subsequently incorporated within the Work.

   2. Grant of Copyright License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      copyright license to reproduce, prepare Derivative Works of,
      publicly display, publicly perform, sublicense, and distribute the
      Work and such Derivative Works in Source or Object form.

   3. Grant of Patent License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      (except as stated in this section) patent license to make, have made,
      use, offer to sell, sell, import, and otherwise transfer the Work,
      where such license applies only to those patent claims licensable
      by such Contributor that are necessarily infringed by their
      Contribution(s) alone or by combination of their Contribution(s)
      with the Work to which such Contribution(s) was submitted. If You
      institute patent litigation against any entity (including a
      cross-claim or counterclaim in a lawsuit) alleging that the Work
      or a Contribution incorporated within the Work constitutes direct
      or contributory patent infringement, then any patent licenses
      granted to You under this License for that Work shall terminate
      as of the date such litigation is filed.

   4. Redistribution. You may reproduce and distribute copies of the
      Work or Derivative Works thereof in any medium, with or without
      modifications, and in Source or Object form, provided that You
      meet the following conditions:

      (a) You must give any other recipients of the Work or
          Derivative Works a copy of this License; and

      (b) You must cause any modified files to carry prominent notices
          stating that You changed the files; and

      (c) You must retain, in the Source form of any Derivative Works
          that You distribute, all copyright, patent, trademark, and
          attribution notices from the Source form of the Work,
          excluding those notices that do not pertain to any part of
          the Derivative Works; and

      (d) If the Work includes a "NOTICE" text file as part of its
          distribution, then any Derivative Works that You distribute must
          include a readable copy of the attribution notices contained
          within such NOTICE file, excluding those notices that do not
          pertain to any part of the Derivative Works, in at least one
          of the following places: within a NOTICE text file distributed
          as part of the Derivative Works; within the Source form or
          documentation, if provided along with the Derivative Works; or,
          within a display generated by the Derivative Works, if and
          wherever such third-party notices normally appear. The contents
          of the NOTICE file are for informational purposes only and
          do not modify the License. You may add Your own attribution
          notices within Derivative Works that You distribute, alongside
          or as an addendum to the NOTICE text from the Work, provided
          that such additional attribution notices cannot be construed
          as modifying the License.

      You may add Your own copyright statement to Your modifications and
      may provide additional or different license terms and conditions
      for use, reproduction, or distribution of Your modifications, or
      for any such Derivative Works as a whole, provided Your use,
      reproduction, and distribution of the Work otherwise complies with
      the conditions stated in this License.

   5. Submission of Contributions. Unless You explicitly state otherwise,
      any Contribution intentionally submitted for inclusion in the Work
      by You to the Licensor shall be under the terms and conditions of
      this License, without any additional terms or conditions.
      Notwithstanding the above, nothing herein shall supersede or modify
      the terms of any separate license agreement you may have executed
      with Licensor regarding such Contributions.

   6. Trademarks. This License does not grant permission to use the trade
      names, trademarks, service marks, or product names of the Licensor,
      except as required for reasonable and customary use in describing the
      origin of the Work and reproducing the content of the NOTICE file.

   7. Disclaimer of Warranty. Unless required by applicable law or
      agreed to in writing, Licensor provides the Work (and each
      Contributor provides its Contributions) on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
      implied, including, without limitation, any warranties or conditions
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
      PARTICULAR PURPOSE. You are solely responsible for determining the
      appropriateness of using or redistributing the Work and assume any
      risks associated with Your exercise of permissions under this License.

   8. Limitation of Liability. In no event and under no legal theory,
      whether in tort (including negligence), contract, or otherwise,
      unless required by applicable law (such as deliberate and grossly
      negligent acts) or agreed to in writing, shall any Contributor be
      liable to You for damages, including any direct, indirect, special,
      incidental, or consequential damages of any character arising as a
      result of this License or out of the use or inability to use the
      Work (including but not limited to damages for loss of goodwill,
      work stoppage, computer failure or malfunction, or any and all
      other commercial damages or losses), even if such Contributor
      has been advised of the possibility of such damages.

   9. Accepting Warranty or Additional Liability. While redistributing
      the Work or Derivative Works thereof, You may choose to offer,
      and charge a fee for, acceptance of support, warranty, indemnity,
      or other liability obligations and/or rights consistent with this
      License. However, in accepting such obligations, You may act only
      on Your own behalf and on Your sole responsibility, not on behalf
      of any other Contributor, and only if You agree to indemnify,
      defend, and hold each Contributor harmless for any liability
      incurred by, or claims asserted against, such Contributor by reason
      of your accepting any such warranty or additional liability.

   END OF TERMS AND CONDITIONS

   APPENDIX: How to apply the Apache License to your work.

      To apply the Apache License to your work, attach the following
      boilerplate notice, with the fields enclosed by brackets "[]"
      replaced with your own identifying information. (Don't include
      the brackets!)  The text should be enclosed in the appropriate
      comment syntax for the file format. We also recommend that a
      file or class name and description of purpose be included on the
      same "printed page" as the copyright notice for easier
      identification within third-party archives.

   Copyright [yyyy] [name of copyright owner]

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.


================================================
FILE: src/static_cdn/admin/fonts/README.txt
================================================
Roboto webfont source: https://www.google.com/fonts/specimen/Roboto
WOFF files extracted using https://github.com/majodev/google-webfonts-helper
Weights used in this project: Light (300), Regular (400), Bold (700)


================================================
FILE: src/static_cdn/admin/img/LICENSE
================================================
The MIT License (MIT)

Copyright (c) 2014 Code Charm Ltd

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.


================================================
FILE: src/static_cdn/admin/img/README.txt
================================================
All icons are taken from Font Awesome (http://fontawesome.io/) project.
The Font Awesome font is licensed under the SIL OFL 1.1:
- https://scripts.sil.org/OFL

SVG icons source: https://github.com/encharm/Font-Awesome-SVG-PNG
Font-Awesome-SVG-PNG is licensed under the MIT license (see file license
in current folder).


================================================
FILE: src/static_cdn/admin/js/SelectBox.js
================================================
(function($) {
    'use strict';
    var SelectBox = {
        cache: {},
        init: function(id) {
            var box = document.getElementById(id);
            var node;
            SelectBox.cache[id] = [];
            var cache = SelectBox.cache[id];
            var boxOptions = box.options;
            var boxOptionsLength = boxOptions.length;
            for (var i = 0, j = boxOptionsLength; i < j; i++) {
                node = boxOptions[i];
                cache.push({value: node.value, text: node.text, displayed: 1});
            }
        },
        redisplay: function(id) {
            // Repopulate HTML select box from cache
            var box = document.getElementById(id);
            var node;
            $(box).empty(); // clear all options
            var new_options = box.outerHTML.slice(0, -9);  // grab just the opening tag
            var cache = SelectBox.cache[id];
            for (var i = 0, j = cache.length; i < j; i++) {
                node = cache[i];
                if (node.displayed) {
                    var new_option = new Option(node.text, node.value, false, false);
                    // Shows a tooltip when hovering over the option
                    new_option.setAttribute("title", node.text);
                    new_options += new_option.outerHTML;
                }
            }
            new_options += '</select>';
            box.outerHTML = new_options;
        },
        filter: function(id, text) {
            // Redisplay the HTML select box, displaying only the choices containing ALL
            // the words in text. (It's an AND search.)
            var tokens = text.toLowerCase().split(/\s+/);
            var node, token;
            var cache = SelectBox.cache[id];
            for (var i = 0, j = cache.length; i < j; i++) {
                node = cache[i];
                node.displayed = 1;
                var node_text = node.text.toLowerCase();
                var numTokens = tokens.length;
                for (var k = 0; k < numTokens; k++) {
                    token = tokens[k];
                    if (node_text.indexOf(token) === -1) {
                        node.displayed = 0;
                        break;  // Once the first token isn't found we're done
                    }
                }
            }
            SelectBox.redisplay(id);
        },
        delete_from_cache: function(id, value) {
            var node, delete_index = null;
            var cache = SelectBox.cache[id];
            for (var i = 0, j = cache.length; i < j; i++) {
                node = cache[i];
                if (node.value === value) {
                    delete_index = i;
                    break;
                }
            }
            cache.splice(delete_index, 1);
        },
        add_to_cache: function(id, option) {
            SelectBox.cache[id].push({value: option.value, text: option.text, displayed: 1});
        },
        cache_contains: function(id, value) {
            // Check if an item is contained in the cache
            var node;
            var cache = SelectBox.cache[id];
            for (var i = 0, j = cache.length; i < j; i++) {
                node = cache[i];
                if (node.value === value) {
                    return true;
                }
            }
            return false;
        },
        move: function(from, to) {
            var from_box = document.getElementById(from);
            var option;
            var boxOptions = from_box.options;
            var boxOptionsLength = boxOptions.length;
            for (var i = 0, j = boxOptionsLength; i < j; i++) {
                option = boxOptions[i];
                var option_value = option.value;
                if (option.selected && SelectBox.cache_contains(from, option_value)) {
                    SelectBox.add_to_cache(to, {value: option_value, text: option.text, displayed: 1});
                    SelectBox.delete_from_cache(from, option_value);
                }
            }
            SelectBox.redisplay(from);
            SelectBox.redisplay(to);
        },
        move_all: function(from, to) {
            var from_box = document.getElementById(from);
            var option;
            var boxOptions = from_box.options;
            var boxOptionsLength = boxOptions.length;
            for (var i = 0, j = boxOptionsLength; i < j; i++) {
                option = boxOptions[i];
                var option_value = option.value;
                if (SelectBox.cache_contains(from, option_value)) {
                    SelectBox.add_to_cache(to, {value: option_value, text: option.text, displayed: 1});
                    SelectBox.delete_from_cache(from, option_value);
                }
            }
            SelectBox.redisplay(from);
            SelectBox.redisplay(to);
        },
        sort: function(id) {
            SelectBox.cache[id].sort(function(a, b) {
                a = a.text.toLowerCase();
                b = b.text.toLowerCase();
                try {
                    if (a > b) {
                        return 1;
                    }
                    if (a < b) {
                        return -1;
                    }
                }
                catch (e) {
                    // silently fail on IE 'unknown' exception
                }
                return 0;
            } );
        },
        select_all: function(id) {
            var box = document.getElementById(id);
            var boxOptions = box.options;
            var boxOptionsLength = boxOptions.length;
            for (var i = 0; i < boxOptionsLength; i++) {
                boxOptions[i].selected = 'selected';
            }
        }
    };
    window.SelectBox = SelectBox;
})(django.jQuery);


================================================
FILE: src/static_cdn/admin/js/SelectFilter2.js
================================================
/*global SelectBox, gettext, interpolate, quickElement, SelectFilter*/
/*
SelectFilter2 - Turns a multiple-select box into a filter interface.

Requires jQuery, core.js, and SelectBox.js.
*/
(function($) {
    'use strict';
    function findForm(node) {
        // returns the node of the form containing the given node
        if (node.tagName.toLowerCase() !== 'form') {
            return findForm(node.parentNode);
        }
        return node;
    }

    window.SelectFilter = {
        init: function(field_id, field_name, is_stacked) {
            if (field_id.match(/__prefix__/)) {
                // Don't initialize on empty forms.
                return;
            }
            var from_box = document.getElementById(field_id);
            from_box.id += '_from'; // change its ID
            from_box.className = 'filtered';

            var ps = from_box.parentNode.getElementsByTagName('p');
            for (var i = 0; i < ps.length; i++) {
                if (ps[i].className.indexOf("info") !== -1) {
                    // Remove <p class="info">, because it just gets in the way.
                    from_box.parentNode.removeChild(ps[i]);
                } else if (ps[i].className.indexOf("help") !== -1) {
                    // Move help text up to the top so it isn't below the select
                    // boxes or wrapped off on the side to the right of the add
                    // button:
                    from_box.parentNode.insertBefore(ps[i], from_box.parentNode.firstChild);
                }
            }

            // <div class="selector"> or <div class="selector stacked">
            var selector_div = quickElement('div', from_box.parentNode);
            selector_div.className = is_stacked ? 'selector stacked' : 'selector';

            // <div class="selector-available">
            var selector_available = quickElement('div', selector_div);
            selector_available.className = 'selector-available';
            var title_available = quickElement('h2', selector_available, interpolate(gettext('Available %s') + ' ', [field_name]));
            quickElement(
                'span', title_available, '',
                'class', 'help help-tooltip help-icon',
                'title', interpolate(
                    gettext(
                        'This is the list of available %s. You may choose some by ' +
                        'selecting them in the box below and then clicking the ' +
                        '"Choose" arrow between the two boxes.'
                    ),
                    [field_name]
                )
            );

            var filter_p = quickElement('p', selector_available, '', 'id', field_id + '_filter');
            filter_p.className = 'selector-filter';

            var search_filter_label = quickElement('label', filter_p, '', 'for', field_id + '_input');

            quickElement(
                'span', search_filter_label, '',
                'class', 'help-tooltip search-label-icon',
                'title', interpolate(gettext("Type into this box to filter down the list of available %s."), [field_name])
            );

            filter_p.appendChild(document.createTextNode(' '));

            var filter_input = quickElement('input', filter_p, '', 'type', 'text', 'placeholder', gettext("Filter"));
            filter_input.id = field_id + '_input';

            selector_available.appendChild(from_box);
            var choose_all = quickElement('a', selector_available, gettext('Choose all'), 'title', interpolate(gettext('Click to choose all %s at once.'), [field_name]), 'href', '#', 'id', field_id + '_add_all_link');
            choose_all.className = 'selector-chooseall';

            // <ul class="selector-chooser">
            var selector_chooser = quickElement('ul', selector_div);
            selector_chooser.className = 'selector-chooser';
            var add_link = quickElement('a', quickElement('li', selector_chooser), gettext('Choose'), 'title', gettext('Choose'), 'href', '#', 'id', field_id + '_add_link');
            add_link.className = 'selector-add';
            var remove_link = quickElement('a', quickElement('li', selector_chooser), gettext('Remove'), 'title', gettext('Remove'), 'href', '#', 'id', field_id + '_remove_link');
            remove_link.className = 'selector-remove';

            // <div class="selector-chosen">
            var selector_chosen = quickElement('div', selector_div);
            selector_chosen.className = 'selector-chosen';
            var title_chosen = quickElement('h2', selector_chosen, interpolate(gettext('Chosen %s') + ' ', [field_name]));
            quickElement(
                'span', title_chosen, '',
                'class', 'help help-tooltip help-icon',
                'title', interpolate(
                    gettext(
                        'This is the list of chosen %s. You may remove some by ' +
                        'selecting them in the box below and then clicking the ' +
                        '"Remove" arrow between the two boxes.'
                    ),
                    [field_name]
                )
            );

            var to_box = quickElement('select', selector_chosen, '', 'id', field_id + '_to', 'multiple', 'multiple', 'size', from_box.size, 'name', from_box.getAttribute('name'));
            to_box.className = 'filtered';
            var clear_all = quickElement('a', selector_chosen, gettext('Remove all'), 'title', interpolate(gettext('Click to remove all chosen %s at once.'), [field_name]), 'href', '#', 'id', field_id + '_remove_all_link');
            clear_all.className = 'selector-clearall';

            from_box.setAttribute('name', from_box.getAttribute('name') + '_old');

            // Set up the JavaScript event handlers for the select box filter interface
            var move_selection = function(e, elem, move_func, from, to) {
                if (elem.className.indexOf('active') !== -1) {
                    move_func(from, to);
                    SelectFilter.refresh_icons(field_id);
                }
                e.preventDefault();
            };
            choose_all.addEventListener('click', function(e) {
                move_selection(e, this, SelectBox.move_all, field_id + '_from', field_id + '_to');
            });
            add_link.addEventListener('click', function(e) {
                move_selection(e, this, SelectBox.move, field_id + '_from', field_id + '_to');
            });
            remove_link.addEventListener('click', function(e) {
                move_selection(e, this, SelectBox.move, field_id + '_to', field_id + '_from');
            });
            clear_all.addEventListener('click', function(e) {
                move_selection(e, this, SelectBox.move_all, field_id + '_to', field_id + '_from');
            });
            filter_input.addEventListener('keypress', function(e) {
                SelectFilter.filter_key_press(e, field_id);
            });
            filter_input.addEventListener('keyup', function(e) {
                SelectFilter.filter_key_up(e, field_id);
            });
            filter_input.addEventListener('keydown', function(e) {
                SelectFilter.filter_key_down(e, field_id);
            });
            selector_div.addEventListener('change', function(e) {
                if (e.target.tagName === 'SELECT') {
                    SelectFilter.refresh_icons(field_id);
                }
            });
            selector_div.addEventListener('dblclick', function(e) {
                if (e.target.tagName === 'OPTION') {
                    if (e.target.closest('select').id === field_id + '_to') {
                        SelectBox.move(field_id + '_to', field_id + '_from');
                    } else {
                        SelectBox.move(field_id + '_from', field_id + '_to');
                    }
                    SelectFilter.refresh_icons(field_id);
                }
            });
            findForm(from_box).addEventListener('submit', function() {
                SelectBox.select_all(field_id + '_to');
            });
            SelectBox.init(field_id + '_from');
            SelectBox.init(field_id + '_to');
            // Move selected from_box options to to_box
            SelectBox.move(field_id + '_from', field_id + '_to');

            if (!is_stacked) {
                // In horizontal mode, give the same height to the two boxes.
                var j_from_box = $('#' + field_id + '_from');
                var j_to_box = $('#' + field_id + '_to');
                j_to_box.height($(filter_p).outerHeight() + j_from_box.outerHeight());
            }

            // Initial icon refresh
            SelectFilter.refresh_icons(field_id);
        },
        any_selected: function(field) {
            var any_selected = false;
            try {
                // Temporarily add the required attribute and check validity.
                // This is much faster in WebKit browsers than the fallback.
                field.attr('required', 'required');
                any_selected = field.is(':valid');
                field.removeAttr('required');
            } catch (e) {
                // Browsers that don't support :valid (IE < 10)
                any_selected = field.find('option:selected').length > 0;
            }
            return any_selected;
        },
        refresh_icons: function(field_id) {
            var from = $('#' + field_id + '_from');
            var to = $('#' + field_id + '_to');
            // Active if at least one item is selected
            $('#' + field_id + '_add_link').toggleClass('active', SelectFilter.any_selected(from));
            $('#' + field_id + '_remove_link').toggleClass('active', SelectFilter.any_selected(to));
            // Active if the corresponding box isn't empty
            $('#' + field_id + '_add_all_link').toggleClass('active', from.find('option').length > 0);
            $('#' + field_id + '_remove_all_link').toggleClass('active', to.find('option').length > 0);
        },
        filter_key_press: function(event, field_id) {
            var from = document.getElementById(field_id + '_from');
            // don't submit form if user pressed Enter
            if ((event.which && event.which === 13) || (event.keyCode && event.keyCode === 13)) {
                from.selectedIndex = 0;
                SelectBox.move(field_id + '_from', field_id + '_to');
                from.selectedIndex = 0;
                event.preventDefault();
                return false;
            }
        },
        filter_key_up: function(event, field_id) {
            var from = document.getElementById(field_id + '_from');
            var temp = from.selectedIndex;
            SelectBox.filter(field_id + '_from', document.getElementById(field_id + '_input').value);
            from.selectedIndex = temp;
            return true;
        },
        filter_key_down: function(event, field_id) {
            var from = document.getElementById(field_id + '_from');
            // right arrow -- move across
            if ((event.which && event.which === 39) || (event.keyCode && event.keyCode === 39)) {
                var old_index = from.selectedIndex;
                SelectBox.move(field_id + '_from', field_id + '_to');
                from.selectedIndex = (old_index === from.length) ? from.length - 1 : old_index;
                return false;
            }
            // down arrow -- wrap around
            if ((event.which && event.which === 40) || (event.keyCode && event.keyCode === 40)) {
                from.selectedIndex = (from.length === from.selectedIndex + 1) ? 0 : from.selectedIndex + 1;
            }
            // up arrow -- wrap around
            if ((event.which && event.which === 38) || (event.keyCode && event.keyCode === 38)) {
                from.selectedIndex = (from.selectedIndex === 0) ? from.length - 1 : from.selectedIndex - 1;
            }
            return true;
        }
    };

    window.addEventListener('load', function(e) {
        $('select.selectfilter, select.selectfilterstacked').each(function() {
            var $el = $(this),
                data = $el.data();
            SelectFilter.init($el.attr('id'), data.fieldName, parseInt(data.isStacked, 10));
        });
    });

})(django.jQuery);


================================================
FILE: src/static_cdn/admin/js/actions.js
================================================
/*global gettext, interpolate, ngettext*/
(function($) {
    'use strict';
    var lastChecked;

    $.fn.actions = function(opts) {
        var options = $.extend({}, $.fn.actions.defaults, opts);
        var actionCheckboxes = $(this);
        var list_editable_changed = false;
        var showQuestion = function() {
            $(options.acrossClears).hide();
            $(options.acrossQuestions).show();
            $(options.allContainer).hide();
        },
        showClear = function() {
            $(options.acrossClears).show();
            $(options.acrossQuestions).hide();
            $(options.actionContainer).toggleClass(options.selectedClass);
            $(options.allContainer).show();
            $(options.counterContainer).hide();
        },
        reset = function() {
            $(options.acrossClears).hide();
            $(options.acrossQuestions).hide();
            $(options.allContainer).hide();
            $(options.counterContainer).show();
        },
        clearAcross = function() {
            reset();
            $(options.acrossInput).val(0);
            $(options.actionContainer).removeClass(options.selectedClass);
        },
        checker = function(checked) {
            if (checked) {
                showQuestion();
            } else {
                reset();
            }
            $(actionCheckboxes).prop("checked", checked)
                .parent().parent().toggleClass(options.selectedClass, checked);
        },
        updateCounter = function() {
            var sel = $(actionCheckboxes).filter(":checked").length;
            // data-actions-icnt is defined in the generated HTML
            // and contains the total amount of objects in the queryset
            var actions_icnt = $('.action-counter').data('actionsIcnt');
            $(options.counterContainer).html(interpolate(
            ngettext('%(sel)s of %(cnt)s selected', '%(sel)s of %(cnt)s selected', sel), {
                sel: sel,
                cnt: actions_icnt
            }, true));
            $(options.allToggle).prop("checked", function() {
                var value;
                if (sel === actionCheckboxes.length) {
                    value = true;
                    showQuestion();
                } else {
                    value = false;
                    clearAcross();
          
Download .txt
gitextract_9q6h212s/

├── README.md
└── src/
    ├── account/
    │   ├── __init__.py
    │   ├── admin.py
    │   ├── api/
    │   │   ├── __init__.py
    │   │   ├── serializers.py
    │   │   ├── urls.py
    │   │   └── views.py
    │   ├── apps.py
    │   ├── backends.py
    │   ├── forms.py
    │   ├── migrations/
    │   │   ├── 0001_initial.py
    │   │   └── __init__.py
    │   ├── models.py
    │   ├── templates/
    │   │   └── account/
    │   │       ├── account.html
    │   │       ├── login.html
    │   │       ├── must_authenticate.html
    │   │       └── register.html
    │   ├── tests.py
    │   └── views.py
    ├── blog/
    │   ├── __init__.py
    │   ├── admin.py
    │   ├── api/
    │   │   ├── __init__.py
    │   │   ├── serializers.py
    │   │   ├── urls.py
    │   │   └── views.py
    │   ├── apps.py
    │   ├── forms.py
    │   ├── migrations/
    │   │   ├── 0001_initial.py
    │   │   └── __init__.py
    │   ├── models.py
    │   ├── templates/
    │   │   └── blog/
    │   │       ├── create_blog.html
    │   │       ├── detail_blog.html
    │   │       ├── edit_blog.html
    │   │       └── snippets/
    │   │           ├── blog_post_pagination.html
    │   │           └── blog_post_snippet.html
    │   ├── tests.py
    │   ├── urls.py
    │   ├── utils.py
    │   └── views.py
    ├── db.sqlite3
    ├── manage.py
    ├── mysite/
    │   ├── __init__.py
    │   ├── settings.py
    │   ├── urls.py
    │   └── wsgi.py
    ├── personal/
    │   ├── __init__.py
    │   ├── admin.py
    │   ├── apps.py
    │   ├── migrations/
    │   │   ├── 0001_initial.py
    │   │   ├── 0002_delete_question.py
    │   │   └── __init__.py
    │   ├── models.py
    │   ├── templates/
    │   │   └── personal/
    │   │       └── home.html
    │   ├── tests.py
    │   └── views.py
    ├── requirements.txt
    ├── static_cdn/
    │   └── admin/
    │       ├── css/
    │       │   ├── autocomplete.css
    │       │   ├── base.css
    │       │   ├── changelists.css
    │       │   ├── dashboard.css
    │       │   ├── fonts.css
    │       │   ├── forms.css
    │       │   ├── login.css
    │       │   ├── responsive.css
    │       │   ├── responsive_rtl.css
    │       │   ├── rtl.css
    │       │   ├── vendor/
    │       │   │   └── select2/
    │       │   │       ├── LICENSE-SELECT2.md
    │       │   │       └── select2.css
    │       │   └── widgets.css
    │       ├── fonts/
    │       │   ├── LICENSE.txt
    │       │   └── README.txt
    │       ├── img/
    │       │   ├── LICENSE
    │       │   └── README.txt
    │       └── js/
    │           ├── SelectBox.js
    │           ├── SelectFilter2.js
    │           ├── actions.js
    │           ├── admin/
    │           │   ├── DateTimeShortcuts.js
    │           │   └── RelatedObjectLookups.js
    │           ├── autocomplete.js
    │           ├── calendar.js
    │           ├── cancel.js
    │           ├── change_form.js
    │           ├── collapse.js
    │           ├── core.js
    │           ├── inlines.js
    │           ├── jquery.init.js
    │           ├── popup_response.js
    │           ├── prepopulate.js
    │           ├── prepopulate_init.js
    │           ├── timeparse.js
    │           ├── urlify.js
    │           └── vendor/
    │               ├── jquery/
    │               │   ├── LICENSE.txt
    │               │   └── jquery.js
    │               ├── select2/
    │               │   ├── LICENSE.md
    │               │   ├── i18n/
    │               │   │   ├── ar.js
    │               │   │   ├── az.js
    │               │   │   ├── bg.js
    │               │   │   ├── ca.js
    │               │   │   ├── cs.js
    │               │   │   ├── da.js
    │               │   │   ├── de.js
    │               │   │   ├── el.js
    │               │   │   ├── en.js
    │               │   │   ├── es.js
    │               │   │   ├── et.js
    │               │   │   ├── eu.js
    │               │   │   ├── fa.js
    │               │   │   ├── fi.js
    │               │   │   ├── fr.js
    │               │   │   ├── gl.js
    │               │   │   ├── he.js
    │               │   │   ├── hi.js
    │               │   │   ├── hr.js
    │               │   │   ├── hu.js
    │               │   │   ├── id.js
    │               │   │   ├── is.js
    │               │   │   ├── it.js
    │               │   │   ├── ja.js
    │               │   │   ├── km.js
    │               │   │   ├── ko.js
    │               │   │   ├── lt.js
    │               │   │   ├── lv.js
    │               │   │   ├── mk.js
    │               │   │   ├── ms.js
    │               │   │   ├── nb.js
    │               │   │   ├── nl.js
    │               │   │   ├── pl.js
    │               │   │   ├── pt-BR.js
    │               │   │   ├── pt.js
    │               │   │   ├── ro.js
    │               │   │   ├── ru.js
    │               │   │   ├── sk.js
    │               │   │   ├── sr-Cyrl.js
    │               │   │   ├── sr.js
    │               │   │   ├── sv.js
    │               │   │   ├── th.js
    │               │   │   ├── tr.js
    │               │   │   ├── uk.js
    │               │   │   ├── vi.js
    │               │   │   ├── zh-CN.js
    │               │   │   └── zh-TW.js
    │               │   └── select2.full.js
    │               └── xregexp/
    │                   ├── LICENSE.txt
    │                   └── xregexp.js
    └── templates/
        ├── base.html
        ├── registration/
        │   ├── password_change.html
        │   ├── password_change_done.html
        │   ├── password_reset_complete.html
        │   ├── password_reset_done.html
        │   ├── password_reset_email.html
        │   ├── password_reset_form.html
        │   └── password_reset_subject.txt
        └── snippets/
            ├── base_css.html
            ├── footer.html
            └── header.html
Download .txt
SYMBOL INDEX (279 symbols across 39 files)

FILE: src/account/admin.py
  class AccountAdmin (line 6) | class AccountAdmin(UserAdmin):

FILE: src/account/api/serializers.py
  class RegistrationSerializer (line 6) | class RegistrationSerializer(serializers.ModelSerializer):
    class Meta (line 10) | class Meta:
    method save (line 18) | def	save(self):
  class AccountPropertiesSerializer (line 33) | class AccountPropertiesSerializer(serializers.ModelSerializer):
    class Meta (line 35) | class Meta:
  class ChangePasswordSerializer (line 42) | class ChangePasswordSerializer(serializers.Serializer):

FILE: src/account/api/views.py
  function registration_view (line 21) | def registration_view(request):
  function validate_email (line 51) | def validate_email(email):
  function validate_username (line 60) | def validate_username(username):
  function account_properties_view (line 76) | def account_properties_view(request):
  function update_account_view (line 94) | def update_account_view(request):
  class ObtainAuthTokenView (line 115) | class ObtainAuthTokenView(APIView):
    method post (line 120) | def post(self, request):
  function does_account_exist_view (line 147) | def does_account_exist_view(request):
  class ChangePasswordView (line 161) | class ChangePasswordView(UpdateAPIView):
    method get_object (line 168) | def get_object(self, queryset=None):
    method update (line 172) | def update(self, request, *args, **kwargs):

FILE: src/account/apps.py
  class AccountConfig (line 4) | class AccountConfig(AppConfig):

FILE: src/account/backends.py
  class CaseInsensitiveModelBackend (line 5) | class CaseInsensitiveModelBackend(ModelBackend):
    method authenticate (line 6) | def authenticate(self, request, username=None, password=None, **kwargs):

FILE: src/account/forms.py
  class RegistrationForm (line 8) | class RegistrationForm(UserCreationForm):
    class Meta (line 11) | class Meta:
    method clean_email (line 15) | def clean_email(self):
    method clean_username (line 23) | def clean_username(self):
  class AccountAuthenticationForm (line 32) | class AccountAuthenticationForm(forms.ModelForm):
    class Meta (line 36) | class Meta:
    method clean (line 40) | def clean(self):
  class AccountUpdateForm (line 48) | class AccountUpdateForm(forms.ModelForm):
    class Meta (line 50) | class Meta:
    method clean_email (line 54) | def clean_email(self):
    method clean_username (line 62) | def clean_username(self):

FILE: src/account/migrations/0001_initial.py
  class Migration (line 6) | class Migration(migrations.Migration):

FILE: src/account/models.py
  class MyAccountManager (line 10) | class MyAccountManager(BaseUserManager):
    method create_user (line 11) | def create_user(self, email, username, password=None):
    method create_superuser (line 26) | def create_superuser(self, email, username, password):
  class Account (line 39) | class Account(AbstractBaseUser):
    method __str__ (line 55) | def __str__(self):
    method has_perm (line 59) | def has_perm(self, perm, obj=None):
    method has_module_perms (line 63) | def has_module_perms(self, app_label):
  function create_auth_token (line 68) | def create_auth_token(sender, instance=None, created=False, **kwargs):

FILE: src/account/views.py
  function registration_view (line 7) | def registration_view(request):
  function logout_view (line 27) | def logout_view(request):
  function login_view (line 32) | def login_view(request):
  function account_view (line 60) | def account_view(request):
  function must_authenticate_view (line 92) | def must_authenticate_view(request):

FILE: src/blog/api/serializers.py
  class BlogPostSerializer (line 15) | class BlogPostSerializer(serializers.ModelSerializer):
    class Meta (line 20) | class Meta:
    method get_username_from_author (line 25) | def get_username_from_author(self, blog_post):
    method validate_image_url (line 29) | def validate_image_url(self, blog_post):
  class BlogPostUpdateSerializer (line 39) | class BlogPostUpdateSerializer(serializers.ModelSerializer):
    class Meta (line 41) | class Meta:
    method validate (line 45) | def validate(self, blog_post):
  class BlogPostCreateSerializer (line 80) | class BlogPostCreateSerializer(serializers.ModelSerializer):
    class Meta (line 83) | class Meta:
    method save (line 88) | def save(self):

FILE: src/blog/api/views.py
  function api_detail_blog_view (line 25) | def api_detail_blog_view(request, slug):
  function api_update_blog_view (line 42) | def api_update_blog_view(request, slug):
  function api_is_author_of_blogpost (line 76) | def api_is_author_of_blogpost(request, slug):
  function api_delete_blog_view (line 96) | def api_delete_blog_view(request, slug):
  function api_create_blog_view (line 120) | def api_create_blog_view(request):
  class ApiBlogListView (line 154) | class ApiBlogListView(ListAPIView):

FILE: src/blog/apps.py
  class BlogConfig (line 4) | class BlogConfig(AppConfig):

FILE: src/blog/forms.py
  class CreateBlogPostForm (line 6) | class CreateBlogPostForm(forms.ModelForm):
    class Meta (line 8) | class Meta:
  class UpdateBlogPostForm (line 14) | class UpdateBlogPostForm(forms.ModelForm):
    class Meta (line 16) | class Meta:
    method save (line 20) | def save(self, commit=True):

FILE: src/blog/migrations/0001_initial.py
  class Migration (line 9) | class Migration(migrations.Migration):

FILE: src/blog/models.py
  function upload_location (line 9) | def upload_location(instance, filename, **kwargs):
  class BlogPost (line 16) | class BlogPost(models.Model):
    method __str__ (line 25) | def __str__(self):
  function submission_delete (line 29) | def submission_delete(sender, instance, **kwargs):
  function pre_save_blog_post_receiever (line 32) | def pre_save_blog_post_receiever(sender, instance, *args, **kwargs):

FILE: src/blog/utils.py
  function is_image_aspect_ratio_valid (line 4) | def is_image_aspect_ratio_valid(img_url):
  function is_image_size_valid (line 15) | def is_image_size_valid(img_url, mb_limit):

FILE: src/blog/views.py
  function create_blog_view (line 11) | def create_blog_view(request):
  function detail_blog_view (line 32) | def detail_blog_view(request, slug):
  function edit_blog_view (line 43) | def edit_blog_view(request, slug):
  function get_blog_queryset (line 76) | def get_blog_queryset(query=None):

FILE: src/manage.py
  function main (line 7) | def main():

FILE: src/personal/apps.py
  class PersonalConfig (line 4) | class PersonalConfig(AppConfig):

FILE: src/personal/migrations/0001_initial.py
  class Migration (line 6) | class Migration(migrations.Migration):

FILE: src/personal/migrations/0002_delete_question.py
  class Migration (line 6) | class Migration(migrations.Migration):

FILE: src/personal/views.py
  function home_screen_view (line 10) | def home_screen_view(request):

FILE: src/static_cdn/admin/js/SelectFilter2.js
  function findForm (line 9) | function findForm(node) {

FILE: src/static_cdn/admin/js/admin/RelatedObjectLookups.js
  function id_to_windowname (line 12) | function id_to_windowname(text) {
  function windowname_to_id (line 18) | function windowname_to_id(text) {
  function showAdminPopup (line 24) | function showAdminPopup(triggeringLink, name_regexp, add_popup) {
  function showRelatedObjectLookupPopup (line 40) | function showRelatedObjectLookupPopup(triggeringLink) {
  function dismissRelatedLookupPopup (line 44) | function dismissRelatedLookupPopup(win, chosenId) {
  function showRelatedObjectPopup (line 55) | function showRelatedObjectPopup(triggeringLink) {
  function updateRelatedObjectLinks (line 59) | function updateRelatedObjectLinks(triggeringLink) {
  function dismissAddRelatedObjectPopup (line 76) | function dismissAddRelatedObjectPopup(win, newId, newRepr) {
  function dismissChangeRelatedObjectPopup (line 101) | function dismissChangeRelatedObjectPopup(win, objId, newRepr, newId) {
  function dismissDeleteRelatedObjectPopup (line 120) | function dismissDeleteRelatedObjectPopup(win, objId) {

FILE: src/static_cdn/admin/js/calendar.js
  function calendarMonth (line 103) | function calendarMonth(y, m) {
  function Calendar (line 148) | function Calendar(div_id, callback, selected) {

FILE: src/static_cdn/admin/js/core.js
  function quickElement (line 8) | function quickElement() {
  function removeChildren (line 24) | function removeChildren(a) {
  function findPosX (line 35) | function findPosX(obj) {
  function findPosY (line 53) | function findPosY(obj) {
  function getStyle (line 199) | function getStyle(oElm, strCssRule) {

FILE: src/static_cdn/admin/js/timeparse.js
  function parseTimeString (line 93) | function parseTimeString(s) {

FILE: src/static_cdn/admin/js/urlify.js
  function downcode (line 153) | function downcode(slug) {
  function URLify (line 161) | function URLify(s, num_chars, allowUnicode) {

FILE: src/static_cdn/admin/js/vendor/jquery/jquery.js
  function DOMEval (line 97) | function DOMEval( code, doc, node ) {
  function toType (line 115) | function toType( obj ) {
  function isArrayLike (line 484) | function isArrayLike( obj ) {
  function Sizzle (line 716) | function Sizzle( selector, context, results, seed ) {
  function createCache (line 855) | function createCache() {
  function markFunction (line 873) | function markFunction( fn ) {
  function assert (line 882) | function assert( fn ) {
  function addHandle (line 904) | function addHandle( attrs, handler ) {
  function siblingCheck (line 919) | function siblingCheck( a, b ) {
  function createInputPseudo (line 945) | function createInputPseudo( type ) {
  function createButtonPseudo (line 956) | function createButtonPseudo( type ) {
  function createDisabledPseudo (line 967) | function createDisabledPseudo( disabled ) {
  function createPositionalPseudo (line 1023) | function createPositionalPseudo( fn ) {
  function testContext (line 1046) | function testContext( context ) {
  function setFilters (line 2128) | function setFilters() {}
  function toSelector (line 2199) | function toSelector( tokens ) {
  function addCombinator (line 2209) | function addCombinator( matcher, combinator, base ) {
  function elementMatcher (line 2273) | function elementMatcher( matchers ) {
  function multipleContexts (line 2287) | function multipleContexts( selector, contexts, results ) {
  function condense (line 2296) | function condense( unmatched, map, filter, context, xml ) {
  function setMatcher (line 2317) | function setMatcher( preFilter, selector, matcher, postFilter, postFinde...
  function matcherFromTokens (line 2410) | function matcherFromTokens( tokens ) {
  function matcherFromGroupMatchers (line 2468) | function matcherFromGroupMatchers( elementMatchers, setMatchers ) {
  function nodeName (line 2804) | function nodeName( elem, name ) {
  function winnow (line 2814) | function winnow( elements, qualifier, not ) {
  function sibling (line 3109) | function sibling( cur, dir ) {
  function createOptions (line 3196) | function createOptions( options ) {
  function Identity (line 3421) | function Identity( v ) {
  function Thrower (line 3424) | function Thrower( ex ) {
  function adoptValue (line 3428) | function adoptValue( value, resolve, reject, noValue ) {
  function resolve (line 3521) | function resolve( depth, deferred, handler, special ) {
  function completed (line 3886) | function completed() {
  function fcamelCase (line 3981) | function fcamelCase( all, letter ) {
  function camelCase (line 3988) | function camelCase( string ) {
  function Data (line 4005) | function Data() {
  function getData (line 4174) | function getData( data ) {
  function dataAttr (line 4199) | function dataAttr( elem, key, data ) {
  function adjustCSS (line 4512) | function adjustCSS( elem, prop, valueParts, tween ) {
  function getDefaultDisplay (line 4579) | function getDefaultDisplay( elem ) {
  function showHide (line 4602) | function showHide( elements, show ) {
  function getAll (line 4703) | function getAll( context, tag ) {
  function setGlobalEval (line 4728) | function setGlobalEval( elems, refElements ) {
  function buildFragment (line 4744) | function buildFragment( elems, context, scripts, selection, ignored ) {
  function returnTrue (line 4867) | function returnTrue() {
  function returnFalse (line 4871) | function returnFalse() {
  function safeActiveElement (line 4877) | function safeActiveElement() {
  function on (line 4883) | function on( elem, types, selector, data, fn, one ) {
  function manipulationTarget (line 5611) | function manipulationTarget( elem, content ) {
  function disableScript (line 5622) | function disableScript( elem ) {
  function restoreScript (line 5626) | function restoreScript( elem ) {
  function cloneCopyEvent (line 5636) | function cloneCopyEvent( src, dest ) {
  function fixInput (line 5671) | function fixInput( src, dest ) {
  function domManip (line 5684) | function domManip( collection, args, callback, ignored ) {
  function remove (line 5774) | function remove( elem, selector, keepData ) {
  function computeStyleTests (line 6067) | function computeStyleTests() {
  function roundPixelMeasures (line 6109) | function roundPixelMeasures( measure ) {
  function curCSS (line 6154) | function curCSS( elem, name, computed ) {
  function addGetHookIf (line 6207) | function addGetHookIf( conditionFn, hookFn ) {
  function vendorPropName (line 6244) | function vendorPropName( name ) {
  function finalPropName (line 6265) | function finalPropName( name ) {
  function setPositiveNumber (line 6273) | function setPositiveNumber( elem, value, subtract ) {
  function boxModelAdjustment (line 6285) | function boxModelAdjustment( elem, dimension, box, isBorderBox, styles, ...
  function getWidthOrHeight (line 6350) | function getWidthOrHeight( elem, dimension, extra ) {
  function Tween (line 6683) | function Tween( elem, options, prop, end, easing ) {
  function schedule (line 6806) | function schedule() {
  function createFxNow (line 6819) | function createFxNow() {
  function genFx (line 6827) | function genFx( type, includeWidth ) {
  function createTween (line 6847) | function createTween( value, prop, animation ) {
  function defaultPrefilter (line 6861) | function defaultPrefilter( elem, props, opts ) {
  function propFilter (line 7033) | function propFilter( props, specialEasing ) {
  function Animation (line 7070) | function Animation( elem, properties, options ) {
  function stripAndCollapse (line 7785) | function stripAndCollapse( value ) {
  function getClass (line 7791) | function getClass( elem ) {
  function classesToArray (line 7795) | function classesToArray( value ) {
  function buildParams (line 8417) | function buildParams( prefix, obj, traditional, add ) {
  function addToPrefiltersOrTransports (line 8567) | function addToPrefiltersOrTransports( structure ) {
  function inspectPrefiltersOrTransports (line 8601) | function inspectPrefiltersOrTransports( structure, options, originalOpti...
  function ajaxExtend (line 8630) | function ajaxExtend( target, src ) {
  function ajaxHandleResponses (line 8650) | function ajaxHandleResponses( s, jqXHR, responses ) {
  function ajaxConvert (line 8708) | function ajaxConvert( s, response, jqXHR, isSuccess ) {
  function done (line 9221) | function done( status, nativeStatusText, responses, headers ) {

FILE: src/static_cdn/admin/js/vendor/select2/i18n/cs.js
  function e (line 3) | function e(e,t){switch(e){case 2:return t?"dva":"dvě";case 3:return"tři"...

FILE: src/static_cdn/admin/js/vendor/select2/i18n/hr.js
  function e (line 3) | function e(e){var t=" "+e+" znak";return e%10<5&&e%10>0&&(e%100<5||e%100...

FILE: src/static_cdn/admin/js/vendor/select2/i18n/lt.js
  function e (line 3) | function e(e,t,n,r){return e%10===1&&(e%100<11||e%100>19)?t:e%10>=2&&e%1...

FILE: src/static_cdn/admin/js/vendor/select2/i18n/lv.js
  function e (line 3) | function e(e,t,n,r){return e===11?t:e%10===1?n:r}

FILE: src/static_cdn/admin/js/vendor/select2/i18n/ru.js
  function e (line 3) | function e(e,t,n,r){return e%10<5&&e%10>0&&e%100<5||e%100>20?e%10>1?n:t:r}

FILE: src/static_cdn/admin/js/vendor/select2/i18n/sr-Cyrl.js
  function e (line 3) | function e(e,t,n,r){return e%10==1&&e%100!=11?t:e%10>=2&&e%10<=4&&(e%100...

FILE: src/static_cdn/admin/js/vendor/select2/i18n/sr.js
  function e (line 3) | function e(e,t,n,r){return e%10==1&&e%100!=11?t:e%10>=2&&e%10<=4&&(e%100...

FILE: src/static_cdn/admin/js/vendor/select2/i18n/uk.js
  function e (line 3) | function e(e,t,n,r){return e%100>10&&e%100<15?r:e%10===1?t:e%10>1&&e%10<...

FILE: src/static_cdn/admin/js/vendor/select2/select2.full.js
  function hasProp (line 53) | function hasProp(obj, prop) {
  function normalize (line 65) | function normalize(name, baseName) {
  function makeRequire (line 177) | function makeRequire(relName, forceSync) {
  function makeNormalize (line 194) | function makeNormalize(relName) {
  function makeLoad (line 200) | function makeLoad(depName) {
  function callDep (line 206) | function callDep(name) {
  function splitPrefix (line 223) | function splitPrefix(name) {
  function makeConfig (line 276) | function makeConfig(name) {
  function BaseConstructor (line 491) | function BaseConstructor () {
  function getMethods (line 508) | function getMethods (theClass) {
  function DecoratedClass (line 534) | function DecoratedClass () {
  function ctr (line 552) | function ctr () {
  function Results (line 764) | function Results ($element, options, dataAdapter) {
  function BaseSelection (line 1315) | function BaseSelection ($element, options) {
  function SingleSelection (line 1475) | function SingleSelection () {
  function MultipleSelection (line 1574) | function MultipleSelection ($element, options) {
  function Placeholder (line 1682) | function Placeholder (decorated, $element, options) {
  function AllowClear (line 1733) | function AllowClear () { }
  function Search (line 1832) | function Search (decorated, $element, options) {
  function EventRelay (line 2053) | function EventRelay () { }
  function Translation (line 2100) | function Translation (dict) {
  function BaseAdapter (line 2984) | function BaseAdapter ($element, options) {
  function SelectAdapter (line 3027) | function SelectAdapter ($element, options) {
  function ArrayAdapter (line 3313) | function ArrayAdapter ($element, options) {
  function onlyItem (line 3348) | function onlyItem (item) {
  function AjaxAdapter (line 3393) | function AjaxAdapter ($element, options) {
  function request (line 3454) | function request () {
  function Tags (line 3501) | function Tags (decorated, $element, options) {
  function wrapper (line 3540) | function wrapper (obj, child) {
  function Tokenizer (line 3627) | function Tokenizer (decorated, $element, options) {
  function createAndSelect (line 3647) | function createAndSelect (data) {
  function select (line 3670) | function select (data) {
  function MinimumInputLength (line 3744) | function MinimumInputLength (decorated, $e, options) {
  function MaximumInputLength (line 3775) | function MaximumInputLength (decorated, $e, options) {
  function MaximumSelectionLength (line 3807) | function MaximumSelectionLength (decorated, $e, options) {
  function Dropdown (line 3840) | function Dropdown ($element, options) {
  function Search (line 3883) | function Search () { }
  function HidePlaceholder (line 3984) | function HidePlaceholder (decorated, $element, options, dataAdapter) {
  function InfiniteScroll (line 4027) | function InfiniteScroll (decorated, $element, options, dataAdapter) {
  function AttachBody (line 4118) | function AttachBody (decorated, $element, options) {
  function countResults (line 4340) | function countResults (data) {
  function MinimumResultsForSearch (line 4356) | function MinimumResultsForSearch (decorated, $element, options, dataAdap...
  function SelectOnClose (line 4380) | function SelectOnClose () { }
  function CloseOnSelect (line 4431) | function CloseOnSelect () { }
  function Defaults (line 4561) | function Defaults () {
  function stripDiacritics (line 4808) | function stripDiacritics (text) {
  function matcher (line 4817) | function matcher (params, data) {
  function Options (line 4912) | function Options (options, $element) {
  function syncCssClasses (line 5645) | function syncCssClasses ($dest, $src, adapter) {
  function _containerAdapter (line 5691) | function _containerAdapter (clazz) {
  function ContainerCSS (line 5695) | function ContainerCSS () { }
  function _dropdownAdapter (line 5748) | function _dropdownAdapter (clazz) {
  function DropdownCSS (line 5752) | function DropdownCSS () { }
  function InitSelection (line 5803) | function InitSelection (decorated, $element, options) {
  function InputData (line 5846) | function InputData (decorated, $element, options) {
  function getSelected (line 5864) | function getSelected (data, selectedIds) {
  function oldMatcher (line 5974) | function oldMatcher (matcher) {
  function Query (line 6017) | function Query (decorated, $element, options) {
  function AttachContainer (line 6044) | function AttachContainer (decorated, $element, options) {
  function StopPropagation (line 6063) | function StopPropagation () { }
  function StopPropagation (line 6102) | function StopPropagation () { }
  function handler (line 6230) | function handler(event) {
  function nullLowestDelta (line 6343) | function nullLowestDelta() {
  function shouldAdjustOldDeltas (line 6347) | function shouldAdjustOldDeltas(orgEvent, absDelta) {

FILE: src/static_cdn/admin/js/vendor/xregexp/xregexp.js
  function augment (line 102) | function augment(regex, captureNames, isNative) {
  function getNativeFlags (line 120) | function getNativeFlags(regex) {
  function copy (line 139) | function copy(regex, addFlags, removeFlags) {
  function lastIndexOf (line 167) | function lastIndexOf(array, value) {
  function isType (line 187) | function isType(value, type) {
  function prepareOptions (line 197) | function prepareOptions(value) {
  function runTokens (line 219) | function runTokens(pattern, pos, scope, context) {
  function setExtensibility (line 255) | function setExtensibility(on) {
  function setNatives (line 265) | function setNatives(on) {
  function slug (line 1296) | function slug(name) {
  function expand (line 1301) | function expand(str) {
  function pad4 (line 1306) | function pad4(str) {
  function dec (line 1314) | function dec(hex) {
  function hex (line 1319) | function hex(dec) {
  function invert (line 1324) | function invert(range) {
  function cacheInversion (line 1348) | function cacheInversion(item) {
  function row (line 1874) | function row(value, name, start, end) {
  function deanchor (line 2064) | function deanchor(pattern) {
  function asXRegExp (line 2079) | function asXRegExp(value) {
  function extend (line 2217) | function extend(a, b) {
Condensed preview — 155 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (957K chars).
[
  {
    "path": "README.md",
    "chars": 1273,
    "preview": "<a href=\"https://codingwithmitch.com/courses/build-a-rest-api/\"><img class='header-img' src='https://codingwithmitch.s3."
  },
  {
    "path": "src/account/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "src/account/admin.py",
    "chars": 448,
    "preview": "from django.contrib import admin\nfrom django.contrib.auth.admin import UserAdmin\nfrom account.models import Account\n\n\ncl"
  },
  {
    "path": "src/account/api/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "src/account/api/serializers.py",
    "chars": 1158,
    "preview": "from rest_framework import serializers\n\nfrom account.models import Account\n\n\nclass RegistrationSerializer(serializers.Mo"
  },
  {
    "path": "src/account/api/urls.py",
    "chars": 721,
    "preview": "from django.urls import path\nfrom account.api.views import(\n\tregistration_view,\n\tObtainAuthTokenView,\n\taccount_propertie"
  },
  {
    "path": "src/account/api/views.py",
    "chars": 5902,
    "preview": "from rest_framework import status\nfrom rest_framework.response import Response\nfrom rest_framework.decorators import api"
  },
  {
    "path": "src/account/apps.py",
    "chars": 89,
    "preview": "from django.apps import AppConfig\n\n\nclass AccountConfig(AppConfig):\n    name = 'account'\n"
  },
  {
    "path": "src/account/backends.py",
    "chars": 921,
    "preview": "from django.contrib.auth import get_user_model\nfrom django.contrib.auth.backends import ModelBackend\n\n\nclass CaseInsensi"
  },
  {
    "path": "src/account/forms.py",
    "chars": 2053,
    "preview": "from django import forms\nfrom django.contrib.auth.forms import UserCreationForm\nfrom django.contrib.auth import authenti"
  },
  {
    "path": "src/account/migrations/0001_initial.py",
    "chars": 1226,
    "preview": "# Generated by Django 2.2.2 on 2019-06-24 20:53\n\nfrom django.db import migrations, models\n\n\nclass Migration(migrations.M"
  },
  {
    "path": "src/account/migrations/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "src/account/models.py",
    "chars": 2105,
    "preview": "from django.db import models\nfrom django.contrib.auth.models import AbstractBaseUser, BaseUserManager\n\nfrom django.conf "
  },
  {
    "path": "src/account/templates/account/account.html",
    "chars": 2429,
    "preview": "{% extends 'base.html' %}\n\n\n{% block content %}\n\n<style type=\"text/css\">\n  .form-signin {\n  width: 100%;\n  max-width: 33"
  },
  {
    "path": "src/account/templates/account/login.html",
    "chars": 1781,
    "preview": "{% extends 'base.html' %}\n{% load static %}\n\n{% block content %}\n\n<style type=\"text/css\">\n  .form-signin {\n  width: 100%"
  },
  {
    "path": "src/account/templates/account/must_authenticate.html",
    "chars": 326,
    "preview": "{% extends 'base.html' %}\n\n\n{% block content %}\n\n<div class=\"container\">\n\t<div class=\"d-flex flex-column\">\n\t\t<p class=\"m"
  },
  {
    "path": "src/account/templates/account/register.html",
    "chars": 2079,
    "preview": "{% extends 'base.html' %}\n{% load static %}\n\n{% block content %}\n\n\n<style type=\"text/css\">\n  .form-signin {\n  width: 100"
  },
  {
    "path": "src/account/tests.py",
    "chars": 60,
    "preview": "from django.test import TestCase\n\n# Create your tests here.\n"
  },
  {
    "path": "src/account/views.py",
    "chars": 2187,
    "preview": "from django.shortcuts import render, redirect\nfrom django.contrib.auth import login, authenticate, logout\nfrom account.f"
  },
  {
    "path": "src/blog/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "src/blog/admin.py",
    "chars": 98,
    "preview": "from django.contrib import admin\n\nfrom blog.models import BlogPost\n\nadmin.site.register(BlogPost)\n"
  },
  {
    "path": "src/blog/api/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "src/blog/api/serializers.py",
    "chars": 3958,
    "preview": "from rest_framework import serializers\nfrom blog.models import BlogPost\n\nimport os\nfrom django.conf import settings\nfrom"
  },
  {
    "path": "src/blog/api/urls.py",
    "chars": 591,
    "preview": "from django.urls import path\nfrom blog.api.views import(\n\tapi_detail_blog_view,\n\tapi_update_blog_view,\n\tapi_delete_blog_"
  },
  {
    "path": "src/blog/api/views.py",
    "chars": 5526,
    "preview": "from rest_framework import status\nfrom rest_framework.response import Response\nfrom rest_framework.decorators import api"
  },
  {
    "path": "src/blog/apps.py",
    "chars": 83,
    "preview": "from django.apps import AppConfig\n\n\nclass BlogConfig(AppConfig):\n    name = 'blog'\n"
  },
  {
    "path": "src/blog/forms.py",
    "chars": 577,
    "preview": "from django import forms\n\nfrom blog.models import BlogPost\n\n\nclass CreateBlogPostForm(forms.ModelForm):\n\n\tclass Meta:\n\t\t"
  },
  {
    "path": "src/blog/migrations/0001_initial.py",
    "chars": 1184,
    "preview": "# Generated by Django 2.2.2 on 2019-06-24 20:55\n\nimport blog.models\nfrom django.conf import settings\nfrom django.db impo"
  },
  {
    "path": "src/blog/migrations/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "src/blog/models.py",
    "chars": 1377,
    "preview": "from django.db import models\n\nfrom django.utils.text import slugify\nfrom django.conf import settings\nfrom django.db.mode"
  },
  {
    "path": "src/blog/templates/blog/create_blog.html",
    "chars": 1213,
    "preview": "{% extends 'base.html' %}\n\n\n{% block content %}\n\n<style type=\"text/css\">\n  .create-form {\n\t    width: 100%;\n\t    max-wid"
  },
  {
    "path": "src/blog/templates/blog/detail_blog.html",
    "chars": 824,
    "preview": "{% extends 'base.html' %}\n\n\n{% block content %}\n<style type=\"text/css\">\n\t\n\t.card {\n\t\tmax-width: 700px;\n\t}\n\n\t.container{\n"
  },
  {
    "path": "src/blog/templates/blog/edit_blog.html",
    "chars": 2832,
    "preview": "{% extends 'base.html' %}\n\n\n{% block content %}\n<style type=\"text/css\">\n   .create-form {\n\t    width: 100%;\n\t    max-wid"
  },
  {
    "path": "src/blog/templates/blog/snippets/blog_post_pagination.html",
    "chars": 1127,
    "preview": "<!-- FILTERED Pagination -->\n<div class=\"d-flex flex-column m-auto pagination mt-5\">\n<nav aria-label=\"Page navigation\">\n"
  },
  {
    "path": "src/blog/templates/blog/snippets/blog_post_snippet.html",
    "chars": 1187,
    "preview": "<style type=\"text/css\">\n\t.card{\n\t\tmax-width: 700px;\n\t\twidth: 100%;\n\t}\n\t.card-body{\n\t\tpadding: 20px;\n\n\t}\n</style>\n\n{% if "
  },
  {
    "path": "src/blog/tests.py",
    "chars": 60,
    "preview": "from django.test import TestCase\n\n# Create your tests here.\n"
  },
  {
    "path": "src/blog/urls.py",
    "chars": 302,
    "preview": "from django.urls import path\nfrom blog.views import(\n\n\tcreate_blog_view,\n\tdetail_blog_view,\n\tedit_blog_view,\n\n)\n\napp_nam"
  },
  {
    "path": "src/blog/utils.py",
    "chars": 537,
    "preview": "import cv2\nimport os\n\ndef is_image_aspect_ratio_valid(img_url):\n\timg = cv2.imread(img_url)\n\tdimensions = tuple(img.shape"
  },
  {
    "path": "src/blog/views.py",
    "chars": 2027,
    "preview": "from django.shortcuts import render, redirect, get_object_or_404\nfrom django.db.models import Q\nfrom django.http import "
  },
  {
    "path": "src/manage.py",
    "chars": 626,
    "preview": "#!/usr/bin/env python\n\"\"\"Django's command-line utility for administrative tasks.\"\"\"\nimport os\nimport sys\n\n\ndef main():\n "
  },
  {
    "path": "src/mysite/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "src/mysite/settings.py",
    "chars": 4163,
    "preview": "\"\"\"\nDjango settings for mysite project.\n\nGenerated by 'django-admin startproject' using Django 2.2.2.\n\nFor more informat"
  },
  {
    "path": "src/mysite/urls.py",
    "chars": 2770,
    "preview": "\"\"\"mysite URL Configuration\n\nThe `urlpatterns` list routes URLs to views. For more information please see:\n    https://d"
  },
  {
    "path": "src/mysite/wsgi.py",
    "chars": 389,
    "preview": "\"\"\"\nWSGI config for mysite project.\n\nIt exposes the WSGI callable as a module-level variable named ``application``.\n\nFor"
  },
  {
    "path": "src/personal/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "src/personal/admin.py",
    "chars": 34,
    "preview": "from django.contrib import admin\n\n"
  },
  {
    "path": "src/personal/apps.py",
    "chars": 91,
    "preview": "from django.apps import AppConfig\n\n\nclass PersonalConfig(AppConfig):\n    name = 'personal'\n"
  },
  {
    "path": "src/personal/migrations/0001_initial.py",
    "chars": 819,
    "preview": "# Generated by Django 2.2.2 on 2019-06-19 18:01\n\nfrom django.db import migrations, models\n\n\nclass Migration(migrations.M"
  },
  {
    "path": "src/personal/migrations/0002_delete_question.py",
    "chars": 287,
    "preview": "# Generated by Django 2.2.2 on 2019-06-19 18:24\n\nfrom django.db import migrations\n\n\nclass Migration(migrations.Migration"
  },
  {
    "path": "src/personal/migrations/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "src/personal/models.py",
    "chars": 649,
    "preview": "from django.db import models\n\n# #Two tuple structure\n# #The first element in each tuple is the value that will be stored"
  },
  {
    "path": "src/personal/templates/personal/home.html",
    "chars": 2449,
    "preview": "{% extends 'base.html' %}\n{% load static %}\n\n{% block content %}\n<style type=\"text/css\">\n\t@media (max-width: 768px) { \n\t"
  },
  {
    "path": "src/personal/tests.py",
    "chars": 60,
    "preview": "from django.test import TestCase\n\n# Create your tests here.\n"
  },
  {
    "path": "src/personal/views.py",
    "chars": 949,
    "preview": "from django.shortcuts import render\nfrom operator import attrgetter\nfrom django.core.paginator import EmptyPage, PageNot"
  },
  {
    "path": "src/requirements.txt",
    "chars": 475,
    "preview": "amqp==2.5.0\nanyjson==0.3.3\nbilliard==3.6.0.0\ncelery==4.3.0\ncertifi==2019.6.16\nchardet==3.0.4\nDjango==2.2.2\ndjango-celery"
  },
  {
    "path": "src/static_cdn/admin/css/autocomplete.css",
    "chars": 8440,
    "preview": "select.admin-autocomplete {\n    width: 20em;\n}\n\n.select2-container--admin-autocomplete.select2-container {\n    min-heigh"
  },
  {
    "path": "src/static_cdn/admin/css/base.css",
    "chars": 16378,
    "preview": "/*\n    DJANGO Admin styles\n*/\n\n@import url(fonts.css);\n\nbody {\n    margin: 0;\n    padding: 0;\n    font-size: 14px;\n    f"
  },
  {
    "path": "src/static_cdn/admin/css/changelists.css",
    "chars": 6170,
    "preview": "/* CHANGELISTS */\n\n#changelist {\n    position: relative;\n    width: 100%;\n}\n\n#changelist table {\n    width: 100%;\n}\n\n.ch"
  },
  {
    "path": "src/static_cdn/admin/css/dashboard.css",
    "chars": 412,
    "preview": "/* DASHBOARD */\n\n.dashboard .module table th {\n    width: 100%;\n}\n\n.dashboard .module table td {\n    white-space: nowrap"
  },
  {
    "path": "src/static_cdn/admin/css/fonts.css",
    "chars": 423,
    "preview": "@font-face {\n    font-family: 'Roboto';\n    src: url('../fonts/Roboto-Bold-webfont.woff');\n    font-weight: 700;\n    fon"
  },
  {
    "path": "src/static_cdn/admin/css/forms.css",
    "chars": 8518,
    "preview": "@import url('widgets.css');\n\n/* FORM ROWS */\n\n.form-row {\n    overflow: hidden;\n    padding: 10px;\n    font-size: 13px;\n"
  },
  {
    "path": "src/static_cdn/admin/css/login.css",
    "chars": 1233,
    "preview": "/* LOGIN FORM */\n\nbody.login {\n    background: #f8f8f8;\n}\n\n.login #header {\n    height: auto;\n    padding: 15px 16px;\n  "
  },
  {
    "path": "src/static_cdn/admin/css/responsive.css",
    "chars": 17944,
    "preview": "/* Tablets */\n\ninput[type=\"submit\"], button {\n    -webkit-appearance: none;\n    appearance: none;\n}\n\n@media (max-width: "
  },
  {
    "path": "src/static_cdn/admin/css/responsive_rtl.css",
    "chars": 1921,
    "preview": "/* TABLETS */\n\n@media (max-width: 1024px) {\n    [dir=\"rtl\"] .colMS {\n        margin-right: 0;\n    }\n\n    [dir=\"rtl\"] #us"
  },
  {
    "path": "src/static_cdn/admin/css/rtl.css",
    "chars": 3808,
    "preview": "body {\n    direction: rtl;\n}\n\n/* LOGIN */\n\n.login .form-row {\n    float: right;\n}\n\n.login .form-row label {\n    float: r"
  },
  {
    "path": "src/static_cdn/admin/css/vendor/select2/LICENSE-SELECT2.md",
    "chars": 1124,
    "preview": "The MIT License (MIT)\n\nCopyright (c) 2012-2015 Kevin Brown, Igor Vaynberg, and Select2 contributors\n\nPermission is hereb"
  },
  {
    "path": "src/static_cdn/admin/css/vendor/select2/select2.css",
    "chars": 17604,
    "preview": ".select2-container {\n  box-sizing: border-box;\n  display: inline-block;\n  margin: 0;\n  position: relative;\n  vertical-al"
  },
  {
    "path": "src/static_cdn/admin/css/widgets.css",
    "chars": 10340,
    "preview": "/* SELECTOR (FILTER INTERFACE) */\n\n.selector {\n    width: 800px;\n    float: left;\n}\n\n.selector select {\n    width: 380px"
  },
  {
    "path": "src/static_cdn/admin/fonts/LICENSE.txt",
    "chars": 11358,
    "preview": "\n                                 Apache License\n                           Version 2.0, January 2004\n                  "
  },
  {
    "path": "src/static_cdn/admin/fonts/README.txt",
    "chars": 214,
    "preview": "Roboto webfont source: https://www.google.com/fonts/specimen/Roboto\nWOFF files extracted using https://github.com/majode"
  },
  {
    "path": "src/static_cdn/admin/img/LICENSE",
    "chars": 1081,
    "preview": "The MIT License (MIT)\n\nCopyright (c) 2014 Code Charm Ltd\n\nPermission is hereby granted, free of charge, to any person ob"
  },
  {
    "path": "src/static_cdn/admin/img/README.txt",
    "chars": 319,
    "preview": "All icons are taken from Font Awesome (http://fontawesome.io/) project.\nThe Font Awesome font is licensed under the SIL "
  },
  {
    "path": "src/static_cdn/admin/js/SelectBox.js",
    "chars": 5755,
    "preview": "(function($) {\n    'use strict';\n    var SelectBox = {\n        cache: {},\n        init: function(id) {\n            var b"
  },
  {
    "path": "src/static_cdn/admin/js/SelectFilter2.js",
    "chars": 12322,
    "preview": "/*global SelectBox, gettext, interpolate, quickElement, SelectFilter*/\n/*\nSelectFilter2 - Turns a multiple-select box in"
  },
  {
    "path": "src/static_cdn/admin/js/actions.js",
    "chars": 6538,
    "preview": "/*global gettext, interpolate, ngettext*/\n(function($) {\n    'use strict';\n    var lastChecked;\n\n    $.fn.actions = func"
  },
  {
    "path": "src/static_cdn/admin/js/admin/DateTimeShortcuts.js",
    "chars": 20274,
    "preview": "/*global Calendar, findPosX, findPosY, getStyle, get_format, gettext, gettext_noop, interpolate, ngettext, quickElement*"
  },
  {
    "path": "src/static_cdn/admin/js/admin/RelatedObjectLookups.js",
    "chars": 6918,
    "preview": "/*global SelectBox, interpolate*/\n// Handles related-objects functionality: lookup link for raw_id_fields\n// and Add Ano"
  },
  {
    "path": "src/static_cdn/admin/js/autocomplete.js",
    "chars": 1124,
    "preview": "(function($) {\n    'use strict';\n    var init = function($element, options) {\n        var settings = $.extend({\n        "
  },
  {
    "path": "src/static_cdn/admin/js/calendar.js",
    "chars": 7777,
    "preview": "/*global gettext, pgettext, get_format, quickElement, removeChildren*/\n/*\ncalendar.js - Calendar functions by Adrian Hol"
  },
  {
    "path": "src/static_cdn/admin/js/cancel.js",
    "chars": 410,
    "preview": "(function($) {\n    'use strict';\n    $(function() {\n        $('.cancel-link').on('click', function(e) {\n            e.pr"
  },
  {
    "path": "src/static_cdn/admin/js/change_form.js",
    "chars": 712,
    "preview": "/*global showAddAnotherPopup, showRelatedObjectLookupPopup showRelatedObjectPopup updateRelatedObjectLinks*/\n\n(function("
  },
  {
    "path": "src/static_cdn/admin/js/collapse.js",
    "chars": 2239,
    "preview": "/*global gettext*/\n(function() {\n    'use strict';\n    var closestElem = function(elem, tagName) {\n        if (elem.node"
  },
  {
    "path": "src/static_cdn/admin/js/core.js",
    "chars": 7135,
    "preview": "// Core javascript helper functions\n\n// basic browser identification & version\nvar isOpera = (navigator.userAgent.indexO"
  },
  {
    "path": "src/static_cdn/admin/js/inlines.js",
    "chars": 13810,
    "preview": "/*global DateTimeShortcuts, SelectFilter*/\n/**\n * Django admin inlines\n *\n * Based on jQuery Formset 1.1\n * @author Stan"
  },
  {
    "path": "src/static_cdn/admin/js/jquery.init.js",
    "chars": 363,
    "preview": "/*global django:true, jQuery:false*/\n/* Puts the included jQuery into our own namespace using noConflict and passing\n * "
  },
  {
    "path": "src/static_cdn/admin/js/popup_response.js",
    "chars": 569,
    "preview": "/*global opener */\n(function() {\n    'use strict';\n    var initData = JSON.parse(document.getElementById('django-admin-p"
  },
  {
    "path": "src/static_cdn/admin/js/prepopulate.js",
    "chars": 1530,
    "preview": "/*global URLify*/\n(function($) {\n    'use strict';\n    $.fn.prepopulate = function(dependencies, maxLength, allowUnicode"
  },
  {
    "path": "src/static_cdn/admin/js/prepopulate_init.js",
    "chars": 495,
    "preview": "(function($) {\n    'use strict';\n    var fields = $('#django-admin-prepopulated-fields-constants').data('prepopulatedFie"
  },
  {
    "path": "src/static_cdn/admin/js/timeparse.js",
    "chars": 2984,
    "preview": "(function() {\n    'use strict';\n    var timeParsePatterns = [\n        // 9\n        {\n            re: /^\\d{1,2}$/i,\n     "
  },
  {
    "path": "src/static_cdn/admin/js/urlify.js",
    "chars": 8507,
    "preview": "/*global XRegExp*/\n(function() {\n    'use strict';\n\n    var LATIN_MAP = {\n        'À': 'A', 'Á': 'A', 'Â': 'A', 'Ã': 'A'"
  },
  {
    "path": "src/static_cdn/admin/js/vendor/jquery/LICENSE.txt",
    "chars": 1282,
    "preview": "Copyright jQuery Foundation and other contributors, https://jquery.org/\n\nThis software consists of voluntary contributio"
  },
  {
    "path": "src/static_cdn/admin/js/vendor/jquery/jquery.js",
    "chars": 271817,
    "preview": "/*!\n * jQuery JavaScript Library v3.3.1\n * https://jquery.com/\n *\n * Includes Sizzle.js\n * https://sizzlejs.com/\n *\n * C"
  },
  {
    "path": "src/static_cdn/admin/js/vendor/select2/LICENSE.md",
    "chars": 1124,
    "preview": "The MIT License (MIT)\n\nCopyright (c) 2012-2015 Kevin Brown, Igor Vaynberg, and Select2 contributors\n\nPermission is hereb"
  },
  {
    "path": "src/static_cdn/admin/js/vendor/select2/i18n/ar.js",
    "chars": 768,
    "preview": "/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */\n\n(function(){if(jQuery&&jQuery.fn&&jQue"
  },
  {
    "path": "src/static_cdn/admin/js/vendor/select2/i18n/az.js",
    "chars": 681,
    "preview": "/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */\n\n(function(){if(jQuery&&jQuery.fn&&jQue"
  },
  {
    "path": "src/static_cdn/admin/js/vendor/select2/i18n/bg.js",
    "chars": 783,
    "preview": "/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */\n\n(function(){if(jQuery&&jQuery.fn&&jQue"
  },
  {
    "path": "src/static_cdn/admin/js/vendor/select2/i18n/ca.js",
    "chars": 865,
    "preview": "/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */\n\n(function(){if(jQuery&&jQuery.fn&&jQue"
  },
  {
    "path": "src/static_cdn/admin/js/vendor/select2/i18n/cs.js",
    "chars": 1175,
    "preview": "/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */\n\n(function(){if(jQuery&&jQuery.fn&&jQue"
  },
  {
    "path": "src/static_cdn/admin/js/vendor/select2/i18n/da.js",
    "chars": 810,
    "preview": "/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */\n\n(function(){if(jQuery&&jQuery.fn&&jQue"
  },
  {
    "path": "src/static_cdn/admin/js/vendor/select2/i18n/de.js",
    "chars": 767,
    "preview": "/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */\n\n(function(){if(jQuery&&jQuery.fn&&jQue"
  },
  {
    "path": "src/static_cdn/admin/js/vendor/select2/i18n/el.js",
    "chars": 925,
    "preview": "/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */\n\n(function(){if(jQuery&&jQuery.fn&&jQue"
  },
  {
    "path": "src/static_cdn/admin/js/vendor/select2/i18n/en.js",
    "chars": 823,
    "preview": "/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */\n\n(function(){if(jQuery&&jQuery.fn&&jQue"
  },
  {
    "path": "src/static_cdn/admin/js/vendor/select2/i18n/es.js",
    "chars": 862,
    "preview": "/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */\n\n(function(){if(jQuery&&jQuery.fn&&jQue"
  },
  {
    "path": "src/static_cdn/admin/js/vendor/select2/i18n/et.js",
    "chars": 768,
    "preview": "/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */\n\n(function(){if(jQuery&&jQuery.fn&&jQue"
  },
  {
    "path": "src/static_cdn/admin/js/vendor/select2/i18n/eu.js",
    "chars": 840,
    "preview": "/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */\n\n(function(){if(jQuery&&jQuery.fn&&jQue"
  },
  {
    "path": "src/static_cdn/admin/js/vendor/select2/i18n/fa.js",
    "chars": 833,
    "preview": "/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */\n\n(function(){if(jQuery&&jQuery.fn&&jQue"
  },
  {
    "path": "src/static_cdn/admin/js/vendor/select2/i18n/fi.js",
    "chars": 691,
    "preview": "/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */\n\n(function(){if(jQuery&&jQuery.fn&&jQue"
  },
  {
    "path": "src/static_cdn/admin/js/vendor/select2/i18n/fr.js",
    "chars": 886,
    "preview": "/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */\n\n(function(){if(jQuery&&jQuery.fn&&jQue"
  },
  {
    "path": "src/static_cdn/admin/js/vendor/select2/i18n/gl.js",
    "chars": 792,
    "preview": "/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */\n\n(function(){if(jQuery&&jQuery.fn&&jQue"
  },
  {
    "path": "src/static_cdn/admin/js/vendor/select2/i18n/he.js",
    "chars": 827,
    "preview": "/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */\n\n(function(){if(jQuery&&jQuery.fn&&jQue"
  },
  {
    "path": "src/static_cdn/admin/js/vendor/select2/i18n/hi.js",
    "chars": 829,
    "preview": "/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */\n\n(function(){if(jQuery&&jQuery.fn&&jQue"
  },
  {
    "path": "src/static_cdn/admin/js/vendor/select2/i18n/hr.js",
    "chars": 833,
    "preview": "/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */\n\n(function(){if(jQuery&&jQuery.fn&&jQue"
  },
  {
    "path": "src/static_cdn/admin/js/vendor/select2/i18n/hu.js",
    "chars": 711,
    "preview": "/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */\n\n(function(){if(jQuery&&jQuery.fn&&jQue"
  },
  {
    "path": "src/static_cdn/admin/js/vendor/select2/i18n/id.js",
    "chars": 748,
    "preview": "/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */\n\n(function(){if(jQuery&&jQuery.fn&&jQue"
  },
  {
    "path": "src/static_cdn/admin/js/vendor/select2/i18n/is.js",
    "chars": 755,
    "preview": "/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */\n\n(function(){if(jQuery&&jQuery.fn&&jQue"
  },
  {
    "path": "src/static_cdn/admin/js/vendor/select2/i18n/it.js",
    "chars": 870,
    "preview": "/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */\n\n(function(){if(jQuery&&jQuery.fn&&jQue"
  },
  {
    "path": "src/static_cdn/admin/js/vendor/select2/i18n/ja.js",
    "chars": 698,
    "preview": "/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */\n\n(function(){if(jQuery&&jQuery.fn&&jQue"
  },
  {
    "path": "src/static_cdn/admin/js/vendor/select2/i18n/km.js",
    "chars": 781,
    "preview": "/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */\n\n(function(){if(jQuery&&jQuery.fn&&jQue"
  },
  {
    "path": "src/static_cdn/admin/js/vendor/select2/i18n/ko.js",
    "chars": 718,
    "preview": "/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */\n\n(function(){if(jQuery&&jQuery.fn&&jQue"
  },
  {
    "path": "src/static_cdn/admin/js/vendor/select2/i18n/lt.js",
    "chars": 896,
    "preview": "/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */\n\n(function(){if(jQuery&&jQuery.fn&&jQue"
  },
  {
    "path": "src/static_cdn/admin/js/vendor/select2/i18n/lv.js",
    "chars": 855,
    "preview": "/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */\n\n(function(){if(jQuery&&jQuery.fn&&jQue"
  },
  {
    "path": "src/static_cdn/admin/js/vendor/select2/i18n/mk.js",
    "chars": 837,
    "preview": "/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */\n\n(function(){if(jQuery&&jQuery.fn&&jQue"
  },
  {
    "path": "src/static_cdn/admin/js/vendor/select2/i18n/ms.js",
    "chars": 787,
    "preview": "/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */\n\n(function(){if(jQuery&&jQuery.fn&&jQue"
  },
  {
    "path": "src/static_cdn/admin/js/vendor/select2/i18n/nb.js",
    "chars": 780,
    "preview": "/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */\n\n(function(){if(jQuery&&jQuery.fn&&jQue"
  },
  {
    "path": "src/static_cdn/admin/js/vendor/select2/i18n/nl.js",
    "chars": 890,
    "preview": "/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */\n\n(function(){if(jQuery&&jQuery.fn&&jQue"
  },
  {
    "path": "src/static_cdn/admin/js/vendor/select2/i18n/pl.js",
    "chars": 910,
    "preview": "/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */\n\n(function(){if(jQuery&&jQuery.fn&&jQue"
  },
  {
    "path": "src/static_cdn/admin/js/vendor/select2/i18n/pt-BR.js",
    "chars": 846,
    "preview": "/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */\n\n(function(){if(jQuery&&jQuery.fn&&jQue"
  },
  {
    "path": "src/static_cdn/admin/js/vendor/select2/i18n/pt.js",
    "chars": 853,
    "preview": "/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */\n\n(function(){if(jQuery&&jQuery.fn&&jQue"
  },
  {
    "path": "src/static_cdn/admin/js/vendor/select2/i18n/ro.js",
    "chars": 888,
    "preview": "/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */\n\n(function(){if(jQuery&&jQuery.fn&&jQue"
  },
  {
    "path": "src/static_cdn/admin/js/vendor/select2/i18n/ru.js",
    "chars": 958,
    "preview": "/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */\n\n(function(){if(jQuery&&jQuery.fn&&jQue"
  },
  {
    "path": "src/static_cdn/admin/js/vendor/select2/i18n/sk.js",
    "chars": 1148,
    "preview": "/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */\n\n(function(){if(jQuery&&jQuery.fn&&jQue"
  },
  {
    "path": "src/static_cdn/admin/js/vendor/select2/i18n/sr-Cyrl.js",
    "chars": 931,
    "preview": "/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */\n\n(function(){if(jQuery&&jQuery.fn&&jQue"
  },
  {
    "path": "src/static_cdn/admin/js/vendor/select2/i18n/sr.js",
    "chars": 928,
    "preview": "/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */\n\n(function(){if(jQuery&&jQuery.fn&&jQue"
  },
  {
    "path": "src/static_cdn/admin/js/vendor/select2/i18n/sv.js",
    "chars": 777,
    "preview": "/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */\n\n(function(){if(jQuery&&jQuery.fn&&jQue"
  },
  {
    "path": "src/static_cdn/admin/js/vendor/select2/i18n/th.js",
    "chars": 715,
    "preview": "/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */\n\n(function(){if(jQuery&&jQuery.fn&&jQue"
  },
  {
    "path": "src/static_cdn/admin/js/vendor/select2/i18n/tr.js",
    "chars": 712,
    "preview": "/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */\n\n(function(){if(jQuery&&jQuery.fn&&jQue"
  },
  {
    "path": "src/static_cdn/admin/js/vendor/select2/i18n/uk.js",
    "chars": 933,
    "preview": "/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */\n\n(function(){if(jQuery&&jQuery.fn&&jQue"
  },
  {
    "path": "src/static_cdn/admin/js/vendor/select2/i18n/vi.js",
    "chars": 747,
    "preview": "/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */\n\n(function(){if(jQuery&&jQuery.fn&&jQue"
  },
  {
    "path": "src/static_cdn/admin/js/vendor/select2/i18n/zh-CN.js",
    "chars": 675,
    "preview": "/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */\n\n(function(){if(jQuery&&jQuery.fn&&jQue"
  },
  {
    "path": "src/static_cdn/admin/js/vendor/select2/i18n/zh-TW.js",
    "chars": 632,
    "preview": "/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */\n\n(function(){if(jQuery&&jQuery.fn&&jQue"
  },
  {
    "path": "src/static_cdn/admin/js/vendor/select2/select2.full.js",
    "chars": 161828,
    "preview": "/*!\n * Select2 4.0.3\n * https://select2.github.io\n *\n * Released under the MIT license\n * https://github.com/select2/sel"
  },
  {
    "path": "src/static_cdn/admin/js/vendor/xregexp/LICENSE.txt",
    "chars": 1103,
    "preview": "The MIT License\n\nCopyright (c) 2007-2012 Steven Levithan <http://xregexp.com/>\n\nPermission is hereby granted, free of ch"
  },
  {
    "path": "src/static_cdn/admin/js/vendor/xregexp/xregexp.js",
    "chars": 128820,
    "preview": "\n/***** xregexp.js *****/\n\n/*!\n * XRegExp v2.0.0\n * (c) 2007-2012 Steven Levithan <http://xregexp.com/>\n * MIT License\n "
  },
  {
    "path": "src/templates/base.html",
    "chars": 736,
    "preview": "<!DOCTYPE html>\n<html lang=en>\n\n<head>\n\t<!-- Required meta tags -->\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\""
  },
  {
    "path": "src/templates/registration/password_change.html",
    "chars": 1391,
    "preview": "{% extends 'base.html' %}\n\n{% block content %}\n<style type=\"text/css\">\n  .form-signin {\n    width: 100%;\n    max-width: "
  },
  {
    "path": "src/templates/registration/password_change_done.html",
    "chars": 192,
    "preview": "{% extends 'base.html' %}\n\n{% block content %}\n\n<div class=\"container\">\n\t<div class=\"d-flex flex-column\">\n\t\t<p class=\"m-"
  },
  {
    "path": "src/templates/registration/password_reset_complete.html",
    "chars": 257,
    "preview": "{% extends 'base.html' %}\n\n\n{% block content %}\n\n<div class=\"container\">\n\t<div class=\"d-flex flex-column\">\n\t\t<p class=\"m"
  },
  {
    "path": "src/templates/registration/password_reset_done.html",
    "chars": 578,
    "preview": "{% extends 'base.html' %}\n\n{% block content %}\n\n<div class=\"container\">\n\n\t<div class=\"d-flex flex-column\">\n\t\t  <p class="
  },
  {
    "path": "src/templates/registration/password_reset_email.html",
    "chars": 366,
    "preview": "{% autoescape off %}\nTo initiate the password reset process for your Account {{ user.email }},\nclick the link below:\n\n{{"
  },
  {
    "path": "src/templates/registration/password_reset_form.html",
    "chars": 2150,
    "preview": "{% extends 'base.html' %}\n\n\n{% block content %}\n\n<style type=\"text/css\">\n  .form-signin {\n    width: 100%;\n    max-width"
  },
  {
    "path": "src/templates/registration/password_reset_subject.txt",
    "chars": 30,
    "preview": "CodingWithMitch password reset"
  },
  {
    "path": "src/templates/snippets/base_css.html",
    "chars": 71,
    "preview": "<style type=\"text/css\">\n\tbody{\n\t\tbackground-color: #f2f2f2;\n\t}\n</style>"
  },
  {
    "path": "src/templates/snippets/footer.html",
    "chars": 838,
    "preview": "<style type=\"text/css\">\n\t.footer{\n\t\tmin-height: 100px;\n\t}\n</style>\n\n<!-- Footer -->\n\n<div class=\"d-flex flex-row align-i"
  },
  {
    "path": "src/templates/snippets/header.html",
    "chars": 1553,
    "preview": "<style type=\"text/css\">\n\t@media (min-width: 768px) {\n\t  html {\n\t    font-size: 16px;\n\t  }\n\t}\n\n\t.search-bar{\n\t\tmax-width:"
  }
]

// ... and 1 more files (download for full content)

About this extraction

This page contains the full source code of the mitchtabian/CodingWithMitchBlog-REST-API GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 155 files (861.0 KB), approximately 252.0k tokens, and a symbol index with 279 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!