New UI
|
@ -3,5 +3,8 @@ Mastodon.py
|
|||
Pillow
|
||||
psycopg2-binary
|
||||
httpx
|
||||
django-tastypie
|
||||
django-admin-list-filter-dropdown
|
||||
wikipedia-api
|
||||
bs4
|
||||
lxml
|
||||
unidecode
|
||||
|
|
|
@ -18,6 +18,7 @@ def run_in_thread(fn):
|
|||
t = threading.Thread(target=fn, args=k, kwargs=kw)
|
||||
t.start()
|
||||
return t
|
||||
|
||||
return run
|
||||
|
||||
|
||||
|
@ -25,35 +26,30 @@ def run_in_thread(fn):
|
|||
class AdminPerson(admin.ModelAdmin):
|
||||
list_per_page = 50
|
||||
list_display = (
|
||||
'name',
|
||||
'country',
|
||||
'is_actor',
|
||||
'is_director',
|
||||
'is_woman',
|
||||
"name",
|
||||
"country",
|
||||
"is_actor",
|
||||
"is_director",
|
||||
"is_woman",
|
||||
)
|
||||
search_fields = ('name',)
|
||||
list_filter = ('is_woman', 'is_actor', 'is_director', 'country')
|
||||
search_fields = ("name",)
|
||||
list_filter = ("is_woman", "is_actor", "is_director", "country")
|
||||
|
||||
|
||||
@admin.register(models.Movie)
|
||||
class AdminMovie(admin.ModelAdmin):
|
||||
list_per_page = 50
|
||||
list_display = (
|
||||
'name',
|
||||
'original_name',
|
||||
'year',
|
||||
'duration',
|
||||
'count')
|
||||
search_fields = ('name', 'original_name')
|
||||
filter_horizontal = ('directors',)
|
||||
list_display = ("name", "original_name", "year", "duration", "count")
|
||||
search_fields = ("name", "original_name")
|
||||
filter_horizontal = ("directors",)
|
||||
list_filter = (
|
||||
'is_digital',
|
||||
('directors__name', DropdownFilter),
|
||||
('countries__name', DropdownFilter),
|
||||
('year', DropdownFilter),
|
||||
)
|
||||
"is_digital",
|
||||
("directors__name", DropdownFilter),
|
||||
("countries__name", DropdownFilter),
|
||||
("year", DropdownFilter),
|
||||
)
|
||||
_is_new = False
|
||||
actions = ['published']
|
||||
actions = ["published"]
|
||||
|
||||
def save_model(self, request, obj, form, change):
|
||||
self._is_new = obj.pk is None
|
||||
|
@ -68,30 +64,32 @@ class AdminMovie(admin.ModelAdmin):
|
|||
return
|
||||
|
||||
def formfield_for_manytomany(self, db_field, request, **kwargs):
|
||||
if db_field.name == 'directors':
|
||||
kwargs['queryset'] = models.Person.objects.filter(is_director=True)
|
||||
if db_field.name == 'actors':
|
||||
kwargs['queryset'] = models.Person.objects.filter(is_actor=True)
|
||||
if db_field.name == "directors":
|
||||
kwargs["queryset"] = models.Person.objects.filter(is_director=True)
|
||||
if db_field.name == "actors":
|
||||
kwargs["queryset"] = models.Person.objects.filter(is_actor=True)
|
||||
return super().formfield_for_manytomany(db_field, request, **kwargs)
|
||||
|
||||
def published(self, request, queryset):
|
||||
obj = queryset[0]
|
||||
self._public(obj)
|
||||
self.message_user(request, 'Publicado correctamente', messages.SUCCESS)
|
||||
published.short_description = 'Republicar en redes'
|
||||
self.message_user(request, "Publicado correctamente", messages.SUCCESS)
|
||||
|
||||
published.short_description = "Republicar en redes"
|
||||
|
||||
def _public_mastodon(self, message, cartel):
|
||||
MT = {
|
||||
'jpg': 'image/jpeg',
|
||||
'png': 'image/png',
|
||||
"jpg": "image/jpeg",
|
||||
"png": "image/png",
|
||||
}
|
||||
message += '\n\n#mauflix'
|
||||
message += "\n\n#mauflix"
|
||||
media_type = MT[cartel.url[-3:]]
|
||||
|
||||
try:
|
||||
server = Mastodon(
|
||||
access_token=settings.TOKEN_MASTODON,
|
||||
api_base_url=settings.URL_MASTODON)
|
||||
api_base_url=settings.URL_MASTODON,
|
||||
)
|
||||
|
||||
media = server.media_post(cartel.read(), media_type)
|
||||
server.status_post(message, media_ids=media)
|
||||
|
@ -100,9 +98,15 @@ class AdminMovie(admin.ModelAdmin):
|
|||
return
|
||||
|
||||
def _public_telegram(self, message, cartel):
|
||||
url = f'https://api.telegram.org/bot{settings.TOKEN_TELEGRAM}/sendPhoto'
|
||||
url = (
|
||||
f"https://api.telegram.org/bot{settings.TOKEN_TELEGRAM}/sendPhoto"
|
||||
)
|
||||
url_cartel = settings.URL_CDN.format(cartel)
|
||||
data = {'chat_id': settings.CHAT_ID, 'photo': url_cartel, 'caption': message}
|
||||
data = {
|
||||
"chat_id": settings.CHAT_ID,
|
||||
"photo": url_cartel,
|
||||
"caption": message,
|
||||
}
|
||||
result = httpx.post(url, data=data).json()
|
||||
return
|
||||
|
||||
|
@ -121,4 +125,3 @@ Año: {obj.year}
|
|||
|
||||
admin.site.register(models.Gender)
|
||||
admin.site.register(models.Country)
|
||||
|
||||
|
|
|
@ -1,79 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
from django.db.models import Q
|
||||
|
||||
from tastypie import fields
|
||||
from tastypie.authentication import Authentication
|
||||
from tastypie.resources import ModelResource
|
||||
from tastypie.throttle import BaseThrottle
|
||||
from tastypie.constants import ALL, ALL_WITH_RELATIONS
|
||||
|
||||
from main.models import Movie
|
||||
from main.models import Person
|
||||
from main.models import Country
|
||||
|
||||
from django.conf import settings
|
||||
|
||||
|
||||
class CustomAuthentication(Authentication):
|
||||
|
||||
def is_authenticated(self, request, **kwargs):
|
||||
if not 'ApiToken' in request.headers:
|
||||
return False
|
||||
|
||||
if request.headers['ApiToken'] != settings.API_TOKEN:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
class ResourceDirectors(ModelResource):
|
||||
|
||||
class Meta:
|
||||
queryset = Person.objects.all().filter(is_director=True)
|
||||
resource_name = 'directors'
|
||||
|
||||
|
||||
class ResourceCountries(ModelResource):
|
||||
|
||||
class Meta:
|
||||
queryset = Country.objects.all()
|
||||
resource_name = 'countries'
|
||||
|
||||
|
||||
class ResourceMovies(ModelResource):
|
||||
directors = fields.ToManyField(ResourceDirectors, 'directors', full=True)
|
||||
countries = fields.ToManyField(ResourceCountries, 'countries', full=True)
|
||||
|
||||
class Meta:
|
||||
queryset = Movie.objects.all().order_by('-id')
|
||||
resource_name = 'movies'
|
||||
excludes = ['count', 'duration', 'published', 'stars']
|
||||
throttle = BaseThrottle(throttle_at=50)
|
||||
allowed_methods = ['get']
|
||||
authentication = CustomAuthentication()
|
||||
limit = 10
|
||||
max_limit = 10
|
||||
filtering = {
|
||||
'name': ALL,
|
||||
'original_name': ALL,
|
||||
}
|
||||
|
||||
def dehydrate_directors(self, bundle):
|
||||
names = ', '.join([d.obj.name for d in bundle.data['directors']])
|
||||
return names
|
||||
|
||||
def dehydrate_countries(self, bundle):
|
||||
names = ', '.join([c.obj.name for c in bundle.data['countries']])
|
||||
return names
|
||||
|
||||
# ~ def build_filters(self, filters=None, **kwargs):
|
||||
# ~ orm_filters = super().build_filters(filters or {}, **kwargs)
|
||||
# ~ return orm_filters
|
||||
|
||||
def apply_filters(self, request, applicable_filters):
|
||||
objects = self.get_object_list(request)
|
||||
query = applicable_filters['name__icontains']
|
||||
qset = (Q(name__icontains=query) | Q(original_name__icontains=query))
|
||||
objects = objects.filter(qset)
|
||||
return objects
|
|
@ -2,4 +2,4 @@ from django.apps import AppConfig
|
|||
|
||||
|
||||
class MainConfig(AppConfig):
|
||||
name = 'main'
|
||||
name = "main"
|
||||
|
|
|
@ -6,12 +6,12 @@ from .models import Movie
|
|||
|
||||
|
||||
class LatestMoviesFeed(Feed):
|
||||
title = 'Lo ultimo en MauFlix'
|
||||
link = ''
|
||||
description = 'Ultimas diez películas disponibles en MauFlix'
|
||||
title = "Lo ultimo en MauFlix"
|
||||
link = ""
|
||||
description = "Ultimas diez películas disponibles en MauFlix"
|
||||
|
||||
def items(self):
|
||||
return Movie.objects.order_by('-id')[:10]
|
||||
return Movie.objects.order_by("-id")[:10]
|
||||
|
||||
def item_title(self, item):
|
||||
return item.name
|
||||
|
@ -21,4 +21,4 @@ class LatestMoviesFeed(Feed):
|
|||
return message
|
||||
|
||||
def item_link(self, item):
|
||||
return reverse('movies', args=[item.pk])
|
||||
return reverse("movies", args=[item.pk])
|
||||
|
|
|
@ -8,76 +8,220 @@ class Migration(migrations.Migration):
|
|||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
]
|
||||
dependencies = []
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Country',
|
||||
name="Country",
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=250)),
|
||||
(
|
||||
"id",
|
||||
models.AutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("name", models.CharField(max_length=250)),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'País',
|
||||
'verbose_name_plural': 'Paises',
|
||||
'ordering': ['name'],
|
||||
'unique_together': {('name',)},
|
||||
"verbose_name": "País",
|
||||
"verbose_name_plural": "Paises",
|
||||
"ordering": ["name"],
|
||||
"unique_together": {("name",)},
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Gender',
|
||||
name="Gender",
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=250)),
|
||||
(
|
||||
"id",
|
||||
models.AutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("name", models.CharField(max_length=250)),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Género',
|
||||
'verbose_name_plural': 'Generos',
|
||||
'ordering': ['name'],
|
||||
'unique_together': {('name',)},
|
||||
"verbose_name": "Género",
|
||||
"verbose_name_plural": "Generos",
|
||||
"ordering": ["name"],
|
||||
"unique_together": {("name",)},
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Person',
|
||||
name="Person",
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=500, verbose_name='Nombre')),
|
||||
('date_born', models.DateField(blank=True, null=True, verbose_name='Fecha de nacimiento')),
|
||||
('is_actor', models.BooleanField(default=False, verbose_name='Es Actor')),
|
||||
('is_director', models.BooleanField(default=False, verbose_name='Es Director')),
|
||||
('photo', models.ImageField(blank=True, null=True, upload_to='%Y/%m/%d/', verbose_name='Fotografía')),
|
||||
('country', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='country', to='main.Country')),
|
||||
(
|
||||
"id",
|
||||
models.AutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
(
|
||||
"name",
|
||||
models.CharField(max_length=500, verbose_name="Nombre"),
|
||||
),
|
||||
(
|
||||
"date_born",
|
||||
models.DateField(
|
||||
blank=True,
|
||||
null=True,
|
||||
verbose_name="Fecha de nacimiento",
|
||||
),
|
||||
),
|
||||
(
|
||||
"is_actor",
|
||||
models.BooleanField(
|
||||
default=False, verbose_name="Es Actor"
|
||||
),
|
||||
),
|
||||
(
|
||||
"is_director",
|
||||
models.BooleanField(
|
||||
default=False, verbose_name="Es Director"
|
||||
),
|
||||
),
|
||||
(
|
||||
"photo",
|
||||
models.ImageField(
|
||||
blank=True,
|
||||
null=True,
|
||||
upload_to="%Y/%m/%d/",
|
||||
verbose_name="Fotografía",
|
||||
),
|
||||
),
|
||||
(
|
||||
"country",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.PROTECT,
|
||||
related_name="country",
|
||||
to="main.Country",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Persona',
|
||||
'verbose_name_plural': 'Personas',
|
||||
'ordering': ['name'],
|
||||
'unique_together': {('name',)},
|
||||
"verbose_name": "Persona",
|
||||
"verbose_name_plural": "Personas",
|
||||
"ordering": ["name"],
|
||||
"unique_together": {("name",)},
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Movie',
|
||||
name="Movie",
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=1000, verbose_name='Nombre')),
|
||||
('original_name', models.CharField(blank=True, default='', max_length=1000, verbose_name='Nombre original')),
|
||||
('file_name', models.CharField(blank=True, default='', max_length=1000, verbose_name='Nombre archivo')),
|
||||
('year', models.PositiveSmallIntegerField(default=1900, verbose_name='Año')),
|
||||
('duration', models.PositiveSmallIntegerField(default=0, verbose_name='Duración')),
|
||||
('cartel', models.ImageField(blank=True, null=True, upload_to='%Y/%m/%d/', verbose_name='Cartel')),
|
||||
('count', models.PositiveIntegerField(default=0, verbose_name='Descargas')),
|
||||
('stars', models.PositiveSmallIntegerField(default=0, verbose_name='Estrellas')),
|
||||
('actors', models.ManyToManyField(blank=True, related_name='actors', to='main.Person', verbose_name='Reparto')),
|
||||
('countries', models.ManyToManyField(blank=True, related_name='countries', to='main.Country', verbose_name='País')),
|
||||
('directors', models.ManyToManyField(related_name='directors', to='main.Person', verbose_name='Director')),
|
||||
('genders', models.ManyToManyField(blank=True, related_name='genders', to='main.Gender', verbose_name='Género')),
|
||||
(
|
||||
"id",
|
||||
models.AutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
(
|
||||
"name",
|
||||
models.CharField(max_length=1000, verbose_name="Nombre"),
|
||||
),
|
||||
(
|
||||
"original_name",
|
||||
models.CharField(
|
||||
blank=True,
|
||||
default="",
|
||||
max_length=1000,
|
||||
verbose_name="Nombre original",
|
||||
),
|
||||
),
|
||||
(
|
||||
"file_name",
|
||||
models.CharField(
|
||||
blank=True,
|
||||
default="",
|
||||
max_length=1000,
|
||||
verbose_name="Nombre archivo",
|
||||
),
|
||||
),
|
||||
(
|
||||
"year",
|
||||
models.PositiveSmallIntegerField(
|
||||
default=1900, verbose_name="Año"
|
||||
),
|
||||
),
|
||||
(
|
||||
"duration",
|
||||
models.PositiveSmallIntegerField(
|
||||
default=0, verbose_name="Duración"
|
||||
),
|
||||
),
|
||||
(
|
||||
"cartel",
|
||||
models.ImageField(
|
||||
blank=True,
|
||||
null=True,
|
||||
upload_to="%Y/%m/%d/",
|
||||
verbose_name="Cartel",
|
||||
),
|
||||
),
|
||||
(
|
||||
"count",
|
||||
models.PositiveIntegerField(
|
||||
default=0, verbose_name="Descargas"
|
||||
),
|
||||
),
|
||||
(
|
||||
"stars",
|
||||
models.PositiveSmallIntegerField(
|
||||
default=0, verbose_name="Estrellas"
|
||||
),
|
||||
),
|
||||
(
|
||||
"actors",
|
||||
models.ManyToManyField(
|
||||
blank=True,
|
||||
related_name="actors",
|
||||
to="main.Person",
|
||||
verbose_name="Reparto",
|
||||
),
|
||||
),
|
||||
(
|
||||
"countries",
|
||||
models.ManyToManyField(
|
||||
blank=True,
|
||||
related_name="countries",
|
||||
to="main.Country",
|
||||
verbose_name="País",
|
||||
),
|
||||
),
|
||||
(
|
||||
"directors",
|
||||
models.ManyToManyField(
|
||||
related_name="directors",
|
||||
to="main.Person",
|
||||
verbose_name="Director",
|
||||
),
|
||||
),
|
||||
(
|
||||
"genders",
|
||||
models.ManyToManyField(
|
||||
blank=True,
|
||||
related_name="genders",
|
||||
to="main.Gender",
|
||||
verbose_name="Género",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Película',
|
||||
'verbose_name_plural': 'Películas',
|
||||
'ordering': ['name'],
|
||||
'unique_together': {('name', 'original_name')},
|
||||
"verbose_name": "Película",
|
||||
"verbose_name_plural": "Películas",
|
||||
"ordering": ["name"],
|
||||
"unique_together": {("name", "original_name")},
|
||||
},
|
||||
),
|
||||
]
|
||||
|
|
|
@ -7,18 +7,23 @@ import main.models
|
|||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('main', '0001_initial'),
|
||||
("main", "0001_initial"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='movie',
|
||||
name='published',
|
||||
field=models.BooleanField(default=True, verbose_name='¿Publicar?'),
|
||||
model_name="movie",
|
||||
name="published",
|
||||
field=models.BooleanField(default=True, verbose_name="¿Publicar?"),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='movie',
|
||||
name='cartel',
|
||||
field=models.ImageField(blank=True, null=True, upload_to=main.models.upload_cartel, verbose_name='Cartel'),
|
||||
model_name="movie",
|
||||
name="cartel",
|
||||
field=models.ImageField(
|
||||
blank=True,
|
||||
null=True,
|
||||
upload_to=main.models.upload_cartel,
|
||||
verbose_name="Cartel",
|
||||
),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -6,13 +6,15 @@ from django.db import migrations, models
|
|||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('main', '0002_auto_20200602_2302'),
|
||||
("main", "0002_auto_20200602_2302"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='movie',
|
||||
name='is_digital',
|
||||
field=models.BooleanField(default=False, verbose_name='Es digital'),
|
||||
model_name="movie",
|
||||
name="is_digital",
|
||||
field=models.BooleanField(
|
||||
default=False, verbose_name="Es digital"
|
||||
),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -6,23 +6,25 @@ from django.db import migrations, models
|
|||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('main', '0003_movie_is_digital'),
|
||||
("main", "0003_movie_is_digital"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='person',
|
||||
name='is_woman',
|
||||
field=models.BooleanField(default=False, verbose_name='Es mujer'),
|
||||
model_name="person",
|
||||
name="is_woman",
|
||||
field=models.BooleanField(default=False, verbose_name="Es mujer"),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='country',
|
||||
name='name',
|
||||
field=models.CharField(max_length=250, verbose_name='País'),
|
||||
model_name="country",
|
||||
name="name",
|
||||
field=models.CharField(max_length=250, verbose_name="País"),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='movie',
|
||||
name='actors',
|
||||
field=models.ManyToManyField(blank=True, related_name='actors', to='main.Person'),
|
||||
model_name="movie",
|
||||
name="actors",
|
||||
field=models.ManyToManyField(
|
||||
blank=True, related_name="actors", to="main.Person"
|
||||
),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
# Generated by Django 3.2.15 on 2023-01-09 18:10
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("main", "0004_auto_20210807_2207"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name="country",
|
||||
name="id",
|
||||
field=models.AutoField(primary_key=True, serialize=False),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="gender",
|
||||
name="id",
|
||||
field=models.AutoField(primary_key=True, serialize=False),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="movie",
|
||||
name="id",
|
||||
field=models.AutoField(primary_key=True, serialize=False),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="person",
|
||||
name="id",
|
||||
field=models.AutoField(primary_key=True, serialize=False),
|
||||
),
|
||||
]
|
|
@ -1,6 +1,12 @@
|
|||
|
||||
import random
|
||||
import time
|
||||
import re
|
||||
import wikipediaapi
|
||||
from bs4 import BeautifulSoup
|
||||
from django.conf import settings
|
||||
from django.db import models
|
||||
from pathlib import Path
|
||||
from unidecode import unidecode
|
||||
|
||||
|
||||
class Gender(models.Model):
|
||||
|
@ -8,10 +14,10 @@ class Gender(models.Model):
|
|||
name = models.CharField(max_length=250)
|
||||
|
||||
class Meta:
|
||||
unique_together = ['name']
|
||||
ordering = ['name']
|
||||
verbose_name = 'Género'
|
||||
verbose_name_plural = 'Generos'
|
||||
unique_together = ["name"]
|
||||
ordering = ["name"]
|
||||
verbose_name = "Género"
|
||||
verbose_name_plural = "Generos"
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
@ -19,162 +25,489 @@ class Gender(models.Model):
|
|||
|
||||
class Country(models.Model):
|
||||
id = models.AutoField(primary_key=True)
|
||||
name = models.CharField(max_length=250, verbose_name='País')
|
||||
name = models.CharField(max_length=250, verbose_name="País")
|
||||
|
||||
class Meta:
|
||||
unique_together = ['name']
|
||||
ordering = ['name']
|
||||
verbose_name = 'País'
|
||||
verbose_name_plural = 'Paises'
|
||||
unique_together = ["name"]
|
||||
ordering = ["name"]
|
||||
verbose_name = "País"
|
||||
verbose_name_plural = "Paises"
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
class PersonQuerySet(models.QuerySet):
|
||||
|
||||
def directors(self):
|
||||
rows = self.filter(is_director=True).values_list('name', flat=True)
|
||||
rows = self.filter(is_director=True).values_list("name", flat=True)
|
||||
return rows
|
||||
|
||||
def actors(self):
|
||||
rows = self.filter(is_actor=True).values_list('name', flat=True)
|
||||
rows = self.filter(is_actor=True).values_list("name", flat=True)
|
||||
return rows
|
||||
|
||||
|
||||
class Person(models.Model):
|
||||
id = models.AutoField(primary_key=True)
|
||||
name = models.CharField('Nombre',
|
||||
max_length=500)
|
||||
country = models.ForeignKey(Country,
|
||||
related_name='country', on_delete=models.PROTECT)
|
||||
date_born = models.DateField('Fecha de nacimiento',
|
||||
null=True, blank=True)
|
||||
is_actor = models.BooleanField('Es Actor',
|
||||
default=False)
|
||||
is_director = models.BooleanField('Es Director',
|
||||
default=False)
|
||||
is_woman = models.BooleanField('Es mujer',
|
||||
default=False)
|
||||
photo = models.ImageField('Fotografía', upload_to='%Y/%m/%d/',
|
||||
null=True, blank=True)
|
||||
name = models.CharField("Nombre", max_length=500)
|
||||
country = models.ForeignKey(
|
||||
Country, related_name="country", on_delete=models.PROTECT
|
||||
)
|
||||
date_born = models.DateField("Fecha de nacimiento", null=True, blank=True)
|
||||
is_actor = models.BooleanField("Es Actor", default=False)
|
||||
is_director = models.BooleanField("Es Director", default=False)
|
||||
is_woman = models.BooleanField("Es mujer", default=False)
|
||||
photo = models.ImageField(
|
||||
"Fotografía", upload_to="%Y/%m/%d/", null=True, blank=True
|
||||
)
|
||||
objects = PersonQuerySet.as_manager()
|
||||
|
||||
class Meta:
|
||||
unique_together = ['name']
|
||||
ordering = ['name']
|
||||
verbose_name = 'Persona'
|
||||
verbose_name_plural = 'Personas'
|
||||
unique_together = ["name"]
|
||||
ordering = ["name"]
|
||||
verbose_name = "Persona"
|
||||
verbose_name_plural = "Personas"
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
class MovieQuerySet(models.QuerySet):
|
||||
def random_pick(self, random_max=6, min_items=20, all=None, **kwargs):
|
||||
"""
|
||||
Regresa películas de manera aleatoria.
|
||||
|
||||
def _to_str(self, q):
|
||||
return ','.join([r.name for r in q.all()])
|
||||
|
||||
def _to_link(self, file_name):
|
||||
# ~ folder = file_name[0].upper()
|
||||
# ~ if folder.isdigit():
|
||||
# ~ folder = '0'
|
||||
url = settings.URL_CDN.format(file_name)
|
||||
return url
|
||||
|
||||
def _to_image(self, img, director=''):
|
||||
data = ''
|
||||
if img:
|
||||
url = '../'
|
||||
if director:
|
||||
url = '../../'
|
||||
data = url + img.url
|
||||
return data
|
||||
|
||||
def to_dict(self, query='', director='', id=0):
|
||||
rows = self.all().order_by('-id')[:10]
|
||||
if query == 'all':
|
||||
rows = self.all()
|
||||
elif id:
|
||||
rows = self.filter(id=id)
|
||||
director = ' '
|
||||
Por defecto tiene que haber al menos 20 películas en la consulta.
|
||||
Por defecto regresa un máximo de 6 películas.
|
||||
"""
|
||||
if all is None:
|
||||
all = list(Movie.objects.filter(**kwargs).values())
|
||||
if len(all) < min_items:
|
||||
return None
|
||||
elif len(all) < random_max:
|
||||
return all
|
||||
else:
|
||||
if director:
|
||||
rows = self.filter(directors__name__iexact=director)
|
||||
elif query:
|
||||
rows = self.filter(name__icontains=query)
|
||||
return random.sample(all, random_max)
|
||||
|
||||
movies = []
|
||||
for row in rows:
|
||||
data = {}
|
||||
data['id'] = row.id
|
||||
data['url'] = self._to_link(row.file_name)
|
||||
data['name'] = row.name
|
||||
data['year'] = row.year
|
||||
data['original_name'] = row.original_name
|
||||
data['duration'] = row.duration
|
||||
data['director'] = self._to_str(row.directors)
|
||||
data['country'] = self._to_str(row.countries)
|
||||
data['count'] = row.count
|
||||
data['image'] = self._to_image(row.cartel, director)
|
||||
movies.append(data)
|
||||
def top_pick(self, key, top_max=6):
|
||||
"""
|
||||
Regresa el top de películas.
|
||||
|
||||
matrix = []
|
||||
for i in range (0, len(movies), 4):
|
||||
matrix.append(movies[i:i+4])
|
||||
return matrix
|
||||
El top corresponde al criterio de búsqueda en 'key'.
|
||||
Por defecto regresa las primeras 6 películas como máximo.
|
||||
"""
|
||||
all = list(Movie.objects.order_by(f"-{key}").values())
|
||||
return self.fix_all(all[:top_max])
|
||||
|
||||
def get_directors(self):
|
||||
data = self.distinct().values_list(
|
||||
'directors__name', flat=True).order_by('directors')
|
||||
return data
|
||||
def top_random_pick(self, key, top_max=6):
|
||||
"""
|
||||
Regresa el top de películas de manera aleatorias.
|
||||
|
||||
El top corresponde al criterio de búsqueda en 'key'.
|
||||
Por defecto regresa un máximo de 6 películas.
|
||||
A diferencia de 'random_pick', de todo el top selecciona de manera
|
||||
aleatoria según el 'top_max'.
|
||||
"""
|
||||
all = list(Movie.objects.order_by(f"-{key}").values())
|
||||
top = []
|
||||
for movie in all:
|
||||
if movie[key] == all[0][key]:
|
||||
top.append(movie)
|
||||
else:
|
||||
break
|
||||
if len(top) > top_max:
|
||||
movies = random.sample(top, top_max)
|
||||
else:
|
||||
movies = top
|
||||
return self.fix_all(movies)
|
||||
|
||||
def home_pick(self):
|
||||
"""
|
||||
Regresa la selección de películas para el 'home'.
|
||||
|
||||
El 'home' es la página principal.
|
||||
"""
|
||||
genders = list(Gender.objects.values_list("id", flat=True))
|
||||
sections = self.home_sections()
|
||||
for gender in genders:
|
||||
key = Gender.objects.get(pk=gender).name
|
||||
picked = self.random_pick(genders=gender)
|
||||
if picked:
|
||||
sections["genders"][key] = self.fix_all(picked)
|
||||
return sections
|
||||
|
||||
def home_sections(self):
|
||||
"""
|
||||
Regresa la selección de películas en secciones para el 'home'.
|
||||
|
||||
Las secciones son novedades, mejor valorados y más descargados.
|
||||
"""
|
||||
sections = {
|
||||
"Novedades": self.top_pick("id"),
|
||||
"Mejor valorados": self.top_random_pick("stars"),
|
||||
"Más descargados": self.top_pick("count"),
|
||||
}
|
||||
sections["genders"] = {}
|
||||
return sections
|
||||
|
||||
def fix_all(self, movies):
|
||||
"""
|
||||
Enmienda los datos de las películas.
|
||||
"""
|
||||
for movie in movies:
|
||||
self.fix_data(movie, wikipedia=False)
|
||||
return movies
|
||||
|
||||
def fix_data(self, movie, wikipedia=True):
|
||||
"""
|
||||
Enmienda los datos de una película.
|
||||
|
||||
Los enmiendos son formateos de unos valores (que se guardan en nuevas
|
||||
llaves) y de rutas a medios.
|
||||
"""
|
||||
if len(movie["file_name"]) == 0 or len(movie["cartel"]) == 0:
|
||||
print(f"WARN: película sin ruta de video o cartel:\n{movie}")
|
||||
movie["duration_formatted"] = self.format_duration(movie["duration"])
|
||||
movie["count_formatted"] = self.format_count(movie["count"])
|
||||
movie["stars_icons"] = self.format_stars(movie["stars"])
|
||||
movie["file_name"] = self.fix_path(movie["file_name"])
|
||||
movie["cartel"] = self.fix_path(movie["cartel"])
|
||||
if wikipedia:
|
||||
movie["wiki"] = self.get_wiki(movie)
|
||||
|
||||
def fix_path(self, el):
|
||||
"""
|
||||
Enmienda ruta a medio.
|
||||
|
||||
La ruta es distinta según se esté en producción o en desarrollo
|
||||
('DEBUG'). La URL_DEBUB apunta a la dirección en nebula.
|
||||
"""
|
||||
if settings.DEBUG:
|
||||
if str(Path(el).parent) == "." and len(el) > 0:
|
||||
el = f"{el[0]}/{el}"
|
||||
return settings.URL_DEBUG.format(el)
|
||||
else:
|
||||
return settings.MEDIA_ROOT / el
|
||||
|
||||
def fix_summ(self, raw):
|
||||
"""
|
||||
Enmienda sinopsis de Wikipedia.
|
||||
|
||||
Los sumarios de artículos a la Wikipedia vienen con notas al pie que no
|
||||
son necesarias. Para la sinopsis estas notas son eliminadas y se
|
||||
regresa el código HTML como una cadena de caracteres.
|
||||
"""
|
||||
html = BeautifulSoup(raw, "lxml")
|
||||
for ref in html.find_all("sup", "reference"):
|
||||
ref.decompose()
|
||||
for crossref in html.find_all("dl"):
|
||||
crossref.decompose()
|
||||
clean = list(map(lambda x: str(x), html.body.children))
|
||||
return " ".join(clean)
|
||||
|
||||
def format_stars(self, num):
|
||||
"""
|
||||
Da formato a la cantidad de estrellas.
|
||||
|
||||
Regresa la cantidad de estrellas en lugar del número de estas.
|
||||
"""
|
||||
stars = "★" * num
|
||||
while len(stars) < 5:
|
||||
stars += "☆"
|
||||
return stars
|
||||
|
||||
def format_count(self, num):
|
||||
"""
|
||||
Da formato a la cantidad de descargas.
|
||||
|
||||
Regresa la cantidad en una cifra separada por comas.
|
||||
"""
|
||||
return "{:,}".format(num)
|
||||
|
||||
def format_duration(self, num):
|
||||
"""
|
||||
Da formato a duración.
|
||||
|
||||
Regresa la duración en 'Nh Nm'; p. ej.: 1h 22m, 2h, 15m.
|
||||
"""
|
||||
secs = num * 60
|
||||
hours = self.format_duration_num("%H", secs)
|
||||
mins = self.format_duration_num("%M", secs)
|
||||
if hours == "":
|
||||
return mins
|
||||
elif mins == "":
|
||||
return hours
|
||||
else:
|
||||
return f"{hours} {mins}"
|
||||
|
||||
def format_duration_num(self, num_type, secs):
|
||||
"""
|
||||
Da formato a cada número de la duración.
|
||||
|
||||
Extrae hora o minuto de la duración, la pasa a int y si no es cero,
|
||||
regresa Ns; p. ej.: 1h, 55m.
|
||||
"""
|
||||
num = int(time.strftime(num_type, time.gmtime(secs)))
|
||||
sym = num_type[-1].lower()
|
||||
if num == 0:
|
||||
return ""
|
||||
else:
|
||||
return f"{num}{sym}"
|
||||
|
||||
def get_wiki(self, movie, again=True):
|
||||
"""
|
||||
Obtiene artículo de Wikipedia.
|
||||
|
||||
Primero intenta obtener el artículo por el nombre original. Si no tiene
|
||||
éxito, intenta obtenerlo por el nombre en español. Regresa None si no
|
||||
tuvo ningún éxito.
|
||||
"""
|
||||
wiki = self.get_wiki_page(movie["original_name"])
|
||||
if not wiki:
|
||||
wiki = self.get_wiki_page(movie["name"])
|
||||
return wiki
|
||||
|
||||
def get_wiki_page(self, title):
|
||||
"""
|
||||
Intenta obtener artículo de Wikipedia.
|
||||
|
||||
Si no tiene éxito, regresa 'None'.
|
||||
"""
|
||||
try:
|
||||
lang = settings.LANGUAGE_CODE.split("-")[0]
|
||||
wiki = wikipediaapi.Wikipedia(
|
||||
lang, extract_format=wikipediaapi.ExtractFormat.HTML
|
||||
)
|
||||
page = wiki.page(title)
|
||||
if page.exists():
|
||||
return {
|
||||
"title": page.title,
|
||||
"url": page.fullurl,
|
||||
"summary": self.fix_summ(page.summary),
|
||||
}
|
||||
else:
|
||||
return None
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
def get_related(self):
|
||||
"""
|
||||
Regresa los campos relacionados a las películas.
|
||||
"""
|
||||
return ["countries", "genders", "directors", "actors"]
|
||||
|
||||
def get_movie_by_id(self, id):
|
||||
"""
|
||||
Obtiene película por id.
|
||||
|
||||
Esta obtención también añade objetos relacionados.
|
||||
"""
|
||||
related = self.get_related()
|
||||
movie = Movie.objects.prefetch_related(*related).get(pk=id).__dict__
|
||||
for key, val in movie["_prefetched_objects_cache"].items():
|
||||
movie[key] = list(map(lambda x: x["name"], val.values()))
|
||||
movie.pop("_state")
|
||||
movie.pop("_prefetched_objects_cache")
|
||||
self.fix_data(movie)
|
||||
return movie
|
||||
|
||||
def get_movies(self, query):
|
||||
"""
|
||||
Obtiene películas buscadas.
|
||||
"""
|
||||
movies = Movie.objects.prefetch_related(*self.get_related())
|
||||
for q in self.get_queries(query):
|
||||
selector = None
|
||||
if re.match(r"^\w:", q) is not None:
|
||||
selector = self.get_selector(re.sub(r"^(\w):.*", r"\1", q))
|
||||
q = re.sub(r"^\w:(.*)", r"\1", q)
|
||||
movies = self.get_movies_by_query(selector, q, movies)
|
||||
if len(movies) != len(Movie.objects.all()):
|
||||
if hasattr(movies, "values"):
|
||||
movies = self.fix_all(list(movies.values()))
|
||||
movies = self.random_pick(random_max=100, min_items=0, all=movies)
|
||||
return movies
|
||||
else:
|
||||
return []
|
||||
|
||||
def get_movies_by_query(self, selector, query, movies):
|
||||
"""
|
||||
Obtiene película por query.
|
||||
"""
|
||||
if selector is None:
|
||||
return self.get_movies_by_query_any(query, movies)
|
||||
elif selector != "section":
|
||||
return self.get_movies_by_query_selector(selector, query, movies)
|
||||
else:
|
||||
return self.get_movies_by_query_section(query, movies)
|
||||
|
||||
def get_movies_by_query_selector(self, selector, query, movies):
|
||||
"""
|
||||
Obtiene película por query que tiene selector 'w:'.
|
||||
"""
|
||||
if selector == "year":
|
||||
selector = f"{selector}__iregex"
|
||||
try:
|
||||
query = int(query)
|
||||
except Exception:
|
||||
query = 0
|
||||
else:
|
||||
if selector == "name" or selector == "original_name":
|
||||
selector = f"{selector}__unaccent__iregex"
|
||||
else:
|
||||
selector = f"{selector}__name__unaccent__iregex"
|
||||
query = f"[[:<:]]{query}"
|
||||
kwargs = {selector: query}
|
||||
return movies.filter(**kwargs)
|
||||
|
||||
def get_movies_by_query_section(self, query, movies):
|
||||
"""
|
||||
Obtiene película por query que tiene selector 'w:' para sección.
|
||||
"""
|
||||
if query == "mejor valorados":
|
||||
return self.top_random_pick("stars", top_max=100)
|
||||
elif query == "novedades":
|
||||
return self.top_pick("id", top_max=100)
|
||||
elif query == "mas descargados":
|
||||
return self.top_pick("count", top_max=100)
|
||||
else:
|
||||
return list(movies.values())
|
||||
|
||||
def get_movies_by_query_any(self, query, movies):
|
||||
"""
|
||||
Obtiene película por query que no tiene ningún selector.
|
||||
"""
|
||||
for field in ["name"] + self.get_related():
|
||||
if field != "name":
|
||||
field = f"{field}__name"
|
||||
kwargs = {f"{field}__unaccent__iregex": f"[[:<:]]{query}"}
|
||||
result = movies.filter(**kwargs)
|
||||
if len(result) > 0:
|
||||
movies = result
|
||||
return movies
|
||||
|
||||
def get_queries(self, query):
|
||||
"""
|
||||
Devuelve un conjunto de queries sanitizado; p. ej.:
|
||||
de: d:Bruno--Stagñaro y:1997 pizzá, birra, faso
|
||||
a: ['d:bruno-stagnaro', 'y:1997', 'pizza', 'birra', 'faso']
|
||||
"""
|
||||
queries = re.sub(r"\s+", " ", unidecode(str(query)).lower()).split()
|
||||
return list(map(lambda q: self.clean_query(q), queries))
|
||||
|
||||
def clean_query(self, word):
|
||||
"""
|
||||
Limpia query de caracteres 'W' al inicio y al final. También sustituye
|
||||
'-' por un espacio.
|
||||
"""
|
||||
word = re.sub(r"^\W+", "", word)
|
||||
word = re.sub(r"\W+$", "", word)
|
||||
word = re.sub(r"-+", " ", word)
|
||||
return word
|
||||
|
||||
def get_selector(self, prefix):
|
||||
"""
|
||||
Obtiene el selector para filtrar la consulta de una búsqueda.
|
||||
|
||||
Esto permite restricciones en la búsqueda con esta sintaxis:
|
||||
* t:Un-Título => Buscará las películas que tengan 'Un T…' en sus 'name'
|
||||
* d:Nombre => Buscará las películas que tengan 'N…' en sus 'directors'
|
||||
El guion (-) se usa como separador de palabras, puede contener acentos
|
||||
o eñe, aunque va a ser decodificado.
|
||||
"""
|
||||
prefixes = {
|
||||
"t": "name",
|
||||
"o": "original_name",
|
||||
"y": "year",
|
||||
"c": "countries",
|
||||
"p": "countries",
|
||||
"d": "directors",
|
||||
"a": "actors",
|
||||
"g": "genders",
|
||||
"s": "section",
|
||||
}
|
||||
if prefix in prefixes:
|
||||
return prefixes[prefix]
|
||||
else:
|
||||
return None
|
||||
|
||||
def api(self, request):
|
||||
"""
|
||||
Obtiene resultados de la API.
|
||||
"""
|
||||
msg = f"Vista {request.get_host()}/help/#api para más información."
|
||||
result = {"ERROR": msg}
|
||||
try:
|
||||
if request.GET.get("q"):
|
||||
query = request.GET["q"]
|
||||
result = {"movies": Movie.objects.get_movies(query)}
|
||||
elif request.GET.get("id"):
|
||||
query = request.GET["id"]
|
||||
result = {"movie": Movie.objects.get_movie_by_id(query)}
|
||||
else:
|
||||
result["Exception"] = f"Invalid {request.GET}"
|
||||
except Exception:
|
||||
result["Exception"] = f"Invalid {request.GET}"
|
||||
if request.user.is_authenticated:
|
||||
return result
|
||||
else:
|
||||
return self.clean_api_response(result)
|
||||
|
||||
def clean_api_response(self, result):
|
||||
"""
|
||||
Elimina la URL de la película para usuarios no autenticados.
|
||||
"""
|
||||
url = "file_name"
|
||||
if "movies" in result:
|
||||
for movie in result["movies"]:
|
||||
del movie[url]
|
||||
elif "movie" in result:
|
||||
del result["movie"][url]
|
||||
return result
|
||||
|
||||
|
||||
def upload_cartel(instance, filename):
|
||||
first = filename[0].upper()
|
||||
if first.isdigit():
|
||||
first = '0'
|
||||
return f'{first}/{filename}'
|
||||
first = "0"
|
||||
return f"{first}/{filename}"
|
||||
|
||||
|
||||
class Movie(models.Model):
|
||||
id = models.AutoField(primary_key=True)
|
||||
name = models.CharField('Nombre',
|
||||
max_length=1000)
|
||||
original_name = models.CharField('Nombre original',
|
||||
max_length=1000, default='', blank=True)
|
||||
file_name = models.CharField('Nombre archivo',
|
||||
max_length=1000, default='', blank=True)
|
||||
year = models.PositiveSmallIntegerField('Año',
|
||||
default=1900)
|
||||
duration = models.PositiveSmallIntegerField('Duración',
|
||||
default=0)
|
||||
directors = models.ManyToManyField(Person, verbose_name='Director',
|
||||
related_name='directors')
|
||||
actors = models.ManyToManyField(Person,
|
||||
related_name='actors', blank=True)
|
||||
countries = models.ManyToManyField(Country,
|
||||
related_name='countries', verbose_name='País', blank=True)
|
||||
genders = models.ManyToManyField(Gender,
|
||||
related_name='genders', verbose_name='Género', blank=True)
|
||||
cartel = models.ImageField('Cartel', upload_to=upload_cartel,
|
||||
null=True, blank=True)
|
||||
count = models.PositiveIntegerField('Descargas',
|
||||
default=0)
|
||||
stars = models.PositiveSmallIntegerField('Estrellas',
|
||||
default=0)
|
||||
published = models.BooleanField('¿Publicar?',
|
||||
default=True)
|
||||
is_digital = models.BooleanField('Es digital',
|
||||
default=False)
|
||||
name = models.CharField("Nombre", max_length=1000)
|
||||
original_name = models.CharField(
|
||||
"Nombre original", max_length=1000, default="", blank=True
|
||||
)
|
||||
file_name = models.CharField(
|
||||
"Nombre archivo", max_length=1000, default="", blank=True
|
||||
)
|
||||
year = models.PositiveSmallIntegerField("Año", default=1900)
|
||||
duration = models.PositiveSmallIntegerField("Duración", default=0)
|
||||
directors = models.ManyToManyField(
|
||||
Person, verbose_name="Director", related_name="directors"
|
||||
)
|
||||
actors = models.ManyToManyField(Person, related_name="actors", blank=True)
|
||||
countries = models.ManyToManyField(
|
||||
Country, related_name="countries", verbose_name="País", blank=True
|
||||
)
|
||||
genders = models.ManyToManyField(
|
||||
Gender, related_name="genders", verbose_name="Género", blank=True
|
||||
)
|
||||
cartel = models.ImageField(
|
||||
"Cartel", upload_to=upload_cartel, null=True, blank=True
|
||||
)
|
||||
count = models.PositiveIntegerField("Descargas", default=0)
|
||||
stars = models.PositiveSmallIntegerField("Estrellas", default=0)
|
||||
published = models.BooleanField("¿Publicar?", default=True)
|
||||
is_digital = models.BooleanField("Es digital", default=False)
|
||||
objects = MovieQuerySet.as_manager()
|
||||
|
||||
class Meta:
|
||||
unique_together = ['name', 'original_name']
|
||||
ordering = ['name']
|
||||
verbose_name = 'Película'
|
||||
verbose_name_plural = 'Películas'
|
||||
unique_together = ["name", "original_name"]
|
||||
ordering = ["name"]
|
||||
verbose_name = "Película"
|
||||
verbose_name_plural = "Películas"
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
|
|
@ -0,0 +1,444 @@
|
|||
/* BASE */
|
||||
|
||||
:root {
|
||||
--color-primary: #375a7f;
|
||||
--color-background: #343c3d;
|
||||
--plyr-color-main: var(--color-primary) !important;
|
||||
}
|
||||
|
||||
#nav {
|
||||
z-index: 101;
|
||||
}
|
||||
|
||||
#nav.is-fixed-top {
|
||||
position: sticky;
|
||||
top: -1px;
|
||||
}
|
||||
|
||||
#menu.force-display {
|
||||
display: flex !important;
|
||||
}
|
||||
|
||||
.navbar-item img {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* Cada flecha en los títulos de secciones */
|
||||
.arrows {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.arrows:before {
|
||||
content: " ❯❯";
|
||||
}
|
||||
|
||||
/* HOME */
|
||||
|
||||
/* Cada sección de pelis */
|
||||
|
||||
.hero-cartels {
|
||||
margin-bottom: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.cartels {
|
||||
clear: both;
|
||||
width: calc(100% + 3rem);
|
||||
margin-left: -1.5rem !important;
|
||||
padding-left: 0;
|
||||
display: grid;
|
||||
align-items: center;
|
||||
grid-template-columns: repeat(1, 1fr);
|
||||
}
|
||||
|
||||
@media screen and (min-width: 300px) {
|
||||
.cartels {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 450px) {
|
||||
.cartels {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 600px) {
|
||||
.cartels {
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 750px) {
|
||||
.cartels {
|
||||
grid-template-columns: repeat(5, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 900px) {
|
||||
.cartels {
|
||||
grid-template-columns: repeat(6, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 1920px) {
|
||||
.cartels {
|
||||
width: auto;
|
||||
margin: 0 !important;
|
||||
grid-template-columns: repeat(6, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
/* Cada peli */
|
||||
|
||||
.cartel {
|
||||
position: relative;
|
||||
float: left;
|
||||
min-width: 150px;
|
||||
min-height: 225px;
|
||||
max-width: 300px;
|
||||
max-height: 450px;
|
||||
}
|
||||
|
||||
.cartel img {
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.cartel .info {
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 102;
|
||||
}
|
||||
|
||||
/* HOME / MOVIE */
|
||||
|
||||
/* Cada ficha de peli */
|
||||
|
||||
.movie-head {
|
||||
padding: 2rem 2rem 0 2rem !important;
|
||||
}
|
||||
|
||||
.movie-body {
|
||||
padding: 2rem !important;
|
||||
}
|
||||
|
||||
.full .movie-body {
|
||||
padding: 0 2rem !important;
|
||||
line-height: 2;
|
||||
}
|
||||
|
||||
.movie-body video {
|
||||
display: block;
|
||||
height: 100%;
|
||||
width: auto;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.info {
|
||||
background-color: var(--color-background);
|
||||
}
|
||||
|
||||
.info .full {
|
||||
height: 100%;
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
.stats span {
|
||||
padding-right: 2rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.stats i {
|
||||
display: inline-block;
|
||||
margin-right: .5rem;
|
||||
}
|
||||
|
||||
.stats i.gg-time {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.stars {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.about {
|
||||
max-width: 70rem;
|
||||
margin: auto;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.about p + p {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.about *:not(h2) + h2 {
|
||||
margin-top: 1.5rem;
|
||||
}
|
||||
|
||||
.infobox td:first-child {
|
||||
text-align: right;
|
||||
width: 8rem;
|
||||
}
|
||||
|
||||
.extra-info {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
background-color: var(--color-primary);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.about figure {
|
||||
width: 50%;
|
||||
height: auto;
|
||||
max-width: 300px;
|
||||
max-height: 450px;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1024px) {
|
||||
table {
|
||||
margin: auto;
|
||||
}
|
||||
}
|
||||
|
||||
/* Despliegue de la ficha; Cfr:
|
||||
* https://stackoverflow.com/questions/42177216/manipulate-css-class-without-javascript
|
||||
* https://stackoverflow.com/questions/55858255/custom-checkbox-with-css-before-not-working-in-firefox-edge
|
||||
*/
|
||||
|
||||
.toggle:checked ~ .info {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.toggle {
|
||||
position: absolute;
|
||||
z-index: 100;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
opacity: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.toggle:checked {
|
||||
display: block;
|
||||
z-index: 103;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 100%;
|
||||
height: calc(100% - 3em);
|
||||
}
|
||||
|
||||
/* Tooltip de la ficha; Cfr:
|
||||
* https://www.w3schools.com/howto/howto_css_tooltip.asp
|
||||
*/
|
||||
|
||||
/* Tooltip container */
|
||||
.tooltip {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* Tooltip text */
|
||||
.tooltip .tooltiptext {
|
||||
visibility: hidden;
|
||||
opacity: 0;
|
||||
width: 100%;
|
||||
background-color: var(--color-background);
|
||||
color: #fff;
|
||||
border-top: 3px solid var(--color-primary);
|
||||
text-align: center;
|
||||
font-size: 1.25rem;
|
||||
padding: .5em;
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
top: 0;
|
||||
left: 0;
|
||||
transition: opacity 0.3s;
|
||||
}
|
||||
|
||||
/* Show the tooltip text when you mouse over the tooltip container */
|
||||
.tooltip:hover .tooltiptext {
|
||||
visibility: visible;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* Reproductor */
|
||||
|
||||
button.plyr__control--overlaid {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
section#notice {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
section#notice div {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
section#notice p {
|
||||
font-weight: bold;
|
||||
color: white;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
section#notice a {
|
||||
display: block;
|
||||
width: fit-content;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
/* SEARCH */
|
||||
|
||||
.search-div > * {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.search-div input {
|
||||
width: calc(100% - 4.1rem);
|
||||
}
|
||||
|
||||
.search-div button {
|
||||
width: 3.8rem;
|
||||
height: 3.8rem;
|
||||
}
|
||||
|
||||
/* ICONS; cfr: https://css.gg */
|
||||
|
||||
.gg-time {
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
display: block;
|
||||
transform: scale(var(--ggs,1));
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
border-radius: 100%;
|
||||
border: 2px solid transparent;
|
||||
box-shadow: 0 0 0 2px currentColor;
|
||||
}
|
||||
|
||||
.gg-time::after {
|
||||
content: "";
|
||||
display: block;
|
||||
box-sizing: border-box;
|
||||
position: absolute;
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-left: 2px solid;
|
||||
border-bottom: 2px solid;
|
||||
top: 0;
|
||||
left: 4px;
|
||||
}
|
||||
|
||||
.gg-software-download {
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
display: block;
|
||||
transform: scale(var(--ggs,1));
|
||||
width: 16px;
|
||||
height: 6px;
|
||||
border: 2px solid;
|
||||
border-top: 0;
|
||||
border-bottom-left-radius: 2px;
|
||||
border-bottom-right-radius: 2px;
|
||||
margin-top: 8px
|
||||
}
|
||||
|
||||
.gg-software-download::after {
|
||||
content: "";
|
||||
display: block;
|
||||
box-sizing: border-box;
|
||||
position: absolute;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-left: 2px solid;
|
||||
border-bottom: 2px solid;
|
||||
transform: rotate(-45deg);
|
||||
left: 2px;
|
||||
bottom: 4px
|
||||
}
|
||||
|
||||
.gg-software-download::before {
|
||||
content: "";
|
||||
display: block;
|
||||
box-sizing: border-box;
|
||||
position: absolute;
|
||||
border-radius: 3px;
|
||||
width: 2px;
|
||||
height: 10px;
|
||||
background: currentColor;
|
||||
left: 5px;
|
||||
bottom: 5px
|
||||
}
|
||||
|
||||
.gg-link {
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
display: block;
|
||||
transform: rotate(-45deg) scale(var(--ggs,1));
|
||||
width: 8px;
|
||||
height: 2px;
|
||||
background: currentColor;
|
||||
border-radius: 4px
|
||||
}
|
||||
|
||||
.gg-link::after,
|
||||
.gg-link::before {
|
||||
content: "";
|
||||
display: block;
|
||||
box-sizing: border-box;
|
||||
position: absolute;
|
||||
border-radius: 3px;
|
||||
width: 8px;
|
||||
height: 10px;
|
||||
border: 2px solid;
|
||||
top: -4px
|
||||
}
|
||||
|
||||
.gg-link::before {
|
||||
border-right: 0;
|
||||
border-top-left-radius: 40px;
|
||||
border-bottom-left-radius: 40px;
|
||||
left: -6px
|
||||
}
|
||||
|
||||
.gg-link::after {
|
||||
border-left: 0;
|
||||
border-top-right-radius: 40px;
|
||||
border-bottom-right-radius: 40px;
|
||||
right: -6px
|
||||
}
|
||||
|
||||
.gg-search {
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
display: block;
|
||||
transform: scale(var(--ggs,1));
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border: 2px solid;
|
||||
border-radius: 100%;
|
||||
margin-left: -4px;
|
||||
margin-top: -4px
|
||||
}
|
||||
|
||||
.gg-search::after {
|
||||
content: "";
|
||||
display: block;
|
||||
box-sizing: border-box;
|
||||
position: absolute;
|
||||
border-radius: 3px;
|
||||
width: 2px;
|
||||
height: 8px;
|
||||
background: currentColor;
|
||||
transform: rotate(-45deg);
|
||||
top: 10px;
|
||||
left: 12px
|
||||
}
|
After Width: | Height: | Size: 6.9 KiB |
|
@ -0,0 +1,130 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
width="512"
|
||||
height="512"
|
||||
viewBox="0 0 135.46666 135.46666"
|
||||
version="1.1"
|
||||
id="svg8"
|
||||
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
|
||||
sodipodi:docname="favicon.svg"
|
||||
inkscape:export-filename="favicon.png"
|
||||
inkscape:export-xdpi="96"
|
||||
inkscape:export-ydpi="96"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<defs
|
||||
id="defs2">
|
||||
<rect
|
||||
x="127.8628"
|
||||
y="131.33685"
|
||||
width="44.228352"
|
||||
height="64.944321"
|
||||
id="rect971" />
|
||||
<rect
|
||||
x="214.46635"
|
||||
y="79.221107"
|
||||
width="123.64536"
|
||||
height="102.02128"
|
||||
id="rect957" />
|
||||
<rect
|
||||
x="214.46635"
|
||||
y="79.221107"
|
||||
width="123.64536"
|
||||
height="102.02128"
|
||||
id="rect1119" />
|
||||
<rect
|
||||
x="214.46635"
|
||||
y="79.221107"
|
||||
width="123.64536"
|
||||
height="102.02128"
|
||||
id="rect1159" />
|
||||
<rect
|
||||
x="214.46635"
|
||||
y="79.221107"
|
||||
width="123.64536"
|
||||
height="102.02128"
|
||||
id="rect1213" />
|
||||
<rect
|
||||
x="214.46635"
|
||||
y="79.221107"
|
||||
width="123.64536"
|
||||
height="102.02128"
|
||||
id="rect1166" />
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#375a7f"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="0.38532642"
|
||||
inkscape:cx="-120.67691"
|
||||
inkscape:cy="-124.56971"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:current-layer="layer1"
|
||||
inkscape:document-rotation="0"
|
||||
showgrid="false"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1011"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
showguides="true"
|
||||
inkscape:guide-bbox="true"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1" />
|
||||
<metadata
|
||||
id="metadata5">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Capa 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1">
|
||||
<g
|
||||
id="rect2398"
|
||||
style="opacity:1"
|
||||
transform="translate(0.01602397)" />
|
||||
<g
|
||||
id="rect2400"
|
||||
style="opacity:1"
|
||||
transform="translate(0.01602397)" />
|
||||
<g
|
||||
id="rect2402"
|
||||
style="opacity:1"
|
||||
transform="translate(0.01602397)" />
|
||||
<g
|
||||
id="rect2404"
|
||||
style="opacity:1" />
|
||||
<g
|
||||
id="rect2406"
|
||||
style="opacity:1" />
|
||||
<g
|
||||
id="rect2408"
|
||||
style="opacity:1" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
id="text969"
|
||||
style="font-size:10.5833px;line-height:1;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;white-space:pre;shape-inside:url(#rect971);display:inline" />
|
||||
<path
|
||||
id="path1541"
|
||||
style="fill:#ffffff;stroke-width:0.332183"
|
||||
d="M 0.09001667,21.743483 V 96.133805 H 23.957074 v 3.321382 H 0.09001667 v 2.315573 H 23.957074 v 3.32143 H 0.08764702 c -0.0010022,0.79886 -0.01360684,1.27101 -0.02280029,2.27787 H 23.957843 v 1.93632 1.38511 H 0.031897 c -0.01359326,1.02732 -0.031898666667,3.03169 -0.031898666667,3.03169 L 30.611198,113.67122 H 63.749981 L 53.352288,100.57164 h 28.257975 l -10.732186,13.09959 h 33.737113 l 30.85148,-0.047 c 0,0 -9.7e-4,-1.95701 -0.003,-2.93278 H 111.5898 v -2.17185 -1.14953 h 23.86948 c -0.001,-1.0085 -0.003,-1.54115 -0.003,-2.27792 h -23.86679 v -2.17422 -1.14716 h 23.85768 l -0.01,-2.315573 h -23.84954 v -2.171896 -1.149536 h 23.84116 L 135.21275,21.743483 90.171052,52.752817 h -0.124906 c -6.977213,-2.430397 -14.470662,-3.754838 -22.27472,-3.754838 -7.798681,0 -15.288346,1.323621 -22.260545,3.750098 h -0.141319 z"
|
||||
inkscape:export-xdpi="300"
|
||||
inkscape:export-ydpi="300" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 4.2 KiB |
After Width: | Height: | Size: 5.7 KiB |
|
@ -0,0 +1,130 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
width="377.03391"
|
||||
height="256"
|
||||
viewBox="0 0 99.756886 67.733332"
|
||||
version="1.1"
|
||||
id="svg8"
|
||||
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
|
||||
sodipodi:docname="logo.svg"
|
||||
inkscape:export-filename="favicon.png"
|
||||
inkscape:export-xdpi="130.36493"
|
||||
inkscape:export-ydpi="130.36493"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<defs
|
||||
id="defs2">
|
||||
<rect
|
||||
x="127.8628"
|
||||
y="131.33685"
|
||||
width="44.228352"
|
||||
height="64.944321"
|
||||
id="rect971" />
|
||||
<rect
|
||||
x="214.46635"
|
||||
y="79.221107"
|
||||
width="123.64536"
|
||||
height="102.02128"
|
||||
id="rect957" />
|
||||
<rect
|
||||
x="214.46635"
|
||||
y="79.221107"
|
||||
width="123.64536"
|
||||
height="102.02128"
|
||||
id="rect1119" />
|
||||
<rect
|
||||
x="214.46635"
|
||||
y="79.221107"
|
||||
width="123.64536"
|
||||
height="102.02128"
|
||||
id="rect1159" />
|
||||
<rect
|
||||
x="214.46635"
|
||||
y="79.221107"
|
||||
width="123.64536"
|
||||
height="102.02128"
|
||||
id="rect1213" />
|
||||
<rect
|
||||
x="214.46635"
|
||||
y="79.221107"
|
||||
width="123.64536"
|
||||
height="102.02128"
|
||||
id="rect1166" />
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#375a7f"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="1"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="0.38532642"
|
||||
inkscape:cx="-120.67691"
|
||||
inkscape:cy="-124.56971"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:current-layer="layer1"
|
||||
inkscape:document-rotation="0"
|
||||
showgrid="false"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1011"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
showguides="true"
|
||||
inkscape:guide-bbox="true"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1" />
|
||||
<metadata
|
||||
id="metadata5">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Capa 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1">
|
||||
<g
|
||||
id="rect2398"
|
||||
style="opacity:1"
|
||||
transform="translate(0.01602397)" />
|
||||
<g
|
||||
id="rect2400"
|
||||
style="opacity:1"
|
||||
transform="translate(0.01602397)" />
|
||||
<g
|
||||
id="rect2402"
|
||||
style="opacity:1"
|
||||
transform="translate(0.01602397)" />
|
||||
<g
|
||||
id="rect2404"
|
||||
style="opacity:1" />
|
||||
<g
|
||||
id="rect2406"
|
||||
style="opacity:1" />
|
||||
<g
|
||||
id="rect2408"
|
||||
style="opacity:1" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
id="text969"
|
||||
style="font-size:10.5833px;line-height:1;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;white-space:pre;shape-inside:url(#rect971);display:inline" />
|
||||
<path
|
||||
id="path1541"
|
||||
style="fill:#ffffff;stroke-width:0.244618"
|
||||
d="m 0.066289,0 v 54.780614 h 17.575567 v 2.445847 H 0.066289 v 1.705175 h 17.575567 v 2.44588 H 0.064544 c -7.38e-4,0.588275 -0.01002,0.935969 -0.01679,1.677414 h 17.594668 v 1.425891 1.019988 H 0.02349 C 0.01348,66.257327 0,67.733333 0,67.733333 l 22.541915,-0.03826 H 46.945128 L 39.288326,58.04861 h 20.809013 l -7.903121,9.646463 H 77.03804 l 22.71885,-0.03458 c 0,0 -7.1e-4,-1.441129 -0.002,-2.159681 H 82.174101 V 63.901477 63.054966 H 99.75145 c -7.4e-4,-0.742653 -0.002,-1.134892 -0.002,-1.677447 H 82.173871 V 59.776437 58.931672 H 99.74253 l -0.007,-1.705174 H 82.172861 V 55.627126 54.780614 H 99.72936 L 99.56991,0 66.401456,22.835099 h -0.09198 c -5.13798,-1.789731 -10.656114,-2.765042 -16.402978,-2.765042 -5.742905,0 -11.258252,0.974707 -16.39254,2.761551 h -0.104067 z"
|
||||
inkscape:export-xdpi="300"
|
||||
inkscape:export-ydpi="300" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 4.1 KiB |
|
@ -0,0 +1,19 @@
|
|||
document.addEventListener('DOMContentLoaded', () => {
|
||||
modMenu();
|
||||
});
|
||||
|
||||
// Habilita el menú de hamburguesa y que esté fijo
|
||||
function modMenu() {
|
||||
document.getElementById('nav').classList.add('is-fixed-top');
|
||||
document.getElementById('menu').classList.remove('force-display');
|
||||
// Cfr: https://bulma.io/documentation/components/navbar/#navbar-burger
|
||||
const $navbarBurgers = Array.prototype.slice.call(document.querySelectorAll('.navbar-burger'), 0);
|
||||
$navbarBurgers.forEach( el => {
|
||||
el.addEventListener('click', () => {
|
||||
const target = el.dataset.target;
|
||||
const $target = document.getElementById(target);
|
||||
el.classList.toggle('is-active');
|
||||
$target.classList.toggle('is-active');
|
||||
});
|
||||
});
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
// Deshabilita scroll con la tecla de espacio
|
||||
// Esto es para que en 'player' quede habilitado para reproducción
|
||||
window.addEventListener('keydown', (e) => {
|
||||
if (e.keyCode === 32 && e.target === document.body) {
|
||||
e.preventDefault();
|
||||
}
|
||||
});
|
||||
|
||||
// Añade un aviso
|
||||
function add_notice () {
|
||||
const divs = document.querySelectorAll('.plyr');
|
||||
// Ejecuta de nuevo hasta que esté implementado el reproductor
|
||||
if (divs.length == 0) {
|
||||
setTimeout(add_notice, 500);
|
||||
} else {
|
||||
let hero_sec = document.createElement('section'),
|
||||
hero_div = document.createElement('div'),
|
||||
hero_tit = document.createElement('p'),
|
||||
hero_btn = document.createElement('a');
|
||||
hero_sec.id = 'notice';
|
||||
hero_sec.classList.add('hero', 'is-small', 'is-warning', 'has-text-centered');
|
||||
hero_div.classList.add('hero-body');
|
||||
hero_btn.classList.add('button', 'is-small', 'is-success');
|
||||
hero_btn.addEventListener('click', remove_notice);
|
||||
hero_tit.innerText = 'Mauflix funciona con pocos recursos. Para un uso más eficiente y ecológico te recomendamos descargar la película.';
|
||||
hero_btn.innerText = '¡Entendido!';
|
||||
hero_tit.appendChild(hero_btn);
|
||||
hero_div.appendChild(hero_tit);
|
||||
hero_sec.appendChild(hero_div);
|
||||
divs[0].appendChild(hero_sec);
|
||||
}
|
||||
}
|
||||
|
||||
// Elimina aviso
|
||||
function remove_notice () {
|
||||
let notice = document.querySelector('#notice');
|
||||
console.log('notice', notice)
|
||||
if (notice !== null) {
|
||||
notice.parentNode.removeChild(notice);
|
||||
}
|
||||
}
|
||||
|
||||
// Implementa el reproductor
|
||||
const player = new Plyr('#player', {
|
||||
keyboard: { focused: true, global: true },
|
||||
controls: ['play-large', 'play', 'progress', 'current-time', 'mute',
|
||||
'volume', 'pip', 'airplay', 'download', 'fullscreen'],
|
||||
listeners: { seek: add_notice() },
|
||||
});
|
|
@ -1,5 +1,6 @@
|
|||
from django.shortcuts import render
|
||||
from django.http import HttpResponse
|
||||
from django.http import JsonResponse
|
||||
from django.db.models import F
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
|
||||
|
@ -7,58 +8,39 @@ from .models import Movie, Person
|
|||
|
||||
|
||||
def home(request):
|
||||
context = {"sections": Movie.objects.home_pick()}
|
||||
return render(request, "home.html", context)
|
||||
|
||||
|
||||
def search(request):
|
||||
if request.GET.get("q"):
|
||||
query = request.GET["q"]
|
||||
else:
|
||||
query = ""
|
||||
context = {"movies": Movie.objects.get_movies(query), "query": query}
|
||||
return render(request, "search.html", context)
|
||||
|
||||
|
||||
def about(request):
|
||||
context = {}
|
||||
return render(request, 'home.html', context)
|
||||
return render(request, "about.html", context)
|
||||
|
||||
|
||||
def movies(request, args=''):
|
||||
if args:
|
||||
try:
|
||||
id = int(args)
|
||||
return by_id(request, id)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
count = 0
|
||||
data = Movie.objects.to_dict(args.strip())
|
||||
directors = Person.objects.directors()
|
||||
if data:
|
||||
count = (len(data) - 1) * 4 + (len(data[-1]))
|
||||
context = {'movies': data, 'args': args, 'count': count,
|
||||
'directors': directors, 'selected_director': ''}
|
||||
return render(request, 'movies.html', context)
|
||||
def help(request):
|
||||
context = {}
|
||||
return render(request, "help.html", context)
|
||||
|
||||
|
||||
def by_id(request, id):
|
||||
count = 0
|
||||
data = Movie.objects.to_dict(id=id)
|
||||
directors = Person.objects.directors()
|
||||
if data:
|
||||
count = (len(data) - 1) * 4 + (len(data[-1]))
|
||||
context = {'movies': data, 'args': id, 'count': count,
|
||||
'directors': directors, 'selected_director': ''}
|
||||
return render(request, 'movies.html', context)
|
||||
def bugs(request):
|
||||
context = {}
|
||||
return render(request, "bugs.html", context)
|
||||
|
||||
|
||||
def by_director(request, name):
|
||||
data = Movie.objects.to_dict(director=name)
|
||||
directors = Person.objects.directors()
|
||||
count = (len(data) - 1) * 4 + (len(data[-1]))
|
||||
context = {'movies': data, 'args': 'by_director', 'count': count,
|
||||
'directors': directors, 'selected_director': name}
|
||||
return render(request, 'movies.html', context)
|
||||
def movie(request, id):
|
||||
context = {"movie": Movie.objects.get_movie_by_id(id)}
|
||||
return render(request, "movie.html", context)
|
||||
|
||||
|
||||
def addcount(request, id):
|
||||
response = ''
|
||||
if id:
|
||||
try:
|
||||
movie = Movie.objects.get(id=id)
|
||||
count = movie.count
|
||||
movie.count = F('count') + 1
|
||||
movie.save()
|
||||
response = count + 1
|
||||
except Movie.DoesNotExist:
|
||||
pass
|
||||
|
||||
return HttpResponse(response)
|
||||
def api(request):
|
||||
context = Movie.objects.api(request)
|
||||
return JsonResponse(context)
|
||||
|
|
|
@ -5,7 +5,7 @@ import sys
|
|||
|
||||
|
||||
def main():
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mauflix.settings')
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mauflix.settings")
|
||||
try:
|
||||
from django.core.management import execute_from_command_line
|
||||
except ImportError as exc:
|
||||
|
@ -17,5 +17,5 @@ def main():
|
|||
execute_from_command_line(sys.argv)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
|
@ -2,4 +2,4 @@
|
|||
|
||||
|
||||
# ~ class SuitConfig(DjangoSuitConfig):
|
||||
# ~ layout = 'horizontal'
|
||||
# ~ layout = 'horizontal'
|
||||
|
|
|
@ -21,70 +21,72 @@ from .conf import (
|
|||
TOKEN_TELEGRAM,
|
||||
CHAT_ID,
|
||||
API_TOKEN,
|
||||
)
|
||||
URL_DEBUG,
|
||||
)
|
||||
|
||||
|
||||
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
|
||||
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
PATH_TEMPLATES = os.path.join(BASE_DIR, 'templates')
|
||||
STATIC_ROOT = os.path.join(BASE_DIR, 'static/')
|
||||
MEDIA_ROOT = os.path.join(BASE_DIR, 'media/')
|
||||
MEDIA_URL = 'media/'
|
||||
PATH_TEMPLATES = os.path.join(BASE_DIR, "templates")
|
||||
STATIC_ROOT = os.path.join(BASE_DIR, "static/")
|
||||
MEDIA_ROOT = os.path.join(BASE_DIR, "media/")
|
||||
MEDIA_URL = "media/"
|
||||
|
||||
|
||||
ALLOWED_HOSTS = ['mauflix.elmau.net', 'elmau.net']
|
||||
ALLOWED_HOSTS = ["mauflix.elmau.net", "elmau.net"]
|
||||
if DEBUG:
|
||||
ALLOWED_HOSTS = ['*']
|
||||
ALLOWED_HOSTS = ["*"]
|
||||
|
||||
|
||||
# Application definition
|
||||
|
||||
INSTALLED_APPS = [
|
||||
'django_admin_listfilter_dropdown',
|
||||
'django.contrib.admin',
|
||||
'django.contrib.auth',
|
||||
'django.contrib.contenttypes',
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
'main.apps.MainConfig',
|
||||
"django_admin_listfilter_dropdown",
|
||||
"django.contrib.admin",
|
||||
"django.contrib.auth",
|
||||
"django.contrib.contenttypes",
|
||||
"django.contrib.sessions",
|
||||
"django.contrib.messages",
|
||||
"django.contrib.staticfiles",
|
||||
"django.contrib.postgres",
|
||||
"main.apps.MainConfig",
|
||||
]
|
||||
|
||||
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',
|
||||
"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 = 'mauflix.urls'
|
||||
ROOT_URLCONF = "mauflix.urls"
|
||||
|
||||
TEMPLATES = [
|
||||
{
|
||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||
'DIRS': [PATH_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',
|
||||
"BACKEND": "django.template.backends.django.DjangoTemplates",
|
||||
"DIRS": [PATH_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 = 'mauflix.wsgi.application'
|
||||
WSGI_APPLICATION = "mauflix.wsgi.application"
|
||||
|
||||
|
||||
# Database
|
||||
# https://docs.djangoproject.com/en/2.2/ref/settings/#databases
|
||||
|
||||
DATABASES = {'default': DEFAULT_DB}
|
||||
DATABASES = {"default": DEFAULT_DB}
|
||||
|
||||
|
||||
# Password validation
|
||||
|
@ -92,16 +94,16 @@ DATABASES = {'default': DEFAULT_DB}
|
|||
|
||||
AUTH_PASSWORD_VALIDATORS = [
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
|
||||
"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
|
||||
"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
|
||||
"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
|
||||
"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",
|
||||
},
|
||||
]
|
||||
|
||||
|
@ -109,9 +111,9 @@ AUTH_PASSWORD_VALIDATORS = [
|
|||
# Internationalization
|
||||
# https://docs.djangoproject.com/en/2.2/topics/i18n/
|
||||
|
||||
LANGUAGE_CODE = 'es-MX'
|
||||
LANGUAGE_CODE = "es-MX"
|
||||
|
||||
TIME_ZONE = 'America/Mexico_City'
|
||||
TIME_ZONE = "America/Mexico_City"
|
||||
|
||||
USE_I18N = True
|
||||
|
||||
|
@ -123,24 +125,24 @@ USE_TZ = False
|
|||
# Static files (CSS, JavaScript, Images)
|
||||
# https://docs.djangoproject.com/en/2.2/howto/static-files/
|
||||
|
||||
STATIC_URL = '/static/'
|
||||
# ~ STATICFILES_DIRS = [STATIC_ROOT]
|
||||
STATIC_URL = "/static/"
|
||||
STATICFILES_DIRS = [
|
||||
os.path.join(BASE_DIR, "main/static/"),
|
||||
]
|
||||
|
||||
DISALLOWED_USER_AGENTS = (
|
||||
re.compile(r'.*Googlebot', re.IGNORECASE),
|
||||
re.compile(r'.*Spider', re.IGNORECASE),
|
||||
re.compile(r'.*bingbot', re.IGNORECASE),
|
||||
re.compile(r'.*MJ12bot', re.IGNORECASE),
|
||||
re.compile(r'.*Slurp', re.IGNORECASE),
|
||||
re.compile(r'.*python-requests', re.IGNORECASE),
|
||||
re.compile(r'.*Netcraft', re.IGNORECASE),
|
||||
re.compile(r'.*AhrefsBot', re.IGNORECASE),
|
||||
re.compile(r".*Googlebot", re.IGNORECASE),
|
||||
re.compile(r".*Spider", re.IGNORECASE),
|
||||
re.compile(r".*bingbot", re.IGNORECASE),
|
||||
re.compile(r".*MJ12bot", re.IGNORECASE),
|
||||
re.compile(r".*Slurp", re.IGNORECASE),
|
||||
re.compile(r".*python-requests", re.IGNORECASE),
|
||||
re.compile(r".*Netcraft", re.IGNORECASE),
|
||||
re.compile(r".*AhrefsBot", re.IGNORECASE),
|
||||
)
|
||||
|
||||
SESSION_EXPIRE_AT_BROWSER_CLOSE = True
|
||||
SESSION_COOKIE_AGE = 3600
|
||||
|
||||
|
||||
# ~ URL_CDN = 'https://sos-ch-dk-2.exo.io/mauflix/{}/{}'
|
||||
URL_CDN = 'https://mauflix.elmau.net/{}'
|
||||
URL_MASTODON = 'https://mstdn.mx/'
|
||||
URL_CDN = "https://mauflix.elmau.net/{}"
|
||||
URL_MASTODON = "https://mstdn.mx/"
|
||||
|
|
|
@ -1,29 +1,20 @@
|
|||
|
||||
from django.contrib import admin
|
||||
from django.urls import path
|
||||
from django.conf import settings
|
||||
from django.conf.urls.static import static
|
||||
from django.conf.urls import include
|
||||
|
||||
from tastypie.api import Api
|
||||
|
||||
from main import views
|
||||
from main.feeds import LatestMoviesFeed
|
||||
from main.api import ResourceMovies
|
||||
|
||||
|
||||
v1_api = Api(api_name='v1')
|
||||
v1_api.register(ResourceMovies())
|
||||
|
||||
|
||||
urlpatterns = [
|
||||
path('', views.home, name='home'),
|
||||
path('movies/', views.movies, name='movies'),
|
||||
path('movies/<str:args>', views.movies, name='movies'),
|
||||
path('movies/director/<str:name>', views.by_director, name='by_director'),
|
||||
path('addcount/<int:id>/', views.addcount, name='addcount'),
|
||||
path('ultimas/rss/', LatestMoviesFeed()),
|
||||
path('admin/', admin.site.urls),
|
||||
path('api/', include(v1_api.urls)),
|
||||
path("", views.home, name="home"),
|
||||
path("search/", views.search, name="search"),
|
||||
path("api/", views.api, name="api"),
|
||||
path("about/", views.about, name="about"),
|
||||
path("help/", views.help, name="help"),
|
||||
path("bugs/", views.bugs, name="bugs"),
|
||||
path("movie/<int:id>", views.movie, name="movie"),
|
||||
path("ultimas/rss/", LatestMoviesFeed()),
|
||||
path("admin/", admin.site.urls),
|
||||
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||
|
||||
|
|
|
@ -11,6 +11,6 @@ import os
|
|||
|
||||
from django.core.wsgi import get_wsgi_application
|
||||
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mauflix.settings')
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mauflix.settings")
|
||||
|
||||
application = get_wsgi_application()
|
||||
|
|
|
@ -14,7 +14,7 @@ select.admin-autocomplete {
|
|||
|
||||
.select2-container--admin-autocomplete.select2-container--focus .select2-selection,
|
||||
.select2-container--admin-autocomplete.select2-container--open .select2-selection {
|
||||
border-color: #999;
|
||||
border-color: var(--body-quiet-color);
|
||||
min-height: 30px;
|
||||
}
|
||||
|
||||
|
@ -29,13 +29,13 @@ select.admin-autocomplete {
|
|||
}
|
||||
|
||||
.select2-container--admin-autocomplete .select2-selection--single {
|
||||
background-color: #fff;
|
||||
border: 1px solid #ccc;
|
||||
background-color: var(--body-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.select2-container--admin-autocomplete .select2-selection--single .select2-selection__rendered {
|
||||
color: #444;
|
||||
color: var(--body-fg);
|
||||
line-height: 30px;
|
||||
}
|
||||
|
||||
|
@ -46,7 +46,7 @@ select.admin-autocomplete {
|
|||
}
|
||||
|
||||
.select2-container--admin-autocomplete .select2-selection--single .select2-selection__placeholder {
|
||||
color: #999;
|
||||
color: var(--body-quiet-color);
|
||||
}
|
||||
|
||||
.select2-container--admin-autocomplete .select2-selection--single .select2-selection__arrow {
|
||||
|
@ -80,7 +80,7 @@ select.admin-autocomplete {
|
|||
}
|
||||
|
||||
.select2-container--admin-autocomplete.select2-container--disabled .select2-selection--single {
|
||||
background-color: #eee;
|
||||
background-color: var(--darkened-bg);
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
|
@ -94,8 +94,8 @@ select.admin-autocomplete {
|
|||
}
|
||||
|
||||
.select2-container--admin-autocomplete .select2-selection--multiple {
|
||||
background-color: white;
|
||||
border: 1px solid #ccc;
|
||||
background-color: var(--body-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
cursor: text;
|
||||
}
|
||||
|
@ -104,8 +104,10 @@ select.admin-autocomplete {
|
|||
box-sizing: border-box;
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0 5px;
|
||||
padding: 0 10px 5px 5px;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.select2-container--admin-autocomplete .select2-selection--multiple .select2-selection__rendered li {
|
||||
|
@ -113,7 +115,7 @@ select.admin-autocomplete {
|
|||
}
|
||||
|
||||
.select2-container--admin-autocomplete .select2-selection--multiple .select2-selection__placeholder {
|
||||
color: #999;
|
||||
color: var(--body-quiet-color);
|
||||
margin-top: 5px;
|
||||
float: left;
|
||||
}
|
||||
|
@ -123,11 +125,13 @@ select.admin-autocomplete {
|
|||
float: right;
|
||||
font-weight: bold;
|
||||
margin: 5px;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.select2-container--admin-autocomplete .select2-selection--multiple .select2-selection__choice {
|
||||
background-color: #e4e4e4;
|
||||
border: 1px solid #ccc;
|
||||
background-color: var(--darkened-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
cursor: default;
|
||||
float: left;
|
||||
|
@ -137,7 +141,7 @@ select.admin-autocomplete {
|
|||
}
|
||||
|
||||
.select2-container--admin-autocomplete .select2-selection--multiple .select2-selection__choice__remove {
|
||||
color: #999;
|
||||
color: var(--body-quiet-color);
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
font-weight: bold;
|
||||
|
@ -145,7 +149,7 @@ select.admin-autocomplete {
|
|||
}
|
||||
|
||||
.select2-container--admin-autocomplete .select2-selection--multiple .select2-selection__choice__remove:hover {
|
||||
color: #333;
|
||||
color: var(--body-fg);
|
||||
}
|
||||
|
||||
.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 {
|
||||
|
@ -163,12 +167,12 @@ select.admin-autocomplete {
|
|||
}
|
||||
|
||||
.select2-container--admin-autocomplete.select2-container--focus .select2-selection--multiple {
|
||||
border: solid #999 1px;
|
||||
border: solid var(--body-quiet-color) 1px;
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
.select2-container--admin-autocomplete.select2-container--disabled .select2-selection--multiple {
|
||||
background-color: #eee;
|
||||
background-color: var(--darkened-bg);
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
|
@ -186,12 +190,20 @@ select.admin-autocomplete {
|
|||
border-bottom-right-radius: 0;
|
||||
}
|
||||
|
||||
.select2-container--admin-autocomplete .select2-search--dropdown {
|
||||
background: var(--darkened-bg);
|
||||
}
|
||||
|
||||
.select2-container--admin-autocomplete .select2-search--dropdown .select2-search__field {
|
||||
border: 1px solid #ccc;
|
||||
background: var(--body-bg);
|
||||
color: var(--body-fg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.select2-container--admin-autocomplete .select2-search--inline .select2-search__field {
|
||||
background: transparent;
|
||||
color: var(--body-fg);
|
||||
border: none;
|
||||
outline: 0;
|
||||
box-shadow: none;
|
||||
|
@ -201,6 +213,8 @@ select.admin-autocomplete {
|
|||
.select2-container--admin-autocomplete .select2-results > .select2-results__options {
|
||||
max-height: 200px;
|
||||
overflow-y: auto;
|
||||
color: var(--body-fg);
|
||||
background: var(--body-bg);
|
||||
}
|
||||
|
||||
.select2-container--admin-autocomplete .select2-results__option[role=group] {
|
||||
|
@ -208,11 +222,12 @@ select.admin-autocomplete {
|
|||
}
|
||||
|
||||
.select2-container--admin-autocomplete .select2-results__option[aria-disabled=true] {
|
||||
color: #999;
|
||||
color: var(--body-quiet-color);
|
||||
}
|
||||
|
||||
.select2-container--admin-autocomplete .select2-results__option[aria-selected=true] {
|
||||
background-color: #ddd;
|
||||
background-color: var(--selected-bg);
|
||||
color: var(--body-fg);
|
||||
}
|
||||
|
||||
.select2-container--admin-autocomplete .select2-results__option .select2-results__option {
|
||||
|
@ -249,8 +264,8 @@ select.admin-autocomplete {
|
|||
}
|
||||
|
||||
.select2-container--admin-autocomplete .select2-results__option--highlighted[aria-selected] {
|
||||
background-color: #79aec8;
|
||||
color: white;
|
||||
background-color: var(--primary);
|
||||
color: var(--primary-fg);
|
||||
}
|
||||
|
||||
.select2-container--admin-autocomplete .select2-results__group {
|
||||
|
|
|
@ -4,6 +4,93 @@
|
|||
|
||||
@import url(fonts.css);
|
||||
|
||||
/* VARIABLE DEFINITIONS */
|
||||
:root {
|
||||
--primary: #79aec8;
|
||||
--secondary: #417690;
|
||||
--accent: #f5dd5d;
|
||||
--primary-fg: #fff;
|
||||
|
||||
--body-fg: #333;
|
||||
--body-bg: #fff;
|
||||
--body-quiet-color: #666;
|
||||
--body-loud-color: #000;
|
||||
|
||||
--header-color: #ffc;
|
||||
--header-branding-color: var(--accent);
|
||||
--header-bg: var(--secondary);
|
||||
--header-link-color: var(--primary-fg);
|
||||
|
||||
--breadcrumbs-fg: #c4dce8;
|
||||
--breadcrumbs-link-fg: var(--body-bg);
|
||||
--breadcrumbs-bg: var(--primary);
|
||||
|
||||
--link-fg: #447e9b;
|
||||
--link-hover-color: #036;
|
||||
--link-selected-fg: #5b80b2;
|
||||
|
||||
--hairline-color: #e8e8e8;
|
||||
--border-color: #ccc;
|
||||
|
||||
--error-fg: #ba2121;
|
||||
|
||||
--message-success-bg: #dfd;
|
||||
--message-warning-bg: #ffc;
|
||||
--message-error-bg: #ffefef;
|
||||
|
||||
--darkened-bg: #f8f8f8; /* A bit darker than --body-bg */
|
||||
--selected-bg: #e4e4e4; /* E.g. selected table cells */
|
||||
--selected-row: #ffc;
|
||||
|
||||
--button-fg: #fff;
|
||||
--button-bg: var(--primary);
|
||||
--button-hover-bg: #609ab6;
|
||||
--default-button-bg: var(--secondary);
|
||||
--default-button-hover-bg: #205067;
|
||||
--close-button-bg: #888; /* Previously #bbb, contrast 1.92 */
|
||||
--close-button-hover-bg: #747474;
|
||||
--delete-button-bg: #ba2121;
|
||||
--delete-button-hover-bg: #a41515;
|
||||
|
||||
--object-tools-fg: var(--button-fg);
|
||||
--object-tools-bg: var(--close-button-bg);
|
||||
--object-tools-hover-bg: var(--close-button-hover-bg);
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--primary: #264b5d;
|
||||
--primary-fg: #eee;
|
||||
|
||||
--body-fg: #eeeeee;
|
||||
--body-bg: #121212;
|
||||
--body-quiet-color: #e0e0e0;
|
||||
--body-loud-color: #ffffff;
|
||||
|
||||
--breadcrumbs-link-fg: #e0e0e0;
|
||||
--breadcrumbs-bg: var(--primary);
|
||||
|
||||
--link-fg: #81d4fa;
|
||||
--link-hover-color: #4ac1f7;
|
||||
--link-selected-fg: #6f94c6;
|
||||
|
||||
--hairline-color: #272727;
|
||||
--border-color: #353535;
|
||||
|
||||
--error-fg: #e35f5f;
|
||||
--message-success-bg: #006b1b;
|
||||
--message-warning-bg: #583305;
|
||||
--message-error-bg: #570808;
|
||||
|
||||
--darkened-bg: #212121;
|
||||
--selected-bg: #1b1b1b;
|
||||
--selected-row: #00363a;
|
||||
|
||||
--close-button-bg: #333333;
|
||||
--close-button-hover-bg: #666666;
|
||||
}
|
||||
}
|
||||
|
||||
html, body {
|
||||
height: 100%;
|
||||
}
|
||||
|
@ -13,19 +100,20 @@ body {
|
|||
padding: 0;
|
||||
font-size: 14px;
|
||||
font-family: "Roboto","Lucida Grande","DejaVu Sans","Bitstream Vera Sans",Verdana,Arial,sans-serif;
|
||||
color: #333;
|
||||
background: #fff;
|
||||
color: var(--body-fg);
|
||||
background: var(--body-bg);
|
||||
}
|
||||
|
||||
/* LINKS */
|
||||
|
||||
a:link, a:visited {
|
||||
color: #447e9b;
|
||||
color: var(--link-fg);
|
||||
text-decoration: none;
|
||||
transition: color 0.15s, background 0.15s;
|
||||
}
|
||||
|
||||
a:focus, a:hover {
|
||||
color: #036;
|
||||
color: var(--link-hover-color);
|
||||
}
|
||||
|
||||
a:focus {
|
||||
|
@ -37,7 +125,7 @@ a img {
|
|||
}
|
||||
|
||||
a.section:link, a.section:visited {
|
||||
color: #fff;
|
||||
color: var(--header-link-color);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
|
@ -64,7 +152,7 @@ h1 {
|
|||
margin: 0 0 20px;
|
||||
font-weight: 300;
|
||||
font-size: 20px;
|
||||
color: #666;
|
||||
color: var(--body-quiet-color);
|
||||
}
|
||||
|
||||
h2 {
|
||||
|
@ -80,7 +168,7 @@ h2.subhead {
|
|||
h3 {
|
||||
font-size: 14px;
|
||||
margin: .8em 0 .3em 0;
|
||||
color: #666;
|
||||
color: var(--body-quiet-color);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
|
@ -93,7 +181,7 @@ h4 {
|
|||
h5 {
|
||||
font-size: 10px;
|
||||
margin: 1.5em 0 .5em 0;
|
||||
color: #666;
|
||||
color: var(--body-quiet-color);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
@ -131,7 +219,7 @@ fieldset {
|
|||
min-width: 0;
|
||||
padding: 0;
|
||||
border: none;
|
||||
border-top: 1px solid #eee;
|
||||
border-top: 1px solid var(--hairline-color);
|
||||
}
|
||||
|
||||
blockquote {
|
||||
|
@ -144,14 +232,14 @@ blockquote {
|
|||
|
||||
code, pre {
|
||||
font-family: "Bitstream Vera Sans Mono", Monaco, "Courier New", Courier, monospace;
|
||||
color: #666;
|
||||
color: var(--body-quiet-color);
|
||||
font-size: 12px;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
pre.literal-block {
|
||||
margin: 10px;
|
||||
background: #eee;
|
||||
background: var(--darkened-bg);
|
||||
padding: 6px 8px;
|
||||
}
|
||||
|
||||
|
@ -161,8 +249,8 @@ code strong {
|
|||
|
||||
hr {
|
||||
clear: both;
|
||||
color: #eee;
|
||||
background-color: #eee;
|
||||
color: var(--hairline-color);
|
||||
background-color: var(--hairline-color);
|
||||
height: 1px;
|
||||
border: none;
|
||||
margin: 0;
|
||||
|
@ -183,7 +271,7 @@ hr {
|
|||
|
||||
.help, p.help, form p.help, div.help, form div.help, div.help li {
|
||||
font-size: 11px;
|
||||
color: #999;
|
||||
color: var(--body-quiet-color);
|
||||
}
|
||||
|
||||
div.help ul {
|
||||
|
@ -199,7 +287,7 @@ p img, h1 img, h2 img, h3 img, h4 img, td img {
|
|||
}
|
||||
|
||||
.quiet, a.quiet:link, a.quiet:visited {
|
||||
color: #999;
|
||||
color: var(--body-quiet-color);
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
|
@ -211,20 +299,23 @@ p img, h1 img, h2 img, h3 img, h4 img, td img {
|
|||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* TABLES */
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
border-color: #ccc;
|
||||
border-color: var(--border-color);
|
||||
}
|
||||
|
||||
td, th {
|
||||
font-size: 13px;
|
||||
line-height: 16px;
|
||||
border-bottom: 1px solid #eee;
|
||||
border-bottom: 1px solid var(--hairline-color);
|
||||
vertical-align: top;
|
||||
padding: 8px;
|
||||
font-family: "Roboto", "Lucida Grande", Verdana, Arial, sans-serif;
|
||||
}
|
||||
|
||||
th {
|
||||
|
@ -234,37 +325,37 @@ th {
|
|||
|
||||
thead th,
|
||||
tfoot td {
|
||||
color: #666;
|
||||
color: var(--body-quiet-color);
|
||||
padding: 5px 10px;
|
||||
font-size: 11px;
|
||||
background: #fff;
|
||||
background: var(--body-bg);
|
||||
border: none;
|
||||
border-top: 1px solid #eee;
|
||||
border-bottom: 1px solid #eee;
|
||||
border-top: 1px solid var(--hairline-color);
|
||||
border-bottom: 1px solid var(--hairline-color);
|
||||
}
|
||||
|
||||
tfoot td {
|
||||
border-bottom: none;
|
||||
border-top: 1px solid #eee;
|
||||
border-top: 1px solid var(--hairline-color);
|
||||
}
|
||||
|
||||
thead th.required {
|
||||
color: #000;
|
||||
color: var(--body-loud-color);
|
||||
}
|
||||
|
||||
tr.alt {
|
||||
background: #f6f6f6;
|
||||
background: var(--darkened-bg);
|
||||
}
|
||||
|
||||
tr:nth-child(odd), .row-form-errors {
|
||||
background: #fff;
|
||||
background: var(--body-bg);
|
||||
}
|
||||
|
||||
tr:nth-child(even),
|
||||
tr:nth-child(even) .errorlist,
|
||||
tr:nth-child(odd) + .row-form-errors,
|
||||
tr:nth-child(odd) + .row-form-errors .errorlist {
|
||||
background: #f9f9f9;
|
||||
background: var(--darkened-bg);
|
||||
}
|
||||
|
||||
/* SORTABLE TABLES */
|
||||
|
@ -273,15 +364,15 @@ thead th {
|
|||
padding: 5px 10px;
|
||||
line-height: normal;
|
||||
text-transform: uppercase;
|
||||
background: #f6f6f6;
|
||||
background: var(--darkened-bg);
|
||||
}
|
||||
|
||||
thead th a:link, thead th a:visited {
|
||||
color: #666;
|
||||
color: var(--body-quiet-color);
|
||||
}
|
||||
|
||||
thead th.sorted {
|
||||
background: #eee;
|
||||
background: var(--selected-bg);
|
||||
}
|
||||
|
||||
thead th.sorted .text {
|
||||
|
@ -300,7 +391,7 @@ table thead th .text a {
|
|||
}
|
||||
|
||||
table thead th .text a:focus, table thead th .text a:hover {
|
||||
background: #eee;
|
||||
background: var(--selected-bg);
|
||||
}
|
||||
|
||||
thead th.sorted a.sortremove {
|
||||
|
@ -347,12 +438,12 @@ table thead th.sorted .sortoptions a.sortremove:after {
|
|||
left: 3px;
|
||||
font-weight: 200;
|
||||
font-size: 18px;
|
||||
color: #999;
|
||||
color: var(--body-quiet-color);
|
||||
}
|
||||
|
||||
table thead th.sorted .sortoptions a.sortremove:focus:after,
|
||||
table thead th.sorted .sortoptions a.sortremove:hover:after {
|
||||
color: #447e9b;
|
||||
color: var(--link-fg);
|
||||
}
|
||||
|
||||
table thead th.sorted .sortoptions a.sortremove:focus,
|
||||
|
@ -399,16 +490,18 @@ textarea {
|
|||
|
||||
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: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
padding: 5px 6px;
|
||||
margin-top: 0;
|
||||
color: var(--body-fg);
|
||||
background-color: var(--body-bg);
|
||||
}
|
||||
|
||||
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;
|
||||
border-color: var(--body-quiet-color);
|
||||
}
|
||||
|
||||
select {
|
||||
|
@ -424,12 +517,13 @@ select[multiple] {
|
|||
/* FORM BUTTONS */
|
||||
|
||||
.button, input[type=submit], input[type=button], .submit-row input, a.button {
|
||||
background: #79aec8;
|
||||
background: var(--button-bg);
|
||||
padding: 10px 15px;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
color: #fff;
|
||||
color: var(--button-fg);
|
||||
cursor: pointer;
|
||||
transition: background 0.15s;
|
||||
}
|
||||
|
||||
a.button {
|
||||
|
@ -439,7 +533,7 @@ a.button {
|
|||
.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;
|
||||
background: var(--button-hover-bg);
|
||||
}
|
||||
|
||||
.button[disabled], input[type=submit][disabled], input[type=button][disabled] {
|
||||
|
@ -450,13 +544,13 @@ a.button {
|
|||
float: right;
|
||||
border: none;
|
||||
font-weight: 400;
|
||||
background: #417690;
|
||||
background: var(--default-button-bg);
|
||||
}
|
||||
|
||||
.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;
|
||||
background: var(--default-button-hover-bg);
|
||||
}
|
||||
|
||||
.button[disabled].default,
|
||||
|
@ -471,7 +565,7 @@ input[type=button][disabled].default {
|
|||
.module {
|
||||
border: none;
|
||||
margin-bottom: 30px;
|
||||
background: #fff;
|
||||
background: var(--body-bg);
|
||||
}
|
||||
|
||||
.module p, .module ul, .module h3, .module h4, .module dl, .module pre {
|
||||
|
@ -497,8 +591,8 @@ input[type=button][disabled].default {
|
|||
font-weight: 400;
|
||||
font-size: 13px;
|
||||
text-align: left;
|
||||
background: #79aec8;
|
||||
color: #fff;
|
||||
background: var(--primary);
|
||||
color: var(--header-link-color);
|
||||
}
|
||||
|
||||
.module caption,
|
||||
|
@ -525,18 +619,18 @@ ul.messagelist li {
|
|||
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: var(--message-success-bg) url(../img/icon-yes.svg) 40px 12px no-repeat;
|
||||
background-size: 16px auto;
|
||||
color: #333;
|
||||
color: var(--body-fg);
|
||||
}
|
||||
|
||||
ul.messagelist li.warning {
|
||||
background: #ffc url(../img/icon-alert.svg) 40px 14px no-repeat;
|
||||
background: var(--message-warning-bg) 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: var(--message-error-bg) url(../img/icon-no.svg) 40px 12px no-repeat;
|
||||
background-size: 16px auto;
|
||||
}
|
||||
|
||||
|
@ -546,24 +640,26 @@ ul.messagelist li.error {
|
|||
display: block;
|
||||
padding: 10px 12px;
|
||||
margin: 0 0 10px 0;
|
||||
color: #ba2121;
|
||||
border: 1px solid #ba2121;
|
||||
color: var(--error-fg);
|
||||
border: 1px solid var(--error-fg);
|
||||
border-radius: 4px;
|
||||
background-color: #fff;
|
||||
background-color: var(--body-bg);
|
||||
background-position: 5px 12px;
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
ul.errorlist {
|
||||
margin: 0 0 4px;
|
||||
padding: 0;
|
||||
color: #ba2121;
|
||||
background: #fff;
|
||||
color: var(--error-fg);
|
||||
background: var(--body-bg);
|
||||
}
|
||||
|
||||
ul.errorlist li {
|
||||
font-size: 13px;
|
||||
display: block;
|
||||
margin-bottom: 4px;
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
ul.errorlist li:first-child {
|
||||
|
@ -587,7 +683,7 @@ td ul.errorlist li {
|
|||
.form-row.errors {
|
||||
margin: 0;
|
||||
border: none;
|
||||
border-bottom: 1px solid #eee;
|
||||
border-bottom: 1px solid var(--hairline-color);
|
||||
background: none;
|
||||
}
|
||||
|
||||
|
@ -597,7 +693,7 @@ td ul.errorlist li {
|
|||
|
||||
.errors input, .errors select, .errors textarea,
|
||||
td ul.errorlist + input, td ul.errorlist + select, td ul.errorlist + textarea {
|
||||
border: 1px solid #ba2121;
|
||||
border: 1px solid var(--error-fg);
|
||||
}
|
||||
|
||||
.description {
|
||||
|
@ -608,20 +704,19 @@ td ul.errorlist + input, td ul.errorlist + select, td ul.errorlist + textarea {
|
|||
/* BREADCRUMBS */
|
||||
|
||||
div.breadcrumbs {
|
||||
background: #79aec8;
|
||||
background: var(--breadcrumbs-bg);
|
||||
padding: 10px 40px;
|
||||
border: none;
|
||||
font-size: 14px;
|
||||
color: #c4dce8;
|
||||
color: var(--breadcrumbs-fg);
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
div.breadcrumbs a {
|
||||
color: #fff;
|
||||
color: var(--breadcrumbs-link-fg);
|
||||
}
|
||||
|
||||
div.breadcrumbs a:focus, div.breadcrumbs a:hover {
|
||||
color: #c4dce8;
|
||||
color: var(--breadcrumbs-fg);
|
||||
}
|
||||
|
||||
/* ACTION ICONS */
|
||||
|
@ -647,11 +742,11 @@ div.breadcrumbs a:focus, div.breadcrumbs a:hover {
|
|||
}
|
||||
|
||||
a.deletelink:link, a.deletelink:visited {
|
||||
color: #CC3434;
|
||||
color: #CC3434; /* XXX Probably unused? */
|
||||
}
|
||||
|
||||
a.deletelink:focus, a.deletelink:hover {
|
||||
color: #993333;
|
||||
color: #993333; /* XXX Probably unused? */
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
|
@ -666,14 +761,6 @@ a.deletelink:focus, a.deletelink:hover {
|
|||
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;
|
||||
|
@ -689,29 +776,29 @@ a.deletelink:focus, a.deletelink:hover {
|
|||
display: block;
|
||||
float: left;
|
||||
padding: 3px 12px;
|
||||
background: #999;
|
||||
background: var(--object-tools-bg);
|
||||
color: var(--object-tools-fg);
|
||||
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;
|
||||
background-color: var(--object-tools-hover-bg);
|
||||
}
|
||||
|
||||
.object-tools a:focus{
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.object-tools a.viewsitelink, .object-tools a.golink,.object-tools a.addlink {
|
||||
.object-tools a.viewsitelink, .object-tools a.addlink {
|
||||
background-repeat: no-repeat;
|
||||
background-position: right 7px center;
|
||||
padding-right: 26px;
|
||||
}
|
||||
|
||||
.object-tools a.viewsitelink, .object-tools a.golink {
|
||||
.object-tools a.viewsitelink {
|
||||
background-image: url(../img/tooltag-arrowright.svg);
|
||||
}
|
||||
|
||||
|
@ -813,13 +900,13 @@ table#change-history tbody th {
|
|||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 10px 40px;
|
||||
background: #417690;
|
||||
color: #ffc;
|
||||
background: var(--header-bg);
|
||||
color: var(--header-color);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#header a:link, #header a:visited {
|
||||
color: #fff;
|
||||
color: var(--header-link-color);
|
||||
}
|
||||
|
||||
#header a:focus , #header a:hover {
|
||||
|
@ -835,11 +922,11 @@ table#change-history tbody th {
|
|||
margin: 0 20px 0 0;
|
||||
font-weight: 300;
|
||||
font-size: 24px;
|
||||
color: #f5dd5d;
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
#branding h1, #branding h1 a:link, #branding h1 a:visited {
|
||||
color: #f5dd5d;
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
#branding h2 {
|
||||
|
@ -847,7 +934,7 @@ table#change-history tbody th {
|
|||
font-size: 14px;
|
||||
margin: -8px 0 8px 0;
|
||||
font-weight: normal;
|
||||
color: #ffc;
|
||||
color: var(--header-color);
|
||||
}
|
||||
|
||||
#branding a:hover {
|
||||
|
@ -871,14 +958,14 @@ table#change-history tbody th {
|
|||
|
||||
#user-tools a:focus, #user-tools a:hover {
|
||||
text-decoration: none;
|
||||
border-bottom-color: #79aec8;
|
||||
color: #79aec8;
|
||||
border-bottom-color: var(--primary);
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
/* SIDEBAR */
|
||||
|
||||
#content-related {
|
||||
background: #f8f8f8;
|
||||
background: var(--darkened-bg);
|
||||
}
|
||||
|
||||
#content-related .module {
|
||||
|
@ -886,8 +973,7 @@ table#change-history tbody th {
|
|||
}
|
||||
|
||||
#content-related h3 {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
color: var(--body-quiet-color);
|
||||
padding: 0 16px;
|
||||
margin: 0 0 16px;
|
||||
}
|
||||
|
@ -916,22 +1002,22 @@ table#change-history tbody th {
|
|||
background: none;
|
||||
padding: 16px;
|
||||
margin-bottom: 16px;
|
||||
border-bottom: 1px solid #eaeaea;
|
||||
border-bottom: 1px solid var(--hairline-color);
|
||||
font-size: 18px;
|
||||
color: #333;
|
||||
color: var(--body-fg);
|
||||
}
|
||||
|
||||
.delete-confirmation form input[type="submit"] {
|
||||
background: #ba2121;
|
||||
background: var(--delete-button-bg);
|
||||
border-radius: 4px;
|
||||
padding: 10px 15px;
|
||||
color: #fff;
|
||||
color: var(--button-fg);
|
||||
}
|
||||
|
||||
.delete-confirmation form input[type="submit"]:active,
|
||||
.delete-confirmation form input[type="submit"]:focus,
|
||||
.delete-confirmation form input[type="submit"]:hover {
|
||||
background: #a41515;
|
||||
background: var(--delete-button-hover-bg);
|
||||
}
|
||||
|
||||
.delete-confirmation form .cancel-link {
|
||||
|
@ -939,17 +1025,17 @@ table#change-history tbody th {
|
|||
vertical-align: middle;
|
||||
height: 15px;
|
||||
line-height: 15px;
|
||||
background: #ddd;
|
||||
border-radius: 4px;
|
||||
padding: 10px 15px;
|
||||
color: #333;
|
||||
color: var(--button-fg);
|
||||
background: var(--close-button-bg);
|
||||
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;
|
||||
background: var(--close-button-hover-bg);
|
||||
}
|
||||
|
||||
/* POPUP */
|
||||
|
|
|
@ -40,13 +40,13 @@
|
|||
}
|
||||
|
||||
#changelist .toplinks {
|
||||
border-bottom: 1px solid #ddd;
|
||||
border-bottom: 1px solid var(--hairline-color);
|
||||
}
|
||||
|
||||
#changelist .paginator {
|
||||
color: #666;
|
||||
border-bottom: 1px solid #eee;
|
||||
background: #fff;
|
||||
color: var(--body-quiet-color);
|
||||
border-bottom: 1px solid var(--hairline-color);
|
||||
background: var(--body-bg);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
|
@ -68,7 +68,7 @@
|
|||
}
|
||||
|
||||
#changelist table tfoot {
|
||||
color: #666;
|
||||
color: var(--body-quiet-color);
|
||||
}
|
||||
|
||||
/* TOOLBAR */
|
||||
|
@ -76,22 +76,22 @@
|
|||
#toolbar {
|
||||
padding: 8px 10px;
|
||||
margin-bottom: 15px;
|
||||
border-top: 1px solid #eee;
|
||||
border-bottom: 1px solid #eee;
|
||||
background: #f8f8f8;
|
||||
color: #666;
|
||||
border-top: 1px solid var(--hairline-color);
|
||||
border-bottom: 1px solid var(--hairline-color);
|
||||
background: var(--darkened-bg);
|
||||
color: var(--body-quiet-color);
|
||||
}
|
||||
|
||||
#toolbar form input {
|
||||
border-radius: 4px;
|
||||
font-size: 14px;
|
||||
padding: 5px;
|
||||
color: #333;
|
||||
color: var(--body-fg);
|
||||
}
|
||||
|
||||
#toolbar #searchbar {
|
||||
height: 19px;
|
||||
border: 1px solid #ccc;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 2px 5px;
|
||||
margin: 0;
|
||||
vertical-align: top;
|
||||
|
@ -100,24 +100,24 @@
|
|||
}
|
||||
|
||||
#toolbar #searchbar:focus {
|
||||
border-color: #999;
|
||||
border-color: var(--body-quiet-color);
|
||||
}
|
||||
|
||||
#toolbar form input[type="submit"] {
|
||||
border: 1px solid #ccc;
|
||||
border: 1px solid var(--border-color);
|
||||
font-size: 13px;
|
||||
padding: 4px 8px;
|
||||
margin: 0;
|
||||
vertical-align: middle;
|
||||
background: #fff;
|
||||
background: var(--body-bg);
|
||||
box-shadow: 0 -15px 20px -10px rgba(0, 0, 0, 0.15) inset;
|
||||
cursor: pointer;
|
||||
color: #333;
|
||||
color: var(--body-fg);
|
||||
}
|
||||
|
||||
#toolbar form input[type="submit"]:focus,
|
||||
#toolbar form input[type="submit"]:hover {
|
||||
border-color: #999;
|
||||
border-color: var(--body-quiet-color);
|
||||
}
|
||||
|
||||
#changelist-search img {
|
||||
|
@ -128,9 +128,9 @@
|
|||
/* FILTER COLUMN */
|
||||
|
||||
#changelist-filter {
|
||||
flex: 0 0 240px;
|
||||
order: 1;
|
||||
width: 240px;
|
||||
background: #f8f8f8;
|
||||
background: var(--darkened-bg);
|
||||
border-left: none;
|
||||
margin: 0 0 0 30px;
|
||||
}
|
||||
|
@ -146,7 +146,6 @@
|
|||
|
||||
#changelist-filter h3 {
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
padding: 0 15px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
@ -154,7 +153,7 @@
|
|||
#changelist-filter ul {
|
||||
margin: 5px 0;
|
||||
padding: 0 15px 15px;
|
||||
border-bottom: 1px solid #eaeaea;
|
||||
border-bottom: 1px solid var(--hairline-color);
|
||||
}
|
||||
|
||||
#changelist-filter ul:last-child {
|
||||
|
@ -169,31 +168,31 @@
|
|||
|
||||
#changelist-filter a {
|
||||
display: block;
|
||||
color: #999;
|
||||
color: var(--body-quiet-color);
|
||||
text-overflow: ellipsis;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
#changelist-filter li.selected {
|
||||
border-left: 5px solid #eaeaea;
|
||||
border-left: 5px solid var(--hairline-color);
|
||||
padding-left: 10px;
|
||||
margin-left: -15px;
|
||||
}
|
||||
|
||||
#changelist-filter li.selected a {
|
||||
color: #5b80b2;
|
||||
color: var(--link-selected-fg);
|
||||
}
|
||||
|
||||
#changelist-filter a:focus, #changelist-filter a:hover,
|
||||
#changelist-filter li.selected a:focus,
|
||||
#changelist-filter li.selected a:hover {
|
||||
color: #036;
|
||||
color: var(--link-hover-color);
|
||||
}
|
||||
|
||||
#changelist-filter #changelist-filter-clear a {
|
||||
font-size: 13px;
|
||||
padding-bottom: 10px;
|
||||
border-bottom: 1px solid #eaeaea;
|
||||
border-bottom: 1px solid var(--hairline-color);
|
||||
}
|
||||
|
||||
/* DATE DRILLDOWN */
|
||||
|
@ -214,12 +213,12 @@
|
|||
}
|
||||
|
||||
.change-list ul.toplinks .date-back a {
|
||||
color: #999;
|
||||
color: var(--body-quiet-color);
|
||||
}
|
||||
|
||||
.change-list ul.toplinks .date-back a:focus,
|
||||
.change-list ul.toplinks .date-back a:hover {
|
||||
color: #036;
|
||||
color: var(--link-hover-color);
|
||||
}
|
||||
|
||||
/* PAGINATOR */
|
||||
|
@ -230,26 +229,26 @@
|
|||
padding-bottom: 10px;
|
||||
line-height: 22px;
|
||||
margin: 0;
|
||||
border-top: 1px solid #ddd;
|
||||
border-top: 1px solid var(--hairline-color);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.paginator a:link, .paginator a:visited {
|
||||
padding: 2px 6px;
|
||||
background: #79aec8;
|
||||
background: var(--button-bg);
|
||||
text-decoration: none;
|
||||
color: #fff;
|
||||
color: var(--button-fg);
|
||||
}
|
||||
|
||||
.paginator a.showall {
|
||||
border: none;
|
||||
background: none;
|
||||
color: #5b80b2;
|
||||
color: var(--link-fg);
|
||||
}
|
||||
|
||||
.paginator a.showall:focus, .paginator a.showall:hover {
|
||||
background: none;
|
||||
color: #036;
|
||||
color: var(--link-hover-color);
|
||||
}
|
||||
|
||||
.paginator .end {
|
||||
|
@ -265,7 +264,7 @@
|
|||
|
||||
.paginator a:focus, .paginator a:hover {
|
||||
color: white;
|
||||
background: #036;
|
||||
background: var(--link-hover-color);
|
||||
}
|
||||
|
||||
/* ACTIONS */
|
||||
|
@ -280,22 +279,22 @@
|
|||
}
|
||||
|
||||
#changelist table tbody tr.selected {
|
||||
background-color: #FFFFCC;
|
||||
background-color: var(--selected-row);
|
||||
}
|
||||
|
||||
#changelist .actions {
|
||||
padding: 10px;
|
||||
background: #fff;
|
||||
background: var(--body-bg);
|
||||
border-top: none;
|
||||
border-bottom: none;
|
||||
line-height: 24px;
|
||||
color: #999;
|
||||
color: var(--body-quiet-color);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#changelist .actions.selected {
|
||||
background: #fffccf;
|
||||
border-top: 1px solid #fffee8;
|
||||
#changelist .actions.selected { /* XXX Probably unused? */
|
||||
background: var(--body-bg);
|
||||
border-top: 1px solid var(--body-bg);
|
||||
border-bottom: 1px solid #edecd6;
|
||||
}
|
||||
|
||||
|
@ -305,7 +304,6 @@
|
|||
#changelist .actions span.question {
|
||||
font-size: 13px;
|
||||
margin: 0 0.5em;
|
||||
display: none;
|
||||
}
|
||||
|
||||
#changelist .actions:last-child {
|
||||
|
@ -315,9 +313,8 @@
|
|||
#changelist .actions select {
|
||||
vertical-align: top;
|
||||
height: 24px;
|
||||
background: none;
|
||||
color: #000;
|
||||
border: 1px solid #ccc;
|
||||
color: var(--body-fg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
font-size: 14px;
|
||||
padding: 0 0 0 4px;
|
||||
|
@ -326,7 +323,7 @@
|
|||
}
|
||||
|
||||
#changelist .actions select:focus {
|
||||
border-color: #999;
|
||||
border-color: var(--body-quiet-color);
|
||||
}
|
||||
|
||||
#changelist .actions label {
|
||||
|
@ -337,18 +334,18 @@
|
|||
|
||||
#changelist .actions .button {
|
||||
font-size: 13px;
|
||||
border: 1px solid #ccc;
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
background: #fff;
|
||||
background: var(--body-bg);
|
||||
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;
|
||||
color: var(--body-fg);
|
||||
}
|
||||
|
||||
#changelist .actions .button:focus, #changelist .actions .button:hover {
|
||||
border-color: #999;
|
||||
border-color: var(--body-quiet-color);
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
overflow: hidden;
|
||||
padding: 10px;
|
||||
font-size: 13px;
|
||||
border-bottom: 1px solid #eee;
|
||||
border-bottom: 1px solid var(--hairline-color);
|
||||
}
|
||||
|
||||
.form-row img, .form-row input {
|
||||
|
@ -22,21 +22,17 @@ form .form-row p {
|
|||
padding-left: 0;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* FORM LABELS */
|
||||
|
||||
label {
|
||||
font-weight: normal;
|
||||
color: #666;
|
||||
color: var(--body-quiet-color);
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.required label, label.required {
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
color: var(--body-fg);
|
||||
}
|
||||
|
||||
/* RADIO BUTTONS */
|
||||
|
@ -219,24 +215,24 @@ fieldset.collapsed h2, fieldset.collapsed {
|
|||
}
|
||||
|
||||
fieldset.collapsed {
|
||||
border: 1px solid #eee;
|
||||
border: 1px solid var(--hairline-color);
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
fieldset.collapsed h2 {
|
||||
background: #f8f8f8;
|
||||
color: #666;
|
||||
background: var(--darkened-bg);
|
||||
color: var(--body-quiet-color);
|
||||
}
|
||||
|
||||
fieldset .collapse-toggle {
|
||||
color: #fff;
|
||||
color: var(--header-link-color);
|
||||
}
|
||||
|
||||
fieldset.collapsed .collapse-toggle {
|
||||
background: transparent;
|
||||
display: inline;
|
||||
color: #447e9b;
|
||||
color: var(--link-fg);
|
||||
}
|
||||
|
||||
/* MONOSPACE TEXTAREAS */
|
||||
|
@ -250,8 +246,8 @@ fieldset.monospace textarea {
|
|||
.submit-row {
|
||||
padding: 12px 14px;
|
||||
margin: 0 0 20px;
|
||||
background: #f8f8f8;
|
||||
border: 1px solid #eee;
|
||||
background: var(--darkened-bg);
|
||||
border: 1px solid var(--hairline-color);
|
||||
border-radius: 4px;
|
||||
text-align: right;
|
||||
overflow: hidden;
|
||||
|
@ -283,35 +279,35 @@ body.popup .submit-row {
|
|||
|
||||
.submit-row a.deletelink {
|
||||
display: block;
|
||||
background: #ba2121;
|
||||
background: var(--delete-button-bg);
|
||||
border-radius: 4px;
|
||||
padding: 10px 15px;
|
||||
height: 15px;
|
||||
line-height: 15px;
|
||||
color: #fff;
|
||||
color: var(--button-fg);
|
||||
}
|
||||
|
||||
.submit-row a.closelink {
|
||||
display: inline-block;
|
||||
background: #bbbbbb;
|
||||
background: var(--close-button-bg);
|
||||
border-radius: 4px;
|
||||
padding: 10px 15px;
|
||||
height: 15px;
|
||||
line-height: 15px;
|
||||
margin: 0 0 0 5px;
|
||||
color: #fff;
|
||||
color: var(--button-fg);
|
||||
}
|
||||
|
||||
.submit-row a.deletelink:focus,
|
||||
.submit-row a.deletelink:hover,
|
||||
.submit-row a.deletelink:active {
|
||||
background: #a41515;
|
||||
background: var(--delete-button-hover-bg);
|
||||
}
|
||||
|
||||
.submit-row a.closelink:focus,
|
||||
.submit-row a.closelink:hover,
|
||||
.submit-row a.closelink:active {
|
||||
background: #aaaaaa;
|
||||
background: var(--close-button-hover-bg);
|
||||
}
|
||||
|
||||
/* CUSTOM FORM FIELDS */
|
||||
|
@ -390,12 +386,12 @@ body.popup .submit-row {
|
|||
|
||||
.inline-related h3 {
|
||||
margin: 0;
|
||||
color: #666;
|
||||
color: var(--body-quiet-color);
|
||||
padding: 5px;
|
||||
font-size: 13px;
|
||||
background: #f8f8f8;
|
||||
border-top: 1px solid #eee;
|
||||
border-bottom: 1px solid #eee;
|
||||
background: var(--darkened-bg);
|
||||
border-top: 1px solid var(--hairline-color);
|
||||
border-bottom: 1px solid var(--hairline-color);
|
||||
}
|
||||
|
||||
.inline-related h3 span.delete {
|
||||
|
@ -409,7 +405,7 @@ body.popup .submit-row {
|
|||
|
||||
.inline-related fieldset {
|
||||
margin: 0;
|
||||
background: #fff;
|
||||
background: var(--body-bg);
|
||||
border: none;
|
||||
width: 100%;
|
||||
}
|
||||
|
@ -421,7 +417,7 @@ body.popup .submit-row {
|
|||
text-align: left;
|
||||
font-weight: bold;
|
||||
background: #bcd;
|
||||
color: #fff;
|
||||
color: var(--body-bg);
|
||||
}
|
||||
|
||||
.inline-group .tabular fieldset.module {
|
||||
|
@ -460,7 +456,7 @@ body.popup .submit-row {
|
|||
overflow: hidden;
|
||||
font-size: 9px;
|
||||
font-weight: bold;
|
||||
color: #666;
|
||||
color: var(--body-quiet-color);
|
||||
_width: 700px;
|
||||
}
|
||||
|
||||
|
@ -477,15 +473,15 @@ body.popup .submit-row {
|
|||
|
||||
.inline-group div.add-row,
|
||||
.inline-group .tabular tr.add-row td {
|
||||
color: #666;
|
||||
background: #f8f8f8;
|
||||
color: var(--body-quiet-color);
|
||||
background: var(--darkened-bg);
|
||||
padding: 8px 10px;
|
||||
border-bottom: 1px solid #eee;
|
||||
border-bottom: 1px solid var(--hairline-color);
|
||||
}
|
||||
|
||||
.inline-group .tabular tr.add-row td {
|
||||
padding: 8px 10px;
|
||||
border-bottom: 1px solid #eee;
|
||||
border-bottom: 1px solid var(--hairline-color);
|
||||
}
|
||||
|
||||
.inline-group ul.tools a.add,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/* LOGIN FORM */
|
||||
|
||||
.login {
|
||||
background: #f8f8f8;
|
||||
background: var(--darkened-bg);
|
||||
height: auto;
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,7 @@
|
|||
}
|
||||
|
||||
.login #header h1 a {
|
||||
color: #fff;
|
||||
color: var(--header-link-color);
|
||||
}
|
||||
|
||||
.login #content {
|
||||
|
@ -24,8 +24,8 @@
|
|||
}
|
||||
|
||||
.login #container {
|
||||
background: #fff;
|
||||
border: 1px solid #eaeaea;
|
||||
background: var(--body-bg);
|
||||
border: 1px solid var(--hairline-color);
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
width: 28em;
|
||||
|
@ -34,44 +34,25 @@
|
|||
height: 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;
|
||||
display: block;
|
||||
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%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.login span.help {
|
||||
font-size: 10px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.login .submit-row {
|
||||
clear: both;
|
||||
padding: 1em 0 0 9.4em;
|
||||
padding: 1em 0 0 0;
|
||||
margin: 0;
|
||||
border: none;
|
||||
background: none;
|
||||
text-align: left;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.login .password-reset-link {
|
||||
|
|
|
@ -12,22 +12,23 @@
|
|||
justify-content: center;
|
||||
flex: 0 0 23px;
|
||||
width: 23px;
|
||||
border-right: 1px solid #eaeaea;
|
||||
background-color: #ffffff;
|
||||
border: 0;
|
||||
border-right: 1px solid var(--hairline-color);
|
||||
background-color: var(--body-bg);
|
||||
cursor: pointer;
|
||||
font-size: 20px;
|
||||
color: #447e9b;
|
||||
color: var(--link-fg);
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
[dir="rtl"] .toggle-nav-sidebar {
|
||||
border-left: 1px solid #eaeaea;
|
||||
border-left: 1px solid var(--hairline-color);
|
||||
border-right: 0;
|
||||
}
|
||||
|
||||
.toggle-nav-sidebar:hover,
|
||||
.toggle-nav-sidebar:focus {
|
||||
background-color: #f6f6f6;
|
||||
background-color: var(--darkened-bg);
|
||||
}
|
||||
|
||||
#nav-sidebar {
|
||||
|
@ -36,13 +37,13 @@
|
|||
left: -276px;
|
||||
margin-left: -276px;
|
||||
border-top: 1px solid transparent;
|
||||
border-right: 1px solid #eaeaea;
|
||||
background-color: #ffffff;
|
||||
border-right: 1px solid var(--hairline-color);
|
||||
background-color: var(--body-bg);
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
[dir="rtl"] #nav-sidebar {
|
||||
border-left: 1px solid #eaeaea;
|
||||
border-left: 1px solid var(--hairline-color);
|
||||
border-right: 0;
|
||||
left: 0;
|
||||
margin-left: 0;
|
||||
|
@ -91,12 +92,12 @@
|
|||
|
||||
#nav-sidebar .current-app .section:link,
|
||||
#nav-sidebar .current-app .section:visited {
|
||||
color: #ffc;
|
||||
color: var(--header-color);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#nav-sidebar .current-model {
|
||||
background: #ffc;
|
||||
background: var(--selected-row);
|
||||
}
|
||||
|
||||
.main > #nav-sidebar + .content {
|
||||
|
|
|
@ -140,7 +140,7 @@ input[type="submit"], button {
|
|||
}
|
||||
|
||||
#changelist .actions select {
|
||||
background: #fff;
|
||||
background: var(--body-bg);
|
||||
}
|
||||
|
||||
#changelist .actions .button {
|
||||
|
@ -157,7 +157,7 @@ input[type="submit"], button {
|
|||
}
|
||||
|
||||
#changelist-filter {
|
||||
width: 200px;
|
||||
flex-basis: 200px;
|
||||
}
|
||||
|
||||
.change-list .filtered .results,
|
||||
|
@ -166,7 +166,7 @@ input[type="submit"], button {
|
|||
.filtered .actions,
|
||||
|
||||
#changelist .paginator {
|
||||
border-top-color: #eee;
|
||||
border-top-color: var(--hairline-color); /* XXX Is this used at all? */
|
||||
}
|
||||
|
||||
#changelist .results + .paginator {
|
||||
|
@ -213,7 +213,7 @@ input[type="submit"], button {
|
|||
fieldset .fieldBox + .fieldBox {
|
||||
margin-top: 10px;
|
||||
padding-top: 10px;
|
||||
border-top: 1px solid #eee;
|
||||
border-top: 1px solid var(--hairline-color);
|
||||
}
|
||||
|
||||
textarea {
|
||||
|
@ -399,11 +399,11 @@ input[type="submit"], button {
|
|||
.datetime .timezonewarning {
|
||||
display: block;
|
||||
font-size: 11px;
|
||||
color: #999;
|
||||
color: var(--body-quiet-color);
|
||||
}
|
||||
|
||||
.datetimeshortcuts {
|
||||
color: #ccc;
|
||||
color: var(--border-color); /* XXX Redundant, .datetime span also sets #ccc */
|
||||
}
|
||||
|
||||
.form-row .datetime input.vDateField, .form-row .datetime input.vTimeField {
|
||||
|
@ -655,7 +655,7 @@ input[type="submit"], button {
|
|||
margin-bottom: -3px;
|
||||
}
|
||||
|
||||
form .aligned ul.radiolist li + li {
|
||||
form .aligned ul.radiolist:not(.inline) li + li {
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
|
@ -740,7 +740,7 @@ input[type="submit"], button {
|
|||
/* Inlines */
|
||||
|
||||
.inline-group[data-inline-type="stacked"] .inline-related {
|
||||
border: 2px solid #eee;
|
||||
border: 1px solid var(--hairline-color);
|
||||
border-radius: 4px;
|
||||
margin-top: 15px;
|
||||
overflow: auto;
|
||||
|
@ -750,18 +750,19 @@ input[type="submit"], button {
|
|||
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 {
|
||||
.inline-group[data-inline-type="stacked"] .inline-related .module .form-row {
|
||||
border-top: 1px solid var(--hairline-color);
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.inline-group[data-inline-type="stacked"] .inline-related .module .form-row:first-child {
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
.inline-group[data-inline-type="stacked"] .inline-related h3 {
|
||||
padding: 10px;
|
||||
border-top-width: 0;
|
||||
|
@ -791,7 +792,7 @@ input[type="submit"], button {
|
|||
|
||||
.inline-group[data-inline-type="stacked"] div.add-row {
|
||||
margin-top: 15px;
|
||||
border: 1px solid #eee;
|
||||
border: 1px solid var(--hairline-color);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
|
@ -885,9 +886,7 @@ input[type="submit"], button {
|
|||
}
|
||||
|
||||
.login .form-row label {
|
||||
display: block;
|
||||
margin: 0 0 5px;
|
||||
padding: 0;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
|
@ -895,7 +894,7 @@ input[type="submit"], button {
|
|||
padding: 15px 0 0;
|
||||
}
|
||||
|
||||
.login br, .login .submit-row label {
|
||||
.login br {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
@ -963,7 +962,7 @@ input[type="submit"], button {
|
|||
}
|
||||
|
||||
.timelist a {
|
||||
background: #fff;
|
||||
background: var(--body-bg);
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,25 +1,3 @@
|
|||
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 {
|
||||
|
@ -119,7 +97,7 @@ thead th.sorted .text {
|
|||
border-left: none;
|
||||
padding-left: 10px;
|
||||
margin-left: 0;
|
||||
border-right: 5px solid #eaeaea;
|
||||
border-right: 5px solid var(--hairline-color);
|
||||
padding-right: 10px;
|
||||
margin-right: -15px;
|
||||
}
|
||||
|
|
|
@ -22,26 +22,25 @@
|
|||
}
|
||||
|
||||
.selector-available h2, .selector-chosen h2 {
|
||||
border: 1px solid #ccc;
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px 4px 0 0;
|
||||
}
|
||||
|
||||
.selector-chosen h2 {
|
||||
background: #79aec8;
|
||||
color: #fff;
|
||||
background: var(--primary);
|
||||
color: var(--header-link-color);
|
||||
}
|
||||
|
||||
.selector .selector-available h2 {
|
||||
background: #f8f8f8;
|
||||
color: #666;
|
||||
background: var(--darkened-bg);
|
||||
color: var(--body-quiet-color);
|
||||
}
|
||||
|
||||
.selector .selector-filter {
|
||||
background: white;
|
||||
border: 1px solid #ccc;
|
||||
border: 1px solid var(--border-color);
|
||||
border-width: 0 1px;
|
||||
padding: 8px;
|
||||
color: #999;
|
||||
color: var(--body-quiet-color);
|
||||
font-size: 10px;
|
||||
margin: 0;
|
||||
text-align: left;
|
||||
|
@ -66,7 +65,7 @@
|
|||
.selector ul.selector-chooser {
|
||||
float: left;
|
||||
width: 22px;
|
||||
background-color: #eee;
|
||||
background-color: var(--selected-bg);
|
||||
border-radius: 10px;
|
||||
margin: 10em 5px 0 5px;
|
||||
padding: 0;
|
||||
|
@ -91,7 +90,7 @@
|
|||
text-indent: -3000px;
|
||||
overflow: hidden;
|
||||
cursor: default;
|
||||
opacity: 0.3;
|
||||
opacity: 0.55;
|
||||
}
|
||||
|
||||
.active.selector-add, .active.selector-remove {
|
||||
|
@ -126,14 +125,14 @@ a.selector-chooseall, a.selector-clearall {
|
|||
overflow: hidden;
|
||||
font-weight: bold;
|
||||
line-height: 16px;
|
||||
color: #666;
|
||||
color: var(--body-quiet-color);
|
||||
text-decoration: none;
|
||||
opacity: 0.3;
|
||||
opacity: 0.55;
|
||||
}
|
||||
|
||||
a.active.selector-chooseall:focus, a.active.selector-clearall:focus,
|
||||
a.active.selector-chooseall:hover, a.active.selector-clearall:hover {
|
||||
color: #447e9b;
|
||||
color: var(--link-fg);
|
||||
}
|
||||
|
||||
a.active.selector-chooseall, a.active.selector-clearall {
|
||||
|
@ -261,7 +260,7 @@ p.datetime {
|
|||
line-height: 20px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
color: #666;
|
||||
color: var(--body-quiet-color);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
|
@ -269,7 +268,7 @@ p.datetime {
|
|||
white-space: nowrap;
|
||||
font-weight: normal;
|
||||
font-size: 11px;
|
||||
color: #ccc;
|
||||
color: var(--body-quiet-color);
|
||||
}
|
||||
|
||||
.datetime input, .form-row .datetime input.vDateField, .form-row .datetime input.vTimeField {
|
||||
|
@ -313,7 +312,7 @@ table p.datetime {
|
|||
|
||||
.timezonewarning {
|
||||
font-size: 11px;
|
||||
color: #999;
|
||||
color: var(--body-quiet-color);
|
||||
}
|
||||
|
||||
/* URL */
|
||||
|
@ -322,7 +321,7 @@ p.url {
|
|||
line-height: 20px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
color: #666;
|
||||
color: var(--body-quiet-color);
|
||||
font-size: 11px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
@ -337,7 +336,7 @@ p.file-upload {
|
|||
line-height: 20px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
color: #666;
|
||||
color: var(--body-quiet-color);
|
||||
font-size: 11px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
@ -355,7 +354,7 @@ p.file-upload {
|
|||
}
|
||||
|
||||
span.clearable-file-input label {
|
||||
color: #333;
|
||||
color: var(--body-fg);
|
||||
font-size: 11px;
|
||||
display: inline;
|
||||
float: none;
|
||||
|
@ -368,8 +367,9 @@ span.clearable-file-input label {
|
|||
font-size: 12px;
|
||||
width: 19em;
|
||||
text-align: center;
|
||||
background: white;
|
||||
border: 1px solid #ddd;
|
||||
background: var(--body-bg);
|
||||
color: var(--body-fg);
|
||||
border: 1px solid var(--hairline-color);
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.15);
|
||||
overflow: hidden;
|
||||
|
@ -397,20 +397,20 @@ span.clearable-file-input label {
|
|||
margin: 0;
|
||||
text-align: center;
|
||||
border-top: none;
|
||||
background: #f5dd5d;
|
||||
font-weight: 700;
|
||||
font-size: 12px;
|
||||
color: #333;
|
||||
background: var(--accent);
|
||||
}
|
||||
|
||||
.calendar th {
|
||||
padding: 8px 5px;
|
||||
background: #f8f8f8;
|
||||
border-bottom: 1px solid #ddd;
|
||||
background: var(--darkened-bg);
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
font-weight: 400;
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
color: #666;
|
||||
color: var(--body-quiet-color);
|
||||
}
|
||||
|
||||
.calendar td {
|
||||
|
@ -418,17 +418,17 @@ span.clearable-file-input label {
|
|||
font-size: 12px;
|
||||
text-align: center;
|
||||
padding: 0;
|
||||
border-top: 1px solid #eee;
|
||||
border-top: 1px solid var(--hairline-color);
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.calendar td.selected a {
|
||||
background: #79aec8;
|
||||
color: #fff;
|
||||
background: var(--primary);
|
||||
color: var(--button-fg);
|
||||
}
|
||||
|
||||
.calendar td.nonday {
|
||||
background: #f8f8f8;
|
||||
background: var(--darkened-bg);
|
||||
}
|
||||
|
||||
.calendar td.today a {
|
||||
|
@ -440,17 +440,17 @@ span.clearable-file-input label {
|
|||
font-weight: 400;
|
||||
padding: 6px;
|
||||
text-decoration: none;
|
||||
color: #444;
|
||||
color: var(--body-quiet-color);
|
||||
}
|
||||
|
||||
.calendar td a:focus, .timelist a:focus,
|
||||
.calendar td a:hover, .timelist a:hover {
|
||||
background: #79aec8;
|
||||
background: var(--primary);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.calendar td a:active, .timelist a:active {
|
||||
background: #417690;
|
||||
background: var(--header-bg);
|
||||
color: white;
|
||||
}
|
||||
|
||||
|
@ -464,16 +464,16 @@ span.clearable-file-input label {
|
|||
|
||||
.calendarnav a:link, #calendarnav a:visited,
|
||||
#calendarnav a:focus, #calendarnav a:hover {
|
||||
color: #999;
|
||||
color: var(--body-quiet-color);
|
||||
}
|
||||
|
||||
.calendar-shortcuts {
|
||||
background: white;
|
||||
background: var(--body-bg);
|
||||
color: var(--body-quiet-color);
|
||||
font-size: 11px;
|
||||
line-height: 11px;
|
||||
border-top: 1px solid #eee;
|
||||
border-top: 1px solid var(--hairline-color);
|
||||
padding: 8px 0;
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
.calendarbox .calendarnav-previous, .calendarbox .calendarnav-next {
|
||||
|
@ -511,8 +511,8 @@ span.clearable-file-input label {
|
|||
padding: 4px 0;
|
||||
font-size: 12px;
|
||||
background: #eee;
|
||||
border-top: 1px solid #ddd;
|
||||
color: #333;
|
||||
border-top: 1px solid var(--border-color);
|
||||
color: var(--body-fg);
|
||||
}
|
||||
|
||||
.calendar-cancel:focus, .calendar-cancel:hover {
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
redisplay: function(id) {
|
||||
// Repopulate HTML select box from cache
|
||||
const box = document.getElementById(id);
|
||||
const scroll_value_from_top = box.scrollTop;
|
||||
box.innerHTML = '';
|
||||
for (const node of SelectBox.cache[id]) {
|
||||
if (node.displayed) {
|
||||
|
@ -22,6 +23,7 @@
|
|||
box.appendChild(new_option);
|
||||
}
|
||||
}
|
||||
box.scrollTop = scroll_value_from_top;
|
||||
},
|
||||
filter: function(id, text) {
|
||||
// Redisplay the HTML select box, displaying only the choices containing ALL
|
||||
|
@ -31,7 +33,7 @@
|
|||
node.displayed = 1;
|
||||
const node_text = node.text.toLowerCase();
|
||||
for (const token of tokens) {
|
||||
if (node_text.indexOf(token) === -1) {
|
||||
if (!node_text.includes(token)) {
|
||||
node.displayed = 0;
|
||||
break; // Once the first token isn't found we're done
|
||||
}
|
||||
|
|
|
@ -1,154 +1,201 @@
|
|||
/*global gettext, interpolate, ngettext*/
|
||||
'use strict';
|
||||
{
|
||||
const $ = django.jQuery;
|
||||
let lastChecked;
|
||||
function show(selector) {
|
||||
document.querySelectorAll(selector).forEach(function(el) {
|
||||
el.classList.remove('hidden');
|
||||
});
|
||||
}
|
||||
|
||||
$.fn.actions = function(opts) {
|
||||
const options = $.extend({}, $.fn.actions.defaults, opts);
|
||||
const actionCheckboxes = $(this);
|
||||
let list_editable_changed = false;
|
||||
const 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() {
|
||||
const sel = $(actionCheckboxes).filter(":checked").length;
|
||||
// data-actions-icnt is defined in the generated HTML
|
||||
// and contains the total amount of objects in the queryset
|
||||
const 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() {
|
||||
let value;
|
||||
if (sel === actionCheckboxes.length) {
|
||||
value = true;
|
||||
showQuestion();
|
||||
} else {
|
||||
value = false;
|
||||
clearAcross();
|
||||
}
|
||||
return value;
|
||||
});
|
||||
};
|
||||
// Show counter by default
|
||||
$(options.counterContainer).show();
|
||||
// Check state of checkboxes and reinit state if needed
|
||||
$(this).filter(":checked").each(function(i) {
|
||||
$(this).parent().parent().toggleClass(options.selectedClass);
|
||||
updateCounter();
|
||||
if ($(options.acrossInput).val() === 1) {
|
||||
showClear();
|
||||
}
|
||||
function hide(selector) {
|
||||
document.querySelectorAll(selector).forEach(function(el) {
|
||||
el.classList.add('hidden');
|
||||
});
|
||||
$(options.allToggle).show().on('click', function() {
|
||||
checker($(this).prop("checked"));
|
||||
updateCounter();
|
||||
}
|
||||
|
||||
function showQuestion(options) {
|
||||
hide(options.acrossClears);
|
||||
show(options.acrossQuestions);
|
||||
hide(options.allContainer);
|
||||
}
|
||||
|
||||
function showClear(options) {
|
||||
show(options.acrossClears);
|
||||
hide(options.acrossQuestions);
|
||||
document.querySelector(options.actionContainer).classList.remove(options.selectedClass);
|
||||
show(options.allContainer);
|
||||
hide(options.counterContainer);
|
||||
}
|
||||
|
||||
function reset(options) {
|
||||
hide(options.acrossClears);
|
||||
hide(options.acrossQuestions);
|
||||
hide(options.allContainer);
|
||||
show(options.counterContainer);
|
||||
}
|
||||
|
||||
function clearAcross(options) {
|
||||
reset(options);
|
||||
const acrossInputs = document.querySelectorAll(options.acrossInput);
|
||||
acrossInputs.forEach(function(acrossInput) {
|
||||
acrossInput.value = 0;
|
||||
});
|
||||
$("a", options.acrossQuestions).on('click', function(event) {
|
||||
event.preventDefault();
|
||||
$(options.acrossInput).val(1);
|
||||
showClear();
|
||||
document.querySelector(options.actionContainer).classList.remove(options.selectedClass);
|
||||
}
|
||||
|
||||
function checker(actionCheckboxes, options, checked) {
|
||||
if (checked) {
|
||||
showQuestion(options);
|
||||
} else {
|
||||
reset(options);
|
||||
}
|
||||
actionCheckboxes.forEach(function(el) {
|
||||
el.checked = checked;
|
||||
el.closest('tr').classList.toggle(options.selectedClass, checked);
|
||||
});
|
||||
$("a", options.acrossClears).on('click', function(event) {
|
||||
event.preventDefault();
|
||||
$(options.allToggle).prop("checked", false);
|
||||
clearAcross();
|
||||
checker(0);
|
||||
updateCounter();
|
||||
});
|
||||
lastChecked = null;
|
||||
$(actionCheckboxes).on('click', function(event) {
|
||||
if (!event) { event = window.event; }
|
||||
const target = event.target ? event.target : event.srcElement;
|
||||
if (lastChecked && $.data(lastChecked) !== $.data(target) && event.shiftKey === true) {
|
||||
let inrange = false;
|
||||
$(lastChecked).prop("checked", target.checked)
|
||||
.parent().parent().toggleClass(options.selectedClass, target.checked);
|
||||
$(actionCheckboxes).each(function() {
|
||||
if ($.data(this) === $.data(lastChecked) || $.data(this) === $.data(target)) {
|
||||
inrange = (inrange) ? false : true;
|
||||
}
|
||||
if (inrange) {
|
||||
$(this).prop("checked", target.checked)
|
||||
.parent().parent().toggleClass(options.selectedClass, target.checked);
|
||||
}
|
||||
});
|
||||
}
|
||||
$(target).parent().parent().toggleClass(options.selectedClass, target.checked);
|
||||
lastChecked = target;
|
||||
updateCounter();
|
||||
});
|
||||
$('form#changelist-form table#result_list tr').on('change', 'td:gt(0) :input', function() {
|
||||
list_editable_changed = true;
|
||||
});
|
||||
$('form#changelist-form button[name="index"]').on('click', function(event) {
|
||||
if (list_editable_changed) {
|
||||
return confirm(gettext("You have unsaved changes on individual editable fields. If you run an action, your unsaved changes will be lost."));
|
||||
}
|
||||
});
|
||||
$('form#changelist-form input[name="_save"]').on('click', function(event) {
|
||||
let action_changed = false;
|
||||
$('select option:selected', options.actionContainer).each(function() {
|
||||
if ($(this).val()) {
|
||||
action_changed = true;
|
||||
}
|
||||
});
|
||||
if (action_changed) {
|
||||
if (list_editable_changed) {
|
||||
return confirm(gettext("You have selected an action, but you haven’t saved your changes to individual fields yet. Please click OK to save. You’ll need to re-run the action."));
|
||||
} else {
|
||||
return confirm(gettext("You have selected an action, and you haven’t made any changes on individual fields. You’re probably looking for the Go button rather than the Save button."));
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
/* Setup plugin defaults */
|
||||
$.fn.actions.defaults = {
|
||||
}
|
||||
|
||||
function updateCounter(actionCheckboxes, options) {
|
||||
const sel = Array.from(actionCheckboxes).filter(function(el) {
|
||||
return el.checked;
|
||||
}).length;
|
||||
const counter = document.querySelector(options.counterContainer);
|
||||
// data-actions-icnt is defined in the generated HTML
|
||||
// and contains the total amount of objects in the queryset
|
||||
const actions_icnt = Number(counter.dataset.actionsIcnt);
|
||||
counter.textContent = interpolate(
|
||||
ngettext('%(sel)s of %(cnt)s selected', '%(sel)s of %(cnt)s selected', sel), {
|
||||
sel: sel,
|
||||
cnt: actions_icnt
|
||||
}, true);
|
||||
const allToggle = document.getElementById(options.allToggleId);
|
||||
allToggle.checked = sel === actionCheckboxes.length;
|
||||
if (allToggle.checked) {
|
||||
showQuestion(options);
|
||||
} else {
|
||||
clearAcross(options);
|
||||
}
|
||||
}
|
||||
|
||||
const defaults = {
|
||||
actionContainer: "div.actions",
|
||||
counterContainer: "span.action-counter",
|
||||
allContainer: "div.actions span.all",
|
||||
acrossInput: "div.actions input.select-across",
|
||||
acrossQuestions: "div.actions span.question",
|
||||
acrossClears: "div.actions span.clear",
|
||||
allToggle: "#action-toggle",
|
||||
allToggleId: "action-toggle",
|
||||
selectedClass: "selected"
|
||||
};
|
||||
$(document).ready(function() {
|
||||
const $actionsEls = $('tr input.action-select');
|
||||
if ($actionsEls.length > 0) {
|
||||
$actionsEls.actions();
|
||||
|
||||
window.Actions = function(actionCheckboxes, options) {
|
||||
options = Object.assign({}, defaults, options);
|
||||
let list_editable_changed = false;
|
||||
let lastChecked = null;
|
||||
let shiftPressed = false;
|
||||
|
||||
document.addEventListener('keydown', (event) => {
|
||||
shiftPressed = event.shiftKey;
|
||||
});
|
||||
|
||||
document.addEventListener('keyup', (event) => {
|
||||
shiftPressed = event.shiftKey;
|
||||
});
|
||||
|
||||
document.getElementById(options.allToggleId).addEventListener('click', function(event) {
|
||||
checker(actionCheckboxes, options, this.checked);
|
||||
updateCounter(actionCheckboxes, options);
|
||||
});
|
||||
|
||||
document.querySelectorAll(options.acrossQuestions + " a").forEach(function(el) {
|
||||
el.addEventListener('click', function(event) {
|
||||
event.preventDefault();
|
||||
const acrossInputs = document.querySelectorAll(options.acrossInput);
|
||||
acrossInputs.forEach(function(acrossInput) {
|
||||
acrossInput.value = 1;
|
||||
});
|
||||
showClear(options);
|
||||
});
|
||||
});
|
||||
|
||||
document.querySelectorAll(options.acrossClears + " a").forEach(function(el) {
|
||||
el.addEventListener('click', function(event) {
|
||||
event.preventDefault();
|
||||
document.getElementById(options.allToggleId).checked = false;
|
||||
clearAcross(options);
|
||||
checker(actionCheckboxes, options, false);
|
||||
updateCounter(actionCheckboxes, options);
|
||||
});
|
||||
});
|
||||
|
||||
function affectedCheckboxes(target, withModifier) {
|
||||
const multiSelect = (lastChecked && withModifier && lastChecked !== target);
|
||||
if (!multiSelect) {
|
||||
return [target];
|
||||
}
|
||||
const checkboxes = Array.from(actionCheckboxes);
|
||||
const targetIndex = checkboxes.findIndex(el => el === target);
|
||||
const lastCheckedIndex = checkboxes.findIndex(el => el === lastChecked);
|
||||
const startIndex = Math.min(targetIndex, lastCheckedIndex);
|
||||
const endIndex = Math.max(targetIndex, lastCheckedIndex);
|
||||
const filtered = checkboxes.filter((el, index) => (startIndex <= index) && (index <= endIndex));
|
||||
return filtered;
|
||||
};
|
||||
|
||||
Array.from(document.getElementById('result_list').tBodies).forEach(function(el) {
|
||||
el.addEventListener('change', function(event) {
|
||||
const target = event.target;
|
||||
if (target.classList.contains('action-select')) {
|
||||
const checkboxes = affectedCheckboxes(target, shiftPressed);
|
||||
checker(checkboxes, options, target.checked);
|
||||
updateCounter(actionCheckboxes, options);
|
||||
lastChecked = target;
|
||||
} else {
|
||||
list_editable_changed = true;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
document.querySelector('#changelist-form button[name=index]').addEventListener('click', function() {
|
||||
if (list_editable_changed) {
|
||||
const confirmed = confirm(gettext("You have unsaved changes on individual editable fields. If you run an action, your unsaved changes will be lost."));
|
||||
if (!confirmed) {
|
||||
event.preventDefault();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const el = document.querySelector('#changelist-form input[name=_save]');
|
||||
// The button does not exist if no fields are editable.
|
||||
if (el) {
|
||||
el.addEventListener('click', function(event) {
|
||||
if (document.querySelector('[name=action]').value) {
|
||||
const text = list_editable_changed
|
||||
? gettext("You have selected an action, but you haven’t saved your changes to individual fields yet. Please click OK to save. You’ll need to re-run the action.")
|
||||
: gettext("You have selected an action, and you haven’t made any changes on individual fields. You’re probably looking for the Go button rather than the Save button.");
|
||||
if (!confirm(text)) {
|
||||
event.preventDefault();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Call function fn when the DOM is loaded and ready. If it is already
|
||||
// loaded, call the function now.
|
||||
// http://youmightnotneedjquery.com/#ready
|
||||
function ready(fn) {
|
||||
if (document.readyState !== 'loading') {
|
||||
fn();
|
||||
} else {
|
||||
document.addEventListener('DOMContentLoaded', fn);
|
||||
}
|
||||
}
|
||||
|
||||
ready(function() {
|
||||
const actionsEls = document.querySelectorAll('tr input.action-select');
|
||||
if (actionsEls.length > 0) {
|
||||
Actions(actionsEls);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -28,8 +28,7 @@
|
|||
timezoneWarningClass: 'timezonewarning', // class of the warning for timezone mismatch
|
||||
timezoneOffset: 0,
|
||||
init: function() {
|
||||
const body = document.getElementsByTagName('body')[0];
|
||||
const serverOffset = body.dataset.adminUtcOffset;
|
||||
const serverOffset = document.body.dataset.adminUtcOffset;
|
||||
if (serverOffset) {
|
||||
const localOffset = new Date().getTimezoneOffset() * -60;
|
||||
DateTimeShortcuts.timezoneOffset = localOffset - serverOffset;
|
||||
|
@ -48,8 +47,7 @@
|
|||
},
|
||||
// Return the current time while accounting for the server timezone.
|
||||
now: function() {
|
||||
const body = document.getElementsByTagName('body')[0];
|
||||
const serverOffset = body.dataset.adminUtcOffset;
|
||||
const serverOffset = document.body.dataset.adminUtcOffset;
|
||||
if (serverOffset) {
|
||||
const localNow = new Date();
|
||||
const localOffset = localNow.getTimezoneOffset() * -60;
|
||||
|
|
|
@ -7,13 +7,9 @@
|
|||
|
||||
function showAdminPopup(triggeringLink, name_regexp, add_popup) {
|
||||
const name = triggeringLink.id.replace(name_regexp, '');
|
||||
let href = triggeringLink.href;
|
||||
const href = new URL(triggeringLink.href);
|
||||
if (add_popup) {
|
||||
if (href.indexOf('?') === -1) {
|
||||
href += '?_popup=1';
|
||||
} else {
|
||||
href += '&_popup=1';
|
||||
}
|
||||
href.searchParams.set('_popup', 1);
|
||||
}
|
||||
const win = window.open(href, name, 'height=500,width=800,resizable=yes,scrollbars=yes');
|
||||
win.focus();
|
||||
|
|
|
@ -7,7 +7,10 @@
|
|||
data: function(params) {
|
||||
return {
|
||||
term: params.term,
|
||||
page: params.page
|
||||
page: params.page,
|
||||
app_label: $element.data('app-label'),
|
||||
model_name: $element.data('model-name'),
|
||||
field_name: $element.data('field-name')
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,20 @@ depends on core.js for utility functions like removeChildren or quickElement
|
|||
gettext('November'),
|
||||
gettext('December')
|
||||
],
|
||||
monthsOfYearAbbrev: [
|
||||
pgettext('abbrev. month January', 'Jan'),
|
||||
pgettext('abbrev. month February', 'Feb'),
|
||||
pgettext('abbrev. month March', 'Mar'),
|
||||
pgettext('abbrev. month April', 'Apr'),
|
||||
pgettext('abbrev. month May', 'May'),
|
||||
pgettext('abbrev. month June', 'Jun'),
|
||||
pgettext('abbrev. month July', 'Jul'),
|
||||
pgettext('abbrev. month August', 'Aug'),
|
||||
pgettext('abbrev. month September', 'Sep'),
|
||||
pgettext('abbrev. month October', 'Oct'),
|
||||
pgettext('abbrev. month November', 'Nov'),
|
||||
pgettext('abbrev. month December', 'Dec')
|
||||
],
|
||||
daysOfWeek: [
|
||||
pgettext('one letter Sunday', 'S'),
|
||||
pgettext('one letter Monday', 'M'),
|
||||
|
|
|
@ -14,10 +14,11 @@
|
|||
ready(function() {
|
||||
function handleClick(event) {
|
||||
event.preventDefault();
|
||||
if (window.location.search.indexOf('&_popup=1') === -1) {
|
||||
window.history.back(); // Go back if not a popup.
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
if (params.has('_popup')) {
|
||||
window.close(); // Close the popup.
|
||||
} else {
|
||||
window.close(); // Otherwise, close the popup.
|
||||
window.history.back(); // Otherwise, go back.
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -85,6 +85,12 @@ function findPosY(obj) {
|
|||
return (this.getSeconds() < 10) ? '0' + this.getSeconds() : this.getSeconds();
|
||||
};
|
||||
|
||||
Date.prototype.getAbbrevMonthName = function() {
|
||||
return typeof window.CalendarNamespace === "undefined"
|
||||
? this.getTwoDigitMonth()
|
||||
: window.CalendarNamespace.monthsOfYearAbbrev[this.getMonth()];
|
||||
};
|
||||
|
||||
Date.prototype.getFullMonthName = function() {
|
||||
return typeof window.CalendarNamespace === "undefined"
|
||||
? this.getTwoDigitMonth()
|
||||
|
@ -93,6 +99,7 @@ function findPosY(obj) {
|
|||
|
||||
Date.prototype.strftime = function(format) {
|
||||
const fields = {
|
||||
b: this.getAbbrevMonthName(),
|
||||
B: this.getFullMonthName(),
|
||||
c: this.toString(),
|
||||
d: this.getTwoDigitDate(),
|
||||
|
|
|
@ -134,8 +134,7 @@
|
|||
for (const lookup of ALL_DOWNCODE_MAPS) {
|
||||
Object.assign(Downcoder.map, lookup);
|
||||
}
|
||||
Downcoder.chars = Object.keys(Downcoder.map);
|
||||
Downcoder.regex = new RegExp(Downcoder.chars.join('|'), 'g');
|
||||
Downcoder.regex = new RegExp(Object.keys(Downcoder.map).join('|'), 'g');
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -149,23 +148,9 @@
|
|||
|
||||
function URLify(s, num_chars, allowUnicode) {
|
||||
// changes, e.g., "Petty theft" to "petty-theft"
|
||||
// remove all these words from the string before urlifying
|
||||
if (!allowUnicode) {
|
||||
s = downcode(s);
|
||||
}
|
||||
const hasUnicodeChars = /[^\u0000-\u007f]/.test(s);
|
||||
// Remove English words only if the string contains ASCII (English)
|
||||
// characters.
|
||||
if (!hasUnicodeChars) {
|
||||
const removeList = [
|
||||
"a", "an", "as", "at", "before", "but", "by", "for", "from",
|
||||
"is", "in", "into", "like", "of", "off", "on", "onto", "per",
|
||||
"since", "than", "the", "this", "that", "to", "up", "via",
|
||||
"with"
|
||||
];
|
||||
const r = new RegExp('\\b(' + removeList.join('|') + ')\\b', 'gi');
|
||||
s = s.replace(r, '');
|
||||
}
|
||||
s = s.toLowerCase(); // convert to lowercase
|
||||
// if downcode doesn't hit, the char will be stripped here
|
||||
if (allowUnicode) {
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
|
||||
|
||||
.custom-article {
|
||||
border-style: solid;
|
||||
border-color: gray;
|
||||
border-width: 1px;
|
||||
padding: 5px;
|
||||
border-left: 3px solid SteelBlue;
|
||||
}
|
Before Width: | Height: | Size: 754 B |
|
@ -1 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M19.07,4.93C17.22,3 14.66,1.96 12,2C9.34,1.96 6.79,3 4.94,4.93C3,6.78 1.96,9.34 2,12C1.96,14.66 3,17.21 4.93,19.06C6.78,21 9.34,22.04 12,22C14.66,22.04 17.21,21 19.06,19.07C21,17.22 22.04,14.66 22,12C22.04,9.34 21,6.78 19.07,4.93M17,12V18H13.5V13H10.5V18H7V12H5L12,5L19.5,12H17Z" /></svg>
|
Before Width: | Height: | Size: 573 B |
|
@ -1,48 +0,0 @@
|
|||
|
||||
|
||||
$(function() {
|
||||
$('.downmovie').click(function(event) {
|
||||
var id = $(this).attr('id')
|
||||
var href = $(this).attr('href')
|
||||
var hc = window.location.href
|
||||
var url = '../addcount/' + id
|
||||
|
||||
if(hc.search('director')){
|
||||
url = '../../addcount/' + id
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
$.ajax({
|
||||
url: url,
|
||||
success : function(data){
|
||||
$('#down' + id).text(data);
|
||||
}
|
||||
})
|
||||
|
||||
var msg = 'Gracias al "honorable" poder legislativo de mi país, ya no es posible compartir nada, contactanos en nuestro canal de Telegram si tienes dudas: @mauflix'
|
||||
alert(msg);
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
function director_change(){
|
||||
var directors = document.getElementById('directors')
|
||||
if(directors.selectedIndex){
|
||||
name = directors.options[directors.selectedIndex].value
|
||||
window.location.href = '/movies/director/' + name
|
||||
}else{
|
||||
window.location.href = '/movies'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function search_movie(){
|
||||
var search = document.getElementById('search')
|
||||
window.location.href = '/movies/' + search.value
|
||||
}
|
||||
|
||||
|
||||
function search_key_press(ev){
|
||||
alert(ev.keyCode)
|
||||
}
|
Before Width: | Height: | Size: 434 KiB |
|
@ -1,6 +0,0 @@
|
|||
/*!
|
||||
Autosize 3.0.15
|
||||
license: MIT
|
||||
http://www.jacklmoore.com/autosize
|
||||
*/
|
||||
!function(e,t){if("function"==typeof define&&define.amd)define(["exports","module"],t);else if("undefined"!=typeof exports&&"undefined"!=typeof module)t(exports,module);else{var n={exports:{}};t(n.exports,n),e.autosize=n.exports}}(this,function(e,t){"use strict";function n(e){function t(){var t=window.getComputedStyle(e,null);p=t.overflowY,"vertical"===t.resize?e.style.resize="none":"both"===t.resize&&(e.style.resize="horizontal"),c="content-box"===t.boxSizing?-(parseFloat(t.paddingTop)+parseFloat(t.paddingBottom)):parseFloat(t.borderTopWidth)+parseFloat(t.borderBottomWidth),isNaN(c)&&(c=0),i()}function n(t){var n=e.style.width;e.style.width="0px",e.offsetWidth,e.style.width=n,p=t,f&&(e.style.overflowY=t),o()}function o(){var t=window.pageYOffset,n=document.body.scrollTop,o=e.style.height;e.style.height="auto";var i=e.scrollHeight+c;return 0===e.scrollHeight?void(e.style.height=o):(e.style.height=i+"px",v=e.clientWidth,document.documentElement.scrollTop=t,void(document.body.scrollTop=n))}function i(){var t=e.style.height;o();var i=window.getComputedStyle(e,null);if(i.height!==e.style.height?"visible"!==p&&n("visible"):"hidden"!==p&&n("hidden"),t!==e.style.height){var r=d("autosize:resized");e.dispatchEvent(r)}}var s=void 0===arguments[1]?{}:arguments[1],a=s.setOverflowX,l=void 0===a?!0:a,u=s.setOverflowY,f=void 0===u?!0:u;if(e&&e.nodeName&&"TEXTAREA"===e.nodeName&&!r.has(e)){var c=null,p=null,v=e.clientWidth,h=function(){e.clientWidth!==v&&i()},y=function(t){window.removeEventListener("resize",h,!1),e.removeEventListener("input",i,!1),e.removeEventListener("keyup",i,!1),e.removeEventListener("autosize:destroy",y,!1),e.removeEventListener("autosize:update",i,!1),r["delete"](e),Object.keys(t).forEach(function(n){e.style[n]=t[n]})}.bind(e,{height:e.style.height,resize:e.style.resize,overflowY:e.style.overflowY,overflowX:e.style.overflowX,wordWrap:e.style.wordWrap});e.addEventListener("autosize:destroy",y,!1),"onpropertychange"in e&&"oninput"in e&&e.addEventListener("keyup",i,!1),window.addEventListener("resize",h,!1),e.addEventListener("input",i,!1),e.addEventListener("autosize:update",i,!1),r.add(e),l&&(e.style.overflowX="hidden",e.style.wordWrap="break-word"),t()}}function o(e){if(e&&e.nodeName&&"TEXTAREA"===e.nodeName){var t=d("autosize:destroy");e.dispatchEvent(t)}}function i(e){if(e&&e.nodeName&&"TEXTAREA"===e.nodeName){var t=d("autosize:update");e.dispatchEvent(t)}}var r="function"==typeof Set?new Set:function(){var e=[];return{has:function(t){return Boolean(e.indexOf(t)>-1)},add:function(t){e.push(t)},"delete":function(t){e.splice(e.indexOf(t),1)}}}(),d=function(e){return new Event(e)};try{new Event("test")}catch(s){d=function(e){var t=document.createEvent("Event");return t.initEvent(e,!0,!1),t}}var a=null;"undefined"==typeof window||"function"!=typeof window.getComputedStyle?(a=function(e){return e},a.destroy=function(e){return e},a.update=function(e){return e}):(a=function(e,t){return e&&Array.prototype.forEach.call(e.length?e:[e],function(e){return n(e,t)}),e},a.destroy=function(e){return e&&Array.prototype.forEach.call(e.length?e:[e],o),e},a.update=function(e){return e&&Array.prototype.forEach.call(e.length?e:[e],i),e}),t.exports=a});
|
|
@ -1,250 +0,0 @@
|
|||
Suit = {};
|
||||
window.Suit = Suit;
|
||||
|
||||
(function ($) {
|
||||
if (!$)
|
||||
return;
|
||||
|
||||
Suit.$ = $;
|
||||
|
||||
// Register callbacks to perform after inline has been added
|
||||
Suit.after_inline = function () {
|
||||
var functions = {};
|
||||
var register = function (fn_name, fn_callback) {
|
||||
functions[fn_name] = fn_callback;
|
||||
};
|
||||
|
||||
var run = function (inline_prefix, row) {
|
||||
for (var fn_name in functions) {
|
||||
functions[fn_name](inline_prefix, row);
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
register: register,
|
||||
run: run
|
||||
};
|
||||
}();
|
||||
|
||||
Suit.ListActionsToggle = function () {
|
||||
var $topActions;
|
||||
|
||||
var init = function () {
|
||||
$(document).ready(function () {
|
||||
$topActions = $('.results').parent().find('.actions').eq(0);
|
||||
if (!$topActions.length)
|
||||
return;
|
||||
|
||||
$("tr input.action-select, #action-toggle").on('click', checkIfSelected);
|
||||
});
|
||||
};
|
||||
|
||||
var checkIfSelected = function () {
|
||||
if ($('tr.selected').length) {
|
||||
$topActions.slideDown('fast');
|
||||
} else {
|
||||
$topActions.slideUp('fast');
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
init: init
|
||||
}
|
||||
|
||||
}();
|
||||
|
||||
|
||||
Suit.FixedBar = function () {
|
||||
var didScroll = false, $fixedItem, $fixedItemParent, $win, $body,
|
||||
itemOffset,
|
||||
extraOffset = 0,
|
||||
fixed = false;
|
||||
|
||||
function init(selector) {
|
||||
$fixedItem = $(selector || '.submit-row');
|
||||
if (!$fixedItem.length)
|
||||
return;
|
||||
|
||||
$fixedItemParent = $fixedItem.parents('form');
|
||||
itemOffset = $fixedItem.offset();
|
||||
$win = $(window);
|
||||
window.onscroll = onScroll;
|
||||
window.onresize = onScroll;
|
||||
onScroll();
|
||||
|
||||
setInterval(function () {
|
||||
if (didScroll) {
|
||||
didScroll = false;
|
||||
}
|
||||
}, 200);
|
||||
}
|
||||
|
||||
function onScroll() {
|
||||
didScroll = true;
|
||||
|
||||
var itemHeight = $fixedItem.height(),
|
||||
scrollTop = $win.scrollTop();
|
||||
|
||||
if (scrollTop + $win.height() - itemHeight - extraOffset < itemOffset.top) {
|
||||
if (!fixed) {
|
||||
$fixedItem.addClass('fixed');
|
||||
$fixedItemParent.addClass('fixed').css('padding-bottom', itemHeight + 'px');
|
||||
fixed = true;
|
||||
}
|
||||
} else {
|
||||
if (fixed) {
|
||||
$fixedItem.removeClass('fixed');
|
||||
$fixedItemParent.removeClass('fixed').css('padding-bottom', '');
|
||||
fixed = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
init: init
|
||||
};
|
||||
}();
|
||||
|
||||
/**
|
||||
* Avoids double-submit issues in the change_form.
|
||||
*/
|
||||
$.fn.suitFormDebounce = function () {
|
||||
var $form = $(this),
|
||||
$saveButtons = $form.find('.submit-row button, .submit-row input[type=button], .submit-row input[type=submit]'),
|
||||
submitting = false;
|
||||
|
||||
$form.submit(function () {
|
||||
if (submitting) {
|
||||
return false;
|
||||
}
|
||||
|
||||
submitting = true;
|
||||
$saveButtons.addClass('disabled');
|
||||
|
||||
setTimeout(function () {
|
||||
$saveButtons.removeClass('disabled');
|
||||
submitting = false;
|
||||
}, 5000);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Content tabs
|
||||
*/
|
||||
$.fn.suitFormTabs = function () {
|
||||
|
||||
var $tabs = $(this);
|
||||
var tabPrefix = $tabs.data('tab-prefix');
|
||||
if (!tabPrefix)
|
||||
return;
|
||||
|
||||
var $tabLinks = $tabs.find('a');
|
||||
|
||||
function tabContents($link) {
|
||||
return $('.' + tabPrefix + '-' + $link.attr('href').replace('#', ''));
|
||||
}
|
||||
|
||||
function activateTabs() {
|
||||
// Init tab by error, by url hash or init first tab
|
||||
if (window.location.hash) {
|
||||
var foundError;
|
||||
$tabLinks.each(function () {
|
||||
var $link = $(this);
|
||||
if (tabContents($link).find('.error, .errorlist').length != 0) {
|
||||
$link.addClass('has-error');
|
||||
$link.trigger('click');
|
||||
foundError = true;
|
||||
}
|
||||
});
|
||||
!foundError && $($tabs).find('a[href=\\' + window.location.hash + ']').click();
|
||||
} else {
|
||||
$tabLinks.first().trigger('click');
|
||||
}
|
||||
}
|
||||
|
||||
$tabLinks.click(function () {
|
||||
var $link = $(this),
|
||||
showEvent = $.Event('shown.suit.tab', {
|
||||
relatedTarget: $link,
|
||||
tab: $link.attr('href').replace('#', '')
|
||||
});
|
||||
$link.parent().parent().find('.active').removeClass('active');
|
||||
$link.addClass('active');
|
||||
$('.' + tabPrefix).removeClass('show').addClass('hidden-xs-up');
|
||||
tabContents($link).removeClass('hidden-xs-up').addClass('show');
|
||||
$link.trigger(showEvent);
|
||||
});
|
||||
|
||||
activateTabs();
|
||||
};
|
||||
|
||||
/* Characters count for CharacterCountTextarea */
|
||||
$.fn.suitCharactersCount = function () {
|
||||
var $elements = $(this);
|
||||
|
||||
if (!$elements.length)
|
||||
return;
|
||||
|
||||
$elements.each(function () {
|
||||
var $el = $(this),
|
||||
$countEl = $('<div class="suit-char-count"></div>');
|
||||
$el.after($countEl);
|
||||
$el.on('keyup', function (e) {
|
||||
updateCount($(e.currentTarget));
|
||||
});
|
||||
updateCount($el);
|
||||
});
|
||||
|
||||
function updateCount($el) {
|
||||
var maxCount = $el.data('suit-maxcount'),
|
||||
twitterCount = $el.data('suit-twitter-count'),
|
||||
value = $el.val(),
|
||||
len = twitterCount ? getTweetLength(value) : value.length,
|
||||
count = maxCount ? maxCount - len : len;
|
||||
if (count < 0)
|
||||
count = '<span class="text-danger">' + count + '</span>';
|
||||
|
||||
$el.next().first().html(count);
|
||||
}
|
||||
|
||||
function getTweetLength(input) {
|
||||
var tmp = "";
|
||||
for (var i = 0; i < 23; i++) {
|
||||
tmp += "o"
|
||||
}
|
||||
return input.replace(/(http:\/\/[\S]*)/g, tmp).length;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Search filters - submit only changed fields
|
||||
*/
|
||||
$.fn.suitSearchFilters = function () {
|
||||
$(this).change(function () {
|
||||
var $field = $(this);
|
||||
var $option = $field.find('option:selected');
|
||||
var select_name = $option.data('name');
|
||||
if (select_name) {
|
||||
$field.attr('name', select_name);
|
||||
} else {
|
||||
$field.removeAttr('name');
|
||||
}
|
||||
// Handle additional values for date filters
|
||||
var additional = $option.data('additional');
|
||||
console.log($field, additional)
|
||||
if (additional) {
|
||||
var hiddenId = $field.data('name') + '_add';
|
||||
var $hidden = $('#' + hiddenId);
|
||||
if (!$hidden.length) {
|
||||
$hidden = $('<input/>').attr('type', 'hidden').attr('id', hiddenId);
|
||||
$field.after($hidden);
|
||||
}
|
||||
additional = additional.split('=');
|
||||
$hidden.attr('name', additional[0]).val(additional[1])
|
||||
}
|
||||
});
|
||||
$(this).trigger('change');
|
||||
};
|
||||
|
||||
|
||||
})(typeof django !== 'undefined' ? django.jQuery : undefined);
|
|
@ -1,183 +0,0 @@
|
|||
/**
|
||||
* List sortables
|
||||
*/
|
||||
(function ($) {
|
||||
$.fn.suit_list_sortable = function () {
|
||||
var $inputs = $(this);
|
||||
if (!$inputs.length)
|
||||
return;
|
||||
|
||||
// Detect if this is normal or mptt table
|
||||
var mptt_table = $inputs.first().closest('table').hasClass('table-mptt');
|
||||
|
||||
function performMove($arrow, $row) {
|
||||
var $next, $prev;
|
||||
|
||||
$row.closest('table').find('tr.selected').removeClass('selected');
|
||||
if (mptt_table) {
|
||||
function getPadding($tr) {
|
||||
return parseInt($tr.find('th:first').css('padding-left'));
|
||||
}
|
||||
|
||||
function findWithChildren($tr) {
|
||||
var padding = getPadding($tr);
|
||||
return $tr.nextUntil(function () {
|
||||
return getPadding($(this)) <= padding
|
||||
}).andSelf();
|
||||
}
|
||||
|
||||
var padding = getPadding($row);
|
||||
var $rows_to_move = findWithChildren($row);
|
||||
if ($arrow.data('dir') === 'down') {
|
||||
$next = $rows_to_move.last().next();
|
||||
if ($next.length && getPadding($next) === padding) {
|
||||
var $after = findWithChildren($next).last();
|
||||
if ($after.length) {
|
||||
$rows_to_move.insertAfter($after).addClass('selected');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$prev = $row.prevUntil(function () {
|
||||
return getPadding($(this)) <= padding
|
||||
}).andSelf().first().prev();
|
||||
if ($prev.length && getPadding($prev) === padding) {
|
||||
$rows_to_move.insertBefore($prev).addClass('selected')
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if ($arrow.data('dir') === 'down') {
|
||||
$next = $row.next();
|
||||
if ($next.is(':visible') && $next.length) {
|
||||
$row.insertAfter($next).addClass('selected')
|
||||
}
|
||||
} else {
|
||||
$prev = $row.prev();
|
||||
if ($prev.is(':visible') && $prev.length) {
|
||||
$row.insertBefore($prev).addClass('selected')
|
||||
}
|
||||
}
|
||||
}
|
||||
markLastInline($row.parent());
|
||||
}
|
||||
|
||||
function onArrowClick(e) {
|
||||
var $sortable = $(this);
|
||||
var $row = $sortable.closest(
|
||||
$sortable.hasClass('sortable-stacked') ? 'div.inline-related' : 'tr'
|
||||
);
|
||||
performMove($sortable, $row);
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
function createLink(text, direction, on_click_func, is_stacked) {
|
||||
return $('<a/>').attr('href', '#')
|
||||
.addClass('sortable sortable-' + direction +
|
||||
(is_stacked ? ' sortable-stacked' : ''))
|
||||
.attr('data-dir', direction).html(text)
|
||||
.on('click', on_click_func);
|
||||
}
|
||||
|
||||
function markLastInline($rowParent) {
|
||||
$rowParent.find(' > .last-sortable').removeClass('last-sortable');
|
||||
$rowParent.find('tr.form-row:visible:last').addClass('last-sortable');
|
||||
}
|
||||
|
||||
var $lastSortable;
|
||||
$inputs.each(function () {
|
||||
var $inline_sortable = $('<div class="inline-sortable"/>'),
|
||||
icon = '<span class="fa fa-lg fa-arrow-up"></span>',
|
||||
$sortable = $(this),
|
||||
is_stacked = $sortable.hasClass('suit-sortable-stacked');
|
||||
|
||||
var $up_link = createLink(icon, 'up', onArrowClick, is_stacked),
|
||||
$down_link = createLink(icon.replace('-up', '-down'), 'down', onArrowClick, is_stacked);
|
||||
|
||||
if (is_stacked) {
|
||||
var $sortable_row = $sortable.closest('div.form-group'),
|
||||
$stacked_block = $sortable.closest('div.inline-related'),
|
||||
$links_span = $('<span/>').attr('class', 'stacked-inline-sortable');
|
||||
|
||||
// Add arrows to header h3, move order input and remove order field row
|
||||
$links_span.append($up_link).append($down_link);
|
||||
$links_span.insertAfter($stacked_block.find('.inline_label'));
|
||||
$stacked_block.append($sortable);
|
||||
$sortable_row.remove();
|
||||
} else {
|
||||
$sortable.parent().append($inline_sortable);
|
||||
$inline_sortable.append($up_link);
|
||||
$inline_sortable.append($down_link);
|
||||
$lastSortable = $sortable;
|
||||
}
|
||||
});
|
||||
|
||||
$lastSortable && markLastInline($lastSortable.closest('.form-row').parent());
|
||||
|
||||
// Filters out unchanged checkboxes, selects and sortable field itself
|
||||
function filter_unchanged(i, input) {
|
||||
if (input.type == 'checkbox') {
|
||||
if (input.defaultChecked == input.checked) {
|
||||
return false;
|
||||
}
|
||||
} else if (input.type == 'select-one' || input.type == 'select-multiple') {
|
||||
var options = input.options, option;
|
||||
for (var j = 0; j < options.length; j++) {
|
||||
option = options[j];
|
||||
if (option.selected && option.selected == option.defaultSelected) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else if ($(input).hasClass('suit-sortable')) {
|
||||
if (input.defaultValue == input.value && input.value == 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Update input count right before submit
|
||||
if ($inputs && $inputs.length) {
|
||||
var $last_input = $inputs.last();
|
||||
var selector = $(this).selector;
|
||||
$($last_input[0].form).submit(function (e) {
|
||||
var i = 0, value;
|
||||
// e.preventDefault();
|
||||
$(selector).each(function () {
|
||||
var $input = $(this);
|
||||
var fieldset_id = $input.attr('name').split(/-\d+-/)[0];
|
||||
// Check if any of new dynamic block values has been added
|
||||
var $set_block = $input.closest('.dynamic-' + fieldset_id);
|
||||
var $changed_fields = $set_block.find(":input[type!='hidden']:not(.suit-sortable)").filter(
|
||||
function () {
|
||||
return $(this).val() != "";
|
||||
}).filter(filter_unchanged);
|
||||
// console.log($changed_fields.length, $changed_fields);
|
||||
var is_changelist = !$set_block.length;
|
||||
if (is_changelist
|
||||
|| $set_block.hasClass('has_original')
|
||||
|| $changed_fields.serializeArray().length
|
||||
// Since jQuery serialize() doesn't include type=file do additional check
|
||||
|| $changed_fields.find(":input[type='file']").addBack().length) {
|
||||
value = i++;
|
||||
$input.val(value);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Suit.after_inline.register('bind_sortable_arrows', function (prefix, row) {
|
||||
$(row).find('.suit-sortable').on('click', onArrowClick);
|
||||
markLastInline($(row).parent());
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
$(function () {
|
||||
$('.suit-sortable').suit_list_sortable();
|
||||
});
|
||||
|
||||
}(django.jQuery));
|
||||
|
||||
// Call Suit.after_inline
|
||||
django.jQuery(document).on('formset:added', function (e, row, prefix) {
|
||||
Suit.after_inline.run(prefix, row);
|
||||
});
|
|
@ -0,0 +1,12 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<div class="hero full">
|
||||
<div class="hero-body">
|
||||
<p class="title">Acerca de Mauflix</p>
|
||||
<p>MauFlix es una videoteca personal. Esta no persigue <b>ningún tipo de
|
||||
lucro</b> y solo cuenta con datos informativos de las películas. Aquí no
|
||||
encontrarás enlaces de descarga.</p>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
|
@ -1,57 +1,26 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<title>MauFlix</title>
|
||||
|
||||
{% load static %}
|
||||
<link rel="shortcut icon" href="{% static 'img/favicon.png' %}">
|
||||
<link rel="stylesheet" href="{% static 'css/bulma.min.css' %}" type="text/css">
|
||||
<link rel="stylesheet" href="{% static 'css/main.css' %}" type="text/css">
|
||||
<script type="text/javascript" src="{% static 'js/jquery-3.4.1.min.js' %}" ></script>
|
||||
<script type="text/javascript" src="{% static 'js/main.js' %}" ></script>
|
||||
{% block media %}{% endblock %}
|
||||
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
});
|
||||
</script>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>MauFlix</title>
|
||||
{% load static %}
|
||||
<link rel="shortcut icon" href="{% static 'img/favicon.png' %}">
|
||||
<link rel="stylesheet" href="{% static 'css/bulma.min.css' %}">
|
||||
<link rel="stylesheet" href="{% static 'css/bulma.darkly.min.css' %}">
|
||||
<link rel="stylesheet" href="{% static 'css/main.css' %}">
|
||||
<script type="text/javascript" src="{% static 'js/main.js' %}"></script>
|
||||
</head>
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<section class="hero is-light">
|
||||
<div class="hero-body">
|
||||
<div class="container">
|
||||
<h2 class="title"> MauFlix </h1>
|
||||
<p class="subtitle">
|
||||
Otras plataformas tienen todas las películas, excepto las que nos gustan :)
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div id="movies">
|
||||
{% block content %}{% endblock %}
|
||||
<section class="hero is-small is-info">
|
||||
<div class="hero-body has-text-centered">
|
||||
<p>Mauflix. Otras plataformas tienen todas las películas, excepto las que nos gustan :)</p>
|
||||
</div>
|
||||
|
||||
<footer class="footer">
|
||||
<div class="content has-text-centered">
|
||||
<p>
|
||||
Site develop thanks to:
|
||||
<a href="https://python.org" target='_blank'>Python</a>,
|
||||
<a href="https://djangoproject.com/" target='_blank'>Django</a>,
|
||||
<a href="https://bulma.io/" target='_blank'>Bulma</a>.
|
||||
The website content is licensed <a href="http://creativecommons.org/licenses/by-nc-sa/4.0/" target='_blank'>CC BY NC SA 4.0</a>.
|
||||
</p>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
</section>
|
||||
{% include 'nav.html' %}
|
||||
<section id="main">
|
||||
{% block content %}{% endblock %}
|
||||
</section>
|
||||
{% include 'footer.html' %}
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<b>TODO: Reporta un problema</b>
|
||||
|
||||
{% endblock %}
|
|
@ -0,0 +1,16 @@
|
|||
<footer class="footer">
|
||||
<div class="content">
|
||||
<p><b>G1</b>: <code>GwhvskhBj6B6JYQLo3E9S97PFfL6gw3LgmYG1cTsjQkF</code></p>
|
||||
</div>
|
||||
<div class="content has-text-centered">
|
||||
<p>Te recomendamos <a href="https://www.videolan.org/vlc/" target="_blank">VLC</a> para ver tus películas.</p>
|
||||
</div>
|
||||
<div class="content has-text-centered">
|
||||
<p>
|
||||
Sitio desarrollado con
|
||||
<a href="https://python.org" target='_blank'>Python</a>,
|
||||
<a href="https://djangoproject.com/" target='_blank'>Django</a> y
|
||||
<a href="https://bulma.io/" target='_blank'>Bulma</a>.
|
||||
</p>
|
||||
</div>
|
||||
</footer>
|
|
@ -0,0 +1,121 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<div class="hero full">
|
||||
<div class="hero-body">
|
||||
<p class="title">Ayuda</p>
|
||||
<div class="content">
|
||||
<h2 id="busqueda">Sobre las búsquedas</h2>
|
||||
<p>En las búsquedas existen las siguientes consideraciones:</p>
|
||||
<ul>
|
||||
<li>
|
||||
Las búsquedas tienen un límite de cien resultados.
|
||||
</li>
|
||||
<li>
|
||||
Las búsquedas ignoran mayúsculas, tildes, eñes y signos de puntuación,
|
||||
así que «<a href="{% url 'search' %}?q=¡Iñarritú!">¡Iñarritú!</a>»
|
||||
es lo mismo que «<a href="{% url 'search' %}?q=inarritu">inarritu</a>».
|
||||
</li>
|
||||
<li>
|
||||
Las búsquedas no buscan palabras exactas, sino las palabras que comienzan
|
||||
con el término de búsqueda, así que «<a href="{% url 'search' %}?q=perro">perro</a>»
|
||||
también incluye los resultados de «perros».
|
||||
</li>
|
||||
<li>
|
||||
Cuando una búsqueda encuentra más de cien películas,
|
||||
los cien resultados son seleccionados y ordenados de manera aleatoria:
|
||||
<u>nunca obtendrás los mismos resultados</u>.
|
||||
</li>
|
||||
</ul>
|
||||
<h2 id="refinamiento">Refinamiento en las búsquedas</h2>
|
||||
<p>Para buscar por campos específicos se usan algunos de los siguientes prefijos:</p>
|
||||
<table class="table is-striped is-narrow">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>prefijo:ejemplo</th>
|
||||
<th>Descripción</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><a href="{% url 'search' %}?q=t:lucio">t:lucio</a></td>
|
||||
<td>Busca «lucio» en los <u>t</u>ítulos</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="{% url 'search' %}?q=o:monkey">o:monkey</a></td>
|
||||
<td>Busca «monkey» en los títulos en idioma <u>o</u>riginal</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="{% url 'search' %}?q=y:1989">y:1989</a></td>
|
||||
<td>Busca películas publicadas en el año (<i><u>y</u>ear</i>) «1989»</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="{% url 'search' %}?q=p:México">p:México</a></td>
|
||||
<td>Busca películas hechas en el <u>p</u>aís «México»</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="{% url 'search' %}?q=d:varda">d:varda</a></td>
|
||||
<td>Busca películas <u>d</u>irigidas por «varda»</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="{% url 'search' %}?q=a:uma">a:uma</a></td>
|
||||
<td>Busca películas en donde <u>a</u>ctúe «uma»</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="{% url 'search' %}?q=g:drama">g:drama</a></td>
|
||||
<td>Busca películas del <u>g</u>énero «drama»</td>
|
||||
</tr>
|
||||
<tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p>Esto añade las siguientes consideraciones:</p>
|
||||
<ul>
|
||||
<li>
|
||||
Para dos o más palabras con prefijos se usan guiones en lugar de espacios,
|
||||
como en «<a href="{% url 'search' %}?q=p:estados-unidos">p:estados-unidos</a>».
|
||||
</li>
|
||||
<li>
|
||||
Se pueden usar varias palabras con prefijos o no para restringir la búsqueda,
|
||||
como en «<a href="{% url 'search' %}?q=p:mexico+d:buñuel+cielo">p:mexico d:buñuel cielo</a>».
|
||||
</li>
|
||||
<li>
|
||||
La inexactitud en los resultados permite búsquedas más generales en los campos.
|
||||
<ul>
|
||||
<li>
|
||||
Ejemplo 1: «<a href="{% url 'search' %}?q=y:192">y:192</a>» da como resultado
|
||||
las películas publicadas en la década de 1920.
|
||||
</li>
|
||||
<li>
|
||||
Ejemplo 2: «<a href="{% url 'search' %}?q=p:a">p:a</a>» da como resultado
|
||||
las películas hechas en los países que comienzan con «a».
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<h2 id="api">API</h2>
|
||||
<p>
|
||||
Los resultados de las búsquedas pueden ser en JSON si usas la
|
||||
<a href="https://es.wikipedia.org/wiki/Interfaz_de_programaci%C3%B3n_de_aplicaciones" target="_blank">API</a>.
|
||||
</p>
|
||||
<h3>Obtención de películas</h3>
|
||||
<p>
|
||||
La API para obtener películas usa la misma sintaxis a una búsqueda ordinaria,
|
||||
solo cambia la url <code>search</code> por <code>api</code>.
|
||||
</p>
|
||||
<p>
|
||||
Es decir, en lugar de
|
||||
<code><a href="{% url 'search' %}?q=p:mexico+d:cuaron">search/?q=p:mexico+d:cuaron</a></code> usa
|
||||
<code><a href="{% url 'api' %}?q=p:mexico+d:cuaron"><u>api</u>/?q=p:mexico+d:cuaron</a></code>.
|
||||
</p>
|
||||
<h3>Obtención de ficha</h3>
|
||||
<p>
|
||||
Para obtener una película en específico solo necesitas su ID.
|
||||
<p>
|
||||
Por ejemplo, para obtener la ficha de <a href="{% url 'movie' id=1596 %}"><i>Gremlins</i></a> usa
|
||||
<code><a href="{% url 'api' %}?id=1596">api/?id=1596</a></code>.
|
||||
</p>
|
||||
<p><i>Happy hacking</i> 😎</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
|
@ -1,31 +1,18 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<section class="section">
|
||||
<div class="container">
|
||||
<p class="subtitle">
|
||||
Te recomendamos usar <a href="https://www.videolan.org/vlc/" target="_blank">VLC</a>
|
||||
para ver tus <a href="/movies">películas</a>.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section">
|
||||
<div class="container">
|
||||
<p class="">
|
||||
<b>BCH</b>: <code>qztd3l00xle5tffdqvh2snvadkuau2ml0uqm4n875d</code>
|
||||
</p>
|
||||
<p class="">
|
||||
<b>BTC</b>: <code>3FhiXcXmAesmQzrNEngjHFnvaJRhU1AGWV</code>
|
||||
</p>
|
||||
<p class="">
|
||||
<b>LTC</b>: <code>MBcgQ3LQJA4W2wsXknTdm2fxRSysLaBJHS</code>
|
||||
</p>
|
||||
<p class="">
|
||||
<b>ETH</b>: <code>0x61a4f614a30ff686445751ed8328b82b77ecfc69</code>
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{% for section, content in sections.items %}
|
||||
{% if section != "genders"%}
|
||||
{% if section == 'Más descargados' %}
|
||||
{% if user.is_authenticated %}
|
||||
{% include 'section.html' with section=section content=content %}
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{% include 'section.html' with section=section content=content %}
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{% for gender, val in content.items %}
|
||||
{% include 'section.html' with gender=True section=gender content=val %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endblock %}
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<script type='text/javascript' charset='utf-8'>
|
||||
|
||||
webix.ready(function(){
|
||||
webix.ui(ui_main)
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
{% endblock %}
|
|
@ -0,0 +1,76 @@
|
|||
<div class="hero-body movie-body">
|
||||
{% if request.get_full_path == "/" %}
|
||||
<div class="stats">
|
||||
<p class="stars">{{ movie.stars_icons }}</p>
|
||||
<p><i class="gg-time"></i>{{ movie.duration_formatted | safe }}</p>
|
||||
{% if user.is_authenticated %}
|
||||
<p><i class="gg-software-download"></i>{{ movie.count_formatted }}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="about">
|
||||
<div class="columns is-desktop">
|
||||
<div class="column">
|
||||
<h2 class="subtitle">Cartel</h2>
|
||||
<figure>
|
||||
<img src="{{ movie.cartel }}">
|
||||
</figure>
|
||||
</div>
|
||||
<div class="column">
|
||||
<h2 class="subtitle">Ficha técnica</h2>
|
||||
<table class="table infobox">
|
||||
<tbody>
|
||||
<tr><td>Título original</td><td>{{ movie.original_name }}<td></tr>
|
||||
<tr><td>Año</td><td>
|
||||
<a href="{% url 'search' %}?q=y:{{ movie.year | slugify }}">{{ movie.year }}</a>
|
||||
<td></tr>
|
||||
<tr><td>País</td><td>
|
||||
{% for country in movie.countries %}
|
||||
<a href="{% url 'search' %}?q=p:{{ country | slugify }}">
|
||||
{{ country }}</a>{% if not forloop.last %},{% endif %}
|
||||
{% endfor %}
|
||||
<td></tr>
|
||||
<tr><td>Duración</td><td>{{ movie.duration }} min<td></tr>
|
||||
<tr><td>Dirección</td><td>
|
||||
{% for director in movie.directors %}
|
||||
<a href="{% url 'search' %}?q=d:{{ director | slugify }}">
|
||||
{{ director }}</a>{% if not forloop.last %},{% endif %}
|
||||
{% endfor %}
|
||||
<td></tr>
|
||||
<tr><td>Reparto</td><td>
|
||||
{% for actor in movie.actors %}
|
||||
<a href="{% url 'search' %}?q=a:{{ actor | slugify}}">
|
||||
{{ actor }}</a>{% if not forloop.last %},{% endif %}
|
||||
{% endfor %}
|
||||
<td></tr>
|
||||
<tr><td>Género</td><td>
|
||||
{% for gender in movie.genders %}
|
||||
<a href="{% url 'search' %}?q=g:{{ gender | slugify }}">
|
||||
{{ gender }}</a>{% if not forloop.last %},{% endif %}
|
||||
{% endfor %}
|
||||
<td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{% if movie.wiki.summary %}
|
||||
<h2 class="subtitle">Sinopsis de <a href="{{ movie.wiki.url }}" target="_blank">Wikipedia</a></h2>
|
||||
{{ movie.wiki.summary | safe }}
|
||||
{% else %}
|
||||
<h2 class="subtitle">Sinopsis de Wikipedia</h2>
|
||||
<p>No se encontró su artículo en Wikipedia, haz <a href="https://es.wikipedia.org/wiki/{{ movie.name }}">clic aquí</a> para escribirlo o solicitarlo.</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if user.is_authenticated %}
|
||||
<video id="player" playsinline controls data-poster="{{ movie.cartel }}">
|
||||
<source src="{{ movie.file_name }}" type="video/mp4">
|
||||
</video>
|
||||
{% load static %}
|
||||
<!-- CSS y JS necesario paara el reproductor -->
|
||||
<!-- Cfr. https://github.com/sampotts/plyr -->
|
||||
<link rel="stylesheet" href="{% static 'css/plyr.css' %}">
|
||||
<script type="text/javascript" src="{% static 'js/plyr.js' %}"></script>
|
||||
<script type="text/javascript" src="{% static 'js/player.js' %}"></script>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
|
@ -0,0 +1,13 @@
|
|||
<div class="hero-foot">
|
||||
<nav class="tabs is-boxed is-fullwidth">
|
||||
<div class="container {% if request.get_full_path == "/" %}extra-info{% endif %}">
|
||||
<ul>
|
||||
{% if request.get_full_path == "/" %}
|
||||
<li><a href="/movie/{{ movie.id }}">Ver</a></li>
|
||||
{% else %}
|
||||
<li><a href="{{ movie.file_name }}" target="_blank" download>Descargar</a></li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
|
@ -0,0 +1,12 @@
|
|||
<div class="hero-head movie-head">
|
||||
<p class="title">{{ movie.name }}</p>
|
||||
{% if request.get_full_path != "/" %}
|
||||
<p class="stats">
|
||||
<span class="stars">{{ movie.stars_icons }}</span>
|
||||
<span><i class="gg-time"></i>{{ movie.duration_formatted | safe }}</span>
|
||||
{% if user.is_authenticated %}
|
||||
<span><i class="gg-software-download"></i>{{ movie.count_formatted }}</span>
|
||||
{% endif %}
|
||||
</p>
|
||||
{% endif %}
|
||||
</div>
|
|
@ -0,0 +1,5 @@
|
|||
<section class="hero {% if request.get_full_path == "/" %}is-primary full{% else %}is-large{% endif%}">
|
||||
{% include 'info-head.html' with movie=movie %}
|
||||
{% include 'info-body.html' with movie=movie %}
|
||||
{% include 'info-foot.html' with movie=movie %}
|
||||
</section>
|
|
@ -0,0 +1,4 @@
|
|||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
{% include 'info.html' with movie=movie %}
|
||||
{% endblock %}
|
|
@ -1,87 +0,0 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<BR>
|
||||
|
||||
|
||||
<!-- Main container -->
|
||||
<nav class="level">
|
||||
<!-- Left side -->
|
||||
<div class="level-left">
|
||||
<div class="level-item">
|
||||
<p class="subtitle is-6">
|
||||
Mostrando <strong>{{count}}</strong> películas
|
||||
</p>
|
||||
</div>
|
||||
<div class="level-item">
|
||||
<div class="field has-addons">
|
||||
<p class="control">
|
||||
<input class="input" id='search' type="text" placeholder="Buscar por nombre">
|
||||
</p>
|
||||
<p class="control">
|
||||
<button class="button is-info is-light" onclick='search_movie();'>Buscar</button>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="level-item">
|
||||
<div class="select is-info">
|
||||
<select id='directors' onChange='director_change()'>
|
||||
<option>Por director</option>
|
||||
{% for director in directors %}
|
||||
{% ifequal selected_director director %}
|
||||
<option selected>{{director}}</option>
|
||||
{% else %}
|
||||
<option>{{director}}</option>
|
||||
{% endifequal %}
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Right side -->
|
||||
<div class="level-right">
|
||||
<p class="level-item">
|
||||
{% if args %}
|
||||
<button class="button is-info is-light"><a href="/movies/">Ultimas</a></button>
|
||||
{% else %}
|
||||
<button class="button is-info is-light"><a href="/movies/all">Ver Todas</a></button>
|
||||
{% endif %}
|
||||
</p>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
|
||||
{% for row in movies %}
|
||||
<div class="columns is-desktop is-variable is-1">
|
||||
{% for cell in row %}
|
||||
<div class="column" style="padding: 0.5rem;">
|
||||
<article class="media has-background-light custom-article">
|
||||
<figure class="media-left" style="padding-top: 0.75rem;">
|
||||
<img src='{{ cell.image }}' height='150' width='100'>
|
||||
</figure>
|
||||
<div class="media-content">
|
||||
<div class="content">
|
||||
<p>
|
||||
<strong><a class='downmovie' href='{{ cell.url }}' id={{ cell.id }}>{{ cell.name }}</a></strong>
|
||||
<BR>
|
||||
({{ cell.original_name }})
|
||||
</p>
|
||||
<p>
|
||||
<strong>Director</strong>: {{ cell.director }} <br>
|
||||
<strong>País</strong>: {{ cell.country }} <br>
|
||||
<strong>Año</strong>: {{ cell.year }}, <strong>Duración</strong>: {{ cell.duration }} <br>
|
||||
<strong>Descargas</strong>: <span id='down{{cell.id}}'>{{ cell.count }}</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
<BR>
|
||||
|
||||
{% endblock %}
|
|
@ -0,0 +1,41 @@
|
|||
<nav id="nav" class="navbar" role="navigation" aria-label="main navigation">
|
||||
{% load static %}
|
||||
<div class="navbar-brand">
|
||||
<a class="navbar-item" href="{% url 'home' %}">
|
||||
<img src="{% static 'img/logo-mauflix.png' %}" width="112" height="28">
|
||||
</a>
|
||||
<a role="button" class="navbar-burger" aria-label="menu" aria-expanded="false" data-target="menu">
|
||||
<span aria-hidden="true"></span>
|
||||
<span aria-hidden="true"></span>
|
||||
<span aria-hidden="true"></span>
|
||||
</a>
|
||||
</div>
|
||||
<div id="menu" class="navbar-menu force-display">
|
||||
<div class="navbar-start">
|
||||
{% if request.get_full_path != "/" %}
|
||||
<a class="navbar-item" href="{% url 'home' %}">Inicio</a>
|
||||
{% endif %}
|
||||
{% if request.path != "/search/" %}
|
||||
<a class="navbar-item" href="{% url 'search' %}">Buscar</a>
|
||||
{% endif %}
|
||||
<div class="navbar-item has-dropdown is-hoverable">
|
||||
<a class="navbar-link">Más</a>
|
||||
<div class="navbar-dropdown">
|
||||
<a class="navbar-item" href="{% url 'about' %}" >Acerca</a>
|
||||
<a class="navbar-item" href="{% url 'help' %}" >Ayuda</a>
|
||||
<!--
|
||||
<hr class="navbar-divider">
|
||||
<a class="navbar-item" href="{% url 'bugs' %}">Reporta un problema</a>
|
||||
-->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="navbar-end">
|
||||
<div class="navbar-item">
|
||||
<div class="buttons">
|
||||
<a class="button is-primary" href="{% url 'admin:index' %}">Ingresa</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
|
@ -0,0 +1,33 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="hero">
|
||||
<form action="/search" method="get">
|
||||
<div class="hero-body search-div">
|
||||
<input id="search-bar" class="input is-primary is-large" type="text" placeholder="Quiero ver…" value="{{ query }}" name="q">
|
||||
<div class="control">
|
||||
<button class="button is-primary"><i class="gg-search"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
{% if movies %}
|
||||
{% include 'section.html' with section='Resultados de búsqueda.' content=movies %}
|
||||
{% else %}
|
||||
<section class="hero">
|
||||
<div class="hero-body">
|
||||
<p class="title">
|
||||
{% if request.get_full_path == "/search/" %}
|
||||
Explora el catálogo de Mauflix.
|
||||
{% else %}
|
||||
¡Ups! Tu búsqueda no arrojó ningún resultado.
|
||||
{% endif %}
|
||||
</p>
|
||||
<p class="subtitle">Visita la <a href="{% url 'help' %}">ayuda</a> para obtener mejores resultados.
|
||||
</div>
|
||||
</section>
|
||||
{% endif %}
|
||||
|
||||
{% endblock %}
|
|
@ -0,0 +1,40 @@
|
|||
<section class="hero">
|
||||
<div class="hero-body hero-cartels">
|
||||
{% if request.get_full_path == "/" %}
|
||||
<p class="title">
|
||||
{% if gender %}
|
||||
<a href="{% url 'search' %}?q=g:{{ section | slugify }}">
|
||||
{% else %}
|
||||
<a href="{% url 'search' %}?q=s:{{ section | slugify }}">
|
||||
{% endif %}
|
||||
{{ section }}<span class="arrows"/></a>
|
||||
</p>
|
||||
{% else %}
|
||||
<p class="title">{{ section }}</p>
|
||||
{% if request.path == "/search/" %}
|
||||
<p class="subtitle">Visita la <a href="{% url 'help' %}">ayuda</a> para obtener mejores resultados.
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
<div class="cartels">
|
||||
{% for movie in content %}
|
||||
<div class="cartel tooltip">
|
||||
<span class="tooltiptext">{{ movie.name }} ({{ movie.duration_formatted | safe }})</span>
|
||||
<!--
|
||||
Lo comentado activaría una ficha que se ve en lugar del cartel.
|
||||
Una idea a futuro es que sirva para mostrar la ficha de la
|
||||
pelicula como ventana flotante.
|
||||
<input type="checkbox" class="toggle">
|
||||
-->
|
||||
<div class="info">
|
||||
{% include 'info.html' with movie=movie %}
|
||||
</div>
|
||||
<figure class="image is-2by3">
|
||||
<a href="/movie/{{ movie.id }}">
|
||||
<img src="{{ movie.cartel }}">
|
||||
</a>
|
||||
</figure>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|