Showing preview only (601K chars total). Download the full file or copy to clipboard to get everything.
Repository: AndrewMohawk/Aurora
Branch: master
Commit: 039dbea649db
Files: 40
Total size: 578.6 KB
Directory structure:
gitextract_hr3ssai2/
├── .gitignore
├── Aurora.py
├── README.md
├── VERSION
├── __init__.py
├── apt-requirements.txt
├── aurora.service
├── extensions/
│ ├── Aurora_Ambient_16x9.py
│ ├── Aurora_Ambient_AutoCrop.py
│ ├── Aurora_Ambient_NoCrop.py
│ ├── Aurora_AudioSpectogram.py
│ ├── Aurora_Configure.py
│ ├── Aurora_Meteor.py
│ ├── Aurora_Rainbow.py
│ ├── __init__.py
│ └── exampleExtension.py
├── install.sh
├── requirements.txt
├── update.sh
└── webserver/
├── static/
│ ├── css/
│ │ ├── bootstrap.css
│ │ └── style.css
│ ├── images/
│ │ ├── icons/
│ │ │ └── license.txt
│ │ └── undraw/
│ │ └── _license_and_link.rtf
│ ├── js/
│ │ ├── aurora-configure.js
│ │ ├── aurora-generic.js
│ │ ├── aurora-index.js
│ │ ├── aurora-view.js
│ │ ├── custom.js
│ │ └── jquery.js
│ └── menu/
│ └── menu-main.html
└── templates/
├── about.html
├── configure.html
├── footer.html
├── header.html
├── index.html
├── menu-colors.html
├── menu-footer.html
├── menu-share.html
├── status.json
└── view.html
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
#Aurora test things
colourtest_from_youtube.mp4
*.jpg
*.jpeg
env/*
.vscode/
audio.py
auroraspec.py
spec.py
nohup.out
config.ini
config.ini.bak
LEDTests/
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
.pybuilder/
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/
================================================
FILE: Aurora.py
================================================
# Main Aurora python file, runs the webserver and the Aurora client, configs are loaded from extensions directory
import cherrypy
import os
import multiprocessing
import time
import json
import cherrypy
import configparser
import threading
import glob
import importlib
import inspect
import base64
import logging
from urllib.request import urlopen
import board
import neopixel
import cv2
import sys
from shutil import copyfile
from jinja2 import Environment, FileSystemLoader
env = Environment(loader=FileSystemLoader("webserver/templates"))
class AuroraManager:
def __init__(self):
self.config_file = "./config.ini"
self.config = {} # config dict
self.extensions = {}
self.extensions_dir = False
self.current_extension = False
self.current_extension_name = False
self.current_extension_meta = False
self.screenshot_path = False
self.extension_started = False
self.loopRunning = False
self.messages = []
self.enabled = False
self.screenshot_b64 = ""
self.pixel_image_b64 = ""
self.vid = False
self.neoPixels = False
# Ironically we need to load the config to figure out the logging level, so if config fails... S.O.L
# process config file
self.loadConfig()
# Set logging
logging.basicConfig(
format="%(asctime)s %(message)s", datefmt="%m/%d/%Y %I:%M:%S %p"
)
self.debug = bool(os.environ["AURORA_DEBUG"])
logging.info(
"DEBUG OS ENV: {} status: {}".format(
os.environ["AURORA_DEBUG"], bool(os.environ["AURORA_DEBUG"])
)
)
if self.debug == True:
logging.getLogger().setLevel(logging.DEBUG)
else:
logging.info("SET DEBUG OFF")
logging.getLogger().setLevel(logging.ERROR)
# Setup NeoPixels
self.setupNeoPixels()
# Setup HDMI
self.setupHDMI()
# populate extensions
self.populateExtensions()
# set/load the extension
self.setCurrentExtension(self.current_extension_name)
def setupNeoPixels(self):
try:
# this is janky and show() speed is impacted by the number of pixels, but we cant re-init this :(
self.neoPixels = neopixel.NeoPixel(board.D18, 500, auto_write=False)
self.neoPixels.fill((0, 0, 0)) # turn them off when we initialise
self.neoPixels.show() # ironic.
except Exception as e:
# Lets not get here chaps.
self.log(
"Error during initialisation of NeoPixel:{}".format(str(e)),
)
sys.exit(1)
def setupHDMI(self):
self.vid = False
# Setup HDMI input
try:
# Try Setup Video Capture devices
for i in range(0, 10):
self.log("Trying video device {}.".format(i))
testVid = cv2.VideoCapture(i)
test, frame = testVid.read()
if test:
self.vid = testVid
self.log("Using video device {}.".format(i))
self.vid.set(cv2.CAP_PROP_SATURATION, 255)
break
else:
logging.error("device {} failed".format(i))
if self.vid == False:
self.log("Failed to initialise video device")
sys.exit(1)
self.vid.set(cv2.CAP_PROP_BUFFERSIZE, 2)
self.vid_w = int(self.vid.get(cv2.CAP_PROP_FRAME_WIDTH))
self.vid_h = int(self.vid.get(cv2.CAP_PROP_FRAME_HEIGHT))
self.log(
"Initialized Aurora with feed of {} x {}".format(self.vid_w, self.vid_h)
)
# Lets save initial values
self.config["HDMI_INITIAL"]["HDMI_BRIGHTNESS"] = str(
int(self.vid.get(cv2.CAP_PROP_BRIGHTNESS))
)
self.config["HDMI_INITIAL"]["HDMI_SATURATION"] = str(
int(self.vid.get(cv2.CAP_PROP_SATURATION))
)
self.config["HDMI_INITIAL"]["HDMI_CONTRAST"] = str(
int(self.vid.get(cv2.CAP_PROP_CONTRAST))
)
self.config["HDMI_INITIAL"]["HDMI_HUE"] = str(
int(self.vid.get(cv2.CAP_PROP_HUE))
)
self.saveConfig()
self.log(
"Default State:\nBrightness:{} Saturation: {} Contrast: {} Hue: {}".format(
int(self.vid.get(cv2.CAP_PROP_BRIGHTNESS)),
int(self.vid.get(cv2.CAP_PROP_SATURATION)),
int(self.vid.get(cv2.CAP_PROP_CONTRAST)),
int(self.vid.get(cv2.CAP_PROP_HUE)),
)
)
except Exception as e:
# Lets not get here either!
self.log("Error during initialisation of HDMI capture:{}".format(str(e)))
sys.exit(1)
# Let set the HDMI input settings
try:
self.vid.set(
cv2.CAP_PROP_SATURATION, int(self.config["HDMI"]["HDMI_SATURATION"])
)
self.vid.set(
cv2.CAP_PROP_BRIGHTNESS, int(self.config["HDMI"]["HDMI_BRIGHTNESS"])
)
self.vid.set(
cv2.CAP_PROP_CONTRAST, int(self.config["HDMI"]["HDMI_CONTRAST"])
)
self.vid.set(cv2.CAP_PROP_HUE, int(self.config["HDMI"]["HDMI_HUE"]))
except Exception as e:
# Lets not get here chaps.
self.log("Error during initialisation of HDMI:{}".format(str(e)))
sys.exit(1)
def saveConfig(self):
with open(self.config_file, "w") as configfile:
self.config.write(configfile)
self.loadConfig()
def log(self, message):
logging.info(message)
# Load the config file
def loadConfig(self):
self.config = configparser.ConfigParser()
self.config.optionxform = str
self.config.read(self.config_file)
# Lets load the enviroment variables
for key, val in self.config["AURORA"].items():
os.environ[key] = val
self.log("setting key {} to {}".format(key, val))
# Setup extensions dir
self.extensions_dir = self.config["EXTENSIONS"]["directory"]
# Set default extension
self.current_extension_name = self.config["EXTENSIONS"]["current_extension"]
# set screenshotpath
self.screenshot_path = self.config["GENERAL"]["screenshot_path"]
# set pixel image path
self.pixel_image_path = self.config["GENERAL"]["pixel_image_path"]
# set enabled flag
self.enabled = self.config.getboolean("GENERAL", "enabled")
# Get a particular extension
def getExtensionClass(self, extension_name, extension_dir):
module = importlib.import_module(
extension_dir + "." + extension_name, package=extension_name
)
importlib.reload(module)
x = False
try:
extensionClass = getattr(module, extension_name)
x = extensionClass(self.neoPixels, self.vid)
logging.info(
"Loaded: {} from ./{}/{}.py".format(
x.Name, extension_dir, extension_name
)
)
except Exception as e:
self.addMessage(
"Could not load module from ./{}/{}.py error: {}".format(
extension_dir, extension_name, str(e)
)
)
logging.info(
"Could not load module from ./{}/{}.py error: {}".format(
extension_dir, extension_name, str(e)
)
)
return x
def fetchMeta(self, extension, filename):
if extension == False:
return False
extension_meta = {}
extension_meta["Author"] = extension.Author
extension_meta["Description"] = extension.Description
extension_meta["Name"] = extension.Name
extension_meta["FileName"] = filename
return extension_meta
# Populate all the extensions from the extensions class
def populateExtensions(self):
self.extensions = {}
extension_dir = self.extensions_dir
for file in glob.glob("./{}/*.py".format(extension_dir)):
filename = os.path.splitext(os.path.basename(file))[0]
# Ignore __ files
if filename.startswith("__"):
continue
if filename not in ["exampleExtension2", "Aurora_Configure"]:
x = self.getExtensionClass(filename, extension_dir)
if x != False:
extension_meta = self.fetchMeta(x, filename)
self.extensions[filename] = extension_meta
def addMessage(self, msg):
if msg not in self.messages:
self.messages.append(msg)
# Get the current extension to be run
def getCurrentExtension(self):
os.environ["AURORA_CURRENT_EXTENSION_NAME"] = self.current_extension_name
current_extension = self.getExtensionClass(
self.current_extension_name, self.extensions_dir
)
self.current_extension = current_extension
return current_extension
def setCurrentExtension(self, new_current_extension):
tempExt = self.getExtensionClass(new_current_extension, self.extensions_dir)
if tempExt != False:
while self.loopRunning == True:
# lets wait this out or things get REEAAAL funky
time.sleep(0.001)
if self.extension_started == True:
self.tearDownExtension()
self.extension_started = False
self.current_extension = tempExt
self.current_extension_name = new_current_extension
os.environ["AURORA_CURRENT_EXTENSION_NAME"] = new_current_extension
self.current_extension_meta = self.fetchMeta(
self.current_extension, new_current_extension
)
self.setupExtension()
if new_current_extension != "Aurora_Configure":
self.config.set(
"EXTENSIONS", "current_extension", self.current_extension_name
)
self.saveConfig()
self.extension_started = True
def takeScreenshot(self):
self.current_extension.takeScreenShot(self.screenshot_path)
def makePixelImage(self):
self.current_extension.makePixelFrame(self.pixel_image_path)
def setupExtension(self):
self.current_extension.setup()
self.extension_started = True
def tearDownExtension(self):
self.extension_started = False
self.current_extension.teardown()
def loop(self):
if self.enabled == True: # only if the entire thing is enabled
if self.extension_started != False: # only loop if the extension is started
# lets let other processes know we are in the middle of a loop
self.loopRunning = True
try:
self.current_extension.visualise()
except Exception as e:
self.log("Error in visualise: {}".format(str(e)))
self.loopRunning = False
class Aurora_Webserver(object):
def __init__(self, Manager):
self.manager = Manager
@cherrypy.tools.json_out()
@cherrypy.expose
def status(self):
self.manager.loadConfig()
enabled_status = self.manager.enabled
current_extension = self.manager.current_extension.Name
current_extension_class = self.manager.current_extension_name
with open("VERSION", "r") as f:
current_version = f.read()
tmpl = env.get_template("status.json")
template_variables = {}
template_variables["current_extension"] = current_extension
template_variables["current_extension_class"] = current_extension_class
template_variables["enabled"] = enabled_status
template_variables["current_version"] = current_version
return tmpl.render(template_variables)
@cherrypy.expose
def about(self):
self.manager.loadConfig()
current_version = "Cannot read version"
with open("VERSION", "r") as f:
current_version = f.read()
github_version = "Cannot read from github"
try:
githubURL = (
"https://raw.githubusercontent.com/AndrewMohawk/Aurora/master/VERSION"
)
github_page = urlopen(githubURL)
github_version = github_page.read().decode("utf-8").strip()
except Exception as e:
self.manager.log("Exception trying to open github page:{}".format(str(e)))
# we cant connect to github?
pass
tmpl = env.get_template("about.html")
template_variables = {}
template_variables["current_version"] = current_version
template_variables["github_version"] = github_version
template_variables["extensions_meta"] = self.manager.extensions
template_variables[
"current_extension_meta"
] = self.manager.current_extension_meta
template_variables["config"] = {
section: dict(self.manager.config[section])
for section in self.manager.config.sections()
}
template_variables["page"] = "about"
template_variables["msg"] = self.manager.messages
self.manager.messages = []
return tmpl.render(template_variables)
@cherrypy.expose
def index(self):
if self.manager.current_extension_name == "Aurora_Configure":
# process config file
self.manager.loadConfig()
# set/load the extension
self.manager.setCurrentExtension(self.manager.current_extension_name)
self.manager.setupExtension()
self.manager.populateExtensions()
tmpl = env.get_template("index.html")
template_variables = {}
template_variables["extensions_meta"] = self.manager.extensions
template_variables[
"current_extension_meta"
] = self.manager.current_extension_meta
if self.manager.current_extension != False:
template_variables["fps"] = self.manager.current_extension.FPS_avg
else:
template_variables["fps"] = 0
template_variables["configured"] = self.manager.config.getboolean(
"GENERAL", "configured"
)
template_variables["enabled"] = self.manager.config.getboolean(
"GENERAL", "enabled"
)
template_variables["page"] = "home"
template_variables["msg"] = self.manager.messages
self.manager.messages = []
return tmpl.render(template_variables)
@cherrypy.expose
def view(self):
if self.manager.current_extension_name == "Aurora_Configure":
# process config file
self.manager.loadConfig()
# set/load the extension
self.manager.setCurrentExtension(self.manager.current_extension_name)
self.manager.setupExtension()
self.manager.populateExtensions()
tmpl = env.get_template("view.html")
self.screenshot()
template_variables = {}
template_variables["extensions_meta"] = self.manager.extensions
template_variables[
"current_extension_meta"
] = self.manager.current_extension_meta
if self.manager.current_extension != False:
template_variables["fps"] = self.manager.current_extension.FPS_avg
else:
template_variables["fps"] = 0
template_variables["configured"] = self.manager.config.getboolean(
"GENERAL", "configured"
)
template_variables["enabled"] = self.manager.config.getboolean(
"GENERAL", "enabled"
)
template_variables["page"] = "view"
template_variables["msg"] = self.manager.messages
self.manager.messages = []
return tmpl.render(template_variables)
@cherrypy.expose
def configure(self):
if self.manager.enabled == False: # Its turned off, we need it on to config
self.manager.enabled = True
self.manager.setCurrentExtension("Aurora_Configure")
# self.manager.setCurrentExtension("Aurora_Ambient_AutoCrop")
self.manager.extension_started = False # so it doesnt loop visualise
self.manager.current_extension.visualise()
self.screenshot()
tmpl = env.get_template("configure.html")
template_variables = {}
template_variables[
"pixels_darkthreshold"
] = self.manager.current_extension.darkThreshhold
template_variables["pixels_left"] = self.manager.current_extension.pixelsLeft
template_variables["pixels_right"] = self.manager.current_extension.pixelsRight
template_variables["pixels_top"] = self.manager.current_extension.pixelsTop
template_variables[
"pixels_bottom"
] = self.manager.current_extension.pixelsBottom
# template_variables["hdmi_saturation"] = self.manager.config.getint(
# "HDMI", "HDMI_SATURATION"
# )
# template_variables["hdmi_brightness"] = self.manager.config.getint(
# "HDMI", "HDMI_BRIGHTNESS"
# )
# template_variables["hdmi_hue"] = self.manager.config.getint("HDMI", "HDMI_HUE")
# template_variables["hdmi_contrast"] = self.manager.config.getint(
# "HDMI", "HDMI_CONTRAST"
# )
# template_variables["hdmi_brightness_default"] = int(self.manager.config["HDMI_INITIAL"]["HDMI_BRIGHTNESS"])
# template_variables["hdmi_saturation_default"] = int(self.manager.config["HDMI_INITIAL"]["HDMI_SATURATION"])
# template_variables["hdmi_contrast_default"] = int(self.manager.config["HDMI_INITIAL"]["HDMI_CONTRAST"])
# template_variables["hdmi_hue_default"] = int(self.manager.config["HDMI_INITIAL"]["HDMI_HUE"])
template_variables["hdmi_gamma"] = self.manager.current_extension.gamma
template_variables["page"] = "configure"
template_variables["msg"] = self.manager.messages
self.manager.messages = []
return tmpl.render(template_variables)
@cherrypy.tools.json_out()
@cherrypy.expose
def toggleEnable(self):
if self.manager.enabled:
self.manager.tearDownExtension()
else:
self.manager.setupExtension()
self.manager.enabled = not self.manager.enabled
return {"status": self.manager.enabled}
@cherrypy.tools.json_in()
@cherrypy.tools.json_out()
@cherrypy.expose
def update_config(self):
input_json = cherrypy.request.json
if "enabled" in input_json:
try:
return_json = {"status": "ok"}
enabled_status = input_json["enabled"]
self.manager.enabled = enabled_status
if enabled_status == False:
# we are turning it off, tear down the extension
self.manager.tearDownExtension()
return_json["message"] = "Aurora successfully turned off"
elif enabled_status == True:
# we are turning it on, lets put everything back
self.manager.setupExtension()
return_json["message"] = "Aurora successfully turned on"
self.manager.config.set("GENERAL", "enabled", str(enabled_status))
self.manager.saveConfig()
return return_json
except Exception as e:
return {"status": "error", "error": str(e)}
pass
else:
return {"status": "error", "error": "No setting found in request"}
@cherrypy.tools.json_out()
@cherrypy.tools.json_in()
@cherrypy.expose
def update_HDMI_config(self):
input_json = cherrypy.request.json
# saturation = int(input_json["hdmi_saturation"])
# hue = int(input_json["hdmi_hue"])
# contrast = int(input_json["hdmi_contrast"])
# brightness = int(input_json["hdmi_brightness"])
errors = []
try:
gamma = float(input_json["hdmi_gamma"])
self.manager.current_extension.gamma = gamma
if "save" in input_json:
self.manager.config.set("AURORA", "AURORA_GAMMA", str(gamma))
self.manager.saveConfig()
self.manager.addMessage("Saved config!")
# self.manager.vid.set(cv2.CAP_PROP_SATURATION, saturation)
# self.manager.vid.set(cv2.CAP_PROP_HUE, hue)
# self.manager.vid.set(cv2.CAP_PROP_BRIGHTNESS, brightness)
# self.manager.vid.set(cv2.CAP_PROP_CONTRAST, contrast)
# self.manager.log(
# "Brightness:{} Saturation: {} Contrast: {} Hue: {}".format(
# int(self.manager.vid.get(cv2.CAP_PROP_BRIGHTNESS)),
# int(self.manager.vid.get(cv2.CAP_PROP_SATURATION)),
# int(self.manager.vid.get(cv2.CAP_PROP_CONTRAST)),
# int(self.manager.vid.get(cv2.CAP_PROP_HUE)),
# )
# )
# for i in range(5):
# self.manager.vid.read()
self.manager.takeScreenshot()
except Exception as e:
errors.append(str(e))
pass
if len(errors) == 0:
return {"status": "ok"}
else:
error_string = ",".join(errors)
return {"status": "error", "error": error_string}
@cherrypy.tools.json_out()
@cherrypy.tools.json_in()
@cherrypy.expose
def update_LED_config(self):
input_json = cherrypy.request.json
pixelcount_left = self.manager.current_extension.pixelsLeft
pixelcount_right = self.manager.current_extension.pixelsRight
pixelcount_top = self.manager.current_extension.pixelsTop
pixelcount_bottom = self.manager.current_extension.pixelsBottom
pixel_darkthreshold = self.manager.current_extension.darkThreshhold
configChange = False
errors = []
if "darkthreshhold" in input_json:
try:
dt = int(input_json["darkthreshhold"])
if dt != pixel_darkthreshold:
configChange = True
pixel_darkthreshold = dt
except Exception as e:
errors.append(str(e))
pass # whatever, you are doing bad things with input
if "pixelcount_left" in input_json:
try:
led_input_count = int(input_json["pixelcount_left"])
if led_input_count != pixelcount_left:
configChange = True
pixelcount_left = led_input_count
except Exception as e:
errors.append(str(e))
pass # whatever, you are doing bad things with input
if "pixelcount_right" in input_json:
try:
led_input_count = int(input_json["pixelcount_right"])
if led_input_count != pixelcount_right:
configChange = True
pixelcount_right = led_input_count
except Exception as e:
errors.append(str(e))
pass # whatever, you are doing bad things with input
if "pixelcount_top" in input_json:
try:
led_input_count = int(input_json["pixelcount_top"])
if led_input_count != pixelcount_top:
configChange = True
pixelcount_top = led_input_count
except Exception as e:
errors.append(str(e))
pass # whatever, you are doing bad things with input
if "pixelcount_bottom" in input_json:
try:
led_input_count = int(input_json["pixelcount_bottom"])
if led_input_count != pixelcount_bottom:
configChange = True
pixelcount_bottom = led_input_count
except Exception as e:
errors.append(str(e))
pass # whatever, you are doing bad things with input
pixelcount_total = (
pixelcount_left + pixelcount_right + pixelcount_top + pixelcount_bottom
)
try:
self.manager.current_extension.pixelsCount = pixelcount_total
self.manager.current_extension.pixelsLeft = pixelcount_left
self.manager.current_extension.pixelsRight = pixelcount_right
self.manager.current_extension.pixelsTop = pixelcount_top
self.manager.current_extension.pixelsBottom = pixelcount_bottom
self.manager.current_extension.setup()
self.manager.current_extension.visualise()
except Exception as e:
errors.append(str(e))
if "save" in input_json:
try:
self.manager.config.set(
"AURORA", "AURORA_PIXELCOUNT_LEFT", str(pixelcount_left)
)
self.manager.config.set(
"AURORA", "AURORA_PIXELCOUNT_RIGHT", str(pixelcount_right)
)
self.manager.config.set(
"AURORA", "AURORA_PIXELCOUNT_TOP", str(pixelcount_top)
)
self.manager.config.set(
"AURORA", "AURORA_PIXELCOUNT_BOTTOM", str(pixelcount_bottom)
)
self.manager.config.set(
"AURORA", "AURORA_PIXELCOUNT_TOTAL", str(pixelcount_total)
)
self.manager.config.set(
"AURORA", "AURORA_DARKTHRESHOLD", str(pixel_darkthreshold)
)
self.manager.config.set("GENERAL", "configured", "True")
self.manager.saveConfig()
self.manager.addMessage("Saved config!")
except Exception as e:
logging.error(str(e))
errors.append(str(e))
if len(errors) == 0:
return {"status": "ok"}
else:
error_string = ",".join(errors)
return {"status": "error", "error": error_string}
@cherrypy.tools.json_out()
@cherrypy.tools.json_in()
@cherrypy.expose
def update_extension(self):
input_json = cherrypy.request.json
if "extension_name" in input_json:
extension_name = input_json["extension_name"]
self.manager.setCurrentExtension(extension_name)
return {"status": "ok"}
@cherrypy.tools.json_out()
@cherrypy.expose
def screenshot(self):
self.manager.takeScreenshot()
self.manager.makePixelImage()
if self.manager.current_extension != False:
return {"status": "ok"}
else:
return {
"status": "error",
"error": "Could not take screenshot and build pixel image",
}
@cherrypy.expose
def load_screenshot(self, **params):
screenshot_path = self.manager.screenshot_path
# Its not enabled, it doesnt use HDMI or its got a 1x1 image (ie nothing on)
if (
self.manager.enabled == False
or self.manager.current_extension.noHDMI == True
or (
self.manager.current_extension.vid_h == 1
or self.manager.current_extension.vid_w == 1
)
):
screenshot_path = os.getcwd() + "/webserver/static/img/emptyimage.jpg"
try:
f = open(screenshot_path, "rb")
contents = f.read()
f.close()
return contents
except Exception as e:
self.manager.log(
"Error loading image {}: Err: {}".format(
self.manager.screenshot_path, str(e)
)
)
return False
@cherrypy.expose
def load_pixel_image(self, **params):
try:
f = open(self.manager.pixel_image_path, "rb")
contents = f.read()
f.close()
return contents
except Exception as e:
self.manager.log(
"Error loading image {}: Err: {}".format(
self.manager.pixel_image_path, str(e)
)
)
return False
if __name__ == "__main__":
AuroraManager = AuroraManager()
if AuroraManager.config.getboolean("WEBSERVER", "enabled") == True:
conf = {
"/": {
"tools.sessions.on": True,
"tools.staticdir.root": os.path.abspath(os.getcwd()),
},
"/assets": {
"tools.staticdir.on": True,
"tools.staticdir.dir": "./webserver/static",
},
"/favicon.ico": {
"tools.staticfile.on": True,
"tools.staticfile.filename": os.path.abspath(os.getcwd())
+ "/webserver/static/favicon/favicon.ico",
},
}
cherrypy.config.update(
{"log.screen": False, "log.access_file": "", "log.error_file": ""}
)
cherrypy.config.update(
{
"server.socket_port": AuroraManager.config.getint(
"WEBSERVER", "server_port"
)
}
)
cherrypy.config.update(
{"server.socket_host": AuroraManager.config.get("WEBSERVER", "listen_host")}
)
cherrypy.config.update({"engine.autoreload.on": False})
cherrypy.tree.mount(Aurora_Webserver(AuroraManager), "/", conf)
cherrypy.engine.start()
while True:
AuroraManager.loop()
time.sleep(0.001)
# do other work
================================================
FILE: README.md
================================================
[](https://join.slack.com/t/auroraambientlighting/shared_invite/zt-sib46ode-0rE3GqXFEcHd_H_y_nG~oA)

# Aurora
Aurora is an ambient light system built with an HDMI switch, and HDMI capture card, a Raspberry Pi and an LED strip. There is a full writeup of how this came to be at https://www.andrewmohawk.com/2021/05/25/aurora-ambient-lighting/ and a build guide at https://www.andrewmohawk.com/2021/05/24/aurora-how-to-build/
# Help
Feel free to submit PRs for the project to improve the code base or add your own visualisations. Please remember to update the VERSION when you are doing a new PR. If you *need* help with any part of the project feel free to email or you can join the gitter at https://gitter.im/AuroraAmbientLighting/community
# Extending
While the documentation for extending and building your own hasnt been fully written up, the TL;DR is to copy the example extenion in the `extensions` folder, change the metadata and restart Aurora with `sudo service aurora restart`, once it has picked up the new file you can simply make changes and then click load extension in the main interface.
Please note when changing your `visualise` function you need to make it non-locking or the interface will not be able to communicate with it. The interface will pause for 0.01s before running visualise() again between each run
# Install
The install for this requires a hardware and software setup. You can follow the install guide at https://www.andrewmohawk.com/2021/05/24/aurora-how-to-build/
The flow diagram looks as follows:

For just grabbing the software you can use this one liner:
```
wget https://raw.githubusercontent.com/AndrewMohawk/Aurora/master/install.sh -O - | sudo /bin/bash
```
================================================
FILE: VERSION
================================================
0.42-catsdontlikefireworks
================================================
FILE: __init__.py
================================================
================================================
FILE: apt-requirements.txt
================================================
================================================
FILE: aurora.service
================================================
[Unit]
Description=Aurora light system
After=multi-user.target
StartLimitInterval=200
StartLimitBurst=5
[Service]
WorkingDirectory=/opt/Aurora/
User=root
Type=idle
ExecStart=/usr/bin/sudo /usr/bin/python3 Aurora.py 2>&1 > /var/log/Aurora.log
Restart=always
RestartSec=30s
[Install]
WantedBy=multi-user.target
================================================
FILE: extensions/Aurora_Ambient_16x9.py
================================================
from lib.AuroraExtension import AuroraExtension
import time
import numpy as np
import pandas as pd
import cv2
class Aurora_Ambient_16x9(AuroraExtension):
def __init__(self, NeoPixels, HDMI):
super().__init__(NeoPixels, HDMI)
self.Author = "Andrew MacPherson (@AndrewMohawk)"
self.Description = "This extension takes the HDMI input and crops the image to 16:9 (hopefully) removing the black borders"
self.Name = "Aurora Ambient Lighting (16x9)"
self.count = 0
self.current_frame = False
self.aspect_cropped_frame = False
self.percent = 3
self.aspectRatio = 16.0 / 9.0
def aspectCrop(self, image, ratio):
vid_h, vid_w, channels = image.shape
# work out the size of the 'gaps'
aspectRatioGap = vid_h - (vid_w / self.aspectRatio)
aspectRatioGap = aspectRatioGap / 2 # since we want to put some top and bottom
aspectRatioGap = int(aspectRatioGap)
aspect_frame = image[aspectRatioGap : vid_h - aspectRatioGap, 0:vid_w]
return aspect_frame
def takeScreenShot(self, filepath):
ret, self.current_frame = self.getFrame()
vid_h, vid_w, channels = self.current_frame.shape
aspectRatioGap = vid_h - (vid_w / self.aspectRatio)
aspectRatioGap = aspectRatioGap / 2 # since we want to put some top and bottom
aspectRatioGap = int(aspectRatioGap)
borderEdges = [0, aspectRatioGap, 0, aspectRatioGap]
return super().takeScreenShot(filepath, borderEdges=borderEdges)
def visualise(self):
# Capture the video frame
ret, self.current_frame = self.getFrame()
self.aspect_cropped_frame = self.aspectCrop(
self.current_frame, self.aspectRatio
)
self.visualiseFrame(self.aspect_cropped_frame)
================================================
FILE: extensions/Aurora_Ambient_AutoCrop.py
================================================
from lib.AuroraExtension import AuroraExtension
import time
import datetime
import numpy as np
import pandas as pd
import cv2
class Aurora_Ambient_AutoCrop(AuroraExtension):
def __init__(self, NeoPixels, HDMI):
super().__init__(NeoPixels, HDMI)
self.Author = "Andrew MacPherson (@AndrewMohawk)"
self.Description = "This extension takes the HDMI input and calculates a border (including cropping for black borders) to work out ambient lighting for behind the display."
self.Name = "Aurora Ambient Lighting ( AutoCrop )"
self.count = 0
self.current_frame = False
self.cropped_frame = False
self.percent = 3
self.edgeDarkness = (
5 # this defines the darkness that makes the edges for autocrop
)
def takeScreenShot(self, filepath, autocrop=False):
return super().takeScreenShot(filepath, self.edgeDarkness)
def autocrop(self, image, threshold=0):
"""Crops any edges below or equal to threshold
Crops blank image to 1x1.
Returns cropped image.
"""
if len(image.shape) == 3:
flatImage = np.max(image, 2)
else:
flatImage = image
assert len(flatImage.shape) == 2
rows = np.where(np.max(flatImage, 0) > threshold)[0]
if rows.size:
cols = np.where(np.max(flatImage, 1) > threshold)[0]
image = image[cols[0] : cols[-1] + 1, rows[0] : rows[-1] + 1]
else:
image = image[:1, :1]
return image
def visualise(self):
# Capture the video frame
# stopwatchStartTime = datetime.datetime.now()
# totalStartTime = stopwatchStartTime
ret, self.current_frame = self.getFrame()
self.vid_h, self.vid_w, self.channels = self.current_frame.shape
# self.log(f"GetFrame: {datetime.datetime.now()-stopwatchStartTime}")
# stopwatchStartTime = datetime.datetime.now()
self.autocropped_frame = self.autocrop(self.current_frame, self.edgeDarkness)
# self.log(f"AutoCropTime: {datetime.datetime.now()-stopwatchStartTime}")
self.visualiseFrame(self.autocropped_frame)
================================================
FILE: extensions/Aurora_Ambient_NoCrop.py
================================================
from lib.AuroraExtension import AuroraExtension
import time
import datetime
import numpy as np
import pandas as pd
import cv2
class Aurora_Ambient_NoCrop(AuroraExtension):
def __init__(self, NeoPixels, HDMI):
super().__init__(NeoPixels, HDMI)
self.Author = "Andrew MacPherson (@AndrewMohawk)"
self.Description = "This extension takes the HDMI input and calculates a border (WITHOUT cropping) to work out ambient lighting for behind the display. This one is slightly faster as it doesnt need to do any of the maths to work out the cropping."
self.Name = "Aurora Ambient Lighting ( No crop )"
self.count = 0
self.current_frame = False
self.percent = 3
def autocrop(self, image, threshold=0):
"""Crops any edges below or equal to threshold
Crops blank image to 1x1.
Returns cropped image.
"""
if len(image.shape) == 3:
flatImage = np.max(image, 2)
else:
flatImage = image
assert len(flatImage.shape) == 2
rows = np.where(np.max(flatImage, 0) > threshold)[0]
if rows.size:
cols = np.where(np.max(flatImage, 1) > threshold)[0]
image = image[cols[0] : cols[-1] + 1, rows[0] : rows[-1] + 1]
else:
image = image[:1, :1]
return image
def takeScreenShot(self, filepath):
screenshot_frame = self.current_frame
widthPixels = int(self.vid_w * (self.percent / 100)) + 1
heightPixels = int(self.vid_h * (self.percent / 100) * 2) + 1
colour = (0, 0, 255)
# top
screenshot_frame = cv2.rectangle(
screenshot_frame, (0, 0), (self.vid_w, heightPixels), (0, 0, 255), 1
)
# bottom
screenshot_frame = cv2.rectangle(
screenshot_frame,
(0, self.vid_h - heightPixels),
(self.vid_w, self.vid_h),
(0, 0, 255),
1,
)
# left
screenshot_frame = cv2.rectangle(
screenshot_frame, (0, 0), (widthPixels, self.vid_h), (0, 0, 255), 1
)
# right
screenshot_frame = cv2.rectangle(
screenshot_frame,
(self.vid_w - widthPixels, 0),
(self.vid_w, self.vid_h),
(0, 0, 255),
1,
)
cv2.imwrite(filepath, screenshot_frame)
return True
def visualise(self):
# Capture the video frame
ret, self.current_frame = self.getFrame()
self.visualiseFrame(self.current_frame)
================================================
FILE: extensions/Aurora_AudioSpectogram.py
================================================
from lib.AuroraExtension import AuroraExtension
import time
import datetime
import numpy as np
import pandas as pd
import sounddevice as sd
from time import sleep
import sys
from collections import deque
import math
class Aurora_AudioSpectogram(AuroraExtension):
def __init__(self, NeoPixels, HDMI):
super().__init__(NeoPixels, HDMI)
self.Author = "Andrew MacPherson (@AndrewMohawk)"
self.Description = "This extension creates a rainbow audio spectogram, based somewhat on the code found at https://python-sounddevice.readthedocs.io/en/0.3.3/examples.html"
self.Name = "Aurora Audio Spectrogram"
self.count = 0
self.current_frame = False
self.columns = 100
self.gain = 10
self.low = 10
self.high = 2500
# Lets find the right device
self.deviceID = sd.default.device["input"]
self.log("Starting Audio spectogram with Device {}".format(self.deviceID))
self.samplerate = sd.query_devices(self.deviceID, "input")["default_samplerate"]
self.delta_f = (self.high - self.low) / (self.columns - 1)
self.fftsize = math.ceil(self.samplerate / self.delta_f)
self.low_bin = math.floor(self.low / self.delta_f)
self.threshhold = 40
self.pixelCount_nobottom = self.pixelsLeft + self.pixelsRight + self.pixelsTop
self.streamstarted = False
self.noHDMI = True
def takeScreenShot(self, filepath):
# We have no screenshot since... well its just LEDs
return True
def startAudioStream(self):
if self.streamstarted == True:
while self.streamstarted == True:
with sd.InputStream(
device=self.deviceID,
channels=1,
callback=self.visualiseAudio,
blocksize=int(self.samplerate * 50 / 1000),
samplerate=self.samplerate,
):
# running stream
sd.wait()
def teardown(self):
# incase things need to be broken down
self.log("Tearing down {}".format(self.Name))
self.fade_out_pixels()
sd.stop()
def fadeToBlack(self, pixelPos):
fadeValue = 128
b, g, r = self.pixels[pixelPos]
r = 0 if r <= 10 else int(r - (r * fadeValue / 255))
g = 0 if g <= 10 else int(g - (g * fadeValue / 255))
b = 0 if b <= 10 else int(b - (b * fadeValue / 255))
self.pixels[pixelPos] = (b, g, r)
def wheel(self, pos):
# Input a value 0 to 255 to get a color value.
# The colours are a transition r - g - b - back to r.
if pos < 0 or pos > 255:
return (0, 0, 0)
if pos < 85:
return (255 - pos * 3, pos * 3, 0)
if pos < 170:
pos -= 85
return (0, 255 - pos * 3, pos * 3)
pos -= 170
return (pos * 3, 0, 255 - pos * 3)
def rainbow_cycle(self, j):
for i in range(self.pixelCount_nobottom):
rc_index = (i * 256 // self.pixelCount_nobottom) + j
self.pixels[i] = self.wheel(rc_index & 255)
self.getFrame(False) # hack to have 'FPS'
self.pixels.show()
def visualiseAudio(self, indata, frames, time, status):
if any(indata):
magnitude = np.abs(np.fft.rfft(indata[:, 0], n=self.fftsize))
magnitude *= self.gain / self.fftsize
audio_channels = []
for x in magnitude[self.low_bin : self.low_bin + self.columns]:
audio_channel_val = int(np.clip(x, 0, 1) * (255))
if audio_channel_val >= self.threshhold:
audio_channels.append(audio_channel_val)
else:
audio_channels.append(0)
d = deque(audio_channels)
d.rotate(self.pixelsLeft) # start top left? why not.
audio_channels = list(d)
chan_led_width = round(self.pixelCount_nobottom / len(audio_channels))
for key, val in enumerate(audio_channels):
first_pixel = key * chan_led_width
last_pixel = first_pixel + chan_led_width - 1
if last_pixel > self.pixelCount_nobottom:
last_pixel = self.pixelCount_nobottom - 1
for pNum in range(first_pixel, last_pixel + 1):
col = self.wheel(pNum)
if val != 0:
self.pixels[pNum] = col
else:
self.fadeToBlack(pNum)
count = 0
for x in audio_channels:
if x != 0:
count += 1
testLine = ""
for i in range(len(audio_channels)):
testLine += str(audio_channels[i]).zfill(2) + "|"
count = 0
for x in self.pixels:
if x != [0, 0, 0]:
count += 1
self.pixels.show()
else:
self.log("No Audio data in")
def visualise(self):
with sd.InputStream(
device=self.deviceID,
channels=1,
callback=self.visualiseAudio,
blocksize=int(self.samplerate * 50 / 1000),
samplerate=self.samplerate,
):
# running stream
sd.wait()
time.sleep(0.01)
================================================
FILE: extensions/Aurora_Configure.py
================================================
from lib.AuroraExtension import AuroraExtension
import neopixel
import time
import datetime
import numpy as np
import pandas as pd
import board
import cv2
import sys
class Aurora_Configure(AuroraExtension):
def __init__(self, NeoPixels, HDMI):
super().__init__(NeoPixels, HDMI)
self.Author = "Andrew MacPherson (@AndrewMohawk)"
self.Description = "This extension configures the LEDs"
self.Name = "Aurora Ambient Lighting Configuration"
self.count = 0
self.current_frame = False
self.percent = 3
def setup(self):
return True
def visualise(self):
# Capture the video frame
ret, self.current_frame = self.getFrame()
self.vid_h, self.vid_w, self.channels = self.current_frame.shape
pos = 0
colour = (255, 0, 0) # red
for i in range(pos, pos + self.pixelsLeft):
self.pixels[i] = colour
pos = pos + self.pixelsLeft
colour = (0, 255, 0) # green
for x in range(pos, pos + self.pixelsTop):
self.pixels[x] = colour
pos = pos + self.pixelsTop
colour = (0, 0, 255) # blue
for y in range(pos, pos + self.pixelsRight):
self.pixels[y] = colour
pos = pos + self.pixelsRight
colour = (255, 255, 255) # white
for z in range(pos, pos + self.pixelsBottom):
self.pixels[z] = colour
self.pixels.show()
================================================
FILE: extensions/Aurora_Meteor.py
================================================
from lib.AuroraExtension import AuroraExtension
import time
import datetime
import numpy as np
import pandas as pd
import cv2
from random import randint
class Aurora_Meteor(AuroraExtension):
def __init__(self, NeoPixels, HDMI):
super().__init__(NeoPixels, HDMI)
self.Author = "Andrew MacPherson (@AndrewMohawk)"
self.Description = (
"This has a 'meteor' effect in a random colour that shoots around the strip"
)
self.Name = "Aurora Meteor Display ( LED Only )"
self.count = 0
self.current_frame = False
self.meteorSize = 10
self.currentCol = (255, 0, 0)
self.noHDMI = True
self.startTime = time.time()
def takeScreenShot(self, filepath):
# We have no screenshot since... well its just LEDs
return True
def fadeToBlack(self, pixelPos):
fadeValue = 64
b, g, r = self.pixels[pixelPos]
r = 0 if r <= 10 else int(r - (r * fadeValue / 255))
g = 0 if g <= 10 else int(g - (g * fadeValue / 255))
b = 0 if b <= 10 else int(b - (b * fadeValue / 255))
self.pixels[pixelPos] = (b, g, r)
def meteorRain(self, i, col):
# Fade out
for j in range(self.pixelsCount):
if randint(0, 10) > 5:
self.fadeToBlack(j)
for j in range(self.meteorSize):
if (i - j < self.pixelsCount) and (i - j >= 0):
self.pixels[i - j] = col
self.pixels.show()
def visualise(self):
# visualise!
self.count += 5
self.meteorRain(self.count, self.currentCol)
if self.count >= self.pixelsCount + self.meteorSize:
end = time.time()
diff = end - self.startTime
self.startTime = time.time()
self.count = 0
self.currentCol = (randint(0, 255), randint(0, 255), randint(0, 255))
================================================
FILE: extensions/Aurora_Rainbow.py
================================================
from lib.AuroraExtension import AuroraExtension
import time
import datetime
import numpy as np
import pandas as pd
import cv2
class Aurora_Rainbow(AuroraExtension):
def __init__(self, NeoPixels, HDMI):
super().__init__(NeoPixels, HDMI)
self.Author = "Andrew MacPherson (@AndrewMohawk)"
self.Description = "This extension displays a rainbow pattern on the LEDs, a lot of this code is from https://learn.adafruit.com/circuitpython-essentials/circuitpython-neopixel"
self.Name = "Aurora Rainbow Display ( LED Only )"
self.count = 0
self.current_frame = False
self.noHDMI = True
def takeScreenShot(self, filepath):
# We have no screenshot since... well its just LEDs
return True
def wheel(self, pos):
# Input a value 0 to 255 to get a color value.
# The colours are a transition r - g - b - back to r.
if pos < 0 or pos > 255:
return (0, 0, 0)
if pos < 85:
return (255 - pos * 3, pos * 3, 0)
if pos < 170:
pos -= 85
return (0, 255 - pos * 3, pos * 3)
pos -= 170
return (pos * 3, 0, 255 - pos * 3)
def rainbow_cycle(self, j):
for i in range(self.pixelsCount):
rc_index = (i * 256 // self.pixelsCount) + j
self.pixels[i] = self.wheel(rc_index & 255)
self.getFrame(False) # hack to have 'FPS'
self.pixels.show()
def visualise(self):
# visualise!
self.count += 1
self.rainbow_cycle(self.count)
if self.count == 255:
self.count = 0
time.sleep(0.01)
================================================
FILE: extensions/__init__.py
================================================
================================================
FILE: extensions/exampleExtension.py
================================================
from lib.AuroraExtension import AuroraExtension
import time
from random import randint, choice
import numpy as np
import cv2
class exampleExtension(AuroraExtension):
def __init__(self, NeoPixels, HDMI):
super().__init__(NeoPixels, HDMI)
self.Author = "Andrew MacPherson (@AndrewMohawk)"
self.Description = "Extension Example"
self.Name = "example extension"
self.count = 0
self.randomLED = 0
self.randomSize = 0
self.log("Extension {} Initiated".format(self.Name))
# This doesnt need to be implemented, but can if you want
# def takeScreenShot(self, filepath):
# Fade LEDs to Black (so they dont instantly drop off)
def fadeToBlack(self, pixelPos):
fadeValue = 0.2
b, g, r = self.pixels[pixelPos]
r = 0 if r <= 10 else int(r - (r * fadeValue))
g = 0 if g <= 10 else int(g - (g * fadeValue))
b = 0 if b <= 10 else int(b - (b * fadeValue))
self.pixels[pixelPos] = (b, g, r)
# Fade LEDs to bright (or first block to 255) so they dont just spotlight
def fadeToBright(self, pixelPos):
fadeValue = 1.2
b, g, r = self.pixels[pixelPos]
if r == 255 or g == 255 or b == 255:
return
r = 255 if r >= 200 else int(r + (r * fadeValue))
g = 255 if g >= 200 else int(g + (g * fadeValue))
b = 255 if b >= 200 else int(b + (b * fadeValue))
self.pixels[pixelPos] = (b, g, r)
# This will 'visualise' whatever you want and will be called every ~0.01s
def visualise(self):
# Grab a 'frame' of the screen:
ret, self.current_frame = self.getFrame()
# Theres something on the screen
if ret:
self.count = self.count + 1
if self.count <= 1:
if self.count == 1:
self.log("Setting up colours")
# Lets get a random size for our group of pixels between 5 and 50
self.randomSize = randint(5, 50)
# Lets pick a random LED to turn on
self.randomLED = randint(0, self.pixelsCount - self.randomSize)
# Lets get a random starting colour that we will scale up
self.colour = (randint(0, 10), randint(0, 10), randint(0, 10))
# Set the colour
for x in range(self.randomLED, self.randomLED + self.randomSize):
self.pixels[x] = self.colour
elif self.count < 20:
if self.count == 2:
self.log("Fading up colours")
# Fade it up
for fade_up_pixel in range(
self.randomLED, (self.randomLED + self.randomSize)
):
self.fadeToBright(fade_up_pixel)
elif self.count < 40:
if self.count == 21:
self.log("Fading out colours")
for fade_out_pixel in range(
self.randomLED, (self.randomLED + self.randomSize)
):
self.fadeToBlack(fade_out_pixel)
else:
if self.count == 40:
self.log("Resetting colours")
self.count = 0
# And show it
self.pixels.show()
================================================
FILE: install.sh
================================================
#!/bin/bash
echo -e "
▄▄▄ █ ██ ██▀███ ▒█████ ██▀███ ▄▄▄
▒████▄ ██ ▓██▒▓██ ▒ ██▒▒██▒ ██▒▓██ ▒ ██▒▒████▄
▒██ ▀█▄ ▓██ ▒██░▓██ ░▄█ ▒▒██░ ██▒▓██ ░▄█ ▒▒██ ▀█▄
░██▄▄▄▄██ ▓▓█ ░██░▒██▀▀█▄ ▒██ ██░▒██▀▀█▄ ░██▄▄▄▄██
▓█ ▓██▒▒▒█████▓ ░██▓ ▒██▒░ ████▓▒░░██▓ ▒██▒ ▓█ ▓██▒
▒▒ ▓▒█░░▒▓▒ ▒ ▒ ░ ▒▓ ░▒▓░░ ▒░▒░▒░ ░ ▒▓ ░▒▓░ ▒▒ ▓▒█░
▒ ▒▒ ░░░▒░ ░ ░ ░▒ ░ ▒░ ░ ▒ ▒░ ░▒ ░ ▒░ ▒ ▒▒ ░
░ ▒ ░░░ ░ ░ ░░ ░ ░ ░ ░ ▒ ░░ ░ ░ ▒
░ ░ ░ ░ ░ ░ ░ ░ ░
-Install Script-
@AndrewMohawk
"
INSTALLDIR="/opt/Aurora"
if [[ $EUID -ne 0 ]]; then
echo "This script must be run as root, please use sudo"
exit 1
fi
if [ -d "$INSTALLDIR" ]
then
echo "Directory $INSTALLDIR exists. Please remove before performing a clean install"
exit 1
fi
echo "[+] Creating Install Directory"
echo "------------------------------"
mkdir $INSTALLDIR
cd $INSTALLDIR
echo "[+] Updating APT and installing dependencies"
echo "------------------------------"
apt update
apt-get install -y libatlas-base-dev libportaudio2 python3-pip git python3-opencv
echo "[+] Git Cloning Aurora base"
echo "------------------------------"
git clone https://github.com/AndrewMohawk/Aurora.git .
cp config.ini.bak config.ini
echo "[+] Installing Python Requirements"
echo "------------------------------"
pip3 install -r requirements.txt
echo "[+] Installing Service"
echo "------------------------------"
cp aurora.service /etc/systemd/system
systemctl enable aurora.service
systemctl start aurora.service
echo "[+] Service Status"
echo "------------------------------"
echo "Aurora status: `systemctl is-active aurora.service`"
echo "[+] Last 20 lines of aurora log"
echo "------------------------------"
journalctl -u aurora -n 20 --no-pager
echo -e "
___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___
(___|___|___|___|___|___|___|___|___|___|___|___)
"
echo "Complete. You can now browse to the web interface to configure any changes you may need."
localip=$(hostname -I)
echo "This will likely be: http://$localip"
================================================
FILE: requirements.txt
================================================
numpy >= 1.16.5
pandas
opencv-python==4.5.1.48
cherrypy
configparser
adafruit-circuitpython-neopixel
blinker
jinja2
sounddevice
pyaudio
================================================
FILE: update.sh
================================================
#!/bin/bash
echo -e "
▄▄▄ █ ██ ██▀███ ▒█████ ██▀███ ▄▄▄
▒████▄ ██ ▓██▒▓██ ▒ ██▒▒██▒ ██▒▓██ ▒ ██▒▒████▄
▒██ ▀█▄ ▓██ ▒██░▓██ ░▄█ ▒▒██░ ██▒▓██ ░▄█ ▒▒██ ▀█▄
░██▄▄▄▄██ ▓▓█ ░██░▒██▀▀█▄ ▒██ ██░▒██▀▀█▄ ░██▄▄▄▄██
▓█ ▓██▒▒▒█████▓ ░██▓ ▒██▒░ ████▓▒░░██▓ ▒██▒ ▓█ ▓██▒
▒▒ ▓▒█░░▒▓▒ ▒ ▒ ░ ▒▓ ░▒▓░░ ▒░▒░▒░ ░ ▒▓ ░▒▓░ ▒▒ ▓▒█░
▒ ▒▒ ░░░▒░ ░ ░ ░▒ ░ ▒░ ░ ▒ ▒░ ░▒ ░ ▒░ ▒ ▒▒ ░
░ ▒ ░░░ ░ ░ ░░ ░ ░ ░ ░ ▒ ░░ ░ ░ ▒
░ ░ ░ ░ ░ ░ ░ ░ ░
-Update Script-
@AndrewMohawk
"
INSTALLDIR="/opt/Aurora"
if [[ $EUID -ne 0 ]]; then
echo "This script must be run as root, please use sudo"
exit 1
fi
if [ ! -d "$INSTALLDIR" ]
then
echo "Directory $INSTALLDIR Does not exist. Please run install first or update the install directory"
exit 1
fi
cd $INSTALLDIR
if [ -d .git ]; then
echo "[+] Git Cloning Aurora base"
echo "------------------------------"
git pull
echo "[+] Stopping Aurora"
echo "------------------------------"
service aurora stop
echo "[+] Service Status"
echo "------------------------------"
echo "Aurora status: `systemctl is-active aurora.service`"
echo "[+] Starting Aurora"
echo "------------------------------"
service aurora start
echo "[+] Service Status"
echo "------------------------------"
echo "Aurora status: `systemctl is-active aurora.service`"
echo "[+] Last 20 lines of aurora log"
echo "------------------------------"
journalctl -u aurora -n 20 --no-pager
echo -e "
___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___
(___|___|___|___|___|___|___|___|___|___|___|___)
"
echo "Complete. You can now browse to the web interface to configure any changes you may need."
localip=$(hostname -I)
echo "This will likely be: http://$localip"
else
echo "This install directory ($INSTALLDIR) is *NOT* a git repo."
fi;
================================================
FILE: webserver/static/css/bootstrap.css
================================================
/*!
* Bootstrap v4.5 (https://getbootstrap.com/)
* Copyright 2011-2019 The Bootstrap Authors
* Copyright 2011-2019 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
*/
html {
box-sizing: border-box;
-ms-overflow-style: scrollbar;
}
*,
*::before,
*::after {
box-sizing: inherit;
}
:root {
--blue: #007bff;
--indigo: #6610f2;
--purple: #6f42c1;
--pink: #e83e8c;
--red: #dc3545;
--orange: #fd7e14;
--yellow: #ffc107;
--green: #28a745;
--teal: #20c997;
--cyan: #17a2b8;
--white: #fff;
--gray: #6c757d;
--gray-dark: #343a40;
--primary: #007bff;
--secondary: #6c757d;
--success: #28a745;
--info: #17a2b8;
--warning: #ffc107;
--danger: #dc3545;
--light: #f8f9fa;
--dark: #343a40;
--breakpoint-xs: 0;
--breakpoint-sm: 576px;
--breakpoint-md: 768px;
--breakpoint-lg: 992px;
--breakpoint-xl: 1200px;
--font-family-sans-serif: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
--font-family-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
}
*,
*::before,
*::after {
box-sizing: border-box;
}
html {
font-family: sans-serif;
line-height: 1.15;
-webkit-text-size-adjust: 100%;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
article, aside, figcaption, figure, footer, header, hgroup, main, nav, section {
display: block;
}
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
font-size: 1rem;
font-weight: 400;
line-height: 1.5;
color: #212529;
text-align: left;
background-color: #fff;
}
[tabindex="-1"]:focus:not(:focus-visible) {
outline: 0 !important;
}
hr {
box-sizing: content-box;
height: 0;
overflow: visible;
}
h1, h2, h3, h4, h5, h6 {
margin-top: 0;
margin-bottom: 0.46875rem;
}
p {
margin-top: 0;
margin-bottom: 1rem;
}
abbr[title],
abbr[data-original-title] {
text-decoration: underline;
text-decoration: underline dotted;
cursor: help;
border-bottom: 0;
text-decoration-skip-ink: none;
}
address {
margin-bottom: 1rem;
font-style: normal;
line-height: inherit;
}
ol,
ul,
dl {
margin-top: 0;
margin-bottom: 1rem;
}
ol ol,
ul ul,
ol ul,
ul ol {
margin-bottom: 0;
}
dt {
font-weight: 700;
}
dd {
margin-bottom: .5rem;
margin-left: 0;
}
blockquote {
margin: 0 0 1rem;
}
b,
strong {
font-weight: bolder;
}
small {
font-size: 80%;
}
sub,
sup {
position: relative;
font-size: 75%;
line-height: 0;
vertical-align: baseline;
}
sub {
bottom: -.25em;
}
sup {
top: -.5em;
}
a {
color: #007bff;
text-decoration: none;
background-color: transparent;
}
a:hover {
color: #0056b3;
text-decoration: underline;
}
a:not([href]) {
color: inherit;
text-decoration: none;
}
a:not([href]):hover {
color: inherit;
text-decoration: none;
}
pre,
code,
kbd,
samp {
font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
font-size: 1em;
}
pre {
margin-top: 0;
margin-bottom: 1rem;
overflow: auto;
-ms-overflow-style: scrollbar;
}
figure {
margin: 0 0 1rem;
}
img {
vertical-align: middle;
border-style: none;
}
svg {
overflow: hidden;
vertical-align: middle;
}
table {
border-collapse: collapse;
}
caption {
padding-top: 0.75rem;
padding-bottom: 0.75rem;
color: #6c757d;
text-align: left;
caption-side: bottom;
}
th {
text-align: inherit;
}
label {
display: inline-block;
margin-bottom: 0.5rem;
}
button {
border-radius: 0;
}
button:focus {
outline: 1px dotted;
outline: 5px auto -webkit-focus-ring-color;
}
input,
button,
select,
optgroup,
textarea {
margin: 0;
font-family: inherit;
font-size: inherit;
line-height: inherit;
}
button,
input {
overflow: visible;
}
button,
select {
text-transform: none;
}
[role="button"] {
cursor: pointer;
}
select {
word-wrap: normal;
}
button,
[type="button"],
[type="reset"],
[type="submit"] {
-webkit-appearance: button;
}
button:not(:disabled),
[type="button"]:not(:disabled),
[type="reset"]:not(:disabled),
[type="submit"]:not(:disabled) {
cursor: pointer;
}
button::-moz-focus-inner,
[type="button"]::-moz-focus-inner,
[type="reset"]::-moz-focus-inner,
[type="submit"]::-moz-focus-inner {
padding: 0;
border-style: none;
}
input[type="radio"],
input[type="checkbox"] {
box-sizing: border-box;
padding: 0;
}
textarea {
overflow: auto;
resize: vertical;
}
fieldset {
min-width: 0;
padding: 0;
margin: 0;
border: 0;
}
legend {
display: block;
width: 100%;
max-width: 100%;
padding: 0;
margin-bottom: .5rem;
font-size: 1.5rem;
line-height: inherit;
color: inherit;
white-space: normal;
}
progress {
vertical-align: baseline;
}
[type="number"]::-webkit-inner-spin-button,
[type="number"]::-webkit-outer-spin-button {
height: auto;
}
[type="search"] {
outline-offset: -2px;
-webkit-appearance: none;
}
[type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}
::-webkit-file-upload-button {
font: inherit;
-webkit-appearance: button;
}
output {
display: inline-block;
}
summary {
display: list-item;
cursor: pointer;
}
template {
display: none;
}
[hidden] {
display: none !important;
}
h1, h2, h3, h4, h5, h6,
.h1, .h2, .h3, .h4, .h5, .h6 {
margin-bottom: 0.46875rem;
font-weight: 500;
line-height: 1.2;
}
h1, .h1 {
font-size: 2.5rem;
}
h2, .h2 {
font-size: 2rem;
}
h3, .h3 {
font-size: 1.75rem;
}
h4, .h4 {
font-size: 1.5rem;
}
h5, .h5 {
font-size: 1.25rem;
}
h6, .h6 {
font-size: 1rem;
}
.lead {
font-size: 1.25rem;
font-weight: 300;
}
.display-1 {
font-size: 6rem;
font-weight: 300;
line-height: 1.2;
}
.display-2 {
font-size: 5.5rem;
font-weight: 300;
line-height: 1.2;
}
.display-3 {
font-size: 4.5rem;
font-weight: 300;
line-height: 1.2;
}
.display-4 {
font-size: 3.5rem;
font-weight: 300;
line-height: 1.2;
}
hr {
margin-top: 0.9375rem;
margin-bottom: 0.9375rem;
border: 0;
border-top: 1px solid rgba(0, 0, 0, 0.1);
}
small,
.small {
font-size: 80%;
font-weight: 400;
}
mark,
.mark {
padding: 0.2em;
background-color: #fcf8e3;
}
.list-unstyled {
padding-left: 0;
list-style: none;
}
.list-inline {
padding-left: 0;
list-style: none;
}
.list-inline-item {
display: inline-block;
}
.list-inline-item:not(:last-child) {
margin-right: 0.5rem;
}
.initialism {
font-size: 90%;
text-transform: uppercase;
}
.blockquote {
margin-bottom: 0.9375rem;
font-size: 1.25rem;
}
.blockquote-footer {
display: block;
font-size: 80%;
color: #6c757d;
}
.blockquote-footer::before {
content: "\2014\00A0";
}
.img-fluid {
max-width: 100%;
height: auto;
}
.img-thumbnail {
padding: 0.25rem;
background-color: #fff;
border: 1px solid #dee2e6;
border-radius: 0.25rem;
max-width: 100%;
height: auto;
}
.figure {
display: inline-block;
}
.figure-img {
margin-bottom: 0.46875rem;
line-height: 1;
}
.figure-caption {
font-size: 90%;
color: #6c757d;
}
.container {
width: 100%;
padding-right: 15px;
padding-left: 15px;
margin-right: auto;
margin-left: auto;
}
@media (min-width: 576px) {
.container {
max-width: 540px;
}
}
@media (min-width: 768px) {
.container {
max-width: 720px;
}
}
@media (min-width: 992px) {
.container {
max-width: 960px;
}
}
@media (min-width: 1200px) {
.container {
max-width: 1140px;
}
}
.container-fluid, .container-sm, .container-md, .container-lg, .container-xl {
width: 100%;
padding-right: 15px;
padding-left: 15px;
margin-right: auto;
margin-left: auto;
}
@media (min-width: 576px) {
.container, .container-sm {
max-width: 540px;
}
}
@media (min-width: 768px) {
.container, .container-sm, .container-md {
max-width: 720px;
}
}
@media (min-width: 992px) {
.container, .container-sm, .container-md, .container-lg {
max-width: 960px;
}
}
@media (min-width: 1200px) {
.container, .container-sm, .container-md, .container-lg, .container-xl {
max-width: 1140px;
}
}
.row {
display: flex;
flex-wrap: wrap;
margin-right: -15px;
margin-left: -15px;
}
.no-gutters {
margin-right: 0;
margin-left: 0;
}
.no-gutters > .col,
.no-gutters > [class*="col-"] {
padding-right: 0;
padding-left: 0;
}
.col-1, .col-2, .col-3, .col-4, .col-5, .col-6, .col-7, .col-8, .col-9, .col-10, .col-11, .col-12, .col,
.col-auto, .col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12, .col-sm,
.col-sm-auto, .col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12, .col-md,
.col-md-auto, .col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12, .col-lg,
.col-lg-auto, .col-xl-1, .col-xl-2, .col-xl-3, .col-xl-4, .col-xl-5, .col-xl-6, .col-xl-7, .col-xl-8, .col-xl-9, .col-xl-10, .col-xl-11, .col-xl-12, .col-xl,
.col-xl-auto {
position: relative;
width: 100%;
padding-right: 15px;
padding-left: 15px;
}
.col {
flex-basis: 0;
flex-grow: 1;
min-width: 0;
max-width: 100%;
}
.row-cols-1 > * {
flex: 0 0 100%;
max-width: 100%;
}
.row-cols-2 > * {
flex: 0 0 50%;
max-width: 50%;
}
.row-cols-3 > * {
flex: 0 0 33.33333%;
max-width: 33.33333%;
}
.row-cols-4 > * {
flex: 0 0 25%;
max-width: 25%;
}
.row-cols-5 > * {
flex: 0 0 20%;
max-width: 20%;
}
.row-cols-6 > * {
flex: 0 0 16.66667%;
max-width: 16.66667%;
}
.col-auto {
flex: 0 0 auto;
width: auto;
max-width: 100%;
}
.col-1 {
flex: 0 0 8.33333%;
max-width: 8.33333%;
}
.col-2 {
flex: 0 0 16.66667%;
max-width: 16.66667%;
}
.col-3 {
flex: 0 0 25%;
max-width: 25%;
}
.col-4 {
flex: 0 0 33.33333%;
max-width: 33.33333%;
}
.col-5 {
flex: 0 0 41.66667%;
max-width: 41.66667%;
}
.col-6 {
flex: 0 0 50%;
max-width: 50%;
}
.col-7 {
flex: 0 0 58.33333%;
max-width: 58.33333%;
}
.col-8 {
flex: 0 0 66.66667%;
max-width: 66.66667%;
}
.col-9 {
flex: 0 0 75%;
max-width: 75%;
}
.col-10 {
flex: 0 0 83.33333%;
max-width: 83.33333%;
}
.col-11 {
flex: 0 0 91.66667%;
max-width: 91.66667%;
}
.col-12 {
flex: 0 0 100%;
max-width: 100%;
}
.order-first {
order: -1;
}
.order-last {
order: 13;
}
.order-0 {
order: 0;
}
.order-1 {
order: 1;
}
.order-2 {
order: 2;
}
.order-3 {
order: 3;
}
.order-4 {
order: 4;
}
.order-5 {
order: 5;
}
.order-6 {
order: 6;
}
.order-7 {
order: 7;
}
.order-8 {
order: 8;
}
.order-9 {
order: 9;
}
.order-10 {
order: 10;
}
.order-11 {
order: 11;
}
.order-12 {
order: 12;
}
.offset-1 {
margin-left: 8.33333%;
}
.offset-2 {
margin-left: 16.66667%;
}
.offset-3 {
margin-left: 25%;
}
.offset-4 {
margin-left: 33.33333%;
}
.offset-5 {
margin-left: 41.66667%;
}
.offset-6 {
margin-left: 50%;
}
.offset-7 {
margin-left: 58.33333%;
}
.offset-8 {
margin-left: 66.66667%;
}
.offset-9 {
margin-left: 75%;
}
.offset-10 {
margin-left: 83.33333%;
}
.offset-11 {
margin-left: 91.66667%;
}
@media (min-width: 576px) {
.col-sm {
flex-basis: 0;
flex-grow: 1;
min-width: 0;
max-width: 100%;
}
.row-cols-sm-1 > * {
flex: 0 0 100%;
max-width: 100%;
}
.row-cols-sm-2 > * {
flex: 0 0 50%;
max-width: 50%;
}
.row-cols-sm-3 > * {
flex: 0 0 33.33333%;
max-width: 33.33333%;
}
.row-cols-sm-4 > * {
flex: 0 0 25%;
max-width: 25%;
}
.row-cols-sm-5 > * {
flex: 0 0 20%;
max-width: 20%;
}
.row-cols-sm-6 > * {
flex: 0 0 16.66667%;
max-width: 16.66667%;
}
.col-sm-auto {
flex: 0 0 auto;
width: auto;
max-width: 100%;
}
.col-sm-1 {
flex: 0 0 8.33333%;
max-width: 8.33333%;
}
.col-sm-2 {
flex: 0 0 16.66667%;
max-width: 16.66667%;
}
.col-sm-3 {
flex: 0 0 25%;
max-width: 25%;
}
.col-sm-4 {
flex: 0 0 33.33333%;
max-width: 33.33333%;
}
.col-sm-5 {
flex: 0 0 41.66667%;
max-width: 41.66667%;
}
.col-sm-6 {
flex: 0 0 50%;
max-width: 50%;
}
.col-sm-7 {
flex: 0 0 58.33333%;
max-width: 58.33333%;
}
.col-sm-8 {
flex: 0 0 66.66667%;
max-width: 66.66667%;
}
.col-sm-9 {
flex: 0 0 75%;
max-width: 75%;
}
.col-sm-10 {
flex: 0 0 83.33333%;
max-width: 83.33333%;
}
.col-sm-11 {
flex: 0 0 91.66667%;
max-width: 91.66667%;
}
.col-sm-12 {
flex: 0 0 100%;
max-width: 100%;
}
.order-sm-first {
order: -1;
}
.order-sm-last {
order: 13;
}
.order-sm-0 {
order: 0;
}
.order-sm-1 {
order: 1;
}
.order-sm-2 {
order: 2;
}
.order-sm-3 {
order: 3;
}
.order-sm-4 {
order: 4;
}
.order-sm-5 {
order: 5;
}
.order-sm-6 {
order: 6;
}
.order-sm-7 {
order: 7;
}
.order-sm-8 {
order: 8;
}
.order-sm-9 {
order: 9;
}
.order-sm-10 {
order: 10;
}
.order-sm-11 {
order: 11;
}
.order-sm-12 {
order: 12;
}
.offset-sm-0 {
margin-left: 0;
}
.offset-sm-1 {
margin-left: 8.33333%;
}
.offset-sm-2 {
margin-left: 16.66667%;
}
.offset-sm-3 {
margin-left: 25%;
}
.offset-sm-4 {
margin-left: 33.33333%;
}
.offset-sm-5 {
margin-left: 41.66667%;
}
.offset-sm-6 {
margin-left: 50%;
}
.offset-sm-7 {
margin-left: 58.33333%;
}
.offset-sm-8 {
margin-left: 66.66667%;
}
.offset-sm-9 {
margin-left: 75%;
}
.offset-sm-10 {
margin-left: 83.33333%;
}
.offset-sm-11 {
margin-left: 91.66667%;
}
}
@media (min-width: 768px) {
.col-md {
flex-basis: 0;
flex-grow: 1;
min-width: 0;
max-width: 100%;
}
.row-cols-md-1 > * {
flex: 0 0 100%;
max-width: 100%;
}
.row-cols-md-2 > * {
flex: 0 0 50%;
max-width: 50%;
}
.row-cols-md-3 > * {
flex: 0 0 33.33333%;
max-width: 33.33333%;
}
.row-cols-md-4 > * {
flex: 0 0 25%;
max-width: 25%;
}
.row-cols-md-5 > * {
flex: 0 0 20%;
max-width: 20%;
}
.row-cols-md-6 > * {
flex: 0 0 16.66667%;
max-width: 16.66667%;
}
.col-md-auto {
flex: 0 0 auto;
width: auto;
max-width: 100%;
}
.col-md-1 {
flex: 0 0 8.33333%;
max-width: 8.33333%;
}
.col-md-2 {
flex: 0 0 16.66667%;
max-width: 16.66667%;
}
.col-md-3 {
flex: 0 0 25%;
max-width: 25%;
}
.col-md-4 {
flex: 0 0 33.33333%;
max-width: 33.33333%;
}
.col-md-5 {
flex: 0 0 41.66667%;
max-width: 41.66667%;
}
.col-md-6 {
flex: 0 0 50%;
max-width: 50%;
}
.col-md-7 {
flex: 0 0 58.33333%;
max-width: 58.33333%;
}
.col-md-8 {
flex: 0 0 66.66667%;
max-width: 66.66667%;
}
.col-md-9 {
flex: 0 0 75%;
max-width: 75%;
}
.col-md-10 {
flex: 0 0 83.33333%;
max-width: 83.33333%;
}
.col-md-11 {
flex: 0 0 91.66667%;
max-width: 91.66667%;
}
.col-md-12 {
flex: 0 0 100%;
max-width: 100%;
}
.order-md-first {
order: -1;
}
.order-md-last {
order: 13;
}
.order-md-0 {
order: 0;
}
.order-md-1 {
order: 1;
}
.order-md-2 {
order: 2;
}
.order-md-3 {
order: 3;
}
.order-md-4 {
order: 4;
}
.order-md-5 {
order: 5;
}
.order-md-6 {
order: 6;
}
.order-md-7 {
order: 7;
}
.order-md-8 {
order: 8;
}
.order-md-9 {
order: 9;
}
.order-md-10 {
order: 10;
}
.order-md-11 {
order: 11;
}
.order-md-12 {
order: 12;
}
.offset-md-0 {
margin-left: 0;
}
.offset-md-1 {
margin-left: 8.33333%;
}
.offset-md-2 {
margin-left: 16.66667%;
}
.offset-md-3 {
margin-left: 25%;
}
.offset-md-4 {
margin-left: 33.33333%;
}
.offset-md-5 {
margin-left: 41.66667%;
}
.offset-md-6 {
margin-left: 50%;
}
.offset-md-7 {
margin-left: 58.33333%;
}
.offset-md-8 {
margin-left: 66.66667%;
}
.offset-md-9 {
margin-left: 75%;
}
.offset-md-10 {
margin-left: 83.33333%;
}
.offset-md-11 {
margin-left: 91.66667%;
}
}
@media (min-width: 992px) {
.col-lg {
flex-basis: 0;
flex-grow: 1;
min-width: 0;
max-width: 100%;
}
.row-cols-lg-1 > * {
flex: 0 0 100%;
max-width: 100%;
}
.row-cols-lg-2 > * {
flex: 0 0 50%;
max-width: 50%;
}
.row-cols-lg-3 > * {
flex: 0 0 33.33333%;
max-width: 33.33333%;
}
.row-cols-lg-4 > * {
flex: 0 0 25%;
max-width: 25%;
}
.row-cols-lg-5 > * {
flex: 0 0 20%;
max-width: 20%;
}
.row-cols-lg-6 > * {
flex: 0 0 16.66667%;
max-width: 16.66667%;
}
.col-lg-auto {
flex: 0 0 auto;
width: auto;
max-width: 100%;
}
.col-lg-1 {
flex: 0 0 8.33333%;
max-width: 8.33333%;
}
.col-lg-2 {
flex: 0 0 16.66667%;
max-width: 16.66667%;
}
.col-lg-3 {
flex: 0 0 25%;
max-width: 25%;
}
.col-lg-4 {
flex: 0 0 33.33333%;
max-width: 33.33333%;
}
.col-lg-5 {
flex: 0 0 41.66667%;
max-width: 41.66667%;
}
.col-lg-6 {
flex: 0 0 50%;
max-width: 50%;
}
.col-lg-7 {
flex: 0 0 58.33333%;
max-width: 58.33333%;
}
.col-lg-8 {
flex: 0 0 66.66667%;
max-width: 66.66667%;
}
.col-lg-9 {
flex: 0 0 75%;
max-width: 75%;
}
.col-lg-10 {
flex: 0 0 83.33333%;
max-width: 83.33333%;
}
.col-lg-11 {
flex: 0 0 91.66667%;
max-width: 91.66667%;
}
.col-lg-12 {
flex: 0 0 100%;
max-width: 100%;
}
.order-lg-first {
order: -1;
}
.order-lg-last {
order: 13;
}
.order-lg-0 {
order: 0;
}
.order-lg-1 {
order: 1;
}
.order-lg-2 {
order: 2;
}
.order-lg-3 {
order: 3;
}
.order-lg-4 {
order: 4;
}
.order-lg-5 {
order: 5;
}
.order-lg-6 {
order: 6;
}
.order-lg-7 {
order: 7;
}
.order-lg-8 {
order: 8;
}
.order-lg-9 {
order: 9;
}
.order-lg-10 {
order: 10;
}
.order-lg-11 {
order: 11;
}
.order-lg-12 {
order: 12;
}
.offset-lg-0 {
margin-left: 0;
}
.offset-lg-1 {
margin-left: 8.33333%;
}
.offset-lg-2 {
margin-left: 16.66667%;
}
.offset-lg-3 {
margin-left: 25%;
}
.offset-lg-4 {
margin-left: 33.33333%;
}
.offset-lg-5 {
margin-left: 41.66667%;
}
.offset-lg-6 {
margin-left: 50%;
}
.offset-lg-7 {
margin-left: 58.33333%;
}
.offset-lg-8 {
margin-left: 66.66667%;
}
.offset-lg-9 {
margin-left: 75%;
}
.offset-lg-10 {
margin-left: 83.33333%;
}
.offset-lg-11 {
margin-left: 91.66667%;
}
}
@media (min-width: 1200px) {
.col-xl {
flex-basis: 0;
flex-grow: 1;
min-width: 0;
max-width: 100%;
}
.row-cols-xl-1 > * {
flex: 0 0 100%;
max-width: 100%;
}
.row-cols-xl-2 > * {
flex: 0 0 50%;
max-width: 50%;
}
.row-cols-xl-3 > * {
flex: 0 0 33.33333%;
max-width: 33.33333%;
}
.row-cols-xl-4 > * {
flex: 0 0 25%;
max-width: 25%;
}
.row-cols-xl-5 > * {
flex: 0 0 20%;
max-width: 20%;
}
.row-cols-xl-6 > * {
flex: 0 0 16.66667%;
max-width: 16.66667%;
}
.col-xl-auto {
flex: 0 0 auto;
width: auto;
max-width: 100%;
}
.col-xl-1 {
flex: 0 0 8.33333%;
max-width: 8.33333%;
}
.col-xl-2 {
flex: 0 0 16.66667%;
max-width: 16.66667%;
}
.col-xl-3 {
flex: 0 0 25%;
max-width: 25%;
}
.col-xl-4 {
flex: 0 0 33.33333%;
max-width: 33.33333%;
}
.col-xl-5 {
flex: 0 0 41.66667%;
max-width: 41.66667%;
}
.col-xl-6 {
flex: 0 0 50%;
max-width: 50%;
}
.col-xl-7 {
flex: 0 0 58.33333%;
max-width: 58.33333%;
}
.col-xl-8 {
flex: 0 0 66.66667%;
max-width: 66.66667%;
}
.col-xl-9 {
flex: 0 0 75%;
max-width: 75%;
}
.col-xl-10 {
flex: 0 0 83.33333%;
max-width: 83.33333%;
}
.col-xl-11 {
flex: 0 0 91.66667%;
max-width: 91.66667%;
}
.col-xl-12 {
flex: 0 0 100%;
max-width: 100%;
}
.order-xl-first {
order: -1;
}
.order-xl-last {
order: 13;
}
.order-xl-0 {
order: 0;
}
.order-xl-1 {
order: 1;
}
.order-xl-2 {
order: 2;
}
.order-xl-3 {
order: 3;
}
.order-xl-4 {
order: 4;
}
.order-xl-5 {
order: 5;
}
.order-xl-6 {
order: 6;
}
.order-xl-7 {
order: 7;
}
.order-xl-8 {
order: 8;
}
.order-xl-9 {
order: 9;
}
.order-xl-10 {
order: 10;
}
.order-xl-11 {
order: 11;
}
.order-xl-12 {
order: 12;
}
.offset-xl-0 {
margin-left: 0;
}
.offset-xl-1 {
margin-left: 8.33333%;
}
.offset-xl-2 {
margin-left: 16.66667%;
}
.offset-xl-3 {
margin-left: 25%;
}
.offset-xl-4 {
margin-left: 33.33333%;
}
.offset-xl-5 {
margin-left: 41.66667%;
}
.offset-xl-6 {
margin-left: 50%;
}
.offset-xl-7 {
margin-left: 58.33333%;
}
.offset-xl-8 {
margin-left: 66.66667%;
}
.offset-xl-9 {
margin-left: 75%;
}
.offset-xl-10 {
margin-left: 83.33333%;
}
.offset-xl-11 {
margin-left: 91.66667%;
}
}
.table {
width: 100%;
margin-bottom: 0.9375rem;
color: #212529;
}
.table th,
.table td {
padding: 0.75rem;
vertical-align: top;
border-top: 1px solid #dee2e6;
}
.table thead th {
vertical-align: bottom;
border-bottom: 2px solid #dee2e6;
}
.table tbody + tbody {
border-top: 2px solid #dee2e6;
}
.table-sm th,
.table-sm td {
padding: 0.3rem;
}
.table-bordered {
border: 1px solid #dee2e6;
}
.table-bordered th,
.table-bordered td {
border: 1px solid #dee2e6;
}
.table-bordered thead th,
.table-bordered thead td {
border-bottom-width: 2px;
}
.table-borderless th,
.table-borderless td,
.table-borderless thead th,
.table-borderless tbody + tbody {
border: 0;
}
.table-striped tbody tr:nth-of-type(odd) {
background-color: rgba(0, 0, 0, 0.05);
}
.table-hover tbody tr:hover {
color: #212529;
background-color: rgba(0, 0, 0, 0.075);
}
.table-primary,
.table-primary > th,
.table-primary > td {
background-color: #b8daff;
}
.table-primary th,
.table-primary td,
.table-primary thead th,
.table-primary tbody + tbody {
border-color: #7abaff;
}
.table-hover .table-primary:hover {
background-color: #9fcdff;
}
.table-hover .table-primary:hover > td,
.table-hover .table-primary:hover > th {
background-color: #9fcdff;
}
.table-secondary,
.table-secondary > th,
.table-secondary > td {
background-color: #d6d8db;
}
.table-secondary th,
.table-secondary td,
.table-secondary thead th,
.table-secondary tbody + tbody {
border-color: #b3b7bb;
}
.table-hover .table-secondary:hover {
background-color: #c8cbcf;
}
.table-hover .table-secondary:hover > td,
.table-hover .table-secondary:hover > th {
background-color: #c8cbcf;
}
.table-success,
.table-success > th,
.table-success > td {
background-color: #c3e6cb;
}
.table-success th,
.table-success td,
.table-success thead th,
.table-success tbody + tbody {
border-color: #8fd19e;
}
.table-hover .table-success:hover {
background-color: #b1dfbb;
}
.table-hover .table-success:hover > td,
.table-hover .table-success:hover > th {
background-color: #b1dfbb;
}
.table-info,
.table-info > th,
.table-info > td {
background-color: #bee5eb;
}
.table-info th,
.table-info td,
.table-info thead th,
.table-info tbody + tbody {
border-color: #86cfda;
}
.table-hover .table-info:hover {
background-color: #abdde5;
}
.table-hover .table-info:hover > td,
.table-hover .table-info:hover > th {
background-color: #abdde5;
}
.table-warning,
.table-warning > th,
.table-warning > td {
background-color: #ffeeba;
}
.table-warning th,
.table-warning td,
.table-warning thead th,
.table-warning tbody + tbody {
border-color: #ffdf7e;
}
.table-hover .table-warning:hover {
background-color: #ffe8a1;
}
.table-hover .table-warning:hover > td,
.table-hover .table-warning:hover > th {
background-color: #ffe8a1;
}
.table-danger,
.table-danger > th,
.table-danger > td {
background-color: #f5c6cb;
}
.table-danger th,
.table-danger td,
.table-danger thead th,
.table-danger tbody + tbody {
border-color: #ed969e;
}
.table-hover .table-danger:hover {
background-color: #f1b0b7;
}
.table-hover .table-danger:hover > td,
.table-hover .table-danger:hover > th {
background-color: #f1b0b7;
}
.table-light,
.table-light > th,
.table-light > td {
background-color: #fdfdfe;
}
.table-light th,
.table-light td,
.table-light thead th,
.table-light tbody + tbody {
border-color: #fbfcfc;
}
.table-hover .table-light:hover {
background-color: #ececf6;
}
.table-hover .table-light:hover > td,
.table-hover .table-light:hover > th {
background-color: #ececf6;
}
.table-dark,
.table-dark > th,
.table-dark > td {
background-color: #c6c8ca;
}
.table-dark th,
.table-dark td,
.table-dark thead th,
.table-dark tbody + tbody {
border-color: #95999c;
}
.table-hover .table-dark:hover {
background-color: #b9bbbe;
}
.table-hover .table-dark:hover > td,
.table-hover .table-dark:hover > th {
background-color: #b9bbbe;
}
.table-active,
.table-active > th,
.table-active > td {
background-color: rgba(0, 0, 0, 0.075);
}
.table-hover .table-active:hover {
background-color: rgba(0, 0, 0, 0.075);
}
.table-hover .table-active:hover > td,
.table-hover .table-active:hover > th {
background-color: rgba(0, 0, 0, 0.075);
}
.table .thead-dark th {
color: #fff;
background-color: #343a40;
border-color: #454d55;
}
.table .thead-light th {
color: #495057;
background-color: #e9ecef;
border-color: #dee2e6;
}
.table-dark {
color: #fff;
background-color: #343a40;
}
.table-dark th,
.table-dark td,
.table-dark thead th {
border-color: #454d55;
}
.table-dark.table-bordered {
border: 0;
}
.table-dark.table-striped tbody tr:nth-of-type(odd) {
background-color: rgba(255, 255, 255, 0.05);
}
.table-dark.table-hover tbody tr:hover {
color: #fff;
background-color: rgba(255, 255, 255, 0.075);
}
@media (max-width: 575.98px) {
.table-responsive-sm {
display: block;
width: 100%;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
}
.table-responsive-sm > .table-bordered {
border: 0;
}
}
@media (max-width: 767.98px) {
.table-responsive-md {
display: block;
width: 100%;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
}
.table-responsive-md > .table-bordered {
border: 0;
}
}
@media (max-width: 991.98px) {
.table-responsive-lg {
display: block;
width: 100%;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
}
.table-responsive-lg > .table-bordered {
border: 0;
}
}
@media (max-width: 1199.98px) {
.table-responsive-xl {
display: block;
width: 100%;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
}
.table-responsive-xl > .table-bordered {
border: 0;
}
}
.table-responsive {
display: block;
width: 100%;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
}
.table-responsive > .table-bordered {
border: 0;
}
.form-control {
display: block;
width: 100%;
height: calc(1.5em + 1.5rem + 2px);
padding: 0.75rem 0.75rem;
font-size: 1rem;
font-weight: 400;
line-height: 1.5;
color: #495057;
background-color: #fff;
background-clip: padding-box;
border: 1px solid #ced4da;
border-radius: 0.25rem;
transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
}
@media (prefers-reduced-motion: reduce) {
.form-control {
transition: none;
}
}
.form-control::-ms-expand {
background-color: transparent;
border: 0;
}
.form-control:-moz-focusring {
color: transparent;
text-shadow: 0 0 0 #495057;
}
.form-control:focus {
color: #495057;
background-color: #fff;
border-color: #80bdff;
outline: 0;
box-shadow: 0 0 0 0 rgba(0, 123, 255, 0);
}
.form-control::placeholder {
color: #6c757d;
opacity: 1;
}
.form-control:disabled, .form-control[readonly] {
background-color: #e9ecef;
opacity: 1;
}
input[type="date"].form-control,
input[type="time"].form-control,
input[type="datetime-local"].form-control,
input[type="month"].form-control {
appearance: none;
}
select.form-control:focus::-ms-value {
color: #495057;
background-color: #fff;
}
.form-control-file,
.form-control-range {
display: block;
width: 100%;
}
.col-form-label {
padding-top: calc(0.75rem + 1px);
padding-bottom: calc(0.75rem + 1px);
margin-bottom: 0;
font-size: inherit;
line-height: 1.5;
}
.col-form-label-lg {
padding-top: calc(0.5rem + 1px);
padding-bottom: calc(0.5rem + 1px);
font-size: 1.25rem;
line-height: 1.5;
}
.col-form-label-sm {
padding-top: calc(0.25rem + 1px);
padding-bottom: calc(0.25rem + 1px);
font-size: 0.875rem;
line-height: 1.5;
}
.form-control-plaintext {
display: block;
width: 100%;
padding: 0.75rem 0;
margin-bottom: 0;
font-size: 1rem;
line-height: 1.5;
color: #212529;
background-color: transparent;
border: solid transparent;
border-width: 1px 0;
}
.form-control-plaintext.form-control-sm, .form-control-plaintext.form-control-lg {
padding-right: 0;
padding-left: 0;
}
.form-control-sm {
height: calc(1.5em + 0.5rem + 2px);
padding: 0.25rem 0.5rem;
font-size: 0.875rem;
line-height: 1.5;
border-radius: 0.2rem;
}
.form-control-lg {
height: calc(1.5em + 1rem + 2px);
padding: 0.5rem 1rem;
font-size: 1.25rem;
line-height: 1.5;
border-radius: 0.3rem;
}
select.form-control[size], select.form-control[multiple] {
height: auto;
}
textarea.form-control {
height: auto;
}
.form-group {
margin-bottom: 1rem;
}
.form-text {
display: block;
margin-top: 0.25rem;
}
.form-row {
display: flex;
flex-wrap: wrap;
margin-right: -5px;
margin-left: -5px;
}
.form-row > .col,
.form-row > [class*="col-"] {
padding-right: 5px;
padding-left: 5px;
}
.form-check {
position: relative;
display: block;
padding-left: 1.25rem;
}
.form-check-input {
position: absolute;
margin-top: 0.3rem;
margin-left: -1.25rem;
}
.form-check-input[disabled] ~ .form-check-label,
.form-check-input:disabled ~ .form-check-label {
color: #6c757d;
}
.form-check-label {
margin-bottom: 0;
}
.form-check-inline {
display: inline-flex;
align-items: center;
padding-left: 0;
margin-right: 0.75rem;
}
.form-check-inline .form-check-input {
position: static;
margin-top: 0;
margin-right: 0.3125rem;
margin-left: 0;
}
.valid-feedback {
display: none;
width: 100%;
margin-top: 0.25rem;
font-size: 80%;
color: #28a745;
}
.valid-tooltip {
position: absolute;
top: 100%;
z-index: 5;
display: none;
max-width: 100%;
padding: 0.25rem 0.5rem;
margin-top: .1rem;
font-size: 0.875rem;
line-height: 1.5;
color: #fff;
background-color: rgba(40, 167, 69, 0.9);
border-radius: 0.25rem;
}
.was-validated :valid ~ .valid-feedback,
.was-validated :valid ~ .valid-tooltip,
.is-valid ~ .valid-feedback,
.is-valid ~ .valid-tooltip {
display: block;
}
.was-validated .form-control:valid, .form-control.is-valid {
border-color: #28a745;
padding-right: calc(1.5em + 1.5rem);
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");
background-repeat: no-repeat;
background-position: right calc(0.375em + 0.375rem) center;
background-size: calc(0.75em + 0.75rem) calc(0.75em + 0.75rem);
}
.was-validated .form-control:valid:focus, .form-control.is-valid:focus {
border-color: #28a745;
box-shadow: 0 0 0 0 rgba(40, 167, 69, 0.25);
}
.was-validated textarea.form-control:valid, textarea.form-control.is-valid {
padding-right: calc(1.5em + 1.5rem);
background-position: top calc(0.375em + 0.375rem) right calc(0.375em + 0.375rem);
}
.was-validated .custom-select:valid, .custom-select.is-valid {
border-color: #28a745;
padding-right: calc(0.75em + 2.875rem);
background: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right 0.75rem center/8px 10px, url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e") #fff no-repeat center right 1.75rem/calc(0.75em + 0.75rem) calc(0.75em + 0.75rem);
}
.was-validated .custom-select:valid:focus, .custom-select.is-valid:focus {
border-color: #28a745;
box-shadow: 0 0 0 0 rgba(40, 167, 69, 0.25);
}
.was-validated .form-check-input:valid ~ .form-check-label, .form-check-input.is-valid ~ .form-check-label {
color: #28a745;
}
.was-validated .form-check-input:valid ~ .valid-feedback,
.was-validated .form-check-input:valid ~ .valid-tooltip, .form-check-input.is-valid ~ .valid-feedback,
.form-check-input.is-valid ~ .valid-tooltip {
display: block;
}
.was-validated .custom-control-input:valid ~ .custom-control-label, .custom-control-input.is-valid ~ .custom-control-label {
color: #28a745;
}
.was-validated .custom-control-input:valid ~ .custom-control-label::before, .custom-control-input.is-valid ~ .custom-control-label::before {
border-color: #28a745;
}
.was-validated .custom-control-input:valid:checked ~ .custom-control-label::before, .custom-control-input.is-valid:checked ~ .custom-control-label::before {
border-color: #34ce57;
background-color: #34ce57;
}
.was-validated .custom-control-input:valid:focus ~ .custom-control-label::before, .custom-control-input.is-valid:focus ~ .custom-control-label::before {
box-shadow: 0 0 0 0 rgba(40, 167, 69, 0.25);
}
.was-validated .custom-control-input:valid:focus:not(:checked) ~ .custom-control-label::before, .custom-control-input.is-valid:focus:not(:checked) ~ .custom-control-label::before {
border-color: #28a745;
}
.was-validated .custom-file-input:valid ~ .custom-file-label, .custom-file-input.is-valid ~ .custom-file-label {
border-color: #28a745;
}
.was-validated .custom-file-input:valid:focus ~ .custom-file-label, .custom-file-input.is-valid:focus ~ .custom-file-label {
border-color: #28a745;
box-shadow: 0 0 0 0 rgba(40, 167, 69, 0.25);
}
.invalid-feedback {
display: none;
width: 100%;
margin-top: 0.25rem;
font-size: 80%;
color: #dc3545;
}
.invalid-tooltip {
position: absolute;
top: 100%;
z-index: 5;
display: none;
max-width: 100%;
padding: 0.25rem 0.5rem;
margin-top: .1rem;
font-size: 0.875rem;
line-height: 1.5;
color: #fff;
background-color: rgba(220, 53, 69, 0.9);
border-radius: 0.25rem;
}
.was-validated :invalid ~ .invalid-feedback,
.was-validated :invalid ~ .invalid-tooltip,
.is-invalid ~ .invalid-feedback,
.is-invalid ~ .invalid-tooltip {
display: block;
}
.was-validated .form-control:invalid, .form-control.is-invalid {
border-color: #dc3545;
padding-right: calc(1.5em + 1.5rem);
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545' viewBox='0 0 12 12'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e");
background-repeat: no-repeat;
background-position: right calc(0.375em + 0.375rem) center;
background-size: calc(0.75em + 0.75rem) calc(0.75em + 0.75rem);
}
.was-validated .form-control:invalid:focus, .form-control.is-invalid:focus {
border-color: #dc3545;
box-shadow: 0 0 0 0 rgba(220, 53, 69, 0.25);
}
.was-validated textarea.form-control:invalid, textarea.form-control.is-invalid {
padding-right: calc(1.5em + 1.5rem);
background-position: top calc(0.375em + 0.375rem) right calc(0.375em + 0.375rem);
}
.was-validated .custom-select:invalid, .custom-select.is-invalid {
border-color: #dc3545;
padding-right: calc(0.75em + 2.875rem);
background: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right 0.75rem center/8px 10px, url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545' viewBox='0 0 12 12'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e") #fff no-repeat center right 1.75rem/calc(0.75em + 0.75rem) calc(0.75em + 0.75rem);
}
.was-validated .custom-select:invalid:focus, .custom-select.is-invalid:focus {
border-color: #dc3545;
box-shadow: 0 0 0 0 rgba(220, 53, 69, 0.25);
}
.was-validated .form-check-input:invalid ~ .form-check-label, .form-check-input.is-invalid ~ .form-check-label {
color: #dc3545;
}
.was-validated .form-check-input:invalid ~ .invalid-feedback,
.was-validated .form-check-input:invalid ~ .invalid-tooltip, .form-check-input.is-invalid ~ .invalid-feedback,
.form-check-input.is-invalid ~ .invalid-tooltip {
display: block;
}
.was-validated .custom-control-input:invalid ~ .custom-control-label, .custom-control-input.is-invalid ~ .custom-control-label {
color: #dc3545;
}
.was-validated .custom-control-input:invalid ~ .custom-control-label::before, .custom-control-input.is-invalid ~ .custom-control-label::before {
border-color: #dc3545;
}
.was-validated .custom-control-input:invalid:checked ~ .custom-control-label::before, .custom-control-input.is-invalid:checked ~ .custom-control-label::before {
border-color: #e4606d;
background-color: #e4606d;
}
.was-validated .custom-control-input:invalid:focus ~ .custom-control-label::before, .custom-control-input.is-invalid:focus ~ .custom-control-label::before {
box-shadow: 0 0 0 0 rgba(220, 53, 69, 0.25);
}
.was-validated .custom-control-input:invalid:focus:not(:checked) ~ .custom-control-label::before, .custom-control-input.is-invalid:focus:not(:checked) ~ .custom-control-label::before {
border-color: #dc3545;
}
.was-validated .custom-file-input:invalid ~ .custom-file-label, .custom-file-input.is-invalid ~ .custom-file-label {
border-color: #dc3545;
}
.was-validated .custom-file-input:invalid:focus ~ .custom-file-label, .custom-file-input.is-invalid:focus ~ .custom-file-label {
border-color: #dc3545;
box-shadow: 0 0 0 0 rgba(220, 53, 69, 0.25);
}
.form-inline {
display: flex;
flex-flow: row wrap;
align-items: center;
}
.form-inline .form-check {
width: 100%;
}
@media (min-width: 576px) {
.form-inline label {
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 0;
}
.form-inline .form-group {
display: flex;
flex: 0 0 auto;
flex-flow: row wrap;
align-items: center;
margin-bottom: 0;
}
.form-inline .form-control {
display: inline-block;
width: auto;
vertical-align: middle;
}
.form-inline .form-control-plaintext {
display: inline-block;
}
.form-inline .input-group,
.form-inline .custom-select {
width: auto;
}
.form-inline .form-check {
display: flex;
align-items: center;
justify-content: center;
width: auto;
padding-left: 0;
}
.form-inline .form-check-input {
position: relative;
flex-shrink: 0;
margin-top: 0;
margin-right: 0.25rem;
margin-left: 0;
}
.form-inline .custom-control {
align-items: center;
justify-content: center;
}
.form-inline .custom-control-label {
margin-bottom: 0;
}
}
.btn {
display: inline-block;
font-weight: 400;
color: #212529;
text-align: center;
vertical-align: middle;
user-select: none;
background-color: transparent;
border: 1px solid transparent;
padding: 0.75rem 0.75rem;
font-size: 1rem;
line-height: 1.5;
border-radius: 0.25rem;
transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
}
@media (prefers-reduced-motion: reduce) {
.btn {
transition: none;
}
}
.btn:hover {
color: #212529;
text-decoration: none;
}
.btn:focus, .btn.focus {
outline: 0;
box-shadow: 0;
}
.btn.disabled, .btn:disabled {
opacity: 0.65;
}
.btn:not(:disabled):not(.disabled) {
cursor: pointer;
}
a.btn.disabled,
fieldset:disabled a.btn {
pointer-events: none;
}
.btn-primary {
color: #fff;
background-color: #007bff;
border-color: #007bff;
}
.btn-primary:hover {
color: #fff;
background-color: #0069d9;
border-color: #0062cc;
}
.btn-primary:focus, .btn-primary.focus {
color: #fff;
background-color: #0069d9;
border-color: #0062cc;
box-shadow: 0 0 0 0 rgba(38, 143, 255, 0.5);
}
.btn-primary.disabled, .btn-primary:disabled {
color: #fff;
background-color: #007bff;
border-color: #007bff;
}
.btn-primary:not(:disabled):not(.disabled):active, .btn-primary:not(:disabled):not(.disabled).active,
.show > .btn-primary.dropdown-toggle {
color: #fff;
background-color: #0062cc;
border-color: #005cbf;
}
.btn-primary:not(:disabled):not(.disabled):active:focus, .btn-primary:not(:disabled):not(.disabled).active:focus,
.show > .btn-primary.dropdown-toggle:focus {
box-shadow: 0 0 0 0 rgba(38, 143, 255, 0.5);
}
.btn-secondary {
color: #fff;
background-color: #6c757d;
border-color: #6c757d;
}
.btn-secondary:hover {
color: #fff;
background-color: #5a6268;
border-color: #545b62;
}
.btn-secondary:focus, .btn-secondary.focus {
color: #fff;
background-color: #5a6268;
border-color: #545b62;
box-shadow: 0 0 0 0 rgba(130, 138, 145, 0.5);
}
.btn-secondary.disabled, .btn-secondary:disabled {
color: #fff;
background-color: #6c757d;
border-color: #6c757d;
}
.btn-secondary:not(:disabled):not(.disabled):active, .btn-secondary:not(:disabled):not(.disabled).active,
.show > .btn-secondary.dropdown-toggle {
color: #fff;
background-color: #545b62;
border-color: #4e555b;
}
.btn-secondary:not(:disabled):not(.disabled):active:focus, .btn-secondary:not(:disabled):not(.disabled).active:focus,
.show > .btn-secondary.dropdown-toggle:focus {
box-shadow: 0 0 0 0 rgba(130, 138, 145, 0.5);
}
.btn-success {
color: #fff;
background-color: #28a745;
border-color: #28a745;
}
.btn-success:hover {
color: #fff;
background-color: #218838;
border-color: #1e7e34;
}
.btn-success:focus, .btn-success.focus {
color: #fff;
background-color: #218838;
border-color: #1e7e34;
box-shadow: 0 0 0 0 rgba(72, 180, 97, 0.5);
}
.btn-success.disabled, .btn-success:disabled {
color: #fff;
background-color: #28a745;
border-color: #28a745;
}
.btn-success:not(:disabled):not(.disabled):active, .btn-success:not(:disabled):not(.disabled).active,
.show > .btn-success.dropdown-toggle {
color: #fff;
background-color: #1e7e34;
border-color: #1c7430;
}
.btn-success:not(:disabled):not(.disabled):active:focus, .btn-success:not(:disabled):not(.disabled).active:focus,
.show > .btn-success.dropdown-toggle:focus {
box-shadow: 0 0 0 0 rgba(72, 180, 97, 0.5);
}
.btn-info {
color: #fff;
background-color: #17a2b8;
border-color: #17a2b8;
}
.btn-info:hover {
color: #fff;
background-color: #138496;
border-color: #117a8b;
}
.btn-info:focus, .btn-info.focus {
color: #fff;
background-color: #138496;
border-color: #117a8b;
box-shadow: 0 0 0 0 rgba(58, 176, 195, 0.5);
}
.btn-info.disabled, .btn-info:disabled {
color: #fff;
background-color: #17a2b8;
border-color: #17a2b8;
}
.btn-info:not(:disabled):not(.disabled):active, .btn-info:not(:disabled):not(.disabled).active,
.show > .btn-info.dropdown-toggle {
color: #fff;
background-color: #117a8b;
border-color: #10707f;
}
.btn-info:not(:disabled):not(.disabled):active:focus, .btn-info:not(:disabled):not(.disabled).active:focus,
.show > .btn-info.dropdown-toggle:focus {
box-shadow: 0 0 0 0 rgba(58, 176, 195, 0.5);
}
.btn-warning {
color: #212529;
background-color: #ffc107;
border-color: #ffc107;
}
.btn-warning:hover {
color: #212529;
background-color: #e0a800;
border-color: #d39e00;
}
.btn-warning:focus, .btn-warning.focus {
color: #212529;
background-color: #e0a800;
border-color: #d39e00;
box-shadow: 0 0 0 0 rgba(222, 170, 12, 0.5);
}
.btn-warning.disabled, .btn-warning:disabled {
color: #212529;
background-color: #ffc107;
border-color: #ffc107;
}
.btn-warning:not(:disabled):not(.disabled):active, .btn-warning:not(:disabled):not(.disabled).active,
.show > .btn-warning.dropdown-toggle {
color: #212529;
background-color: #d39e00;
border-color: #c69500;
}
.btn-warning:not(:disabled):not(.disabled):active:focus, .btn-warning:not(:disabled):not(.disabled).active:focus,
.show > .btn-warning.dropdown-toggle:focus {
box-shadow: 0 0 0 0 rgba(222, 170, 12, 0.5);
}
.btn-danger {
color: #fff;
background-color: #dc3545;
border-color: #dc3545;
}
.btn-danger:hover {
color: #fff;
background-color: #c82333;
border-color: #bd2130;
}
.btn-danger:focus, .btn-danger.focus {
color: #fff;
background-color: #c82333;
border-color: #bd2130;
box-shadow: 0 0 0 0 rgba(225, 83, 97, 0.5);
}
.btn-danger.disabled, .btn-danger:disabled {
color: #fff;
background-color: #dc3545;
border-color: #dc3545;
}
.btn-danger:not(:disabled):not(.disabled):active, .btn-danger:not(:disabled):not(.disabled).active,
.show > .btn-danger.dropdown-toggle {
color: #fff;
background-color: #bd2130;
border-color: #b21f2d;
}
.btn-danger:not(:disabled):not(.disabled):active:focus, .btn-danger:not(:disabled):not(.disabled).active:focus,
.show > .btn-danger.dropdown-toggle:focus {
box-shadow: 0 0 0 0 rgba(225, 83, 97, 0.5);
}
.btn-light {
color: #212529;
background-color: #f8f9fa;
border-color: #f8f9fa;
}
.btn-light:hover {
color: #212529;
background-color: #e2e6ea;
border-color: #dae0e5;
}
.btn-light:focus, .btn-light.focus {
color: #212529;
background-color: #e2e6ea;
border-color: #dae0e5;
box-shadow: 0 0 0 0 rgba(216, 217, 219, 0.5);
}
.btn-light.disabled, .btn-light:disabled {
color: #212529;
background-color: #f8f9fa;
border-color: #f8f9fa;
}
.btn-light:not(:disabled):not(.disabled):active, .btn-light:not(:disabled):not(.disabled).active,
.show > .btn-light.dropdown-toggle {
color: #212529;
background-color: #dae0e5;
border-color: #d3d9df;
}
.btn-light:not(:disabled):not(.disabled):active:focus, .btn-light:not(:disabled):not(.disabled).active:focus,
.show > .btn-light.dropdown-toggle:focus {
box-shadow: 0 0 0 0 rgba(216, 217, 219, 0.5);
}
.btn-dark {
color: #fff;
background-color: #343a40;
border-color: #343a40;
}
.btn-dark:hover {
color: #fff;
background-color: #23272b;
border-color: #1d2124;
}
.btn-dark:focus, .btn-dark.focus {
color: #fff;
background-color: #23272b;
border-color: #1d2124;
box-shadow: 0 0 0 0 rgba(82, 88, 93, 0.5);
}
.btn-dark.disabled, .btn-dark:disabled {
color: #fff;
background-color: #343a40;
border-color: #343a40;
}
.btn-dark:not(:disabled):not(.disabled):active, .btn-dark:not(:disabled):not(.disabled).active,
.show > .btn-dark.dropdown-toggle {
color: #fff;
background-color: #1d2124;
border-color: #171a1d;
}
.btn-dark:not(:disabled):not(.disabled):active:focus, .btn-dark:not(:disabled):not(.disabled).active:focus,
.show > .btn-dark.dropdown-toggle:focus {
box-shadow: 0 0 0 0 rgba(82, 88, 93, 0.5);
}
.btn-outline-primary {
color: #007bff;
border-color: #007bff;
}
.btn-outline-primary:hover {
color: #fff;
background-color: #007bff;
border-color: #007bff;
}
.btn-outline-primary:focus, .btn-outline-primary.focus {
box-shadow: 0 0 0 0 rgba(0, 123, 255, 0.5);
}
.btn-outline-primary.disabled, .btn-outline-primary:disabled {
color: #007bff;
background-color: transparent;
}
.btn-outline-primary:not(:disabled):not(.disabled):active, .btn-outline-primary:not(:disabled):not(.disabled).active,
.show > .btn-outline-primary.dropdown-toggle {
color: #fff;
background-color: #007bff;
border-color: #007bff;
}
.btn-outline-primary:not(:disabled):not(.disabled):active:focus, .btn-outline-primary:not(:disabled):not(.disabled).active:focus,
.show > .btn-outline-primary.dropdown-toggle:focus {
box-shadow: 0 0 0 0 rgba(0, 123, 255, 0.5);
}
.btn-outline-secondary {
color: #6c757d;
border-color: #6c757d;
}
.btn-outline-secondary:hover {
color: #fff;
background-color: #6c757d;
border-color: #6c757d;
}
.btn-outline-secondary:focus, .btn-outline-secondary.focus {
box-shadow: 0 0 0 0 rgba(108, 117, 125, 0.5);
}
.btn-outline-secondary.disabled, .btn-outline-secondary:disabled {
color: #6c757d;
background-color: transparent;
}
.btn-outline-secondary:not(:disabled):not(.disabled):active, .btn-outline-secondary:not(:disabled):not(.disabled).active,
.show > .btn-outline-secondary.dropdown-toggle {
color: #fff;
background-color: #6c757d;
border-color: #6c757d;
}
.btn-outline-secondary:not(:disabled):not(.disabled):active:focus, .btn-outline-secondary:not(:disabled):not(.disabled).active:focus,
.show > .btn-outline-secondary.dropdown-toggle:focus {
box-shadow: 0 0 0 0 rgba(108, 117, 125, 0.5);
}
.btn-outline-success {
color: #28a745;
border-color: #28a745;
}
.btn-outline-success:hover {
color: #fff;
background-color: #28a745;
border-color: #28a745;
}
.btn-outline-success:focus, .btn-outline-success.focus {
box-shadow: 0 0 0 0 rgba(40, 167, 69, 0.5);
}
.btn-outline-success.disabled, .btn-outline-success:disabled {
color: #28a745;
background-color: transparent;
}
.btn-outline-success:not(:disabled):not(.disabled):active, .btn-outline-success:not(:disabled):not(.disabled).active,
.show > .btn-outline-success.dropdown-toggle {
color: #fff;
background-color: #28a745;
border-color: #28a745;
}
.btn-outline-success:not(:disabled):not(.disabled):active:focus, .btn-outline-success:not(:disabled):not(.disabled).active:focus,
.show > .btn-outline-success.dropdown-toggle:focus {
box-shadow: 0 0 0 0 rgba(40, 167, 69, 0.5);
}
.btn-outline-info {
color: #17a2b8;
border-color: #17a2b8;
}
.btn-outline-info:hover {
color: #fff;
background-color: #17a2b8;
border-color: #17a2b8;
}
.btn-outline-info:focus, .btn-outline-info.focus {
box-shadow: 0 0 0 0 rgba(23, 162, 184, 0.5);
}
.btn-outline-info.disabled, .btn-outline-info:disabled {
color: #17a2b8;
background-color: transparent;
}
.btn-outline-info:not(:disabled):not(.disabled):active, .btn-outline-info:not(:disabled):not(.disabled).active,
.show > .btn-outline-info.dropdown-toggle {
color: #fff;
background-color: #17a2b8;
border-color: #17a2b8;
}
.btn-outline-info:not(:disabled):not(.disabled):active:focus, .btn-outline-info:not(:disabled):not(.disabled).active:focus,
.show > .btn-outline-info.dropdown-toggle:focus {
box-shadow: 0 0 0 0 rgba(23, 162, 184, 0.5);
}
.btn-outline-warning {
color: #ffc107;
border-color: #ffc107;
}
.btn-outline-warning:hover {
color: #212529;
background-color: #ffc107;
border-color: #ffc107;
}
.btn-outline-warning:focus, .btn-outline-warning.focus {
box-shadow: 0 0 0 0 rgba(255, 193, 7, 0.5);
}
.btn-outline-warning.disabled, .btn-outline-warning:disabled {
color: #ffc107;
background-color: transparent;
}
.btn-outline-warning:not(:disabled):not(.disabled):active, .btn-outline-warning:not(:disabled):not(.disabled).active,
.show > .btn-outline-warning.dropdown-toggle {
color: #212529;
background-color: #ffc107;
border-color: #ffc107;
}
.btn-outline-warning:not(:disabled):not(.disabled):active:focus, .btn-outline-warning:not(:disabled):not(.disabled).active:focus,
.show > .btn-outline-warning.dropdown-toggle:focus {
box-shadow: 0 0 0 0 rgba(255, 193, 7, 0.5);
}
.btn-outline-danger {
color: #dc3545;
border-color: #dc3545;
}
.btn-outline-danger:hover {
color: #fff;
background-color: #dc3545;
border-color: #dc3545;
}
.btn-outline-danger:focus, .btn-outline-danger.focus {
box-shadow: 0 0 0 0 rgba(220, 53, 69, 0.5);
}
.btn-outline-danger.disabled, .btn-outline-danger:disabled {
color: #dc3545;
background-color: transparent;
}
.btn-outline-danger:not(:disabled):not(.disabled):active, .btn-outline-danger:not(:disabled):not(.disabled).active,
.show > .btn-outline-danger.dropdown-toggle {
color: #fff;
background-color: #dc3545;
border-color: #dc3545;
}
.btn-outline-danger:not(:disabled):not(.disabled):active:focus, .btn-outline-danger:not(:disabled):not(.disabled).active:focus,
.show > .btn-outline-danger.dropdown-toggle:focus {
box-shadow: 0 0 0 0 rgba(220, 53, 69, 0.5);
}
.btn-outline-light {
color: #f8f9fa;
border-color: #f8f9fa;
}
.btn-outline-light:hover {
color: #212529;
background-color: #f8f9fa;
border-color: #f8f9fa;
}
.btn-outline-light:focus, .btn-outline-light.focus {
box-shadow: 0 0 0 0 rgba(248, 249, 250, 0.5);
}
.btn-outline-light.disabled, .btn-outline-light:disabled {
color: #f8f9fa;
background-color: transparent;
}
.btn-outline-light:not(:disabled):not(.disabled):active, .btn-outline-light:not(:disabled):not(.disabled).active,
.show > .btn-outline-light.dropdown-toggle {
color: #212529;
background-color: #f8f9fa;
border-color: #f8f9fa;
}
.btn-outline-light:not(:disabled):not(.disabled):active:focus, .btn-outline-light:not(:disabled):not(.disabled).active:focus,
.show > .btn-outline-light.dropdown-toggle:focus {
box-shadow: 0 0 0 0 rgba(248, 249, 250, 0.5);
}
.btn-outline-dark {
color: #343a40;
border-color: #343a40;
}
.btn-outline-dark:hover {
color: #fff;
background-color: #343a40;
border-color: #343a40;
}
.btn-outline-dark:focus, .btn-outline-dark.focus {
box-shadow: 0 0 0 0 rgba(52, 58, 64, 0.5);
}
.btn-outline-dark.disabled, .btn-outline-dark:disabled {
color: #343a40;
background-color: transparent;
}
.btn-outline-dark:not(:disabled):not(.disabled):active, .btn-outline-dark:not(:disabled):not(.disabled).active,
.show > .btn-outline-dark.dropdown-toggle {
color: #fff;
background-color: #343a40;
border-color: #343a40;
}
.btn-outline-dark:not(:disabled):not(.disabled):active:focus, .btn-outline-dark:not(:disabled):not(.disabled).active:focus,
.show > .btn-outline-dark.dropdown-toggle:focus {
box-shadow: 0 0 0 0 rgba(52, 58, 64, 0.5);
}
.btn-link {
font-weight: 400;
color: #007bff;
text-decoration: none;
}
.btn-link:hover {
color: #0056b3;
text-decoration: underline;
}
.btn-link:focus, .btn-link.focus {
text-decoration: underline;
}
.btn-link:disabled, .btn-link.disabled {
color: #6c757d;
pointer-events: none;
}
.btn-lg, .btn-group-lg > .btn {
padding: 0.5rem 1rem;
font-size: 1.25rem;
line-height: 1.5;
border-radius: 0.3rem;
}
.btn-sm, .btn-group-sm > .btn {
padding: 0.25rem 0.5rem;
font-size: 0.875rem;
line-height: 1.5;
border-radius: 0.2rem;
}
.btn-block {
display: block;
width: 100%;
}
.btn-block + .btn-block {
margin-top: 0.5rem;
}
input[type="submit"].btn-block,
input[type="reset"].btn-block,
input[type="button"].btn-block {
width: 100%;
}
.breadcrumb {
display: flex;
flex-wrap: wrap;
padding: 0.75rem 1rem;
margin-bottom: 1rem;
list-style: none;
background-color: #e9ecef;
border-radius: 0.25rem;
}
.breadcrumb-item {
display: flex;
}
.breadcrumb-item + .breadcrumb-item {
padding-left: 0.5rem;
}
.breadcrumb-item + .breadcrumb-item::before {
display: inline-block;
padding-right: 0.5rem;
color: #6c757d;
content: "/";
}
.breadcrumb-item + .breadcrumb-item:hover::before {
text-decoration: underline;
}
.breadcrumb-item + .breadcrumb-item:hover::before {
text-decoration: none;
}
.breadcrumb-item.active {
color: #6c757d;
}
.fade {
transition: opacity 0.15s linear;
}
@media (prefers-reduced-motion: reduce) {
.fade {
transition: none;
}
}
.fade:not(.show) {
opacity: 0;
}
.collapse:not(.show) {
display: none;
}
.collapsing {
position: relative;
height: 0;
overflow: hidden;
transition: height 0.35s ease;
}
@media (prefers-reduced-motion: reduce) {
.collapsing {
transition: none;
}
}
.dropup,
.dropright,
.dropdown,
.dropleft {
position: relative;
}
.dropdown-toggle {
white-space: nowrap;
}
.dropdown-toggle::after {
display: inline-block;
margin-left: 0.255em;
vertical-align: 0.255em;
content: "";
border-top: 0.3em solid;
border-right: 0.3em solid transparent;
border-bottom: 0;
border-left: 0.3em solid transparent;
}
.dropdown-toggle:empty::after {
margin-left: 0;
}
.dropdown-menu {
position: absolute;
top: 100%;
left: 0;
z-index: 1000;
display: none;
float: left;
min-width: 10rem;
padding: 0.5rem 0;
margin: 0.125rem 0 0;
font-size: 1rem;
color: #212529;
text-align: left;
list-style: none;
background-color: #fff;
background-clip: padding-box;
border: 1px solid rgba(0, 0, 0, 0.15);
border-radius: 0.25rem;
}
.dropdown-menu-left {
right: auto;
left: 0;
}
.dropdown-menu-right {
right: 0;
left: auto;
}
@media (min-width: 576px) {
.dropdown-menu-sm-left {
right: auto;
left: 0;
}
.dropdown-menu-sm-right {
right: 0;
left: auto;
}
}
@media (min-width: 768px) {
.dropdown-menu-md-left {
right: auto;
left: 0;
}
.dropdown-menu-md-right {
right: 0;
left: auto;
}
}
@media (min-width: 992px) {
.dropdown-menu-lg-left {
right: auto;
left: 0;
}
.dropdown-menu-lg-right {
right: 0;
left: auto;
}
}
@media (min-width: 1200px) {
.dropdown-menu-xl-left {
right: auto;
left: 0;
}
.dropdown-menu-xl-right {
right: 0;
left: auto;
}
}
.dropup .dropdown-menu {
top: auto;
bottom: 100%;
margin-top: 0;
margin-bottom: 0.125rem;
}
.dropup .dropdown-toggle::after {
display: inline-block;
margin-left: 0.255em;
vertical-align: 0.255em;
content: "";
border-top: 0;
border-right: 0.3em solid transparent;
border-bottom: 0.3em solid;
border-left: 0.3em solid transparent;
}
.dropup .dropdown-toggle:empty::after {
margin-left: 0;
}
.dropright .dropdown-menu {
top: 0;
right: auto;
left: 100%;
margin-top: 0;
margin-left: 0.125rem;
}
.dropright .dropdown-toggle::after {
display: inline-block;
margin-left: 0.255em;
vertical-align: 0.255em;
content: "";
border-top: 0.3em solid transparent;
border-right: 0;
border-bottom: 0.3em solid transparent;
border-left: 0.3em solid;
}
.dropright .dropdown-toggle:empty::after {
margin-left: 0;
}
.dropright .dropdown-toggle::after {
vertical-align: 0;
}
.dropleft .dropdown-menu {
top: 0;
right: 100%;
left: auto;
margin-top: 0;
margin-right: 0.125rem;
}
.dropleft .dropdown-toggle::after {
display: inline-block;
margin-left: 0.255em;
vertical-align: 0.255em;
content: "";
}
.dropleft .dropdown-toggle::after {
display: none;
}
.dropleft .dropdown-toggle::before {
display: inline-block;
margin-right: 0.255em;
vertical-align: 0.255em;
content: "";
border-top: 0.3em solid transparent;
border-right: 0.3em solid;
border-bottom: 0.3em solid transparent;
}
.dropleft .dropdown-toggle:empty::after {
margin-left: 0;
}
.dropleft .dropdown-toggle::before {
vertical-align: 0;
}
.dropdown-menu[x-placement^="top"], .dropdown-menu[x-placement^="right"], .dropdown-menu[x-placement^="bottom"], .dropdown-menu[x-placement^="left"] {
right: auto;
bottom: auto;
}
.dropdown-divider {
height: 0;
margin: 0.46875rem 0;
overflow: hidden;
border-top: 1px solid #e9ecef;
}
.dropdown-item {
display: block;
width: 100%;
padding: 0.25rem 1.5rem;
clear: both;
font-weight: 400;
color: #212529;
text-align: inherit;
white-space: nowrap;
background-color: transparent;
border: 0;
}
.dropdown-item:hover, .dropdown-item:focus {
color: #16181b;
text-decoration: none;
background-color: #f8f9fa;
}
.dropdown-item.active, .dropdown-item:active {
color: #fff;
text-decoration: none;
background-color: #007bff;
}
.dropdown-item.disabled, .dropdown-item:disabled {
color: #6c757d;
pointer-events: none;
background-color: transparent;
}
.dropdown-menu.show {
display: block;
}
.dropdown-header {
display: block;
padding: 0.5rem 1.5rem;
margin-bottom: 0;
font-size: 0.875rem;
color: #6c757d;
white-space: nowrap;
}
.dropdown-item-text {
display: block;
padding: 0.25rem 1.5rem;
color: #212529;
}
.btn-group,
.btn-group-vertical {
position: relative;
display: inline-flex;
vertical-align: middle;
}
.btn-group > .btn,
.btn-group-vertical > .btn {
position: relative;
flex: 1 1 auto;
}
.btn-group > .btn:hover,
.btn-group-vertical > .btn:hover {
z-index: 1;
}
.btn-group > .btn:focus, .btn-group > .btn:active, .btn-group > .btn.active,
.btn-group-vertical > .btn:focus,
.btn-group-vertical > .btn:active,
.btn-group-vertical > .btn.active {
z-index: 1;
}
.btn-toolbar {
display: flex;
flex-wrap: wrap;
justify-content: flex-start;
}
.btn-toolbar .input-group {
width: auto;
}
.btn-group > .btn:not(:first-child),
.btn-group > .btn-group:not(:first-child) {
margin-left: -1px;
}
.btn-group > .btn:not(:last-child):not(.dropdown-toggle),
.btn-group > .btn-group:not(:last-child) > .btn {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
.btn-group > .btn:not(:first-child),
.btn-group > .btn-group:not(:first-child) > .btn {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
.dropdown-toggle-split {
padding-right: 0.5625rem;
padding-left: 0.5625rem;
}
.dropdown-toggle-split::after,
.dropup .dropdown-toggle-split::after,
.dropright .dropdown-toggle-split::after {
margin-left: 0;
}
.dropleft .dropdown-toggle-split::before {
margin-right: 0;
}
.btn-sm + .dropdown-toggle-split, .btn-group-sm > .btn + .dropdown-toggle-split {
padding-right: 0.375rem;
padding-left: 0.375rem;
}
.btn-lg + .dropdown-toggle-split, .btn-group-lg > .btn + .dropdown-toggle-split {
padding-right: 0.75rem;
padding-left: 0.75rem;
}
.btn-group-vertical {
flex-direction: column;
align-items: flex-start;
justify-content: center;
}
.btn-group-vertical > .btn,
.btn-group-vertical > .btn-group {
width: 100%;
}
.btn-group-vertical > .btn:not(:first-child),
.btn-group-vertical > .btn-group:not(:first-child) {
margin-top: -1px;
}
.btn-group-vertical > .btn:not(:last-child):not(.dropdown-toggle),
.btn-group-vertical > .btn-group:not(:last-child) > .btn {
border-bottom-right-radius: 0;
border-bottom-left-radius: 0;
}
.btn-group-vertical > .btn:not(:first-child),
.btn-group-vertical > .btn-group:not(:first-child) > .btn {
border-top-left-radius: 0;
border-top-right-radius: 0;
}
.btn-group-toggle > .btn,
.btn-group-toggle > .btn-group > .btn {
margin-bottom: 0;
}
.btn-group-toggle > .btn input[type="radio"],
.btn-group-toggle > .btn input[type="checkbox"],
.btn-group-toggle > .btn-group > .btn input[type="radio"],
.btn-group-toggle > .btn-group > .btn input[type="checkbox"] {
position: absolute;
clip: rect(0, 0, 0, 0);
pointer-events: none;
}
.input-group {
position: relative;
display: flex;
flex-wrap: wrap;
align-items: stretch;
width: 100%;
}
.input-group > .form-control,
.input-group > .form-control-plaintext,
.input-group > .custom-select,
.input-group > .custom-file {
position: relative;
flex: 1 1 auto;
width: 1%;
min-width: 0;
margin-bottom: 0;
}
.input-group > .form-control + .form-control,
.input-group > .form-control + .custom-select,
.input-group > .form-control + .custom-file,
.input-group > .form-control-plaintext + .form-control,
.input-group > .form-control-plaintext + .custom-select,
.input-group > .form-control-plaintext + .custom-file,
.input-group > .custom-select + .form-control,
.input-group > .custom-select + .custom-select,
.input-group > .custom-select + .custom-file,
.input-group > .custom-file + .form-control,
.input-group > .custom-file + .custom-select,
.input-group > .custom-file + .custom-file {
margin-left: -1px;
}
.input-group > .form-control:focus,
.input-group > .custom-select:focus,
.input-group > .custom-file .custom-file-input:focus ~ .custom-file-label {
z-index: 3;
}
.input-group > .custom-file .custom-file-input:focus {
z-index: 4;
}
.input-group > .form-control:not(:last-child),
.input-group > .custom-select:not(:last-child) {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
.input-group > .form-control:not(:first-child),
.input-group > .custom-select:not(:first-child) {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
.input-group > .custom-file {
display: flex;
align-items: center;
}
.input-group > .custom-file:not(:last-child) .custom-file-label,
.input-group > .custom-file:not(:last-child) .custom-file-label::after {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
.input-group > .custom-file:not(:first-child) .custom-file-label {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
.input-group-prepend,
.input-group-append {
display: flex;
}
.input-group-prepend .btn,
.input-group-append .btn {
position: relative;
z-index: 2;
}
.input-group-prepend .btn:focus,
.input-group-append .btn:focus {
z-index: 3;
}
.input-group-prepend .btn + .btn,
.input-group-prepend .btn + .input-group-text,
.input-group-prepend .input-group-text + .input-group-text,
.input-group-prepend .input-group-text + .btn,
.input-group-append .btn + .btn,
.input-group-append .btn + .input-group-text,
.input-group-append .input-group-text + .input-group-text,
.input-group-append .input-group-text + .btn {
margin-left: -1px;
}
.input-group-prepend {
margin-right: -1px;
}
.input-group-append {
margin-left: -1px;
}
.input-group-text {
display: flex;
align-items: center;
padding: 0.75rem 0.75rem;
margin-bottom: 0;
font-size: 1rem;
font-weight: 400;
line-height: 1.5;
color: #495057;
text-align: center;
white-space: nowrap;
background-color: #e9ecef;
border: 1px solid #ced4da;
border-radius: 0.25rem;
}
.input-group-text input[type="radio"],
.input-group-text input[type="checkbox"] {
margin-top: 0;
}
.input-group-lg > .form-control:not(textarea),
.input-group-lg > .custom-select {
height: calc(1.5em + 1rem + 2px);
}
.input-group-lg > .form-control,
.input-group-lg > .custom-select,
.input-group-lg > .input-group-prepend > .input-group-text,
.input-group-lg > .input-group-append > .input-group-text,
.input-group-lg > .input-group-prepend > .btn,
.input-group-lg > .input-group-append > .btn {
padding: 0.5rem 1rem;
font-size: 1.25rem;
line-height: 1.5;
border-radius: 0.3rem;
}
.input-group-sm > .form-control:not(textarea),
.input-group-sm > .custom-select {
height: calc(1.5em + 0.5rem + 2px);
}
.input-group-sm > .form-control,
.input-group-sm > .custom-select,
.input-group-sm > .input-group-prepend > .input-group-text,
.input-group-sm > .input-group-append > .input-group-text,
.input-group-sm > .input-group-prepend > .btn,
.input-group-sm > .input-group-append > .btn {
padding: 0.25rem 0.5rem;
font-size: 0.875rem;
line-height: 1.5;
border-radius: 0.2rem;
}
.input-group-lg > .custom-select,
.input-group-sm > .custom-select {
padding-right: 1.75rem;
}
.input-group > .input-group-prepend > .btn,
.input-group > .input-group-prepend > .input-group-text,
.input-group > .input-group-append:not(:last-child) > .btn,
.input-group > .input-group-append:not(:last-child) > .input-group-text,
.input-group > .input-group-append:last-child > .btn:not(:last-child):not(.dropdown-toggle),
.input-group > .input-group-append:last-child > .input-group-text:not(:last-child) {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
.input-group > .input-group-append > .btn,
.input-group > .input-group-append > .input-group-text,
.input-group > .input-group-prepend:not(:first-child) > .btn,
.input-group > .input-group-prepend:not(:first-child) > .input-group-text,
.input-group > .input-group-prepend:first-child > .btn:not(:first-child),
.input-group > .input-group-prepend:first-child > .input-group-text:not(:first-child) {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
.custom-control {
position: relative;
display: block;
min-height: 1.5rem;
padding-left: 1.5rem;
}
.custom-control-inline {
display: inline-flex;
margin-right: 1rem;
}
.custom-control-input {
position: absolute;
left: 0;
z-index: -1;
width: 1rem;
height: 1.25rem;
opacity: 0;
}
.custom-control-input:checked ~ .custom-control-label::before {
color: #fff;
border-color: #007bff;
background-color: #007bff;
}
.custom-control-input:focus ~ .custom-control-label::before {
box-shadow: 0 0 0 0 rgba(0, 123, 255, 0);
}
.custom-control-input:focus:not(:checked) ~ .custom-control-label::before {
border-color: #80bdff;
}
.custom-control-input:not(:disabled):active ~ .custom-control-label::before {
color: #fff;
background-color: #b3d7ff;
border-color: #b3d7ff;
}
.custom-control-input[disabled] ~ .custom-control-label, .custom-control-input:disabled ~ .custom-control-label {
color: #6c757d;
}
.custom-control-input[disabled] ~ .custom-control-label::before, .custom-control-input:disabled ~ .custom-control-label::before {
background-color: #e9ecef;
}
.custom-control-label {
position: relative;
margin-bottom: 0;
vertical-align: top;
}
.custom-control-label::before {
position: absolute;
top: 0.25rem;
left: -1.5rem;
display: block;
width: 1rem;
height: 1rem;
pointer-events: none;
content: "";
background-color: #fff;
border: #adb5bd solid 1px;
}
.custom-control-label::after {
position: absolute;
top: 0.25rem;
left: -1.5rem;
display: block;
width: 1rem;
height: 1rem;
content: "";
background: no-repeat 50% / 50% 50%;
}
.custom-checkbox .custom-control-label::before {
border-radius: 0.25rem;
}
.custom-checkbox .custom-control-input:checked ~ .custom-control-label::after {
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26l2.974 2.99L8 2.193z'/%3e%3c/svg%3e");
}
.custom-checkbox .custom-control-input:indeterminate ~ .custom-control-label::before {
border-color: #007bff;
background-color: #007bff;
}
.custom-checkbox .custom-control-input:indeterminate ~ .custom-control-label::after {
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='4' viewBox='0 0 4 4'%3e%3cpath stroke='%23fff' d='M0 2h4'/%3e%3c/svg%3e");
}
.custom-checkbox .custom-control-input:disabled:checked ~ .custom-control-label::before {
background-color: rgba(0, 123, 255, 0.5);
}
.custom-checkbox .custom-control-input:disabled:indeterminate ~ .custom-control-label::before {
background-color: rgba(0, 123, 255, 0.5);
}
.custom-radio .custom-control-label::before {
border-radius: 50%;
}
.custom-radio .custom-control-input:checked ~ .custom-control-label::after {
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e");
}
.custom-radio .custom-control-input:disabled:checked ~ .custom-control-label::before {
background-color: rgba(0, 123, 255, 0.5);
}
.custom-switch {
padding-left: 2.25rem;
}
.custom-switch .custom-control-label::before {
left: -2.25rem;
width: 1.75rem;
pointer-events: all;
border-radius: 0.5rem;
}
.custom-switch .custom-control-label::after {
top: calc(0.25rem + 2px);
left: calc(-2.25rem + 2px);
width: calc(1rem - 4px);
height: calc(1rem - 4px);
background-color: #adb5bd;
border-radius: 0.5rem;
transition: transform 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
}
@media (prefers-reduced-motion: reduce) {
.custom-switch .custom-control-label::after {
transition: none;
}
}
.custom-switch .custom-control-input:checked ~ .custom-control-label::after {
background-color: #fff;
transform: translateX(0.75rem);
}
.custom-switch .custom-control-input:disabled:checked ~ .custom-control-label::before {
background-color: rgba(0, 123, 255, 0.5);
}
.custom-select {
display: inline-block;
width: 100%;
height: calc(1.5em + 1.5rem + 2px);
padding: 0.75rem 1.75rem 0.75rem 0.75rem;
font-size: 1rem;
font-weight: 400;
line-height: 1.5;
color: #495057;
vertical-align: middle;
background: #fff url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right 0.75rem center/8px 10px;
border: 1px solid #ced4da;
border-radius: 0.25rem;
appearance: none;
}
.custom-select:focus {
border-color: #80bdff;
outline: 0;
box-shadow: 0 0 0 0 rgba(0, 123, 255, 0);
}
.custom-select:focus::-ms-value {
color: #495057;
background-color: #fff;
}
.custom-select[multiple], .custom-select[size]:not([size="1"]) {
height: auto;
padding-right: 0.75rem;
background-image: none;
}
.custom-select:disabled {
color: #6c757d;
background-color: #e9ecef;
}
.custom-select::-ms-expand {
display: none;
}
.custom-select:-moz-focusring {
color: transparent;
text-shadow: 0 0 0 #495057;
}
.custom-select-sm {
height: calc(1.5em + 0.5rem + 2px);
padding-top: 0.25rem;
padding-bottom: 0.25rem;
padding-left: 0.5rem;
font-size: 0.875rem;
}
.custom-select-lg {
height: calc(1.5em + 1rem + 2px);
padding-top: 0.5rem;
padding-bottom: 0.5rem;
padding-left: 1rem;
font-size: 1.25rem;
}
.custom-file {
position: relative;
display: inline-block;
width: 100%;
height: calc(1.5em + 1.5rem + 2px);
margin-bottom: 0;
}
.custom-file-input {
position: relative;
z-index: 2;
width: 100%;
height: calc(1.5em + 1.5rem + 2px);
margin: 0;
opacity: 0;
}
.custom-file-input:focus ~ .custom-file-label {
border-color: #80bdff;
box-shadow: 0 0 0 0 rgba(0, 123, 255, 0);
}
.custom-file-input[disabled] ~ .custom-file-label,
.custom-file-input:disabled ~ .custom-file-label {
background-color: #e9ecef;
}
.custom-file-input:lang(en) ~ .custom-file-label::after {
content: "Browse";
}
.custom-file-input ~ .custom-file-label[data-browse]::after {
content: attr(data-browse);
}
.custom-file-label {
position: absolute;
top: 0;
right: 0;
left: 0;
z-index: 1;
height: calc(1.5em + 1.5rem + 2px);
padding: 0.75rem 0.75rem;
font-weight: 400;
line-height: 1.5;
color: #495057;
background-color: #fff;
border: 1px solid #ced4da;
border-radius: 0.25rem;
}
.custom-file-label::after {
position: absolute;
top: 0;
right: 0;
bottom: 0;
z-index: 3;
display: block;
height: calc(1.5em + 1.5rem);
padding: 0.75rem 0.75rem;
line-height: 1.5;
color: #495057;
content: "Browse";
background-color: #e9ecef;
border-left: inherit;
border-radius: 0 0.25rem 0.25rem 0;
}
.custom-range {
width: 100%;
height: 1rem;
padding: 0;
background-color: transparent;
appearance: none;
}
.custom-range:focus {
outline: none;
}
.custom-range:focus::-webkit-slider-thumb {
box-shadow: 0 0 0 1px #fff, 0 0 0 0 rgba(0, 123, 255, 0);
}
.custom-range:focus::-moz-range-thumb {
box-shadow: 0 0 0 1px #fff, 0 0 0 0 rgba(0, 123, 255, 0);
}
.custom-range:focus::-ms-thumb {
box-shadow: 0 0 0 1px #fff, 0 0 0 0 rgba(0, 123, 255, 0);
}
.custom-range::-moz-focus-outer {
border: 0;
}
.custom-range::-webkit-slider-thumb {
width: 1rem;
height: 1rem;
margin-top: -0.25rem;
background-color: #007bff;
border: 0;
border-radius: 1rem;
transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
appearance: none;
}
@media (prefers-reduced-motion: reduce) {
.custom-range::-webkit-slider-thumb {
transition: none;
}
}
.custom-range::-webkit-slider-thumb:active {
background-color: #b3d7ff;
}
.custom-range::-webkit-slider-runnable-track {
width: 100%;
height: 0.5rem;
color: transparent;
cursor: pointer;
background-color: #dee2e6;
border-color: transparent;
border-radius: 1rem;
}
.custom-range::-moz-range-thumb {
width: 1rem;
height: 1rem;
background-color: #007bff;
border: 0;
border-radius: 1rem;
transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
appearance: none;
}
@media (prefers-reduced-motion: reduce) {
.custom-range::-moz-range-thumb {
transition: none;
}
}
.custom-range::-moz-range-thumb:active {
background-color: #b3d7ff;
}
.custom-range::-moz-range-track {
width: 100%;
height: 0.5rem;
color: transparent;
cursor: pointer;
background-color: #dee2e6;
border-color: transparent;
border-radius: 1rem;
}
.custom-range::-ms-thumb {
width: 1rem;
height: 1rem;
margin-top: 0;
margin-right: 0;
margin-left: 0;
background-color: #007bff;
border: 0;
border-radius: 1rem;
transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
appearance: none;
}
@media (prefers-reduced-motion: reduce) {
.custom-range::-ms-thumb {
transition: none;
}
}
.custom-range::-ms-thumb:active {
background-color: #b3d7ff;
}
.custom-range::-ms-track {
width: 100%;
height: 0.5rem;
color: transparent;
cursor: pointer;
background-color: transparent;
border-color: transparent;
border-width: 0.5rem;
}
.custom-range::-ms-fill-lower {
background-color: #dee2e6;
border-radius: 1rem;
}
.custom-range::-ms-fill-upper {
margin-right: 15px;
background-color: #dee2e6;
border-radius: 1rem;
}
.custom-range:disabled::-webkit-slider-thumb {
background-color: #adb5bd;
}
.custom-range:disabled::-webkit-slider-runnable-track {
cursor: default;
}
.custom-range:disabled::-moz-range-thumb {
background-color: #adb5bd;
}
.custom-range:disabled::-moz-range-track {
cursor: default;
}
.custom-range:disabled::-ms-thumb {
background-color: #adb5bd;
}
.custom-control-label::before,
.custom-file-label,
.custom-select {
transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
}
@media (prefers-reduced-motion: reduce) {
.custom-control-label::before,
.custom-file-label,
.custom-select {
transition: none;
}
}
.card {
position: relative;
display: flex;
flex-direction: column;
min-width: 0;
word-wrap: break-word;
background-color: #fff;
background-clip: border-box;
border: 1px solid rgba(0, 0, 0, 0.125);
border-radius: 0.25rem;
}
.card > hr {
margin-right: 0;
margin-left: 0;
}
.card > .list-group {
border-top: inherit;
border-bottom: inherit;
}
.card > .list-group:first-child {
border-top-width: 0;
border-top-left-radius: calc(0.25rem - 1px);
border-top-right-radius: calc(0.25rem - 1px);
}
.card > .list-group:last-child {
border-bottom-width: 0;
border-bottom-right-radius: calc(0.25rem - 1px);
border-bottom-left-radius: calc(0.25rem - 1px);
}
.card-body {
flex: 1 1 auto;
min-height: 1px;
padding: 1.25rem;
}
.card-title {
margin-bottom: 0.75rem;
}
.card-subtitle {
margin-top: -0.375rem;
margin-bottom: 0;
}
.card-text:last-child {
margin-bottom: 0;
}
.card-link:hover {
text-decoration: none;
}
.card-link + .card-link {
margin-left: 1.25rem;
}
.card-header {
padding: 0.75rem 1.25rem;
margin-bottom: 0;
background-color: rgba(0, 0, 0, 0.03);
border-bottom: 1px solid rgba(0, 0, 0, 0.125);
}
.card-header:first-child {
border-radius: calc(0.25rem - 1px) calc(0.25rem - 1px) 0 0;
}
.card-header + .list-group .list-group-item:first-child {
border-top: 0;
}
.card-footer {
padding: 0.75rem 1.25rem;
background-color: rgba(0, 0, 0, 0.03);
border-top: 1px solid rgba(0, 0, 0, 0.125);
}
.card-footer:last-child {
border-radius: 0 0 calc(0.25rem - 1px) calc(0.25rem - 1px);
}
.card-header-tabs {
margin-right: -0.625rem;
margin-bottom: -0.75rem;
margin-left: -0.625rem;
border-bottom: 0;
}
.card-header-pills {
margin-right: -0.625rem;
margin-left: -0.625rem;
}
.card-img-overlay {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
padding: 1.25rem;
}
.card-img,
.card-img-top,
.card-img-bottom {
flex-shrink: 0;
width: 100%;
}
.card-img,
.card-img-top {
border-top-left-radius: calc(0.25rem - 1px);
border-top-right-radius: calc(0.25rem - 1px);
}
.card-img,
.card-img-bottom {
border-bottom-right-radius: calc(0.25rem - 1px);
border-bottom-left-radius: calc(0.25rem - 1px);
}
.card-deck .card {
margin-bottom: 15px;
}
@media (min-width: 576px) {
.card-deck {
display: flex;
flex-flow: row wrap;
margin-right: -15px;
margin-left: -15px;
}
.card-deck .card {
flex: 1 0 0%;
margin-right: 15px;
margin-bottom: 0;
margin-left: 15px;
}
}
.card-group > .card {
margin-bottom: 15px;
}
@media (min-width: 576px) {
.card-group {
display: flex;
flex-flow: row wrap;
}
.card-group > .card {
flex: 1 0 0%;
margin-bottom: 0;
}
.card-group > .card + .card {
margin-left: 0;
border-left: 0;
}
.card-group > .card:not(:last-child) {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
.card-group > .card:not(:last-child) .card-img-top,
.card-group > .card:not(:last-child) .card-header {
border-top-right-radius: 0;
}
.card-group > .card:not(:last-child) .card-img-bottom,
.card-group > .card:not(:last-child) .card-footer {
border-bottom-right-radius: 0;
}
.card-group > .card:not(:first-child) {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
.card-group > .card:not(:first-child) .card-img-top,
.card-group > .card:not(:first-child) .card-header {
border-top-left-radius: 0;
}
.card-group > .card:not(:first-child) .card-img-bottom,
.card-group > .card:not(:first-child) .card-footer {
border-bottom-left-radius: 0;
}
}
.card-columns .card {
margin-bottom: 0.75rem;
}
@media (min-width: 576px) {
.card-columns {
column-count: 3;
column-gap: 1.25rem;
orphans: 1;
widows: 1;
}
.card-columns .card {
display: inline-block;
width: 100%;
}
}
.accordion > .card {
overflow: hidden;
}
.accordion > .card:not(:last-of-type) {
border-bottom: 0;
border-bottom-right-radius: 0;
border-bottom-left-radius: 0;
}
.accordion > .card:not(:first-of-type) {
border-top-left-radius: 0;
border-top-right-radius: 0;
}
.accordion > .card > .card-header {
border-radius: 0;
margin-bottom: -1px;
}
.pagination {
display: flex;
padding-left: 0;
list-style: none;
border-radius: 0.25rem;
}
.page-link {
position: relative;
display: block;
padding: 0.5rem 0.75rem;
margin-left: -1px;
line-height: 1.25;
color: #007bff;
background-color: #fff;
border: 1px solid #dee2e6;
}
.page-link:hover {
z-index: 2;
color: #0056b3;
text-decoration: none;
background-color: #e9ecef;
border-color: #dee2e6;
}
.page-link:focus {
z-index: 3;
outline: 0;
box-shadow: 0 0 0 0 rgba(0, 123, 255, 0);
}
.page-item:first-child .page-link {
margin-left: 0;
border-top-left-radius: 0.25rem;
border-bottom-left-radius: 0.25rem;
}
.page-item:last-child .page-link {
border-top-right-radius: 0.25rem;
border-bottom-right-radius: 0.25rem;
}
.page-item.active .page-link {
z-index: 3;
color: #fff;
background-color: #007bff;
border-color: #007bff;
}
.page-item.disabled .page-link {
color: #6c757d;
pointer-events: none;
cursor: auto;
background-color: #fff;
border-color: #dee2e6;
}
.pagination-lg .page-link {
padding: 0.75rem 1.5rem;
font-size: 1.25rem;
line-height: 1.5;
}
.pagination-lg .page-item:first-child .page-link {
border-top-left-radius: 0.3rem;
border-bottom-left-radius: 0.3rem;
}
.pagination-lg .page-item:last-child .page-link {
border-top-right-radius: 0.3rem;
border-bottom-right-radius: 0.3rem;
}
.pagination-sm .page-link {
padding: 0.25rem 0.5rem;
font-size: 0.875rem;
line-height: 1.5;
}
.pagination-sm .page-item:first-child .page-link {
border-top-left-radius: 0.2rem;
border-bottom-left-radius: 0.2rem;
}
.pagination-sm .page-item:last-child .page-link {
border-top-right-radius: 0.2rem;
border-bottom-right-radius: 0.2rem;
}
.badge {
display: inline-block;
padding: 0.25em 0.4em;
font-size: 75%;
font-weight: 700;
line-height: 1;
text-align: center;
white-space: nowrap;
vertical-align: baseline;
border-radius: 0.25rem;
transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
}
@media (prefers-reduced-motion: reduce) {
.badge {
transition: none;
}
}
a.badge:hover, a.badge:focus {
text-decoration: none;
}
.badge:empty {
display: none;
}
.btn .badge {
position: relative;
top: -1px;
}
.badge-pill {
padding-right: 0.6em;
padding-left: 0.6em;
border-radius: 10rem;
}
.badge-primary {
color: #fff;
background-color: #007bff;
}
a.badge-primary:hover, a.badge-primary:focus {
color: #fff;
background-color: #0062cc;
}
a.badge-primary:focus, a.badge-primary.focus {
outline: 0;
box-shadow: 0 0 0 0 rgba(0, 123, 255, 0.5);
}
.badge-secondary {
color: #fff;
background-color: #6c757d;
}
a.badge-secondary:hover, a.badge-secondary:focus {
color: #fff;
background-color: #545b62;
}
a.badge-secondary:focus, a.badge-secondary.focus {
outline: 0;
box-shadow: 0 0 0 0 rgba(108, 117, 125, 0.5);
}
.badge-success {
color: #fff;
background-color: #28a745;
}
a.badge-success:hover, a.badge-success:focus {
color: #fff;
background-color: #1e7e34;
}
a.badge-success:focus, a.badge-success.focus {
outline: 0;
box-shadow: 0 0 0 0 rgba(40, 167, 69, 0.5);
}
.badge-info {
color: #fff;
background-color: #17a2b8;
}
a.badge-info:hover, a.badge-info:focus {
color: #fff;
background-color: #117a8b;
}
a.badge-info:focus, a.badge-info.focus {
outline: 0;
box-shadow: 0 0 0 0 rgba(23, 162, 184, 0.5);
}
.badge-warning {
color: #212529;
background-color: #ffc107;
}
a.badge-warning:hover, a.badge-warning:focus {
color: #212529;
background-color: #d39e00;
}
a.badge-warning:focus, a.badge-warning.focus {
outline: 0;
box-shadow: 0 0 0 0 rgba(255, 193, 7, 0.5);
}
.badge-danger {
color: #fff;
background-color: #dc3545;
}
a.badge-danger:hover, a.badge-danger:focus {
color: #fff;
background-color: #bd2130;
}
a.badge-danger:focus, a.badge-danger.focus {
outline: 0;
box-shadow: 0 0 0 0 rgba(220, 53, 69, 0.5);
}
.badge-light {
color: #212529;
background-color: #f8f9fa;
}
a.badge-light:hover, a.badge-light:focus {
color: #212529;
background-color: #dae0e5;
}
a.badge-light:focus, a.badge-light.focus {
outline: 0;
box-shadow: 0 0 0 0 rgba(248, 249, 250, 0.5);
}
.badge-dark {
color: #fff;
background-color: #343a40;
}
a.badge-dark:hover, a.badge-dark:focus {
color: #fff;
background-color: #1d2124;
}
a.badge-dark:focus, a.badge-dark.focus {
outline: 0;
box-shadow: 0 0 0 0 rgba(52, 58, 64, 0.5);
}
.tooltip {
position: absolute;
z-index: 1070;
display: block;
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
font-style: normal;
font-weight: 400;
line-height: 1.5;
text-align: left;
text-align: start;
text-decoration: none;
text-shadow: none;
text-transform: none;
letter-spacing: normal;
word-break: normal;
word-spacing: normal;
white-space: normal;
line-break: auto;
font-size: 0.875rem;
word-wrap: break-word;
opacity: 0;
}
.tooltip.show {
opacity: 0.9;
}
.tooltip .arrow {
position: absolute;
display: block;
width: 0.8rem;
height: 0.4rem;
}
.tooltip .arrow::before {
position: absolute;
content: "";
border-color: transparent;
border-style: solid;
}
.bs-tooltip-top, .bs-tooltip-auto[x-placement^="top"] {
padding: 0.4rem 0;
}
.bs-tooltip-top .arrow, .bs-tooltip-auto[x-placement^="top"] .arrow {
bottom: 0;
}
.bs-tooltip-top .arrow::before, .bs-tooltip-auto[x-placement^="top"] .arrow::before {
top: 0;
border-width: 0.4rem 0.4rem 0;
border-top-color: #000;
}
.bs-tooltip-right, .bs-tooltip-auto[x-placement^="right"] {
padding: 0 0.4rem;
}
.bs-tooltip-right .arrow, .bs-tooltip-auto[x-placement^="right"] .arrow {
left: 0;
width: 0.4rem;
height: 0.8rem;
}
.bs-tooltip-right .arrow::before, .bs-tooltip-auto[x-placement^="right"] .arrow::before {
right: 0;
border-width: 0.4rem 0.4rem 0.4rem 0;
border-right-color: #000;
}
.bs-tooltip-bottom, .bs-tooltip-auto[x-placement^="bottom"] {
padding: 0.4rem 0;
}
.bs-tooltip-bottom .arrow, .bs-tooltip-auto[x-placement^="bottom"] .arrow {
top: 0;
}
.bs-tooltip-bottom .arrow::before, .bs-tooltip-auto[x-placement^="bottom"] .arrow::before {
bottom: 0;
border-width: 0 0.4rem 0.4rem;
border-bottom-color: #000;
}
.bs-tooltip-left, .bs-tooltip-auto[x-placement^="left"] {
padding: 0 0.4rem;
}
.bs-tooltip-left .arrow, .bs-tooltip-auto[x-placement^="left"] .arrow {
right: 0;
width: 0.4rem;
height: 0.8rem;
}
.bs-tooltip-left .arrow::before, .bs-tooltip-auto[x-placement^="left"] .arrow::before {
left: 0;
border-width: 0.4rem 0 0.4rem 0.4rem;
border-left-color: #000;
}
.tooltip-inner {
max-width: 200px;
padding: 0.25rem 0.5rem;
color: #fff;
text-align: center;
background-color: #000;
border-radius: 0.25rem;
}
.alert {
position: relative;
padding: 0.75rem 1.25rem;
margin-bottom: 1rem;
border: 1px solid transparent;
border-radius: 0.25rem;
}
.alert-heading {
color: inherit;
}
.alert-link {
font-weight: 700;
}
.alert-dismissible {
padding-right: 4rem;
}
.alert-dismissible .close {
position: absolute;
top: 0;
right: 0;
padding: 0.75rem 1.25rem;
color: inherit;
}
.alert-primary {
color: #004085;
background-color: #cce5ff;
border-color: #b8daff;
}
.alert-primary hr {
border-top-color: #9fcdff;
}
.alert-primary .alert-link {
color: #002752;
}
.alert-secondary {
color: #383d41;
background-color: #e2e3e5;
border-color: #d6d8db;
}
.alert-secondary hr {
border-top-color: #c8cbcf;
}
.alert-secondary .alert-link {
color: #202326;
}
.alert-success {
color: #155724;
background-color: #d4edda;
border-color: #c3e6cb;
}
.alert-success hr {
border-top-color: #b1dfbb;
}
.alert-success .alert-link {
color: #0b2e13;
}
.alert-info {
color: #0c5460;
background-color: #d1ecf1;
border-color: #bee5eb;
}
.alert-info hr {
border-top-color: #abdde5;
}
.alert-info .alert-link {
color: #062c33;
}
.alert-warning {
color: #856404;
background-color: #fff3cd;
border-color: #ffeeba;
}
.alert-warning hr {
border-top-color: #ffe8a1;
}
.alert-warning .alert-link {
color: #533f03;
}
.alert-danger {
color: #721c24;
background-color: #f8d7da;
border-color: #f5c6cb;
}
.alert-danger hr {
border-top-color: #f1b0b7;
}
.alert-danger .alert-link {
color: #491217;
}
.alert-light {
color: #818182;
background-color: #fefefe;
border-color: #fdfdfe;
}
.alert-light hr {
border-top-color: #ececf6;
}
.alert-light .alert-link {
color: #686868;
}
.alert-dark {
color: #1b1e21;
background-color: #d6d8d9;
border-color: #c6c8ca;
}
.alert-dark hr {
border-top-color: #b9bbbe;
}
.alert-dark .alert-link {
color: #040505;
}
@keyframes progress-bar-stripes {
from {
background-position: 1rem 0;
}
to {
background-position: 0 0;
}
}
.progress {
display: flex;
height: 1rem;
overflow: hidden;
line-height: 0;
font-size: 0.75rem;
background-color: #e9ecef;
border-radius: 0.25rem;
}
.progress-bar {
display: flex;
flex-direction: column;
justify-content: center;
overflow: hidden;
color: #fff;
text-align: center;
white-space: nowrap;
background-color: #007bff;
transition: width 0.6s ease;
}
@media (prefers-reduced-motion: reduce) {
.progress-bar {
transition: none;
}
}
.progress-bar-striped {
background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
background-size: 1rem 1rem;
}
.progress-bar-animated {
animation: progress-bar-stripes 1s linear infinite;
}
@media (prefers-reduced-motion: reduce) {
.progress-bar-animated {
animation: none;
}
}
.media {
display: flex;
align-items: flex-start;
}
.media-body {
flex: 1;
}
.list-group {
display: flex;
flex-direction: column;
padding-left: 0;
margin-bottom: 0;
border-radius: 0.25rem;
}
.list-group-item-action {
width: 100%;
color: #495057;
text-align: inherit;
}
.list-group-item-action:hover, .list-group-item-action:focus {
z-index: 1;
color: #495057;
text-decoration: none;
background-color: #f8f9fa;
}
.list-group-item-action:active {
color: #212529;
background-color: #e9ecef;
}
.list-group-item {
position: relative;
display: block;
padding: 0.75rem 1.25rem;
background-color: #fff;
border: 1px solid rgba(0, 0, 0, 0.125);
}
.list-group-item:first-child {
border-top-left-radius: inherit;
border-top-right-radius: inherit;
}
.list-group-item:last-child {
border-bottom-right-radius: inherit;
border-bottom-left-radius: inherit;
}
.list-group-item.disabled, .list-group-item:disabled {
color: #6c757d;
pointer-events: none;
background-color: #fff;
}
.list-group-item.active {
z-index: 2;
color: #fff;
background-color: #007bff;
border-color: #007bff;
}
.list-group-item + .list-group-item {
border-top-width: 0;
}
.list-group-item + .list-group-item.active {
margin-top: -1px;
border-top-width: 1px;
}
.list-group-horizontal {
flex-direction: row;
}
.list-group-horizontal > .list-group-item:first-child {
border-bottom-left-radius: 0.25rem;
border-top-right-radius: 0;
}
.list-group-horizontal > .list-group-item:last-child {
border-top-right-radius: 0.25rem;
border-bottom-left-radius: 0;
}
.list-group-horizontal > .list-group-item.active {
margin-top: 0;
}
.list-group-horizontal > .list-group-item + .list-group-item {
border-top-width: 1px;
border-left-width: 0;
}
.list-group-horizontal > .list-group-item + .list-group-item.active {
margin-left: -1px;
border-left-width: 1px;
}
@media (min-width: 576px) {
.list-group-horizontal-sm {
flex-direction: row;
}
.list-group-horizontal-sm > .list-group-item:first-child {
border-bottom-left-radius: 0.25rem;
border-top-right-radius: 0;
}
.list-group-horizontal-sm > .list-group-item:last-child {
border-top-right-radius: 0.25rem;
border-bottom-left-radius: 0;
}
.list-group-horizontal-sm > .list-group-item.active {
margin-top: 0;
}
.list-group-horizontal-sm > .list-group-item + .list-group-item {
border-top-width: 1px;
border-left-width: 0;
}
.list-group-horizontal-sm > .list-group-item + .list-group-item.active {
margin-left: -1px;
border-left-width: 1px;
}
}
@media (min-width: 768px) {
.list-group-horizontal-md {
flex-direction: row;
}
.list-group-horizontal-md > .list-group-item:first-child {
border-bottom-left-radius: 0.25rem;
border-top-right-radius: 0;
}
.list-group-horizontal-md > .list-group-item:last-child {
border-top-right-radius: 0.25rem;
border-bottom-left-radius: 0;
}
.list-group-horizontal-md > .list-group-item.active {
margin-top: 0;
}
.list-group-horizontal-md > .list-group-item + .list-group-item {
border-top-width: 1px;
border-left-width: 0;
}
.list-group-horizontal-md > .list-group-item + .list-group-item.active {
margin-left: -1px;
border-left-width: 1px;
}
}
@media (min-width: 992px) {
.list-group-horizontal-lg {
flex-direction: row;
}
.list-group-horizontal-lg > .list-group-item:first-child {
border-bottom-left-radius: 0.25rem;
border-top-right-radius: 0;
}
.list-group-horizontal-lg > .list-group-item:last-child {
border-top-right-radius: 0.25rem;
border-bottom-left-radius: 0;
}
.list-group-horizontal-lg > .list-group-item.active {
margin-top: 0;
}
.list-group-horizontal-lg > .list-group-item + .list-group-item {
border-top-width: 1px;
border-left-width: 0;
}
.list-group-horizontal-lg > .list-group-item + .list-group-item.active {
margin-left: -1px;
border-left-width: 1px;
}
}
@media (min-width: 1200px) {
.list-group-horizontal-xl {
flex-direction: row;
}
.list-group-horizontal-xl > .list-group-item:first-child {
border-bottom-left-radius: 0.25rem;
border-top-right-radius: 0;
}
.list-group-horizontal-xl > .list-group-item:last-child {
border-top-right-radius: 0.25rem;
border-bottom-left-radius: 0;
}
.list-group-horizontal-xl > .list-group-item.active {
margin-top: 0;
}
.list-group-horizontal-xl > .list-group-item + .list-group-item {
border-top-width: 1px;
border-left-width: 0;
}
.list-group-horizontal-xl > .list-group-item + .list-group-item.active {
margin-left: -1px;
border-left-width: 1px;
}
}
.list-group-flush {
border-radius: 0;
}
.list-group-flush > .list-group-item {
border-width: 0 0 1px;
}
.list-group-flush > .list-group-item:last-child {
border-bottom-width: 0;
}
.list-group-item-primary {
color: #004085;
background-color: #b8daff;
}
.list-group-item-primary.list-group-item-action:hover, .list-group-item-primary.list-group-item-action:focus {
color: #004085;
background-color: #9fcdff;
}
.list-group-item-primary.list-group-item-action.active {
color: #fff;
background-color: #004085;
border-color: #004085;
}
.list-group-item-secondary {
color: #383d41;
background-color: #d6d8db;
}
.list-group-item-secondary.list-group-item-action:hover, .list-group-item-secondary.list-group-item-action:focus {
color: #383d41;
background-color: #c8cbcf;
}
.list-group-item-secondary.list-group-item-action.active {
color: #fff;
background-color: #383d41;
border-color: #383d41;
}
.list-group-item-success {
color: #155724;
background-color: #c3e6cb;
}
.list-group-item-success.list-group-item-action:hover, .list-group-item-success.list-group-item-action:focus {
color: #155724;
background-color: #b1dfbb;
}
.list-group-item-success.list-group-item-action.active {
color: #fff;
background-color: #155724;
border-color: #155724;
}
.list-group-item-info {
color: #0c5460;
background-color: #bee5eb;
}
.list-group-item-info.list-group-item-action:hover, .list-group-item-info.list-group-item-action:focus {
color: #0c5460;
background-color: #abdde5;
}
.list-group-item-info.list-group-item-action.active {
color: #fff;
background-color: #0c5460;
border-color: #0c5460;
}
.list-group-item-warning {
color: #856404;
background-color: #ffeeba;
}
.list-group-item-warning.list-group-item-action:hover, .list-group-item-warning.list-group-item-action:focus {
color: #856404;
background-color: #ffe8a1;
}
.list-group-item-warning.list-group-item-action.active {
color: #fff;
background-color: #856404;
border-color: #856404;
}
.list-group-item-danger {
color: #721c24;
background-color: #f5c6cb;
}
.list-group-item-danger.list-group-item-action:hover, .list-group-item-danger.list-group-item-action:focus {
color: #721c24;
background-color: #f1b0b7;
}
.list-group-item-danger.list-group-item-action.active {
color: #fff;
background-color: #721c24;
border-color: #721c24;
}
.list-group-item-light {
color: #818182;
background-color: #fdfdfe;
}
.list-group-item-light.list-group-item-action:hover, .list-group-item-light.list-group-item-action:focus {
color: #818182;
background-color: #ececf6;
}
.list-group-item-light.list-group-item-action.active {
color: #fff;
background-color: #818182;
border-color: #818182;
}
.list-group-item-dark {
color: #1b1e21;
background-color: #c6c8ca;
}
.list-group-item-dark.list-group-item-action:hover, .list-group-item-dark.list-group-item-action:focus {
color: #1b1e21;
background-color: #b9bbbe;
}
.list-group-item-dark.list-group-item-action.active {
color: #fff;
background-color: #1b1e21;
border-color: #1b1e21;
}
.close {
float: right;
font-size: 1.5rem;
font-weight: 700;
line-height: 1;
color: #000;
text-shadow: 0 1px 0 #fff;
opacity: .5;
}
.close:hover {
color: #000;
text-decoration: none;
}
.close:not(:disabled):not(.disabled):hover, .close:not(:disabled):not(.disabled):focus {
opacity: .75;
}
button.close {
padding: 0;
background-color: transparent;
border: 0;
}
a.close.disabled {
pointer-events: none;
}
.toast {
max-width: 350px;
overflow: hidden;
font-size: 0.875rem;
background-color: rgba(255, 255, 255, 0.85);
background-clip: padding-box;
border: 1px solid rgba(0, 0, 0, 0.1);
box-shadow: 0 0.25rem 0.75rem rgba(0, 0, 0, 0.1);
backdrop-filter: blur(10px);
opacity: 0;
border-radius: 0.25rem;
}
.toast:not(:last-child) {
margin-bottom: 0.75rem;
}
.toast.showing {
opacity: 1;
}
.toast.show {
display: block;
opacity: 1;
}
.toast.hide {
display: none;
}
.toast-header {
display: flex;
align-items: center;
padding: 0.25rem 0.75rem;
color: #6c757d;
background-color: rgba(255, 255, 255, 0.85);
background-clip: padding-box;
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
}
.toast-body {
padding: 0.75rem;
}
.modal-open {
overflow: hidden;
}
.modal-open .modal {
overflow-x: hidden;
overflow-y: auto;
}
.modal {
position: fixed;
top: 0;
left: 0;
z-index: 1050;
display: none;
width: 100%;
height: 100%;
overflow: hidden;
outline: 0;
}
.modal-dialog {
position: relative;
width: auto;
margin: 0.5rem;
pointer-events: none;
}
.modal.fade .modal-dialog {
transition: transform 0.3s ease-out;
transform: translate(0, -50px);
}
@media (prefers-reduced-motion: reduce) {
.modal.fade .modal-dialog {
transition: none;
}
}
.modal.show .modal-dialog {
transform: none;
}
.modal.modal-static .modal-dialog {
transform: scale(1.02);
}
.modal-dialog-scrollable {
display: flex;
max-height: calc(100% - 1rem);
}
.modal-dialog-scrollable .modal-content {
max-height: calc(100vh - 1rem);
overflow: hidden;
}
.modal-dialog-scrollable .modal-header,
.modal-dialog-scrollable .modal-footer {
flex-shrink: 0;
}
.modal-dialog-scrollable .modal-body {
overflow-y: auto;
}
.modal-dialog-centered {
display: flex;
align-items: center;
min-height: calc(100% - 1rem);
}
.modal-dialog-centered::before {
display: block;
height: calc(100vh - 1rem);
height: min-content;
content: "";
}
.modal-dialog-centered.modal-dialog-scrollable {
flex-direction: column;
justify-content: center;
height: 100%;
}
.modal-dialog-centered.modal-dialog-scrollable .modal-content {
max-height: none;
}
.modal-dialog-centered.modal-dialog-scrollable::before {
content: none;
}
.modal-content {
position: relative;
display: flex;
flex-direction: column;
width: 100%;
pointer-events: auto;
background-color: #fff;
background-clip: padding-box;
border: 1px solid rgba(0, 0, 0, 0.2);
border-radius: 0.3rem;
outline: 0;
}
.modal-backdrop {
position: fixed;
top: 0;
left: 0;
z-index: 1040;
width: 100vw;
height: 100vh;
background-color: #000;
}
.modal-backdrop.fade {
opacity: 0;
}
.modal-backdrop.show {
opacity: 0.5;
}
.modal-header {
display: flex;
align-items: flex-start;
justify-content: space-between;
padding: 1rem 1rem;
border-bottom: 1px solid #dee2e6;
border-top-left-radius: calc(0.3rem - 1px);
border-top-right-radius: calc(0.3rem - 1px);
}
.modal-header .close {
padding: 1rem 1rem;
margin: -1rem -1rem -1rem auto;
}
.modal-title {
margin-bottom: 0;
line-height: 1.5;
}
.modal-body {
position: relative;
flex: 1 1 auto;
padding: 1rem;
}
.modal-footer {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: flex-end;
padding: 0.75rem;
border-top: 1px solid #dee2e6;
border-bottom-right-radius: calc(0.3rem - 1px);
border-bottom-left-radius: calc(0.3rem - 1px);
}
.modal-footer > * {
margin: 0.25rem;
}
.modal-scrollbar-measure {
position: absolute;
top: -9999px;
width: 50px;
height: 50px;
overflow: scroll;
}
@media (min-width: 576px) {
.modal-dialog {
max-width: 500px;
margin: 1.75rem auto;
}
.modal-dialog-scrollable {
max-height: calc(100% - 3.5rem);
}
.modal-dialog-scrollable .modal-content {
max-height: calc(100vh - 3.5rem);
}
.modal-dialog-centered {
min-height: calc(100% - 3.5rem);
}
.modal-dialog-centered::before {
height: calc(100vh - 3.5rem);
height: min-content;
}
.modal-sm {
max-width: 300px;
}
}
@media (min-width: 992px) {
.modal-lg,
.modal-xl {
max-width: 800px;
}
}
@media (min-width: 1200px) {
.modal-xl {
max-width: 1140px;
}
}
@keyframes spinner-border {
to {
transform: rotate(360deg);
}
}
.spinner-border {
display: inline-block;
width: 2rem;
height: 2rem;
vertical-align: text-bottom;
border: 0.25em solid currentColor;
border-right-color: transparent;
border-radius: 50%;
animation: spinner-border .75s linear infinite;
}
.spinner-border-sm {
width: 1rem;
height: 1rem;
border-width: 0.2em;
}
@keyframes spinner-grow {
0% {
transform: scale(0);
}
50% {
opacity: 1;
transform: none;
}
}
.spinner-grow {
display: inline-block;
width: 2rem;
height: 2rem;
vertical-align: text-bottom;
background-color: currentColor;
border-radius: 50%;
opacity: 0;
animation: spinner-grow .75s linear infinite;
}
.spinner-grow-sm {
width: 1rem;
height: 1rem;
}
.align-baseline {
vertical-align: baseline !important;
}
.align-top {
vertical-align: top !important;
}
.align-middle {
vertical-align: middle !important;
}
.align-bottom {
vertical-align: bottom !important;
}
.align-text-bottom {
vertical-align: text-bottom !important;
}
.align-text-top {
vertical-align: text-top !important;
}
.bg-primary {
background-color: #007bff !important;
}
a.bg-primary:hover, a.bg-primary:focus,
button.bg-primary:hover,
button.bg-primary:focus {
background-color: #0062cc !important;
}
.bg-secondary {
background-color: #6c757d !important;
}
a.bg-secondary:hover, a.bg-secondary:focus,
button.bg-secondary:hover,
button.bg-secondary:focus {
background-color: #545b62 !important;
}
.bg-success {
background-color: #28a745 !important;
}
a.bg-success:hover, a.bg-success:focus,
button.bg-success:hover,
button.bg-success:focus {
background-color: #1e7e34 !important;
}
.bg-info {
background-color: #17a2b8 !important;
}
a.bg-info:hover, a.bg-info:focus,
button.bg-info:hover,
button.bg-info:focus {
background-color: #117a8b !important;
}
.bg-warning {
background-color: #ffc107 !important;
}
a.bg-warning:hover, a.bg-warning:focus,
button.bg-warning:hover,
button.bg-warning:focus {
background-color: #d39e00 !important;
}
.bg-danger {
background-color: #dc3545 !important;
}
a.bg-danger:hover, a.bg-danger:focus,
button.bg-danger:hover,
button.bg-danger:focus {
background-color: #bd2130 !important;
}
.bg-light {
background-color: #f8f9fa !important;
}
a.bg-light:hover, a.bg-light:focus,
button.bg-light:hover,
button.bg-light:focus {
background-color: #dae0e5 !important;
}
.bg-dark {
background-color: #343a40 !important;
}
a.bg-dark:hover, a.bg-dark:focus,
button.bg-dark:hover,
button.bg-dark:focus {
background-color: #1d2124 !important;
}
.bg-white {
background-color: #fff !important;
}
.bg-transparent {
background-color: transparent !important;
}
.border {
border: 1px solid #dee2e6 !important;
}
.border-top {
border-top: 1px solid #dee2e6 !important;
}
.border-right {
border-right: 1px solid #dee2e6 !important;
}
.border-bottom {
border-bottom: 1px solid #dee2e6 !important;
}
.border-left {
border-left: 1px solid #dee2e6 !important;
}
.border-0 {
border: 0 !important;
}
.border-top-0 {
border-top: 0 !important;
}
.border-right-0 {
border-right: 0 !important;
}
.border-bottom-0 {
border-bottom: 0 !important;
}
.border-left-0 {
border-left: 0 !important;
}
.border-primary {
border-color: #007bff !important;
}
.border-secondary {
border-color: #6c757d !important;
}
.border-success {
border-color: #28a745 !important;
}
.border-info {
border-color: #17a2b8 !important;
}
.border-warning {
border-color: #ffc107 !important;
}
.border-danger {
border-color: #dc3545 !important;
}
.border-light {
border-color: #f8f9fa !important;
}
.border-dark {
border-color: #343a40 !important;
}
.border-white {
border-color: #fff !important;
}
.rounded-sm {
border-radius: 0.2rem !important;
}
.rounded {
border-radius: 0.25rem !important;
}
.rounded-top {
border-top-left-radius: 0.25rem !important;
border-top-right-radius: 0.25rem !important;
}
.rounded-right {
border-top-right-radius: 0.25rem !important;
border-bottom-right-radius: 0.25rem !important;
}
.rounded-bottom {
border-bottom-right-radius: 0.25rem !important;
border-bottom-left-radius: 0.25rem !important;
}
.rounded-left {
border-top-left-radius: 0.25rem !important;
border-bottom-left-radius: 0.25rem !important;
}
.rounded-lg {
border-radius: 0.3rem !important;
}
.rounded-circle {
border-radius: 50% !important;
}
.rounded-pill {
border-radius: 50rem !important;
}
.rounded-0 {
border-radius: 0 !important;
}
.clearfix::after {
display: block;
clear: both;
content: "";
}
.d-none {
display: none !important;
}
.d-inline {
display: inline !important;
}
.d-inline-block {
display: inline-block !important;
}
.d-block {
display: block !important;
}
.d-table {
display: table !important;
}
.d-table-row {
display: table-row !important;
}
.d-table-cell {
display: table-cell !important;
}
.d-flex {
display: flex !important;
}
.d-inline-flex {
display: inline-flex !important;
}
@media (min-width: 576px) {
.d-sm-none {
display: none !important;
}
.d-sm-inline {
display: inline !important;
}
.d-sm-inline-block {
display: inline-block !important;
}
.d-sm-block {
display: block !important;
}
.d-sm-table {
display: table !important;
}
.d-sm-table-row {
display: table-row !important;
}
.d-sm-table-cell {
display: table-cell !important;
}
.d-sm-flex {
display: flex !important;
}
.d-sm-inline-flex {
display: inline-flex !important;
}
}
@media (min-width: 768px) {
.d-md-none {
display: none !important;
}
.d-md-inline {
display: inline !important;
}
.d-md-inline-block {
display: inline-block !important;
}
.d-md-block {
display: block !important;
}
.d-md-table {
display: table !important;
}
.d-md-table-row {
display: table-row !important;
}
.d-md-table-cell {
display: table-cell !important;
}
.d-md-flex {
display: flex !important;
}
.d-md-inline-flex {
display: inline-flex !important;
}
}
@media (min-width: 992px) {
.d-lg-none {
display: none !important;
}
.d-lg-inline {
display: inline !important;
}
.d-lg-inline-block {
display: inline-block !important;
}
.d-lg-block {
display: block !important;
}
.d-lg-table {
display: table !important;
}
.d-lg-table-row {
display: table-row !important;
}
.d-lg-table-cell {
display: table-cell !important;
}
.d-lg-flex {
display: flex !important;
}
.d-lg-inline-flex {
display: inline-flex !important;
}
}
@media (min-width: 1200px) {
.d-xl-none {
display: none !important;
}
.d-xl-inline {
display: inline !important;
}
.d-xl-inline-block {
display: inline-block !important;
}
.d-xl-block {
display: block !important;
}
.d-xl-table {
display: table !important;
}
.d-xl-table-row {
display: table-row !important;
}
.d-xl-table-cell {
display: table-cell !important;
}
.d-xl-flex {
display: flex !important;
}
.d-xl-inline-flex {
display: inline-flex !important;
}
}
@media print {
.d-print-none {
display: none !important;
}
.d-print-inline {
display: inline !important;
}
.d-print-inline-block {
display: inline-block !important;
}
.d-print-block {
display: block !important;
}
.d-print-table {
display: table !important;
}
.d-print-table-row {
display: table-row !important;
}
.d-print-table-cell {
display: table-cell !important;
}
.d-print-flex {
display: flex !important;
}
.d-print-inline-flex {
display: inline-flex !important;
}
}
.embed-responsive {
position: relative;
display: block;
width: 100%;
padding: 0;
overflow: hidden;
}
.embed-responsive::before {
display: block;
content: "";
}
.embed-responsive .embed-responsive-item,
.embed-responsive iframe,
.embed-responsive embed,
.embed-responsive object,
.embed-responsive video {
position: absolute;
top: 0;
bottom: 0;
left: 0;
width: 100%;
height: 100%;
border: 0;
}
.embed-responsive-21by9::before {
padding-top: 42.85714%;
}
.embed-responsive-16by9::before {
padding-top: 56.25%;
}
.embed-responsive-4by3::before {
padding-top: 75%;
}
.embed-responsive-1by1::before {
padding-top: 100%;
}
.flex-row {
flex-direction: row !important;
}
.flex-column {
flex-direction: column !important;
}
.flex-row-reverse {
flex-direction: row-reverse !important;
}
.flex-column-reverse {
flex-direction: column-reverse !important;
}
.flex-wrap {
flex-wrap: wrap !important;
}
.flex-nowrap {
flex-wrap: nowrap !important;
}
.flex-wrap-reverse {
flex-wrap: wrap-reverse !important;
}
.flex-fill {
flex: 1 1 auto !important;
}
.flex-grow-0 {
flex-grow: 0 !important;
}
.flex-grow-1 {
flex-grow: 1 !important;
}
.flex-shrink-0 {
flex-shrink: 0 !important;
}
.flex-shrink-1 {
flex-shrink: 1 !important;
}
.justify-content-start {
justify-content: flex-start !important;
}
.justify-content-end {
justify-content: flex-end !important;
}
.justify-content-center {
justify-content: center !important;
}
.justify-content-between {
justify-content: space-between !important;
}
.justify-content-around {
justify-content: space-around !important;
}
.align-items-start {
align-items: flex-start !important;
}
.align-items-end {
align-items: flex-end !important;
}
.align-items-center {
align-items: center !important;
}
.align-items-baseline {
align-items: baseline !important;
}
.align-items-stretch {
align-items: stretch !important;
}
.align-content-start {
align-content: flex-start !important;
}
.align-content-end {
align-content: flex-end !important;
}
.align-content-center {
align-content: center !important;
}
.align-content-between {
align-content: space-between !important;
}
.align-content-around {
align-content: space-around !important;
}
.align-content-stretch {
align-content: stretch !important;
}
.align-self-auto {
align-self: auto !important;
}
.align-self-start {
align-self: flex-start !important;
}
.align-self-end {
align-self: flex-end !important;
}
.align-self-center {
align-self: center !important;
}
.align-self-baseline {
align-self: baseline !important;
}
.align-self-stretch {
align-self: stretch !important;
}
@media (min-width: 576px) {
.flex-sm-row {
flex-direction: row !important;
}
.flex-sm-column {
flex-direction: column !important;
}
.flex-sm-row-reverse {
flex-direction: row-reverse !important;
}
.flex-sm-column-reverse {
flex-direction: column-reverse !important;
}
.flex-sm-wrap {
flex-wrap: wrap !important;
}
.flex-sm-nowrap {
flex-wrap: nowrap !important;
}
.flex-sm-wrap-reverse {
flex-wrap: wrap-reverse !important;
}
.flex-sm-fill {
flex: 1 1 auto !important;
}
.flex-sm-grow-0 {
flex-grow: 0 !important;
}
.flex-sm-grow-1 {
flex-grow: 1 !important;
}
.flex-sm-shrink-0 {
flex-shrink: 0 !important;
}
.flex-sm-shrink-1 {
flex-shrink: 1 !important;
}
.justify-content-sm-start {
justify-content: flex-start !important;
}
.justify-content-sm-end {
justify-content: flex-end !important;
}
.justify-content-sm-center {
justify-content: center !important;
}
.justify-content-sm-between {
justify-content: space-between !important;
}
.justify-content-sm-around {
justify-content: space-around !important;
}
.align-items-sm-start {
align-items: flex-start !important;
}
.align-items-sm-end {
align-items: flex-end !important;
}
.align-items-sm-center {
align-items: center !important;
}
.align-items-sm-baseline {
align-items: baseline !important;
}
.align-items-sm-stretch {
align-items: stretch !important;
}
.align-content-sm-start {
align-content: flex-start !important;
}
.align-content-sm-end {
align-content: flex-end !important;
}
.align-content-sm-center {
align-content: center !important;
}
.align-content-sm-between {
align-content: space-between !important;
}
.align-content-sm-around {
align-content: space-around !important;
}
.align-content-sm-stretch {
align-content: stretch !important;
}
.align-self-sm-auto {
align-self: auto !important;
}
.align-self-sm-start {
align-self: flex-start !important;
}
.align-self-sm-end {
align-self: flex-end !important;
}
.align-self-sm-center {
align-self: center !important;
}
.align-self-sm-baseline {
align-self: baseline !important;
}
.align-self-sm-stretch {
align-self: stretch !important;
}
}
@media (min-width: 768px) {
.flex-md-row {
flex-direction: row !important;
}
.flex-md-column {
flex-direction: column !important;
}
.flex-md-row-reverse {
flex-direction: row-reverse !important;
}
.flex-md-column-reverse {
flex-direction: column-reverse !important;
}
.flex-md-wrap {
flex-wrap: wrap !important;
}
.flex-md-nowrap {
flex-wrap: nowrap !important;
}
.flex-md-wrap-reverse {
flex-wrap: wrap-reverse !important;
}
.flex-md-fill {
flex: 1 1 auto !important;
}
.flex-md-grow-0 {
flex-grow: 0 !important;
}
.flex-md-grow-1 {
flex-grow: 1 !important;
}
.flex-md-shrink-0 {
flex-shrink: 0 !important;
}
.flex-md-shrink-1 {
flex-shrink: 1 !important;
}
.justify-content-md-start {
justify-content: flex-start !important;
}
.justify-content-md-end {
justify-content: flex-end !important;
}
.justify-content-md-center {
justify-content: center !important;
}
.justify-content-md-between {
justify-content: space-between !important;
}
.justify-content-md-around {
justify-content: space-around !important;
}
.align-items-md-start {
align-items: flex-start !important;
}
.align-items-md-end {
align-items: flex-end !important;
}
.align-items-md-center {
align-items: center !important;
}
.align-items-md-baseline {
align-items: baseline !important;
}
.align-items-md-stretch {
align-items: stretch !important;
}
.align-content-md-start {
align-content: flex-start !important;
}
.align-content-md-end {
align-content: flex-end !important;
}
.align-content-md-center {
align-content: center !important;
}
.align-content-md-between {
align-content: space-between !important;
}
.align-content-md-around {
align-content: space-around !important;
}
.align-content-md-stretch {
align-content: stretch !important;
}
.align-self-md-auto {
align-self: auto !important;
}
.align-self-md-start {
align-self: flex-start !important;
}
.align-self-md-end {
align-self: flex-end !important;
}
.align-self-md-center {
align-self: center !important;
}
.align-self-md-baseline {
align-self: baseline !important;
}
.align-self-md-stretch {
align-self: stretch !important;
}
}
@media (min-width: 992px) {
.flex-lg-row {
flex-direction: row !important;
}
.flex-lg-column {
flex-direction: column !important;
}
.flex-lg-row-reverse {
flex-direction: row-reverse !important;
}
.flex-lg-column-reverse {
flex-direction: column-reverse !important;
}
.flex-lg-wrap {
flex-wrap: wrap !important;
}
.flex-lg-nowrap {
flex-wrap: nowrap !important;
}
.flex-lg-wrap-reverse {
flex-wrap: wrap-reverse !important;
}
.flex-lg-fill {
flex: 1 1 auto !important;
}
.flex-lg-grow-0 {
flex-grow: 0 !important;
}
.flex-lg-grow-1 {
flex-grow: 1 !important;
}
.flex-lg-shrink-0 {
flex-shrink: 0 !important;
}
.flex-lg-shrink-1 {
flex-shrink: 1 !important;
}
.justify-content-lg-start {
justify-content: flex-start !important;
}
.justify-content-lg-end {
justify-content: flex-end !important;
}
.justify-content-lg-center {
justify-content: center !important;
}
.justify-content-lg-between {
justify-content: space-between !important;
}
.justify-content-lg-around {
justify-content: space-around !important;
}
.align-items-lg-start {
align-items: flex-start !important;
}
.align-items-lg-end {
align-items: flex-end !important;
}
.align-items-lg-center {
align-items: center !important;
}
.align-items-lg-baseline {
align-items: baseline !important;
}
.align-items-lg-stretch {
align-items: stretch !important;
}
.align-content-lg-start {
align-content: flex-start !important;
}
.align-content-lg-end {
align-content: flex-end !important;
}
.align-content-lg-center {
align-content: center !important;
}
.align-content-lg-between {
align-content: space-between !important;
}
.align-content-lg-around {
align-content: space-around !important;
}
.align-content-lg-stretch {
align-content: stretch !important;
}
.align-self-lg-auto {
align-self: auto !important;
}
.align-self-lg-start {
align-self: flex-start !important;
}
.align-self-lg-end {
align-self: flex-end !important;
}
.align-self-lg-center {
align-self: center !important;
}
.align-self-lg-baseline {
align-self: baseline !important;
}
.align-self-lg-stretch {
align-self: stretch !important;
}
}
@media (min-width: 1200px) {
.flex-xl-row {
flex-direction: row !important;
}
.flex-xl-column {
flex-direction: column !important;
}
.flex-xl-row-reverse {
flex-direction: row-reverse !important;
}
.flex-xl-column-reverse {
flex-direction: column-reverse !important;
}
.flex-xl-wrap {
flex-wrap: wrap !important;
}
.flex-xl-nowrap {
flex-wrap: nowrap !important;
}
.flex-xl-wrap-reverse {
flex-wrap: wrap-reverse !important;
}
.flex-xl-fill {
flex: 1 1 auto !important;
}
.flex-xl-grow-0 {
flex-grow: 0 !important;
}
.flex-xl-grow-1 {
flex-grow: 1 !important;
}
.flex-xl-shrink-0 {
flex-shrink: 0 !important;
}
.flex-xl-shrink-1 {
flex-shrink: 1 !important;
}
.justify-content-xl-start {
justify-content: flex-start !important;
}
.justify-content-xl-end {
justify-content: flex-end !important;
}
.justify-content-xl-center {
justify-content: center !important;
}
.justify-content-xl-between {
justify-content: space-between !important;
}
.justify-content-xl-around {
justify-content: space-around !important;
}
.align-items-xl-start {
align-items: flex-start !important;
}
.align-items-xl-end {
align-items: flex-end !important;
}
.align-items-xl-center {
align-items: center !important;
}
.align-items-xl-baseline {
align-items: baseline !important;
}
.align-items-xl-stretch {
align-items: stretch !important;
}
.align-content-xl-start {
align-content: flex-start !important;
}
.align-content-xl-end {
align-content: flex-end !important;
}
.align-content-xl-center {
align-content: center !important;
}
.align-content-xl-between {
align-content: space-between !important;
}
.align-content-xl-around {
align-content: space-around !important;
}
.align-content-xl-stretch {
align-content: stretch !important;
}
.align-self-xl-auto {
align-self: auto !important;
}
.align-self-xl-start {
align-self: flex-start !important;
}
.align-self-xl-end {
align-self: flex-end !important;
}
.align-self-xl-center {
align-self: center !important;
}
.align-self-xl-baseline {
align-self: baseline !important;
}
.align-self-xl-stretch {
align-self: stretch !important;
}
}
.float-left {
float: left !important;
}
.float-right {
float: right !important;
}
.float-none {
float: none !important;
}
@media (min-width: 576px) {
.float-sm-left {
float: left !important;
}
.float-sm-right {
float: right !important;
}
.float-sm-none {
float: none !important;
}
}
@media (min-width: 768px) {
.float-md-left {
float: left !important;
}
.float-md-right {
float: right !important;
}
.float-md-none {
float: none !important;
}
}
@media (min-width: 992px) {
.float-lg-left {
float: left !important;
}
.float-lg-right {
float: right !important;
}
.float-lg-none {
float: none !important;
}
}
@media (min-width: 1200px) {
.float-xl-left {
float: left !important;
}
.float-xl-right {
float: right !important;
}
.float-xl-none {
float: none !important;
}
}
.user-select-all {
user-select: all !important;
}
.user-select-auto {
user-select: auto !important;
}
.user-select-none {
user-select: none !important;
}
.overflow-auto {
overflow: auto !important;
}
.overflow-hidden {
overflow: hidden !important;
}
.position-static {
position: static !important;
}
.position-relative {
position: relative !important;
}
.position-absolute {
position: absolute !important;
}
.position-fixed {
position: fixed !important;
}
.position-sticky {
position: sticky !important;
}
.fixed-top {
position: fixed;
top: 0;
right: 0;
left: 0;
z-index: 1030;
}
.fixed-bottom {
position: fixed;
right: 0;
bottom: 0;
left: 0;
z-index: 1030;
}
@supports (position: sticky) {
.sticky-top {
position: sticky;
top: 0;
z-index: 1020;
}
}
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
.sr-only-focusable:active, .sr-only-focusable:focus {
position: static;
width: auto;
height: auto;
overflow: visible;
clip: auto;
white-space: normal;
}
.shadow-sm {
box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075) !important;
}
.shadow {
box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15) !important;
}
.shadow-lg {
box-shadow: 0 1rem 3rem rgba(0, 0, 0, 0.175) !important;
}
.shadow-none {
box-shadow: none !important;
}
.w-25 {
width: 25% !important;
}
.w-50 {
width: 50% !important;
}
.w-75 {
width: 75% !important;
}
.w-100 {
width: 100% !important;
}
.w-auto {
width: auto !important;
}
.h-25 {
height: 25% !important;
}
.h-50 {
height: 50% !important;
}
.h-75 {
height: 75% !important;
}
.h-100 {
height: 100% !important;
}
.h-auto {
height: auto !important;
}
.mw-100 {
max-width: 100% !important;
}
.mh-100 {
max-height: 100% !important;
}
.min-vw-100 {
min-width: 100vw !important;
}
.min-vh-100 {
min-height: 100vh !important;
}
.vw-100 {
width: 100vw !important;
}
.vh-100 {
height: 100vh !important;
}
.m-0 {
margin: 0 !important;
}
.mt-0,
.my-0 {
margin-top: 0 !important;
}
.mr-0,
.mx-0 {
margin-right: 0 !important;
}
.mb-0,
.my-0 {
margin-bottom: 0 !important;
}
.ml-0,
.mx-0 {
margin-left: 0 !important;
}
.m-1 {
margin: 0.3125rem !important;
}
.mt-1,
.my-1 {
margin-top: 0.3125rem !important;
}
.mr-1,
.mx-1 {
margin-right: 0.3125rem !important;
}
.mb-1,
.my-1 {
margin-bottom: 0.3125rem !important;
}
.ml-1,
.mx-1 {
margin-left: 0.3125rem !important;
}
.m-2 {
margin: 0.625rem !important;
}
.mt-2,
.my-2 {
margin-top: 0.625rem !important;
}
.mr-2,
.mx-2 {
margin-right: 0.625rem !important;
}
.mb-2,
.my-2 {
margin-bottom: 0.625rem !important;
}
.ml-2,
.mx-2 {
margin-left: 0.625rem !important;
}
.m-3 {
margin: 0.9375rem !important;
}
.mt-3,
.my-3 {
margin-top: 0.9375rem !important;
}
.mr-3,
.mx-3 {
margin-right: 0.9375rem !important;
}
.mb-3,
.my-3 {
margin-bottom: 0.9375rem !important;
}
.ml-3,
.mx-3 {
margin-left: 0.9375rem !important;
}
.m-4 {
margin: 1.875rem !important;
}
.mt-4,
.my-4 {
margin-top: 1.875rem !important;
}
.mr-4,
.mx-4 {
margin-right: 1.875rem !important;
}
.mb-4,
.my-4 {
margin-bottom: 1.875rem !important;
}
.ml-4,
.mx-4 {
margin-left: 1.875rem !important;
}
.m-5 {
margin: 2.8125rem !important;
}
.mt-5,
.my-5 {
margin-top: 2.8125rem !important;
}
.mr-5,
.mx-5 {
margin-right: 2.8125rem !important;
}
.mb-5,
.my-5 {
margin-bottom: 2.8125rem !important;
}
.ml-5,
.mx-5 {
margin-left: 2.8125rem !important;
}
.p-0 {
padding: 0 !important;
}
.pt-0,
.py-0 {
padding-top: 0 !important;
}
.pr-0,
.px-0 {
padding-right: 0 !important;
}
.pb-0,
.py-0 {
padding-bottom: 0 !important;
}
.pl-0,
.px-0 {
padding-left: 0 !important;
}
.p-1 {
padding: 0.3125rem !important;
}
.pt-1,
.py-1 {
padding-top: 0.3125rem !important;
}
.pr-1,
.px-1 {
padding-right: 0.3125rem !important;
}
.pb-1,
.py-1 {
padding-bottom: 0.3125rem !important;
}
.pl-1,
.px-1 {
padding-left: 0.3125rem !important;
}
.p-2 {
padding: 0.625rem !important;
}
.pt-2,
.py-2 {
padding-top: 0.625rem !important;
}
.pr-2,
.px-2 {
padding-right: 0.625rem !important;
}
.pb-2,
.py-2 {
padding-bottom: 0.625rem !important;
}
.pl-2,
.px-2 {
padding-left: 0.625rem !important;
}
.p-3 {
padding: 0.9375rem !important;
}
.pt-3,
.py-3 {
padding-top: 0.9375rem !important;
}
.pr-3,
.px-3 {
padding-right: 0.9375rem !important;
}
.pb-3,
.py-3 {
padding-bottom: 0.9375rem !important;
}
.pl-3,
.px-3 {
padding-left: 0.9375rem !important;
}
.p-4 {
padding: 1.875rem !important;
}
.pt-4,
.py-4 {
padding-top: 1.875rem !important;
}
.pr-4,
.px-4 {
padding-right: 1.875rem !important;
}
.pb-4,
.py-4 {
padding-bottom: 1.875rem !important;
}
.pl-4,
.px-4 {
padding-left: 1.875rem !important;
}
.p-5 {
padding: 2.8125rem !important;
}
.pt-5,
.py-5 {
padding-top: 2.8125rem !important;
}
.pr-5,
.px-5 {
padding-right: 2.8125rem !important;
}
.pb-5,
.py-5 {
padding-bottom: 2.8125rem !important;
}
.pl-5,
.px-5 {
padding-left: 2.8125rem !important;
}
.m-n1 {
margin: -0.3125rem !important;
}
.mt-n1,
.my-n1 {
margin-top: -0.3125rem !important;
}
.mr-n1,
.mx-n1 {
margin-right: -0.3125rem !important;
}
.mb-n1,
.my-n1 {
margin-bottom: -0.3125rem !important;
}
.ml-n1,
.mx-n1 {
margin-left: -0.3125rem !important;
}
.m-n2 {
margin: -0.625rem !important;
}
.mt-n2,
.my-n2 {
margin-top: -0.625rem !important;
}
.mr-n2,
.mx-n2 {
margin-right: -0.625rem !important;
}
.mb-n2,
.my-n2 {
margin-bottom: -0.625rem !important;
}
.ml-n2,
.mx-n2 {
margin-left: -0.625rem !important;
}
.m-n3 {
margin: -0.9375rem !important;
}
.mt-n3,
.my-n3 {
margin-top: -0.9375rem !important;
}
.mr-n3,
.mx-n3 {
margin-right: -0.9375rem !important;
}
.mb-n3,
.my-n3 {
margin-bottom: -0.9375rem !important;
}
.ml-n3,
.mx-n3 {
margin-left: -0.9375rem !important;
}
.m-n4 {
margin: -1.875rem !important;
}
.mt-n4,
.my-n4 {
margin-top: -1.875rem !important;
}
.mr-n4,
.mx-n4 {
margin-right: -1.875rem !important;
}
.mb-n4,
.my-n4 {
margin-bottom: -1.875rem !important;
}
.ml-n4,
.mx-n4 {
margin-left: -1.875rem !important;
}
.m-n5 {
margin: -2.8125rem !important;
}
.mt-n5,
.my-n5 {
margin-top: -2.8125rem !important;
}
.mr-n5,
.mx-n5 {
margin-right: -2.8125rem !important;
}
.mb-n5,
.my-n5 {
margin-bottom: -2.8125rem !important;
}
.ml-n5,
.mx-n5 {
margin-left: -2.8125rem !important;
}
.m-auto {
margin: auto !important;
}
.mt-auto,
.my-auto {
margin-top: auto !important;
}
.mr-auto,
.mx-auto {
margin-right: auto !important;
}
.mb-auto,
.my-auto {
margin-bottom: auto !important;
}
.ml-auto,
.mx-auto {
margin-left: auto !important;
}
@media (min-width: 576px) {
.m-sm-0 {
margin: 0 !important;
}
.mt-sm-0,
.my-sm-0 {
margin-top: 0 !important;
}
.mr-sm-0,
.mx-sm-0 {
margin-right: 0 !important;
}
.mb-sm-0,
.my-sm-0 {
margin-bottom: 0 !important;
}
.ml-sm-0,
.mx-sm-0 {
margin-left: 0 !important;
}
.m-sm-1 {
margin: 0.3125rem !important;
}
.mt-sm-1,
.my-sm-1 {
margin-top: 0.3125rem !important;
}
.mr-sm-1,
.mx-sm-1 {
margin-right: 0.3125rem !important;
}
.mb-sm-1,
.my-sm-1 {
margin-bottom: 0.3125rem !important;
}
.ml-sm-1,
.mx-sm-1 {
margin-left: 0.3125rem !important;
}
.m-sm-2 {
margin: 0.625rem !important;
}
.mt-sm-2,
.my-sm-2 {
margin-top: 0.625rem !important;
}
.mr-sm-2,
.mx-sm-2 {
margin-right: 0.625rem !important;
}
.mb-sm-2,
.my-sm-2 {
margin-bottom: 0.625rem !important;
}
.ml-sm-2,
.mx-sm-2 {
margin-left: 0.625rem !important;
}
.m-sm-3 {
margin: 0.9375rem !important;
}
.mt-sm-3,
.my-sm-3 {
margin-top: 0.9375rem !important;
}
.mr-sm-3,
.mx-sm-3 {
margin-right: 0.9375rem !important;
}
.mb-sm-3,
.my-sm-3 {
margin-bottom: 0.9375rem !important;
}
.ml-sm-3,
.mx-sm-3 {
margin-left: 0.9375rem !important;
}
.m-sm-4 {
margin: 1.875rem !important;
}
.mt-sm-4,
.my-sm-4 {
margin-top: 1.875rem !important;
}
.mr-sm-4,
.mx-sm-4 {
margin-right: 1.875rem !important;
}
.mb-sm-4,
.my-sm-4 {
margin-bottom: 1.875rem !important;
}
.ml-sm-4,
.mx-sm-4 {
margin-left: 1.875rem !important;
}
.m-sm-5 {
margin: 2.8125rem !important;
}
.mt-sm-5,
.my-sm-5 {
margin-top: 2.8125rem !important;
}
.mr-sm-5,
.mx-sm-5 {
margin-right: 2.8125rem !important;
}
.mb-sm-5,
.my-sm-5 {
margin-bottom: 2.8125rem !important;
}
.ml-sm-5,
.mx-sm-5 {
margin-left: 2.8125rem !important;
}
.p-sm-0 {
padding: 0 !important;
}
.pt-sm-0,
.py-sm-0 {
padding-top: 0 !important;
}
.pr-sm-0,
.px-sm-0 {
padding-right: 0 !important;
}
.pb-sm-0,
.py-sm-0 {
padding-bottom: 0 !important;
}
.pl-sm-0,
.px-sm-0 {
padding-left: 0 !important;
}
.p-sm-1 {
padding: 0.3125rem !important;
}
.pt-sm-1,
.py-sm-1 {
padding-top: 0.3125rem !important;
}
.pr-sm-1,
.px-sm-1 {
padding-right: 0.3125rem !important;
}
.pb-sm-1,
.py-sm-1 {
padding-bottom: 0.3125rem !important;
}
.pl-sm-1,
.px-sm-1 {
padding-left: 0.3125rem !important;
}
.p-sm-2 {
padding: 0.625rem !important;
}
.pt-sm-2,
.py-sm-2 {
padding-top: 0.625rem !important;
}
.pr-sm-2,
.px-sm-2 {
padding-right: 0.625rem !important;
}
.pb-sm-2,
.py-sm-2 {
padding-bottom: 0.625rem !important;
}
.pl-sm-2,
.px-sm-2 {
padding-left: 0.625rem !important;
}
.p-sm-3 {
padding: 0.9375rem !important;
}
.pt-sm-3,
.py-sm-3 {
padding-top: 0.9375rem !important;
}
.pr-sm-3,
.px-sm-3 {
padding-right: 0.9375rem !important;
}
.pb-sm-3,
.py-sm-3 {
padding-bottom: 0.9375rem !important;
}
.pl-sm-3,
.px-sm-3 {
padding-left: 0.9375rem !important;
}
.p-sm-4 {
padding: 1.875rem !important;
}
.pt-sm-4,
.py-sm-4 {
padding-top: 1.875rem !important;
}
.pr-sm-4,
.px-sm-4 {
padding-right: 1.875rem !important;
}
.pb-sm-4,
.py-sm-4 {
padding-bottom: 1.875rem !important;
}
.pl-sm-4,
.px-sm-4 {
padding-left: 1.875rem !important;
}
.p-sm-5 {
padding: 2.8125rem !important;
}
.pt-sm-5,
.py-sm-5 {
padding-top: 2.8125rem !important;
}
.pr-sm-5,
.px-sm-5 {
padding-right: 2.8125rem !important;
}
.pb-sm-5,
.py-sm-5 {
padding-bottom: 2.8125rem !important;
}
.pl-sm-5,
.px-sm-5 {
padding-left: 2.8125rem !important;
}
.m-sm-n1 {
margin: -0.3125rem !important;
}
.mt-sm-n1,
.my-sm-n1 {
margin-top: -0.3125rem !important;
}
.mr-sm-n1,
.mx-sm-n1 {
margin-right: -0.3125rem !important;
}
.mb-sm-n1,
.my-sm-n1 {
margin-bottom: -0.3125rem !important;
}
.ml-sm-n1,
.mx-sm-n1 {
margin-left: -0.3125rem !important;
}
.m-sm-n2 {
margin: -0.625rem !important;
}
.mt-sm-n2,
.my-sm-n2 {
margin-top: -0.625rem !important;
}
.mr-sm-n2,
.mx-sm-n2 {
margin-right: -0.625rem !important;
}
.mb-sm-n2,
.my-sm-n2 {
margin-bottom: -0.625rem !important;
}
.ml-sm-n2,
.mx-sm-n2 {
margin-left: -0.625rem !important;
}
.m-sm-n3 {
margin: -0.9375rem !important;
}
.mt-sm-n3,
.my-sm-n3 {
margin-top: -0.9375rem !important;
}
.mr-sm-n3,
.mx-sm-n3 {
margin-right: -0.9375rem !important;
}
.mb-sm-n3,
.my-sm-n3 {
margin-bottom: -0.9375rem !important;
}
.ml-sm-n3,
.mx-sm-n3 {
margin-left: -0.9375rem !important;
}
.m-sm-n4 {
margin: -1.875rem !important;
}
.mt-sm-n4,
.my-sm-n4 {
margin-top: -1.875rem !important;
}
.mr-sm-n4,
.mx-sm-n4 {
margin-right: -1.875rem !important;
}
.mb-sm-n4,
.my-sm-n4 {
margin-bottom: -1.875rem !important;
}
.ml-sm-n4,
.mx-sm-n4 {
margin-left: -1.875rem !important;
}
.m-sm-n5 {
margin: -2.8125rem !important;
}
.mt-sm-n5,
.my-sm-n5 {
margin-top: -2.8125rem !important;
}
.mr-sm-n5,
.mx-sm-n5 {
margin-right: -2.8125rem !important;
}
.mb-sm-n5,
.my-sm-n5 {
margin-bottom: -2.8125rem !important;
}
.ml-sm-n5,
.mx-sm-n5 {
margin-left: -2.8125rem !important;
}
.m-sm-auto {
margin: auto !important;
}
.mt-s
gitextract_hr3ssai2/
├── .gitignore
├── Aurora.py
├── README.md
├── VERSION
├── __init__.py
├── apt-requirements.txt
├── aurora.service
├── extensions/
│ ├── Aurora_Ambient_16x9.py
│ ├── Aurora_Ambient_AutoCrop.py
│ ├── Aurora_Ambient_NoCrop.py
│ ├── Aurora_AudioSpectogram.py
│ ├── Aurora_Configure.py
│ ├── Aurora_Meteor.py
│ ├── Aurora_Rainbow.py
│ ├── __init__.py
│ └── exampleExtension.py
├── install.sh
├── requirements.txt
├── update.sh
└── webserver/
├── static/
│ ├── css/
│ │ ├── bootstrap.css
│ │ └── style.css
│ ├── images/
│ │ ├── icons/
│ │ │ └── license.txt
│ │ └── undraw/
│ │ └── _license_and_link.rtf
│ ├── js/
│ │ ├── aurora-configure.js
│ │ ├── aurora-generic.js
│ │ ├── aurora-index.js
│ │ ├── aurora-view.js
│ │ ├── custom.js
│ │ └── jquery.js
│ └── menu/
│ └── menu-main.html
└── templates/
├── about.html
├── configure.html
├── footer.html
├── header.html
├── index.html
├── menu-colors.html
├── menu-footer.html
├── menu-share.html
├── status.json
└── view.html
SYMBOL INDEX (179 symbols across 15 files)
FILE: Aurora.py
class AuroraManager (line 27) | class AuroraManager:
method __init__ (line 28) | def __init__(self):
method setupNeoPixels (line 79) | def setupNeoPixels(self):
method setupHDMI (line 92) | def setupHDMI(self):
method saveConfig (line 164) | def saveConfig(self):
method log (line 169) | def log(self, message):
method loadConfig (line 173) | def loadConfig(self):
method getExtensionClass (line 199) | def getExtensionClass(self, extension_name, extension_dir):
method fetchMeta (line 227) | def fetchMeta(self, extension, filename):
method populateExtensions (line 238) | def populateExtensions(self):
method addMessage (line 254) | def addMessage(self, msg):
method getCurrentExtension (line 260) | def getCurrentExtension(self):
method setCurrentExtension (line 268) | def setCurrentExtension(self, new_current_extension):
method takeScreenshot (line 296) | def takeScreenshot(self):
method makePixelImage (line 299) | def makePixelImage(self):
method setupExtension (line 302) | def setupExtension(self):
method tearDownExtension (line 306) | def tearDownExtension(self):
method loop (line 310) | def loop(self):
class Aurora_Webserver (line 322) | class Aurora_Webserver(object):
method __init__ (line 323) | def __init__(self, Manager):
method status (line 328) | def status(self):
method about (line 344) | def about(self):
method index (line 380) | def index(self):
method view (line 413) | def view(self):
method configure (line 446) | def configure(self):
method toggleEnable (line 489) | def toggleEnable(self):
method update_config (line 502) | def update_config(self):
method update_HDMI_config (line 531) | def update_HDMI_config(self):
method update_LED_config (line 578) | def update_LED_config(self):
method update_extension (line 691) | def update_extension(self):
method screenshot (line 701) | def screenshot(self):
method load_screenshot (line 714) | def load_screenshot(self, **params):
method load_pixel_image (line 740) | def load_pixel_image(self, **params):
FILE: extensions/Aurora_Ambient_16x9.py
class Aurora_Ambient_16x9 (line 9) | class Aurora_Ambient_16x9(AuroraExtension):
method __init__ (line 10) | def __init__(self, NeoPixels, HDMI):
method aspectCrop (line 21) | def aspectCrop(self, image, ratio):
method takeScreenShot (line 30) | def takeScreenShot(self, filepath):
method visualise (line 39) | def visualise(self):
FILE: extensions/Aurora_Ambient_AutoCrop.py
class Aurora_Ambient_AutoCrop (line 9) | class Aurora_Ambient_AutoCrop(AuroraExtension):
method __init__ (line 10) | def __init__(self, NeoPixels, HDMI):
method takeScreenShot (line 23) | def takeScreenShot(self, filepath, autocrop=False):
method autocrop (line 26) | def autocrop(self, image, threshold=0):
method visualise (line 46) | def visualise(self):
FILE: extensions/Aurora_Ambient_NoCrop.py
class Aurora_Ambient_NoCrop (line 9) | class Aurora_Ambient_NoCrop(AuroraExtension):
method __init__ (line 10) | def __init__(self, NeoPixels, HDMI):
method autocrop (line 19) | def autocrop(self, image, threshold=0):
method takeScreenShot (line 39) | def takeScreenShot(self, filepath):
method visualise (line 74) | def visualise(self):
FILE: extensions/Aurora_AudioSpectogram.py
class Aurora_AudioSpectogram (line 13) | class Aurora_AudioSpectogram(AuroraExtension):
method __init__ (line 14) | def __init__(self, NeoPixels, HDMI):
method takeScreenShot (line 40) | def takeScreenShot(self, filepath):
method startAudioStream (line 44) | def startAudioStream(self):
method teardown (line 57) | def teardown(self):
method fadeToBlack (line 63) | def fadeToBlack(self, pixelPos):
method wheel (line 73) | def wheel(self, pos):
method rainbow_cycle (line 86) | def rainbow_cycle(self, j):
method visualiseAudio (line 93) | def visualiseAudio(self, indata, frames, time, status):
method visualise (line 146) | def visualise(self):
FILE: extensions/Aurora_Configure.py
class Aurora_Configure (line 12) | class Aurora_Configure(AuroraExtension):
method __init__ (line 13) | def __init__(self, NeoPixels, HDMI):
method setup (line 22) | def setup(self):
method visualise (line 25) | def visualise(self):
FILE: extensions/Aurora_Meteor.py
class Aurora_Meteor (line 10) | class Aurora_Meteor(AuroraExtension):
method __init__ (line 11) | def __init__(self, NeoPixels, HDMI):
method takeScreenShot (line 25) | def takeScreenShot(self, filepath):
method fadeToBlack (line 29) | def fadeToBlack(self, pixelPos):
method meteorRain (line 39) | def meteorRain(self, i, col):
method visualise (line 52) | def visualise(self):
FILE: extensions/Aurora_Rainbow.py
class Aurora_Rainbow (line 9) | class Aurora_Rainbow(AuroraExtension):
method __init__ (line 10) | def __init__(self, NeoPixels, HDMI):
method takeScreenShot (line 19) | def takeScreenShot(self, filepath):
method wheel (line 23) | def wheel(self, pos):
method rainbow_cycle (line 36) | def rainbow_cycle(self, j):
method visualise (line 43) | def visualise(self):
FILE: extensions/exampleExtension.py
class exampleExtension (line 8) | class exampleExtension(AuroraExtension):
method __init__ (line 9) | def __init__(self, NeoPixels, HDMI):
method fadeToBlack (line 23) | def fadeToBlack(self, pixelPos):
method fadeToBright (line 33) | def fadeToBright(self, pixelPos):
method visualise (line 47) | def visualise(self):
FILE: webserver/static/js/aurora-configure.js
function fetch_config_data (line 1) | function fetch_config_data() {
function save_hdmi_image (line 51) | function save_hdmi_image()
function reloadImages (line 62) | function reloadImages() {
function setHDMIValues (line 70) | function setHDMIValues()
function reset_gamma (line 79) | function reset_gamma(hue_gamma)
FILE: webserver/static/js/aurora-generic.js
function togglePower (line 3) | function togglePower()
function make_AJAX_Call (line 10) | function make_AJAX_Call(url, data_dict, callback_function = false) {
function create_snackbar (line 51) | function create_snackbar(heading, message, type) {
function showExtensionDetails (line 73) | function showExtensionDetails(name) {
function loadExtension (line 86) | function loadExtension(extension_name = false) {
function reload_pixel_image (line 100) | function reload_pixel_image() {
function reload_hdmi_image (line 113) | function reload_hdmi_image() {
FILE: webserver/static/js/aurora-index.js
function change_system_status (line 1) | function change_system_status()
function toast_system_status (line 16) | function toast_system_status(system_status_call)
FILE: webserver/static/js/aurora-view.js
function change_system_status (line 1) | function change_system_status() {
function toast_system_status (line 15) | function toast_system_status(system_status_call) {
function reloadImages (line 30) | function reloadImages() {
function reloadCounter (line 37) | function reloadCounter() {
FILE: webserver/static/js/custom.js
function init_template (line 29) | function init_template(){
FILE: webserver/static/js/jquery.js
function m (line 2) | function m(e,t,n){var i,o=(t=t||r).createElement("script");if(o.text=e,n...
function x (line 2) | function x(e){return null==e?e+"":"object"==typeof e||"function"==typeof...
function C (line 2) | function C(e){var t=!!e&&"length"in e&&e.length,n=x(e);return!g(e)&&!y(e...
function oe (line 2) | function oe(e,t,r,i){var o,s,l,c,f,h,v,m=t&&t.ownerDocument,T=t?t.nodeTy...
function ae (line 2) | function ae(){var e=[];function t(n,i){return e.push(n+" ")>r.cacheLengt...
function se (line 2) | function se(e){return e[b]=!0,e}
function ue (line 2) | function ue(e){var t=d.createElement("fieldset");try{return!!e(t)}catch(...
function le (line 2) | function le(e,t){var n=e.split("|"),i=n.length;while(i--)r.attrHandle[n[...
function ce (line 2) | function ce(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourc...
function fe (line 2) | function fe(e){return function(t){return"input"===t.nodeName.toLowerCase...
function pe (line 2) | function pe(e){return function(t){var n=t.nodeName.toLowerCase();return(...
function de (line 2) | function de(e){return function(t){return"form"in t?t.parentNode&&!1===t....
function he (line 2) | function he(e){return se(function(t){return t=+t,se(function(n,r){var i,...
function ge (line 2) | function ge(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}
function ye (line 2) | function ye(){}
function ve (line 2) | function ve(e){for(var t=0,n=e.length,r="";t<n;t++)r+=e[t].value;return r}
function me (line 2) | function me(e,t,n){var r=t.dir,i=t.next,o=i||r,a=n&&"parentNode"===o,s=C...
function xe (line 2) | function xe(e){return e.length>1?function(t,n,r){var i=e.length;while(i-...
function be (line 2) | function be(e,t,n){for(var r=0,i=t.length;r<i;r++)oe(e,t[r],n);return n}
function we (line 2) | function we(e,t,n,r,i){for(var o,a=[],s=0,u=e.length,l=null!=t;s<u;s++)(...
function Te (line 2) | function Te(e,t,n,r,i,o){return r&&!r[b]&&(r=Te(r)),i&&!i[b]&&(i=Te(i,o)...
function Ce (line 2) | function Ce(e){for(var t,n,i,o=e.length,a=r.relative[e[0].type],s=a||r.r...
function Ee (line 2) | function Ee(e,t){var n=t.length>0,i=e.length>0,o=function(o,a,s,u,c){var...
function N (line 2) | function N(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerC...
function j (line 2) | function j(e,t,n){return g(t)?w.grep(e,function(e,r){return!!t.call(e,r,...
function P (line 2) | function P(e,t){while((e=e[t])&&1!==e.nodeType);return e}
function R (line 2) | function R(e){var t={};return w.each(e.match(M)||[],function(e,n){t[n]=!...
function I (line 2) | function I(e){return e}
function W (line 2) | function W(e){throw e}
function $ (line 2) | function $(e,t,n,r){var i;try{e&&g(i=e.promise)?i.call(e).done(t).fail(n...
function a (line 2) | function a(t,n,r,i){return function(){var s=this,u=arguments,l=function(...
function _ (line 2) | function _(){r.removeEventListener("DOMContentLoaded",_),e.removeEventLi...
function V (line 2) | function V(e,t){return t.toUpperCase()}
function G (line 2) | function G(e){return e.replace(X,"ms-").replace(U,V)}
function Q (line 2) | function Q(){this.expando=w.expando+Q.uid++}
function te (line 2) | function te(e){return"true"===e||"false"!==e&&("null"===e?null:e===+e+""...
function ne (line 2) | function ne(e,t,n){var r;if(void 0===n&&1===e.nodeType)if(r="data-"+t.re...
function ue (line 2) | function ue(e,t,n,r){var i,o,a=20,s=r?function(){return r.cur()}:functio...
function ce (line 2) | function ce(e){var t,n=e.ownerDocument,r=e.nodeName,i=le[r];return i||(t...
function fe (line 2) | function fe(e,t){for(var n,r,i=[],o=0,a=e.length;o<a;o++)(r=e[o]).style&...
function ye (line 2) | function ye(e,t){var n;return n="undefined"!=typeof e.getElementsByTagNa...
function ve (line 2) | function ve(e,t){for(var n=0,r=e.length;n<r;n++)J.set(e[n],"globalEval",...
function xe (line 2) | function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),...
function Ee (line 2) | function Ee(){return!0}
function ke (line 2) | function ke(){return!1}
function Se (line 2) | function Se(){try{return r.activeElement}catch(e){}}
function De (line 2) | function De(e,t,n,r,i,o){var a,s;if("object"==typeof t){"string"!=typeof...
function Le (line 2) | function Le(e,t){return N(e,"table")&&N(11!==t.nodeType?t:t.firstChild,"...
function He (line 2) | function He(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}
function Oe (line 2) | function Oe(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.sli...
function Pe (line 2) | function Pe(e,t){var n,r,i,o,a,s,u,l;if(1===t.nodeType){if(J.hasData(e)&...
function Me (line 2) | function Me(e,t){var n=t.nodeName.toLowerCase();"input"===n&&pe.test(e.t...
function Re (line 2) | function Re(e,t,n,r){t=a.apply([],t);var i,o,s,u,l,c,f=0,p=e.length,d=p-...
function Ie (line 2) | function Ie(e,t,n){for(var r,i=t?w.filter(t,e):e,o=0;null!=(r=i[o]);o++)...
function t (line 2) | function t(){if(c){l.style.cssText="position:absolute;left:-11111px;widt...
function n (line 2) | function n(e){return Math.round(parseFloat(e))}
function Fe (line 2) | function Fe(e,t,n){var r,i,o,a,s=e.style;return(n=n||$e(e))&&(""!==(a=n....
function _e (line 2) | function _e(e,t){return{get:function(){if(!e())return(this.get=t).apply(...
function Qe (line 2) | function Qe(e){if(e in Ye)return e;var t=e[0].toUpperCase()+e.slice(1),n...
function Je (line 2) | function Je(e){var t=w.cssProps[e];return t||(t=w.cssProps[e]=Qe(e)||e),t}
function Ke (line 2) | function Ke(e,t,n){var r=ie.exec(t);return r?Math.max(0,r[2]-(n||0))+(r[...
function Ze (line 2) | function Ze(e,t,n,r,i,o){var a="width"===t?1:0,s=0,u=0;if(n===(r?"border...
function et (line 2) | function et(e,t,n){var r=$e(e),i=Fe(e,t,r),o="border-box"===w.css(e,"box...
function tt (line 2) | function tt(e,t,n,r,i){return new tt.prototype.init(e,t,n,r,i)}
function at (line 2) | function at(){rt&&(!1===r.hidden&&e.requestAnimationFrame?e.requestAnima...
function st (line 2) | function st(){return e.setTimeout(function(){nt=void 0}),nt=Date.now()}
function ut (line 2) | function ut(e,t){var n,r=0,i={height:e};for(t=t?1:0;r<4;r+=2-t)i["margin...
function lt (line 2) | function lt(e,t,n){for(var r,i=(pt.tweeners[t]||[]).concat(pt.tweeners["...
function ct (line 2) | function ct(e,t,n){var r,i,o,a,s,u,l,c,f="width"in t||"height"in t,p=thi...
function ft (line 2) | function ft(e,t){var n,r,i,o,a;for(n in e)if(r=G(n),i=t[r],o=e[n],Array....
function pt (line 2) | function pt(e,t,n){var r,i,o=0,a=pt.prefilters.length,s=w.Deferred().alw...
function vt (line 2) | function vt(e){return(e.match(M)||[]).join(" ")}
function mt (line 2) | function mt(e){return e.getAttribute&&e.getAttribute("class")||""}
function xt (line 2) | function xt(e){return Array.isArray(e)?e:"string"==typeof e?e.match(M)||...
function jt (line 2) | function jt(e,t,n,r){var i;if(Array.isArray(t))w.each(t,function(t,i){n|...
function Ft (line 2) | function Ft(e){return function(t,n){"string"!=typeof t&&(n=t,t="*");var ...
function _t (line 2) | function _t(e,t,n,r){var i={},o=e===Wt;function a(s){var u;return i[s]=!...
function zt (line 2) | function zt(e,t){var n,r,i=w.ajaxSettings.flatOptions||{};for(n in t)voi...
function Xt (line 2) | function Xt(e,t,n){var r,i,o,a,s=e.contents,u=e.dataTypes;while("*"===u[...
function Ut (line 2) | function Ut(e,t,n,r){var i,o,a,s,u,l={},c=e.dataTypes.slice();if(c[1])fo...
function k (line 2) | function k(t,n,r,s){var l,p,d,b,T,C=n;c||(c=!0,u&&e.clearTimeout(u),i=vo...
Condensed preview — 40 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (626K chars).
[
{
"path": ".gitignore",
"chars": 2191,
"preview": "#Aurora test things\ncolourtest_from_youtube.mp4\n*.jpg\n*.jpeg\nenv/*\n.vscode/\naudio.py\nauroraspec.py\nspec.py\nnohup.out\ncon"
},
{
"path": "Aurora.py",
"chars": 29997,
"preview": "# Main Aurora python file, runs the webserver and the Aurora client, configs are loaded from extensions directory\nimport"
},
{
"path": "README.md",
"chars": 1988,
"preview": "[](https://join.slack.com/t/auroraambientlighting/shared_invi"
},
{
"path": "VERSION",
"chars": 27,
"preview": "0.42-catsdontlikefireworks\n"
},
{
"path": "__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "apt-requirements.txt",
"chars": 0,
"preview": ""
},
{
"path": "aurora.service",
"chars": 310,
"preview": "[Unit]\nDescription=Aurora light system\nAfter=multi-user.target\nStartLimitInterval=200\nStartLimitBurst=5\n[Service]\nWorkin"
},
{
"path": "extensions/Aurora_Ambient_16x9.py",
"chars": 1827,
"preview": "from lib.AuroraExtension import AuroraExtension\nimport time\n\nimport numpy as np\nimport pandas as pd\nimport cv2\n\n\nclass A"
},
{
"path": "extensions/Aurora_Ambient_AutoCrop.py",
"chars": 2185,
"preview": "from lib.AuroraExtension import AuroraExtension\nimport time\nimport datetime\nimport numpy as np\nimport pandas as pd\nimpor"
},
{
"path": "extensions/Aurora_Ambient_NoCrop.py",
"chars": 2556,
"preview": "from lib.AuroraExtension import AuroraExtension\nimport time\nimport datetime\nimport numpy as np\nimport pandas as pd\nimpor"
},
{
"path": "extensions/Aurora_AudioSpectogram.py",
"chars": 5390,
"preview": "from lib.AuroraExtension import AuroraExtension\nimport time\nimport datetime\nimport numpy as np\nimport pandas as pd\nimpor"
},
{
"path": "extensions/Aurora_Configure.py",
"chars": 1448,
"preview": "from lib.AuroraExtension import AuroraExtension\nimport neopixel\nimport time\nimport datetime\nimport numpy as np\nimport pa"
},
{
"path": "extensions/Aurora_Meteor.py",
"chars": 1902,
"preview": "from lib.AuroraExtension import AuroraExtension\nimport time\nimport datetime\nimport numpy as np\nimport pandas as pd\nimpor"
},
{
"path": "extensions/Aurora_Rainbow.py",
"chars": 1645,
"preview": "from lib.AuroraExtension import AuroraExtension\nimport time\nimport datetime\nimport numpy as np\nimport pandas as pd\nimpor"
},
{
"path": "extensions/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "extensions/exampleExtension.py",
"chars": 3312,
"preview": "from lib.AuroraExtension import AuroraExtension\nimport time\nfrom random import randint, choice\nimport numpy as np\nimport"
},
{
"path": "install.sh",
"chars": 2168,
"preview": "#!/bin/bash\necho -e \"\n ▄▄▄ █ ██ ██▀███ ▒█████ ██▀███ ▄▄▄ \n▒████▄ ██ ▓██▒▓██ ▒ ██▒▒██▒ ██▒▓██ "
},
{
"path": "requirements.txt",
"chars": 136,
"preview": "numpy >= 1.16.5\npandas\nopencv-python==4.5.1.48\ncherrypy\nconfigparser\nadafruit-circuitpython-neopixel\nblinker\njinja2\nsoun"
},
{
"path": "update.sh",
"chars": 2037,
"preview": "#!/bin/bash\necho -e \"\n ▄▄▄ █ ██ ██▀███ ▒█████ ██▀███ ▄▄▄ \n▒████▄ ██ ▓██▒▓██ ▒ ██▒▒██▒ ██▒▓██ "
},
{
"path": "webserver/static/css/bootstrap.css",
"chars": 202432,
"preview": "/*!\n * Bootstrap v4.5 (https://getbootstrap.com/)\n * Copyright 2011-2019 The Bootstrap Authors\n * Copyright 2011-2019 Tw"
},
{
"path": "webserver/static/css/style.css",
"chars": 119218,
"preview": "/*\n\n1. Table of Contents \n2. Typography\n3. Structure\n4. Page Logos\n5. Highlight Colors\n6. Header and Page Title\n7."
},
{
"path": "webserver/static/images/icons/license.txt",
"chars": 140,
"preview": "https://www.iconfinder.com/iconsets/round-varieties\r\n\r\n19 PNG icons by Creaticca Ltd - License: Creative Commons (Attri"
},
{
"path": "webserver/static/images/undraw/_license_and_link.rtf",
"chars": 2594,
"preview": "{\\rtf1\\ansi\\ansicpg1252\\cocoartf2512\n\\cocoatextscaling0\\cocoaplatform0{\\fonttbl\\f0\\fswiss\\fcharset0 Helvetica;\\f1\\froman"
},
{
"path": "webserver/static/js/aurora-configure.js",
"chars": 2563,
"preview": "function fetch_config_data() {\n ajax_send_data = {}\n ajax_send_data[\"pixelcount_left\"] = $('#aurora_configure_left').v"
},
{
"path": "webserver/static/js/aurora-generic.js",
"chars": 3794,
"preview": "currentAjaxRequest = null; // Stores our current ajax request so we can cancel it if we do another\n\nfunction togglePower"
},
{
"path": "webserver/static/js/aurora-index.js",
"chars": 932,
"preview": "function change_system_status()\n{\n \n $('#toggle_aurora_enabled').prop(\"disabled\",true)\n enabled_stats = $('#tog"
},
{
"path": "webserver/static/js/aurora-view.js",
"chars": 1350,
"preview": "function change_system_status() {\n\n $('#toggle_aurora_enabled').prop(\"disabled\", true)\n enabled_stats = $('#toggle"
},
{
"path": "webserver/static/js/custom.js",
"chars": 82064,
"preview": "$(window).on('load',function(){\r\n $('.menu').css('display','block');\r\n $('#preloader').addClass('preloader-hide');"
},
{
"path": "webserver/static/js/jquery.js",
"chars": 86926,
"preview": "/*! jQuery v3.3.1 | (c) JS Foundation and other contributors | jquery.org/license */\n!function(e,t){\"use strict\";\"object"
},
{
"path": "webserver/static/menu/menu-main.html",
"chars": 2532,
"preview": "<div class=\"card rounded-0 bg-aurora\" data-card-height=\"150\">\r\n <div class=\"card-top\">\r\n <a href=\"#\" class=\"cl"
},
{
"path": "webserver/templates/about.html",
"chars": 2837,
"preview": "{% include 'header.html' %}\r\n {% if configured == False %}\r\n <div class=\"alert mr-3 ml-3 rounded-s bg-yell"
},
{
"path": "webserver/templates/configure.html",
"chars": 7206,
"preview": "{% include 'header.html' %}\r\n<div class=\"card card-style\" style=\"max-width:40rem;\">\r\n <div class=\"content mb-0\">\r\n "
},
{
"path": "webserver/templates/footer.html",
"chars": 1389,
"preview": "</div>\n<!-- Page content ends here-->\n\n\n\n<!-- Main Menu-->\n<div id=\"menu-main\" class=\"menu menu-box-left rounded-0\" data"
},
{
"path": "webserver/templates/header.html",
"chars": 4321,
"preview": "<!DOCTYPE HTML>\n<html lang=\"en\">\n\n<head>\n <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\n <"
},
{
"path": "webserver/templates/index.html",
"chars": 5049,
"preview": "{% include 'header.html' %}\r\n {% if configured == False %}\r\n <div class=\"alert mr-3 ml-3 rounded-s bg-yell"
},
{
"path": "webserver/templates/menu-colors.html",
"chars": 3091,
"preview": "<div class=\"menu-title\">\r\n <p class=\"color-highlight font-600\">Choose your Favorite</p>\r\n <h1>Highlight</h1>\r\n "
},
{
"path": "webserver/templates/menu-footer.html",
"chars": 1206,
"preview": "<div class=\"card card-style\">\r\n <h4 class=\"font-28 text-center color-theme font-800 pt-3 mt-3\">AppKit</h4>\r\n <p cl"
},
{
"path": "webserver/templates/menu-share.html",
"chars": 1556,
"preview": "<div class=\"menu-title\">\r\n <p class=\"color-highlight\">Tap a link to</p>\r\n <h1>Share</h1>\r\n <a href=\"#\" class=\"c"
},
{
"path": "webserver/templates/status.json",
"chars": 188,
"preview": "{\n \"enabled\": {{enabled}},\n \"current_extension\": \"{{current_extension}}\",\n \"current_extension_class\": \"{{curren"
},
{
"path": "webserver/templates/view.html",
"chars": 1969,
"preview": " {% include 'header.html' %}\r\n\r\n \r\n\r\n <div class=\"row mb-1 text-center d-flex justify-content-center\">\r\n "
}
]
About this extraction
This page contains the full source code of the AndrewMohawk/Aurora GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 40 files (578.6 KB), approximately 179.9k tokens, and a symbol index with 179 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.