|
@ -11,7 +11,8 @@ import logging
|
|||
from flask_compress import Compress
|
||||
from flask_caching import Cache
|
||||
from flask_assets import Environment, Bundle
|
||||
from flask import Flask, render_template
|
||||
from flask import Flask, render_template, abort
|
||||
from werkzeug.exceptions import HTTPException
|
||||
|
||||
# Configuration
|
||||
import platformdirs
|
||||
|
@ -59,7 +60,7 @@ def create_app(test_config=None):
|
|||
app.config.from_mapping(test_config)
|
||||
|
||||
# Load theme
|
||||
theme_manager.CompileTheme('default', app.root_path)
|
||||
theme_manager.compile_theme('default', app.root_path)
|
||||
|
||||
# Bundle JS files
|
||||
js_scripts = Bundle('js/*.js', output='gen/packed.js')
|
||||
|
@ -68,6 +69,10 @@ def create_app(test_config=None):
|
|||
# Error handlers
|
||||
@app.errorhandler(Exception)
|
||||
def error_page(err):
|
||||
# If the error is not an HTTPException, return a 500 error
|
||||
if not isinstance(err, HTTPException):
|
||||
abort(500)
|
||||
|
||||
error = err.code
|
||||
msg = err.description
|
||||
return render_template('error.html', error=error, msg=msg), err.code
|
||||
|
@ -77,7 +82,7 @@ def create_app(test_config=None):
|
|||
app.register_blueprint(auth.blueprint)
|
||||
|
||||
# Load the different routes
|
||||
from .routes import api, groups, routing, settings
|
||||
from gallery.routes import api, groups, routing, settings
|
||||
app.register_blueprint(api.blueprint)
|
||||
app.register_blueprint(groups.blueprint)
|
||||
app.register_blueprint(routing.blueprint)
|
||||
|
|
|
@ -65,15 +65,16 @@ def register():
|
|||
"""
|
||||
Register a new user
|
||||
"""
|
||||
username = request.form['username']
|
||||
email = request.form['email']
|
||||
password = request.form['password']
|
||||
password_repeat = request.form['password-repeat']
|
||||
# Thanks Fennec for reminding me to strip out the whitespace lol
|
||||
username = request.form['username'].strip()
|
||||
email = request.form['email'].strip()
|
||||
password = request.form['password'].strip()
|
||||
password_repeat = request.form['password-repeat'].strip()
|
||||
|
||||
error = []
|
||||
|
||||
email_regex = re.compile(r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b')
|
||||
username_regex = re.compile(r'\b[A-Za-z0-9._%+-]+\b')
|
||||
username_regex = re.compile(r'\b[A-Za-z0-9._-]+\b')
|
||||
|
||||
if not username or not username_regex.match(username):
|
||||
error.append('Username is invalid!')
|
||||
|
@ -116,8 +117,8 @@ def login():
|
|||
"""
|
||||
Log in a registered user by adding the user id to the session
|
||||
"""
|
||||
username = request.form['username']
|
||||
password = request.form['password']
|
||||
username = request.form['username'].strip()
|
||||
password = request.form['password'].strip()
|
||||
|
||||
user = db_session.query(db.Users).filter_by(username=username).first()
|
||||
error = []
|
||||
|
|
|
@ -13,6 +13,7 @@ USER_DIR = platformdirs.user_config_dir('onlylegs')
|
|||
DB_PATH = os.path.join(USER_DIR, 'gallery.sqlite')
|
||||
|
||||
|
||||
# In the future, I want to add support for other databases
|
||||
# engine = create_engine('postgresql://username:password@host:port/database_name', echo=False)
|
||||
# engine = create_engine('mysql://username:password@host:port/database_name', echo=False)
|
||||
engine = create_engine(f'sqlite:///{DB_PATH}', echo=False)
|
||||
|
@ -61,18 +62,6 @@ class Posts (base): # pylint: disable=too-few-public-methods, C0103
|
|||
junction = relationship('GroupJunction', backref='posts')
|
||||
|
||||
|
||||
class Thumbnails (base): # pylint: disable=too-few-public-methods, C0103
|
||||
"""
|
||||
Thumbnail table
|
||||
"""
|
||||
__tablename__ = 'thumbnails'
|
||||
|
||||
id = Column(Integer, primary_key=True)
|
||||
file_name = Column(String, unique=True, nullable=False)
|
||||
file_ext = Column(String, nullable=False)
|
||||
data = Column(PickleType, nullable=False)
|
||||
|
||||
|
||||
class Groups (base): # pylint: disable=too-few-public-methods, C0103
|
||||
"""
|
||||
Group table
|
||||
|
|
0
gallery/routes/__init__.py
Normal file
|
@ -1,26 +1,24 @@
|
|||
"""
|
||||
Onlylegs - API endpoints
|
||||
Used internally by the frontend and possibly by other applications
|
||||
"""
|
||||
from uuid import uuid4
|
||||
import os
|
||||
import pathlib
|
||||
import io
|
||||
import logging
|
||||
from datetime import datetime as dt
|
||||
import platformdirs
|
||||
|
||||
from flask import (Blueprint, send_from_directory, send_file,
|
||||
abort, flash, jsonify, request, g, current_app)
|
||||
from flask import Blueprint, send_from_directory, abort, flash, jsonify, request, g, current_app
|
||||
from werkzeug.utils import secure_filename
|
||||
|
||||
from colorthief import ColorThief
|
||||
from PIL import Image, ImageOps, ImageFilter
|
||||
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
from gallery.auth import login_required
|
||||
|
||||
from gallery import db
|
||||
from gallery.utils import metadata as mt
|
||||
from gallery.utils.generate_image import generate_thumbnail
|
||||
|
||||
|
||||
blueprint = Blueprint('api', __name__, url_prefix='/api')
|
||||
|
@ -29,18 +27,13 @@ db_session = db_session()
|
|||
|
||||
|
||||
@blueprint.route('/file/<file_name>', methods=['GET'])
|
||||
def get_file(file_name):
|
||||
def file(file_name):
|
||||
"""
|
||||
Returns a file from the uploads folder
|
||||
r for resolution, 400x400 or thumb for thumbnail
|
||||
f is whether to apply filters to the image, such as blurring NSFW images
|
||||
b is whether to force blur the image, even if it's not NSFW
|
||||
"""
|
||||
# Get args
|
||||
res = request.args.get('r', default=None, type=str) # Type of file (thumb, etc)
|
||||
filtered = request.args.get('f', default=False, type=bool) # Whether to apply filters # pylint: disable=W0612
|
||||
blur = request.args.get('b', default=False, type=bool) # Whether to force blur
|
||||
|
||||
ext = request.args.get('e', default=None, type=str) # File extension
|
||||
file_name = secure_filename(file_name) # Sanitize file name
|
||||
|
||||
# if no args are passed, return the raw file
|
||||
|
@ -50,64 +43,12 @@ def get_file(file_name):
|
|||
|
||||
return send_from_directory(current_app.config['UPLOAD_FOLDER'], file_name)
|
||||
|
||||
buff = io.BytesIO()
|
||||
img = None # Image object to be set
|
||||
thumb = generate_thumbnail(file_name, res, ext)
|
||||
|
||||
try: # Open image and set extension
|
||||
img = Image.open(os.path.join(current_app.config['UPLOAD_FOLDER'], file_name))
|
||||
except FileNotFoundError: # FileNotFound is raised if the file doesn't exist
|
||||
logging.error('File not found: %s', file_name)
|
||||
if not thumb:
|
||||
abort(404)
|
||||
except OSError as err: # OSError is raised if the file is broken or corrupted
|
||||
logging.error('Possibly broken image %s, error: %s', file_name, err)
|
||||
abort(500)
|
||||
|
||||
img_ext = pathlib.Path(file_name).suffix.replace('.', '').lower() # Get file extension
|
||||
img_ext = current_app.config['ALLOWED_EXTENSIONS'][img_ext] # Convert to MIME type
|
||||
img_icc = img.info.get("icc_profile") # Get ICC profile
|
||||
|
||||
img = ImageOps.exif_transpose(img) # Rotate image based on EXIF data
|
||||
|
||||
# Todo: If type is thumb(nail), return from database instead of file system pylint: disable=W0511
|
||||
# as it's faster than generating a new thumbnail on every request
|
||||
if res:
|
||||
if res in ['thumb', 'thumbnail']:
|
||||
width, height = 400, 400
|
||||
elif res in ['prev', 'preview']:
|
||||
width, height = 1920, 1080
|
||||
else:
|
||||
try:
|
||||
width, height = res.split('x')
|
||||
width = int(width)
|
||||
height = int(height)
|
||||
except ValueError:
|
||||
abort(400)
|
||||
|
||||
img.thumbnail((width, height), Image.LANCZOS)
|
||||
|
||||
# Todo: If the image has a NSFW tag, blur image for example pylint: disable=W0511
|
||||
# if filtered:
|
||||
# pass
|
||||
|
||||
# If forced to blur, blur image
|
||||
if blur:
|
||||
img = img.filter(ImageFilter.GaussianBlur(20))
|
||||
|
||||
try:
|
||||
img.save(buff, img_ext, icc_profile=img_icc)
|
||||
except OSError:
|
||||
# This usually happens when saving a JPEG with an ICC profile,
|
||||
# so we convert to RGB and try again
|
||||
img = img.convert('RGB')
|
||||
img.save(buff, img_ext, icc_profile=img_icc)
|
||||
except Exception as err:
|
||||
logging.error('Could not resize image %s, error: %s', file_name, err)
|
||||
abort(500)
|
||||
|
||||
img.close() # Close image to free memory, learned the hard way
|
||||
buff.seek(0) # Reset buffer to start
|
||||
|
||||
return send_file(buff, mimetype='image/' + img_ext)
|
||||
return send_from_directory(os.path.dirname(thumb), os.path.basename(thumb))
|
||||
|
||||
|
||||
@blueprint.route('/upload', methods=['POST'])
|
||||
|
@ -171,34 +112,37 @@ def delete_image(image_id):
|
|||
"""
|
||||
img = db_session.query(db.Posts).filter_by(id=image_id).first()
|
||||
|
||||
# Check if image exists and if user is allowed to delete it (author)
|
||||
if img is None:
|
||||
abort(404)
|
||||
if img.author_id != g.user.id:
|
||||
abort(403)
|
||||
|
||||
# Delete file
|
||||
try:
|
||||
os.remove(os.path.join(current_app.config['UPLOAD_FOLDER'],img.file_name))
|
||||
except FileNotFoundError:
|
||||
# File was already deleted or doesn't exist
|
||||
logging.warning('File not found: %s, already deleted or never existed', img.file_name)
|
||||
except Exception as err:
|
||||
logging.error('Could not remove file: %s', err)
|
||||
abort(500)
|
||||
|
||||
try:
|
||||
db_session.query(db.Posts).filter_by(id=image_id).delete()
|
||||
# Delete cached files
|
||||
cache_path = os.path.join(platformdirs.user_config_dir('onlylegs'), 'cache')
|
||||
cache_name = img.file_name.rsplit('.')[0]
|
||||
for cache_file in pathlib.Path(cache_path).glob(cache_name + '*'):
|
||||
os.remove(cache_file)
|
||||
|
||||
groups = db_session.query(db.GroupJunction).filter_by(post_id=image_id).all()
|
||||
for group in groups:
|
||||
db_session.delete(group)
|
||||
# Delete from database
|
||||
db_session.query(db.Posts).filter_by(id=image_id).delete()
|
||||
|
||||
db_session.commit()
|
||||
except Exception as err:
|
||||
logging.error('Could not remove from database: %s', err)
|
||||
abort(500)
|
||||
# Remove all entries in junction table
|
||||
groups = db_session.query(db.GroupJunction).filter_by(post_id=image_id).all()
|
||||
for group in groups:
|
||||
db_session.delete(group)
|
||||
|
||||
# Commit all changes
|
||||
db_session.commit()
|
||||
|
||||
logging.info('Removed image (%s) %s', image_id, img.file_name)
|
||||
flash(['Image was all in Le Head!', 1])
|
||||
flash(['Image was all in Le Head!', '1'])
|
||||
return 'Gwa Gwa'
|
||||
|
||||
|
||||
|
@ -266,40 +210,3 @@ def metadata(img_id):
|
|||
exif = mt.Metadata(img_path).yoink()
|
||||
|
||||
return jsonify(exif)
|
||||
|
||||
|
||||
@blueprint.route('/logfile')
|
||||
@login_required
|
||||
def logfile():
|
||||
"""
|
||||
Gets the log file and returns it as a JSON object
|
||||
"""
|
||||
log_dict = {}
|
||||
|
||||
with open('only.log', encoding='utf-8', mode='r') as file:
|
||||
for i, line in enumerate(file):
|
||||
line = line.split(' : ')
|
||||
|
||||
event = line[0].strip().split(' ')
|
||||
event_data = {
|
||||
'date': event[0],
|
||||
'time': event[1],
|
||||
'severity': event[2],
|
||||
'owner': event[3]
|
||||
}
|
||||
|
||||
message = line[1].strip()
|
||||
try:
|
||||
message_data = {
|
||||
'code': int(message[1:4]),
|
||||
'message': message[5:].strip()
|
||||
}
|
||||
except ValueError:
|
||||
message_data = {'code': 0, 'message': message}
|
||||
except Exception as err:
|
||||
logging.error('Could not parse log file: %s', err)
|
||||
abort(500)
|
||||
|
||||
log_dict[i] = {'event': event_data, 'message': message_data}
|
||||
|
||||
return jsonify(log_dict)
|
||||
|
|
|
@ -7,6 +7,7 @@ from flask import Blueprint, abort, render_template, url_for
|
|||
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
from gallery import db
|
||||
from gallery.utils import contrast
|
||||
|
||||
|
||||
blueprint = Blueprint('group', __name__, url_prefix='/group')
|
||||
|
@ -19,21 +20,29 @@ def groups():
|
|||
"""
|
||||
Group overview, shows all image groups
|
||||
"""
|
||||
group_list = db_session.query(db.Groups).all()
|
||||
groups = db_session.query(db.Groups).all()
|
||||
|
||||
for group_item in group_list:
|
||||
thumbnail = db_session.query(db.GroupJunction.post_id)\
|
||||
.filter(db.GroupJunction.group_id == group_item.id)\
|
||||
# For each group, get the 3 most recent images
|
||||
for group in groups:
|
||||
group.author_username = db_session.query(db.Users.username)\
|
||||
.filter(db.Users.id == group.author_id)\
|
||||
.first()[0]
|
||||
|
||||
# Get the 3 most recent images
|
||||
images = db_session.query(db.GroupJunction.post_id)\
|
||||
.filter(db.GroupJunction.group_id == group.id)\
|
||||
.order_by(db.GroupJunction.date_added.desc())\
|
||||
.first()
|
||||
.limit(3)
|
||||
|
||||
if thumbnail:
|
||||
group_item.thumbnail = db_session.query(db.Posts.file_name, db.Posts.post_alt,
|
||||
db.Posts.image_colours, db.Posts.id)\
|
||||
.filter(db.Posts.id == thumbnail[0])\
|
||||
.first()
|
||||
# For each image, get the image data and add it to the group item
|
||||
group.images = []
|
||||
for image in images:
|
||||
group.images.append(db_session.query(db.Posts.file_name, db.Posts.post_alt,
|
||||
db.Posts.image_colours, db.Posts.id)\
|
||||
.filter(db.Posts.id == image[0])\
|
||||
.first())
|
||||
|
||||
return render_template('groups/list.html', groups=group_list)
|
||||
return render_template('groups/list.html', groups=groups)
|
||||
|
||||
|
||||
@blueprint.route('/<int:group_id>')
|
||||
|
@ -41,26 +50,39 @@ def group(group_id):
|
|||
"""
|
||||
Group view, shows all images in a group
|
||||
"""
|
||||
group_item = db_session.query(db.Groups).filter(db.Groups.id == group_id).first()
|
||||
|
||||
if group_item is None:
|
||||
# Get the group, if it doesn't exist, 404
|
||||
group = db_session.query(db.Groups).filter(db.Groups.id == group_id).first()
|
||||
if group is None:
|
||||
abort(404, 'Group not found! D:')
|
||||
|
||||
group_item.author_username = db_session.query(db.Users.username)\
|
||||
.filter(db.Users.id == group_item.author_id)\
|
||||
# Get the group's author username
|
||||
group.author_username = db_session.query(db.Users.username)\
|
||||
.filter(db.Users.id == group.author_id)\
|
||||
.first()[0]
|
||||
|
||||
group_images = db_session.query(db.GroupJunction.post_id)\
|
||||
# Get all images in the group from the junction table
|
||||
junction = db_session.query(db.GroupJunction.post_id)\
|
||||
.filter(db.GroupJunction.group_id == group_id)\
|
||||
.order_by(db.GroupJunction.date_added.desc())\
|
||||
.all()
|
||||
|
||||
# Get the image data for each image in the group
|
||||
images = []
|
||||
for image in group_images:
|
||||
for image in junction:
|
||||
image = db_session.query(db.Posts).filter(db.Posts.id == image[0]).first()
|
||||
images.append(image)
|
||||
|
||||
return render_template('groups/group.html', group=group_item, images=images)
|
||||
# Check contrast for the first image in the group for the banner
|
||||
text_colour = 'rgb(var(--fg-black))'
|
||||
if images[0]:
|
||||
text_colour = contrast.contrast(images[0].image_colours[0],
|
||||
'rgb(var(--fg-black))',
|
||||
'rgb(var(--fg-white))')
|
||||
|
||||
return render_template('groups/group.html',
|
||||
group=group,
|
||||
images=images,
|
||||
text_colour=text_colour)
|
||||
|
||||
|
||||
@blueprint.route('/<int:group_id>/<int:image_id>')
|
||||
|
@ -68,39 +90,45 @@ def group_post(group_id, image_id):
|
|||
"""
|
||||
Image view, shows the image and its metadata from a specific group
|
||||
"""
|
||||
img = db_session.query(db.Posts).filter(db.Posts.id == image_id).first()
|
||||
|
||||
if img is None:
|
||||
# Get the image, if it doesn't exist, 404
|
||||
image = db_session.query(db.Posts).filter(db.Posts.id == image_id).first()
|
||||
if image is None:
|
||||
abort(404, 'Image not found')
|
||||
|
||||
img.author_username = db_session.query(db.Users.username)\
|
||||
.filter(db.Users.id == img.author_id)\
|
||||
# Get the image's author username
|
||||
image.author_username = db_session.query(db.Users.username)\
|
||||
.filter(db.Users.id == image.author_id)\
|
||||
.first()[0]
|
||||
|
||||
group_list = db_session.query(db.GroupJunction.group_id)\
|
||||
# Get all groups the image is in
|
||||
groups = db_session.query(db.GroupJunction.group_id)\
|
||||
.filter(db.GroupJunction.post_id == image_id)\
|
||||
.all()
|
||||
|
||||
img.group_list = []
|
||||
for group_item in group_list:
|
||||
group_item = db_session.query(db.Groups).filter(db.Groups.id == group_item[0]).first()
|
||||
img.group_list.append(group_item)
|
||||
# Get the group data for each group the image is in
|
||||
image.groups = []
|
||||
for group in groups:
|
||||
group = db_session.query(db.Groups.id, db.Groups.name)\
|
||||
.filter(db.Groups.id == group[0])\
|
||||
.first()
|
||||
image.groups.append(group)
|
||||
|
||||
# Get the next and previous images in the group
|
||||
next_url = db_session.query(db.GroupJunction.post_id)\
|
||||
.filter(db.GroupJunction.group_id == group_id)\
|
||||
.filter(db.GroupJunction.post_id > image_id)\
|
||||
.order_by(db.GroupJunction.date_added.asc())\
|
||||
.first()
|
||||
|
||||
prev_url = db_session.query(db.GroupJunction.post_id)\
|
||||
.filter(db.GroupJunction.group_id == group_id)\
|
||||
.filter(db.GroupJunction.post_id < image_id)\
|
||||
.order_by(db.GroupJunction.date_added.desc())\
|
||||
.first()
|
||||
|
||||
# If there is a next or previous image, get the URL for it
|
||||
if next_url is not None:
|
||||
next_url = url_for('group.group_post', group_id=group_id, image_id=next_url[0])
|
||||
if prev_url is not None:
|
||||
prev_url = url_for('group.group_post', group_id=group_id, image_id=prev_url[0])
|
||||
|
||||
return render_template('image.html', image=img, next_url=next_url, prev_url=prev_url)
|
||||
return render_template('image.html', image=image, next_url=next_url, prev_url=prev_url)
|
||||
|
|
|
@ -35,22 +35,28 @@ def image(image_id):
|
|||
"""
|
||||
Image view, shows the image and its metadata
|
||||
"""
|
||||
img = db_session.query(db.Posts).filter(db.Posts.id == image_id).first()
|
||||
|
||||
if not img:
|
||||
# Get the image, if it doesn't exist, 404
|
||||
image = db_session.query(db.Posts).filter(db.Posts.id == image_id).first()
|
||||
if not image:
|
||||
abort(404, 'Image not found :<')
|
||||
|
||||
img.author_username = db_session.query(db.Users.username)\
|
||||
.filter(db.Users.id == img.author_id).first()[0]
|
||||
# Get the image's author username
|
||||
image.author_username = db_session.query(db.Users.username)\
|
||||
.filter(db.Users.id == image.author_id).first()[0]
|
||||
|
||||
# Get the image's groups
|
||||
groups = db_session.query(db.GroupJunction.group_id)\
|
||||
.filter(db.GroupJunction.post_id == image_id).all()
|
||||
|
||||
img.groups = []
|
||||
# For each group, get the group data and add it to the image item
|
||||
image.groups = []
|
||||
for group in groups:
|
||||
group = db_session.query(db.Groups).filter(db.Groups.id == group[0]).first()
|
||||
img.groups.append(group)
|
||||
group = db_session.query(db.Groups.id, db.Groups.name)\
|
||||
.filter(db.Groups.id == group[0])\
|
||||
.first()
|
||||
image.groups.append(group)
|
||||
|
||||
# Get the next and previous images
|
||||
next_url = db_session.query(db.Posts.id)\
|
||||
.filter(db.Posts.id > image_id)\
|
||||
.order_by(db.Posts.id.asc())\
|
||||
|
@ -60,12 +66,13 @@ def image(image_id):
|
|||
.order_by(db.Posts.id.desc())\
|
||||
.first()
|
||||
|
||||
# If there is a next or previous image, get the url
|
||||
if next_url:
|
||||
next_url = url_for('gallery.image', image_id=next_url[0])
|
||||
if prev_url:
|
||||
prev_url = url_for('gallery.image', image_id=prev_url[0])
|
||||
|
||||
return render_template('image.html', image=img, next_url=next_url, prev_url=prev_url)
|
||||
return render_template('image.html', image=image, next_url=next_url, prev_url=prev_url)
|
||||
|
||||
|
||||
@blueprint.route('/profile')
|
||||
|
|
Before Width: | Height: | Size: 134 KiB After Width: | Height: | Size: 134 KiB |
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 29 KiB |
Before Width: | Height: | Size: 225 KiB |
25
gallery/static/js/lable.js
Normal file
|
@ -0,0 +1,25 @@
|
|||
document.addEventListener('DOMContentLoaded', function() {
|
||||
let labels = document.querySelectorAll('[data-label]');
|
||||
|
||||
for (let i = 0; i < labels.length; i++) {
|
||||
labels[i].addEventListener('mouseover', function() {
|
||||
let label = document.createElement('div');
|
||||
label.classList.add('label');
|
||||
label.innerHTML = this.dataset.label;
|
||||
|
||||
document.body.appendChild(label);
|
||||
|
||||
label.style.left = (this.offsetLeft + this.offsetWidth + 8) + 'px';
|
||||
label.style.top = (this.offsetTop + (label.offsetHeight / 2) - 2) + 'px';
|
||||
|
||||
setTimeout(function() {
|
||||
label.style.opacity = 1;
|
||||
}.bind(this), 250);
|
||||
});
|
||||
|
||||
labels[i].addEventListener('mouseout', function() {
|
||||
let label = document.querySelector('.label');
|
||||
label.parentNode.removeChild(label);
|
||||
});
|
||||
}
|
||||
});
|
|
@ -1,14 +1,43 @@
|
|||
// Function to show login
|
||||
function showLogin() {
|
||||
// Create elements
|
||||
cancelBtn = document.createElement('button');
|
||||
cancelBtn.classList.add('btn-block');
|
||||
cancelBtn.innerHTML = 'nuuuuuuuu';
|
||||
cancelBtn.onclick = popupDissmiss;
|
||||
|
||||
loginBtn = document.createElement('button');
|
||||
loginBtn.classList.add('btn-block');
|
||||
loginBtn.classList.add('primary');
|
||||
loginBtn.innerHTML = 'Login';
|
||||
loginBtn.type = 'submit';
|
||||
loginBtn.setAttribute('form', 'loginForm');
|
||||
|
||||
// Create form
|
||||
loginForm = document.createElement('form');
|
||||
loginForm.id = 'loginForm';
|
||||
loginForm.setAttribute('onsubmit', 'return login(event);');
|
||||
|
||||
usernameInput = document.createElement('input');
|
||||
usernameInput.classList.add('input-block');
|
||||
usernameInput.type = 'text';
|
||||
usernameInput.placeholder = 'Namey';
|
||||
usernameInput.id = 'username';
|
||||
|
||||
passwordInput = document.createElement('input');
|
||||
passwordInput.classList.add('input-block');
|
||||
passwordInput.type = 'password';
|
||||
passwordInput.placeholder = 'Passywassy';
|
||||
passwordInput.id = 'password';
|
||||
|
||||
loginForm.appendChild(usernameInput);
|
||||
loginForm.appendChild(passwordInput);
|
||||
|
||||
popUpShow(
|
||||
'Login!',
|
||||
'Need an account? <span class="link" onclick="showRegister()">Register!</span>',
|
||||
'<button class="btn-block" onclick="popupDissmiss()">Cancelee</button>' +
|
||||
'<button class="btn-block primary" form="loginForm" type="submit">Login</button>',
|
||||
'<form id="loginForm" onsubmit="return login(event)">' +
|
||||
'<input class="input-block" type="text" placeholder="Namey" id="username"/>' +
|
||||
'<input class="input-block" type="password" placeholder="Passywassy" id="password"/>' +
|
||||
'</form>'
|
||||
loginForm,
|
||||
[cancelBtn, loginBtn]
|
||||
);
|
||||
}
|
||||
// Function to login
|
||||
|
@ -29,16 +58,13 @@ function login(event) {
|
|||
formData.append("username", formUsername);
|
||||
formData.append("password", formPassword);
|
||||
|
||||
$.ajax({
|
||||
url: '/auth/login',
|
||||
type: 'post',
|
||||
data: formData,
|
||||
contentType: false,
|
||||
processData: false,
|
||||
success: function (response) {
|
||||
fetch('/auth/login', {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
}).then(response => {
|
||||
if (response.status === 200) {
|
||||
location.reload();
|
||||
},
|
||||
error: function (response) {
|
||||
} else {
|
||||
switch (response.status) {
|
||||
case 500:
|
||||
addNotification('Server exploded, F\'s in chat', 2);
|
||||
|
@ -51,25 +77,68 @@ function login(event) {
|
|||
break;
|
||||
}
|
||||
}
|
||||
}).catch(error => {
|
||||
addNotification('Error logging in, blame someone', 2);
|
||||
});
|
||||
}
|
||||
// Function to show register
|
||||
function showRegister() {
|
||||
// Create buttons
|
||||
cancelBtn = document.createElement('button');
|
||||
cancelBtn.classList.add('btn-block');
|
||||
cancelBtn.innerHTML = 'nuuuuuuuu';
|
||||
cancelBtn.onclick = popupDissmiss;
|
||||
|
||||
registerBtn = document.createElement('button');
|
||||
registerBtn.classList.add('btn-block');
|
||||
registerBtn.classList.add('primary');
|
||||
registerBtn.innerHTML = 'Register';
|
||||
registerBtn.type = 'submit';
|
||||
registerBtn.setAttribute('form', 'registerForm');
|
||||
|
||||
// Create form
|
||||
registerForm = document.createElement('form');
|
||||
registerForm.id = 'registerForm';
|
||||
registerForm.setAttribute('onsubmit', 'return register(event);');
|
||||
|
||||
usernameInput = document.createElement('input');
|
||||
usernameInput.classList.add('input-block');
|
||||
usernameInput.type = 'text';
|
||||
usernameInput.placeholder = 'Namey';
|
||||
usernameInput.id = 'username';
|
||||
|
||||
emailInput = document.createElement('input');
|
||||
emailInput.classList.add('input-block');
|
||||
emailInput.type = 'text';
|
||||
emailInput.placeholder = 'E mail!';
|
||||
emailInput.id = 'email';
|
||||
|
||||
passwordInput = document.createElement('input');
|
||||
passwordInput.classList.add('input-block');
|
||||
passwordInput.type = 'password';
|
||||
passwordInput.placeholder = 'Passywassy';
|
||||
passwordInput.id = 'password';
|
||||
|
||||
passwordInputRepeat = document.createElement('input');
|
||||
passwordInputRepeat.classList.add('input-block');
|
||||
passwordInputRepeat.type = 'password';
|
||||
passwordInputRepeat.placeholder = 'Passywassy again!';
|
||||
passwordInputRepeat.id = 'password-repeat';
|
||||
|
||||
registerForm.appendChild(usernameInput);
|
||||
registerForm.appendChild(emailInput);
|
||||
registerForm.appendChild(passwordInput);
|
||||
registerForm.appendChild(passwordInputRepeat);
|
||||
|
||||
popUpShow(
|
||||
'Who are you?',
|
||||
'Already have an account? <span class="link" onclick="showLogin()">Login!</span>',
|
||||
'<button class="btn-block" onclick="popupDissmiss()">Canceleee</button>\
|
||||
<button class="btn-block primary" form="registerForm" type="submit">Register</button>',
|
||||
'<form id="registerForm" onsubmit="return register(event)">\
|
||||
<input class="input-block" type="text" placeholder="Namey" id="username"/>\
|
||||
<input class="input-block" type="text" placeholder="E mail!" id="email"/>\
|
||||
<input class="input-block" type="password" placeholder="Passywassy" id="password"/>\
|
||||
<input class="input-block" type="password" placeholder="Passywassy again!" id="password-repeat"/>\
|
||||
</form>'
|
||||
registerForm,
|
||||
[cancelBtn, registerBtn]
|
||||
);
|
||||
}
|
||||
// Function to register
|
||||
function register(obj) {
|
||||
function register(event) {
|
||||
// AJAX takes control of subby form
|
||||
event.preventDefault();
|
||||
|
||||
|
@ -90,13 +159,12 @@ function register(obj) {
|
|||
formData.append("password", formPassword);
|
||||
formData.append("password-repeat", formPasswordRepeat);
|
||||
|
||||
$.ajax({
|
||||
url: '/auth/register',
|
||||
type: 'post',
|
||||
data: formData,
|
||||
contentType: false,
|
||||
processData: false,
|
||||
success: function (response) {
|
||||
// Send form to server
|
||||
fetch('/auth/login', {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
}).then(response => {
|
||||
if (response.status === 200) {
|
||||
if (response === "gwa gwa") {
|
||||
addNotification('Registered successfully! Now please login to continue', 1);
|
||||
showLogin();
|
||||
|
@ -105,19 +173,20 @@ function register(obj) {
|
|||
addNotification(response[i], 2);
|
||||
}
|
||||
}
|
||||
},
|
||||
error: function (response) {
|
||||
} else {
|
||||
switch (response.status) {
|
||||
case 500:
|
||||
addNotification('Server exploded, F\'s in chat', 2);
|
||||
break;
|
||||
case 403:
|
||||
addNotification('None but devils play past here...', 2);
|
||||
addNotification('None but devils play past here... Wrong information', 2);
|
||||
break;
|
||||
default:
|
||||
addNotification('Error logging in, blame someone', 2);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}).catch(error => {
|
||||
addNotification('Error logging in, blame someone', 2);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -2,34 +2,6 @@
|
|||
function imgFade(obj, time = 250) {
|
||||
$(obj).animate({ opacity: 1 }, time);
|
||||
}
|
||||
// https://stackoverflow.com/questions/3942878/how-to-decide-font-color-in-white-or-black-depending-on-background-color
|
||||
function colourContrast(bgColor, lightColor, darkColor, threshold = 0.179) {
|
||||
// if color is in hex format then convert to rgb else parese rgb
|
||||
let r = 0
|
||||
let g = 0
|
||||
let b = 0
|
||||
if (bgColor.charAt(0) === '#') {
|
||||
const color = (bgColor.charAt(0) === '#') ? bgColor.substring(1, 7) : bgColor;
|
||||
r = parseInt(color.substring(0, 2), 16); // hexToR
|
||||
g = parseInt(color.substring(2, 4), 16); // hexToG
|
||||
b = parseInt(color.substring(4, 6), 16); // hexToB
|
||||
} else {
|
||||
const color = bgColor.replace('rgb(', '').replace(')', '').split(',');
|
||||
r = color[0];
|
||||
g = color[1];
|
||||
b = color[2];
|
||||
}
|
||||
|
||||
const uicolors = [r / 255, g / 255, b / 255];
|
||||
const c = uicolors.map((col) => {
|
||||
if (col <= 0.03928) {
|
||||
return col / 12.92;
|
||||
}
|
||||
return Math.pow((col + 0.055) / 1.055, 2.4);
|
||||
});
|
||||
const L = (0.2126 * c[0]) + (0.7152 * c[1]) + (0.0722 * c[2]);
|
||||
return (L > threshold) ? darkColor : lightColor;
|
||||
}
|
||||
// Lazy load images when they are in view
|
||||
function loadOnView() {
|
||||
let lazyLoad = document.querySelectorAll('#lazy-load');
|
||||
|
@ -38,7 +10,7 @@ function loadOnView() {
|
|||
let image = lazyLoad[i];
|
||||
if (image.getBoundingClientRect().top < window.innerHeight && image.getBoundingClientRect().bottom > 0) {
|
||||
if (!image.src) {
|
||||
image.src = `/api/file/${image.getAttribute('data-src')}?r=thumb`
|
||||
image.src = `/api/file/${image.getAttribute('data-src')}?r=thumb` // e=webp
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -47,14 +19,6 @@ function loadOnView() {
|
|||
window.onload = function () {
|
||||
loadOnView();
|
||||
|
||||
const darkColor = '#151515';
|
||||
const lightColor = '#E8E3E3';
|
||||
let contrastCheck = document.querySelectorAll('#contrast-check');
|
||||
for (let i = 0; i < contrastCheck.length; i++) {
|
||||
let bgColor = contrastCheck[i].getAttribute('data-color');
|
||||
contrastCheck[i].style.color = colourContrast(bgColor, lightColor, darkColor);
|
||||
}
|
||||
|
||||
let times = document.querySelectorAll('.time');
|
||||
for (let i = 0; i < times.length; i++) {
|
||||
// Remove milliseconds
|
||||
|
@ -88,16 +52,20 @@ window.onload = function () {
|
|||
|
||||
// Info button
|
||||
let infoButton = document.querySelector('.info-button');
|
||||
if (document.body.scrollTop > 300 || document.documentElement.scrollTop > 20) {
|
||||
infoButton.classList.remove('show');
|
||||
} else {
|
||||
infoButton.classList.add('show');
|
||||
}
|
||||
infoButton.onclick = function () {
|
||||
popUpShow('OnlyLegs Gallery',
|
||||
'Using <a href="https://phosphoricons.com/">Phosphoricons</a> and <a href="https://www.gent.media/manrope">Manrope</a> <br>' +
|
||||
'Made by Fluffy and others with ❤️ <br>' +
|
||||
'<a href="https://github.com/Fluffy-Bean/onlylegs">V23.03.23</a>');
|
||||
|
||||
if (infoButton) {
|
||||
if (document.body.scrollTop > 300 || document.documentElement.scrollTop > 20) {
|
||||
infoButton.classList.remove('show');
|
||||
} else {
|
||||
infoButton.classList.add('show');
|
||||
}
|
||||
infoButton.onclick = function () {
|
||||
popUpShow('OnlyLegs on Flask',
|
||||
'Using <a href="https://phosphoricons.com/">Phosphoricons</a> and ' +
|
||||
'<a href="https://www.gent.media/manrope">Manrope</a> <br>' +
|
||||
'Made by Fluffy and others with ❤️ <br>' +
|
||||
'<a href="https://github.com/Fluffy-Bean/onlylegs">V23.03.30</a>');
|
||||
}
|
||||
}
|
||||
};
|
||||
window.onscroll = function () {
|
||||
|
@ -113,10 +81,13 @@ window.onscroll = function () {
|
|||
|
||||
// Info button
|
||||
let infoButton = document.querySelector('.info-button');
|
||||
if (document.body.scrollTop > 300 || document.documentElement.scrollTop > 20) {
|
||||
infoButton.classList.remove('show');
|
||||
} else {
|
||||
infoButton.classList.add('show');
|
||||
|
||||
if (infoButton) {
|
||||
if (document.body.scrollTop > 300 || document.documentElement.scrollTop > 20) {
|
||||
infoButton.classList.remove('show');
|
||||
} else {
|
||||
infoButton.classList.add('show');
|
||||
}
|
||||
}
|
||||
};
|
||||
window.onresize = function () {
|
||||
|
|
|
@ -1,67 +1,69 @@
|
|||
function addNotification(text='Sample notification', type=4) {
|
||||
let container = document.querySelector('.notifications');
|
||||
function addNotification(notificationText, notificationLevel) {
|
||||
let notificationContainer = document.querySelector('.notifications');
|
||||
|
||||
// Set the different icons for the different notification levels
|
||||
let successIcon = '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M229.66,77.66l-128,128a8,8,0,0,1-11.32,0l-56-56a8,8,0,0,1,11.32-11.32L96,188.69,218.34,66.34a8,8,0,0,1,11.32,11.32Z"></path></svg>';
|
||||
let criticalIcon = '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M236.8,188.09,149.35,36.22h0a24.76,24.76,0,0,0-42.7,0L19.2,188.09a23.51,23.51,0,0,0,0,23.72A24.35,24.35,0,0,0,40.55,224h174.9a24.35,24.35,0,0,0,21.33-12.19A23.51,23.51,0,0,0,236.8,188.09ZM222.93,203.8a8.5,8.5,0,0,1-7.48,4.2H40.55a8.5,8.5,0,0,1-7.48-4.2,7.59,7.59,0,0,1,0-7.72L120.52,44.21a8.75,8.75,0,0,1,15,0l87.45,151.87A7.59,7.59,0,0,1,222.93,203.8ZM120,144V104a8,8,0,0,1,16,0v40a8,8,0,0,1-16,0Zm20,36a12,12,0,1,1-12-12A12,12,0,0,1,140,180Z"></path></svg>';
|
||||
let warningIcon = '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M128,24A104,104,0,1,0,232,128,104.11,104.11,0,0,0,128,24Zm0,192a88,88,0,1,1,88-88A88.1,88.1,0,0,1,128,216Zm16-40a8,8,0,0,1-8,8,16,16,0,0,1-16-16V128a8,8,0,0,1,0-16,16,16,0,0,1,16,16v40A8,8,0,0,1,144,176ZM112,84a12,12,0,1,1,12,12A12,12,0,0,1,112,84Z"></path></svg>';
|
||||
let infoIcon = '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M128,24A104,104,0,1,0,232,128,104.11,104.11,0,0,0,128,24Zm0,192a88,88,0,1,1,88-88A88.1,88.1,0,0,1,128,216Zm16-40a8,8,0,0,1-8,8,16,16,0,0,1-16-16V128a8,8,0,0,1,0-16,16,16,0,0,1,16,16v40A8,8,0,0,1,144,176ZM112,84a12,12,0,1,1,12,12A12,12,0,0,1,112,84Z"></path></svg>';
|
||||
|
||||
// Create notification element
|
||||
let div = document.createElement('div');
|
||||
div.classList.add('sniffle__notification');
|
||||
div.onclick = function() {
|
||||
if (div.parentNode) {
|
||||
div.classList.add('sniffle__notification--hide');
|
||||
let notification = document.createElement('div');
|
||||
notification.classList.add('sniffle__notification');
|
||||
notification.onclick = function() {
|
||||
if (notification) {
|
||||
notification.classList.add('hide');
|
||||
|
||||
setTimeout(function() {
|
||||
container.removeChild(div);
|
||||
notificationContainer.removeChild(notification);
|
||||
}, 500);
|
||||
}
|
||||
};
|
||||
|
||||
// Create icon element and append to notification
|
||||
let icon = document.createElement('span');
|
||||
icon.classList.add('sniffle__notification-icon');
|
||||
switch (type) {
|
||||
case 1:
|
||||
div.classList.add('sniffle__notification--success');
|
||||
icon.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M237.66,85.26l-128.4,128.4a8,8,0,0,1-11.32,0l-71.6-72a8,8,0,0,1,0-11.31l24-24a8,8,0,0,1,11.32,0l36.68,35.32a8,8,0,0,0,11.32,0l92.68-91.32a8,8,0,0,1,11.32,0l24,23.6A8,8,0,0,1,237.66,85.26Z" opacity="0.2"></path><path d="M243.28,68.24l-24-23.56a16,16,0,0,0-22.58,0L104,136h0l-.11-.11L67.25,100.62a16,16,0,0,0-22.57.06l-24,24a16,16,0,0,0,0,22.61l71.62,72a16,16,0,0,0,22.63,0L243.33,90.91A16,16,0,0,0,243.28,68.24ZM103.62,208,32,136l24-24,.11.11,36.64,35.27a16,16,0,0,0,22.52,0L208.06,56,232,79.6Z"></path></svg>';
|
||||
break;
|
||||
case 2:
|
||||
div.classList.add('sniffle__notification--error');
|
||||
icon.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M215.46,216H40.54C27.92,216,20,202.79,26.13,192.09L113.59,40.22c6.3-11,22.52-11,28.82,0l87.46,151.87C236,202.79,228.08,216,215.46,216Z" opacity="0.2"></path><path d="M236.8,188.09,149.35,36.22h0a24.76,24.76,0,0,0-42.7,0L19.2,188.09a23.51,23.51,0,0,0,0,23.72A24.35,24.35,0,0,0,40.55,224h174.9a24.35,24.35,0,0,0,21.33-12.19A23.51,23.51,0,0,0,236.8,188.09ZM222.93,203.8a8.5,8.5,0,0,1-7.48,4.2H40.55a8.5,8.5,0,0,1-7.48-4.2,7.59,7.59,0,0,1,0-7.72L120.52,44.21a8.75,8.75,0,0,1,15,0l87.45,151.87A7.59,7.59,0,0,1,222.93,203.8ZM120,144V104a8,8,0,0,1,16,0v40a8,8,0,0,1-16,0Zm20,36a12,12,0,1,1-12-12A12,12,0,0,1,140,180Z"></path></svg>';
|
||||
break;
|
||||
case 3:
|
||||
div.classList.add('sniffle__notification--warning');
|
||||
icon.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M215.46,216H40.54C27.92,216,20,202.79,26.13,192.09L113.59,40.22c6.3-11,22.52-11,28.82,0l87.46,151.87C236,202.79,228.08,216,215.46,216Z" opacity="0.2"></path><path d="M236.8,188.09,149.35,36.22h0a24.76,24.76,0,0,0-42.7,0L19.2,188.09a23.51,23.51,0,0,0,0,23.72A24.35,24.35,0,0,0,40.55,224h174.9a24.35,24.35,0,0,0,21.33-12.19A23.51,23.51,0,0,0,236.8,188.09ZM222.93,203.8a8.5,8.5,0,0,1-7.48,4.2H40.55a8.5,8.5,0,0,1-7.48-4.2,7.59,7.59,0,0,1,0-7.72L120.52,44.21a8.75,8.75,0,0,1,15,0l87.45,151.87A7.59,7.59,0,0,1,222.93,203.8ZM120,144V104a8,8,0,0,1,16,0v40a8,8,0,0,1-16,0Zm20,36a12,12,0,1,1-12-12A12,12,0,0,1,140,180Z"></path></svg>';
|
||||
break;
|
||||
default:
|
||||
div.classList.add('sniffle__notification--info');
|
||||
icon.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M224,128a96,96,0,1,1-96-96A96,96,0,0,1,224,128Z" opacity="0.2"></path><path d="M144,176a8,8,0,0,1-8,8,16,16,0,0,1-16-16V128a8,8,0,0,1,0-16,16,16,0,0,1,16,16v40A8,8,0,0,1,144,176Zm88-48A104,104,0,1,1,128,24,104.11,104.11,0,0,1,232,128Zm-16,0a88,88,0,1,0-88,88A88.1,88.1,0,0,0,216,128ZM124,96a12,12,0,1,0-12-12A12,12,0,0,0,124,96Z"></path></svg>';
|
||||
break;
|
||||
let iconElement = document.createElement('span');
|
||||
iconElement.classList.add('sniffle__notification-icon');
|
||||
notification.appendChild(iconElement);
|
||||
// Set the icon based on the notification level, not pretty but it works :3
|
||||
if (notificationLevel == 1) {
|
||||
notification.classList.add('success');
|
||||
iconElement.innerHTML = successIcon;
|
||||
} else if (notificationLevel == 2) {
|
||||
notification.classList.add('critical');
|
||||
iconElement.innerHTML = criticalIcon;
|
||||
} else if (notificationLevel == 3) {
|
||||
notification.classList.add('warning');
|
||||
iconElement.innerHTML = warningIcon;
|
||||
} else {
|
||||
notification.classList.add('info');
|
||||
iconElement.innerHTML = infoIcon;
|
||||
}
|
||||
div.appendChild(icon);
|
||||
|
||||
// Create text element and append to notification
|
||||
let description = document.createElement('span');
|
||||
description.classList.add('sniffle__notification-text');
|
||||
description.innerHTML = text;
|
||||
div.appendChild(description);
|
||||
description.innerHTML = notificationText;
|
||||
notification.appendChild(description);
|
||||
|
||||
// Create span to show time remaining
|
||||
let timer = document.createElement('span');
|
||||
timer.classList.add('sniffle__notification-time');
|
||||
div.appendChild(timer);
|
||||
notification.appendChild(timer);
|
||||
|
||||
// Append notification to container
|
||||
container.appendChild(div);
|
||||
setTimeout(function() {
|
||||
div.classList.add('sniffle__notification-show');
|
||||
}, 100);
|
||||
notificationContainer.appendChild(notification);
|
||||
setTimeout(function() { notification.classList.add('show'); }, 5);
|
||||
|
||||
// Remove notification after 5 seconds
|
||||
setTimeout(function() {
|
||||
if (div.parentNode) {
|
||||
div.classList.add('sniffle__notification--hide');
|
||||
if (notification) {
|
||||
notification.classList.add('hide');
|
||||
|
||||
setTimeout(function() {
|
||||
container.removeChild(div);
|
||||
notificationContainer.removeChild(notification);
|
||||
}, 500);
|
||||
}
|
||||
}, 5000);
|
||||
}
|
||||
}
|
||||
|
||||
// uwu
|
||||
|
|
|
@ -1,42 +1,47 @@
|
|||
function popUpShow(title, body, actions='<button class="btn-block" onclick="popupDissmiss()">Close</button>', content='') {
|
||||
// Stop scrolling
|
||||
document.querySelector("html").style.overflow = "hidden";
|
||||
|
||||
function popUpShow(titleText, subtitleText, bodyContent=null, userActions=null) {
|
||||
// Get popup elements
|
||||
let popup = document.querySelector('.pop-up');
|
||||
let popupContent = document.querySelector('.pop-up-content');
|
||||
let popupActions = document.querySelector('.pop-up-controlls');
|
||||
|
||||
// Set popup content
|
||||
popupContent.innerHTML = `<h3>${title}</h3><p>${body}</p>${content}`;
|
||||
let popupSelector = document.querySelector('.pop-up');
|
||||
let headerSelector = document.querySelector('.pop-up-header');
|
||||
let actionsSelector = document.querySelector('.pop-up-controlls');
|
||||
|
||||
// Clear popup elements
|
||||
headerSelector.innerHTML = '';
|
||||
actionsSelector.innerHTML = '';
|
||||
|
||||
// Set popup header and subtitle
|
||||
let titleElement = document.createElement('h2');
|
||||
titleElement.innerHTML = titleText;
|
||||
headerSelector.appendChild(titleElement);
|
||||
|
||||
let subtitleElement = document.createElement('p');
|
||||
subtitleElement.innerHTML = subtitleText;
|
||||
headerSelector.appendChild(subtitleElement);
|
||||
|
||||
if (bodyContent) {
|
||||
headerSelector.appendChild(bodyContent);
|
||||
}
|
||||
|
||||
// Set buttons that will be displayed
|
||||
popupActions.innerHTML = actions;
|
||||
if (userActions) {
|
||||
// for each user action, add the element
|
||||
for (let i = 0; i < userActions.length; i++) {
|
||||
let action = userActions[i];
|
||||
actionsSelector.appendChild(action);
|
||||
}
|
||||
} else {
|
||||
actionsSelector.innerHTML = '<button class="btn-block" onclick="popupDissmiss()">Close</button>';
|
||||
}
|
||||
|
||||
// Show popup
|
||||
popup.style.display = 'block';
|
||||
setTimeout(function() {
|
||||
popup.classList.add('active')
|
||||
}, 10);
|
||||
// Stop scrolling and show popup
|
||||
document.querySelector("html").style.overflow = "hidden";
|
||||
popupSelector.style.display = 'block';
|
||||
setTimeout(function() { popupSelector.classList.add('active') }, 5); // 2ms delay to allow for css transition >:C
|
||||
}
|
||||
|
||||
function popupDissmiss() {
|
||||
// un-Stop scrolling
|
||||
let popupSelector = document.querySelector('.pop-up');
|
||||
|
||||
document.querySelector("html").style.overflow = "auto";
|
||||
|
||||
let popup = document.querySelector('.pop-up');
|
||||
|
||||
popup.classList.remove('active');
|
||||
|
||||
setTimeout(function() {
|
||||
popup.style.display = 'none';
|
||||
}, 200);
|
||||
popupSelector.classList.remove('active');
|
||||
setTimeout(function() { popupSelector.style.display = 'none'; }, 200);
|
||||
}
|
||||
|
||||
document.addEventListener('keydown', function(event) {
|
||||
if (event.key === 'Escape') {
|
||||
if (document.querySelector('.pop-up').classList.contains('active')) {
|
||||
popupDissmiss();
|
||||
}
|
||||
}
|
||||
});
|
|
@ -1,196 +1,281 @@
|
|||
window.addEventListener("dragover",(event) => {
|
||||
event.preventDefault();
|
||||
},false);
|
||||
window.addEventListener("drop",(event) => {
|
||||
event.preventDefault();
|
||||
},false);
|
||||
// Remove default events on file drop, otherwise the browser will open the file
|
||||
window.addEventListener("dragover", (event) => {
|
||||
event.preventDefault();
|
||||
}, false);
|
||||
window.addEventListener("drop", (event) => {
|
||||
event.preventDefault();
|
||||
}, false);
|
||||
|
||||
function fileChanged(obj) {
|
||||
document.querySelector('.fileDrop-block').classList.remove('error');
|
||||
|
||||
if ($(obj).val() === '') {
|
||||
document.querySelector('.fileDrop-block').classList.remove('active');
|
||||
document.querySelector('.fileDrop-block .status').innerHTML = 'Choose or Drop file';
|
||||
} else {
|
||||
document.querySelector('.fileDrop-block').classList.add('active');
|
||||
document.querySelector('.fileDrop-block .status').innerHTML = obj.files[0].name;
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Function to upload images
|
||||
const uploadForm = document.querySelector('#uploadForm');
|
||||
const fileDrop = document.querySelector('.fileDrop-block');
|
||||
const fileDropTitle = fileDrop.querySelector('.status');
|
||||
const fileUpload = uploadForm.querySelector('#file');
|
||||
|
||||
|
||||
$(fileUpload).val('');
|
||||
|
||||
|
||||
// Choose or drop file button
|
||||
['dragover', 'dragenter'].forEach(eventName => {
|
||||
fileDrop.addEventListener(eventName, fileActivate, false);
|
||||
});
|
||||
['dragleave', 'drop'].forEach(eventName => {
|
||||
fileDrop.addEventListener(eventName, fileDefault, false);
|
||||
})
|
||||
|
||||
// Drop file into box
|
||||
fileDrop.addEventListener('drop', fileDropHandle, false);
|
||||
|
||||
|
||||
// Edging the file plunge :3
|
||||
function fileActivate(event) {
|
||||
fileDrop.classList.remove('error');
|
||||
fileDrop.classList.add('edging');
|
||||
fileDropTitle.innerHTML = 'Drop to upload!';
|
||||
}
|
||||
function fileDefault(event) {
|
||||
fileDrop.classList.remove('error');
|
||||
fileDrop.classList.remove('edging');
|
||||
fileDropTitle.innerHTML = 'Choose or Drop file';
|
||||
}
|
||||
|
||||
function fileDropHandle(event) {
|
||||
event.preventDefault()
|
||||
fileUpload.files = event.dataTransfer.files;
|
||||
|
||||
fileDropTitle.innerHTML = fileUpload.files[0].name;
|
||||
fileDrop.classList.add('active');
|
||||
}
|
||||
|
||||
|
||||
uploadForm.addEventListener('submit', (event) => {
|
||||
// AJAX takes control of subby form
|
||||
event.preventDefault()
|
||||
|
||||
const jobList = document.querySelector(".upload-jobs");
|
||||
|
||||
// Check for empty upload
|
||||
if ($(fileUpload).val() === '') {
|
||||
fileDrop.classList.add('error');
|
||||
fileDropTitle.innerHTML = 'No file selected!';
|
||||
} else {
|
||||
// Make form
|
||||
let formData = new FormData();
|
||||
formData.append("file", $("#file").prop("files")[0]);
|
||||
formData.append("alt", $("#alt").val());
|
||||
formData.append("description", $("#description").val());
|
||||
formData.append("tags", $("#tags").val());
|
||||
formData.append("submit", $("#submit").val());
|
||||
|
||||
// Upload the information
|
||||
$.ajax({
|
||||
url: '/api/upload',
|
||||
type: 'post',
|
||||
data: formData,
|
||||
contentType: false,
|
||||
processData: false,
|
||||
beforeSend: function () {
|
||||
jobContainer = document.createElement("div");
|
||||
jobContainer.classList.add("job");
|
||||
|
||||
jobStatus = document.createElement("span");
|
||||
jobStatus.classList.add("job__status");
|
||||
jobStatus.innerHTML = "Uploading...";
|
||||
|
||||
jobProgress = document.createElement("span");
|
||||
jobProgress.classList.add("progress");
|
||||
|
||||
jobImg = document.createElement("img");
|
||||
jobImg.src = URL.createObjectURL($("#file").prop("files")[0]);
|
||||
|
||||
jobImgFilter = document.createElement("span");
|
||||
jobImgFilter.classList.add("img-filter");
|
||||
|
||||
jobContainer.appendChild(jobStatus);
|
||||
jobContainer.appendChild(jobProgress);
|
||||
jobContainer.appendChild(jobImg);
|
||||
jobContainer.appendChild(jobImgFilter);
|
||||
jobList.appendChild(jobContainer);
|
||||
},
|
||||
success: function (response) {
|
||||
jobContainer.classList.add("success");
|
||||
jobStatus.innerHTML = "Uploaded!";
|
||||
if (!document.querySelector(".upload-panel").classList.contains("open")) {
|
||||
addNotification("Image uploaded successfully", 1);
|
||||
}
|
||||
},
|
||||
error: function (response) {
|
||||
jobContainer.classList.add("critical");
|
||||
switch (response.status) {
|
||||
case 500:
|
||||
jobStatus.innerHTML = "Server exploded, F's in chat";
|
||||
break;
|
||||
case 400:
|
||||
case 404:
|
||||
jobStatus.innerHTML = "Error uploading. Blame yourself";
|
||||
break;
|
||||
case 403:
|
||||
jobStatus.innerHTML = "None but devils play past here...";
|
||||
break;
|
||||
case 413:
|
||||
jobStatus.innerHTML = "File too large!!!!!!";
|
||||
break;
|
||||
default:
|
||||
jobStatus.innerHTML = "Error uploading file, blame someone";
|
||||
break;
|
||||
}
|
||||
if (!document.querySelector(".upload-panel").classList.contains("open")) {
|
||||
addNotification("Error uploading file", 2);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
// Empty values
|
||||
$(fileUpload).val('');
|
||||
$("#alt").val('');
|
||||
$("#description").val('');
|
||||
$("#tags").val('');
|
||||
|
||||
// Reset drop
|
||||
fileDrop.classList.remove('active');
|
||||
fileDropTitle.innerHTML = 'Choose or Drop file';
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// open upload tab
|
||||
function openUploadTab() {
|
||||
// Stop scrolling
|
||||
let uploadTab = document.querySelector(".upload-panel");
|
||||
|
||||
// Stop scrolling and open upload tab
|
||||
document.querySelector("html").style.overflow = "hidden";
|
||||
document.querySelector(".content").tabIndex = "-1";
|
||||
|
||||
// Open upload tab
|
||||
const uploadTab = document.querySelector(".upload-panel");
|
||||
uploadTab.style.display = "block";
|
||||
|
||||
setTimeout(function () {
|
||||
uploadTab.classList.add("open");
|
||||
}, 10);
|
||||
setTimeout(function () { uploadTab.classList.add("open"); }, 5);
|
||||
}
|
||||
|
||||
// close upload tab
|
||||
function closeUploadTab() {
|
||||
// un-Stop scrolling
|
||||
let uploadTab = document.querySelector(".upload-panel");
|
||||
let uploadTabContainer = document.querySelector(".upload-panel .container");
|
||||
|
||||
// un-Stop scrolling and close upload tab
|
||||
document.querySelector("html").style.overflow = "auto";
|
||||
document.querySelector(".content").tabIndex = "";
|
||||
|
||||
// Close upload tab
|
||||
const uploadTab = document.querySelector(".upload-panel");
|
||||
uploadTab.classList.remove("open");
|
||||
|
||||
setTimeout(function () {
|
||||
uploadTab.style.display = "none";
|
||||
|
||||
uploadTabContainer.style.transform = "";
|
||||
uploadTab.dataset.lastY = 0;
|
||||
}, 250);
|
||||
}
|
||||
|
||||
// toggle upload tab
|
||||
function toggleUploadTab() {
|
||||
if (document.querySelector(".upload-panel").classList.contains("open")) {
|
||||
let uploadTab = document.querySelector(".upload-panel");
|
||||
|
||||
if (uploadTab.classList.contains("open")) {
|
||||
closeUploadTab();
|
||||
} else {
|
||||
openUploadTab();
|
||||
}
|
||||
}
|
||||
|
||||
function tabDragStart(event) {
|
||||
event.preventDefault();
|
||||
|
||||
let uploadTab = document.querySelector(".upload-panel .container");
|
||||
let offset = uploadTab.getBoundingClientRect().y;
|
||||
|
||||
uploadTab.classList.add("dragging");
|
||||
|
||||
document.addEventListener('touchmove', event => {
|
||||
if (uploadTab.classList.contains("dragging")) {
|
||||
if (event.touches[0].clientY - offset >= 0) {
|
||||
uploadTab.dataset.lastY = event.touches[0].clientY;
|
||||
} else {
|
||||
uploadTab.dataset.lastY = offset;
|
||||
}
|
||||
|
||||
uploadTab.style.transform = `translateY(${uploadTab.dataset.lastY - offset}px)`;
|
||||
}
|
||||
});
|
||||
}
|
||||
function tabDragStopped(event) {
|
||||
event.preventDefault();
|
||||
|
||||
let uploadTab = document.querySelector(".upload-panel .container");
|
||||
|
||||
uploadTab.classList.remove("dragging");
|
||||
|
||||
if (uploadTab.dataset.lastY > (screen.height * 0.3)) {
|
||||
closeUploadTab();
|
||||
} else {
|
||||
uploadTab.style.transition = "transform 0.25s cubic-bezier(0.76, 0, 0.17, 1)";
|
||||
uploadTab.style.transform = "translateY(0px)";
|
||||
setTimeout(function () { uploadTab.style.transition = ""; }, 250);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Edging the file plunge :3
|
||||
function fileActivate(event) {
|
||||
event.preventDefault()
|
||||
|
||||
let fileDrop = document.querySelector('.fileDrop-block');
|
||||
let fileDropTitle = fileDrop.querySelector('.status');
|
||||
|
||||
fileDrop.classList.remove('error');
|
||||
fileDrop.classList.add('edging');
|
||||
fileDropTitle.innerHTML = 'Drop to upload!';
|
||||
}
|
||||
function fileDefault() {
|
||||
let fileDrop = document.querySelector('.fileDrop-block');
|
||||
let fileDropTitle = fileDrop.querySelector('.status');
|
||||
|
||||
fileDrop.classList.remove('error');
|
||||
fileDrop.classList.remove('edging');
|
||||
fileDropTitle.innerHTML = 'Choose or Drop file';
|
||||
}
|
||||
|
||||
function fileDropHandle(event) {
|
||||
event.preventDefault()
|
||||
|
||||
let fileDrop = document.querySelector('.fileDrop-block');
|
||||
let fileUpload = fileDrop.querySelector('#file');
|
||||
|
||||
fileUpload.files = event.dataTransfer.files;
|
||||
|
||||
fileDefault();
|
||||
fileChanged();
|
||||
}
|
||||
|
||||
function fileChanged() {
|
||||
let dropBlock = document.querySelector('.fileDrop-block');
|
||||
let dropBlockStatus = dropBlock.querySelector('.status');
|
||||
let dropBlockInput = dropBlock.querySelector('#file');
|
||||
|
||||
if (dropBlockInput.value !== "") {
|
||||
dropBlock.classList.add('active');
|
||||
dropBlockStatus.innerHTML = dropBlockInput.files[0].name;
|
||||
} else {
|
||||
fileDefault();
|
||||
}
|
||||
}
|
||||
|
||||
function clearUpload() {
|
||||
let fileDrop = document.querySelector('#uploadForm');
|
||||
|
||||
let fileUpload = fileDrop.querySelector('#file');
|
||||
let fileAlt = fileDrop.querySelector('#alt');
|
||||
let fileDescription = fileDrop.querySelector('#description');
|
||||
let fileTags = fileDrop.querySelector('#tags');
|
||||
|
||||
fileUpload.value = "";
|
||||
fileAlt.value = "";
|
||||
fileDescription.value = "";
|
||||
fileTags.value = "";
|
||||
}
|
||||
|
||||
|
||||
function createJob(file) {
|
||||
jobContainer = document.createElement("div");
|
||||
jobContainer.classList.add("job");
|
||||
|
||||
jobStatus = document.createElement("span");
|
||||
jobStatus.classList.add("job__status");
|
||||
jobStatus.innerHTML = "Uploading...";
|
||||
|
||||
jobProgress = document.createElement("span");
|
||||
jobProgress.classList.add("progress");
|
||||
|
||||
jobImg = document.createElement("img");
|
||||
jobImg.src = URL.createObjectURL(file);
|
||||
|
||||
jobImgFilter = document.createElement("span");
|
||||
jobImgFilter.classList.add("img-filter");
|
||||
|
||||
jobContainer.appendChild(jobStatus);
|
||||
jobContainer.appendChild(jobProgress);
|
||||
jobContainer.appendChild(jobImg);
|
||||
jobContainer.appendChild(jobImgFilter);
|
||||
|
||||
return jobContainer;
|
||||
}
|
||||
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Function to upload images
|
||||
let uploadTab = document.querySelector(".upload-panel");
|
||||
let uploadTabDrag = uploadTab.querySelector("#dragIndicator");
|
||||
let uploadForm = uploadTab.querySelector('#uploadForm');
|
||||
let jobList = document.querySelector(".upload-jobs");
|
||||
|
||||
let fileDrop = uploadForm.querySelector('.fileDrop-block');
|
||||
let fileDropTitle = fileDrop.querySelector('.status');
|
||||
let fileUpload = fileDrop.querySelector('#file');
|
||||
|
||||
let fileAlt = uploadForm.querySelector('#alt');
|
||||
let fileDescription = uploadForm.querySelector('#description');
|
||||
let fileTags = uploadForm.querySelector('#tags');
|
||||
|
||||
|
||||
clearUpload();
|
||||
fileDefault();
|
||||
|
||||
|
||||
// Tab is dragged
|
||||
uploadTabDrag.addEventListener('touchstart', tabDragStart, false);
|
||||
uploadTabDrag.addEventListener('touchend', tabDragStopped, false);
|
||||
|
||||
// Drag over/enter event
|
||||
fileDrop.addEventListener('dragover', fileActivate, false);
|
||||
fileDrop.addEventListener('dragenter', fileActivate, false);
|
||||
// Drag out
|
||||
fileDrop.addEventListener('dragleave', fileDefault, false);
|
||||
// Drop file into box
|
||||
fileDrop.addEventListener('drop', fileDropHandle, false);
|
||||
|
||||
// File upload change
|
||||
fileUpload.addEventListener('change', fileChanged, false);
|
||||
// File upload clicked
|
||||
fileUpload.addEventListener('click', fileDefault, false);
|
||||
|
||||
|
||||
// Submit form
|
||||
uploadForm.addEventListener('submit', (event) => {
|
||||
// AJAX takes control of subby form
|
||||
event.preventDefault()
|
||||
|
||||
if (fileUpload.value === "") {
|
||||
fileDrop.classList.add('error');
|
||||
fileDropTitle.innerHTML = 'No file selected!';
|
||||
// Stop the function
|
||||
return;
|
||||
}
|
||||
|
||||
// Make form
|
||||
let formData = new FormData();
|
||||
|
||||
formData.append("file", fileUpload.files[0]);
|
||||
formData.append("alt", fileAlt.value);
|
||||
formData.append("description", fileDescription.value);
|
||||
formData.append("tags", fileTags.value);
|
||||
|
||||
jobItem = createJob(fileUpload.files[0]);
|
||||
jobStatus = jobItem.querySelector(".job__status");
|
||||
|
||||
// Upload the information
|
||||
$.ajax({
|
||||
url: '/api/upload',
|
||||
type: 'post',
|
||||
data: formData,
|
||||
contentType: false,
|
||||
processData: false,
|
||||
beforeSend: function () {
|
||||
// Add job to list
|
||||
jobList.appendChild(jobItem);
|
||||
},
|
||||
success: function (response) {
|
||||
jobItem.classList.add("success");
|
||||
jobStatus.innerHTML = "Uploaded successfully";
|
||||
if (!document.querySelector(".upload-panel").classList.contains("open")) {
|
||||
addNotification("Image uploaded successfully", 1);
|
||||
}
|
||||
},
|
||||
error: function (response) {
|
||||
jobItem.classList.add("critical");
|
||||
switch (response.status) {
|
||||
case 500:
|
||||
jobStatus.innerHTML = "Server exploded, F's in chat";
|
||||
break;
|
||||
case 400:
|
||||
case 404:
|
||||
jobStatus.innerHTML = "Error uploading. Blame yourself";
|
||||
break;
|
||||
case 403:
|
||||
jobStatus.innerHTML = "None but devils play past here...";
|
||||
break;
|
||||
case 413:
|
||||
jobStatus.innerHTML = "File too large!!!!!!";
|
||||
break;
|
||||
default:
|
||||
jobStatus.innerHTML = "Error uploading file, blame someone";
|
||||
break;
|
||||
}
|
||||
if (!document.querySelector(".upload-panel").classList.contains("open")) {
|
||||
addNotification("Error uploading file", 2);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
clearUpload();
|
||||
|
||||
// Reset drop
|
||||
fileDrop.classList.remove('active');
|
||||
fileDropTitle.innerHTML = 'Choose or Drop file';
|
||||
});
|
||||
});
|
||||
|
|
Before Width: | Height: | Size: 7.9 KiB After Width: | Height: | Size: 7.9 KiB |
Before Width: | Height: | Size: 8.1 KiB After Width: | Height: | Size: 8.1 KiB |
|
@ -1,8 +1,7 @@
|
|||
{% extends 'layout.html' %}
|
||||
|
||||
{% block content %}
|
||||
<span class="error-page">
|
||||
<h1>{{error}}</h1>
|
||||
<p>{{msg}}</p>
|
||||
</span>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
|
|
@ -1,29 +1,40 @@
|
|||
{% extends 'layout.html' %}
|
||||
{% block nav_groups %}navigation-item__selected{% endblock %}
|
||||
{% block content %}
|
||||
<div class="banner">
|
||||
{% block nav_groups %}selected{% endblock %}
|
||||
{% block head %}
|
||||
<style>
|
||||
{% if images %}
|
||||
<img
|
||||
src="/api/file/{{ images.0.file_name }}?w=1920&h=1080"
|
||||
onload="imgFade(this)"
|
||||
style="opacity:0; background-color:rgb({{ images.0.image_colours.0.0 }}, {{ images.0.image_colours.0.1 }}, {{ images.0.image_colours.0.2 }})"
|
||||
/>
|
||||
<span
|
||||
class="banner-filter"
|
||||
style="background: linear-gradient(to right, rgb({{ images.0.image_colours.0.0 }}, {{ images.0.image_colours.0.1 }}, {{ images.0.image_colours.0.2 }}), transparent)"
|
||||
></span>
|
||||
.banner-content p {
|
||||
color: {{ text_colour }} !important;
|
||||
}
|
||||
.banner-content h1 {
|
||||
color: {{ text_colour }} !important;
|
||||
}
|
||||
|
||||
.banner-filter {
|
||||
background: linear-gradient(90deg, rgb({{ images.0.image_colours.0.0 }}, {{ images.0.image_colours.0.1 }}, {{ images.0.image_colours.0.2 }}), rgba({{ images.0.image_colours.0.0 }}, {{ images.0.image_colours.0.1 }}, {{ images.0.image_colours.0.2 }}, 0.3)) !important;
|
||||
}
|
||||
@media (max-width: 800px) {
|
||||
.banner-filter {
|
||||
background: linear-gradient(180deg, rgba({{ images.0.image_colours.0.0 }}, {{ images.0.image_colours.0.1 }}, {{ images.0.image_colours.0.2 }}, 0.8), rgba({{ images.0.image_colours.0.0 }}, {{ images.0.image_colours.0.1 }}, {{ images.0.image_colours.0.2 }}, 0.5)) !important;
|
||||
}
|
||||
}
|
||||
{% endif %}
|
||||
</style>
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<div class="banner {% if not images %}small{% endif %}">
|
||||
{% if not images %}
|
||||
<div class="banner-content">
|
||||
<p><span id="contrast-check" data-color="rgb({{ images.0.image_colours.0.0 }}, {{ images.0.image_colours.0.1 }}, {{ images.0.image_colours.0.2 }})">{{ group.description }}</span></p>
|
||||
<h1><span id="contrast-check" data-color="rgb({{ images.0.image_colours.0.0 }}, {{ images.0.image_colours.0.1 }}, {{ images.0.image_colours.0.2 }})">{{ group.name }}</span></h1>
|
||||
<p><span id="contrast-check" data-color="rgb({{ images.0.image_colours.0.0 }}, {{ images.0.image_colours.0.1 }}, {{ images.0.image_colours.0.2 }})">By {{ group.author_username }} - {{ images|length }} Images</span></p>
|
||||
<h1>{{ group.name }}</h1>
|
||||
<p>By {{ group.author_username }}</p>
|
||||
</div>
|
||||
{% else %}
|
||||
<img src="{{ url_for('static', filename='images/bg.svg') }}" onload="imgFade(this)" style="opacity:0;"/>
|
||||
<span></span>
|
||||
<img src="{{ url_for('api.file', file_name=images.0.file_name ) }}?r=prev" onload="imgFade(this)" style="opacity:0;"/>
|
||||
<span class="banner-filter"></span>
|
||||
<div class="banner-content">
|
||||
<p>{{ group.description }}</p>
|
||||
<h1>{{ group.name }}</h1>
|
||||
<p>By {{ group.author_username }} - {{ images|length }} Images</p>
|
||||
<h1>{{ group.name }}</h1>
|
||||
<p>{{ group.description }}</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
@ -43,17 +54,17 @@
|
|||
<p class="image-subtitle"></p>
|
||||
<p class="image-title"><span class="time">{{ image.created_at }}</span></p>
|
||||
</div>
|
||||
<img alt="{{ image.post_alt }}" data-src="{{ image.file_name }}" onload="imgFade(this)" style="opacity:0;" id="lazy-load"/>
|
||||
<img alt="{{ image.post_alt }}" data-src="{{ image.file_name }}" onload="this.classList.add('loaded');" id="lazy-load"/>
|
||||
</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="big-text">
|
||||
<h1>No image!</h1>
|
||||
<h1>*crickets chirping*</h1>
|
||||
{% if g.user %}
|
||||
<p>You can get started by uploading an image!</p>
|
||||
<p>Add some images to the group!</p>
|
||||
{% else %}
|
||||
<p>Login to start uploading images!</p>
|
||||
<p>Login to start managing this image group!</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
|
|
@ -1,16 +1,15 @@
|
|||
{% extends 'layout.html' %}
|
||||
{% block nav_groups %}navigation-item__selected{% endblock %}
|
||||
{% block nav_groups %}selected{% endblock %}
|
||||
{% block content %}
|
||||
<div class="banner">
|
||||
<div class="banner small">
|
||||
<div class="banner-content">
|
||||
<p>{{ config.WEBSITE.motto }}</p>
|
||||
<h1>Groups</h1>
|
||||
<h1>{{ config.WEBSITE.name }}</h1>
|
||||
{% if groups|length == 0 %}
|
||||
<p>0 photo groups :<</p>
|
||||
<p>0 groups :<</p>
|
||||
{% elif groups|length == 69 %}
|
||||
<p>{{ groups|length }} photo groups, uwu</p>
|
||||
<p>{{ groups|length }} groups, uwu</p>
|
||||
{% else %}
|
||||
<p>{{ groups|length }} photo groups</p>
|
||||
<p>{{ groups|length }} groups</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -24,37 +23,31 @@
|
|||
{% if groups %}
|
||||
<div class="gallery-grid">
|
||||
{% for group in groups %}
|
||||
{% if group.thumbnail %}
|
||||
<a id="group-{{ group.id }}" class="gallery-item" href="{{ url_for('group.group', group_id=group.id) }}" style="background-color: rgb({{ group.thumbnail.image_colours.0.0 }}, {{ group.thumbnail.image_colours.0.1 }}, {{ group.thumbnail.image_colours.0.2 }})">
|
||||
<div class="image-filter">
|
||||
<p class="image-subtitle"></p>
|
||||
<p class="image-title">{{ group.name }}</p>
|
||||
</div>
|
||||
<img data-src="{{ group.thumbnail.file_name }}" onload="imgFade(this)" style="opacity:0;" id="lazy-load"/>
|
||||
</a>
|
||||
{% else %}
|
||||
<a id="group-{{ group.id }}" class="gallery-item" href="{{ url_for('group.group', group_id=group.id) }}">
|
||||
<div class="image-filter">
|
||||
<p class="image-subtitle"></p>
|
||||
<p class="image-title">{{ group.name }}</p>
|
||||
</div>
|
||||
<img src="{{ url_for('static', filename='images/error.png') }}" onload="imgFade(this)" style="opacity:0;"/>
|
||||
</a>
|
||||
{% endif %}
|
||||
<a id="group-{{ group.id }}" class="group-item" href="{{ url_for('group.group', group_id=group.id) }}">
|
||||
<div class="image-filter">
|
||||
<p class="image-subtitle">By {{ group.author_username }}</p>
|
||||
<p class="image-title">{{ group.name }}</p>
|
||||
</div>
|
||||
<div class="images size-{{ group.images|length }}">
|
||||
{% if group.images|length > 0 %}
|
||||
{% for image in group.images %}
|
||||
<img data-src="{{ image.file_name }}" onload="this.classList.add('loaded');" id="lazy-load" class="data-{{ loop.index }}"/>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<img src="{{ url_for('static', filename='error.png') }}" class="loaded"/>
|
||||
{% endif %}
|
||||
</div>
|
||||
</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="big-text">
|
||||
<h1>No image groups!</h1>
|
||||
<h1>*crickets chirping*</h1>
|
||||
{% if g.user %}
|
||||
<p>You can get started by creating a new image group!</p>
|
||||
{% else %}
|
||||
<p>Login to get started!</p>
|
||||
<p>Login to start seeing anything here!</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block script %}
|
||||
<script></script>
|
||||
{% endblock %}
|
|
@ -1,29 +1,105 @@
|
|||
{% extends 'layout.html' %}
|
||||
|
||||
{% block head %}
|
||||
<meta property="og:image" content="/api/file/{{ image.file_name }}"/>
|
||||
<meta name="theme-color" content="#{{ image.image_colours.0.0 }}{{ image.image_colours.0.1 }}{{ image.image_colours.0.2 }}"/>
|
||||
{% endblock %}
|
||||
|
||||
{% block wrapper_class %}image-wrapper{% endblock %}
|
||||
{% block head %}
|
||||
<meta property="og:image" content="{{ url_for('api.file', file_name=image.file_name) }}"/>
|
||||
<meta name="theme-color" content="#{{ image.image_colours.0.0 }}{{ image.image_colours.0.1 }}{{ image.image_colours.0.2 }}"/>
|
||||
|
||||
<script type="text/javascript">
|
||||
function imageFullscreenOff() {
|
||||
document.querySelector("html").style.overflow = "auto";
|
||||
let fullscreen = document.querySelector('.image-fullscreen')
|
||||
|
||||
fullscreen.classList.remove('active');
|
||||
|
||||
setTimeout(function() {
|
||||
fullscreen.style.display = 'none';
|
||||
}, 200);
|
||||
}
|
||||
function imageFullscreenOn() {
|
||||
let fullscreen = document.querySelector('.image-fullscreen')
|
||||
|
||||
fullscreen.querySelector('img').src = '{{ url_for('api.file', file_name=image.file_name) }}';
|
||||
|
||||
document.querySelector("html").style.overflow = "hidden";
|
||||
fullscreen.style.display = 'flex';
|
||||
setTimeout(function() { fullscreen.classList.add('active'); }, 5);
|
||||
}
|
||||
function imageShare() {
|
||||
try {
|
||||
navigator.clipboard.writeText(window.location.href)
|
||||
addNotification("Copied link!", 4);
|
||||
} catch (err) {
|
||||
addNotification("Failed to copy link! Are you on HTTP?", 2);
|
||||
}
|
||||
}
|
||||
|
||||
{% if g.user['id'] == image['author_id'] %}
|
||||
cancelBtn = document.createElement('button');
|
||||
cancelBtn.classList.add('btn-block');
|
||||
cancelBtn.innerHTML = 'nuuuuuuuu';
|
||||
cancelBtn.onclick = popupDissmiss;
|
||||
|
||||
deleteBtn = document.createElement('button');
|
||||
deleteBtn.classList.add('btn-block');
|
||||
deleteBtn.classList.add('critical');
|
||||
deleteBtn.innerHTML = 'Dewww eeeet!';
|
||||
deleteBtn.onclick = deleteConfirm;
|
||||
|
||||
function imageDelete() {
|
||||
popUpShow(
|
||||
'DESTRUCTION!!!!!!',
|
||||
'Do you want to delete this image along with all of its data??? This action is irreversible!',
|
||||
null,
|
||||
[cancelBtn, deleteBtn]
|
||||
);
|
||||
}
|
||||
function deleteConfirm() {
|
||||
popupDissmiss();
|
||||
|
||||
$.ajax({
|
||||
url: '{{ url_for('api.delete_image', image_id=image['id']) }}',
|
||||
type: 'post',
|
||||
data: {
|
||||
action: 'delete'
|
||||
},
|
||||
success: function (response) {
|
||||
window.location.href = '/';
|
||||
},
|
||||
error: function (response) {
|
||||
addNotification(`Image *clings*: ${response}`, 2);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function imageEdit() {
|
||||
addNotification("Not an option, oops!", 3);
|
||||
}
|
||||
{% endif %}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.background span {
|
||||
background-image: linear-gradient(to top, rgba({{ image.image_colours.0.0 }}, {{ image.image_colours.0.1 }}, {{ image.image_colours.0.2 }}, 1), transparent);
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<div class="background">
|
||||
<img src="/api/file/{{ image.file_name }}?r=prev" alt="{{ image.post_alt }}" onload="imgFade(this)" style="opacity:0;"/>
|
||||
<span style="background-image: linear-gradient(to top, rgba({{ image.image_colours.0.0 }}, {{ image.image_colours.0.1 }}, {{ image.image_colours.0.2 }}, 1), transparent);"></span>
|
||||
<img src="{{ url_for('api.file', file_name=image.file_name) }}?r=prev" alt="{{ image.post_alt }}" onload="imgFade(this)" style="opacity:0;"/>
|
||||
<span></span>
|
||||
</div>
|
||||
|
||||
<div class="image-fullscreen">
|
||||
<div class="image-fullscreen" onclick="imageFullscreenOff()">
|
||||
<img src="" alt="{{ image.post_alt }}" onload="imgFade(this);" style="opacity:0;" />
|
||||
</div>
|
||||
|
||||
<div class="image-grid">
|
||||
<div class="image-container" id="image-container">
|
||||
<img
|
||||
src="/api/file/{{ image.file_name }}?r=prev"
|
||||
src="{{ url_for('api.file', file_name=image.file_name) }}?r=prev"
|
||||
alt="{{ image.post_alt }}"
|
||||
onload="imgFade(this)" style="opacity:0;"
|
||||
onerror="this.src='/static/images/error.png'"
|
||||
onerror="this.src='{{ url_for('static', filename='error.png')}}'"
|
||||
{% if "File" in image.image_exif %}
|
||||
width="{{ image.image_exif.File.Width.raw }}"
|
||||
height="{{ image.image_exif.File.Height.raw }}"
|
||||
|
@ -35,7 +111,7 @@
|
|||
{% if next_url %}
|
||||
<div>
|
||||
<a class="pill-item" href="{{ next_url }}">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M112,56V200L40,128Z" opacity="0.2"></path><path d="M216,120H120V56a8,8,0,0,0-13.66-5.66l-72,72a8,8,0,0,0,0,11.32l72,72A8,8,0,0,0,120,200V136h96a8,8,0,0,0,0-16ZM104,180.69,51.31,128,104,75.31Z"></path></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M128,24A104,104,0,1,0,232,128,104.11,104.11,0,0,0,128,24Zm40,112H107.31l18.35,18.34a8,8,0,0,1-11.32,11.32l-32-32a8,8,0,0,1,0-11.32l32-32a8,8,0,0,1,11.32,11.32L107.31,120H168a8,8,0,0,1,0,16Z"></path></svg>
|
||||
<span class="tool-tip">
|
||||
Previous
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M213.66,101.66l-80,80a8,8,0,0,1-11.32,0l-80-80A8,8,0,0,1,48,88H208a8,8,0,0,1,5.66,13.66Z"></path></svg>
|
||||
|
@ -44,22 +120,22 @@
|
|||
</div>
|
||||
{% endif %}
|
||||
<div>
|
||||
<button class="pill-item" id="img-fullscreen">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M224,56V200a8,8,0,0,1-8,8H40a8,8,0,0,1-8-8V56a8,8,0,0,1,8-8H216A8,8,0,0,1,224,56Z" opacity="0.2"></path><path d="M200,80v32a8,8,0,0,1-16,0V88H160a8,8,0,0,1,0-16h32A8,8,0,0,1,200,80ZM96,168H72V144a8,8,0,0,0-16,0v32a8,8,0,0,0,8,8H96a8,8,0,0,0,0-16ZM232,56V200a16,16,0,0,1-16,16H40a16,16,0,0,1-16-16V56A16,16,0,0,1,40,40H216A16,16,0,0,1,232,56ZM216,200V56H40V200H216Z"></path></svg>
|
||||
<button class="pill-item" onclick="imageFullscreenOn()">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M117.66,138.34a8,8,0,0,1,0,11.32L83.31,184l18.35,18.34A8,8,0,0,1,96,216H48a8,8,0,0,1-8-8V160a8,8,0,0,1,13.66-5.66L72,172.69l34.34-34.35A8,8,0,0,1,117.66,138.34ZM208,40H160a8,8,0,0,0-5.66,13.66L172.69,72l-34.35,34.34a8,8,0,0,0,11.32,11.32L184,83.31l18.34,18.35A8,8,0,0,0,216,96V48A8,8,0,0,0,208,40Z"></path></svg>
|
||||
<span class="tool-tip">
|
||||
Fullscreen
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M213.66,101.66l-80,80a8,8,0,0,1-11.32,0l-80-80A8,8,0,0,1,48,88H208a8,8,0,0,1,5.66,13.66Z"></path></svg>
|
||||
</span>
|
||||
</button>
|
||||
<button class="pill-item" id="img-share">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M208,104V216H48V104Z" opacity="0.2"></path><path d="M216,112v96a16,16,0,0,1-16,16H56a16,16,0,0,1-16-16V112A16,16,0,0,1,56,96H80a8,8,0,0,1,0,16H56v96H200V112H176a8,8,0,0,1,0-16h24A16,16,0,0,1,216,112ZM93.66,69.66,120,43.31V136a8,8,0,0,0,16,0V43.31l26.34,26.35a8,8,0,0,0,11.32-11.32l-40-40a8,8,0,0,0-11.32,0l-40,40A8,8,0,0,0,93.66,69.66Z"></path></svg>
|
||||
<button class="pill-item" onclick="imageShare()">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M212,200a36,36,0,1,1-69.85-12.25l-53-34.05a36,36,0,1,1,0-51.4l53-34a36.09,36.09,0,1,1,8.67,13.45l-53,34.05a36,36,0,0,1,0,24.5l53,34.05A36,36,0,0,1,212,200Z"></path></svg>
|
||||
<span class="tool-tip">
|
||||
Share
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M213.66,101.66l-80,80a8,8,0,0,1-11.32,0l-80-80A8,8,0,0,1,48,88H208a8,8,0,0,1,5.66,13.66Z"></path></svg>
|
||||
</span>
|
||||
</button>
|
||||
<a class="pill-item" href="/api/file/{{ image.file_name }}" download onclick="addNotification('Download started!', 4)">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M232,136v64a8,8,0,0,1-8,8H32a8,8,0,0,1-8-8V136a8,8,0,0,1,8-8H224A8,8,0,0,1,232,136Z" opacity="0.2"></path><path d="M240,136v64a16,16,0,0,1-16,16H32a16,16,0,0,1-16-16V136a16,16,0,0,1,16-16H72a8,8,0,0,1,0,16H32v64H224V136H184a8,8,0,0,1,0-16h40A16,16,0,0,1,240,136Zm-117.66-2.34a8,8,0,0,0,11.32,0l48-48a8,8,0,0,0-11.32-11.32L136,108.69V24a8,8,0,0,0-16,0v84.69L85.66,74.34A8,8,0,0,0,74.34,85.66ZM200,168a12,12,0,1,0-12,12A12,12,0,0,0,200,168Z"></path></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M74.34,85.66A8,8,0,0,1,85.66,74.34L120,108.69V24a8,8,0,0,1,16,0v84.69l34.34-34.35a8,8,0,0,1,11.32,11.32l-48,48a8,8,0,0,1-11.32,0ZM240,136v64a16,16,0,0,1-16,16H32a16,16,0,0,1-16-16V136a16,16,0,0,1,16-16H84.4a4,4,0,0,1,2.83,1.17L111,145A24,24,0,0,0,145,145l23.8-23.8A4,4,0,0,1,171.6,120H224A16,16,0,0,1,240,136Zm-40,32a12,12,0,1,0-12,12A12,12,0,0,0,200,168Z"></path></svg>
|
||||
<span class="tool-tip">
|
||||
Download
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M213.66,101.66l-80,80a8,8,0,0,1-11.32,0l-80-80A8,8,0,0,1,48,88H208a8,8,0,0,1,5.66,13.66Z"></path></svg>
|
||||
|
@ -68,15 +144,15 @@
|
|||
</div>
|
||||
{% if image.author_id == g.user.id %}
|
||||
<div>
|
||||
<button class="pill-item pill__critical" id="img-delete">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M200,56V208a8,8,0,0,1-8,8H64a8,8,0,0,1-8-8V56Z" opacity="0.2"></path><path d="M216,48H176V40a24,24,0,0,0-24-24H104A24,24,0,0,0,80,40v8H40a8,8,0,0,0,0,16h8V208a16,16,0,0,0,16,16H192a16,16,0,0,0,16-16V64h8a8,8,0,0,0,0-16ZM96,40a8,8,0,0,1,8-8h48a8,8,0,0,1,8,8v8H96Zm96,168H64V64H192ZM112,104v64a8,8,0,0,1-16,0V104a8,8,0,0,1,16,0Zm48,0v64a8,8,0,0,1-16,0V104a8,8,0,0,1,16,0Z"></path></svg>
|
||||
<button class="pill-item pill__critical" onclick="imageDelete()">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M216,48H176V40a24,24,0,0,0-24-24H104A24,24,0,0,0,80,40v8H40a8,8,0,0,0,0,16h8V208a16,16,0,0,0,16,16H192a16,16,0,0,0,16-16V64h8a8,8,0,0,0,0-16ZM112,168a8,8,0,0,1-16,0V104a8,8,0,0,1,16,0Zm48,0a8,8,0,0,1-16,0V104a8,8,0,0,1,16,0Zm0-120H96V40a8,8,0,0,1,8-8h48a8,8,0,0,1,8,8Z"></path></svg>
|
||||
<span class="tool-tip">
|
||||
Delete
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M213.66,101.66l-80,80a8,8,0,0,1-11.32,0l-80-80A8,8,0,0,1,48,88H208a8,8,0,0,1,5.66,13.66Z"></path></svg>
|
||||
</span>
|
||||
</button>
|
||||
<button class="pill-item pill__critical" id="img-edit">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M221.66,90.34,192,120,136,64l29.66-29.66a8,8,0,0,1,11.31,0L221.66,79A8,8,0,0,1,221.66,90.34Z" opacity="0.2"></path><path d="M227.31,73.37,182.63,28.68a16,16,0,0,0-22.63,0L36.69,152A15.86,15.86,0,0,0,32,163.31V208a16,16,0,0,0,16,16H92.69A15.86,15.86,0,0,0,104,219.31L227.31,96a16,16,0,0,0,0-22.63ZM92.69,208H48V163.31l88-88L180.69,120ZM192,108.68,147.31,64l24-24L216,84.68Z"></path></svg>
|
||||
<button class="pill-item pill__critical" onclick="imageEdit()">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M227.31,73.37,182.63,28.68a16,16,0,0,0-22.63,0L36.69,152A15.86,15.86,0,0,0,32,163.31V208a16,16,0,0,0,16,16H92.69A15.86,15.86,0,0,0,104,219.31L227.31,96a16,16,0,0,0,0-22.63ZM51.31,160l90.35-90.35,16.68,16.69L68,176.68ZM48,179.31,76.69,208H48Zm48,25.38L79.31,188l90.35-90.35h0l16.68,16.69Z"></path></svg>
|
||||
<span class="tool-tip">
|
||||
Edit
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M213.66,101.66l-80,80a8,8,0,0,1-11.32,0l-80-80A8,8,0,0,1,48,88H208a8,8,0,0,1,5.66,13.66Z"></path></svg>
|
||||
|
@ -87,7 +163,7 @@
|
|||
{% if prev_url %}
|
||||
<div>
|
||||
<a class="pill-item" href="{{ prev_url }}">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M216,128l-72,72V56Z" opacity="0.2"></path><path d="M221.66,122.34l-72-72A8,8,0,0,0,136,56v64H40a8,8,0,0,0,0,16h96v64a8,8,0,0,0,13.66,5.66l72-72A8,8,0,0,0,221.66,122.34ZM152,180.69V75.31L204.69,128Z"></path></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M128,24A104,104,0,1,0,232,128,104.11,104.11,0,0,0,128,24Zm45.66,109.66-32,32a8,8,0,0,1-11.32-11.32L148.69,136H88a8,8,0,0,1,0-16h60.69l-18.35-18.34a8,8,0,0,1,11.32-11.32l32,32A8,8,0,0,1,173.66,133.66Z"></path></svg>
|
||||
<span class="tool-tip">
|
||||
Next
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M213.66,101.66l-80,80a8,8,0,0,1-11.32,0l-80-80A8,8,0,0,1,48,88H208a8,8,0,0,1,5.66,13.66Z"></path></svg>
|
||||
|
@ -101,7 +177,7 @@
|
|||
{% if image.post_description %}
|
||||
<div class="info-tab">
|
||||
<div class="info-header">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M224,56V200a8,8,0,0,1-8,8H40a8,8,0,0,1-8-8V56a8,8,0,0,1,8-8H216A8,8,0,0,1,224,56Z" opacity="0.2"></path><path d="M216,40H40A16,16,0,0,0,24,56V200a16,16,0,0,0,16,16H216a16,16,0,0,0,16-16V56A16,16,0,0,0,216,40Zm0,160H40V56H216V200ZM184,96a8,8,0,0,1-8,8H80a8,8,0,0,1,0-16h96A8,8,0,0,1,184,96Zm0,32a8,8,0,0,1-8,8H80a8,8,0,0,1,0-16h96A8,8,0,0,1,184,128Zm0,32a8,8,0,0,1-8,8H80a8,8,0,0,1,0-16h96A8,8,0,0,1,184,160Z"></path></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M216,40H40A16,16,0,0,0,24,56V200a16,16,0,0,0,16,16H216a16,16,0,0,0,16-16V56A16,16,0,0,0,216,40Zm0,160H40V56H216V200ZM184,96a8,8,0,0,1-8,8H80a8,8,0,0,1,0-16h96A8,8,0,0,1,184,96Zm0,32a8,8,0,0,1-8,8H80a8,8,0,0,1,0-16h96A8,8,0,0,1,184,128Zm0,32a8,8,0,0,1-8,8H80a8,8,0,0,1,0-16h96A8,8,0,0,1,184,160Z"></path></svg>
|
||||
<h2>Description</h2>
|
||||
<svg class="collapse-indicator" xmlns="http://www.w3.org/2000/svg" viewBox="-5 -8 24 24" fill="currentColor">
|
||||
<path d="M7.071 5.314l4.95-4.95a1 1 0 1 1 1.414 1.414L7.778 7.435a1 1 0 0 1-1.414 0L.707 1.778A1 1 0 1 1 2.121.364l4.95 4.95z"></path>
|
||||
|
@ -114,52 +190,36 @@
|
|||
{% endif %}
|
||||
<div class="info-tab">
|
||||
<div class="info-header">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M224,128a96,96,0,1,1-96-96A96,96,0,0,1,224,128Z" opacity="0.2"></path><path d="M144,176a8,8,0,0,1-8,8,16,16,0,0,1-16-16V128a8,8,0,0,1,0-16,16,16,0,0,1,16,16v40A8,8,0,0,1,144,176Zm88-48A104,104,0,1,1,128,24,104.11,104.11,0,0,1,232,128Zm-16,0a88,88,0,1,0-88,88A88.1,88.1,0,0,0,216,128ZM124,96a12,12,0,1,0-12-12A12,12,0,0,0,124,96Z"></path></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M128,24A104,104,0,1,0,232,128,104.11,104.11,0,0,0,128,24Zm0,192a88,88,0,1,1,88-88A88.1,88.1,0,0,1,128,216Zm16-40a8,8,0,0,1-8,8,16,16,0,0,1-16-16V128a8,8,0,0,1,0-16,16,16,0,0,1,16,16v40A8,8,0,0,1,144,176ZM112,84a12,12,0,1,1,12,12A12,12,0,0,1,112,84Z"></path></svg>
|
||||
<h2>Info</h2>
|
||||
<svg class="collapse-indicator" xmlns="http://www.w3.org/2000/svg" viewBox="-5 -8 24 24" fill="currentColor">
|
||||
<path d="M7.071 5.314l4.95-4.95a1 1 0 1 1 1.414 1.414L7.778 7.435a1 1 0 0 1-1.414 0L.707 1.778A1 1 0 1 1 2.121.364l4.95 4.95z"></path>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="info-table">
|
||||
<div class="img-colours">
|
||||
{% for col in image.image_colours %}
|
||||
<span style="background-color: rgb({{col.0}}, {{col.1}}, {{col.2}})"></span>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<table>
|
||||
<tr>
|
||||
<td>Image ID</td>
|
||||
<td>{{ image['id'] }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Author</td>
|
||||
<td>{{ image.author_username }}</td>
|
||||
<td><a href="{{ url_for('gallery.profile_id', user_id=image.author_id) }}" class="link">{{ image.author_username }}</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Upload date</td>
|
||||
<td><span class="time">{{ image.created_at }}</span></td>
|
||||
</tr>
|
||||
</table>
|
||||
{% if group and image.author_id == g.user.id %}
|
||||
<div class="img-colours">
|
||||
{% for col in image.image_colours %}
|
||||
<span style="background-color: rgb({{col.0}}, {{col.1}}, {{col.2}})"></span>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% if image.groups %}
|
||||
<div class="img-groups">
|
||||
{% for group in image.groups %}
|
||||
<a href="/group/{{ group.id }}" class="tag-icon">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M224,88V200.89a7.11,7.11,0,0,1-7.11,7.11H40a8,8,0,0,1-8-8V64a8,8,0,0,1,8-8H93.33a8,8,0,0,1,4.8,1.6l27.74,20.8a8,8,0,0,0,4.8,1.6H216A8,8,0,0,1,224,88Z" opacity="0.2"></path><path d="M216,72H130.67L102.93,51.2a16.12,16.12,0,0,0-9.6-3.2H40A16,16,0,0,0,24,64V200a16,16,0,0,0,16,16H216.89A15.13,15.13,0,0,0,232,200.89V88A16,16,0,0,0,216,72Zm0,128H40V64H93.33l27.74,20.8a16.12,16.12,0,0,0,9.6,3.2H216Z"></path></svg>
|
||||
<a href="{{ url_for('group.group', group_id=group.id) }}" class="tag-icon">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M216,72H131.31L104,44.69A15.86,15.86,0,0,0,92.69,40H40A16,16,0,0,0,24,56V200.62A15.4,15.4,0,0,0,39.38,216H216.89A15.13,15.13,0,0,0,232,200.89V88A16,16,0,0,0,216,72ZM40,56H92.69l16,16H40ZM216,200H40V88H216Z"></path></svg>
|
||||
{{ group['name'] }}
|
||||
</a>
|
||||
{% endfor %}
|
||||
|
||||
<button class="tag-icon" id="#img-group">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M224,128a8,8,0,0,1-8,8H136v80a8,8,0,0,1-16,0V136H40a8,8,0,0,1,0-16h80V40a8,8,0,0,1,16,0v80h80A8,8,0,0,1,224,128Z"></path></svg>
|
||||
Add
|
||||
</button>
|
||||
</div>
|
||||
{% elif image.author_id == g.user.id %}
|
||||
<div class="img-groups">
|
||||
<button class="tag-icon" id="#img-group">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M224,128a8,8,0,0,1-8,8H136v80a8,8,0,0,1-16,0V136H40a8,8,0,0,1,0-16h80V40a8,8,0,0,1,16,0v80h80A8,8,0,0,1,224,128Z"></path></svg>
|
||||
Add
|
||||
</button>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
@ -168,19 +228,19 @@
|
|||
<div class="info-tab">
|
||||
<div class="info-header">
|
||||
{% if tag == 'Photographer' %}
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M104,40a24,24,0,1,1,24,24A24,24,0,0,1,104,40Zm108.49,99.51L167.17,88.13a24,24,0,0,0-18-8.13H106.83a24,24,0,0,0-18,8.13L43.51,139.51a12,12,0,0,0,17,17L96,128,73.13,214.93a12,12,0,0,0,21.75,10.14L128,168l33.12,57.07a12,12,0,0,0,21.75-10.14L160,128l35.51,28.49a12,12,0,0,0,17-17Z" opacity="0.2"></path><path d="M160,40a32,32,0,1,0-32,32A32,32,0,0,0,160,40ZM128,56a16,16,0,1,1,16-16A16,16,0,0,1,128,56Zm90.34,78.05L173.17,82.83a32,32,0,0,0-24-10.83H106.83a32,32,0,0,0-24,10.83L37.66,134.05a20,20,0,0,0,28.13,28.43l16.3-13.08L65.55,212.28A20,20,0,0,0,102,228.8l26-44.87,26,44.87a20,20,0,0,0,36.41-16.52L173.91,149.4l16.3,13.08a20,20,0,0,0,28.13-28.43Zm-11.51,16.77a4,4,0,0,1-5.66,0c-.21-.2-.42-.4-.65-.58L165,121.76A8,8,0,0,0,152.26,130L175.14,217a7.72,7.72,0,0,0,.48,1.35,4,4,0,1,1-7.25,3.38,6.25,6.25,0,0,0-.33-.63L134.92,164a8,8,0,0,0-13.84,0L88,221.05a6.25,6.25,0,0,0-.33.63,4,4,0,0,1-2.26,2.07,4,4,0,0,1-5-5.45,7.72,7.72,0,0,0,.48-1.35L103.74,130A8,8,0,0,0,91,121.76L55.48,150.24c-.23.18-.44.38-.65.58a4,4,0,1,1-5.66-5.65c.12-.12.23-.24.34-.37L94.83,93.41a16,16,0,0,1,12-5.41h42.34a16,16,0,0,1,12,5.41l45.32,51.39c.11.13.22.25.34.37A4,4,0,0,1,206.83,150.82Z"></path></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M160,40a32,32,0,1,0-32,32A32,32,0,0,0,160,40ZM128,56a16,16,0,1,1,16-16A16,16,0,0,1,128,56Zm90.34,78.05L173.17,82.83a32,32,0,0,0-24-10.83H106.83a32,32,0,0,0-24,10.83L37.66,134.05a20,20,0,0,0,28.13,28.43l16.3-13.08L65.55,212.28A20,20,0,0,0,102,228.8l26-44.87,26,44.87a20,20,0,0,0,36.41-16.52L173.91,149.4l16.3,13.08a20,20,0,0,0,28.13-28.43Zm-11.51,16.77a4,4,0,0,1-5.66,0c-.21-.2-.42-.4-.65-.58L165,121.76A8,8,0,0,0,152.26,130L175.14,217a7.72,7.72,0,0,0,.48,1.35,4,4,0,1,1-7.25,3.38,6.25,6.25,0,0,0-.33-.63L134.92,164a8,8,0,0,0-13.84,0L88,221.05a6.25,6.25,0,0,0-.33.63,4,4,0,0,1-2.26,2.07,4,4,0,0,1-5-5.45,7.72,7.72,0,0,0,.48-1.35L103.74,130A8,8,0,0,0,91,121.76L55.48,150.24c-.23.18-.44.38-.65.58a4,4,0,1,1-5.66-5.65c.12-.12.23-.24.34-.37L94.83,93.41a16,16,0,0,1,12-5.41h42.34a16,16,0,0,1,12,5.41l45.32,51.39c.11.13.22.25.34.37A4,4,0,0,1,206.83,150.82Z"></path></svg>
|
||||
<h2>Photographer</h2>
|
||||
{% elif tag == 'Camera' %}
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M208,64H176L160,40H96L80,64H48A16,16,0,0,0,32,80V192a16,16,0,0,0,16,16H208a16,16,0,0,0,16-16V80A16,16,0,0,0,208,64ZM128,168a36,36,0,1,1,36-36A36,36,0,0,1,128,168Z" opacity="0.2"></path><path d="M208,56H180.28L166.65,35.56A8,8,0,0,0,160,32H96a8,8,0,0,0-6.65,3.56L75.71,56H48A24,24,0,0,0,24,80V192a24,24,0,0,0,24,24H208a24,24,0,0,0,24-24V80A24,24,0,0,0,208,56Zm8,136a8,8,0,0,1-8,8H48a8,8,0,0,1-8-8V80a8,8,0,0,1,8-8H80a8,8,0,0,0,6.66-3.56L100.28,48h55.43l13.63,20.44A8,8,0,0,0,176,72h32a8,8,0,0,1,8,8ZM128,88a44,44,0,1,0,44,44A44.05,44.05,0,0,0,128,88Zm0,72a28,28,0,1,1,28-28A28,28,0,0,1,128,160Z"></path></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M208,56H180.28L166.65,35.56A8,8,0,0,0,160,32H96a8,8,0,0,0-6.65,3.56L75.71,56H48A24,24,0,0,0,24,80V192a24,24,0,0,0,24,24H208a24,24,0,0,0,24-24V80A24,24,0,0,0,208,56Zm8,136a8,8,0,0,1-8,8H48a8,8,0,0,1-8-8V80a8,8,0,0,1,8-8H80a8,8,0,0,0,6.66-3.56L100.28,48h55.43l13.63,20.44A8,8,0,0,0,176,72h32a8,8,0,0,1,8,8ZM128,88a44,44,0,1,0,44,44A44.05,44.05,0,0,0,128,88Zm0,72a28,28,0,1,1,28-28A28,28,0,0,1,128,160Z"></path></svg>
|
||||
<h2>Camera</h2>
|
||||
{% elif tag == 'Software' %}
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M240,48V208a8,8,0,0,1-8,8H152a8,8,0,0,1-8-8V48a8,8,0,0,1,8-8h80A8,8,0,0,1,240,48Z" opacity="0.2"></path><path d="M24,96v72a8,8,0,0,0,8,8h80a8,8,0,0,1,0,16H96v16h16a8,8,0,0,1,0,16H64a8,8,0,0,1,0-16H80V192H32A24,24,0,0,1,8,168V96A24,24,0,0,1,32,72h80a8,8,0,0,1,0,16H32A8,8,0,0,0,24,96ZM208,64H176a8,8,0,0,0,0,16h32a8,8,0,0,0,0-16Zm0,32H176a8,8,0,0,0,0,16h32a8,8,0,0,0,0-16Zm40-48V208a16,16,0,0,1-16,16H152a16,16,0,0,1-16-16V48a16,16,0,0,1,16-16h80A16,16,0,0,1,248,48ZM232,208V48H152V208h80Zm-40-40a12,12,0,1,0,12,12A12,12,0,0,0,192,168Z"></path></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M24,96v72a8,8,0,0,0,8,8h80a8,8,0,0,1,0,16H96v16h16a8,8,0,0,1,0,16H64a8,8,0,0,1,0-16H80V192H32A24,24,0,0,1,8,168V96A24,24,0,0,1,32,72h80a8,8,0,0,1,0,16H32A8,8,0,0,0,24,96ZM208,64H176a8,8,0,0,0,0,16h32a8,8,0,0,0,0-16Zm0,32H176a8,8,0,0,0,0,16h32a8,8,0,0,0,0-16Zm40-48V208a16,16,0,0,1-16,16H152a16,16,0,0,1-16-16V48a16,16,0,0,1,16-16h80A16,16,0,0,1,248,48ZM232,208V48H152V208h80Zm-40-40a12,12,0,1,0,12,12A12,12,0,0,0,192,168Z"></path></svg>
|
||||
<h2>Software</h2>
|
||||
{% elif tag == 'File' %}
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M104,152l48,72H24l36-56,16.36,25.45ZM152,32V88h56Z" opacity="0.2"></path><path d="M110.66,147.56a8,8,0,0,0-13.32,0L76.49,178.85l-9.76-15.18a8,8,0,0,0-13.46,0l-36,56A8,8,0,0,0,24,232H152a8,8,0,0,0,6.66-12.44ZM38.65,216,60,182.79l9.63,15a8,8,0,0,0,6.67,3.67A7.91,7.91,0,0,0,83,197.89l21-31.47L137.05,216Zm175-133.66-56-56A8,8,0,0,0,152,24H56A16,16,0,0,0,40,40v96a8,8,0,0,0,16,0V40h88V88a8,8,0,0,0,8,8h48V216h-8a8,8,0,0,0,0,16h8a16,16,0,0,0,16-16V88A8,8,0,0,0,213.66,82.34ZM160,51.31,188.69,80H160Z"></path></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M110.66,147.56a8,8,0,0,0-13.32,0L76.49,178.85l-9.76-15.18a8,8,0,0,0-13.46,0l-36,56A8,8,0,0,0,24,232H152a8,8,0,0,0,6.66-12.44ZM38.65,216,60,182.79l9.63,15a8,8,0,0,0,13.39.11l21-31.47L137.05,216Zm175-133.66-56-56A8,8,0,0,0,152,24H56A16,16,0,0,0,40,40v96a8,8,0,0,0,16,0V40h88V88a8,8,0,0,0,8,8h48V216h-8a8,8,0,0,0,0,16h8a16,16,0,0,0,16-16V88A8,8,0,0,0,213.66,82.34ZM160,51.31,188.69,80H160Z"></path></svg>
|
||||
<h2>File</h2>
|
||||
{% else %}
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M104,152l48,72H24l36-56,16.36,25.45ZM152,32V88h56Z" opacity="0.2"></path><path d="M110.66,147.56a8,8,0,0,0-13.32,0L76.49,178.85l-9.76-15.18a8,8,0,0,0-13.46,0l-36,56A8,8,0,0,0,24,232H152a8,8,0,0,0,6.66-12.44ZM38.65,216,60,182.79l9.63,15a8,8,0,0,0,6.67,3.67A7.91,7.91,0,0,0,83,197.89l21-31.47L137.05,216Zm175-133.66-56-56A8,8,0,0,0,152,24H56A16,16,0,0,0,40,40v96a8,8,0,0,0,16,0V40h88V88a8,8,0,0,0,8,8h48V216h-8a8,8,0,0,0,0,16h8a16,16,0,0,0,16-16V88A8,8,0,0,0,213.66,82.34ZM160,51.31,188.69,80H160Z"></path></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M110.66,147.56a8,8,0,0,0-13.32,0L76.49,178.85l-9.76-15.18a8,8,0,0,0-13.46,0l-36,56A8,8,0,0,0,24,232H152a8,8,0,0,0,6.66-12.44ZM38.65,216,60,182.79l9.63,15a8,8,0,0,0,13.39.11l21-31.47L137.05,216Zm175-133.66-56-56A8,8,0,0,0,152,24H56A16,16,0,0,0,40,40v96a8,8,0,0,0,16,0V40h88V88a8,8,0,0,0,8,8h48V216h-8a8,8,0,0,0,0,16h8a16,16,0,0,0,16-16V88A8,8,0,0,0,213.66,82.34ZM160,51.31,188.69,80H160Z"></path></svg>
|
||||
<h2>{{ tag }}</h2>
|
||||
{% endif %}
|
||||
<svg class="collapse-indicator" xmlns="http://www.w3.org/2000/svg" viewBox="-5 -8 24 24" fill="currentColor">
|
||||
|
@ -214,77 +274,12 @@
|
|||
{% endblock %}
|
||||
|
||||
{% block script %}
|
||||
<script>
|
||||
var infoTab = document.querySelectorAll('.info-tab');
|
||||
for (var i = 0; i < infoTab.length; i++) {
|
||||
<script type="text/javascript">
|
||||
let infoTab = document.querySelectorAll('.info-tab');
|
||||
for (let i = 0; i < infoTab.length; i++) {
|
||||
infoTab[i].querySelector('.info-header').addEventListener('click', function() {
|
||||
this.parentNode.classList.toggle('collapsed');
|
||||
});
|
||||
}
|
||||
|
||||
$('.image-fullscreen').click(function() {
|
||||
// un-Stop scrolling
|
||||
document.querySelector("html").style.overflow = "auto";
|
||||
let fullscreen = document.querySelector('.image-fullscreen')
|
||||
|
||||
fullscreen.classList.remove('active');
|
||||
|
||||
setTimeout(function() {
|
||||
fullscreen.style.display = 'none';
|
||||
}, 200);
|
||||
});
|
||||
$('#img-fullscreen').click(function() {
|
||||
// Stop scrolling
|
||||
document.querySelector("html").style.overflow = "hidden";
|
||||
let fullscreen = document.querySelector('.image-fullscreen')
|
||||
|
||||
fullscreen.querySelector('img').src = '/api/file/{{ image.file_name }}';
|
||||
fullscreen.style.display = 'flex';
|
||||
|
||||
setTimeout(function() {
|
||||
fullscreen.classList.add('active');
|
||||
}, 10);
|
||||
});
|
||||
|
||||
$('#img-share').click(function() {
|
||||
try {
|
||||
navigator.clipboard.writeText(window.location.href);
|
||||
addNotification("Copied link!", 4);
|
||||
} catch (err) {
|
||||
addNotification("Failed to copy link! Are you on HTTP?", 2);
|
||||
}
|
||||
});
|
||||
|
||||
{% if g.user['id'] == image['author_id'] %}
|
||||
$('#img-delete').click(function() {
|
||||
popUpShow(
|
||||
'DESTRUCTION!!!!!!',
|
||||
'Do you want to delete this image along with all of its data??? This action is irreversible!',
|
||||
'<button class="btn-block" onclick="popupDissmiss()">Nuuu</button>' +
|
||||
'<button class="btn-block critical" onclick="deleteImage()">Dewww eeeet!</button>'
|
||||
);
|
||||
});
|
||||
function deleteImage() {
|
||||
popupDissmiss();
|
||||
|
||||
$.ajax({
|
||||
url: '{{ url_for('api.delete_image', image_id=image['id']) }}',
|
||||
type: 'post',
|
||||
data: {
|
||||
action: 'delete'
|
||||
},
|
||||
success: function (response) {
|
||||
window.location.href = '/';
|
||||
},
|
||||
error: function (response) {
|
||||
addNotification(`Image *clings*: ${response}`, 2);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$('#img-edit').click(function() {
|
||||
window.location.href = '/image/{{ image.id }}/edit';
|
||||
});
|
||||
{% endif %}
|
||||
</script>
|
||||
{% endblock %}
|
|
@ -1,51 +1,47 @@
|
|||
{% extends 'layout.html' %}
|
||||
|
||||
{% block nav_home %}navigation-item__selected{% endblock %}
|
||||
|
||||
{% block nav_home %}selected{% endblock %}
|
||||
{% block content %}
|
||||
<div class="banner">
|
||||
<div class="banner small">
|
||||
<div class="banner-content">
|
||||
<p>{{ config.WEBSITE.motto }}</p>
|
||||
<h1>{{ config.WEBSITE.name }}</h1>
|
||||
{% if images|length == 0 %}
|
||||
<p>Serving 0 images :<</p>
|
||||
<p>0 images :<</p>
|
||||
{% elif images|length == 69 %}
|
||||
<p>Serving {{ images|length }} images, nice</p>
|
||||
<p>{{ images|length }} images, nice</p>
|
||||
{% else %}
|
||||
<p>Serving {{ images|length }} images</p>
|
||||
<p>{{ images|length }} images</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if images %}
|
||||
<div class="gallery-grid">
|
||||
{% for image in images %}
|
||||
<a id="image-{{ image.id }}" class="gallery-item" href="/image/{{ image.id }}" style="background-color: rgb({{ image.image_colours.0.0 }}, {{ image.image_colours.0.1 }}, {{ image.image_colours.0.2 }})">
|
||||
<a id="image-{{ image.id }}" class="gallery-item" href="{{ url_for('gallery.image', image_id=image.id) }}" style="background-color: rgb({{ image.image_colours.0.0 }}, {{ image.image_colours.0.1 }}, {{ image.image_colours.0.2 }})">
|
||||
<div class="image-filter">
|
||||
<p class="image-subtitle"></p>
|
||||
<p class="image-title"><span class="time">{{ image.created_at }}</span></p>
|
||||
</div>
|
||||
<img alt="{{ image.post_alt }}" data-src="{{ image.file_name }}" onload="imgFade(this)" style="opacity:0;" id="lazy-load"/>
|
||||
<img alt="{{ image.post_alt }}" data-src="{{ image.file_name }}" onload="this.classList.add('loaded');" id="lazy-load"/>
|
||||
</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="big-text">
|
||||
<h1>*crickets chirping*</h1>
|
||||
<p>There are no images here yet, upload some!</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block script %}
|
||||
<script>
|
||||
<script type="text/javascript">
|
||||
if (document.referrer.includes('image')) {
|
||||
try {
|
||||
let referrerId = document.referrer.split('/').pop();
|
||||
|
||||
|
||||
let imgOffset = document.getElementById('image-' + referrerId).offsetTop;
|
||||
let imgHeight = document.getElementById('image-' + referrerId).offsetHeight;
|
||||
let windowHeight = window.innerHeight;
|
||||
|
||||
|
||||
document.querySelector('html').style.scrollBehavior = 'auto';
|
||||
window.scrollTo(0, imgOffset + (imgHeight / 2) - (windowHeight / 2));
|
||||
document.querySelector('html').style.scrollBehavior = 'smooth';
|
||||
|
|
|
@ -17,12 +17,12 @@
|
|||
<meta name="twitter:card" content="summary_large_image">
|
||||
|
||||
<link
|
||||
href="{{url_for('static', filename='images/logo-black.svg')}}"
|
||||
href="{{url_for('static', filename='logo-black.svg')}}"
|
||||
rel="icon"
|
||||
type="image/svg+xml"
|
||||
media="(prefers-color-scheme: light)"/>
|
||||
<link
|
||||
href="{{url_for('static', filename='images/logo-white.svg')}}"
|
||||
href="{{url_for('static', filename='logo-white.svg')}}"
|
||||
rel="icon"
|
||||
type="image/svg+xml"
|
||||
media="(prefers-color-scheme: dark)"/>
|
||||
|
@ -32,39 +32,36 @@
|
|||
{% assets "js_all" %}
|
||||
<script type="text/javascript" src="{{ ASSET_URL }}"></script>
|
||||
{% endassets %}
|
||||
|
||||
|
||||
{% block head %}{% endblock %}
|
||||
</head>
|
||||
<body>
|
||||
<div class="notifications"></div>
|
||||
|
||||
<button class="top-of-page">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M200,144H56l72-72Z" opacity="0.2"></path><path d="M133.66,66.34a8,8,0,0,0-11.32,0l-72,72A8,8,0,0,0,56,152h64v72a8,8,0,0,0,16,0V152h64a8,8,0,0,0,5.66-13.66ZM75.31,136,128,83.31,180.69,136ZM224,40a8,8,0,0,1-8,8H40a8,8,0,0,1,0-16H216A8,8,0,0,1,224,40Z"></path></svg>
|
||||
</button>
|
||||
<button class="info-button">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M224,128a96,96,0,1,1-96-96A96,96,0,0,1,224,128Z" opacity="0.2"></path><path d="M144,176a8,8,0,0,1-8,8,16,16,0,0,1-16-16V128a8,8,0,0,1,0-16,16,16,0,0,1,16,16v40A8,8,0,0,1,144,176Zm88-48A104,104,0,1,1,128,24,104.11,104.11,0,0,1,232,128Zm-16,0a88,88,0,1,0-88,88A88.1,88.1,0,0,0,216,128ZM124,96a12,12,0,1,0-12-12A12,12,0,0,0,124,96Z"></path></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M184,216a8,8,0,0,1-8,8H80a8,8,0,0,1,0-16h96A8,8,0,0,1,184,216Zm45.66-101.66-96-96a8,8,0,0,0-11.32,0l-96,96A8,8,0,0,0,32,128H72v24a8,8,0,0,0,8,8h96a8,8,0,0,0,8-8V128h40a8,8,0,0,0,5.66-13.66ZM176,176H80a8,8,0,0,0,0,16h96a8,8,0,0,0,0-16Z"></path></svg>
|
||||
</button>
|
||||
{% if request.path == "/" %}
|
||||
<button class="info-button">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M128,24A104,104,0,1,0,232,128,104.11,104.11,0,0,0,128,24Zm-4,48a12,12,0,1,1-12,12A12,12,0,0,1,124,72Zm12,112a16,16,0,0,1-16-16V128a8,8,0,0,1,0-16,16,16,0,0,1,16,16v40a8,8,0,0,1,0,16Z"></path></svg>
|
||||
</button>
|
||||
{% endif %}
|
||||
|
||||
<div class="pop-up">
|
||||
<span class="pop-up__click-off" onclick="popupDissmiss()"></span>
|
||||
<div class="pop-up-wrapper">
|
||||
<div class="pop-up-content">
|
||||
<h3>Title</h3>
|
||||
<p>Very very very drawn out example description</p>
|
||||
</div>
|
||||
<div class="pop-up-controlls">
|
||||
<button class="pop-up__btn" onclick="popupDissmiss()">Cancel</button>
|
||||
</div>
|
||||
<div class="pop-up-header"></div>
|
||||
<div class="pop-up-controlls"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="wrapper">
|
||||
|
||||
<div class="navigation">
|
||||
<img src="{{url_for('static', filename='images/icon.png')}}" alt="Logo" class="logo" onload="imgFade(this)" style="opacity:0;">
|
||||
<img src="{{url_for('static', filename='icon.png')}}" alt="Logo" class="logo" onload="this.style.opacity=1;" style="opacity:0">
|
||||
|
||||
<a href="{{url_for('gallery.index')}}" class="navigation-item {% block nav_home %}{% endblock %}">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M208,40H80a8,8,0,0,0-8,8V176a8,8,0,0,0,8,8H96.69l77.65-77.66a8,8,0,0,1,11.32,0L216,136.69V48A8,8,0,0,0,208,40Zm-88,64a16,16,0,1,1,16-16A16,16,0,0,1,120,104Z" opacity="0.2"></path><path d="M208,32H80A16,16,0,0,0,64,48V64H48A16,16,0,0,0,32,80V208a16,16,0,0,0,16,16H176a16,16,0,0,0,16-16V192h16a16,16,0,0,0,16-16V48A16,16,0,0,0,208,32ZM80,48H208v69.38l-16.7-16.7a16,16,0,0,0-22.62,0L93.37,176H80Zm96,160H48V80H64v96a16,16,0,0,0,16,16h96Zm32-32H116l64-64,28,28v36Zm-88-64A24,24,0,1,0,96,88,24,24,0,0,0,120,112Zm0-32a8,8,0,1,1-8,8A8,8,0,0,1,120,80Z"></path></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M208,32H80A16,16,0,0,0,64,48V64H48A16,16,0,0,0,32,80V208a16,16,0,0,0,16,16H176a16,16,0,0,0,16-16V192h16a16,16,0,0,0,16-16V48A16,16,0,0,0,208,32ZM80,48H208v69.38l-16.7-16.7a16,16,0,0,0-22.62,0L93.37,176H80Zm96,160H48V80H64v96a16,16,0,0,0,16,16h96ZM104,88a16,16,0,1,1,16,16A16,16,0,0,1,104,88Z"></path></svg>
|
||||
<span>
|
||||
Home
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M168,48V208a8,8,0,0,1-13.66,5.66l-80-80a8,8,0,0,1,0-11.32l80-80A8,8,0,0,1,168,48Z"></path></svg>
|
||||
|
@ -72,7 +69,7 @@
|
|||
</a>
|
||||
|
||||
<a href="{{url_for('group.groups')}}" class="navigation-item {% block nav_groups %}{% endblock %}">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M208,88v24H146.42a8.07,8.07,0,0,0-4.44,1.34l-20,13.32a8.07,8.07,0,0,1-4.44,1.34H69.42A8,8,0,0,0,62,133L32,208V64a8,8,0,0,1,8-8H93.33a8,8,0,0,1,4.8,1.6l27.74,20.8a8,8,0,0,0,4.8,1.6H200A8,8,0,0,1,208,88Z" opacity="0.2"></path><path d="M245,110.64A16,16,0,0,0,232,104H216V88a16,16,0,0,0-16-16H130.67L102.94,51.2a16.14,16.14,0,0,0-9.6-3.2H40A16,16,0,0,0,24,64V208h0a8,8,0,0,0,8,8H211.1a8,8,0,0,0,7.59-5.47l28.49-85.47A16.05,16.05,0,0,0,245,110.64ZM93.34,64l27.73,20.8a16.12,16.12,0,0,0,9.6,3.2H200v16H146.43a16,16,0,0,0-8.88,2.69l-20,13.31H69.42a15.94,15.94,0,0,0-14.86,10.06L40,166.46V64Zm112,136H43.82l25.6-64h48.16a16,16,0,0,0,8.88-2.69l20-13.31H232Z"></path></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M245,110.64A16,16,0,0,0,232,104H216V88a16,16,0,0,0-16-16H130.67L102.94,51.2a16.14,16.14,0,0,0-9.6-3.2H40A16,16,0,0,0,24,64V208h0a8,8,0,0,0,8,8H211.1a8,8,0,0,0,7.59-5.47l28.49-85.47A16.05,16.05,0,0,0,245,110.64ZM93.34,64l27.73,20.8a16.12,16.12,0,0,0,9.6,3.2H200v16H146.43a16,16,0,0,0-8.88,2.69l-20,13.31H69.42a15.94,15.94,0,0,0-14.86,10.06L40,166.46V64Z"></path></svg>
|
||||
<span>
|
||||
Groups
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M168,48V208a8,8,0,0,1-13.66,5.66l-80-80a8,8,0,0,1,0-11.32l80-80A8,8,0,0,1,168,48Z"></path></svg>
|
||||
|
@ -81,7 +78,7 @@
|
|||
|
||||
{% if g.user %}
|
||||
<button class="navigation-item {% block nav_upload %}{% endblock %}" onclick="toggleUploadTab()">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M168,80H88l40-40Z" opacity="0.2"></path><path d="M224,152v56a16,16,0,0,1-16,16H48a16,16,0,0,1-16-16V152a8,8,0,0,1,16,0v56H208V152a8,8,0,0,1,16,0ZM80.61,83.06a8,8,0,0,1,1.73-8.72l40-40a8,8,0,0,1,11.32,0l40,40A8,8,0,0,1,168,88H136v64a8,8,0,0,1-16,0V88H88A8,8,0,0,1,80.61,83.06ZM107.31,72h41.38L128,51.31Z"></path></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M74.34,77.66a8,8,0,0,1,0-11.32l48-48a8,8,0,0,1,11.32,0l48,48a8,8,0,0,1-11.32,11.32L136,43.31V128a8,8,0,0,1-16,0V43.31L85.66,77.66A8,8,0,0,1,74.34,77.66ZM240,136v64a16,16,0,0,1-16,16H32a16,16,0,0,1-16-16V136a16,16,0,0,1,16-16h68a4,4,0,0,1,4,4v3.46c0,13.45,11,24.79,24.46,24.54A24,24,0,0,0,152,128v-4a4,4,0,0,1,4-4h68A16,16,0,0,1,240,136Zm-40,32a12,12,0,1,0-12,12A12,12,0,0,0,200,168Z"></path></svg>
|
||||
<span>
|
||||
Upload
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M168,48V208a8,8,0,0,1-13.66,5.66l-80-80a8,8,0,0,1,0-11.32l80-80A8,8,0,0,1,168,48Z"></path></svg>
|
||||
|
@ -89,11 +86,11 @@
|
|||
</button>
|
||||
{% endif %}
|
||||
|
||||
<span></span>
|
||||
<span class="navigation-spacer"></span>
|
||||
|
||||
{% if g.user %}
|
||||
<a href="{{url_for('gallery.profile')}}" class="navigation-item {% block nav_profile %}{% endblock %}">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M216,176a24,24,0,1,1-24-24A24,24,0,0,1,216,176Z" opacity="0.2"></path><path d="M214.61,198.62a32,32,0,1,0-45.23,0,40,40,0,0,0-17.11,23.32,8,8,0,0,0,5.67,9.79A8.15,8.15,0,0,0,160,232a8,8,0,0,0,7.73-5.95C170.56,215.42,180.54,208,192,208s21.44,7.42,24.27,18.05a8,8,0,1,0,15.46-4.11A40,40,0,0,0,214.61,198.62ZM192,160a16,16,0,1,1-16,16A16,16,0,0,1,192,160Zm24-88H131.31L104,44.69A15.86,15.86,0,0,0,92.69,40H40A16,16,0,0,0,24,56V200.61A15.4,15.4,0,0,0,39.38,216h81.18a8,8,0,0,0,0-16H40V88H216v32a8,8,0,0,0,16,0V88A16,16,0,0,0,216,72ZM92.69,56l16,16H40V56Z"></path></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M231.73,221.94A8,8,0,0,1,224,232H160A8,8,0,0,1,152.27,222a40,40,0,0,1,17.11-23.33,32,32,0,1,1,45.24,0A40,40,0,0,1,231.73,221.94ZM216,72H130.67L102.93,51.2a16.12,16.12,0,0,0-9.6-3.2H40A16,16,0,0,0,24,64V200a16,16,0,0,0,16,16h80a8,8,0,0,0,0-16H40V64H93.33l27.74,20.8a16.12,16.12,0,0,0,9.6,3.2H216v32a8,8,0,0,0,16,0V88A16,16,0,0,0,216,72Z"></path></svg>
|
||||
<span>
|
||||
Profile
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M168,48V208a8,8,0,0,1-13.66,5.66l-80-80a8,8,0,0,1,0-11.32l80-80A8,8,0,0,1,168,48Z"></path></svg>
|
||||
|
@ -101,7 +98,7 @@
|
|||
</a>
|
||||
|
||||
<a href="{{url_for('settings.general')}}" class="navigation-item {% block nav_settings %}{% endblock %}">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M207.86,123.18l16.78-21a99.14,99.14,0,0,0-10.07-24.29l-26.7-3a81,81,0,0,0-6.81-6.81l-3-26.71a99.43,99.43,0,0,0-24.3-10l-21,16.77a81.59,81.59,0,0,0-9.64,0l-21-16.78A99.14,99.14,0,0,0,77.91,41.43l-3,26.7a81,81,0,0,0-6.81,6.81l-26.71,3a99.43,99.43,0,0,0-10,24.3l16.77,21a81.59,81.59,0,0,0,0,9.64l-16.78,21a99.14,99.14,0,0,0,10.07,24.29l26.7,3a81,81,0,0,0,6.81,6.81l3,26.71a99.43,99.43,0,0,0,24.3,10l21-16.77a81.59,81.59,0,0,0,9.64,0l21,16.78a99.14,99.14,0,0,0,24.29-10.07l3-26.7a81,81,0,0,0,6.81-6.81l26.71-3a99.43,99.43,0,0,0,10-24.3l-16.77-21A81.59,81.59,0,0,0,207.86,123.18ZM128,168a40,40,0,1,1,40-40A40,40,0,0,1,128,168Z" opacity="0.2"></path><path d="M128,80a48,48,0,1,0,48,48A48.05,48.05,0,0,0,128,80Zm0,80a32,32,0,1,1,32-32A32,32,0,0,1,128,160Zm88-29.84q.06-2.16,0-4.32l14.92-18.64a8,8,0,0,0,1.48-7.06,107.6,107.6,0,0,0-10.88-26.25,8,8,0,0,0-6-3.93l-23.72-2.64q-1.48-1.56-3-3L186,40.54a8,8,0,0,0-3.94-6,107.29,107.29,0,0,0-26.25-10.86,8,8,0,0,0-7.06,1.48L130.16,40Q128,40,125.84,40L107.2,25.11a8,8,0,0,0-7.06-1.48A107.6,107.6,0,0,0,73.89,34.51a8,8,0,0,0-3.93,6L67.32,64.27q-1.56,1.49-3,3L40.54,70a8,8,0,0,0-6,3.94,107.71,107.71,0,0,0-10.87,26.25,8,8,0,0,0,1.49,7.06L40,125.84Q40,128,40,130.16L25.11,148.8a8,8,0,0,0-1.48,7.06,107.6,107.6,0,0,0,10.88,26.25,8,8,0,0,0,6,3.93l23.72,2.64q1.49,1.56,3,3L70,215.46a8,8,0,0,0,3.94,6,107.71,107.71,0,0,0,26.25,10.87,8,8,0,0,0,7.06-1.49L125.84,216q2.16.06,4.32,0l18.64,14.92a8,8,0,0,0,7.06,1.48,107.21,107.21,0,0,0,26.25-10.88,8,8,0,0,0,3.93-6l2.64-23.72q1.56-1.48,3-3L215.46,186a8,8,0,0,0,6-3.94,107.71,107.71,0,0,0,10.87-26.25,8,8,0,0,0-1.49-7.06Zm-16.1-6.5a73.93,73.93,0,0,1,0,8.68,8,8,0,0,0,1.74,5.48l14.19,17.73a91.57,91.57,0,0,1-6.23,15L187,173.11a8,8,0,0,0-5.1,2.64,74.11,74.11,0,0,1-6.14,6.14,8,8,0,0,0-2.64,5.1l-2.51,22.58a91.32,91.32,0,0,1-15,6.23l-17.74-14.19a8,8,0,0,0-5-1.75h-.48a73.93,73.93,0,0,1-8.68,0,8.06,8.06,0,0,0-5.48,1.74L100.45,215.8a91.57,91.57,0,0,1-15-6.23L82.89,187a8,8,0,0,0-2.64-5.1,74.11,74.11,0,0,1-6.14-6.14,8,8,0,0,0-5.1-2.64L46.43,170.6a91.32,91.32,0,0,1-6.23-15l14.19-17.74a8,8,0,0,0,1.74-5.48,73.93,73.93,0,0,1,0-8.68,8,8,0,0,0-1.74-5.48L40.2,100.45a91.57,91.57,0,0,1,6.23-15L69,82.89a8,8,0,0,0,5.1-2.64,74.11,74.11,0,0,1,6.14-6.14A8,8,0,0,0,82.89,69L85.4,46.43a91.32,91.32,0,0,1,15-6.23l17.74,14.19a8,8,0,0,0,5.48,1.74,73.93,73.93,0,0,1,8.68,0,8.06,8.06,0,0,0,5.48-1.74L155.55,40.2a91.57,91.57,0,0,1,15,6.23L173.11,69a8,8,0,0,0,2.64,5.1,74.11,74.11,0,0,1,6.14,6.14,8,8,0,0,0,5.1,2.64l22.58,2.51a91.32,91.32,0,0,1,6.23,15l-14.19,17.74A8,8,0,0,0,199.87,123.66Z"></path></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M216,130.16q.06-2.16,0-4.32l14.92-18.64a8,8,0,0,0,1.48-7.06,107.6,107.6,0,0,0-10.88-26.25,8,8,0,0,0-6-3.93l-23.72-2.64q-1.48-1.56-3-3L186,40.54a8,8,0,0,0-3.94-6,107.29,107.29,0,0,0-26.25-10.86,8,8,0,0,0-7.06,1.48L130.16,40Q128,40,125.84,40L107.2,25.11a8,8,0,0,0-7.06-1.48A107.6,107.6,0,0,0,73.89,34.51a8,8,0,0,0-3.93,6L67.32,64.27q-1.56,1.49-3,3L40.54,70a8,8,0,0,0-6,3.94,107.71,107.71,0,0,0-10.87,26.25,8,8,0,0,0,1.49,7.06L40,125.84Q40,128,40,130.16L25.11,148.8a8,8,0,0,0-1.48,7.06,107.6,107.6,0,0,0,10.88,26.25,8,8,0,0,0,6,3.93l23.72,2.64q1.49,1.56,3,3L70,215.46a8,8,0,0,0,3.94,6,107.71,107.71,0,0,0,26.25,10.87,8,8,0,0,0,7.06-1.49L125.84,216q2.16.06,4.32,0l18.64,14.92a8,8,0,0,0,7.06,1.48,107.21,107.21,0,0,0,26.25-10.88,8,8,0,0,0,3.93-6l2.64-23.72q1.56-1.48,3-3L215.46,186a8,8,0,0,0,6-3.94,107.71,107.71,0,0,0,10.87-26.25,8,8,0,0,0-1.49-7.06ZM128,168a40,40,0,1,1,40-40A40,40,0,0,1,128,168Z"></path></svg>
|
||||
<span>
|
||||
Settings
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M168,48V208a8,8,0,0,1-13.66,5.66l-80-80a8,8,0,0,1,0-11.32l80-80A8,8,0,0,1,168,48Z"></path></svg>
|
||||
|
@ -109,7 +106,7 @@
|
|||
</a>
|
||||
{% else %}
|
||||
<button class="navigation-item {% block nav_login %}{% endblock %}" onclick="showLogin()">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M136,128,96,168V88Z" opacity="0.2"></path><path d="M141.66,122.34l-40-40A8,8,0,0,0,88,88v32H24a8,8,0,0,0,0,16H88v32a8,8,0,0,0,13.66,5.66l40-40A8,8,0,0,0,141.66,122.34ZM104,148.69V107.31L124.69,128ZM208,48V208a16,16,0,0,1-16,16H136a8,8,0,0,1,0-16h56V48H136a8,8,0,0,1,0-16h56A16,16,0,0,1,208,48Z"></path></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M141.66,133.66l-40,40A8,8,0,0,1,88,168V136H24a8,8,0,0,1,0-16H88V88a8,8,0,0,1,13.66-5.66l40,40A8,8,0,0,1,141.66,133.66ZM192,32H136a8,8,0,0,0,0,16h56V208H136a8,8,0,0,0,0,16h56a16,16,0,0,0,16-16V48A16,16,0,0,0,192,32Z"></path></svg>
|
||||
<span>
|
||||
Login
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M168,48V208a8,8,0,0,1-13.66,5.66l-80-80a8,8,0,0,1,0-11.32l80-80A8,8,0,0,1,168,48Z"></path></svg>
|
||||
|
@ -122,14 +119,15 @@
|
|||
<div class="upload-panel">
|
||||
<span class="click-off" onclick="closeUploadTab()"></span>
|
||||
<div class="container">
|
||||
<span id="dragIndicator"></span>
|
||||
<h3>Upload stuffs</h3>
|
||||
<p>May the world see your stuff 👀</p>
|
||||
<form id="uploadForm">
|
||||
<div class="fileDrop-block">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M232,136v64a8,8,0,0,1-8,8H32a8,8,0,0,1-8-8V136a8,8,0,0,1,8-8H224A8,8,0,0,1,232,136Z" opacity="0.2"></path><path d="M240,136v64a16,16,0,0,1-16,16H32a16,16,0,0,1-16-16V136a16,16,0,0,1,16-16H80a8,8,0,0,1,0,16H32v64H224V136H176a8,8,0,0,1,0-16h48A16,16,0,0,1,240,136ZM85.66,77.66,120,43.31V128a8,8,0,0,0,16,0V43.31l34.34,34.35a8,8,0,0,0,11.32-11.32l-48-48a8,8,0,0,0-11.32,0l-48,48A8,8,0,0,0,85.66,77.66ZM200,168a12,12,0,1,0-12,12A12,12,0,0,0,200,168Z"></path></svg>
|
||||
<button class="fileDrop-block" type="button">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M240,136v64a16,16,0,0,1-16,16H32a16,16,0,0,1-16-16V136a16,16,0,0,1,16-16H80a8,8,0,0,1,0,16H32v64H224V136H176a8,8,0,0,1,0-16h48A16,16,0,0,1,240,136ZM85.66,77.66,120,43.31V128a8,8,0,0,0,16,0V43.31l34.34,34.35a8,8,0,0,0,11.32-11.32l-48-48a8,8,0,0,0-11.32,0l-48,48A8,8,0,0,0,85.66,77.66ZM200,168a12,12,0,1,0-12,12A12,12,0,0,0,200,168Z"></path></svg>
|
||||
<span class="status">Choose or Drop file</span>
|
||||
<input type="file" id="file" onchange="fileChanged(this)" onclick="this.value=null; fileChanged(this)"/>
|
||||
</div>
|
||||
<input type="file" id="file" tab-index="-1"/>
|
||||
</button>
|
||||
|
||||
<input class="input-block" type="text" placeholder="alt" id="alt"/>
|
||||
<input class="input-block" type="text" placeholder="description" id="description"/>
|
||||
|
@ -147,12 +145,13 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
<script type="text/javascript">
|
||||
// Show notifications on page load
|
||||
{% for message in get_flashed_messages() %}
|
||||
// Show notifications on page load
|
||||
addNotification('{{ message[0] }}', '{{ message[1] }}');
|
||||
addNotification('{{ message[0] }}', {{ message[1] }});
|
||||
{% endfor %}
|
||||
</script>
|
||||
|
||||
{% block script %}{% endblock %}
|
||||
</body>
|
||||
</html>
|
|
@ -1,9 +1,9 @@
|
|||
{% extends 'layout.html' %}
|
||||
|
||||
{% block nav_profile %}navigation-item__selected{% endblock %}
|
||||
{% block nav_profile %}selected{% endblock %}
|
||||
{% block content %}
|
||||
<div class="banner">
|
||||
<img src="{{ url_for('static', filename='images/bg.svg') }}" onload="imgFade(this)" style="opacity:0;"/>
|
||||
<img src="{{ url_for('static', filename='') }}" onload="imgFade(this)" style="opacity:0;"/>
|
||||
<span></span>
|
||||
|
||||
<div class="banner-content">
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{% extends 'layout.html' %}
|
||||
|
||||
{% block nav_settings %}navigation-item__selected{% endblock %}
|
||||
{% block nav_settings %}selected{% endblock %}
|
||||
{% block content %}
|
||||
<div class="banner">
|
||||
<img src="{{ url_for('static', filename='images/bg.svg') }}" onload="imgFade(this)" style="opacity:0;"/>
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
|
||||
position: relative
|
||||
|
||||
background-color: $black
|
||||
color: $white
|
||||
background-color: RGB($bg-300)
|
||||
color: RGB($fg-white)
|
||||
|
||||
overflow: hidden
|
||||
transition: opacity 0.3s ease-in-out
|
||||
|
@ -18,7 +18,7 @@
|
|||
width: 100%
|
||||
height: 100%
|
||||
|
||||
background-color: $black2
|
||||
background-color: RGB($bg-300)
|
||||
|
||||
object-fit: cover
|
||||
object-position: center center
|
||||
|
@ -31,7 +31,7 @@
|
|||
width: 100%
|
||||
height: 100%
|
||||
|
||||
background: linear-gradient(to right, rgba($primary, 1), rgba($primary, 0))
|
||||
background: linear-gradient(to right, RGB($primary), transparent)
|
||||
|
||||
z-index: +1
|
||||
|
||||
|
@ -57,17 +57,38 @@
|
|||
font-weight: 800
|
||||
text-align: left
|
||||
|
||||
color: $primary
|
||||
color: RGB($primary)
|
||||
|
||||
p
|
||||
margin: 0
|
||||
padding: 0
|
||||
|
||||
font-size: 1rem
|
||||
font-weight: 500
|
||||
font-weight: 600
|
||||
line-height: 1
|
||||
text-align: left
|
||||
|
||||
&.small
|
||||
height: 3.5rem
|
||||
background-color: RGB($bg-100)
|
||||
|
||||
.banner-content
|
||||
padding: 0.5rem
|
||||
|
||||
flex-direction: row
|
||||
justify-content: flex-start
|
||||
align-items: center
|
||||
gap: 1rem
|
||||
|
||||
h1
|
||||
padding-bottom: 0.25rem
|
||||
font-size: 1.5rem
|
||||
text-align: left
|
||||
|
||||
p
|
||||
font-size: 0.9rem
|
||||
text-align: left
|
||||
|
||||
@media (max-width: $breakpoint)
|
||||
.banner
|
||||
width: 100vw
|
||||
|
@ -81,9 +102,18 @@
|
|||
align-items: center
|
||||
|
||||
h1
|
||||
font-size: 3.5rem
|
||||
font-size: 3rem
|
||||
text-align: center
|
||||
|
||||
p
|
||||
font-size: 1.1rem
|
||||
text-align: center
|
||||
text-align: center
|
||||
|
||||
&.small .banner-content
|
||||
justify-content: center
|
||||
|
||||
h1
|
||||
text-align: center
|
||||
|
||||
p
|
||||
display: none
|
|
@ -1,13 +1,12 @@
|
|||
@mixin btn-block($color)
|
||||
background-color: rgba($color, 0)
|
||||
color: $color
|
||||
color: RGB($color)
|
||||
|
||||
&:hover
|
||||
background-color: rgba($color, 0.2)
|
||||
color: $color
|
||||
background-color: RGBA($color, 0.1)
|
||||
color: RGB($color)
|
||||
|
||||
&:focus-visible
|
||||
outline: 2px solid rgba($color, 0.5)
|
||||
outline: 2px solid RGBA($color, 0.3)
|
||||
|
||||
.btn-block
|
||||
padding: 0.5rem 1rem
|
||||
|
@ -26,20 +25,19 @@
|
|||
font-weight: 600
|
||||
text-align: center
|
||||
|
||||
background-color: rgba($white, 0)
|
||||
color: $white
|
||||
background-color: transparent
|
||||
color: RGB($white)
|
||||
border: none
|
||||
border-radius: $rad-inner
|
||||
|
||||
cursor: pointer
|
||||
|
||||
transition: background-color 0.2s ease-in-out, color 0.2s ease-in-out
|
||||
|
||||
&:hover
|
||||
background-color: rgba($white, 0.2)
|
||||
background-color: RGBA($white, 0.1)
|
||||
|
||||
&:focus-visible
|
||||
outline: 2px solid rgba($white, 0.5)
|
||||
outline: 2px solid RGBA($white, 0.3)
|
||||
|
||||
&.primary
|
||||
@include btn-block($primary)
|
||||
|
@ -76,24 +74,24 @@
|
|||
font-weight: 600
|
||||
text-align: left
|
||||
|
||||
background-color: rgba($white, 0.1)
|
||||
color: $white
|
||||
background-color: RGBA($white, 0.1)
|
||||
color: RGB($white)
|
||||
|
||||
border: none
|
||||
border-bottom: 3px solid rgba($white, 0.1)
|
||||
border-bottom: 3px solid RGBA($white, 0.1)
|
||||
border-radius: $rad-inner
|
||||
|
||||
cursor: pointer
|
||||
transition: background-color 0.2s ease-in-out, color 0.2s ease-in-out
|
||||
|
||||
&:not(:focus):not([value=""]):not(:placeholder-shown)
|
||||
border-color: rgba($white, 0.3)
|
||||
border-color: RGBA($white, 0.3)
|
||||
|
||||
&:hover
|
||||
border-color: rgba($white, 0.3)
|
||||
border-color: RGBA($white, 0.3)
|
||||
|
||||
&:focus
|
||||
border-color: $primary
|
||||
border-color: RGB($primary)
|
||||
outline: none
|
||||
|
||||
&.black
|
||||
|
@ -117,8 +115,8 @@
|
|||
font-weight: 600
|
||||
text-align: center
|
||||
|
||||
background-color: rgba($white, 0.1)
|
||||
color: $white
|
||||
background-color: RGBA($white, 0.1)
|
||||
color: RGB($white)
|
||||
|
||||
border: none
|
||||
border-radius: $rad-inner
|
||||
|
@ -133,20 +131,23 @@
|
|||
cursor: pointer
|
||||
|
||||
&:hover
|
||||
background-color: rgba($white, 0.2)
|
||||
color: $white
|
||||
background-color: RGBA($white, 0.2)
|
||||
color: RGB($white)
|
||||
|
||||
&:focus-visible
|
||||
outline: 2px solid RGBA($white, 0.3)
|
||||
|
||||
&.active
|
||||
background-color: rgba($primary, 0.2)
|
||||
color: $primary
|
||||
background-color: RGBA($primary, 0.2)
|
||||
color: RGB($primary)
|
||||
|
||||
&.edging
|
||||
background-color: rgba($white, 0.2)
|
||||
color: $white
|
||||
background-color: RGBA($white, 0.2)
|
||||
color: RGB($white)
|
||||
|
||||
input
|
||||
display: none // So it doesnt get in the way of the drop as that breaks things
|
||||
|
||||
&.error
|
||||
background-color: rgba($critical, 0.2)
|
||||
color: $critical
|
||||
background-color: RGBA($critical, 0.2)
|
||||
color: RGB($critical)
|
||||
|
|
|
@ -13,8 +13,8 @@
|
|||
justify-content: center
|
||||
align-items: center
|
||||
|
||||
background-color: $black2
|
||||
color: $white
|
||||
background-color: RGB($bg-300)
|
||||
color: RGB($fg-white)
|
||||
border-radius: $rad
|
||||
border: none
|
||||
opacity: 0
|
||||
|
@ -24,7 +24,7 @@
|
|||
transition: all 0.2s cubic-bezier(.86, 0, .07, 1)
|
||||
|
||||
&:hover
|
||||
color: $info
|
||||
color: RGB($info)
|
||||
|
||||
svg
|
||||
margin: 0.5rem
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
display: flex
|
||||
|
||||
background-color: $black
|
||||
background-color: RGB($bg-100)
|
||||
border-radius: $rad
|
||||
|
||||
.pill-item
|
||||
|
@ -34,7 +34,7 @@
|
|||
|
||||
border: none
|
||||
background-color: transparent
|
||||
color: $white
|
||||
color: RGB($fg-white)
|
||||
|
||||
svg
|
||||
width: 1.25rem
|
||||
|
@ -43,34 +43,38 @@
|
|||
&:hover
|
||||
cursor: pointer
|
||||
|
||||
color: $primary
|
||||
color: RGB($primary)
|
||||
|
||||
.tool-tip
|
||||
opacity: 1
|
||||
top: -2.7rem
|
||||
top: -2.3rem
|
||||
transform: translateX(calc(-50% + 1.25rem ))
|
||||
|
||||
.pill__critical
|
||||
color: $critical
|
||||
color: RGB($critical)
|
||||
|
||||
span
|
||||
color: $critical
|
||||
background: RGB($critical)
|
||||
color: RGB($fg-white)
|
||||
|
||||
svg
|
||||
color: RGB($critical)
|
||||
|
||||
&:hover
|
||||
color: $white
|
||||
color: RGB($fg-white)
|
||||
|
||||
.pill__info
|
||||
color: $info
|
||||
color: RGB($info)
|
||||
|
||||
span
|
||||
color: $info
|
||||
color: RGB($info)
|
||||
|
||||
&:hover
|
||||
color: $white
|
||||
color: RGB($fg-white)
|
||||
|
||||
.tool-tip
|
||||
margin: 0
|
||||
padding: 0.5rem 0.75rem
|
||||
padding: 0.35rem 0.7rem
|
||||
|
||||
width: auto
|
||||
|
||||
|
@ -81,11 +85,11 @@
|
|||
left: 0
|
||||
transform: translateX(calc(-50% + 1.25rem ))
|
||||
|
||||
font-size: 1rem
|
||||
font-weight: 600
|
||||
font-size: 0.9rem
|
||||
font-weight: 700
|
||||
|
||||
background-color: #000000
|
||||
color: $white
|
||||
color: RGB($fg-white)
|
||||
opacity: 0
|
||||
border-radius: $rad-inner
|
||||
|
||||
|
@ -107,4 +111,8 @@
|
|||
bottom: -0.46rem
|
||||
transform: translateX(-50%)
|
||||
|
||||
color: #000000
|
||||
color: #000000
|
||||
|
||||
@media (max-width: $breakpoint)
|
||||
.tool-tip
|
||||
display: none
|
|
@ -13,8 +13,8 @@
|
|||
justify-content: center
|
||||
align-items: center
|
||||
|
||||
background-color: $black2
|
||||
color: $white
|
||||
background-color: RGB($bg-300)
|
||||
color: RGB($fg-white)
|
||||
border-radius: $rad
|
||||
border: none
|
||||
opacity: 0
|
||||
|
@ -24,7 +24,7 @@
|
|||
transition: all 0.2s cubic-bezier(.86, 0, .07, 1)
|
||||
|
||||
&:hover
|
||||
color: $primary
|
||||
color: RGB($primary)
|
||||
|
||||
svg
|
||||
margin: 0.5rem
|
||||
|
|
33
gallery/themes/default/components/elements/labels.sass
Normal file
|
@ -0,0 +1,33 @@
|
|||
.label
|
||||
padding: 0.4rem 0.7rem
|
||||
|
||||
display: block
|
||||
position: absolute
|
||||
|
||||
font-size: 0.9rem
|
||||
font-weight: 600
|
||||
|
||||
background-color: RGB($bg-dim)
|
||||
color: RGB($fg-white)
|
||||
border-radius: $rad-inner
|
||||
opacity: 0
|
||||
|
||||
transition: opacity 0.2s cubic-bezier(.76,0,.17,1), left 0.2s cubic-bezier(.76,0,.17,1)
|
||||
pointer-events: none
|
||||
z-index: 999
|
||||
|
||||
svg
|
||||
margin: 0
|
||||
font-size: 1rem
|
||||
|
||||
width: 0.75rem
|
||||
height: 0.75rem
|
||||
|
||||
display: block
|
||||
|
||||
position: absolute
|
||||
top: 50%
|
||||
left: -0.45rem
|
||||
transform: translateY(-50%)
|
||||
|
||||
color: RGB($bg-dim)
|
|
@ -1,8 +1,8 @@
|
|||
@mixin notification($color)
|
||||
color: $color
|
||||
color: RGB($color)
|
||||
|
||||
.sniffle__notification-time
|
||||
background-color: $color
|
||||
background-color: RGB($color)
|
||||
|
||||
.notifications
|
||||
margin: 0
|
||||
|
@ -33,9 +33,9 @@
|
|||
|
||||
position: relative
|
||||
|
||||
background-color: $black
|
||||
background-color: RGB($bg-300)
|
||||
border-radius: $rad-inner
|
||||
color: $white
|
||||
color: RGB($fg-white)
|
||||
opacity: 0
|
||||
transform: scale(0.8)
|
||||
|
||||
|
@ -44,6 +44,28 @@
|
|||
|
||||
transition: all 0.25s ease-in-out, opacity 0.2s ease-in-out, transform 0.2s cubic-bezier(.68,-0.55,.27,1.55)
|
||||
|
||||
&.success
|
||||
@include notification($success)
|
||||
&.warning
|
||||
@include notification($warning)
|
||||
&.critical
|
||||
@include notification($critical)
|
||||
&.info
|
||||
@include notification($info)
|
||||
|
||||
&.show
|
||||
opacity: 1
|
||||
transform: scale(1)
|
||||
|
||||
&.hide
|
||||
margin: 0
|
||||
max-height: 0
|
||||
|
||||
opacity: 0
|
||||
transform: translateX(100%)
|
||||
|
||||
transition: all 0.4s ease-in-out, max-height 0.2s ease-in-out
|
||||
|
||||
.sniffle__notification-icon
|
||||
margin: 0
|
||||
padding: 1rem
|
||||
|
@ -55,7 +77,7 @@
|
|||
justify-content: center
|
||||
align-items: center
|
||||
|
||||
background-color: $black2
|
||||
background-color: RGB($bg-200)
|
||||
|
||||
svg
|
||||
width: 1.25rem
|
||||
|
@ -89,27 +111,10 @@
|
|||
bottom: 0px
|
||||
left: 0px
|
||||
|
||||
background-color: $white
|
||||
background-color: RGB($fg-white)
|
||||
|
||||
animation: notificationTimeout 5.1s linear
|
||||
|
||||
@each $name, $colour in (success: $success, error: $critical, warning: $warning, info: $info)
|
||||
.sniffle__notification--#{$name}
|
||||
@include notification($colour)
|
||||
|
||||
.sniffle__notification-show
|
||||
opacity: 1
|
||||
transform: scale(1)
|
||||
|
||||
.sniffle__notification--hide
|
||||
margin: 0
|
||||
max-height: 0
|
||||
|
||||
opacity: 0
|
||||
transform: translateX(100%)
|
||||
|
||||
transition: all 0.4s ease-in-out, max-height 0.2s ease-in-out
|
||||
|
||||
@media (max-width: $breakpoint)
|
||||
.notifications
|
||||
width: calc(100vw - 0.6rem)
|
||||
|
@ -117,10 +122,10 @@
|
|||
|
||||
.sniffle__notification
|
||||
width: 100%
|
||||
|
||||
&.hide
|
||||
opacity: 0
|
||||
transform: translateY(-5rem)
|
||||
|
||||
.sniffle__notification-time
|
||||
width: 100%
|
||||
|
||||
.sniffle__notification--hide
|
||||
opacity: 0
|
||||
transform: translateY(-5rem)
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
display: none
|
||||
|
||||
background-color: rgba($black, 0.8)
|
||||
background-color: $bg-transparent
|
||||
opacity: 0
|
||||
|
||||
z-index: 101
|
||||
|
@ -38,14 +38,14 @@
|
|||
display: flex
|
||||
flex-direction: column
|
||||
|
||||
background-color: $black
|
||||
background-color: RGB($bg-200)
|
||||
border-radius: $rad
|
||||
overflow: hidden
|
||||
|
||||
z-index: +2
|
||||
transition: transform 0.2s $animation-smooth
|
||||
|
||||
.pop-up-content
|
||||
.pop-up-header
|
||||
margin: 0
|
||||
padding: 1rem
|
||||
|
||||
|
@ -61,7 +61,7 @@
|
|||
text-size-adjust: auto
|
||||
text-overflow: ellipsis
|
||||
|
||||
h3
|
||||
h2, h3
|
||||
margin: 0
|
||||
|
||||
width: 100%
|
||||
|
@ -73,7 +73,7 @@
|
|||
font-weight: 800
|
||||
text-align: center
|
||||
|
||||
color: $white
|
||||
color: RGB($fg-white)
|
||||
|
||||
p
|
||||
margin: 0
|
||||
|
@ -84,7 +84,7 @@
|
|||
font-weight: 500
|
||||
text-align: center
|
||||
|
||||
color: $white
|
||||
color: RGB($fg-white)
|
||||
|
||||
svg
|
||||
width: 1rem
|
||||
|
@ -99,7 +99,7 @@
|
|||
text-align: center
|
||||
line-height: 1
|
||||
|
||||
color: $primary
|
||||
color: RGB($primary)
|
||||
|
||||
cursor: pointer
|
||||
text-decoration: none
|
||||
|
@ -133,7 +133,7 @@
|
|||
|
||||
.pop-up-controlls
|
||||
margin: 0
|
||||
padding: 0.5rem
|
||||
padding: 0.25rem
|
||||
|
||||
width: 100%
|
||||
height: auto
|
||||
|
@ -141,9 +141,9 @@
|
|||
display: flex
|
||||
flex-direction: row
|
||||
justify-content: flex-end
|
||||
gap: 0.5rem
|
||||
gap: 0.25rem
|
||||
|
||||
background-color: $black2
|
||||
background-color: RGB($bg-100)
|
||||
|
||||
&.active
|
||||
opacity: 1
|
||||
|
|
|
@ -2,23 +2,26 @@
|
|||
margin: 0
|
||||
padding: 0.25rem 0.5rem
|
||||
|
||||
|
||||
display: flex
|
||||
align-items: center
|
||||
justify-content: center
|
||||
gap: 0.25rem
|
||||
|
||||
font-size: 1rem
|
||||
font-size: 0.9rem
|
||||
font-weight: 500
|
||||
text-decoration: none
|
||||
|
||||
border-radius: $rad-inner
|
||||
background-color: $primary
|
||||
color: $black
|
||||
border: none
|
||||
background-color: RGBA($primary, 0.1)
|
||||
color: RGB($primary)
|
||||
|
||||
cursor: pointer
|
||||
transition: background-color 0.2s ease-in-out, color 0.2s ease-in-out
|
||||
|
||||
svg
|
||||
width: 1.15rem
|
||||
height: 1.15rem
|
||||
height: 1.15rem
|
||||
|
||||
&:hover
|
||||
background-color: RGBA($primary, 0.3)
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
width: calc(100% - 3.5rem)
|
||||
height: 100vh
|
||||
|
||||
background-color: rgba($black, 0)
|
||||
background-color: transparent
|
||||
|
||||
overflow: hidden
|
||||
z-index: 68
|
||||
|
@ -18,10 +18,10 @@
|
|||
margin: 0
|
||||
padding: 0
|
||||
|
||||
font-size: 1.75rem
|
||||
font-size: 1.5rem
|
||||
font-weight: 700
|
||||
|
||||
color: $primary
|
||||
color: RGB($primary)
|
||||
|
||||
p
|
||||
margin: 0
|
||||
|
@ -30,7 +30,7 @@
|
|||
font-size: 1rem
|
||||
font-weight: 500
|
||||
|
||||
color: $white
|
||||
color: RGB($fg-white)
|
||||
|
||||
form
|
||||
margin: 0
|
||||
|
@ -56,7 +56,6 @@
|
|||
|
||||
z-index: +1
|
||||
|
||||
|
||||
.container
|
||||
padding: 1rem
|
||||
|
||||
|
@ -71,13 +70,38 @@
|
|||
flex-direction: column
|
||||
gap: 1rem
|
||||
|
||||
background-color: $black
|
||||
background-color: RGB($bg-200)
|
||||
opacity: 0
|
||||
|
||||
z-index: +2
|
||||
|
||||
transition: left 0.25s cubic-bezier(0.76, 0, 0.17, 1), bottom 0.25s cubic-bezier(0.76, 0, 0.17, 1), opacity 0.25s cubic-bezier(0.76, 0, 0.17, 1)
|
||||
|
||||
#dragIndicator
|
||||
display: none
|
||||
|
||||
position: absolute
|
||||
top: 0
|
||||
left: 0
|
||||
|
||||
width: 100%
|
||||
height: 5rem
|
||||
|
||||
z-index: +1
|
||||
|
||||
&::after
|
||||
content: ''
|
||||
width: 8rem
|
||||
height: 3px
|
||||
|
||||
position: absolute
|
||||
top: 0.5rem
|
||||
left: 50%
|
||||
transform: translate(-50%, -50%)
|
||||
|
||||
background-color: RGB($bg-400)
|
||||
border-radius: $rad-inner
|
||||
|
||||
.upload-jobs
|
||||
display: flex
|
||||
flex-direction: column
|
||||
|
@ -98,7 +122,7 @@
|
|||
align-items: center
|
||||
gap: 0.5rem
|
||||
|
||||
background-color: $black2
|
||||
background-color: RGB($bg-200)
|
||||
border-radius: $rad
|
||||
|
||||
overflow: hidden
|
||||
|
@ -121,7 +145,7 @@
|
|||
width: 100%
|
||||
height: 100%
|
||||
|
||||
background-image: linear-gradient(to right, rgba($black, 0.8), rgba($black, 0))
|
||||
background-image: linear-gradient(to right, RGB($bg-100), transparent)
|
||||
|
||||
.job__status
|
||||
margin: 0
|
||||
|
@ -134,7 +158,7 @@
|
|||
font-size: 1rem
|
||||
font-weight: 600
|
||||
|
||||
color: $white
|
||||
color: RGB($fg-white)
|
||||
|
||||
z-index: +3
|
||||
|
||||
|
@ -148,7 +172,7 @@
|
|||
bottom: 0
|
||||
left: -100%
|
||||
|
||||
background-color: $primary
|
||||
background-color: RGB($primary)
|
||||
|
||||
animation: uploadingLoop 1s cubic-bezier(0.76, 0, 0.17, 1) infinite
|
||||
|
||||
|
@ -157,23 +181,23 @@
|
|||
|
||||
&.critical
|
||||
.job__status, .progress
|
||||
color: $critical
|
||||
color: RGB($critical)
|
||||
&.success
|
||||
.job__status
|
||||
color: $success
|
||||
color: RGB($success)
|
||||
.progress
|
||||
height: 0
|
||||
animation: none
|
||||
&.warning
|
||||
.job__status, .progress
|
||||
color: $warning
|
||||
color: RGB($warning)
|
||||
|
||||
&.critical, &.success, &.warning
|
||||
.progress
|
||||
height: 0
|
||||
|
||||
&.open
|
||||
background-color: rgba($black, 0.5)
|
||||
background-color: $bg-transparent
|
||||
|
||||
.container
|
||||
left: 0
|
||||
|
@ -183,20 +207,24 @@
|
|||
.upload-panel
|
||||
width: 100%
|
||||
height: calc(100vh - 3.5rem)
|
||||
height: calc(100dvh - 3.5rem)
|
||||
|
||||
left: 0
|
||||
bottom: 3.5rem
|
||||
|
||||
.container
|
||||
width: 100%
|
||||
height: calc(100% - 10rem)
|
||||
height: 95%
|
||||
|
||||
left: 0
|
||||
bottom: calc(-100vh + 3.5rem)
|
||||
|
||||
border-radius: $rad $rad 0 0
|
||||
|
||||
#dragIndicator
|
||||
display: block
|
||||
|
||||
&.open
|
||||
.container
|
||||
left: 0
|
||||
bottom: 0
|
||||
bottom: 0
|
||||
|
|
|
@ -26,51 +26,40 @@
|
|||
padding: 0.5rem
|
||||
|
||||
width: 100%
|
||||
height: 100%
|
||||
height: 30%
|
||||
|
||||
position: absolute
|
||||
left: 0
|
||||
bottom: -1rem
|
||||
bottom: 0
|
||||
|
||||
display: flex
|
||||
flex-direction: column
|
||||
justify-content: flex-end
|
||||
|
||||
background-image: linear-gradient(to bottom, rgba($black, 0), rgba($black, 0.8))
|
||||
|
||||
z-index: +1
|
||||
|
||||
background-image: linear-gradient(to top, rgba($bg-100, 0.5), transparent)
|
||||
opacity: 0 // hide
|
||||
transform: scale(1.05) // scale up
|
||||
transition: all 0.3s cubic-bezier(.79, .14, .15, .86)
|
||||
|
||||
.image-title
|
||||
margin: 0
|
||||
z-index: +4
|
||||
transition: background-image 0.3s cubic-bezier(.79, .14, .15, .86), opacity 0.3s cubic-bezier(.79, .14, .15, .86)
|
||||
|
||||
font-size: 1rem
|
||||
font-weight: 600
|
||||
.image-title,
|
||||
.image-subtitle
|
||||
margin: 0
|
||||
padding: 0
|
||||
|
||||
color: $white
|
||||
white-space: nowrap
|
||||
text-overflow: ellipsis
|
||||
overflow: hidden
|
||||
|
||||
text-overflow: ellipsis
|
||||
overflow: hidden
|
||||
color: RGB($fg-white)
|
||||
|
||||
opacity: 0 // hide
|
||||
transition: all 0.2s ease-in-out
|
||||
.image-title
|
||||
font-size: 0.9rem
|
||||
font-weight: 800
|
||||
|
||||
.image-subtitle
|
||||
margin: 0
|
||||
|
||||
font-size: 0.8rem
|
||||
font-weight: 500
|
||||
|
||||
color: $white
|
||||
|
||||
text-overflow: ellipsis
|
||||
overflow: hidden
|
||||
|
||||
opacity: 0 // hide
|
||||
transition: all 0.2s ease-in-out
|
||||
.image-subtitle
|
||||
font-size: 0.8rem
|
||||
font-weight: 600
|
||||
|
||||
img
|
||||
width: 100%
|
||||
|
@ -81,11 +70,17 @@
|
|||
|
||||
object-fit: cover
|
||||
object-position: center
|
||||
|
||||
background-color: $white
|
||||
transform: scale(1.05)
|
||||
|
||||
transition: all 0.3s cubic-bezier(.79, .14, .15, .86)
|
||||
background-color: RGB($fg-white)
|
||||
filter: blur(0.5rem)
|
||||
opacity: 0
|
||||
|
||||
transition: all 0.2s cubic-bezier(.79, .14, .15, .86)
|
||||
|
||||
&.loaded
|
||||
filter: blur(0)
|
||||
opacity: 1
|
||||
|
||||
&:after
|
||||
content: ""
|
||||
|
@ -94,16 +89,155 @@
|
|||
|
||||
&:hover
|
||||
.image-filter
|
||||
bottom: 0
|
||||
opacity: 1
|
||||
transform: scale(1)
|
||||
|
||||
.image-title,
|
||||
.image-subtitle
|
||||
background-image: linear-gradient(to top, rgba($bg-100, 0.69), transparent)
|
||||
opacity: 1
|
||||
|
||||
img
|
||||
transform: scale(1)
|
||||
|
||||
.group-item
|
||||
margin: 0
|
||||
padding: 0
|
||||
|
||||
height: auto
|
||||
|
||||
position: relative
|
||||
|
||||
border-radius: $rad
|
||||
|
||||
box-sizing: border-box
|
||||
overflow: hidden
|
||||
|
||||
.image-filter
|
||||
margin: 0
|
||||
padding: 0.5rem
|
||||
|
||||
width: 100%
|
||||
height: 30%
|
||||
|
||||
position: absolute
|
||||
left: 0
|
||||
bottom: 0
|
||||
|
||||
display: flex
|
||||
flex-direction: column
|
||||
justify-content: flex-end
|
||||
|
||||
background-image: linear-gradient(to top, rgba($bg-100, 0.8), transparent)
|
||||
|
||||
z-index: +4
|
||||
|
||||
.image-title,
|
||||
.image-subtitle
|
||||
margin: 0
|
||||
padding: 0
|
||||
|
||||
white-space: nowrap
|
||||
text-overflow: ellipsis
|
||||
overflow: hidden
|
||||
|
||||
color: RGB($fg-white)
|
||||
|
||||
.image-title
|
||||
font-size: 0.9rem
|
||||
font-weight: 800
|
||||
|
||||
.image-subtitle
|
||||
font-size: 0.8rem
|
||||
font-weight: 600
|
||||
|
||||
.images
|
||||
margin: 0
|
||||
padding: 0
|
||||
|
||||
width: 100%
|
||||
height: 100%
|
||||
|
||||
position: absolute
|
||||
inset: 0
|
||||
|
||||
display: block
|
||||
|
||||
background-color: RGB($fg-white)
|
||||
|
||||
img
|
||||
width: 100%
|
||||
height: 100%
|
||||
|
||||
position: absolute
|
||||
top: 0
|
||||
left: 0
|
||||
|
||||
object-fit: cover
|
||||
object-position: center
|
||||
|
||||
background-color: RGB($fg-white)
|
||||
border-radius: $rad-inner
|
||||
box-shadow: 0 0 0.4rem 0.25rem RGBA($bg-100, 0.25)
|
||||
filter: blur(0.5rem)
|
||||
opacity: 0
|
||||
|
||||
transition: all 0.2s cubic-bezier(.79, .14, .15, .86)
|
||||
|
||||
&.loaded
|
||||
filter: blur(0)
|
||||
opacity: 1
|
||||
|
||||
&.size-1
|
||||
.data-1
|
||||
transform: scale(0.8) rotate(3deg)
|
||||
|
||||
&.size-2
|
||||
.data-1
|
||||
transform: scale(0.7) rotate(-3deg) translate(10%, 10%)
|
||||
z-index: +2
|
||||
.data-2
|
||||
transform: scale(0.7) rotate(3deg) translate(-10%, -10%)
|
||||
z-index: +1
|
||||
|
||||
&.size-3
|
||||
.data-1
|
||||
transform: scale(0.6) rotate(3deg) translate(-25%, 25%)
|
||||
z-index: +3
|
||||
.data-2
|
||||
transform: scale(0.6) rotate(-5deg) translate(25%, 10%)
|
||||
z-index: +2
|
||||
.data-3
|
||||
transform: scale(0.6) rotate(-1deg) translate(-15%, -25%)
|
||||
z-index: +1
|
||||
|
||||
&:after
|
||||
content: ""
|
||||
display: block
|
||||
padding-bottom: 100%
|
||||
|
||||
&:hover
|
||||
.images
|
||||
img
|
||||
box-shadow: 0 0 0.4rem 0.25rem RGBA($bg-100, 0.1)
|
||||
|
||||
&.size-1
|
||||
.data-1
|
||||
transform: scale(0.9) rotate(0deg)
|
||||
|
||||
&.size-2
|
||||
.data-1
|
||||
transform: scale(0.75) rotate(-1deg) translate(12%, 14%)
|
||||
z-index: +2
|
||||
.data-2
|
||||
transform: scale(0.75) rotate(1deg) translate(-12%, -10%)
|
||||
z-index: +1
|
||||
|
||||
&.size-3
|
||||
.data-1
|
||||
transform: scale(0.65) rotate(1deg) translate(-24%, 24%)
|
||||
z-index: +3
|
||||
.data-2
|
||||
transform: scale(0.65) rotate(-2deg) translate(24%, 10%)
|
||||
z-index: +2
|
||||
.data-3
|
||||
transform: scale(0.65) rotate(0deg) translate(-15%, -25%)
|
||||
z-index: +1
|
||||
|
||||
@media (max-width: 800px)
|
||||
.gallery-grid
|
||||
|
|
|
@ -6,9 +6,8 @@
|
|||
top: 0
|
||||
left: 0
|
||||
|
||||
background-color: $white
|
||||
|
||||
background-image: linear-gradient(to right, darken($white, 1%) 15%, darken($white, 10%) 35%, darken($white, 1%) 50%)
|
||||
background-color: RGB($bg-300)
|
||||
background-image: linear-gradient(to right, RGB($bg-400) 15%, RGB($bg-200) 35%, RGB($bg-400) 50%)
|
||||
background-size: 1000px 640px
|
||||
animation: imgLoading 1.8s linear infinite forwards
|
||||
|
||||
|
@ -24,7 +23,7 @@
|
|||
width: 100%
|
||||
height: 100%
|
||||
|
||||
background-color: $white
|
||||
background-color: RGB($fg-white)
|
||||
|
||||
filter: blur(1rem)
|
||||
transform: scale(1.1)
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
display: none
|
||||
opacity: 0 // hide
|
||||
|
||||
background-color: rgba($black, 0.8)
|
||||
background-color: $bg-transparent
|
||||
z-index: 21
|
||||
|
||||
box-sizing: border-box
|
||||
|
|
|
@ -4,22 +4,21 @@
|
|||
|
||||
display: flex
|
||||
flex-direction: column
|
||||
gap: 0.5rem
|
||||
gap: 0
|
||||
|
||||
background-color: $black
|
||||
background-color: RGB($bg-200)
|
||||
|
||||
overflow-y: auto
|
||||
|
||||
.info-tab
|
||||
width: 100%
|
||||
max-height: 100%
|
||||
|
||||
display: flex
|
||||
flex-direction: column
|
||||
|
||||
position: relative
|
||||
|
||||
background-color: $black
|
||||
background-color: RGB($bg-200)
|
||||
border-radius: $rad
|
||||
|
||||
transition: max-height 0.3s cubic-bezier(.79, .14, .15, .86)
|
||||
|
@ -42,7 +41,7 @@
|
|||
top: 0.6rem
|
||||
right: 0.6rem
|
||||
|
||||
color: $primary
|
||||
color: RGB($primary)
|
||||
|
||||
z-index: +2
|
||||
|
||||
|
@ -65,7 +64,7 @@
|
|||
top: 0
|
||||
z-index: +1
|
||||
|
||||
background: $black2
|
||||
background-color: RGB($bg-200)
|
||||
|
||||
svg
|
||||
margin: 0
|
||||
|
@ -74,16 +73,16 @@
|
|||
width: 1.25rem
|
||||
height: 1.25rem
|
||||
|
||||
fill: $primary
|
||||
fill: RGB($primary)
|
||||
|
||||
h2
|
||||
margin: 0
|
||||
padding: 0
|
||||
|
||||
font-size: 1.25rem
|
||||
font-size: 1.1rem
|
||||
font-weight: 600
|
||||
|
||||
color: $primary
|
||||
color: RGB($primary)
|
||||
|
||||
text-overflow: ellipsis
|
||||
overflow: hidden
|
||||
|
@ -96,12 +95,10 @@
|
|||
flex-direction: column
|
||||
gap: 1rem
|
||||
|
||||
color: $white
|
||||
color: RGB($fg-white)
|
||||
|
||||
overflow-x: hidden
|
||||
|
||||
transition: opacity 0.3s cubic-bezier(.79, .14, .15, .86)
|
||||
|
||||
p
|
||||
margin: 0
|
||||
padding: 0
|
||||
|
@ -112,11 +109,28 @@
|
|||
text-overflow: ellipsis
|
||||
overflow: hidden
|
||||
|
||||
.link
|
||||
margin: 0
|
||||
padding: 0
|
||||
|
||||
font-size: 1rem
|
||||
font-weight: 500
|
||||
text-align: center
|
||||
line-height: 1
|
||||
|
||||
color: RGB($primary)
|
||||
|
||||
cursor: pointer
|
||||
text-decoration: none
|
||||
|
||||
&:hover
|
||||
text-decoration: underline
|
||||
|
||||
table
|
||||
margin: 0
|
||||
padding: 0
|
||||
|
||||
max-width: 100%
|
||||
width: 100%
|
||||
|
||||
overflow-x: hidden
|
||||
border-collapse: collapse
|
||||
|
@ -180,11 +194,15 @@
|
|||
justify-content: center
|
||||
align-items: center
|
||||
|
||||
border-radius: 50%
|
||||
// border: 1px solid $white
|
||||
border-radius: $rad-inner
|
||||
// border: 1px solid RGB($white)
|
||||
|
||||
.img-groups
|
||||
width: 100%
|
||||
|
||||
display: flex
|
||||
gap: 0.5rem
|
||||
flex-wrap: wrap
|
||||
gap: 0.5rem
|
||||
|
||||
@media (max-width: 1100px)
|
||||
.info-container
|
||||
gap: 0.5rem
|
||||
|
|
|
@ -14,15 +14,11 @@
|
|||
top: 0
|
||||
left: 0
|
||||
|
||||
background-color: $black2
|
||||
color: $white
|
||||
background-color: RGB($bg-100)
|
||||
color: RGB($fg-white)
|
||||
|
||||
z-index: 69
|
||||
|
||||
// Spacer
|
||||
> span
|
||||
height: 100%
|
||||
|
||||
.logo
|
||||
margin: 0
|
||||
padding: 0
|
||||
|
@ -35,33 +31,44 @@
|
|||
flex-direction: row
|
||||
align-items: center
|
||||
|
||||
.navigation-spacer
|
||||
height: 100%
|
||||
|
||||
.navigation-item
|
||||
margin: 0
|
||||
padding: 1rem
|
||||
padding: 0
|
||||
|
||||
width: 3.5rem
|
||||
height: 3.5rem
|
||||
|
||||
display: flex
|
||||
flex-direction: row
|
||||
align-items: center
|
||||
min-height: 3.5rem
|
||||
|
||||
position: relative
|
||||
|
||||
background-color: $black2
|
||||
display: flex
|
||||
flex-direction: row
|
||||
justify-content: center
|
||||
align-items: center
|
||||
|
||||
background-color: transparent
|
||||
border: none
|
||||
|
||||
text-decoration: none
|
||||
|
||||
> svg
|
||||
margin: 0
|
||||
font-size: 1.5rem
|
||||
color: $white
|
||||
transition: color 0.2s ease-out
|
||||
padding: 0.5rem
|
||||
|
||||
width: 2.5rem
|
||||
height: 2.5rem
|
||||
|
||||
color: RGB($fg-white)
|
||||
border-radius: $rad-inner
|
||||
|
||||
transition: color 0.2s ease-out, transform 0.2s ease-out
|
||||
|
||||
span
|
||||
margin: 0
|
||||
padding: 0.5rem 0.75rem
|
||||
padding: 0.35rem 0.7rem
|
||||
|
||||
display: block
|
||||
|
||||
|
@ -70,11 +77,11 @@
|
|||
left: 3rem
|
||||
transform: translateY(-50%)
|
||||
|
||||
font-size: 1rem
|
||||
font-weight: 600
|
||||
font-size: 0.9rem
|
||||
font-weight: 700
|
||||
|
||||
background-color: #000000
|
||||
color: $white
|
||||
color: RGB($fg-white)
|
||||
opacity: 0
|
||||
border-radius: $rad-inner
|
||||
|
||||
|
@ -99,27 +106,30 @@
|
|||
color: #000000
|
||||
|
||||
&:hover
|
||||
background-color: $black2
|
||||
|
||||
> svg
|
||||
color: $primary
|
||||
background: RGB($bg-300)
|
||||
|
||||
span
|
||||
opacity: 1
|
||||
left: 3.9rem
|
||||
|
||||
.navigation-item__selected
|
||||
background: $primary
|
||||
|
||||
> svg
|
||||
color: $black
|
||||
|
||||
&:hover
|
||||
background: $primary
|
||||
|
||||
&.selected
|
||||
> svg
|
||||
color: $white
|
||||
color: RGB($primary)
|
||||
|
||||
&::before
|
||||
content: ''
|
||||
display: block
|
||||
|
||||
position: absolute
|
||||
top: 3px
|
||||
left: 0
|
||||
|
||||
width: 3px
|
||||
height: calc(100% - 6px)
|
||||
|
||||
background-color: RGB($primary)
|
||||
border-radius: 0 $rad-inner $rad-inner 0
|
||||
|
||||
@media (max-width: $breakpoint)
|
||||
.navigation
|
||||
|
@ -146,14 +156,17 @@
|
|||
|
||||
width: 3rem
|
||||
height: 3rem
|
||||
|
||||
border-radius: $rad-inner
|
||||
|
||||
svg
|
||||
margin: auto
|
||||
|
||||
width: 1.5rem
|
||||
height: 1.5rem
|
||||
min-height: 3rem
|
||||
|
||||
span
|
||||
display: none
|
||||
|
||||
&.selected::before
|
||||
top: unset
|
||||
bottom: 0
|
||||
left: 0
|
||||
|
||||
width: 100%
|
||||
height: 3px
|
||||
|
||||
border-radius: $rad-inner
|
|
@ -8,6 +8,7 @@
|
|||
@import "components/elements/pop-up"
|
||||
@import "components/elements/upload-panel"
|
||||
@import "components/elements/tags"
|
||||
@import "components/elements/labels"
|
||||
|
||||
@import "components/navigation"
|
||||
@import "components/banner"
|
||||
|
@ -25,6 +26,15 @@
|
|||
box-sizing: border-box
|
||||
font-family: $font
|
||||
|
||||
::-webkit-scrollbar
|
||||
width: 0.5rem
|
||||
::-webkit-scrollbar-track
|
||||
background: RGB($bg-200)
|
||||
::-webkit-scrollbar-thumb
|
||||
background: RGB($primary)
|
||||
::-webkit-scrollbar-thumb:hover
|
||||
background: RGB($fg-white)
|
||||
|
||||
html, body
|
||||
margin: 0
|
||||
padding: 0
|
||||
|
@ -32,7 +42,7 @@ html, body
|
|||
min-height: 100vh
|
||||
max-width: 100vw
|
||||
|
||||
background-color: $white
|
||||
background-color: RGB($fg-white)
|
||||
|
||||
scroll-behavior: smooth
|
||||
overflow-x: hidden
|
||||
|
@ -46,8 +56,8 @@ html, body
|
|||
display: flex
|
||||
flex-direction: column
|
||||
|
||||
background-color: $white
|
||||
color: $black
|
||||
background-color: RGB($bg-bright)
|
||||
color: RGB($bg-100)
|
||||
|
||||
.big-text
|
||||
height: 20rem
|
||||
|
@ -57,7 +67,7 @@ html, body
|
|||
justify-content: center
|
||||
align-items: center
|
||||
|
||||
color: $black
|
||||
color: RGB($bg-100)
|
||||
|
||||
h1
|
||||
margin: 0 2rem
|
||||
|
@ -83,7 +93,7 @@ html, body
|
|||
justify-content: center
|
||||
align-items: center
|
||||
|
||||
background-color: $black
|
||||
background-color: RGB($bg-bright)
|
||||
|
||||
h1
|
||||
margin: 0 2rem
|
||||
|
@ -102,10 +112,7 @@ html, body
|
|||
font-weight: 400
|
||||
text-align: center
|
||||
|
||||
color: $white
|
||||
|
||||
#contrast-check
|
||||
transition: color 0.15s ease-in-out
|
||||
color: $fg-black
|
||||
|
||||
|
||||
@media (max-width: $breakpoint)
|
||||
|
@ -126,4 +133,4 @@ html, body
|
|||
|
||||
p
|
||||
max-width: 100%
|
||||
font-size: 1rem
|
||||
font-size: 1rem
|
||||
|
|
|
@ -1,30 +1,80 @@
|
|||
$black: #151515
|
||||
$black2: #101010
|
||||
$gray: #666
|
||||
$white: #E8E3E3
|
||||
$bg-transparent: rgba(var(--bg-dim), 0.8)
|
||||
$bg-dim: var(--bg-dim)
|
||||
$bg-bright: var(--bg-bright)
|
||||
$bg-100: var(--bg-100)
|
||||
$bg-200: var(--bg-200)
|
||||
$bg-300: var(--bg-300)
|
||||
$bg-400: var(--bg-400)
|
||||
$bg-500: var(--bg-500)
|
||||
$bg-600: var(--bg-600)
|
||||
|
||||
$red: #B66467
|
||||
$orange: #D98C5F
|
||||
$yellow: #D9BC8C
|
||||
$green: #8C977D
|
||||
$blue: #8DA3B9
|
||||
$purple: #A988B0
|
||||
$fg-dim: var(--fg-dim)
|
||||
$fg-white: var(--fg-white)
|
||||
$fg-black: var(--fg-black)
|
||||
|
||||
$primary: $green
|
||||
$warning: $orange
|
||||
$critical: $red
|
||||
$success: $green
|
||||
$info: $blue
|
||||
$black: var(--black)
|
||||
$white: var(--white)
|
||||
$red: var(--red)
|
||||
$orange: var(--orange)
|
||||
$yellow: var(--yellow)
|
||||
$green: var(--green)
|
||||
$blue: var(--blue)
|
||||
$purple: var(--purple)
|
||||
|
||||
$rad: 6px
|
||||
$rad-inner: 3px
|
||||
$primary: var(--primary)
|
||||
$warning: var(--warning)
|
||||
$critical: var(--critical)
|
||||
$success: var(--success)
|
||||
$info: var(--info)
|
||||
|
||||
$animation-smooth: cubic-bezier(0.76, 0, 0.17, 1)
|
||||
$animation-bounce: cubic-bezier(.68,-0.55,.27,1.55)
|
||||
$rad: var(--rad)
|
||||
$rad-inner: var(--rad-inner)
|
||||
|
||||
$font: "Manrope", sans-serif
|
||||
$animation-smooth: var(--animation-smooth)
|
||||
$animation-bounce: var(--animation-bounce)
|
||||
|
||||
$font: 'Manrope', sans-serif
|
||||
$breakpoint: 800px
|
||||
|
||||
|
||||
\:root
|
||||
--bg-dim: 16, 16, 16
|
||||
--bg-bright: 232, 227, 227
|
||||
--bg-100: 21, 21, 21
|
||||
--bg-200: #{red(adjust-color(rgb(21, 21, 21), $lightness: 2%)), green(adjust-color(rgb(21, 21, 21), $lightness: 2%)), blue(adjust-color(rgb(21, 21, 21), $lightness: 2%))}
|
||||
--bg-300: #{red(adjust-color(rgb(21, 21, 21), $lightness: 4%)), green(adjust-color(rgb(21, 21, 21), $lightness: 4%)), blue(adjust-color(rgb(21, 21, 21), $lightness: 4%))}
|
||||
--bg-400: #{red(adjust-color(rgb(21, 21, 21), $lightness: 6%)), green(adjust-color(rgb(21, 21, 21), $lightness: 6%)), blue(adjust-color(rgb(21, 21, 21), $lightness: 6%))}
|
||||
--bg-500: #{red(adjust-color(rgb(21, 21, 21), $lightness: 8%)), green(adjust-color(rgb(21, 21, 21), $lightness: 8%)), blue(adjust-color(rgb(21, 21, 21), $lightness: 8%))}
|
||||
--bg-600: #{red(adjust-color(rgb(21, 21, 21), $lightness: 10%)), green(adjust-color(rgb(21, 21, 21), $lightness: 10%)), blue(adjust-color(rgb(21, 21, 21), $lightness: 10%))}
|
||||
|
||||
--fg-dim: 102, 102, 102
|
||||
--fg-white: 232, 227, 227
|
||||
--fg-black: 16, 16, 16
|
||||
|
||||
--black: 21, 21, 21
|
||||
--white: 232, 227, 227
|
||||
--red: 182, 100, 103
|
||||
--orange: 217, 140, 95
|
||||
--yellow: 217, 188, 140
|
||||
--green: 140, 151, 125
|
||||
--blue: 141, 163, 185
|
||||
--purple: 169, 136, 176
|
||||
|
||||
--primary: var(--green) // 183, 169, 151
|
||||
--warning: var(--orange)
|
||||
--critical: var(--red)
|
||||
--success: var(--green)
|
||||
--info: var(--blue)
|
||||
|
||||
--rad: 6px
|
||||
--rad-inner: calc(var(--rad) / 2)
|
||||
|
||||
--animation-smooth: cubic-bezier(0.76, 0, 0.17, 1)
|
||||
--animation-bounce: cubic-bezier(.68,-0.55,.27,1.55)
|
||||
|
||||
--breakpoint: 800px
|
||||
|
||||
|
||||
@font-face
|
||||
font-family: 'Work Sans'
|
||||
src: url('fonts/worksans-regular.woff2')
|
||||
|
@ -43,5 +93,5 @@ $breakpoint: 800px
|
|||
@font-face
|
||||
font-family: 'Manrope'
|
||||
src: url('fonts/Manrope[wght].woff2') format('woff2')
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
font-style: normal
|
||||
font-display: swap
|
||||
|
|
22
gallery/utils/contrast.py
Normal file
|
@ -0,0 +1,22 @@
|
|||
"""
|
||||
Calculate the contrast between two colors
|
||||
"""
|
||||
|
||||
|
||||
def contrast(background, light, dark, threshold = 0.179):
|
||||
"""
|
||||
background: tuple of (r, g, b) values
|
||||
light: color to use if the background is light
|
||||
dark: color to use if the background is dark
|
||||
threshold: the threshold to use for determining lightness, the default is w3 recommended
|
||||
"""
|
||||
red = background[0]
|
||||
green = background[1]
|
||||
blue = background[2]
|
||||
|
||||
# Calculate contrast
|
||||
uicolors = [red / 255, green / 255, blue / 255]
|
||||
cont = [col / 12.92 if col <= 0.03928 else ((col + 0.055) / 1.055) ** 2.4 for col in uicolors]
|
||||
lightness = (0.2126 * cont[0]) + (0.7152 * cont[1]) + (0.0722 * cont[2])
|
||||
|
||||
return light if lightness > threshold else dark
|
79
gallery/utils/generate_image.py
Normal file
|
@ -0,0 +1,79 @@
|
|||
"""
|
||||
Tools for generating images and thumbnails
|
||||
"""
|
||||
|
||||
import os
|
||||
import platformdirs
|
||||
from PIL import Image, ImageOps #, ImageFilter
|
||||
from werkzeug.utils import secure_filename
|
||||
|
||||
|
||||
CACHE_PATH = platformdirs.user_config_dir('onlylegs') + '/cache'
|
||||
UPLOAD_PATH = platformdirs.user_config_dir('onlylegs') + '/uploads'
|
||||
|
||||
|
||||
def generate_thumbnail(file_name, resolution, ext=None):
|
||||
"""
|
||||
Image thumbnail generator
|
||||
Uses PIL to generate a thumbnail of the image and saves it to the cache directory
|
||||
Name is the filename
|
||||
resolution: 400x400 or thumb, or any other resolution
|
||||
ext is the file extension of the image
|
||||
"""
|
||||
# Make image cache directory if it doesn't exist
|
||||
if not os.path.exists(CACHE_PATH):
|
||||
os.makedirs(CACHE_PATH)
|
||||
|
||||
# no sussy business
|
||||
file_name, file_ext = secure_filename(file_name).rsplit('.')
|
||||
if not ext:
|
||||
ext = file_ext.strip('.')
|
||||
|
||||
# PIL doesnt like jpg so we convert it to jpeg
|
||||
if ext.lower() == "jpg":
|
||||
ext = "jpeg"
|
||||
|
||||
# Set resolution based on preset resolutions
|
||||
if resolution in ['prev', 'preview']:
|
||||
res_x, res_y = (1920, 1080)
|
||||
elif resolution in ['thumb', 'thumbnail']:
|
||||
res_x, res_y = (400, 400)
|
||||
elif resolution in ['icon', 'favicon']:
|
||||
res_x, res_y = (10, 10)
|
||||
elif len(resolution.split('x')) == 2:
|
||||
res_x, res_y = resolution.split('x')
|
||||
else:
|
||||
return None
|
||||
|
||||
# If image has been already generated, return it from the cache
|
||||
if os.path.exists(os.path.join(CACHE_PATH, f'{file_name}_{res_x}x{res_y}.{ext}')):
|
||||
return os.path.join(CACHE_PATH, f'{file_name}_{res_x}x{res_y}.{ext}')
|
||||
|
||||
# Check if image exists in the uploads directory
|
||||
if not os.path.exists(os.path.join(UPLOAD_PATH, f'{file_name}.{file_ext}')):
|
||||
return None
|
||||
|
||||
# Open image and rotate it based on EXIF data and get ICC profile so colors are correct
|
||||
image = Image.open(os.path.join(UPLOAD_PATH, f'{file_name}.{file_ext}'))
|
||||
image_icc = image.info.get("icc_profile")
|
||||
img_x, img_y = image.size
|
||||
|
||||
# Resize image to fit the resolution
|
||||
image = ImageOps.exif_transpose(image)
|
||||
image.thumbnail((min(img_x, int(res_x)), min(img_y, int(res_y))), Image.ANTIALIAS)
|
||||
|
||||
# Save image to cache directory
|
||||
try:
|
||||
image.save(os.path.join(CACHE_PATH,f'{file_name}_{res_x}x{res_y}.{ext}'),
|
||||
icc_profile=image_icc)
|
||||
except OSError:
|
||||
# This usually happens when saving a JPEG with an ICC profile,
|
||||
# so we convert to RGB and try again
|
||||
image = image.convert('RGB')
|
||||
image.save(os.path.join(CACHE_PATH, f'{file_name}_{res_x}x{res_y}.{ext}'),
|
||||
icc_profile=image_icc)
|
||||
|
||||
# No need to keep the image in memory, learned the hard way
|
||||
image.close()
|
||||
|
||||
return os.path.join(CACHE_PATH, f'{file_name}_{res_x}x{res_y}.{ext}')
|
|
@ -8,75 +8,56 @@ from datetime import datetime
|
|||
import sass
|
||||
|
||||
|
||||
class CompileTheme:
|
||||
def compile_theme(theme_name, app_path):
|
||||
"""
|
||||
Compiles the theme into the static folder
|
||||
"""
|
||||
def __init__(self, theme_name, app_path):
|
||||
"""
|
||||
Initialize the theme manager
|
||||
Compiles the theme into the static folder and loads the fonts
|
||||
"""
|
||||
print(f"Loading '{theme_name}' theme...")
|
||||
|
||||
print(f"Loading '{theme_name}' theme...")
|
||||
# Set Paths
|
||||
theme_source = os.path.join(app_path, 'themes', theme_name)
|
||||
theme_destination = os.path.join(app_path, 'static', 'theme')
|
||||
|
||||
theme_path = os.path.join(app_path, 'themes', theme_name)
|
||||
theme_dest = os.path.join(app_path, 'static', 'theme')
|
||||
# If the theme doesn't exist, exit
|
||||
if not os.path.exists(theme_source):
|
||||
print("Theme does not exist!")
|
||||
sys.exit(1)
|
||||
|
||||
if not os.path.exists(theme_path):
|
||||
print("Theme does not exist!")
|
||||
sys.exit(1)
|
||||
# If the destination folder doesn't exist, create it
|
||||
if not os.path.exists(theme_destination):
|
||||
os.makedirs(theme_destination)
|
||||
|
||||
if not os.path.exists(theme_dest):
|
||||
os.makedirs(theme_dest)
|
||||
|
||||
self.load_sass(theme_path, theme_dest)
|
||||
self.load_fonts(theme_path, theme_dest)
|
||||
|
||||
print(f"{datetime.now().hour}:{datetime.now().minute}:{datetime.now().second} - Done!\n")
|
||||
|
||||
@staticmethod
|
||||
def load_sass(source_path, css_dest):
|
||||
"""
|
||||
Compile the sass (or scss) file into css and save it to the static folder
|
||||
"""
|
||||
if os.path.join(source_path, 'style.sass'):
|
||||
sass_path = os.path.join(source_path, 'style.sass')
|
||||
elif os.path.join(source_path, 'style.scss'):
|
||||
sass_path = os.path.join(source_path, 'style.scss')
|
||||
else:
|
||||
print("No sass file found!")
|
||||
sys.exit(1)
|
||||
|
||||
with open(os.path.join(css_dest, 'style.css'), encoding='utf-8', mode='w+') as file:
|
||||
try:
|
||||
file.write(sass.compile(filename=sass_path,output_style='compressed'))
|
||||
except sass.CompileError as err:
|
||||
print("Failed to compile!\n", err)
|
||||
sys.exit(1)
|
||||
|
||||
print("Compiled successfully!")
|
||||
|
||||
@staticmethod
|
||||
def load_fonts(source_path, font_dest):
|
||||
"""
|
||||
Copy the fonts folder to the static folder
|
||||
"""
|
||||
# Append fonts to the destination path
|
||||
source_path = os.path.join(source_path, 'fonts')
|
||||
font_dest = os.path.join(font_dest, 'fonts')
|
||||
|
||||
if os.path.exists(font_dest):
|
||||
try:
|
||||
shutil.rmtree(font_dest)
|
||||
except Exception as err:
|
||||
print("Failed to remove old fonts!\n", err)
|
||||
sys.exit(1)
|
||||
# Theme source file doesn't exist, exit
|
||||
if not os.path.join(theme_source, 'style.sass'):
|
||||
print("No sass file found!")
|
||||
sys.exit(1)
|
||||
|
||||
# Compile the theme
|
||||
with open(os.path.join(theme_destination, 'style.css'),
|
||||
encoding='utf-8', mode='w+') as file:
|
||||
try:
|
||||
shutil.copytree(source_path, font_dest)
|
||||
file.write(sass.compile(filename=os.path.join(theme_source, 'style.sass'),
|
||||
output_style='compressed'))
|
||||
except sass.CompileError as err:
|
||||
print("Failed to compile!\n", err)
|
||||
sys.exit(1)
|
||||
print("Compiled successfully!")
|
||||
|
||||
# If the destination folder exists, remove it
|
||||
if os.path.exists(os.path.join(theme_destination, 'fonts')):
|
||||
try:
|
||||
shutil.rmtree(os.path.join(theme_destination, 'fonts'))
|
||||
except Exception as err:
|
||||
print("Failed to copy fonts!\n", err)
|
||||
print("Failed to remove old fonts!\n", err)
|
||||
sys.exit(1)
|
||||
|
||||
# Copy the fonts
|
||||
try:
|
||||
shutil.copytree(os.path.join(theme_source, 'fonts'),
|
||||
os.path.join(theme_destination, 'fonts'))
|
||||
print("Fonts copied successfully!")
|
||||
except Exception as err:
|
||||
print("Failed to copy fonts!\n", err)
|
||||
sys.exit(1)
|
||||
|
||||
print(f"{datetime.now().hour}:{datetime.now().minute}:{datetime.now().second} - Done!\n")
|
||||
|
|
28
poetry.lock
generated
|
@ -2,14 +2,14 @@
|
|||
|
||||
[[package]]
|
||||
name = "astroid"
|
||||
version = "2.15.0"
|
||||
version = "2.15.1"
|
||||
description = "An abstract syntax tree for Python with inference support."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7.2"
|
||||
files = [
|
||||
{file = "astroid-2.15.0-py3-none-any.whl", hash = "sha256:e3e4d0ffc2d15d954065579689c36aac57a339a4679a679579af6401db4d3fdb"},
|
||||
{file = "astroid-2.15.0.tar.gz", hash = "sha256:525f126d5dc1b8b0b6ee398b33159105615d92dc4a17f2cd064125d57f6186fa"},
|
||||
{file = "astroid-2.15.1-py3-none-any.whl", hash = "sha256:89860bda98fe2bbd1f5d262229be7629d778ce280de68d95d4a73d1f592ad268"},
|
||||
{file = "astroid-2.15.1.tar.gz", hash = "sha256:af4e0aff46e2868218502789898269ed95b663fba49e65d91c1e09c966266c34"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
|
@ -641,19 +641,19 @@ tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "pa
|
|||
|
||||
[[package]]
|
||||
name = "platformdirs"
|
||||
version = "3.1.1"
|
||||
version = "3.2.0"
|
||||
description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "platformdirs-3.1.1-py3-none-any.whl", hash = "sha256:e5986afb596e4bb5bde29a79ac9061aa955b94fca2399b7aaac4090860920dd8"},
|
||||
{file = "platformdirs-3.1.1.tar.gz", hash = "sha256:024996549ee88ec1a9aa99ff7f8fc819bb59e2c3477b410d90a16d32d6e707aa"},
|
||||
{file = "platformdirs-3.2.0-py3-none-any.whl", hash = "sha256:ebe11c0d7a805086e99506aa331612429a72ca7cd52a1f0d277dc4adc20cb10e"},
|
||||
{file = "platformdirs-3.2.0.tar.gz", hash = "sha256:d5b638ca397f25f979350ff789db335903d7ea010ab28903f57b27e1b16c2b08"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
docs = ["furo (>=2022.12.7)", "proselint (>=0.13)", "sphinx (>=6.1.3)", "sphinx-autodoc-typehints (>=1.22,!=1.23.4)"]
|
||||
test = ["appdirs (==1.4.4)", "covdefaults (>=2.2.2)", "pytest (>=7.2.1)", "pytest-cov (>=4)", "pytest-mock (>=3.10)"]
|
||||
test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.2.2)", "pytest-cov (>=4)", "pytest-mock (>=3.10)"]
|
||||
|
||||
[[package]]
|
||||
name = "pylint"
|
||||
|
@ -752,14 +752,14 @@ files = [
|
|||
|
||||
[[package]]
|
||||
name = "setuptools"
|
||||
version = "67.6.0"
|
||||
version = "67.6.1"
|
||||
description = "Easily download, build, install, upgrade, and uninstall Python packages"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "setuptools-67.6.0-py3-none-any.whl", hash = "sha256:b78aaa36f6b90a074c1fa651168723acbf45d14cb1196b6f02c0fd07f17623b2"},
|
||||
{file = "setuptools-67.6.0.tar.gz", hash = "sha256:2ee892cd5f29f3373097f5a814697e397cf3ce313616df0af11231e2ad118077"},
|
||||
{file = "setuptools-67.6.1-py3-none-any.whl", hash = "sha256:e728ca814a823bf7bf60162daf9db95b93d532948c4c0bea762ce62f60189078"},
|
||||
{file = "setuptools-67.6.1.tar.gz", hash = "sha256:257de92a9d50a60b8e22abfcbb771571fde0dbf3ec234463212027a4eeecbe9a"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
|
@ -859,14 +859,14 @@ files = [
|
|||
|
||||
[[package]]
|
||||
name = "tomlkit"
|
||||
version = "0.11.6"
|
||||
version = "0.11.7"
|
||||
description = "Style preserving TOML library"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "tomlkit-0.11.6-py3-none-any.whl", hash = "sha256:07de26b0d8cfc18f871aec595fda24d95b08fef89d147caa861939f37230bf4b"},
|
||||
{file = "tomlkit-0.11.6.tar.gz", hash = "sha256:71b952e5721688937fb02cf9d354dbcf0785066149d2855e44531ebdd2b65d73"},
|
||||
{file = "tomlkit-0.11.7-py3-none-any.whl", hash = "sha256:5325463a7da2ef0c6bbfefb62a3dc883aebe679984709aee32a317907d0a8d3c"},
|
||||
{file = "tomlkit-0.11.7.tar.gz", hash = "sha256:f392ef70ad87a672f02519f99967d28a4d3047133e2d1df936511465fbb3791d"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[tool.poetry]
|
||||
name = "onlylegs"
|
||||
version = "23.03.23"
|
||||
version = "23.03.30"
|
||||
description = "Gallery built for fast and simple image management"
|
||||
authors = ["Fluffy-Bean <michal-gdula@protonmail.com>"]
|
||||
license = "MIT"
|
||||
|
@ -29,4 +29,6 @@ build-backend = "poetry.core.masonry.api"
|
|||
[tool.pylint.messages_control]
|
||||
# C0415: Flask uses it to register blueprints
|
||||
# W0718: Exception are logged so we don't need to raise them
|
||||
disable = "C0415, W0718"
|
||||
# W0621: Flask deals with this fine, so I dont care about it lol
|
||||
# R0801: Duplicate code will be dealt with later
|
||||
disable = "C0415, W0718, W0621, R0801"
|
||||
|
|
16
run.py
|
@ -6,13 +6,15 @@ from setup.configuration import Configuration
|
|||
|
||||
|
||||
print("""
|
||||
___ _ _
|
||||
/ _ \\ _ __ | |_ _| | ___ __ _ ___
|
||||
| | | | '_ \\| | | | | | / _ \\/ _` / __|
|
||||
| |_| | | | | | |_| | |__| __/ (_| \\__ \\
|
||||
\\___/|_| |_|_|\\__, |_____\\___|\\__, |___/
|
||||
|___/ |___/
|
||||
Created by Fluffy Bean - Version 23.03.23
|
||||
:::::::: :::: ::: ::: ::: ::: ::: ::::::::: ::::::::: ::::::::
|
||||
:+: :+: :+:+: :+: :+: :+: :+: :+: :+: :+: :+: :+: :+:
|
||||
+:+ +:+ :+:+:+ +:+ +:+ +:+ +:+ +:+ +:+ +:+ +:+
|
||||
+#+ +:+ +#+ +:+ +#+ +#+ +#++: +#+ +#++:++# :#: +#++:++#++
|
||||
+#+ +#+ +#+ +#+#+# +#+ +#+ +#+ +#+ +#+ +#+# +#+
|
||||
#+# #+# #+# #+#+# #+# #+# #+# #+# #+# #+# #+# #+#
|
||||
######## ### #### ########## ### ########## ######### ######### ########
|
||||
|
||||
Created by Fluffy Bean - Version 23.03.30
|
||||
""")
|
||||
|
||||
|
||||
|
|