pride flags
78
_server.py
|
@ -1,3 +1,6 @@
|
|||
# MAKE SURE YOU INSTALL ALL NEEDED LIBRARIES!
|
||||
# pip install flask dotindex ensure-file
|
||||
|
||||
CONTENT_DIRECTORY = "./public/"
|
||||
SAVING_DIRECTORY = "./save/"
|
||||
|
||||
|
@ -12,6 +15,7 @@ import os
|
|||
import re
|
||||
|
||||
from DotIndex import DotIndex
|
||||
from ensure_file import ensure_file
|
||||
from typing import Union, Callable
|
||||
from flask import request, redirect
|
||||
from werkzeug.middleware.proxy_fix import ProxyFix
|
||||
|
@ -19,6 +23,36 @@ from werkzeug.middleware.proxy_fix import ProxyFix
|
|||
app = flask.Flask(__name__)
|
||||
app.url_map.strict_slashes = False
|
||||
|
||||
FLAGS = {
|
||||
"agender": "Agender",
|
||||
"ally": "Ally",
|
||||
"aroace": "Aroace",
|
||||
"aro": "Aromantic",
|
||||
"ace": "Asexual",
|
||||
"bicurious": "Bicurious",
|
||||
"bigender": "Bigender",
|
||||
"bi": "Bisexual",
|
||||
"cisgender": "Cisgender",
|
||||
"demiboy": "Demiboy",
|
||||
"demigirl": "Demigirl",
|
||||
"demiromantic": "Demiromantic",
|
||||
"demisexual": "Demisexual",
|
||||
"gay": "Gay (Rainbow)",
|
||||
"gayman": "Gay Man",
|
||||
"genderfluid": "Genderfluid",
|
||||
"intersex": "Intersex",
|
||||
"lesbian": "Lesbian",
|
||||
"nonbinary": "Nonbinary",
|
||||
"omnigender": "Omnigender",
|
||||
"pan": "Pansexual",
|
||||
"polyamory": "Polyamorous",
|
||||
"straight": "Straight",
|
||||
"transfem": "Transfeminine",
|
||||
"trans": "Transgender",
|
||||
"transmasc": "Transmasculine"
|
||||
}
|
||||
|
||||
|
||||
SOCIALS_REGEX = {
|
||||
"discord" : re.compile(r"^(?!.*\.\.)(?=.{2,32}$)[a-z0-9_.]+$"),
|
||||
"twitter" : re.compile(r"^(?!.*twitter)(?!.*admin)[a-z0-9_]{1,15}$", re.IGNORECASE),
|
||||
|
@ -126,24 +160,6 @@ def sha(string: Union[str, bytes]) -> str:
|
|||
return hashlib.sha256(string).hexdigest()
|
||||
return ""
|
||||
|
||||
def ensure_file(path: str, *, default_value: str="", folder: bool=False) -> None:
|
||||
if os.path.exists(path):
|
||||
if folder and not os.path.isdir(path):
|
||||
os.remove(path)
|
||||
os.makedirs(path)
|
||||
elif not folder and os.path.isdir(path):
|
||||
shutil.rmtree(path, ignore_errors=True)
|
||||
f = open(path, "w")
|
||||
f.write(default_value)
|
||||
f.close()
|
||||
else:
|
||||
if folder:
|
||||
os.makedirs(path)
|
||||
else:
|
||||
f = open(path, "w")
|
||||
f.write(default_value)
|
||||
f.close()
|
||||
|
||||
def escape_html(string: str) -> str:
|
||||
return string.replace("&", "&").replace("<", "<").replace("\"", """)
|
||||
|
||||
|
@ -226,11 +242,10 @@ def get_template(json, username):
|
|||
inner = add_to_output(inner, json, "compliments", "Compliments");
|
||||
inner = add_to_output(inner, json, "relationship", "Relationship<br>Descriptions");
|
||||
|
||||
try:
|
||||
social = json.social # type: ignore
|
||||
if "social" in json and len(json.social):
|
||||
inner += '<div class="added" id="social"><h2>Social Links</h2>'
|
||||
|
||||
for i in social:
|
||||
for i in json.social:
|
||||
if SOCIAL_INFO[i[1]]["link"]:
|
||||
inner += f"<div>{SOCIAL_ICONS[i[1]]} <a href='{SOCIAL_INFO[i[1]]['link'].replace('%q', i[0])}' target='_blank'>{SOCIAL_INFO[i[1]]['prefix']}{escape_html(i[0])}</a></div>"
|
||||
else:
|
||||
|
@ -238,10 +253,15 @@ def get_template(json, username):
|
|||
|
||||
inner += "</div>"
|
||||
|
||||
except AttributeError as e:
|
||||
print(e)
|
||||
if "flags" in json and len(json.flags):
|
||||
inner += '<div class="added" id="flags"><h2>Pride Flags</h2><div class="img-list">'
|
||||
|
||||
inner += "</div>"
|
||||
for i in json.flags:
|
||||
inner += f'<img src="/img/flags/{i}.png" title="{FLAGS[i]}">'
|
||||
|
||||
inner += "</div></div>"
|
||||
|
||||
inner += f'</div><div id="key">Key:<br>{icons["4"]} - Great<br>{icons["3"]} - Good<br>{icons["2"]} - Okay<br>{icons["1"]} - Bad</div><footer>Icons from <a href="https://fontawesome.com" target="_blank">Font Awesome</a></footer>'
|
||||
|
||||
return title, inner, styles, embed
|
||||
|
||||
|
@ -535,6 +555,13 @@ def api_save():
|
|||
social.append(i)
|
||||
user_data["social"] = sort_list(social, True)
|
||||
|
||||
if "flags" in x:
|
||||
flags = {}
|
||||
for i in x["flags"]:
|
||||
if i in FLAGS:
|
||||
flags[i] = None
|
||||
user_data["flags"] = sorted([i for i in flags])
|
||||
|
||||
f = open(f"{SAVING_DIRECTORY}{username}.json", "w")
|
||||
f.write(json.dumps(user_data))
|
||||
f.close()
|
||||
|
@ -590,8 +617,9 @@ app.route("/editor")(create_file_serve("editor.html"))
|
|||
app.route("/u/<path:user>")(get_user_page)
|
||||
app.route("/home")(home)
|
||||
|
||||
app.route("/js/<path:file>")(create_folder_serve("js"))
|
||||
app.route("/css/<path:file>")(create_folder_serve("css"))
|
||||
app.route("/img/flags/<path:file>")(create_folder_serve("img/flags"))
|
||||
app.route("/js/<path:file>")(create_folder_serve("js"))
|
||||
|
||||
app.route("/api/account/login", methods=["POST"])(api_account_login)
|
||||
app.route("/api/account/signup", methods=["POST"])(api_account_signup)
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
|
||||
function load(fromStart) {
|
||||
if (fromStart) { next = 0; }
|
||||
fetch(`/api/browse?sort=alphabetical&page=${next}`, {
|
||||
fetch(`/api/browse?sort=random&page=${next}`, {
|
||||
"method": "GET"
|
||||
}).then((request) => (request.json()))
|
||||
.then((json) => {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
body {
|
||||
padding-bottom: calc(20px + 8em);
|
||||
padding-bottom: calc(30px + 8em);
|
||||
}
|
||||
|
||||
svg {
|
||||
|
@ -26,6 +26,19 @@ footer a, footer a:visited, footer a:link {
|
|||
white-space: break-word;
|
||||
}
|
||||
|
||||
.added .img-list {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
column-gap: 0.5em;
|
||||
max-width: 10em;
|
||||
}
|
||||
|
||||
.added img {
|
||||
max-width: 4em;
|
||||
}
|
||||
|
||||
.added h2 {
|
||||
text-align: center;
|
||||
}
|
||||
|
|
BIN
public/img/flags/ace.png
Normal file
After Width: | Height: | Size: 9.5 KiB |
BIN
public/img/flags/agender.png
Normal file
After Width: | Height: | Size: 8.3 KiB |
BIN
public/img/flags/ally.png
Normal file
After Width: | Height: | Size: 36 KiB |
BIN
public/img/flags/aro.png
Normal file
After Width: | Height: | Size: 10 KiB |
BIN
public/img/flags/aroace.png
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
public/img/flags/bi.png
Normal file
After Width: | Height: | Size: 8.1 KiB |
BIN
public/img/flags/bicurious.png
Normal file
After Width: | Height: | Size: 9.5 KiB |
BIN
public/img/flags/bigender.png
Normal file
After Width: | Height: | Size: 8.5 KiB |
BIN
public/img/flags/cisgender.png
Normal file
After Width: | Height: | Size: 8 KiB |
BIN
public/img/flags/demiboy.png
Normal file
After Width: | Height: | Size: 9 KiB |
BIN
public/img/flags/demigirl.png
Normal file
After Width: | Height: | Size: 8.3 KiB |
BIN
public/img/flags/demiromantic.png
Normal file
After Width: | Height: | Size: 230 KiB |
BIN
public/img/flags/demisexual.png
Normal file
After Width: | Height: | Size: 25 KiB |
BIN
public/img/flags/gay.png
Normal file
After Width: | Height: | Size: 9.9 KiB |
BIN
public/img/flags/gayman.png
Normal file
After Width: | Height: | Size: 10 KiB |
BIN
public/img/flags/genderfluid.png
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
public/img/flags/intersex.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
public/img/flags/lesbian.png
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
public/img/flags/nonbinary.png
Normal file
After Width: | Height: | Size: 9.7 KiB |
BIN
public/img/flags/omnigender.png
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
public/img/flags/pan.png
Normal file
After Width: | Height: | Size: 9.9 KiB |
BIN
public/img/flags/polyamory.png
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
public/img/flags/straight.png
Normal file
After Width: | Height: | Size: 9.1 KiB |
BIN
public/img/flags/trans.png
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
public/img/flags/transfem.png
Normal file
After Width: | Height: | Size: 8.3 KiB |
BIN
public/img/flags/transmasc.png
Normal file
After Width: | Height: | Size: 8.6 KiB |
|
@ -158,6 +158,35 @@ const socialRegex = {
|
|||
}
|
||||
}
|
||||
|
||||
const flags = {
|
||||
agender: "Agender",
|
||||
ally: "Ally",
|
||||
aroace: "Aroace",
|
||||
aro: "Aromantic",
|
||||
ace: "Asexual",
|
||||
bicurious: "Bicurious",
|
||||
bigender: "Bigender",
|
||||
bi: "Bisexual",
|
||||
cisgender: "Cisgender",
|
||||
demiboy: "Demiboy",
|
||||
demigirl: "Demigirl",
|
||||
demiromantic: "Demiromantic",
|
||||
demisexual: "Demisexual",
|
||||
gay: "Gay (Rainbow)",
|
||||
gayman: "Gay Man",
|
||||
genderfluid: "Genderfluid",
|
||||
intersex: "Intersex",
|
||||
lesbian: "Lesbian",
|
||||
nonbinary: "Nonbinary",
|
||||
omnigender: "Omnigender",
|
||||
pan: "Pansexual",
|
||||
polyamory: "Polyamorous",
|
||||
straight: "Straight",
|
||||
transfem: "Transfeminine",
|
||||
trans: "Transgender",
|
||||
transmasc: "Transmasculine"
|
||||
}
|
||||
|
||||
const icons = {
|
||||
1: '<svg style="fill: var(--text-low-opacity);" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M323.8 477.2c-38.2 10.9-78.1-11.2-89-49.4l-5.7-20c-3.7-13-10.4-25-19.5-35l-51.3-56.4c-8.9-9.8-8.2-25 1.6-33.9s25-8.2 33.9 1.6l51.3 56.4c14.1 15.5 24.4 34 30.1 54.1l5.7 20c3.6 12.7 16.9 20.1 29.7 16.5s20.1-16.9 16.5-29.7l-5.7-20c-5.7-19.9-14.7-38.7-26.6-55.5-5.2-7.3-5.8-16.9-1.7-24.9s12.3-13 21.3-13H448c8.8 0 16-7.2 16-16 0-6.8-4.3-12.7-10.4-15-7.4-2.8-13-9-14.9-16.7s.1-15.8 5.3-21.7c2.5-2.8 4-6.5 4-10.6 0-7.8-5.6-14.3-13-15.7-8.2-1.6-15.1-7.3-18-15.2s-1.6-16.7 3.6-23.3c2.1-2.7 3.4-6.1 3.4-9.9 0-6.7-4.2-12.6-10.2-14.9-11.5-4.5-17.7-16.9-14.4-28.8.4-1.3.6-2.8.6-4.3 0-8.8-7.2-16-16-16h-97.5c-12.6 0-25 3.7-35.5 10.7l-61.7 41.1c-11 7.4-25.9 4.4-33.3-6.7s-4.4-25.9 6.7-33.3l61.7-41.1c18.4-12.3 40-18.8 62.1-18.8H384c34.7 0 62.9 27.6 64 62 14.6 11.7 24 29.7 24 50 0 4.5-.5 8.8-1.3 13 15.4 11.7 25.3 30.2 25.3 51 0 6.5-1 12.8-2.8 18.7 11.6 11.8 18.8 27.8 18.8 45.5 0 35.3-28.6 64-64 64h-92.3c4.7 10.4 8.7 21.2 11.8 32.2l5.7 20c10.9 38.2-11.2 78.1-49.4 89zM32 384c-17.7 0-32-14.3-32-32V128c0-17.7 14.3-32 32-32h64c17.7 0 32 14.3 32 32v224c0 17.7-14.3 32-32 32H32z"/></svg>',
|
||||
2: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path d="M100.5 176c-29 0-52.5 23.5-52.5 52.5V320c0 13.3-10.7 24-24 24S0 333.3 0 320v-91.5C0 173 45 128 100.5 128c29.6 0 57.6 13 76.7 35.6l130.2 153.8c10 11.8 24.6 18.6 40.1 18.6 29 0 52.5-23.5 52.5-52.5V192c0-13.3 10.7-24 24-24s24 10.7 24 24v91.5C448 339 403 384 347.5 384c-29.6 0-57.6-13-76.7-35.6L140.6 194.6c-10-11.8-24.6-18.6-40.1-18.6z"/></svg>',
|
||||
|
|
|
@ -58,11 +58,11 @@ function updateColors() {
|
|||
document.body.setAttribute("style", `--primary: ${colors.text}; --secondary-low-opacity: ${colors.text}22; --background: ${colors.background}; --background-low-opacity: ${colors.background}33; --accent: ${colors.accent}; --accent-low-opacity: ${colors.accent}66; --text: ${colors.text}; --text-low-opacity: ${colors.text}88;`);
|
||||
}
|
||||
|
||||
function get_list(key) {
|
||||
function get_list(key, hasInput=true) {
|
||||
let output = [];
|
||||
[...document.querySelectorAll(`#${key} div[id^="${key}-"]`)].forEach((val, index) => {
|
||||
if (!val.classList.contains("bad")) {
|
||||
output.push([val.querySelector("input").value, val.querySelector("select").value]);
|
||||
output.push(hasInput ? [val.querySelector("input").value, val.querySelector("select").value] : val.querySelector("select").value);
|
||||
}
|
||||
});
|
||||
return output;
|
||||
|
@ -94,6 +94,12 @@ for (const key of Object.keys(socialRegex)) {
|
|||
}
|
||||
socialInput += `</select><input class="bad" oninput="validate_input(this);" data-id="social-%i"></div><svg onclick="dom('social-%i').remove()">${icons.x}</svg>`;
|
||||
|
||||
let flagInput = "<select data-id=\"flags-%i\">";
|
||||
for (const key of Object.keys(flags)) {
|
||||
flagInput += `<option value="${key}">${flags[key]}</option>`
|
||||
}
|
||||
flagInput += `</select><svg onclick="dom('flags-%i').remove()">${icons.x}</svg>`
|
||||
|
||||
fetch("/api/account/self", {
|
||||
"method": "GET"
|
||||
}).then((response) => (response.json()))
|
||||
|
@ -129,7 +135,15 @@ fetch("/api/account/self", {
|
|||
i++;
|
||||
}
|
||||
|
||||
inner += `</div><button onclick="add_input('social', '${socialInput.replaceAll("\"", """).replaceAll("\'", "\\\'")}');">Add</button></div></div>`;
|
||||
inner += `</div><button onclick="add_input('social', '${socialInput.replaceAll("\"", """).replaceAll("\'", "\\\'")}');">Add</button></div></div>
|
||||
<div class="added" style="text-align: center;"><div style="text-align: left; margin-bottom: 10px;" id='flags'><h2>Pride Flags</h2>`;
|
||||
|
||||
i = 0;
|
||||
for (const flag of (json.flags || [])) {
|
||||
inner += `<div id="social-${i}" data-id="${i}">${flagInput.split("</select")[0].replaceAll("%i", i).replace(`value="${flag}"`, `selected value="${flag}"`)}</select><svg onclick="dom('social-${i}').remove()">${icons.x}</svg></div>`;
|
||||
i++;
|
||||
}
|
||||
inner += `</div><button onclick="add_input('flags', '${flagInput.replaceAll("\"", """).replaceAll("\'", "\\\'")}');">Add</button></div></div>`
|
||||
|
||||
x.id = "container";
|
||||
x.innerHTML = inner;
|
||||
|
@ -157,6 +171,7 @@ fetch("/api/account/self", {
|
|||
compliments: get_list("compliments"),
|
||||
relationship: get_list("relationship"),
|
||||
social: get_list("social"),
|
||||
flags: get_list("flags", false),
|
||||
public: dom("public").checked
|
||||
})
|
||||
}).then((response) => (response.text()))
|
||||
|
|
|
@ -12,23 +12,5 @@
|
|||
|
||||
<body>
|
||||
{{TEMPLATE}}
|
||||
|
||||
<div id="key">
|
||||
Key:<br>
|
||||
<span data-icon="4"></span> - Great<br>
|
||||
<span data-icon="3"></span> - Good<br>
|
||||
<span data-icon="2"></span> - Okay<br>
|
||||
<span data-icon="1"></span> - Bad
|
||||
</div>
|
||||
|
||||
<footer>
|
||||
Icons from <a href="https://fontawesome.com" target="_blank">Font Awesome</a>
|
||||
</footer>
|
||||
|
||||
<script>
|
||||
[...document.querySelectorAll("[data-icon]")].forEach(function(val, index) {
|
||||
val.innerHTML = icons[val.dataset.icon];
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|