casino + style shit

remotes/1693045480750635534/spooky-22
Aevann1 2022-09-05 01:15:37 +02:00
parent 554c53633c
commit 0c32d56cd6
71 changed files with 1722 additions and 4267 deletions

View File

@ -1,5 +1,5 @@
document.getElementById('awardModal').addEventListener('show.bs.modal', function (event) { document.getElementById('awardModal').addEventListener('show.bs.modal', function (event) {
document.getElementById("awardTarget").action = event.relatedTarget.dataset.url; document.getElementById("awardTarget").action = event.relatedTarget.dataset.url;
}); });
// TODO: Refactor this ugly shit who wrote this lmao // TODO: Refactor this ugly shit who wrote this lmao
@ -9,7 +9,7 @@ function vote(type, id, dir) {
const scoretexts = document.getElementsByClassName(type + '-score-' + id); const scoretexts = document.getElementsByClassName(type + '-score-' + id);
for (let i=0; i<upvotes.length; i++) { for (let i=0; i<upvotes.length; i++) {
const upvote = upvotes[i] const upvote = upvotes[i]
const downvote = downvotes[i] const downvote = downvotes[i]
const scoretext = scoretexts[i] const scoretext = scoretexts[i]
@ -90,7 +90,7 @@ function vote(type, id, dir) {
} }
} }
} }
const xhr = new XMLHttpRequest(); const xhr = new XMLHttpRequest();
xhr.open("POST", "/vote/" + type.replace('-mobile','') + "/" + id + "/" + votedirection); xhr.open("POST", "/vote/" + type.replace('-mobile','') + "/" + id + "/" + votedirection);
xhr.setRequestHeader('xhr', 'xhr'); xhr.setRequestHeader('xhr', 'xhr');

View File

@ -24,7 +24,7 @@ function banModal(link, id, name) {
bootstrap.Toast.getOrCreateInstance(document.getElementById('toast-post-error')).show(); bootstrap.Toast.getOrCreateInstance(document.getElementById('toast-post-error')).show();
} }
}; };
xhr.send(form); xhr.send(form);
} }
} }

View File

@ -13,7 +13,7 @@ if (window.location.pathname != '/submit')
const submitButtonDOMs = formDOM.querySelectorAll('input[type=submit], .btn-primary'); const submitButtonDOMs = formDOM.querySelectorAll('input[type=submit], .btn-primary');
if(submitButtonDOMs.length === 0) if(submitButtonDOMs.length === 0)
throw new TypeError("I am unable to find the submit button :(. Contact the head custodian immediately.") throw new TypeError("I am unable to find the submit button :(. Contact the head custodian immediately.")
const btn = submitButtonDOMs[0] const btn = submitButtonDOMs[0]
btn.click(); btn.click();
btn.disabled = true; btn.disabled = true;
@ -169,7 +169,7 @@ var bsTriggerOnReady = function() {
bs_trigger(document); bs_trigger(document);
} }
if (document.readyState === "complete" || if (document.readyState === "complete" ||
(document.readyState !== "loading" && !document.documentElement.doScroll)) { (document.readyState !== "loading" && !document.documentElement.doScroll)) {
bsTriggerOnReady(); bsTriggerOnReady();
} else { } else {

View File

@ -1,215 +1,215 @@
// Shared // Shared
function updatePlayerCoins(updated) { function updatePlayerCoins(updated) {
if (updated.coins) { if (updated.coins) {
document.getElementById("user-coins-amount").innerText = updated.coins; document.getElementById("user-coins-amount").innerText = updated.coins;
} }
if (updated.procoins) { if (updated.procoins) {
document.getElementById("user-bux-amount").innerText = updated.procoins; document.getElementById("user-bux-amount").innerText = updated.procoins;
} }
} }
// Slots // Slots
function pullSlots() { function pullSlots() {
const wager = document.getElementById("casinoSlotsBet").value; const wager = document.getElementById("casinoSlotsBet").value;
const currency = document.querySelector( const currency = document.querySelector(
'input[name="casinoSlotsCurrency"]:checked' 'input[name="casinoSlotsCurrency"]:checked'
).value; ).value;
document.getElementById("casinoSlotsBet").disabled = true; document.getElementById("casinoSlotsBet").disabled = true;
document.getElementById("casinoSlotsPull").disabled = true; document.getElementById("casinoSlotsPull").disabled = true;
const xhr = new XMLHttpRequest(); const xhr = new XMLHttpRequest();
xhr.open("post", "/casino/slots"); xhr.open("post", "/casino/slots");
xhr.onload = handleSlotsResponse.bind(null, xhr); xhr.onload = handleSlotsResponse.bind(null, xhr);
const form = new FormData(); const form = new FormData();
form.append("formkey", formkey()); form.append("formkey", formkey());
form.append("wager", wager); form.append("wager", wager);
form.append("currency", currency); form.append("currency", currency);
xhr.send(form); xhr.send(form);
} }
function handleSlotsResponse(xhr) { function handleSlotsResponse(xhr) {
let response; let response;
try { try {
response = JSON.parse(xhr.response); response = JSON.parse(xhr.response);
} catch (error) { } catch (error) {
console.error(error); console.error(error);
} }
const succeeded = const succeeded =
xhr.status >= 200 && xhr.status < 300 && response && !response.error; xhr.status >= 200 && xhr.status < 300 && response && !response.error;
const slotsResult = document.getElementById("casinoSlotsResult"); const slotsResult = document.getElementById("casinoSlotsResult");
slotsResult.classList.remove("text-success", "text-danger"); slotsResult.classList.remove("text-success", "text-danger");
if (succeeded) { if (succeeded) {
const { game_state, gambler } = response; const { game_state, gambler } = response;
const state = JSON.parse(game_state); const state = JSON.parse(game_state);
const reels = Array.from(document.querySelectorAll(".reel")); const reels = Array.from(document.querySelectorAll(".reel"));
const symbols = state.symbols.split(","); const symbols = state.symbols.split(",");
for (let i = 0; i < 3; i++) { for (let i = 0; i < 3; i++) {
reels[i].innerHTML = symbols[i]; reels[i].innerHTML = symbols[i];
} }
slotsResult.style.visibility = "visible"; slotsResult.style.visibility = "visible";
slotsResult.innerText = state.text; slotsResult.innerText = state.text;
if (state.text.includes("Won")) { if (state.text.includes("Won")) {
if (state.text.includes("Jackpot")) { if (state.text.includes("Jackpot")) {
slotsResult.classList.add("text-warning"); slotsResult.classList.add("text-warning");
} else { } else {
slotsResult.classList.add("text-success"); slotsResult.classList.add("text-success");
} }
} else if (state.text.includes("Lost")) { } else if (state.text.includes("Lost")) {
slotsResult.classList.add("text-danger"); slotsResult.classList.add("text-danger");
} }
updatePlayerCoins(gambler); updatePlayerCoins(gambler);
} else { } else {
slotsResult.style.visibility = "visible"; slotsResult.style.visibility = "visible";
slotsResult.innerText = response.error; slotsResult.innerText = response.error;
slotsResult.classList.add("text-danger"); slotsResult.classList.add("text-danger");
console.error(response.error); console.error(response.error);
} }
document.getElementById("casinoSlotsBet").disabled = false; document.getElementById("casinoSlotsBet").disabled = false;
document.getElementById("casinoSlotsPull").disabled = false; document.getElementById("casinoSlotsPull").disabled = false;
} }
// Blackjack // Blackjack
// When the casino loads, look up the "blackjack status" of a player to either start a new game or continue an existing game. // When the casino loads, look up the "blackjack status" of a player to either start a new game or continue an existing game.
if ( if (
document.readyState === "complete" || document.readyState === "complete" ||
(document.readyState !== "loading" && !document.documentElement.doScroll) (document.readyState !== "loading" && !document.documentElement.doScroll)
) { ) {
checkBlackjackStatus(); checkBlackjackStatus();
} else { } else {
document.addEventListener("DOMContentLoaded", checkBlackjackStatus); document.addEventListener("DOMContentLoaded", checkBlackjackStatus);
} }
function checkBlackjackStatus() { function checkBlackjackStatus() {
const xhr = new XMLHttpRequest(); const xhr = new XMLHttpRequest();
xhr.open("get", "/casino/blackjack"); xhr.open("get", "/casino/blackjack");
xhr.onload = handleBlackjackStatusResponse.bind(null, xhr); xhr.onload = handleBlackjackStatusResponse.bind(null, xhr);
xhr.send(); xhr.send();
} }
function handleBlackjackStatusResponse(xhr) { function handleBlackjackStatusResponse(xhr) {
let response; let response;
try { try {
response = JSON.parse(xhr.response); response = JSON.parse(xhr.response);
} catch (error) { } catch (error) {
console.error(error); console.error(error);
} }
const succeeded = const succeeded =
xhr.status >= 200 && xhr.status < 300 && response && !response.error; xhr.status >= 200 && xhr.status < 300 && response && !response.error;
if (succeeded) { if (succeeded) {
if (response.active) { if (response.active) {
updateBlackjack(response.game_state); updateBlackjack(response.game_state);
} }
} else { } else {
console.error("error"); console.error("error");
} }
} }
// When starting a new game or taking an action in an existing one, a new state will be returned, and the DOM must be updated. // When starting a new game or taking an action in an existing one, a new state will be returned, and the DOM must be updated.
function updateBlackjack(state) { function updateBlackjack(state) {
const { player, dealer, status } = state; const { player, dealer, status } = state;
const lettersToSuits = { const lettersToSuits = {
S: "♠️", S: "♠️",
H: "♥️", H: "♥️",
C: "♣️", C: "♣️",
D: "♦️", D: "♦️",
"?": "?", "?": "?",
}; };
const suitsToColors = { const suitsToColors = {
"♠️": "black", "♠️": "black",
"♥️": "red", "♥️": "red",
"♣️": "black", "♣️": "black",
"♦️": "red", "♦️": "red",
"?": "black", "?": "black",
}; };
// Clear everything. // Clear everything.
Array.from(document.querySelectorAll(".playing-card")).forEach((card) => { Array.from(document.querySelectorAll(".playing-card")).forEach((card) => {
card.innerText = ""; card.innerText = "";
card.style.color = "unset"; card.style.color = "unset";
card.classList.remove("dealt"); card.classList.remove("dealt");
}); });
// Show dealer cards. // Show dealer cards.
const dealerSlots = Array.from( const dealerSlots = Array.from(
document.querySelectorAll('.playing-card[data-who="dealer"]') document.querySelectorAll('.playing-card[data-who="dealer"]')
); );
for (let i = 0; i < dealer.length; i++) { for (let i = 0; i < dealer.length; i++) {
const slot = dealerSlots[i]; const slot = dealerSlots[i];
if (slot) { if (slot) {
// Technically, the dealer can use more than 5 cards, though it's rare. // Technically, the dealer can use more than 5 cards, though it's rare.
// In that case, the result message is good enough. // In that case, the result message is good enough.
// Thanks, Carp. 🐠 // Thanks, Carp. 🐠
slot.classList.add("dealt"); slot.classList.add("dealt");
if (i > 0 && status === "active") { if (i > 0 && status === "active") {
break; break;
} }
const rank = dealer[i][0]; const rank = dealer[i][0];
const suit = lettersToSuits[dealer[i][1]]; const suit = lettersToSuits[dealer[i][1]];
const card = rank + suit; const card = rank + suit;
slot.innerText = card; slot.innerText = card;
slot.style.color = suitsToColors[suit]; slot.style.color = suitsToColors[suit];
} }
} }
// Show player cards. // Show player cards.
const playerSlots = Array.from( const playerSlots = Array.from(
document.querySelectorAll('.playing-card[data-who="player"]') document.querySelectorAll('.playing-card[data-who="player"]')
); );
for (let i = 0; i < player.length; i++) { for (let i = 0; i < player.length; i++) {
const slot = playerSlots[i]; const slot = playerSlots[i];
const rank = player[i][0]; const rank = player[i][0];
const suit = lettersToSuits[player[i][1]]; const suit = lettersToSuits[player[i][1]];
const card = rank + suit; const card = rank + suit;
slot.innerText = card; slot.innerText = card;
slot.style.color = suitsToColors[suit]; slot.style.color = suitsToColors[suit];
slot.classList.add("dealt"); slot.classList.add("dealt");
} }
updateBlackjackActions(state); updateBlackjackActions(state);
if (status !== "active") { if (status !== "active") {
revealBlackjackResult(state); revealBlackjackResult(state);
} }
} }
function revealBlackjackResult(state) { function revealBlackjackResult(state) {
const blackjackResult = document.getElementById("casinoBlackjackResult"); const blackjackResult = document.getElementById("casinoBlackjackResult");
const lookup = { const lookup = {
bust: ["Bust. Didn't work out for you, did it?", "danger"], bust: ["Bust. Didn't work out for you, did it?", "danger"],
push: ["Pushed. This whole hand never happened.", "secondary"], push: ["Pushed. This whole hand never happened.", "secondary"],
insured_loss: ["Lost, but at least you had insurance.", "secondary"], insured_loss: ["Lost, but at least you had insurance.", "secondary"],
lost: ["Lost. That was pathetic.", "danger"], lost: ["Lost. That was pathetic.", "danger"],
won: ["Won. This time.", "success"], won: ["Won. This time.", "success"],
blackjack: ["Blackjack! Must be your lucky day.", "warning"], blackjack: ["Blackjack! Must be your lucky day.", "warning"],
}; };
const [resultText, resultClass] = lookup[state.status]; const [resultText, resultClass] = lookup[state.status];
blackjackResult.style.visibility = "visible"; blackjackResult.style.visibility = "visible";
blackjackResult.innerText = resultText; blackjackResult.innerText = resultText;
blackjackResult.classList.add(`text-${resultClass}`); blackjackResult.classList.add(`text-${resultClass}`);
} }
function buildBlackjackAction(id, method, title, fullWidth = false) { function buildBlackjackAction(id, method, title, fullWidth = false) {
return ` return `
<button <button
type="button" type="button"
class="btn btn-${fullWidth ? "primary" : "secondary"} lottery-page--action" class="btn btn-${fullWidth ? "primary" : "secondary"} lottery-page--action"
@ -223,88 +223,88 @@ function buildBlackjackAction(id, method, title, fullWidth = false) {
} }
function clearBlackjackActions() { function clearBlackjackActions() {
const actionWrapper = document.getElementById("casinoBlackjackActions"); const actionWrapper = document.getElementById("casinoBlackjackActions");
actionWrapper.innerHTML = ""; actionWrapper.innerHTML = "";
} }
function updateBlackjackActions(state) { function updateBlackjackActions(state) {
const actionWrapper = document.getElementById("casinoBlackjackActions"); const actionWrapper = document.getElementById("casinoBlackjackActions");
clearBlackjackActions(); clearBlackjackActions();
if (state.status === "active") { if (state.status === "active") {
document.getElementById("casinoBlackjackWager").style.display = "none"; document.getElementById("casinoBlackjackWager").style.display = "none";
const actionLookup = { const actionLookup = {
hit: buildBlackjackAction("casinoBlackjackHit", "hitBlackjack", "Hit"), hit: buildBlackjackAction("casinoBlackjackHit", "hitBlackjack", "Hit"),
stay: buildBlackjackAction( stay: buildBlackjackAction(
"casinoBlackjackStay", "casinoBlackjackStay",
"stayBlackjack", "stayBlackjack",
"Stay" "Stay"
), ),
double_down: buildBlackjackAction( double_down: buildBlackjackAction(
"casinoBlackjackDouble", "casinoBlackjackDouble",
"doubleBlackjack", "doubleBlackjack",
"Double Down" "Double Down"
), ),
insure: buildBlackjackAction( insure: buildBlackjackAction(
"casinoBlackjackInsure", "casinoBlackjackInsure",
"insureBlackjack", "insureBlackjack",
"Insure" "Insure"
), ),
}; };
const actions = state.actions.map((action) => actionLookup[action]); const actions = state.actions.map((action) => actionLookup[action]);
actionWrapper.innerHTML = actions.join("\n"); actionWrapper.innerHTML = actions.join("\n");
} else { } else {
// Game is over, deal a new game. // Game is over, deal a new game.
document.getElementById("casinoBlackjackWager").style.display = "flex"; document.getElementById("casinoBlackjackWager").style.display = "flex";
document.getElementById("casinoBlackjackBet").disabled = false; document.getElementById("casinoBlackjackBet").disabled = false;
const deal = buildBlackjackAction( const deal = buildBlackjackAction(
"casinoBlackjackDeal", "casinoBlackjackDeal",
"dealBlackjack", "dealBlackjack",
"Deal", "Deal",
true true
); );
actionWrapper.innerHTML = deal; actionWrapper.innerHTML = deal;
} }
} }
function dealBlackjack() { function dealBlackjack() {
const wager = document.getElementById("casinoBlackjackBet").value; const wager = document.getElementById("casinoBlackjackBet").value;
const currency = document.querySelector( const currency = document.querySelector(
'input[name="casinoBlackjackCurrency"]:checked' 'input[name="casinoBlackjackCurrency"]:checked'
).value; ).value;
document.getElementById("casinoBlackjackBet").disabled = true; document.getElementById("casinoBlackjackBet").disabled = true;
document.getElementById("casinoBlackjackDeal").disabled = true; document.getElementById("casinoBlackjackDeal").disabled = true;
document.getElementById("casinoBlackjackWager").style.display = "none"; document.getElementById("casinoBlackjackWager").style.display = "none";
document.getElementById("casinoBlackjackResult").style.visibility = "hidden"; document.getElementById("casinoBlackjackResult").style.visibility = "hidden";
const xhr = new XMLHttpRequest(); const xhr = new XMLHttpRequest();
xhr.open("post", "/casino/blackjack"); xhr.open("post", "/casino/blackjack");
xhr.onload = handleBlackjackResponse.bind(null, xhr); xhr.onload = handleBlackjackResponse.bind(null, xhr);
const form = new FormData(); const form = new FormData();
form.append("formkey", formkey()); form.append("formkey", formkey());
form.append("wager", wager); form.append("wager", wager);
form.append("currency", currency); form.append("currency", currency);
xhr.send(form); xhr.send(form);
} }
function takeBlackjackAction(action) { function takeBlackjackAction(action) {
const xhr = new XMLHttpRequest(); const xhr = new XMLHttpRequest();
xhr.open("post", "/casino/blackjack/action"); xhr.open("post", "/casino/blackjack/action");
xhr.onload = handleBlackjackResponse.bind(null, xhr); xhr.onload = handleBlackjackResponse.bind(null, xhr);
const form = new FormData(); const form = new FormData();
form.append("formkey", formkey()); form.append("formkey", formkey());
form.append("action", action); form.append("action", action);
xhr.send(form); xhr.send(form);
} }
const hitBlackjack = takeBlackjackAction.bind(null, "hit"); const hitBlackjack = takeBlackjackAction.bind(null, "hit");
@ -313,32 +313,32 @@ const doubleBlackjack = takeBlackjackAction.bind(null, "double_down");
const insureBlackjack = takeBlackjackAction.bind(null, "insure"); const insureBlackjack = takeBlackjackAction.bind(null, "insure");
function handleBlackjackResponse(xhr) { function handleBlackjackResponse(xhr) {
let response; let response;
try { try {
response = JSON.parse(xhr.response); response = JSON.parse(xhr.response);
} catch (error) { } catch (error) {
console.error(error); console.error(error);
} }
const succeeded = const succeeded =
xhr.status >= 200 && xhr.status < 300 && response && !response.error; xhr.status >= 200 && xhr.status < 300 && response && !response.error;
const blackjackResult = document.getElementById("casinoBlackjackResult"); const blackjackResult = document.getElementById("casinoBlackjackResult");
blackjackResult.classList.remove("text-success", "text-danger"); blackjackResult.classList.remove("text-success", "text-danger");
if (succeeded) { if (succeeded) {
if (response.game_state) { if (response.game_state) {
updateBlackjack(response.game_state); updateBlackjack(response.game_state);
} }
if (response.gambler) { if (response.gambler) {
updatePlayerCoins(response.gambler); updatePlayerCoins(response.gambler);
} }
} else { } else {
blackjackResult.style.visibility = "visible"; blackjackResult.style.visibility = "visible";
blackjackResult.innerText = response.error; blackjackResult.innerText = response.error;
blackjackResult.classList.add("text-danger"); blackjackResult.classList.add("text-danger");
console.error(response.error); console.error(response.error);
} }
} }

View File

@ -14,7 +14,7 @@ function category_modal(id, title, sub) {
document.getElementById("category-modal-body").innerHTML = ''; document.getElementById("category-modal-body").innerHTML = '';
categories.forEach(function (c) { categories.forEach(function (c) {
document.getElementById("category-modal-body").innerHTML += document.getElementById("category-modal-body").innerHTML +=
`<div class="category--tag-button" data-category="${c.id}">` + `<div class="category--tag-button" data-category="${c.id}">` +
`<span class="post--category-tag" style="color:${c.color_text}; ` + `<span class="post--category-tag" style="color:${c.color_text}; ` +
`background-color:${c.color_bg};">${c.name}</span>` + `background-color:${c.color_bg};">${c.name}</span>` +

View File

@ -183,9 +183,9 @@ socket.on('typing', function (users){
}) })
function scroll_chat() { function scroll_chat() {
setTimeout(function () { setTimeout(function () {
box.scrollTo(0, box.scrollHeight) box.scrollTo(0, box.scrollHeight)
}, 200); }, 200);
} }
scroll_chat(); scroll_chat();

View File

@ -1,7 +1,7 @@
function pinned_timestamp(id) { function pinned_timestamp(id) {
const el = document.getElementById(id) const el = document.getElementById(id)
const time = new Date(parseInt(el.dataset.timestamp)*1000) const time = new Date(parseInt(el.dataset.timestamp)*1000)
const pintooltip = el.getAttribute("data-bs-original-title") const pintooltip = el.getAttribute("data-bs-original-title")
if (!pintooltip.includes('until')) el.setAttribute("data-bs-original-title", `${pintooltip} until ${time}`) if (!pintooltip.includes('until')) el.setAttribute("data-bs-original-title", `${pintooltip} until ${time}`)
} }
@ -12,21 +12,21 @@ popClickBadgeTemplateDOM.loading = "lazy";
popClickBadgeTemplateDOM.alt = "badge"; popClickBadgeTemplateDOM.alt = "badge";
/** /**
* @param {MouseEvent} e * @param {MouseEvent} e
*/ */
function popclick(e) { function popclick(e) {
// We let through those methods // We let through those methods
if(e.ctrlKey || e.metaKey || e.shiftKey || e.altKey) if(e.ctrlKey || e.metaKey || e.shiftKey || e.altKey)
return true; return true;
e.preventDefault(); e.preventDefault();
if(e.currentTarget.dataset.popInfo === undefined) if(e.currentTarget.dataset.popInfo === undefined)
throw new SyntaxError("ill formed HTML! data-pop-info not present!!!"); throw new SyntaxError("ill formed HTML! data-pop-info not present!!!");
const author = JSON.parse(e.currentTarget.dataset.popInfo); const author = JSON.parse(e.currentTarget.dataset.popInfo);
if(!(author instanceof Object)) if(!(author instanceof Object))
throw new SyntaxError("data-pop-info was not valid!"); throw new SyntaxError("data-pop-info was not valid!");
// This is terrible lol. in header.js:bs_trigger() we should add // This is terrible lol. in header.js:bs_trigger() we should add
// to the ANCHOR element an event handler for "show.bs.tooltip" to build this // to the ANCHOR element an event handler for "show.bs.tooltip" to build this
// when the DOM is ready. // when the DOM is ready.

View File

@ -11,7 +11,7 @@ function expandMarkdown(id,type) {
const items = document.getElementsByClassName(`expand-text-${type}-${id}`) const items = document.getElementsByClassName(`expand-text-${type}-${id}`)
for (let i=0; i < items.length; i++) for (let i=0; i < items.length; i++)
{ {
const e = items[i] const e = items[i]
if (e.innerHTML == 'View source') e.innerHTML = 'Hide source' if (e.innerHTML == 'View source') e.innerHTML = 'Hide source'
else e.innerHTML = 'View source' else e.innerHTML = 'View source'

View File

@ -94,7 +94,7 @@ function toggleEdit(id){
function delete_commentModal(id) { function delete_commentModal(id) {
document.getElementById("deleteCommentButton").onclick = function() { document.getElementById("deleteCommentButton").onclick = function() {
const xhr = new XMLHttpRequest(); const xhr = new XMLHttpRequest();
xhr.open("POST", `/delete/comment/${id}`); xhr.open("POST", `/delete/comment/${id}`);
xhr.setRequestHeader('xhr', 'xhr'); xhr.setRequestHeader('xhr', 'xhr');
@ -223,7 +223,7 @@ function post_comment(fullname, hide){
form.append('file', e); form.append('file', e);
} }
catch(e) {} catch(e) {}
const xhr = new XMLHttpRequest(); const xhr = new XMLHttpRequest();
xhr.open("post", "/comment"); xhr.open("post", "/comment");
xhr.setRequestHeader('xhr', 'xhr'); xhr.setRequestHeader('xhr', 'xhr');
@ -329,7 +329,7 @@ function handle_action(type, cid, thing) {
form.append('formkey', formkey()); form.append('formkey', formkey());
form.append('comment_id', cid); form.append('comment_id', cid);
form.append('thing', thing); form.append('thing', thing);
const xhr = new XMLHttpRequest(); const xhr = new XMLHttpRequest();
xhr.open("post", `/${type}/${cid}`); xhr.open("post", `/${type}/${cid}`);
xhr.setRequestHeader('xhr', 'xhr'); xhr.setRequestHeader('xhr', 'xhr');
@ -351,7 +351,7 @@ function handle_action(type, cid, thing) {
} }
setTimeout(() => { setTimeout(() => {
for (const btn of btns) for (const btn of btns)
{ {
btn.disabled = false; btn.disabled = false;
btn.classList.remove('disabled'); btn.classList.remove('disabled');
} }

View File

@ -1,5 +1,5 @@
function delete_postModal(id) { function delete_postModal(id) {
document.getElementById("deletePostButton").onclick = function() { document.getElementById("deletePostButton").onclick = function() {
const xhr = new XMLHttpRequest(); const xhr = new XMLHttpRequest();
xhr.open("POST", `/delete_post/${id}`); xhr.open("POST", `/delete_post/${id}`);
xhr.setRequestHeader('xhr', 'xhr'); xhr.setRequestHeader('xhr', 'xhr');

View File

@ -5,13 +5,13 @@ published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version. License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details. GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
Copyright (C) 2022 Dr Steven Transmisia, anti-evil engineer, Copyright (C) 2022 Dr Steven Transmisia, anti-evil engineer,
2022 Nekobit, king autist 2022 Nekobit, king autist
*/ */
// Status // Status
@ -50,14 +50,14 @@ const EMOIJ_SEARCH_ENGINE_MIN_INTERVAL = 350;
let emojiSearcher = { let emojiSearcher = {
working: false, working: false,
queries: [], queries: [],
addQuery: function(query) addQuery: function(query)
{ {
this.queries.push(query); this.queries.push(query);
if(!this.working) if(!this.working)
this.work(); this.work();
}, },
work: async function work() { work: async function work() {
this.working = true; this.working = true;
@ -82,7 +82,7 @@ let emojiSearcher = {
// Search // Search
const resultSet = emojisSearchDictionary.completeSearch(query); const resultSet = emojisSearchDictionary.completeSearch(query);
// update stuff // update stuff
for(const [emojiName, emojiDOM] of Object.entries(emojiDOMs)) for(const [emojiName, emojiDOM] of Object.entries(emojiDOMs))
emojiDOM.hidden = !resultSet.has(emojiName); emojiDOM.hidden = !resultSet.has(emojiName);
@ -132,7 +132,7 @@ const emojisSearchDictionary = {
/** /**
* We find the name of each emojis that has a tag that starts with query. * We find the name of each emojis that has a tag that starts with query.
* *
* Basically I run a binary search to find a tag that starts with a query, then I look left and right * Basically I run a binary search to find a tag that starts with a query, then I look left and right
* for other tags tat start with the query. As the array is ordered this algo is sound. * for other tags tat start with the query. As the array is ordered this algo is sound.
* @param {String} tag * @param {String} tag
@ -159,11 +159,11 @@ const emojisSearchDictionary = {
for(let i = target; i >= 0 && this.dict[i].tag.startsWith(query); i--) for(let i = target; i >= 0 && this.dict[i].tag.startsWith(query); i--)
for(let j = 0; j < this.dict[i].emojiNames.length; j++) for(let j = 0; j < this.dict[i].emojiNames.length; j++)
result.add(this.dict[i].emojiNames[j]); result.add(this.dict[i].emojiNames[j]);
for(let i = target + 1; i < this.dict.length && this.dict[i].tag.startsWith(query); i++) for(let i = target + 1; i < this.dict.length && this.dict[i].tag.startsWith(query); i++)
for(let j = 0; j < this.dict[i].emojiNames.length; j++) for(let j = 0; j < this.dict[i].emojiNames.length; j++)
result.add(this.dict[i].emojiNames[j]); result.add(this.dict[i].emojiNames[j]);
return result; return result;
}, },
@ -179,7 +179,7 @@ const emojisSearchDictionary = {
if(this.dict[i].tag.includes(query)) if(this.dict[i].tag.includes(query))
for(let j = 0; j < this.dict[i].emojiNames.length; j++) for(let j = 0; j < this.dict[i].emojiNames.length; j++)
result.add(this.dict[i].emojiNames[j]) result.add(this.dict[i].emojiNames[j])
return result; return result;
} }
}; };
@ -188,7 +188,7 @@ const emojisSearchDictionary = {
const emojiRequest = new XMLHttpRequest(); const emojiRequest = new XMLHttpRequest();
emojiRequest.open("GET", '/marsey_list.json'); emojiRequest.open("GET", '/marsey_list.json');
emojiRequest.onload = async (e) => { emojiRequest.onload = async (e) => {
let emojis = JSON.parse(emojiRequest.response); let emojis = JSON.parse(emojiRequest.response);
if(! (emojis instanceof Array )) if(! (emojis instanceof Array ))
throw new TypeError("[EMOJI DIALOG] rDrama's server should have sent a JSON-coded Array!"); throw new TypeError("[EMOJI DIALOG] rDrama's server should have sent a JSON-coded Array!");
@ -221,13 +221,13 @@ emojiRequest.onload = async (e) => {
emojiDOM.dataset.className = emoji.class; emojiDOM.dataset.className = emoji.class;
emojiDOM.dataset.emojiName = emoji.name; emojiDOM.dataset.emojiName = emoji.name;
emojiDOM.onclick = emojiAddToInput; emojiDOM.onclick = emojiAddToInput;
emojiDOM.hidden = true; emojiDOM.hidden = true;
const emojiIMGDOM = emojiDOM.children[0]; const emojiIMGDOM = emojiDOM.children[0];
emojiIMGDOM.src = "/e/" + emoji.name + ".webp"; emojiIMGDOM.src = "/e/" + emoji.name + ".webp";
emojiIMGDOM.alt = emoji.name; emojiIMGDOM.alt = emoji.name;
/** Disableing lazy loading seems to reduce cpu usage somehow (?) /** Disableing lazy loading seems to reduce cpu usage somehow (?)
* idk it is difficult to benchmark */ * idk it is difficult to benchmark */
emojiIMGDOM.loading = "lazy"; emojiIMGDOM.loading = "lazy";
// Save reference // Save reference
@ -257,8 +257,8 @@ emojiRequest.onload = async (e) => {
// Show favorite for start. // Show favorite for start.
await classesSelectorDOM.children[0].children[0].click(); await classesSelectorDOM.children[0].children[0].click();
// Send it to the render machine! // Send it to the render machine!
emojiResultsDOM.appendChild(bussyDOM); emojiResultsDOM.appendChild(bussyDOM);
emojiResultsDOM.hidden = false; emojiResultsDOM.hidden = false;
@ -267,9 +267,9 @@ emojiRequest.onload = async (e) => {
} }
/** /**
* *
* @param {Event} e * @param {Event} e
*/ */
function switchEmojiTab(e) function switchEmojiTab(e)
{ {
const className = e.currentTarget.dataset.className; const className = e.currentTarget.dataset.className;
@ -292,14 +292,14 @@ function switchEmojiTab(e)
const favs = emojiFirstBoot ? emojisSearchDictionary.searchFor("anton-d") : Object.keys(Object.fromEntries( const favs = emojiFirstBoot ? emojisSearchDictionary.searchFor("anton-d") : Object.keys(Object.fromEntries(
Object.entries(favorite_emojis).sort(([,a],[,b]) => b-a) Object.entries(favorite_emojis).sort(([,a],[,b]) => b-a)
)).slice(0, 25); )).slice(0, 25);
for (const emoji of favs) for (const emoji of favs)
if(emojiDOMs[emoji] instanceof HTMLElement) if(emojiDOMs[emoji] instanceof HTMLElement)
emojiDOMs[emoji].hidden = false; emojiDOMs[emoji].hidden = false;
return; return;
} }
emojiNewUserDOM.hidden = true; emojiNewUserDOM.hidden = true;
for(const emojiDOM of Object.values(emojiDOMs)) for(const emojiDOM of Object.values(emojiDOMs))
@ -315,53 +315,53 @@ async function start_search() {
} }
/** /**
* Add the selected emoji to the targeted text area * Add the selected emoji to the targeted text area
* @param {Event} event * @param {Event} event
*/ */
function emojiAddToInput(event) function emojiAddToInput(event)
{ {
// This should not happen if used properly but whatever // This should not happen if used properly but whatever
if(!(emojiInputTargetDOM instanceof HTMLTextAreaElement) && !(emojiInputTargetDOM instanceof HTMLInputElement)) if(!(emojiInputTargetDOM instanceof HTMLTextAreaElement) && !(emojiInputTargetDOM instanceof HTMLInputElement))
return; return;
// If a range is selected, setRangeText will overwrite it. Maybe better to ask the r-slured if he really wants this behaviour // If a range is selected, setRangeText will overwrite it. Maybe better to ask the r-slured if he really wants this behaviour
if(emojiInputTargetDOM.selectionStart !== emojiInputTargetDOM.selectionEnd && !confirm("You've selected a range of text.\nThe emoji will overwrite it! Do you want to continue?")) if(emojiInputTargetDOM.selectionStart !== emojiInputTargetDOM.selectionEnd && !confirm("You've selected a range of text.\nThe emoji will overwrite it! Do you want to continue?"))
return; return;
let strToInsert = event.currentTarget.dataset.emojiName; let strToInsert = event.currentTarget.dataset.emojiName;
for(let i = 0; i < emojiSelectPostfixDOMs.length; i++) for(let i = 0; i < emojiSelectPostfixDOMs.length; i++)
if(emojiSelectPostfixDOMs[i].checked) if(emojiSelectPostfixDOMs[i].checked)
strToInsert = strToInsert + emojiSelectPostfixDOMs[i].value; strToInsert = strToInsert + emojiSelectPostfixDOMs[i].value;
for(let i = 0; i < emojiSelectSuffixDOMs.length; i++) for(let i = 0; i < emojiSelectSuffixDOMs.length; i++)
if(emojiSelectSuffixDOMs[i].checked) if(emojiSelectSuffixDOMs[i].checked)
strToInsert = emojiSelectSuffixDOMs[i].value + strToInsert; strToInsert = emojiSelectSuffixDOMs[i].value + strToInsert;
strToInsert = ":" + strToInsert + ":" strToInsert = ":" + strToInsert + ":"
const newPos = emojiInputTargetDOM.selectionStart + strToInsert.length; const newPos = emojiInputTargetDOM.selectionStart + strToInsert.length;
emojiInputTargetDOM.setRangeText(strToInsert); emojiInputTargetDOM.setRangeText(strToInsert);
// Sir, come out and drink your Chromium complaint web // Sir, come out and drink your Chromium complaint web
// I HATE CHROME. I HATE CHROME // I HATE CHROME. I HATE CHROME
if(window.chrome !== undefined) if(window.chrome !== undefined)
setTimeout(function(){ setTimeout(function(){
console.warn("Chrome detected, r-slured mode enabled."); console.warn("Chrome detected, r-slured mode enabled.");
// AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA // AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
// JUST WORK STUPID CHROME PIECE OF SHIT // JUST WORK STUPID CHROME PIECE OF SHIT
emojiInputTargetDOM.focus(); emojiInputTargetDOM.focus();
for(let i = 0; i < 2; i++) for(let i = 0; i < 2; i++)
emojiInputTargetDOM.setSelectionRange(newPos, newPos); emojiInputTargetDOM.setSelectionRange(newPos, newPos);
emojiInputTargetDOM.focus(); emojiInputTargetDOM.focus();
for(let i = 0; i < 2; i++) for(let i = 0; i < 2; i++)
emojiInputTargetDOM.setSelectionRange(newPos, newPos); emojiInputTargetDOM.setSelectionRange(newPos, newPos);
}, 1); }, 1);
else else
emojiInputTargetDOM.setSelectionRange(newPos, newPos); emojiInputTargetDOM.setSelectionRange(newPos, newPos);
// kick-start the preview // kick-start the preview
emojiInputTargetDOM.dispatchEvent(new Event('input')); emojiInputTargetDOM.dispatchEvent(new Event('input'));
@ -374,190 +374,190 @@ function emojiAddToInput(event)
} }
(function() { (function() {
const insertAt = (str, sub, pos) => `${str.slice(0, pos)}${sub}${str.slice(pos)}`; const insertAt = (str, sub, pos) => `${str.slice(0, pos)}${sub}${str.slice(pos)}`;
let emoji_typing_state = false;
function update_ghost_div_textarea(text) let emoji_typing_state = false;
{
let ghostdiv = text.parentNode.querySelector(".ghostdiv");
if (!ghostdiv) return;
ghostdiv.innerText = text.value.substring(0, text.selectionStart); function update_ghost_div_textarea(text)
ghostdiv.innerHTML += "<span></span>"; {
let ghostdiv = text.parentNode.querySelector(".ghostdiv");
if (!ghostdiv) return;
// Now lets get coordinates ghostdiv.innerText = text.value.substring(0, text.selectionStart);
ghostdiv.innerHTML += "<span></span>";
ghostdiv.style.display = "initial";
let end = ghostdiv.querySelector("span");
const carot_coords = end.getBoundingClientRect();
const ghostdiv_coords = ghostdiv.getBoundingClientRect();
ghostdiv.style.display = "none";
return { pos: text.selectionStart, x: carot_coords.x, y: carot_coords.y - ghostdiv_coords.y };
}
// Used for anything where a user is typing, specifically for the emoji modal // Now lets get coordinates
// Just leave it global, I don't care
let speed_carot_modal = document.createElement("div");
speed_carot_modal.id = "speed-carot-modal";
speed_carot_modal.style.position = "absolute";
speed_carot_modal.style.left = "0px";
speed_carot_modal.style.top = "0px";
speed_carot_modal.style.display = "none";
document.body.appendChild(speed_carot_modal);
let e ghostdiv.style.display = "initial";
let end = ghostdiv.querySelector("span");
const carot_coords = end.getBoundingClientRect();
const ghostdiv_coords = ghostdiv.getBoundingClientRect();
ghostdiv.style.display = "none";
return { pos: text.selectionStart, x: carot_coords.x, y: carot_coords.y - ghostdiv_coords.y };
}
let current_word = ""; // Used for anything where a user is typing, specifically for the emoji modal
// Just leave it global, I don't care
let speed_carot_modal = document.createElement("div");
speed_carot_modal.id = "speed-carot-modal";
speed_carot_modal.style.position = "absolute";
speed_carot_modal.style.left = "0px";
speed_carot_modal.style.top = "0px";
speed_carot_modal.style.display = "none";
document.body.appendChild(speed_carot_modal);
let e
let current_word = "";
let selecting; let selecting;
let emoji_index = 0; let emoji_index = 0;
function curr_word_is_emoji() function curr_word_is_emoji()
{ {
return current_word && current_word.charAt(0) == ":" && return current_word && current_word.charAt(0) == ":" &&
current_word.charAt(current_word.length-1) != ":"; current_word.charAt(current_word.length-1) != ":";
} }
function populate_speed_emoji_modal(results, textbox) function populate_speed_emoji_modal(results, textbox)
{ {
selecting = true; selecting = true;
if (!results || results.size === 0) if (!results || results.size === 0)
{ {
speed_carot_modal.style.display = "none"; speed_carot_modal.style.display = "none";
return -1; return -1;
} }
emoji_index = 0; emoji_index = 0;
speed_carot_modal.innerHTML = ""; speed_carot_modal.innerHTML = "";
const MAXXX = 25; const MAXXX = 25;
// Not sure why the results is a Set... but oh well // Not sure why the results is a Set... but oh well
let i = 0; let i = 0;
for (let result of results) for (let result of results)
{ {
if (i++ > MAXXX) return i; if (i++ > MAXXX) return i;
let emoji_option = document.createElement("div"); let emoji_option = document.createElement("div");
emoji_option.className = "speed-modal-option emoji-option " + (i === 1 ? "selected" : ""); emoji_option.className = "speed-modal-option emoji-option " + (i === 1 ? "selected" : "");
emoji_option.tabIndex = 0; emoji_option.tabIndex = 0;
let emoji_option_img = document.createElement("img"); let emoji_option_img = document.createElement("img");
emoji_option_img.className = "speed-modal-image emoji-option-image"; emoji_option_img.className = "speed-modal-image emoji-option-image";
// This is a bit // This is a bit
emoji_option_img.src = `/e/${result}.webp`; emoji_option_img.src = `/e/${result}.webp`;
let emoji_option_text = document.createElement("span"); let emoji_option_text = document.createElement("span");
emoji_option_text.title = result; emoji_option_text.title = result;
emoji_option_text.innerText = result; emoji_option_text.innerText = result;
if (current_word.includes("#")) result = `#${result}` if (current_word.includes("#")) result = `#${result}`
if (current_word.includes("!")) result = `!${result}` if (current_word.includes("!")) result = `!${result}`
emoji_option.onclick = (e) => { emoji_option.onclick = (e) => {
selecting = false; selecting = false;
speed_carot_modal.style.display = "none"; speed_carot_modal.style.display = "none";
textbox.value = textbox.value.replace(new RegExp(current_word+"(?=\\s|$)", "g"), `:${result}:`) textbox.value = textbox.value.replace(new RegExp(current_word+"(?=\\s|$)", "g"), `:${result}:`)
markdown(textbox) markdown(textbox)
}; };
// Pack // Pack
emoji_option.appendChild(emoji_option_img); emoji_option.appendChild(emoji_option_img);
emoji_option.appendChild(emoji_option_text); emoji_option.appendChild(emoji_option_text);
speed_carot_modal.appendChild(emoji_option); speed_carot_modal.appendChild(emoji_option);
} }
if (i === 0) speed_carot_modal.style.display = "none"; if (i === 0) speed_carot_modal.style.display = "none";
else speed_carot_modal.style.display = "initial"; else speed_carot_modal.style.display = "initial";
return i; return i;
} }
function update_speed_emoji_modal(event) function update_speed_emoji_modal(event)
{ {
const box_coords = update_ghost_div_textarea(event.target); const box_coords = update_ghost_div_textarea(event.target);
let text = event.target.value; let text = event.target.value;
// Unused, but left incase anyone wants to use this more efficient method for emojos // Unused, but left incase anyone wants to use this more efficient method for emojos
switch (event.data) switch (event.data)
{ {
case ':': case ':':
emoji_typing_state = true; emoji_typing_state = true;
break; break;
case ' ': case ' ':
emoji_typing_state = false; emoji_typing_state = false;
break; break;
default: default:
break; break;
} }
// Get current word at string, such as ":marse" or "word" // Get current word at string, such as ":marse" or "word"
let coords = text.indexOf(' ',box_coords.pos); let coords = text.indexOf(' ',box_coords.pos);
current_word = /\S+$/.exec(text.slice(0, coords === -1 ? text.length : coords)); current_word = /\S+$/.exec(text.slice(0, coords === -1 ? text.length : coords));
if (current_word) current_word = current_word.toString(); if (current_word) current_word = current_word.toString();
/* We could also check emoji_typing_state here, which is less accurate but more efficient. I've /* We could also check emoji_typing_state here, which is less accurate but more efficient. I've
* kept it unless someone wants to provide an option to toggle it for performance */ * kept it unless someone wants to provide an option to toggle it for performance */
if (curr_word_is_emoji() && current_word != ":") if (curr_word_is_emoji() && current_word != ":")
{ {
loadEmojis(null); loadEmojis(null);
let modal_pos = event.target.getBoundingClientRect(); let modal_pos = event.target.getBoundingClientRect();
modal_pos.x += window.scrollX; modal_pos.x += window.scrollX;
modal_pos.y += window.scrollY; modal_pos.y += window.scrollY;
speed_carot_modal.style.display = "initial"; speed_carot_modal.style.display = "initial";
speed_carot_modal.style.left = box_coords.x - 35 + "px"; speed_carot_modal.style.left = box_coords.x - 35 + "px";
speed_carot_modal.style.top = modal_pos.y + box_coords.y + 14 + "px"; speed_carot_modal.style.top = modal_pos.y + box_coords.y + 14 + "px";
// Do the search (and do something with it)
populate_speed_emoji_modal(emojisSearchDictionary.searchFor(current_word.substr(1).replace(/#/g, "").replace(/!/g, "")), event.target);
} // Do the search (and do something with it)
else { populate_speed_emoji_modal(emojisSearchDictionary.searchFor(current_word.substr(1).replace(/#/g, "").replace(/!/g, "")), event.target);
speed_carot_modal.style.display = "none";
}
}
function speed_carot_navigate(e) }
{ else {
speed_carot_modal.style.display = "none";
}
}
function speed_carot_navigate(e)
{
if (!selecting) return; if (!selecting) return;
let select_items = speed_carot_modal.querySelectorAll(".speed-modal-option"); let select_items = speed_carot_modal.querySelectorAll(".speed-modal-option");
if (!select_items || !curr_word_is_emoji()) return false; if (!select_items || !curr_word_is_emoji()) return false;
// Up or down arrow or enter // Up or down arrow or enter
if (e.keyCode == 38 || e.keyCode == 40 || e.keyCode == 13) if (e.keyCode == 38 || e.keyCode == 40 || e.keyCode == 13)
{ {
if (emoji_index > select_items.length) if (emoji_index > select_items.length)
emoji_index = select_items; emoji_index = select_items;
select_items[emoji_index].classList.remove("selected");
switch (e.keyCode)
{
case 38: // Up arrow
if (emoji_index)
emoji_index--;
break;
case 40: // Down arrow
if (emoji_index < select_items.length-1) emoji_index++;
break;
case 13: select_items[emoji_index].classList.remove("selected");
select_items[emoji_index].click(); switch (e.keyCode)
{
default: case 38: // Up arrow
break; if (emoji_index)
} emoji_index--;
break;
select_items[emoji_index].classList.add("selected"); case 40: // Down arrow
e.preventDefault(); if (emoji_index < select_items.length-1) emoji_index++;
} break;
}
// Let's get it running now case 13:
let forms = document.querySelectorAll("textarea, .allow-emojis"); select_items[emoji_index].click();
forms.forEach(i => {
let pseudo_div = document.createElement("div"); default:
pseudo_div.className = "ghostdiv"; break;
pseudo_div.style.display = "none"; }
i.after(pseudo_div);
i.addEventListener('input', update_speed_emoji_modal, false); select_items[emoji_index].classList.add("selected");
i.addEventListener('keydown', speed_carot_navigate, false); e.preventDefault();
}); }
}
// Let's get it running now
let forms = document.querySelectorAll("textarea, .allow-emojis");
forms.forEach(i => {
let pseudo_div = document.createElement("div");
pseudo_div.className = "ghostdiv";
pseudo_div.style.display = "none";
i.after(pseudo_div);
i.addEventListener('input', update_speed_emoji_modal, false);
i.addEventListener('keydown', speed_carot_navigate, false);
});
})(); })();
@ -569,8 +569,8 @@ function loadEmojis(inputTargetIDName)
emojiRequest.send(); emojiRequest.send();
} }
if (inputTargetIDName) if (inputTargetIDName)
emojiInputTargetDOM = document.getElementById(inputTargetIDName); emojiInputTargetDOM = document.getElementById(inputTargetIDName);
} }
document.getElementById('emojiModal').addEventListener('shown.bs.modal', function () { document.getElementById('emojiModal').addEventListener('shown.bs.modal', function () {

View File

@ -12,27 +12,27 @@ setTimeout(() => {
var ypos = 95 var ypos = 95
firework.style.top=ypos+"%" firework.style.top=ypos+"%"
firework.style.left=xpos+"%" firework.style.left=xpos+"%"
firework.style.display="inline-block" firework.style.display="inline-block"
var hue = Math.floor(Math.random()*360)+1 var hue = Math.floor(Math.random()*360)+1
firework.style.filter="hue-rotate("+hue+"deg)" firework.style.filter="hue-rotate("+hue+"deg)"
var id = null var id = null
var height = Math.floor(Math.random()*60)+15 var height = Math.floor(Math.random()*60)+15
clearInterval(id); clearInterval(id);
id = setInterval(frame, 20); id = setInterval(frame, 20);
var vnum = Math.floor(Math.random()*1000) var vnum = Math.floor(Math.random()*1000)
function frame() { function frame() {
if (ypos <= height) { if (ypos <= height) {
clearInterval(id); clearInterval(id);
firework.firstElementChild.src = "/i/firework-explosion.webp?v="+vnum firework.firstElementChild.src = "/i/firework-explosion.webp?v="+vnum
} else { } else {
ypos--; ypos--;
firework.style.top=ypos+"%" firework.style.top=ypos+"%"
} }
} }
}, 5000) }, 5000)
}, timeout) }, timeout)
} }

View File

@ -1,5 +1,5 @@
function removeFollower(event, username) { function removeFollower(event, username) {
post_toast(event.target,'/remove_follow/' + username); post_toast(event.target,'/remove_follow/' + username);
let table = document.getElementById("followers-table"); let table = document.getElementById("followers-table");
table.removeChild(event.target.parentElement.parentElement); table.removeChild(event.target.parentElement.parentElement);
} }

View File

@ -1,5 +1,5 @@
function removeFollowing(event, username) { function removeFollowing(event, username) {
post_toast(event.target,'/unfollow/' + username); post_toast(event.target,'/unfollow/' + username);
let table = document.getElementById("followers-table"); let table = document.getElementById("followers-table");
table.removeChild(event.target.parentElement.parentElement); table.removeChild(event.target.parentElement.parentElement);
} }

View File

@ -1,64 +1,9 @@
class LiteYTEmbed extends HTMLElement { class LiteYTEmbed extends HTMLElement{connectedCallback(){this.videoId=this.getAttribute('videoid');let playBtnEl=this.querySelector('.lty-playbtn');this.playLabel=(playBtnEl&&playBtnEl.textContent.trim())||this.getAttribute('playlabel')||'Play';if(!this.style.backgroundImage){this.style.backgroundImage=`url("https://i.ytimg.com/vi/${this.videoId}/hqdefault.jpg")`}
connectedCallback() { if(!playBtnEl){playBtnEl=document.createElement('button');playBtnEl.type='button';playBtnEl.classList.add('lty-playbtn');this.append(playBtnEl)}
this.videoId = this.getAttribute('videoid'); if(!playBtnEl.textContent){const playBtnLabelEl=document.createElement('span');playBtnLabelEl.className='lyt-visually-hidden';playBtnLabelEl.textContent=this.playLabel;playBtnEl.append(playBtnLabelEl)}
this.addEventListener('pointerover',LiteYTEmbed.warmConnections,{once:!0});this.addEventListener('click',this.addIframe)}
let playBtnEl = this.querySelector('.lty-playbtn'); static addPrefetch(kind,url,as){const linkEl=document.createElement('link');linkEl.rel=kind;linkEl.href=url;if(as){linkEl.as=as}
this.playLabel = (playBtnEl && playBtnEl.textContent.trim()) || this.getAttribute('playlabel') || 'Play'; document.head.append(linkEl)}
if (!this.style.backgroundImage) { static warmConnections(){if(LiteYTEmbed.preconnected)return;LiteYTEmbed.addPrefetch('preconnect','https://www.youtube-nocookie.com');LiteYTEmbed.addPrefetch('preconnect','https://www.google.com');LiteYTEmbed.addPrefetch('preconnect','https://googleads.g.doubleclick.net');LiteYTEmbed.addPrefetch('preconnect','https://static.doubleclick.net');LiteYTEmbed.preconnected=!0}
this.style.backgroundImage = `url("https://i.ytimg.com/vi/${this.videoId}/hqdefault.jpg")`; addIframe(){if(this.classList.contains('lyt-activated'))return;this.classList.add('lyt-activated');const params=new URLSearchParams(this.getAttribute('params')||[]);params.append('autoplay','1');const iframeEl=document.createElement('iframe');iframeEl.width=560;iframeEl.height=315;iframeEl.sandbox='allow-scripts allow-same-origin allow-popups';iframeEl.title=this.playLabel;iframeEl.allow='accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture';iframeEl.allowFullscreen=!0;iframeEl.src=`https://www.youtube-nocookie.com/embed/${encodeURIComponent(this.videoId)}?${params.toString()}`;this.append(iframeEl);iframeEl.focus()}}
} customElements.define('lite-youtube',LiteYTEmbed)
if (!playBtnEl) {
playBtnEl = document.createElement('button');
playBtnEl.type = 'button';
playBtnEl.classList.add('lty-playbtn');
this.append(playBtnEl);
}
if (!playBtnEl.textContent) {
const playBtnLabelEl = document.createElement('span');
playBtnLabelEl.className = 'lyt-visually-hidden';
playBtnLabelEl.textContent = this.playLabel;
playBtnEl.append(playBtnLabelEl);
}
this.addEventListener('pointerover', LiteYTEmbed.warmConnections, {once: true});
this.addEventListener('click', this.addIframe);
}
static addPrefetch(kind, url, as) {
const linkEl = document.createElement('link');
linkEl.rel = kind;
linkEl.href = url;
if (as) {
linkEl.as = as;
}
document.head.append(linkEl);
}
static warmConnections() {
if (LiteYTEmbed.preconnected) return;
LiteYTEmbed.addPrefetch('preconnect', 'https://www.youtube-nocookie.com');
LiteYTEmbed.addPrefetch('preconnect', 'https://www.google.com');
LiteYTEmbed.addPrefetch('preconnect', 'https://googleads.g.doubleclick.net');
LiteYTEmbed.addPrefetch('preconnect', 'https://static.doubleclick.net');
LiteYTEmbed.preconnected = true;
}
addIframe() {
if (this.classList.contains('lyt-activated')) return;
this.classList.add('lyt-activated');
const params = new URLSearchParams(this.getAttribute('params') || []);
params.append('autoplay', '1');
const iframeEl = document.createElement('iframe');
iframeEl.width = 560;
iframeEl.height = 315;
iframeEl.sandbox = 'allow-scripts allow-same-origin allow-popups';
iframeEl.title = this.playLabel;
iframeEl.allow = 'accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture';
iframeEl.allowFullscreen = true;
iframeEl.src = `https://www.youtube-nocookie.com/embed/${encodeURIComponent(this.videoId)}?${params.toString()}`;
this.append(iframeEl);
iframeEl.focus();
}
}
customElements.define('lite-youtube', LiteYTEmbed);

View File

@ -1,200 +1,200 @@
let purchaseQuantity = 1; let purchaseQuantity = 1;
var lotteryOnReady = function () { var lotteryOnReady = function () {
checkLotteryStats(); checkLotteryStats();
// Show ticket being pulled. // Show ticket being pulled.
const ticketPulled = document.getElementById("lotteryTicketPulled"); const ticketPulled = document.getElementById("lotteryTicketPulled");
const purchaseTicket = document.getElementById("purchaseTicket"); const purchaseTicket = document.getElementById("purchaseTicket");
purchaseTicket.addEventListener("click", () => { purchaseTicket.addEventListener("click", () => {
ticketPulled.style.display = "block"; ticketPulled.style.display = "block";
setTimeout(() => { setTimeout(() => {
ticketPulled.style.display = "none"; ticketPulled.style.display = "none";
ticketPulled.src = ticketPulled.src =
"/i/rDrama/lottery_active.webp?v=2000&t=" + "/i/rDrama/lottery_active.webp?v=2000&t=" +
new Date().getTime(); new Date().getTime();
purchaseTicket.disabled = false; purchaseTicket.disabled = false;
}, 1780); }, 1780);
}); });
// Update the quantity field // Update the quantity field
const purchaseQuantityField = document.getElementById( const purchaseQuantityField = document.getElementById(
"totalQuantityOfTickets" "totalQuantityOfTickets"
); );
const purchaseTotalCostField = document.getElementById("totalCostOfTickets"); const purchaseTotalCostField = document.getElementById("totalCostOfTickets");
const ticketPurchaseQuantityInput = document.getElementById( const ticketPurchaseQuantityInput = document.getElementById(
"ticketPurchaseQuantity" "ticketPurchaseQuantity"
); );
ticketPurchaseQuantityInput.addEventListener("change", (event) => { ticketPurchaseQuantityInput.addEventListener("change", (event) => {
const value = Math.max(1, parseInt(event.target.value)) const value = Math.max(1, parseInt(event.target.value))
purchaseQuantity = value purchaseQuantity = value
purchaseQuantityField.innerText = value purchaseQuantityField.innerText = value
purchaseTotalCostField.innerText = value * 12 purchaseTotalCostField.innerText = value * 12
}); });
}; };
if ( if (
document.readyState === "complete" || document.readyState === "complete" ||
(document.readyState !== "loading" && !document.documentElement.doScroll) (document.readyState !== "loading" && !document.documentElement.doScroll)
) { ) {
lotteryOnReady(); lotteryOnReady();
} else { } else {
document.addEventListener("DOMContentLoaded", lotteryOnReady); document.addEventListener("DOMContentLoaded", lotteryOnReady);
} }
function purchaseLotteryTicket() { function purchaseLotteryTicket() {
return handleLotteryRequest("buy", "POST"); return handleLotteryRequest("buy", "POST");
} }
function checkLotteryStats() { function checkLotteryStats() {
return handleLotteryRequest("active", "GET"); return handleLotteryRequest("active", "GET");
} }
// Admin // Admin
function ensureIntent() { function ensureIntent() {
return window.confirm("Are you sure you want to end the current lottery?"); return window.confirm("Are you sure you want to end the current lottery?");
} }
function startLotterySession() { function startLotterySession() {
checkLotteryStats(); checkLotteryStats();
if (ensureIntent()) { if (ensureIntent()) {
return handleLotteryRequest("start", "POST", () => return handleLotteryRequest("start", "POST", () =>
window.location.reload() window.location.reload()
); );
} }
} }
function endLotterySession() { function endLotterySession() {
checkLotteryStats(); checkLotteryStats();
if (ensureIntent()) { if (ensureIntent()) {
return handleLotteryRequest("end", "POST", () => window.location.reload()); return handleLotteryRequest("end", "POST", () => window.location.reload());
} }
} }
// Composed // Composed
function handleLotteryRequest(uri, method, callback = () => {}) { function handleLotteryRequest(uri, method, callback = () => {}) {
const xhr = new XMLHttpRequest(); const xhr = new XMLHttpRequest();
const url = `/lottery/${uri}`; const url = `/lottery/${uri}`;
xhr.open(method, url); xhr.open(method, url);
xhr.onload = handleLotteryResponse.bind(null, xhr, method, callback); xhr.onload = handleLotteryResponse.bind(null, xhr, method, callback);
const form = new FormData(); const form = new FormData();
form.append("formkey", formkey()); form.append("formkey", formkey());
form.append("quantity", purchaseQuantity) form.append("quantity", purchaseQuantity)
xhr.send(form); xhr.send(form);
} }
function handleLotteryResponse(xhr, method, callback) { function handleLotteryResponse(xhr, method, callback) {
let response; let response;
try { try {
response = JSON.parse(xhr.response); response = JSON.parse(xhr.response);
} catch (error) { } catch (error) {
console.error(error); console.error(error);
} }
if (method === "POST") { if (method === "POST") {
const succeeded = const succeeded =
xhr.status >= 200 && xhr.status < 300 && response && response.message; xhr.status >= 200 && xhr.status < 300 && response && response.message;
if (succeeded) { if (succeeded) {
// Display success. // Display success.
const toast = document.getElementById("lottery-post-success"); const toast = document.getElementById("lottery-post-success");
const toastMessage = document.getElementById("lottery-post-success-text"); const toastMessage = document.getElementById("lottery-post-success-text");
toastMessage.innerText = response.message; toastMessage.innerText = response.message;
bootstrap.Toast.getOrCreateInstance(toast).show(); bootstrap.Toast.getOrCreateInstance(toast).show();
callback(); callback();
} else { } else {
// Display error. // Display error.
const toast = document.getElementById("lottery-post-error"); const toast = document.getElementById("lottery-post-error");
const toastMessage = document.getElementById("lottery-post-error-text"); const toastMessage = document.getElementById("lottery-post-error-text");
toastMessage.innerText = toastMessage.innerText =
(response && response.error) || "Error, please try again later."; (response && response.error) || "Error, please try again later.";
bootstrap.Toast.getOrCreateInstance(toast).show(); bootstrap.Toast.getOrCreateInstance(toast).show();
}
} }
}
if (response && response.stats) { if (response && response.stats) {
lastStats = response.stats; lastStats = response.stats;
const { user, lottery, participants } = response.stats; const { user, lottery, participants } = response.stats;
const [ const [
prizeImage, prizeImage,
prizeField,
timeLeftField,
ticketsSoldThisSessionField,
participantsThisSessionField,
ticketsHeldCurrentField,
ticketsHeldTotalField,
winningsField,
purchaseTicketButton,
] = [
"prize-image",
"prize",
"timeLeft",
"ticketsSoldThisSession",
"participantsThisSession",
"ticketsHeldCurrent",
"ticketsHeldTotal",
"winnings",
"purchaseTicket",
].map((id) => document.getElementById(id));
if (lottery) {
prizeImage.style.display = "inline";
prizeField.textContent = lottery.prize;
timeLeftField.textContent = formatTimeLeft(lottery.timeLeft);
if (participants) {
participantsThisSessionField.textContent = participants;
}
ticketsSoldThisSessionField.textContent = lottery.ticketsSoldThisSession;
ticketsHeldCurrentField.textContent = user.ticketsHeld.current;
} else {
prizeImage.style.display = "none";
[
prizeField, prizeField,
timeLeftField, timeLeftField,
ticketsSoldThisSessionField, ticketsSoldThisSessionField,
participantsThisSessionField, participantsThisSessionField,
ticketsHeldCurrentField, ticketsHeldCurrentField,
].forEach((e) => (e.textContent = "-")); ticketsHeldTotalField,
purchaseTicketButton.disabled = true; winningsField,
purchaseTicketButton,
] = [
"prize-image",
"prize",
"timeLeft",
"ticketsSoldThisSession",
"participantsThisSession",
"ticketsHeldCurrent",
"ticketsHeldTotal",
"winnings",
"purchaseTicket",
].map((id) => document.getElementById(id));
if (lottery) {
prizeImage.style.display = "inline";
prizeField.textContent = lottery.prize;
timeLeftField.textContent = formatTimeLeft(lottery.timeLeft);
if (participants) {
participantsThisSessionField.textContent = participants;
}
ticketsSoldThisSessionField.textContent = lottery.ticketsSoldThisSession;
ticketsHeldCurrentField.textContent = user.ticketsHeld.current;
} else {
prizeImage.style.display = "none";
[
prizeField,
timeLeftField,
ticketsSoldThisSessionField,
participantsThisSessionField,
ticketsHeldCurrentField,
].forEach((e) => (e.textContent = "-"));
purchaseTicketButton.disabled = true;
} }
ticketsHeldTotalField.textContent = user.ticketsHeld.total; ticketsHeldTotalField.textContent = user.ticketsHeld.total;
winningsField.textContent = user.winnings; winningsField.textContent = user.winnings;
const [endButton, startButton] = [ const [endButton, startButton] = [
"endLotterySession", "endLotterySession",
"startLotterySession", "startLotterySession",
].map((id) => document.getElementById(id)); ].map((id) => document.getElementById(id));
if (response.stats.lottery) { if (response.stats.lottery) {
endButton.style.display = "block"; endButton.style.display = "block";
startButton.style.display = "none"; startButton.style.display = "none";
} else { } else {
endButton.style.display = "none"; endButton.style.display = "none";
startButton.style.display = "block"; startButton.style.display = "block";
}
} }
}
} }
function formatTimeLeft(secondsLeft) { function formatTimeLeft(secondsLeft) {
const minutesLeft = Math.floor(secondsLeft / 60); const minutesLeft = Math.floor(secondsLeft / 60);
const seconds = secondsLeft % 60; const seconds = secondsLeft % 60;
const minutes = minutesLeft % 60; const minutes = minutesLeft % 60;
const hours = Math.floor(minutesLeft / 60); const hours = Math.floor(minutesLeft / 60);
return `${hours}h, ${minutes}m, ${seconds}s`; return `${hours}h, ${minutes}m, ${seconds}s`;
} }

View File

@ -10,12 +10,12 @@ marked.use({
return match != null ? match.index : -1; return match != null ? match.index : -1;
}, },
tokenizer: function(src) { tokenizer: function(src) {
const rule = /^@[a-zA-Z0-9_\-]+/; const rule = /^@[a-zA-Z0-9_\-]+/;
const match = rule.exec(src); const match = rule.exec(src);
if(match){ if(match){
return { return {
type: 'mention', type: 'mention',
raw: match[0], raw: match[0],
text: match[0].trim().slice(1), text: match[0].trim().slice(1),
tokens: [] tokens: []
}; };
@ -93,7 +93,7 @@ function markdown(t) {
input += `<div class="custom-control"><input type="radio" name="choice" class="custom-control-input" id="choice-${i}"><label class="custom-control-label" for="choice-${i}">${option2} - <a>0 votes</a></label></div>`; input += `<div class="custom-control"><input type="radio" name="choice" class="custom-control-input" id="choice-${i}"><label class="custom-control-label" for="choice-${i}">${option2} - <a>0 votes</a></label></div>`;
} }
} }
input = marked(input) input = marked(input)
input = input.replace(/\n\n/g, '<br>') input = input.replace(/\n\n/g, '<br>')

View File

@ -13,7 +13,7 @@ window.onscroll = function () {
if (bottomBar != null) { if (bottomBar != null) {
if (prevScrollpos > currentScrollPos && (window.innerHeight + currentScrollPos) < (document.body.offsetHeight - 65)) { if (prevScrollpos > currentScrollPos && (window.innerHeight + currentScrollPos) < (document.body.offsetHeight - 65)) {
bottomBar.style.bottom = "0px"; bottomBar.style.bottom = "0px";
} }
else if (currentScrollPos <= 125 && (window.innerHeight + currentScrollPos) < (document.body.offsetHeight - 65)) { else if (currentScrollPos <= 125 && (window.innerHeight + currentScrollPos) < (document.body.offsetHeight - 65)) {
bottomBar.style.bottom = "0px"; bottomBar.style.bottom = "0px";
} }
@ -29,7 +29,7 @@ window.onscroll = function () {
if (prevScrollpos > currentScrollPos) { if (prevScrollpos > currentScrollPos) {
topBar.style.top = "48px"; topBar.style.top = "48px";
navbar.classList.remove("shadow"); navbar.classList.remove("shadow");
} }
else if (currentScrollPos <= 125) { else if (currentScrollPos <= 125) {
topBar.style.top = "48px"; topBar.style.top = "48px";
navbar.classList.remove("shadow"); navbar.classList.remove("shadow");

File diff suppressed because one or more lines are too long

View File

@ -1,7 +1,7 @@
var isleft = true var isleft = true
setInterval(() => { setInterval(() => {
let ricardo1 = document.getElementById("ricardo1") let ricardo1 = document.getElementById("ricardo1")
var height = Math.floor(Math.random()*60)+10 var height = Math.floor(Math.random()*60)+10
if (ricardo1) { if (ricardo1) {
ricardo1.firstElementChild.src = "" ricardo1.firstElementChild.src = ""

View File

@ -1,8 +1,8 @@
function addParam(e) { function addParam(e) {
e = e || window.event; e = e || window.event;
let paramExample = e.target.innerText; let paramExample = e.target.innerText;
let param = paramExample.split(":")[0]; let param = paramExample.split(":")[0];
let searchInput = document.querySelector("#large_searchbar input"); let searchInput = document.querySelector("#large_searchbar input");
searchInput.value = `${searchInput.value} ${param}:`; searchInput.value = `${searchInput.value} ${param}:`;
searchInput.focus(); searchInput.focus();
} }

View File

@ -25,7 +25,7 @@ function updatebgselection(){
}, },
{ {
folder: "fantasy", folder: "fantasy",
backgrounds: backgrounds:
[ [
"1.webp", "1.webp",
"2.webp", "2.webp",
@ -37,7 +37,7 @@ function updatebgselection(){
}, },
{ {
folder: "solarpunk", folder: "solarpunk",
backgrounds: backgrounds:
[ [
"1.webp", "1.webp",
"2.webp", "2.webp",

View File

@ -1,26 +1,26 @@
let sortAscending = {}; let sortAscending = {};
function sort_table(n) { function sort_table(n) {
const table = this.event.target.parentElement.parentElement.parentElement const table = this.event.target.parentElement.parentElement.parentElement
const rows = table.rows; const rows = table.rows;
let items = []; let items = [];
for (let i = 1; i < rows.length; i++) { for (let i = 1; i < rows.length; i++) {
const ele = rows[i]; const ele = rows[i];
let x = rows[i].getElementsByTagName("TD")[n]; let x = rows[i].getElementsByTagName("TD")[n];
x = x.getElementsByTagName('a')[0] || x; x = x.getElementsByTagName('a')[0] || x;
const attr = x.dataset.time ? parseInt(x.dataset.time) : parseInt(x.innerHTML); const attr = x.dataset.time ? parseInt(x.dataset.time) : parseInt(x.innerHTML);
console.log(attr); console.log(attr);
items.push({ ele, attr }); items.push({ ele, attr });
} }
if (sortAscending[n]) { if (sortAscending[n]) {
items.sort((a, b) => a.attr - b.attr); items.sort((a, b) => a.attr - b.attr);
sortAscending[n] = false; sortAscending[n] = false;
} else { } else {
items.sort((a, b) => b.attr - a.attr); items.sort((a, b) => b.attr - a.attr);
sortAscending[n] = true; sortAscending[n] = true;
} }
for (let i = items.length - 1; i--;) { for (let i = items.length - 1; i--;) {
items[i].ele.parentNode.insertBefore(items[i].ele, items[i + 1].ele); items[i].ele.parentNode.insertBefore(items[i].ele, items[i + 1].ele);
} }
} }

View File

@ -96,7 +96,7 @@ document.getElementById('file-upload').addEventListener('change', function(){
{ {
var fileReader = new FileReader(); var fileReader = new FileReader();
fileReader.readAsDataURL(f.files[0]); fileReader.readAsDataURL(f.files[0]);
fileReader.addEventListener("load", function () {document.getElementById('image-preview').setAttribute('src', this.result);}); fileReader.addEventListener("load", function () {document.getElementById('image-preview').setAttribute('src', this.result);});
} }
checkForRequired(); checkForRequired();
}) })
@ -150,7 +150,7 @@ function draft(t) {
followers.disabled = true; followers.disabled = true;
} else { } else {
followers.disabled = false; followers.disabled = false;
} }
} }
function checkRepost() { function checkRepost() {
@ -168,7 +168,7 @@ function checkRepost() {
xhr.onload=function(){ xhr.onload=function(){
try {data = JSON.parse(xhr.response)} try {data = JSON.parse(xhr.response)}
catch(e) {console.log(e)} catch(e) {console.log(e)}
if (data && data["permalink"]) { if (data && data["permalink"]) {
const permalink = data["permalink"] const permalink = data["permalink"]
if (permalink) { if (permalink) {
@ -196,7 +196,7 @@ function updateCategories() {
document.getElementById("submit-categories").innerHTML = ''; document.getElementById("submit-categories").innerHTML = '';
data[sub].forEach(function (c) { data[sub].forEach(function (c) {
document.getElementById("submit-categories").innerHTML += document.getElementById("submit-categories").innerHTML +=
`<input type="radio" id="category-${c.id}" name="category" value="${c.id}">` + `<input type="radio" id="category-${c.id}" name="category" value="${c.id}">` +
`<label for="category-${c.id}" class="post--category-tag" ` + `<label for="category-${c.id}" class="post--category-tag" ` +
`style="color:${c.color_text}; background-color:${c.color_bg};">` + `style="color:${c.color_text}; background-color:${c.color_bg};">` +
@ -207,12 +207,12 @@ function updateCategories() {
} }
document.addEventListener('keydown', (e) => { document.addEventListener('keydown', (e) => {
if(!((e.ctrlKey || e.metaKey) && e.key === "Enter")) if(!((e.ctrlKey || e.metaKey) && e.key === "Enter"))
return; return;
const submitButton = document.getElementById('create_button') const submitButton = document.getElementById('create_button')
submitButton.click(); submitButton.click();
}); });
checkRepost(); checkRepost();

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -14,7 +14,7 @@ if (u_username)
audio.play(); audio.play();
document.getElementById('userpage').addEventListener('click', () => { document.getElementById('userpage').addEventListener('click', () => {
if (audio.paused) audio.play(); if (audio.paused) audio.play();
}, {once : true}); }, {once : true});
} }
else else
@ -28,7 +28,7 @@ else
let audio = new Audio(`/@${v_username}/song`); let audio = new Audio(`/@${v_username}/song`);
audio.loop=true; audio.loop=true;
function toggle() { function toggle() {
if (audio.paused) if (audio.paused)
{ {
@ -41,12 +41,12 @@ else
localStorage.setItem("paused", "1") localStorage.setItem("paused", "1")
} }
} }
if (!paused) if (!paused)
{ {
audio.play(); audio.play();
window.addEventListener('click', () => { window.addEventListener('click', () => {
if (audio.paused) audio.play(); if (audio.paused) audio.play();
}, {once : true}); }, {once : true});
} }
} }
@ -54,7 +54,7 @@ else
function badge_timestamp(t) { function badge_timestamp(t) {
const date = new Date(t.dataset.until*1000); const date = new Date(t.dataset.until*1000);
const text = t.getAttribute("data-bs-original-title") const text = t.getAttribute("data-bs-original-title")
t.setAttribute("data-bs-original-title", `${text} ${date.toString()}`); t.setAttribute("data-bs-original-title", `${text} ${date.toString()}`);
t.removeAttribute("onmouseover") t.removeAttribute("onmouseover")
} }

View File

@ -92,7 +92,7 @@ function transferCoins(mobile=false) {
let transferred = amount - Math.ceil(amount*TRANSFER_TAX); let transferred = amount - Math.ceil(amount*TRANSFER_TAX);
let username = document.getElementById('username').innerHTML; let username = document.getElementById('username').innerHTML;
post_toast_callback(`/@${username}/transfer_coins`, post_toast_callback(`/@${username}/transfer_coins`,
{ {
"amount": document.getElementById(mobile ? "coin-transfer-amount-mobile" : "coin-transfer-amount").value, "amount": document.getElementById(mobile ? "coin-transfer-amount-mobile" : "coin-transfer-amount").value,
"reason": document.getElementById(mobile ? "coin-transfer-reason-mobile" : "coin-transfer-reason").value "reason": document.getElementById(mobile ? "coin-transfer-reason-mobile" : "coin-transfer-reason").value
@ -141,7 +141,7 @@ function submitFormAjax(e) {
document.getElementById('message-mobile').classList.add('d-none'); document.getElementById('message-mobile').classList.add('d-none');
document.getElementById('message-preview').classList.add('d-none'); document.getElementById('message-preview').classList.add('d-none');
document.getElementById('message-preview-mobile').classList.add('d-none'); document.getElementById('message-preview-mobile').classList.add('d-none');
const form = e.target; const form = e.target;
const xhr = new XMLHttpRequest(); const xhr = new XMLHttpRequest();
e.preventDefault(); e.preventDefault();

View File

@ -26,7 +26,7 @@ class Badge(Base):
__tablename__ = "badges" __tablename__ = "badges"
user_id = Column(Integer, ForeignKey('users.id'), primary_key=True) user_id = Column(Integer, ForeignKey('users.id'), primary_key=True)
badge_id = Column(Integer, ForeignKey('badge_defs.id'), primary_key=True) badge_id = Column(Integer, ForeignKey('badge_defs.id'), primary_key=True)
description = Column(String) description = Column(String)
url = Column(String) url = Column(String)
created_utc = Column(Integer) created_utc = Column(Integer)

View File

@ -6,22 +6,22 @@ import time
class Casino_Game(Base): class Casino_Game(Base):
__tablename__ = "casino_games" __tablename__ = "casino_games"
id = Column(Integer, primary_key=True) id = Column(Integer, primary_key=True)
user_id = Column(Integer, ForeignKey("users.id")) user_id = Column(Integer, ForeignKey("users.id"))
created_utc = Column(Integer) created_utc = Column(Integer)
active = Column(Boolean, default=True) active = Column(Boolean, default=True)
currency = Column(String) currency = Column(String)
wager = Column(Integer) wager = Column(Integer)
winnings = Column(Integer) winnings = Column(Integer)
kind = Column(String) kind = Column(String)
game_state = Column(JSON) game_state = Column(JSON)
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
if "created_utc" not in kwargs: if "created_utc" not in kwargs:
kwargs["created_utc"] = int(time.time()) kwargs["created_utc"] = int(time.time())
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
def __repr__(self): def __repr__(self):
return f"<CasinoGame(id={self.id})>" return f"<CasinoGame(id={self.id})>"

View File

@ -278,7 +278,7 @@ class Comment(Base):
@property @property
def json(self): def json(self):
if self.is_banned: if self.is_banned:
data= {'is_banned': True, data = {'is_banned': True,
'ban_reason': self.ban_reason, 'ban_reason': self.ban_reason,
'id': self.id, 'id': self.id,
'post': self.post.id if self.post else 0, 'post': self.post.id if self.post else 0,
@ -286,7 +286,7 @@ class Comment(Base):
'parent': self.parent_fullname 'parent': self.parent_fullname
} }
elif self.deleted_utc: elif self.deleted_utc:
data= {'deleted_utc': self.deleted_utc, data = {'deleted_utc': self.deleted_utc,
'id': self.id, 'id': self.id,
'post': self.post.id if self.post else 0, 'post': self.post.id if self.post else 0,
'level': self.level, 'level': self.level,
@ -296,7 +296,7 @@ class Comment(Base):
flags = {} flags = {}
for f in self.flags: flags[f.user.username] = f.reason for f in self.flags: flags[f.user.username] = f.reason
data= { data = {
'id': self.id, 'id': self.id,
'level': self.level, 'level': self.level,
'author_name': self.author_name, 'author_name': self.author_name,

View File

@ -78,7 +78,7 @@ class ModAction(Base):
@lazy @lazy
def string(self): def string(self):
output = ACTIONTYPES[self.kind]["str"].format(self=self, cc=CC_TITLE) output = ACTIONTYPES[self.kind]["str"].format(self=self, cc=CC_TITLE)
if self.note: output += f" <i>({self.note})</i>" if self.note: output += f" <i>({self.note})</i>"
@ -106,7 +106,7 @@ class ModAction(Base):
@property @property
@lazy @lazy
def permalink(self): def permalink(self):
return f"{SITE_FULL}/log/{self.id}" return f"{SITE_FULL}/log/{self.id}"
ACTIONTYPES = { ACTIONTYPES = {
'agendaposter': { 'agendaposter': {

View File

@ -14,243 +14,243 @@ maximum_bet = INFINITY
def build_game(gambler, currency_kind, wager): def build_game(gambler, currency_kind, wager):
casino_game = Casino_Game() casino_game = Casino_Game()
casino_game.user_id = gambler.id casino_game.user_id = gambler.id
casino_game.currency = currency_kind casino_game.currency = currency_kind
casino_game.wager = wager casino_game.wager = wager
casino_game.winnings = 0 casino_game.winnings = 0
casino_game.kind = 'blackjack' casino_game.kind = 'blackjack'
casino_game.game_state = json.dumps(build_initial_state()) casino_game.game_state = json.dumps(build_initial_state())
g.db.add(casino_game) g.db.add(casino_game)
g.db.flush() g.db.flush()
def build_initial_state(): def build_initial_state():
player, dealer, deck = deal_initial_cards() player, dealer, deck = deal_initial_cards()
state = { state = {
"player": player, "player": player,
"dealer": dealer, "dealer": dealer,
"deck": deck, "deck": deck,
"actions": [], "actions": [],
"insurance": False, "insurance": False,
"doubled_down": False, "doubled_down": False,
"status": "active" "status": "active"
} }
state['actions'] = determine_actions(state) state['actions'] = determine_actions(state)
return state return state
def save_game_state(game, new_state): def save_game_state(game, new_state):
game.game_state = json.dumps(new_state) game.game_state = json.dumps(new_state)
g.db.add(game) g.db.add(game)
def get_active_game(gambler): def get_active_game(gambler):
game = g.db.query(Casino_Game) \ game = g.db.query(Casino_Game) \
.filter(Casino_Game.active == True and .filter(Casino_Game.active == True,
Casino_Game.kind == 'blackjack' and Casino_Game.kind == 'blackjack',
Casino_Game.user_id == gambler.id).first() Casino_Game.user_id == gambler.id).first()
if game: if game:
return game, json.loads(game.game_state) return game, json.loads(game.game_state)
else: else:
return None, None return None, None
def get_safe_game_state(gambler): def get_safe_game_state(gambler):
game, game_state = get_active_game(gambler) game, game_state = get_active_game(gambler)
if game: if game:
return { return {
"player": game_state['player'], "player": game_state['player'],
"dealer": [game_state['dealer'][0], "?"], "dealer": [game_state['dealer'][0], "?"],
"actions": game_state['actions'], "actions": game_state['actions'],
"insurance": game_state['insurance'], "insurance": game_state['insurance'],
"doubled_down": game_state['doubled_down'], "doubled_down": game_state['doubled_down'],
"status": game_state['status'] "status": game_state['status']
} }
else: else:
return None return None
def apply_blackjack_result(gambler): def apply_blackjack_result(gambler):
game, game_state = get_active_game(gambler) game, game_state = get_active_game(gambler)
if game and game.active: if game and game.active:
result = game_state['status'] result = game_state['status']
if result == 'push' or result == 'insured_loss': if result == 'push' or result == 'insured_loss':
reward = 0 reward = 0
elif result == 'won': elif result == 'won':
reward = game.wager reward = game.wager
elif result == 'blackjack': elif result == 'blackjack':
reward = floor(game.wager * 3/2) reward = floor(game.wager * 3/2)
else: else:
reward = -game.wager reward = -game.wager
gambler.winnings += reward gambler.winnings += reward
if (reward > -1): if (reward > -1):
currency_value = int(getattr(gambler, game.currency, 0)) currency_value = int(getattr(gambler, game.currency, 0))
setattr(gambler, game.currency, setattr(gambler, game.currency,
currency_value + game.wager + reward) currency_value + game.wager + reward)
game.active = False game.active = False
game.winnings = reward game.winnings = reward
g.db.add(game) g.db.add(game)
def deal_blackjack_game(gambler, wager_value, currency): def deal_blackjack_game(gambler, wager_value, currency):
over_min = wager_value >= minimum_bet over_min = wager_value >= minimum_bet
under_max = wager_value <= maximum_bet under_max = wager_value <= maximum_bet
using_dramacoin = currency == "dramacoin" using_dramacoin = currency == "dramacoin"
using_marseybux = not using_dramacoin using_marseybux = not using_dramacoin
has_proper_funds = (using_dramacoin and gambler.coins >= wager_value) or ( has_proper_funds = (using_dramacoin and gambler.coins >= wager_value) or (
using_marseybux and gambler.procoins >= wager_value) using_marseybux and gambler.procoins >= wager_value)
currency_prop = "coins" if using_dramacoin else "procoins" currency_prop = "coins" if using_dramacoin else "procoins"
currency_value = getattr(gambler, currency_prop, 0) currency_value = getattr(gambler, currency_prop, 0)
if (over_min and under_max and has_proper_funds): if (over_min and under_max and has_proper_funds):
# Charge the gambler for the game, reduce their winnings, and start the game. # Charge the gambler for the game, reduce their winnings, and start the game.
setattr(gambler, currency_prop, currency_value - wager_value) setattr(gambler, currency_prop, currency_value - wager_value)
gambler.winnings -= wager_value gambler.winnings -= wager_value
build_game(gambler, currency_prop, wager_value) build_game(gambler, currency_prop, wager_value)
game, game_state = get_active_game(gambler) game, game_state = get_active_game(gambler)
player_value = get_hand_value(game_state['player']) player_value = get_hand_value(game_state['player'])
dealer_value = get_hand_value(game_state['dealer']) dealer_value = get_hand_value(game_state['dealer'])
if player_value == 21 and dealer_value == 21: if player_value == 21 and dealer_value == 21:
game_state["status"] = 'push' game_state["status"] = 'push'
save_game_state(game, game_state) save_game_state(game, game_state)
apply_blackjack_result(gambler) apply_blackjack_result(gambler)
elif player_value == 21: elif player_value == 21:
game_state["status"] = 'blackjack' game_state["status"] = 'blackjack'
save_game_state(game, game_state) save_game_state(game, game_state)
apply_blackjack_result(gambler) apply_blackjack_result(gambler)
g.db.flush() g.db.flush()
return True return True
else: else:
return False return False
# region Actions # region Actions
def gambler_hit(gambler): def gambler_hit(gambler):
game, game_state = get_active_game(gambler.id) game, game_state = get_active_game(gambler)
if game: if game:
player = game_state['player'] player = game_state['player']
deck = game_state['deck'] deck = game_state['deck']
doubled_down = game_state['doubled_down'] doubled_down = game_state['doubled_down']
player.append(deck.pop(0)) player.append(deck.pop(0))
player_value = get_hand_value(player) player_value = get_hand_value(player)
went_bust = player_value == -1 went_bust = player_value == -1
five_card_charlied = len(player) >= 5 five_card_charlied = len(player) >= 5
if went_bust: if went_bust:
game_state['status'] = 'bust' game_state['status'] = 'bust'
save_game_state(game, game_state) save_game_state(game, game_state)
apply_blackjack_result(gambler) apply_blackjack_result(gambler)
elif five_card_charlied: elif five_card_charlied:
game_state['status'] = 'won' game_state['status'] = 'won'
save_game_state(game, game_state) save_game_state(game, game_state)
apply_blackjack_result(gambler) apply_blackjack_result(gambler)
else: else:
save_game_state(game, game_state) save_game_state(game, game_state)
if doubled_down or player_value == 21: if doubled_down or player_value == 21:
forced_stay_success, forced_stay_state = gambler_stayed(gambler) forced_stay_success, forced_stay_state = gambler_stayed(gambler)
return forced_stay_success, forced_stay_state return forced_stay_success, forced_stay_state
else: else:
return True, game_state return True, game_state
else: else:
return False, game_state return False, game_state
def gambler_stayed(gambler): def gambler_stayed(gambler):
game, game_state = get_active_game(gambler.id) game, game_state = get_active_game(gambler)
if game: if game:
player = game_state['player'] player = game_state['player']
dealer = game_state['dealer'] dealer = game_state['dealer']
deck = game_state['deck'] deck = game_state['deck']
insured = game_state['insurance'] insured = game_state['insurance']
player_value = get_hand_value(player) player_value = get_hand_value(player)
dealer_value = get_hand_value(dealer) dealer_value = get_hand_value(dealer)
if dealer_value == 21 and insured: if dealer_value == 21 and insured:
game_state["status"] = 'insured_loss' game_state["status"] = 'insured_loss'
save_game_state(game, game_state) save_game_state(game, game_state)
apply_blackjack_result(gambler) apply_blackjack_result(gambler)
else: else:
while dealer_value < 17 and dealer_value != -1: while dealer_value < 17 and dealer_value != -1:
next = deck.pop(0) next = deck.pop(0)
dealer.append(next) dealer.append(next)
dealer_value = get_hand_value(dealer) dealer_value = get_hand_value(dealer)
if player_value > dealer_value or dealer_value == -1: if player_value > dealer_value or dealer_value == -1:
game_state["status"] = 'won' game_state["status"] = 'won'
elif dealer_value > player_value: elif dealer_value > player_value:
game_state["status"] = 'lost' game_state["status"] = 'lost'
else: else:
game_state["status"] = 'push' game_state["status"] = 'push'
save_game_state(game, game_state) save_game_state(game, game_state)
apply_blackjack_result(gambler) apply_blackjack_result(gambler)
return True, game_state return True, game_state
else: else:
return False, game_state return False, game_state
def gambler_doubled_down(gambler): def gambler_doubled_down(gambler):
game, game_state = get_active_game(gambler.id) game, game_state = get_active_game(gambler)
if game and not game_state['doubled_down']: if game and not game_state['doubled_down']:
currency_value = getattr(gambler, game.currency, 0) currency_value = getattr(gambler, game.currency, 0)
if (currency_value < game.wager): if (currency_value < game.wager):
return False return False
setattr(gambler, game.currency, currency_value - game.wager) setattr(gambler, game.currency, currency_value - game.wager)
game.wager *= 2 game.wager *= 2
game_state['doubled_down'] = True game_state['doubled_down'] = True
save_game_state(game, game_state) save_game_state(game, game_state)
g.db.flush() g.db.flush()
last_hit_success, last_hit_state = gambler_hit(gambler) last_hit_success, last_hit_state = gambler_hit(gambler)
return last_hit_success, last_hit_state return last_hit_success, last_hit_state
else: else:
return False, game_state return False, game_state
def gambler_purchased_insurance(gambler): def gambler_purchased_insurance(gambler):
game, game_state = get_active_game(gambler.id) game, game_state = get_active_game(gambler)
if game and not game_state['insurance']: if game and not game_state['insurance']:
insurance_cost = game.wager / 2 insurance_cost = game.wager / 2
currency_value = getattr(gambler, game.currency, 0) currency_value = getattr(gambler, game.currency, 0)
if (currency_value < insurance_cost): if (currency_value < insurance_cost):
return False, game_state return False, game_state
setattr(gambler, game.currency, currency_value - insurance_cost) setattr(gambler, game.currency, currency_value - insurance_cost)
game_state['insurance'] = True game_state['insurance'] = True
game_state['actions'] = determine_actions(game_state) game_state['actions'] = determine_actions(game_state)
save_game_state(game, game_state) save_game_state(game, game_state)
return True, game_state return True, game_state
else: else:
return False, game_state return False, game_state
# endregion # endregion
@ -258,40 +258,40 @@ def gambler_purchased_insurance(gambler):
def shuffle(x): def shuffle(x):
random.shuffle(x) random.shuffle(x)
return x return x
def determine_actions(state): def determine_actions(state):
actions = ['hit', 'stay', 'double_down'] actions = ['hit', 'stay', 'double_down']
if (state['dealer'][0][0] == "A" and not state['insurance']): if (state['dealer'][0][0] == "A" and not state['insurance']):
actions.append('insure') actions.append('insure')
return actions return actions
def deal_initial_cards(): def deal_initial_cards():
deck = shuffle( deck = shuffle(
[rank + suit for rank in ranks for suit in suits for _ in range(deck_count)]) [rank + suit for rank in ranks for suit in suits for _ in range(deck_count)])
p1, d1, p2, d2, *rest_of_deck = deck p1, d1, p2, d2, *rest_of_deck = deck
return [p1, p2], [d1, d2], rest_of_deck return [p1, p2], [d1, d2], rest_of_deck
def get_card_value(card): def get_card_value(card):
rank = card[0] rank = card[0]
return 0 if rank == "A" else min(ranks.index(rank) + 2, 10) return 0 if rank == "A" else min(ranks.index(rank) + 2, 10)
def get_hand_value(hand): def get_hand_value(hand):
without_aces = sum(map(get_card_value, hand)) without_aces = sum(map(get_card_value, hand))
ace_count = sum("A" in c for c in hand) ace_count = sum("A" in c for c in hand)
possibilities = [] possibilities = []
for i in range(ace_count + 1): for i in range(ace_count + 1):
value = without_aces + (ace_count - i) + i * 11 value = without_aces + (ace_count - i) + i * 11
possibilities.append(-1 if value > 21 else value) possibilities.append(-1 if value > 21 else value)
return max(possibilities) return max(possibilities)
# endregion # endregion

View File

@ -774,37 +774,37 @@ AWARDS_DISABLED = [
HOUSE_AWARDS = { HOUSE_AWARDS = {
"Furry": { "Furry": {
"kind": "Furry", "kind": "Furry",
"title": "OwOify", "title": "OwOify",
"description": "OwOifies the recipient's comments for 6 hours.", "description": "OwOifies the recipient's comments for 6 hours.",
"icon": "fas fa-paw-simple", "icon": "fas fa-paw-simple",
"color": "text-purple", "color": "text-purple",
"price": 400 "price": 400
}, },
"Femboy": { "Femboy": {
"kind": "Femboy", "kind": "Femboy",
"title": "Marsify", "title": "Marsify",
"description": "Marsifies the recipient's comments for 6 hours.", "description": "Marsifies the recipient's comments for 6 hours.",
"icon": "fas fa-cat", "icon": "fas fa-cat",
"color": "text-white", "color": "text-white",
"price": 400 "price": 400
}, },
"Vampire": { "Vampire": {
"kind": "Vampire", "kind": "Vampire",
"title": "Bite", "title": "Bite",
"description": "Turns the recipient into a vampire for 24 hours.", "description": "Turns the recipient into a vampire for 24 hours.",
"icon": "fas fa-bat", "icon": "fas fa-bat",
"color": "text-gray", "color": "text-gray",
"price": 777 "price": 777
}, },
"Racist": { "Racist": {
"kind": "Racist", "kind": "Racist",
"title": "Early Life", "title": "Early Life",
"description": "Checks the recipient's Early Life section on Wikipedia. Notices.", "description": "Checks the recipient's Early Life section on Wikipedia. Notices.",
"icon": "fas fa-star-of-david", "icon": "fas fa-star-of-david",
"color": "text-yellow", "color": "text-yellow",
"price": 400 "price": 400
}, },
} }
temp = deepcopy(HOUSE_AWARDS).items() temp = deepcopy(HOUSE_AWARDS).items()

View File

@ -23,7 +23,7 @@ def get_active_lottery_stats():
active_lottery = get_active_lottery() active_lottery = get_active_lottery()
participating_users = get_users_participating_in_lottery() participating_users = get_users_participating_in_lottery()
return None if active_lottery is None else active_lottery.stats, len(participating_users) return None if active_lottery is None else active_lottery.stats, len(participating_users)
def end_lottery_session(): def end_lottery_session():

View File

@ -81,33 +81,33 @@ def allowed_attributes(tag, name, value):
def build_url_re(tlds, protocols): def build_url_re(tlds, protocols):
"""Builds the url regex used by linkifier """Builds the url regex used by linkifier
If you want a different set of tlds or allowed protocols, pass those in If you want a different set of tlds or allowed protocols, pass those in
and stomp on the existing ``url_re``:: and stomp on the existing ``url_re``::
from bleach import linkifier from bleach import linkifier
my_url_re = linkifier.build_url_re(my_tlds_list, my_protocols) my_url_re = linkifier.build_url_re(my_tlds_list, my_protocols)
linker = LinkifyFilter(url_re=my_url_re) linker = LinkifyFilter(url_re=my_url_re)
""" """
return re.compile( return re.compile(
r"""\(* # Match any opening parentheses. r"""\(*# Match any opening parentheses.
\b(?<![@.])(?:(?:{0}):/{{0,3}}(?:(?:\w+:)?\w+@)?)? # http:// \b(?<![@.])(?:(?:{0}):/{{0,3}}(?:(?:\w+:)?\w+@)?)?# http://
([\w-]+\.)+(?:{1})(?:\:[0-9]+)?(?!\.\w)\b # xx.yy.tld(:##)? ([\w-]+\.)+(?:{1})(?:\:[0-9]+)?(?!\.\w)\b# xx.yy.tld(:##)?
(?:[/?][^#\s\{{\}}\|\\\^\[\]`<>"]*)? (?:[/?][^#\s\{{\}}\|\\\^\[\]`<>"]*)?
# /path/zz (excluding "unsafe" chars from RFC 1738, # /path/zz (excluding "unsafe" chars from RFC 1738,
# except for ~, which happens in practice) # except for ~, which happens in practice)
(?:\#[^#\s\|\\\^\[\]`<>"]*)? (?:\#[^#\s\|\\\^\[\]`<>"]*)?
# #hash (excluding "unsafe" chars from RFC 1738, # #hash (excluding "unsafe" chars from RFC 1738,
# except for ~, which happens in practice) # except for ~, which happens in practice)
""".format( """.format(
"|".join(sorted(protocols)), "|".join(sorted(tlds)) "|".join(sorted(protocols)), "|".join(sorted(tlds))
), ),
re.IGNORECASE | re.VERBOSE | re.UNICODE, re.IGNORECASE | re.VERBOSE | re.UNICODE,
) )
url_re = build_url_re(tlds=TLDS, protocols=['http', 'https']) url_re = build_url_re(tlds=TLDS, protocols=['http', 'https'])

View File

@ -6,119 +6,119 @@ from files.classes.casino_game import Casino_Game
from flask import g from flask import g
if SITE_NAME == 'rDrama': if SITE_NAME == 'rDrama':
minimum_bet = 100 minimum_bet = 100
else: else:
minimum_bet = 10 minimum_bet = 10
maximum_bet = INFINITY maximum_bet = INFINITY
payout_to_symbols = { payout_to_symbols = {
2: ["👣", "🍀", "🌈", "⭐️"], 2: ["👣", "🍀", "🌈", "⭐️"],
3: ["🍎", "🔞", "⚛️", "☢️"], 3: ["🍎", "🔞", "⚛️", "☢️"],
5: ["✡️", "⚔️", "🍆", "🍒"], 5: ["✡️", "⚔️", "🍆", "🍒"],
12: ["🐱"] 12: ["🐱"]
} }
def casino_slot_pull(gambler, wager_value, currency): def casino_slot_pull(gambler, wager_value, currency):
over_min = wager_value >= minimum_bet over_min = wager_value >= minimum_bet
under_max = wager_value <= maximum_bet under_max = wager_value <= maximum_bet
using_dramacoin = currency == "dramacoin" using_dramacoin = currency == "dramacoin"
using_marseybux = not using_dramacoin using_marseybux = not using_dramacoin
has_proper_funds = (using_dramacoin and gambler.coins >= wager_value) or ( has_proper_funds = (using_dramacoin and gambler.coins >= wager_value) or (
using_marseybux and gambler.procoins >= wager_value) using_marseybux and gambler.procoins >= wager_value)
currency_prop = "coins" if using_dramacoin else "procoins" currency_prop = "coins" if using_dramacoin else "procoins"
currency_value = getattr(gambler, currency_prop, 0) currency_value = getattr(gambler, currency_prop, 0)
if (over_min and under_max and has_proper_funds): if (over_min and under_max and has_proper_funds):
setattr(gambler, currency_prop, currency_value - wager_value) setattr(gambler, currency_prop, currency_value - wager_value)
gambler.winnings -= wager_value gambler.winnings -= wager_value
payout = determine_payout() payout = determine_payout()
reward = wager_value * payout reward = wager_value * payout
currency_value = getattr(gambler, currency_prop, 0) currency_value = getattr(gambler, currency_prop, 0)
setattr(gambler, currency_prop, currency_value + reward) setattr(gambler, currency_prop, currency_value + reward)
gambler.winnings += reward gambler.winnings += reward
symbols = build_symbols(payout) symbols = build_symbols(payout)
text = build_text(wager_value, payout, currency) text = build_text(wager_value, payout, currency)
game_state = { game_state = {
"symbols": symbols, "symbols": symbols,
"text": text "text": text
} }
casino_game = Casino_Game() casino_game = Casino_Game()
casino_game.active = False casino_game.active = False
casino_game.user_id = gambler.id casino_game.user_id = gambler.id
casino_game.currency = currency_prop casino_game.currency = currency_prop
casino_game.wager = wager_value casino_game.wager = wager_value
casino_game.winnings = reward - wager_value casino_game.winnings = reward - wager_value
casino_game.kind = 'slots' casino_game.kind = 'slots'
casino_game.game_state = json.dumps(game_state) casino_game.game_state = json.dumps(game_state)
g.db.add(casino_game) g.db.add(casino_game)
return True, casino_game.game_state return True, casino_game.game_state
else: else:
return False, "{}" return False, "{}"
def build_symbols(for_payout): def build_symbols(for_payout):
all_symbols = [] all_symbols = []
for payout in payout_to_symbols: for payout in payout_to_symbols:
for symbol in payout_to_symbols[payout]: for symbol in payout_to_symbols[payout]:
all_symbols.append(symbol) all_symbols.append(symbol)
shuffle(all_symbols) shuffle(all_symbols)
if for_payout == 0: if for_payout == 0:
return "".join([all_symbols[0], ",", all_symbols[1], ",", all_symbols[2]]) return "".join([all_symbols[0], ",", all_symbols[1], ",", all_symbols[2]])
elif for_payout == 1: elif for_payout == 1:
indices = shuffle([0, 1, 2]) indices = shuffle([0, 1, 2])
symbol_set = ["", "", ""] symbol_set = ["", "", ""]
match_a = indices[0] match_a = indices[0]
match_b = indices[1] match_b = indices[1]
nonmatch = indices[2] nonmatch = indices[2]
matching_symbol = all_symbols[0] matching_symbol = all_symbols[0]
other_symbol = all_symbols[1] other_symbol = all_symbols[1]
symbol_set[match_a] = matching_symbol symbol_set[match_a] = matching_symbol
symbol_set[match_b] = matching_symbol symbol_set[match_b] = matching_symbol
symbol_set[nonmatch] = other_symbol symbol_set[nonmatch] = other_symbol
return "".join([symbol_set[0], ",", symbol_set[1], ",", symbol_set[2]]) return "".join([symbol_set[0], ",", symbol_set[1], ",", symbol_set[2]])
else: else:
relevantSymbols = shuffle(payout_to_symbols[for_payout]) relevantSymbols = shuffle(payout_to_symbols[for_payout])
symbol = relevantSymbols[0] symbol = relevantSymbols[0]
return "".join([symbol, ",", symbol, ",", symbol]) return "".join([symbol, ",", symbol, ",", symbol])
def build_text(wager_value, result, currency): def build_text(wager_value, result, currency):
if result == 0: if result == 0:
return f'Lost {wager_value} {currency}' return f'Lost {wager_value} {currency}'
elif result == 1: elif result == 1:
return 'Broke Even' return 'Broke Even'
elif result == 12: elif result == 12:
return f'Jackpot! Won {wager_value * (result-1)} {currency}' return f'Jackpot! Won {wager_value * (result-1)} {currency}'
else: else:
return f'Won {wager_value * (result-1)} {currency}' return f'Won {wager_value * (result-1)} {currency}'
def determine_payout(): def determine_payout():
value = random.randint(1, 100) value = random.randint(1, 100)
if value == 100: if value == 100:
return 12 return 12
elif value >= 96: elif value >= 96:
return 5 return 5
elif value >= 88: elif value >= 88:
return 3 return 3
elif value >= 72: elif value >= 72:
return 2 return 2
elif value >= 61: elif value >= 61:
return 1 return 1
else: else:
return 0 return 0
def shuffle(stuff): def shuffle(stuff):
random.shuffle(stuff) random.shuffle(stuff)
return stuff return stuff

View File

@ -13,7 +13,7 @@ from files.classes.award import AwardRelationship
from files.helpers.const import * from files.helpers.const import *
def generate_charts_task(site): def generate_charts_task(site):
chart(kind='daily', site=site) chart(kind='daily', site=site)
chart(kind='weekly', site=site) chart(kind='weekly', site=site)
stats(site=site) stats(site=site)
@ -63,14 +63,14 @@ def chart(kind, site):
plt.rcParams['figure.figsize'] = (chart_width, 20) plt.rcParams['figure.figsize'] = (chart_width, 20)
signup_chart = plt.subplot2grid((chart_width, 20), (0, 0), rowspan=6, colspan=chart_width) signup_chart = plt.subplot2grid((chart_width, 20), (0, 0), rowspan=6, colspan=chart_width)
posts_chart = plt.subplot2grid((chart_width, 20), (10, 0), rowspan=6, colspan=chart_width) posts_chart = plt.subplot2grid((chart_width, 20), (10, 0), rowspan=6, colspan=chart_width)
comments_chart = plt.subplot2grid((chart_width, 20), (20, 0), rowspan=6, colspan=chart_width) comments_chart = plt.subplot2grid((chart_width, 20), (20, 0), rowspan=6, colspan=chart_width)
signup_chart.grid(), posts_chart.grid(), comments_chart.grid() signup_chart.grid(), posts_chart.grid(), comments_chart.grid()
signup_chart.plot (daily_times, daily_signups, color='red') signup_chart.plot(daily_times, daily_signups, color='red')
posts_chart.plot (daily_times, post_stats, color='blue') posts_chart.plot(daily_times, post_stats, color='blue')
comments_chart.plot(daily_times, comment_stats, color='purple') comments_chart.plot(daily_times, comment_stats, color='purple')
signup_chart.set_ylim(ymin=0) signup_chart.set_ylim(ymin=0)
posts_chart.set_ylim(ymin=0) posts_chart.set_ylim(ymin=0)

View File

@ -32,11 +32,11 @@ def send_verification_email(user, email=None):
link = url + params link = url + params
send_mail(to_address=email, send_mail(to_address=email,
html=render_template("email/email_verify.html", html=render_template("email/email_verify.html",
action_url=link, action_url=link,
v=user), v=user),
subject=f"Validate your {SITE_NAME} account email." subject=f"Validate your {SITE_NAME} account email."
) )
@app.post("/verify_email") @app.post("/verify_email")
@ -66,7 +66,7 @@ def activate(v):
if int(time.time()) - timestamp > 3600: if int(time.time()) - timestamp > 3600:
return render_template("message.html", v=v, title="Verification link expired.", return render_template("message.html", v=v, title="Verification link expired.",
message="That link has expired. Visit your settings to send yourself another verification email."), 410 message="That link has expired. Visit your settings to send yourself another verification email."), 410
user = get_account(id) user = get_account(id)

View File

@ -394,7 +394,7 @@ def reported_posts(v):
listing = get_posts(listing, v=v) listing = get_posts(listing, v=v)
return render_template("admin/reported_posts.html", return render_template("admin/reported_posts.html",
next_exists=next_exists, listing=listing, page=page, v=v) next_exists=next_exists, listing=listing, page=page, v=v)
@app.get("/admin/reported/comments") @app.get("/admin/reported/comments")
@ -404,7 +404,7 @@ def reported_comments(v):
page = max(1, int(request.values.get("page", 1))) page = max(1, int(request.values.get("page", 1)))
listing = g.db.query(Comment listing = g.db.query(Comment
).filter_by( ).filter_by(
is_approved=None, is_approved=None,
is_banned=False is_banned=False
).join(Comment.flags).order_by(Comment.id.desc()).offset(25 * (page - 1)).limit(26).all() ).join(Comment.flags).order_by(Comment.id.desc()).offset(25 * (page - 1)).limit(26).all()
@ -416,11 +416,11 @@ def reported_comments(v):
listing = get_comments(listing, v=v) listing = get_comments(listing, v=v)
return render_template("admin/reported_comments.html", return render_template("admin/reported_comments.html",
next_exists=next_exists, next_exists=next_exists,
listing=listing, listing=listing,
page=page, page=page,
v=v, v=v,
standalone=True) standalone=True)
@app.get("/admin") @app.get("/admin")
@admin_level_required(2) @admin_level_required(2)
@ -640,11 +640,11 @@ def users_list(v):
users = users[:25] users = users[:25]
return render_template("admin/new_users.html", return render_template("admin/new_users.html",
v=v, v=v,
users=users, users=users,
next_exists=next_exists, next_exists=next_exists,
page=page, page=page,
) )
@app.get("/badge_owners/<bid>") @app.get("/badge_owners/<bid>")
@ -663,11 +663,11 @@ def bid_list(v, bid):
users = users[:25] users = users[:25]
return render_template("admin/new_users.html", return render_template("admin/new_users.html",
v=v, v=v,
users=users, users=users,
next_exists=next_exists, next_exists=next_exists,
page=page, page=page,
) )
@app.get("/admin/alt_votes") @app.get("/admin/alt_votes")
@ -769,11 +769,11 @@ def alt_votes_get(v):
u2_comment_downs) if u2_comment_downs else 0 u2_comment_downs) if u2_comment_downs else 0
return render_template("admin/alt_votes.html", return render_template("admin/alt_votes.html",
u1=u1, u1=u1,
u2=u2, u2=u2,
v=v, v=v,
data=data data=data
) )
@app.post("/admin/link_accounts") @app.post("/admin/link_accounts")
@ -823,11 +823,11 @@ def admin_removed(v):
posts = get_posts(ids, v=v) posts = get_posts(ids, v=v)
return render_template("admin/removed_posts.html", return render_template("admin/removed_posts.html",
v=v, v=v,
listing=posts, listing=posts,
page=page, page=page,
next_exists=next_exists next_exists=next_exists
) )
@app.get("/admin/removed/comments") @app.get("/admin/removed/comments")
@ -848,11 +848,11 @@ def admin_removed_comments(v):
comments = get_comments(ids, v=v) comments = get_comments(ids, v=v)
return render_template("admin/removed_comments.html", return render_template("admin/removed_comments.html",
v=v, v=v,
listing=comments, listing=comments,
page=page, page=page,
next_exists=next_exists next_exists=next_exists
) )
@app.post("/agendaposter/<user_id>") @app.post("/agendaposter/<user_id>")

View File

@ -13,106 +13,106 @@ from files.helpers.lottery import *
@app.get("/casino") @app.get("/casino")
@auth_required @auth_required
def casino(v): def casino(v):
participants = get_users_participating_in_lottery() participants = get_users_participating_in_lottery()
return render_template("casino.html", v=v, participants=participants) return render_template("casino.html", v=v, participants=participants)
@app.post("/casino/slots") @app.post("/casino/slots")
@auth_required @auth_required
def pull_slots(v): def pull_slots(v):
try: try:
wager = int(request.values.get("wager")) wager = int(request.values.get("wager"))
except: except:
return {"error": "Invalid wager."} return {"error": "Invalid wager."}
try: try:
currency = request.values.get("currency") currency = request.values.get("currency")
except: except:
return {"error": "Invalid currency (expected 'dramacoin' or 'marseybux')."} return {"error": "Invalid currency (expected 'dramacoin' or 'marseybux')."}
if (currency == "dramacoin" and wager > v.coins) or (currency == "marseybux" and wager > v.procoins): if (currency == "dramacoin" and wager > v.coins) or (currency == "marseybux" and wager > v.procoins):
return {"error": f"Not enough {currency} to make that bet."} return {"error": f"Not enough {currency} to make that bet."}
success, game_state = casino_slot_pull(v, wager, currency) success, game_state = casino_slot_pull(v, wager, currency)
if success: if success:
return {"game_state": game_state, "gambler": { "coins": v.coins, "procoins": v.procoins }} return {"game_state": game_state, "gambler": { "coins": v.coins, "procoins": v.procoins }}
else: else:
return {"error": "Wager must be more than 100 {currency}."} return {"error": "Wager must be more than 100 {currency}."}
@app.get("/casino/blackjack") @app.get("/casino/blackjack")
@auth_required @auth_required
def get_player_blackjack_status(v): def get_player_blackjack_status(v):
game, game_state = get_active_game(v) game, game_state = get_active_game(v)
if game and game.active: if game and game.active:
safe_state = get_safe_game_state(v) safe_state = get_safe_game_state(v)
return {"active": True, "game_state": safe_state} return {"active": True, "game_state": safe_state}
else: else:
return {"active": False, "game_state": game_state} return {"active": False, "game_state": game_state}
@app.post("/casino/blackjack") @app.post("/casino/blackjack")
@auth_required @auth_required
def deal_blackjack(v): def deal_blackjack(v):
try: try:
wager = int(request.values.get("wager")) wager = int(request.values.get("wager"))
except: except:
return {"error": "Invalid wager."} return {"error": "Invalid wager."}
try: try:
currency = request.values.get("currency") currency = request.values.get("currency")
except: except:
return {"error": "Invalid currency (expected 'dramacoin' or 'marseybux')."} return {"error": "Invalid currency (expected 'dramacoin' or 'marseybux')."}
if (currency == "dramacoin" and wager > v.coins) or (currency == "marseybux" and wager > v.procoins): if (currency == "dramacoin" and wager > v.coins) or (currency == "marseybux" and wager > v.procoins):
return {"error": f"Not enough {currency} to make that bet."} return {"error": f"Not enough {currency} to make that bet."}
success = deal_blackjack_game(v, wager, currency) success = deal_blackjack_game(v, wager, currency)
if success: if success:
game, game_state = get_active_game(v) game, game_state = get_active_game(v)
if game and game.active: if game and game.active:
safe_state = get_safe_game_state(v) safe_state = get_safe_game_state(v)
return {"game_state": safe_state, "gambler": { "coins": v.coins, "procoins": v.procoins }} return {"game_state": safe_state, "gambler": { "coins": v.coins, "procoins": v.procoins }}
else: else:
return {"game_state": game_state, "gambler": { "coins": v.coins, "procoins": v.procoins }} return {"game_state": game_state, "gambler": { "coins": v.coins, "procoins": v.procoins }}
else: else:
return {"error": "Wager must be more than 100 {currency}."} return {"error": "Wager must be more than 100 {currency}."}
@app.post("/casino/blackjack/action") @app.post("/casino/blackjack/action")
@auth_required @auth_required
def player_took_blackjack_action(v): def player_took_blackjack_action(v):
try: try:
action = request.values.get("action") action = request.values.get("action")
except: except:
return {"error": "Invalid action."} return {"error": "Invalid action."}
was_successful = False was_successful = False
state = None state = None
if action == 'hit': if action == 'hit':
success, game_state = gambler_hit(v) success, game_state = gambler_hit(v)
was_successful = success was_successful = success
state = game_state state = game_state
elif action == 'stay': elif action == 'stay':
success, game_state = gambler_stayed(v) success, game_state = gambler_stayed(v)
was_successful = success was_successful = success
state = game_state state = game_state
elif action == 'double_down': elif action == 'double_down':
success, game_state = gambler_doubled_down(v) success, game_state = gambler_doubled_down(v)
was_successful = success was_successful = success
state = game_state state = game_state
elif action == 'insure': elif action == 'insure':
success, game_state = gambler_purchased_insurance(v) success, game_state = gambler_purchased_insurance(v)
was_successful = success was_successful = success
state = game_state state = game_state
if was_successful: if was_successful:
return {"active": True, "game_state": state, "gambler": { "coins": v.coins, "procoins": v.procoins }} return {"active": True, "game_state": state, "gambler": { "coins": v.coins, "procoins": v.procoins }}
else: else:
return {"active": False, "game_state": None} return {"active": False, "game_state": None}

View File

@ -117,7 +117,7 @@ def frontlist(v=None, sort="hot", page=1, t="all", ids_only=True, ccmode="false"
if v and filter_words: if v and filter_words:
for word in filter_words: for word in filter_words:
word = word.replace('\\', '').replace('_', '\_').replace('%', '\%').strip() word = word.replace('\\', '').replace('_', '\_').replace('%', '\%').strip()
posts=posts.filter(not_(Submission.title.ilike(f'%{word}%'))) posts=posts.filter(not_(Submission.title.ilike(f'%{word}%')))
if not (v and v.shadowbanned): if not (v and v.shadowbanned):

View File

@ -114,8 +114,8 @@ def hat_owners(v, hat_id):
users = users[:25] users = users[:25]
return render_template("admin/new_users.html", return render_template("admin/new_users.html",
v=v, v=v,
users=users, users=users,
next_exists=next_exists, next_exists=next_exists,
page=page, page=page,
) )

View File

@ -88,7 +88,7 @@ def login_post():
username = request.values.get("username") username = request.values.get("username")
if not username: abort(400) if not username: abort(400)
username = username.lstrip('@').replace('\\', '').replace('_', '\_').replace('%', '').strip() username = username.lstrip('@').replace('\\', '').replace('_', '\_').replace('%', '').strip()
if not username: abort(400) if not username: abort(400)
if username.startswith('@'): username = username[1:] if username.startswith('@'): username = username[1:]
@ -113,11 +113,11 @@ def login_post():
now = int(time.time()) now = int(time.time())
hash = generate_hash(f"{account.id}+{now}+2fachallenge") hash = generate_hash(f"{account.id}+{now}+2fachallenge")
return render_template("login_2fa.html", return render_template("login_2fa.html",
v=account, v=account,
time=now, time=now,
hash=hash, hash=hash,
redirect=request.values.get("redirect", "/") redirect=request.values.get("redirect", "/")
) )
elif request.values.get("2fa_token", "x"): elif request.values.get("2fa_token", "x"):
now = int(time.time()) now = int(time.time())
@ -134,11 +134,11 @@ def login_post():
if not account.validate_2fa(request.values.get("2fa_token", "").strip()): if not account.validate_2fa(request.values.get("2fa_token", "").strip()):
hash = generate_hash(f"{account.id}+{now}+2fachallenge") hash = generate_hash(f"{account.id}+{now}+2fachallenge")
return render_template("login_2fa.html", return render_template("login_2fa.html",
v=account, v=account,
time=now, time=now,
hash=hash, hash=hash,
failed=True, failed=True,
) )
else: else:
abort(400) abort(400)
@ -191,7 +191,7 @@ def sign_up_get(v):
ref = request.values.get("ref") ref = request.values.get("ref")
if ref: if ref:
ref = ref.replace('\\', '').replace('_', '\_').replace('%', '').strip() ref = ref.replace('\\', '').replace('_', '\_').replace('%', '').strip()
ref_user = g.db.query(User).filter(User.username.ilike(ref)).one_or_none() ref_user = g.db.query(User).filter(User.username.ilike(ref)).one_or_none()
else: else:
@ -207,9 +207,9 @@ def sign_up_get(v):
formkey_hashstr = str(now) + token + g.agent formkey_hashstr = str(now) + token + g.agent
formkey = hmac.new(key=bytes(MASTER_KEY, "utf-16"), formkey = hmac.new(key=bytes(MASTER_KEY, "utf-16"),
msg=bytes(formkey_hashstr, "utf-16"), msg=bytes(formkey_hashstr, "utf-16"),
digestmod='md5' digestmod='md5'
).hexdigest() ).hexdigest()
error = request.values.get("error") error = request.values.get("error")
@ -219,13 +219,13 @@ def sign_up_get(v):
if not is_site_url(redir): redir = "/" if not is_site_url(redir): redir = "/"
return render_template("sign_up.html", return render_template("sign_up.html",
formkey=formkey, formkey=formkey,
now=now, now=now,
ref_user=ref_user, ref_user=ref_user,
hcaptcha=HCAPTCHA_SITEKEY, hcaptcha=HCAPTCHA_SITEKEY,
error=error, error=error,
redirect=redir redirect=redir
) )
@app.post("/signup") @app.post("/signup")
@ -248,7 +248,7 @@ def sign_up_post(v):
correct_formkey = hmac.new(key=bytes(MASTER_KEY, "utf-16"), correct_formkey = hmac.new(key=bytes(MASTER_KEY, "utf-16"),
msg=bytes(correct_formkey_hashstr, "utf-16"), msg=bytes(correct_formkey_hashstr, "utf-16"),
digestmod='md5' digestmod='md5'
).hexdigest() ).hexdigest()
now = int(time.time()) now = int(time.time())
@ -333,7 +333,7 @@ def sign_up_post(v):
password=request.values.get("password"), password=request.values.get("password"),
email=email, email=email,
referred_by=ref_id or None, referred_by=ref_id or None,
ban_evade = int(any((x.is_banned or x.shadowbanned) and not x.unban_utc for x in g.db.query(User).filter(User.id.in_(session.get("history", []))).all() if x)), ban_evade =int(any((x.is_banned or x.shadowbanned) and not x.unban_utc for x in g.db.query(User).filter(User.id.in_(session.get("history", []))).all() if x)),
profileurl=profileurl profileurl=profileurl
) )
@ -400,8 +400,8 @@ def post_forgot():
return render_template("forgot_password.html", error="Invalid email.") return render_template("forgot_password.html", error="Invalid email.")
username = username.lstrip('@').replace('\\', '').replace('_', '\_').replace('%', '').strip() username = username.lstrip('@').replace('\\', '').replace('_', '\_').replace('%', '').strip()
email = email.replace('\\', '').replace('_', '\_').replace('%', '').strip() email = email.replace('\\', '').replace('_', '\_').replace('%', '').strip()
user = g.db.query(User).filter( user = g.db.query(User).filter(
User.username.ilike(username), User.username.ilike(username),
@ -413,14 +413,14 @@ def post_forgot():
url = f"{SITE_FULL}/reset?id={user.id}&time={now}&token={token}" url = f"{SITE_FULL}/reset?id={user.id}&time={now}&token={token}"
send_mail(to_address=user.email, send_mail(to_address=user.email,
subject="Password Reset Request", subject="Password Reset Request",
html=render_template("email/password_reset.html", html=render_template("email/password_reset.html",
action_url=url, action_url=url,
v=user) v=user)
) )
return render_template("forgot_password.html", return render_template("forgot_password.html",
msg="If the username and email matches an account, you will be sent a password reset email. You have ten minutes to complete the password reset process.") msg="If the username and email matches an account, you will be sent a password reset email. You have ten minutes to complete the password reset process.")
@app.get("/reset") @app.get("/reset")
@ -451,10 +451,10 @@ def get_reset():
reset_token = generate_hash(f"{user.id}+{timestamp}+reset+{user.login_nonce}") reset_token = generate_hash(f"{user.id}+{timestamp}+reset+{user.login_nonce}")
return render_template("reset_password.html", return render_template("reset_password.html",
v=user, v=user,
token=reset_token, token=reset_token,
time=timestamp, time=timestamp,
) )
@app.post("/reset") @app.post("/reset")
@ -475,8 +475,8 @@ def post_reset(v):
if now - timestamp > 600: if now - timestamp > 600:
return render_template("message.html", return render_template("message.html",
title="Password reset expired", title="Password reset expired",
error="That password reset form has expired.") error="That password reset form has expired.")
user = get_account(user_id) user = get_account(user_id)
@ -487,18 +487,18 @@ def post_reset(v):
if password != confirm_password: if password != confirm_password:
return render_template("reset_password.html", return render_template("reset_password.html",
v=user, v=user,
token=token, token=token,
time=timestamp, time=timestamp,
error="Passwords didn't match.") error="Passwords didn't match.")
user.passhash = hash_password(password) user.passhash = hash_password(password)
g.db.add(user) g.db.add(user)
return render_template("message_success.html", return render_template("message_success.html",
title="Password reset successful!", title="Password reset successful!",
message="Login normally to access your account.") message="Login normally to access your account.")
@app.get("/lost_2fa") @app.get("/lost_2fa")
@auth_desired @auth_desired
@ -517,8 +517,8 @@ def request_2fa_disable():
user=get_user(username, graceful=True) user=get_user(username, graceful=True)
if not user or not user.email or not user.mfa_secret: if not user or not user.email or not user.mfa_secret:
return render_template("message.html", return render_template("message.html",
title="Removal request received", title="Removal request received",
message="If username, password, and email match, we will send you an email.") message="If username, password, and email match, we will send you an email.")
email=request.values.get("email").strip().lower() email=request.values.get("email").strip().lower()
@ -529,8 +529,8 @@ def request_2fa_disable():
password =request.values.get("password") password =request.values.get("password")
if not user.verifyPass(password): if not user.verifyPass(password):
return render_template("message.html", return render_template("message.html",
title="Removal request received", title="Removal request received",
message="If username, password, and email match, we will send you an email.") message="If username, password, and email match, we will send you an email.")
valid=int(time.time()) valid=int(time.time())
token=generate_hash(f"{user.id}+{user.username}+disable2fa+{valid}+{user.mfa_secret}+{user.login_nonce}") token=generate_hash(f"{user.id}+{user.username}+disable2fa+{valid}+{user.mfa_secret}+{user.login_nonce}")
@ -538,15 +538,15 @@ def request_2fa_disable():
action_url=f"{SITE_FULL}/reset_2fa?id={user.id}&t={valid}&token={token}" action_url=f"{SITE_FULL}/reset_2fa?id={user.id}&t={valid}&token={token}"
send_mail(to_address=user.email, send_mail(to_address=user.email,
subject="2FA Removal Request", subject="2FA Removal Request",
html=render_template("email/2fa_remove.html", html=render_template("email/2fa_remove.html",
action_url=action_url, action_url=action_url,
v=user) v=user)
) )
return render_template("message.html", return render_template("message.html",
title="Removal request received", title="Removal request received",
message="If username, password, and email match, we will send you an email.") message="If username, password, and email match, we will send you an email.")
@app.get("/reset_2fa") @app.get("/reset_2fa")
def reset_2fa(): def reset_2fa():
@ -558,8 +558,8 @@ def reset_2fa():
if now > t+3600*24: if now > t+3600*24:
return render_template("message.html", return render_template("message.html",
title="Expired Link", title="Expired Link",
error="That link has expired.") error="That link has expired.")
token=request.values.get("token") token=request.values.get("token")
uid=request.values.get("id") uid=request.values.get("id")
@ -575,5 +575,5 @@ def reset_2fa():
return render_template("message_success.html", return render_template("message_success.html",
title="Two-factor authentication removed.", title="Two-factor authentication removed.",
message="Login normally to access your account.") message="Login normally to access your account.")

View File

@ -56,7 +56,7 @@ def notifications_modmail(v):
page=page, page=page,
standalone=True, standalone=True,
render_replies=True, render_replies=True,
) )
@ -85,7 +85,7 @@ def notifications_messages(v):
page=page, page=page,
standalone=True, standalone=True,
render_replies=True, render_replies=True,
) )
@app.get("/notifications/posts") @app.get("/notifications/posts")
@ -126,7 +126,7 @@ def notifications_posts(v):
page=page, page=page,
standalone=True, standalone=True,
render_replies=True, render_replies=True,
) )
@app.get("/notifications/modactions") @app.get("/notifications/modactions")
@ -153,7 +153,7 @@ def notifications_modactions(v):
page=page, page=page,
standalone=True, standalone=True,
render_replies=True, render_replies=True,
) )
@ -197,7 +197,7 @@ def notifications_reddit(v):
page=page, page=page,
standalone=True, standalone=True,
render_replies=True, render_replies=True,
) )
@ -267,4 +267,4 @@ def notifications(v):
page=page, page=page,
standalone=True, standalone=True,
render_replies=True, render_replies=True,
) )

View File

@ -71,13 +71,13 @@ def request_api_keys(v):
new_comment = Comment(author_id=AUTOJANNY_ID, new_comment = Comment(author_id=AUTOJANNY_ID,
parent_submission=None, parent_submission=None,
level=1, level=1,
body_html=body_html, body_html=body_html,
sentto=2, sentto=2,
distinguish_level=6, distinguish_level=6,
is_bot=True is_bot=True
) )
g.db.add(new_comment) g.db.add(new_comment)
g.db.flush() g.db.flush()
@ -233,11 +233,11 @@ def admin_app_id(v, aid):
posts=get_posts(pids, v=v) posts=get_posts(pids, v=v)
return render_template("admin/app.html", return render_template("admin/app.html",
v=v, v=v,
app=oauth, app=oauth,
listing=posts, listing=posts,
next_exists=next_exists next_exists=next_exists
) )
@app.get("/admin/app/<aid>/comments") @app.get("/admin/app/<aid>/comments")
@admin_level_required(3) @admin_level_required(3)
@ -257,12 +257,12 @@ def admin_app_id_comments(v, aid):
return render_template("admin/app.html", return render_template("admin/app.html",
v=v, v=v,
app=oauth, app=oauth,
comments=comments, comments=comments,
next_exists=next_exists, next_exists=next_exists,
standalone=True standalone=True
) )
@app.get("/admin/apps") @app.get("/admin/apps")

View File

@ -61,9 +61,9 @@ def option_votes(option_id, v):
ups = g.db.query(SubmissionOptionVote).filter_by(option_id=option_id).order_by(SubmissionOptionVote.created_utc).all() ups = g.db.query(SubmissionOptionVote).filter_by(option_id=option_id).order_by(SubmissionOptionVote.created_utc).all()
return render_template("poll_votes.html", return render_template("poll_votes.html",
v=v, v=v,
thing=option, thing=option,
ups=ups) ups=ups)
@ -113,6 +113,6 @@ def option_votes_comment(option_id, v):
ups = g.db.query(CommentOptionVote).filter_by(option_id=option_id).order_by(CommentOptionVote.created_utc).all() ups = g.db.query(CommentOptionVote).filter_by(option_id=option_id).order_by(CommentOptionVote.created_utc).all()
return render_template("poll_votes.html", return render_template("poll_votes.html",
v=v, v=v,
thing=option, thing=option,
ups=ups) ups=ups)

View File

@ -866,7 +866,7 @@ def submit_post(v, sub=None):
send_repeatable_notification(v.id, text) send_repeatable_notification(v.id, text)
v.ban(reason="Spamming.", v.ban(reason="Spamming.",
days=1) days=1)
for post in similar_posts + similar_urls: for post in similar_posts + similar_urls:
post.is_banned = True post.is_banned = True

View File

@ -171,15 +171,15 @@ def searchposts(v):
if request.headers.get("Authorization"): return {"total":total, "data":[x.json for x in posts]} if request.headers.get("Authorization"): return {"total":total, "data":[x.json for x in posts]}
return render_template("search.html", return render_template("search.html",
v=v, v=v,
query=query, query=query,
total=total, total=total,
page=page, page=page,
listing=posts, listing=posts,
sort=sort, sort=sort,
t=t, t=t,
next_exists=next_exists next_exists=next_exists
) )
@app.get("/search/comments") @app.get("/search/comments")
@auth_required @auth_required

View File

@ -131,15 +131,15 @@ def settings_profile_post(v):
if len(sig_html) > 1000: if len(sig_html) > 1000:
return render_template("settings_profile.html", return render_template("settings_profile.html",
v=v, v=v,
error="Your sig is too long") error="Your sig is too long")
v.sig = sig[:200] v.sig = sig[:200]
v.sig_html=sig_html v.sig_html=sig_html
g.db.add(v) g.db.add(v)
return render_template("settings_profile.html", return render_template("settings_profile.html",
v=v, v=v,
msg="Your sig has been updated.") msg="Your sig has been updated.")
@ -151,8 +151,8 @@ def settings_profile_post(v):
if len(friends_html) > 2000: if len(friends_html) > 2000:
return render_template("settings_profile.html", return render_template("settings_profile.html",
v=v, v=v,
error="Your friends list is too long") error="Your friends list is too long")
notify_users = NOTIFY_USERS(friends, v) notify_users = NOTIFY_USERS(friends, v)
@ -166,8 +166,8 @@ def settings_profile_post(v):
v.friends_html=friends_html v.friends_html=friends_html
g.db.add(v) g.db.add(v)
return render_template("settings_profile.html", return render_template("settings_profile.html",
v=v, v=v,
msg="Your friends list has been updated.") msg="Your friends list has been updated.")
elif FEATURES['USERS_PROFILE_BODYTEXT'] and request.values.get("enemies"): elif FEATURES['USERS_PROFILE_BODYTEXT'] and request.values.get("enemies"):
@ -177,8 +177,8 @@ def settings_profile_post(v):
if len(enemies_html) > 2000: if len(enemies_html) > 2000:
return render_template("settings_profile.html", return render_template("settings_profile.html",
v=v, v=v,
error="Your enemies list is too long") error="Your enemies list is too long")
notify_users = NOTIFY_USERS(enemies, v) notify_users = NOTIFY_USERS(enemies, v)
@ -192,8 +192,8 @@ def settings_profile_post(v):
v.enemies_html=enemies_html v.enemies_html=enemies_html
g.db.add(v) g.db.add(v)
return render_template("settings_profile.html", return render_template("settings_profile.html",
v=v, v=v,
msg="Your enemies list has been updated.") msg="Your enemies list has been updated.")
elif FEATURES['USERS_PROFILE_BODYTEXT'] and \ elif FEATURES['USERS_PROFILE_BODYTEXT'] and \
@ -208,8 +208,8 @@ def settings_profile_post(v):
if len(bio_html) > 10000: if len(bio_html) > 10000:
return render_template("settings_profile.html", return render_template("settings_profile.html",
v=v, v=v,
error="Your bio is too long") error="Your bio is too long")
if len(bio_html) > 10000: abort(400) if len(bio_html) > 10000: abort(400)
@ -217,8 +217,8 @@ def settings_profile_post(v):
v.bio_html=bio_html v.bio_html=bio_html
g.db.add(v) g.db.add(v)
return render_template("settings_profile.html", return render_template("settings_profile.html",
v=v, v=v,
msg="Your bio has been updated.") msg="Your bio has been updated.")
frontsize = request.values.get("frontsize") frontsize = request.values.get("frontsize")
@ -439,11 +439,11 @@ def settings_security_post(v):
link = url + params link = url + params
send_mail(to_address=new_email, send_mail(to_address=new_email,
subject="Verify your email address.", subject="Verify your email address.",
html=render_template("email/email_change.html", html=render_template("email/email_change.html",
action_url=link, action_url=link,
v=v) v=v)
) )
return render_template("settings_security.html", v=v, msg="Check your email and click the verification link to complete the email change.") return render_template("settings_security.html", v=v, msg="Check your email and click the verification link to complete the email change.")
@ -633,8 +633,8 @@ def settings_block_user(v):
return {"error": "You can't block this user."}, 409 return {"error": "You can't block this user."}, 409
new_block = UserBlock(user_id=v.id, new_block = UserBlock(user_id=v.id,
target_id=user.id, target_id=user.id,
) )
g.db.add(new_block) g.db.add(new_block)
if user.admin_level >= PERMS['USER_BLOCKS_VISIBLE']: if user.admin_level >= PERMS['USER_BLOCKS_VISIBLE']:
@ -706,17 +706,17 @@ def settings_name_change(v):
if new_name==v.username: if new_name==v.username:
return render_template("settings_profile.html", return render_template("settings_profile.html",
v=v, v=v,
error="You didn't change anything") error="You didn't change anything")
if not valid_username_regex.fullmatch(new_name): if not valid_username_regex.fullmatch(new_name):
return render_template("settings_profile.html", return render_template("settings_profile.html",
v=v, v=v,
error="This isn't a valid username.") error="This isn't a valid username.")
search_name = new_name.replace('\\', '').replace('_','\_').replace('%','') search_name = new_name.replace('\\', '').replace('_','\_').replace('%','')
x= g.db.query(User).filter( x = g.db.query(User).filter(
or_( or_(
User.username.ilike(search_name), User.username.ilike(search_name),
User.original_username.ilike(search_name) User.original_username.ilike(search_name)
@ -725,8 +725,8 @@ def settings_name_change(v):
if x and x.id != v.id: if x and x.id != v.id:
return render_template("settings_profile.html", return render_template("settings_profile.html",
v=v, v=v,
error=f"Username `{new_name}` is already in use.") error=f"Username `{new_name}` is already in use.")
v=get_account(v.id) v=get_account(v.id)
@ -840,8 +840,8 @@ def settings_song_change(v):
except Exception as e: except Exception as e:
print(e, flush=True) print(e, flush=True)
return render_template("settings_profile.html", return render_template("settings_profile.html",
v=v, v=v,
error="Age-restricted videos aren't allowed.") error="Age-restricted videos aren't allowed.")
files = os.listdir("/songs/") files = os.listdir("/songs/")
paths = [path.join("/songs/", basename) for basename in files] paths = [path.join("/songs/", basename) for basename in files]

View File

@ -206,11 +206,11 @@ def submit_contact(v):
body_html = sanitize(body) body_html = sanitize(body)
new_comment = Comment(author_id=v.id, new_comment = Comment(author_id=v.id,
parent_submission=None, parent_submission=None,
level=1, level=1,
body_html=body_html, body_html=body_html,
sentto=2 sentto=2
) )
g.db.add(new_comment) g.db.add(new_comment)
g.db.flush() g.db.flush()
new_comment.top_comment_id = new_comment.id new_comment.top_comment_id = new_comment.id
@ -342,7 +342,7 @@ def blocks(v):
targets = [] targets = []
for x in blocks: for x in blocks:
acc_user = get_account(x.user_id) acc_user = get_account(x.user_id)
acc_tgt = get_account(x.target_id) acc_tgt = get_account(x.target_id)
if acc_user.shadowbanned or acc_tgt.shadowbanned: continue if acc_user.shadowbanned or acc_tgt.shadowbanned: continue
users.append(acc_user) users.append(acc_user)
targets.append(acc_tgt) targets.append(acc_tgt)
@ -372,9 +372,9 @@ def serviceworker():
def settings_security(v): def settings_security(v):
return render_template("settings_security.html", return render_template("settings_security.html",
v=v, v=v,
mfa_secret=pyotp.random_base32() if not v.mfa_secret else None mfa_secret=pyotp.random_base32() if not v.mfa_secret else None
) )
@app.post("/dismiss_mobile_tip") @app.post("/dismiss_mobile_tip")

View File

@ -682,11 +682,11 @@ def message2(v, username):
if existing: return {"error": "Message already exists."}, 403 if existing: return {"error": "Message already exists."}, 403
c = Comment(author_id=v.id, c = Comment(author_id=v.id,
parent_submission=None, parent_submission=None,
level=1, level=1,
sentto=user.id, sentto=user.id,
body_html=body_html body_html=body_html
) )
g.db.add(c) g.db.add(c)
g.db.flush() g.db.flush()
@ -849,7 +849,7 @@ def is_available(name):
name2 = name.replace('\\', '').replace('_','\_').replace('%','') name2 = name.replace('\\', '').replace('_','\_').replace('%','')
x= g.db.query(User).filter( x = g.db.query(User).filter(
or_( or_(
User.username.ilike(name2), User.username.ilike(name2),
User.original_username.ilike(name2) User.original_username.ilike(name2)

View File

@ -51,10 +51,10 @@ def vote_info_get(v, link):
else: abort(400) else: abort(400)
return render_template("votes.html", return render_template("votes.html",
v=v, v=v,
thing=thing, thing=thing,
ups=ups, ups=ups,
downs=downs) downs=downs)
@app.post("/vote/post/<post_id>/<new>") @app.post("/vote/post/<post_id>/<new>")

View File

@ -89,17 +89,17 @@
</div> </div>
<div class="custom-control custom-switch"> <div class="custom-control custom-switch">
<input autocomplete="off" type="checkbox" class="custom-control-input" id="bots" {% if site_settings['Bots'] %}checked{% endif %} onchange="post_toast(this,'/admin/site_settings/Bots');"> <input autocomplete="off" type="checkbox" class="custom-control-input" id="bots" {% if site_settings['Bots'] %}checked{% endif %} onchange="post_toast(this,'/admin/site_settings/Bots');">
<label class="custom-control-label" for="bots">Bots</label> <label class="custom-control-label" for="bots">Bots</label>
</div> </div>
<div class="custom-control custom-switch"> <div class="custom-control custom-switch">
<input autocomplete="off" type="checkbox" class="custom-control-input" id="Fart mode" {% if site_settings['Fart mode'] %}checked{% endif %} onchange="post_toast(this,'/admin/site_settings/Fart mode');"> <input autocomplete="off" type="checkbox" class="custom-control-input" id="Fart mode" {% if site_settings['Fart mode'] %}checked{% endif %} onchange="post_toast(this,'/admin/site_settings/Fart mode');">
<label class="custom-control-label" for="Fart mode">Fart mode</label> <label class="custom-control-label" for="Fart mode">Fart mode</label>
</div> </div>
<div class="custom-control custom-switch"> <div class="custom-control custom-switch">
<input autocomplete="off" type="checkbox" class="custom-control-input" id="Read-only mode" {% if site_settings['Read-only mode'] %}checked{% endif %} onchange="post_toast(this,'/admin/site_settings/Read-only mode');"> <input autocomplete="off" type="checkbox" class="custom-control-input" id="Read-only mode" {% if site_settings['Read-only mode'] %}checked{% endif %} onchange="post_toast(this,'/admin/site_settings/Read-only mode');">
<label class="custom-control-label" for="Read-only mode">Read-only mode</label> <label class="custom-control-label" for="Read-only mode">Read-only mode</label>
</div> </div>

View File

@ -4,127 +4,250 @@
<!-- New --> <!-- New -->
<div class="casino-games"> <div class="casino-games">
<!-- Slots --> <!-- Slots -->
<div id="slots-block" class="casino-block"> <div id="slots-block" class="casino-block">
<div class="casino-block-title"> <div class="casino-block-title">
Slots Slots
<hr style="flex: 1; margin-left: 1rem" /> <hr style="flex: 1; margin-left: 1rem" />
</div> </div>
<div class="casino-block-inner"> <div class="casino-block-inner">
<div class="casino-block-left"> <div class="casino-block-left">
<!-- Game --> <!-- Game -->
<div class="casino-block-game"> <div class="casino-block-game">
<div> <div>
<div class="casino-slots-results" style="flex: 1"> <div class="casino-slots-results" style="flex: 1">
<div class="reel"> <div class="reel">
<img src="/i/rDrama/coins.webp?v=3009" alt="coin" /> <img src="/i/rDrama/coins.webp?v=3009" alt="coin" />
</div> </div>
<div class="reel"> <div class="reel">
<img src="/i/rDrama/coins.webp?v=3009" alt="coin" /> <img src="/i/rDrama/coins.webp?v=3009" alt="coin" />
</div> </div>
<div class="reel"> <div class="reel">
<img src="/i/rDrama/coins.webp?v=3009" alt="coin" /> <img src="/i/rDrama/coins.webp?v=3009" alt="coin" />
</div> </div>
</div> </div>
<div class="casino-slots-outcome" id="casinoSlotsResult"> <div class="casino-slots-outcome" id="casinoSlotsResult">
&nbsp; &nbsp;
</div> </div>
</div> </div>
</div> </div>
<!-- Bet --> <!-- Bet -->
<div class="casino-block-bet"> <div class="casino-block-bet">
<div class="lottery-page--stat"> <div class="lottery-page--stat">
<div class="lottery-page--stat-keys" style="margin-right: 1rem"> <div class="lottery-page--stat-keys" style="margin-right: 1rem">
<div>Enter Bet</div> <div>Enter Bet</div>
<div> <div>
<input <input
id="casinoSlotsBet" id="casinoSlotsBet"
class="form-control" class="form-control"
autocomplete="off" autocomplete="off"
value="100" value="100"
min="100" min="100"
step="1" step="1"
aria-label="Bet" aria-label="Bet"
name="casinoSlotsBet" name="casinoSlotsBet"
type="number" type="number"
style="flex: 1; max-width: 200px; text-align: right" style="flex: 1; max-width: 200px; text-align: right"
/> />
</div> </div>
</div> </div>
<div class="lottery-page--stat-values"> <div class="lottery-page--stat-values">
<div class="form-check"> <div class="form-check">
<input <input
class="form-check-input" class="form-check-input"
type="radio" type="radio"
name="casinoSlotsCurrency" name="casinoSlotsCurrency"
id="casinoSlotsCurrencyDramacoin" id="casinoSlotsCurrencyDramacoin"
value="dramacoin" value="dramacoin"
checked checked
/> />
<label <label
class="form-check-label" class="form-check-label"
for="casinoSlotsCurrencyDramacoin" for="casinoSlotsCurrencyDramacoin"
> >
<img <img
src="/i/rDrama/coins.webp?v=3009" src="/i/rDrama/coins.webp?v=3009"
alt="coin" alt="coin"
width="40" width="40"
data-bs-toggle="tooltip" data-bs-toggle="tooltip"
data-bs-placement="bottom" data-bs-placement="bottom"
title="Dramacoin" title="Dramacoin"
aria-label="Dramacoin" aria-label="Dramacoin"
/> />
</label> </label>
</div> </div>
<div class="form-check"> <div class="form-check">
<input <input
class="form-check-input" class="form-check-input"
type="radio" type="radio"
name="casinoSlotsCurrency" name="casinoSlotsCurrency"
id="casinoSlotsCurrencyMarseybux" id="casinoSlotsCurrencyMarseybux"
value="marseybux" value="marseybux"
/> />
<label <label
class="form-check-label" class="form-check-label"
for="casinoSlotsCurrencyMarseybux" for="casinoSlotsCurrencyMarseybux"
> >
<img <img
src="/i/marseybux.webp?v=2000" src="/i/marseybux.webp?v=2000"
alt="marseybux" alt="marseybux"
width="40" width="40"
data-bs-toggle="tooltip" data-bs-toggle="tooltip"
data-bs-placement="bottom" data-bs-placement="bottom"
title="Marseybux" title="Marseybux"
aria-label="Marseybux" aria-label="Marseybux"
/> />
</label> </label>
</div> </div>
</div> </div>
</div> </div>
<button <button
type="button" type="button"
class="btn btn-success lottery-page--action" class="btn btn-success lottery-page--action"
id="casinoSlotsPull" id="casinoSlotsPull"
style="width: 100%" style="width: 100%"
onclick="pullSlots()" onclick="pullSlots()"
> >
Pull Pull
</button> </button>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<!-- Blackjack --> <!-- Blackjack -->
<div id="blackjack-block" class="casino-block"> <div id="blackjack-block" class="casino-block">
<div class="casino-block-title"> <div class="casino-block-title">
Blackjack Blackjack
<hr style="flex: 1; margin-left: 1rem" /> <hr style="flex: 1; margin-left: 1rem" />
</div> </div>
<div>Give us a moment to fix things.</div> <div class="casino-block-inner">
</div> <div class="casino-block-left">
<!-- Game -->
<div class="casino-block-game">
<div class="casino-game">
<div style="flex: 1">
<div class="blackjack-table">
<div style="display: flex; align-items: center">
<small style="margin-right: 0.5rem">Dealer</small>
<hr style="flex: 1" />
</div>
<div class="hand" id="casinoBlackjackDealerHand">
<div class="playing-card" data-who="dealer"></div>
<div class="playing-card" data-who="dealer"></div>
<div class="playing-card" data-who="dealer"></div>
<div class="playing-card" data-who="dealer"></div>
<div class="playing-card" data-who="dealer"></div>
</div>
<hr />
<div class="hand" id="casinoBlackjackPlayerHand">
<div class="playing-card" data-who="player"></div>
<div class="playing-card" data-who="player"></div>
<div class="playing-card" data-who="player"></div>
<div class="playing-card" data-who="player"></div>
<div class="playing-card" data-who="player"></div>
</div>
<div style="display: flex; align-items: center">
<hr style="flex: 1; margin-right: 0.5rem" />
<small>Player</small>
</div>
</div>
<div
id="casinoBlackjackResult"
class="casino-blackjack-outcome"
></div>
</div>
</div>
</div>
<!-- Bet -->
<div class="casino-block-bet">
<div id="casinoBlackjackWager" class="lottery-page--stat">
<div class="lottery-page--stat-keys" style="margin-right: 1rem">
<div>Enter Bet</div>
<div>
<input
id="casinoBlackjackBet"
class="form-control"
autocomplete="off"
value="100"
min="100"
step="1"
aria-label="Bet"
name="casinoBlackjackBet"
type="number"
style="flex: 1; max-width: 200px; text-align: right"
/>
</div>
</div>
<div class="lottery-page--stat-values">
<div class="form-check">
<input
class="form-check-input"
type="radio"
name="casinoBlackjackCurrency"
id="casinoBlackjackCurrencyDramacoin"
value="dramacoin"
checked
/>
<label
class="form-check-label"
for="casinoBlackjackCurrencyDramacoin"
>
<img
src="/i/rDrama/coins.webp?v=3009"
alt="coin"
width="40"
data-bs-toggle="tooltip"
data-bs-placement="bottom"
title="Dramacoin"
aria-label="Dramacoin"
/>
</label>
</div>
<div class="form-check">
<input
class="form-check-input"
type="radio"
name="casinoBlackjackCurrency"
id="casinoBlackjackCurrencyMarseybux"
value="marseybux"
/>
<label
class="form-check-label"
for="casinoBlackjackCurrencyMarseybux"
>
<img
src="/i/marseybux.webp?v=2000"
alt="marseybux"
width="40"
data-bs-toggle="tooltip"
data-bs-placement="bottom"
title="Marseybux"
aria-label="Marseybux"
/>
</label>
</div>
</div>
</div>
<div id="casinoBlackjackActions" class="casino-blackjack-actions">
<button
type="button"
class="btn btn-success lottery-page--action"
id="casinoBlackjackDeal"
style="width: 100%"
onclick="dealBlackjack()"
>
Deal
</button>
</div>
</div>
</div>
</div>
</div>
<div class="casino-lottery">{% include "lottery.html" %}</div>
{% endblock %}
</div> </div>
<div class="casino-lottery">{% include "lottery.html" %}</div>
{% endblock %}

View File

@ -324,7 +324,7 @@
<span class="mr-2 arrow-up comment-{{c.id}}-up active"></span> <span class="mr-2 arrow-up comment-{{c.id}}-up active"></span>
{% endif %} {% endif %}
<span class="comment-mobile-score-{{c.id}} score comment-score-{{c.id}} {% if voted==1 %}score-up{% elif voted==-1%}score-down{% endif %}{% if c.controversial %} controversial{% endif %}"{% if not c.is_banned %} data-bs-toggle="tooltip" data-bs-placement="top" title="+{{ups}} | -{{downs}}"{% endif %}>{{score}}</span> <span class="comment-mobile-score-{{c.id}} score comment-score-{{c.id}} {% if voted==1 %}score-up{% elif voted==-1%}score-down{% endif %}{% if c.controversial %} controversial{% endif %}"{% if not c.is_banned %} data-bs-toggle="tooltip" data-bs-placement="top" title="+{{ups}} | -{{downs}}"{% endif %}>{{score}}</span>
{% if voted==-1 %} {% if voted==-1 %}
<span class="ml-2 my-0 arrow-down comment-{{c.id}}-down active"></span> <span class="ml-2 my-0 arrow-down comment-{{c.id}}-down active"></span>
@ -336,7 +336,7 @@
<span tabindex="0" role="button" onclick="vote('comment-mobile', '{{c.id}}', '1')" class="comment-mobile-{{c.id}}-up mx-0 pr-1 arrow-up upvote-button comment-{{c.id}}-up {% if voted==1 %}active{% endif %}"> <span tabindex="0" role="button" onclick="vote('comment-mobile', '{{c.id}}', '1')" class="comment-mobile-{{c.id}}-up mx-0 pr-1 arrow-up upvote-button comment-{{c.id}}-up {% if voted==1 %}active{% endif %}">
</span> </span>
<span class="comment-mobile-score-{{c.id}} score comment-score-{{c.id}} {% if voted==1 %}score-up{% elif voted==-1%}score-down{% endif %}{% if c.controversial %} controversial{% endif %}"{% if not c.is_banned %} data-bs-toggle="tooltip" data-bs-placement="top" title="+{{ups}} | -{{downs}}"{% endif %}>{{score}}</span> <span class="comment-mobile-score-{{c.id}} score comment-score-{{c.id}} {% if voted==1 %}score-up{% elif voted==-1%}score-down{% endif %}{% if c.controversial %} controversial{% endif %}"{% if not c.is_banned %} data-bs-toggle="tooltip" data-bs-placement="top" title="+{{ups}} | -{{downs}}"{% endif %}>{{score}}</span>
<span {% if DISABLE_DOWNVOTES %}style="display:None!important"{% endif %} tabindex="0" role="button" onclick="vote('comment-mobile', '{{c.id}}', '-1')" class="comment-mobile-{{c.id}}-down mx-0 pl-1 my-0 arrow-down downvote-button comment-{{c.id}}-down {% if voted==-1 %}active{% endif %}"> <span {% if DISABLE_DOWNVOTES %}style="display:None!important"{% endif %} tabindex="0" role="button" onclick="vote('comment-mobile', '{{c.id}}', '-1')" class="comment-mobile-{{c.id}}-down mx-0 pl-1 my-0 arrow-down downvote-button comment-{{c.id}}-down {% if voted==-1 %}active{% endif %}">
</span> </span>
@ -348,7 +348,7 @@
<i class="fas fa-arrow-alt-up mx-0" aria-hidden="true"></i> <i class="fas fa-arrow-alt-up mx-0" aria-hidden="true"></i>
</span> </span>
<span class="comment-mobile-score-{{c.id}} score{% if c.controversial %} controversial{% endif %}"{% if not c.is_banned %} data-bs-toggle="tooltip" data-bs-placement="top" title="+{{ups}} | -{{downs}}"{% endif %}>{{score}}</span> <span class="comment-mobile-score-{{c.id}} score{% if c.controversial %} controversial{% endif %}"{% if not c.is_banned %} data-bs-toggle="tooltip" data-bs-placement="top" title="+{{ups}} | -{{downs}}"{% endif %}>{{score}}</span>
<span tabindex="0" class="arrow-{{c.id}}-mobile-down arrow-mobile-down mx-0 pl-1 my-0" onclick="location.href='/login';"> <span tabindex="0" class="arrow-{{c.id}}-mobile-down arrow-mobile-down mx-0 pl-1 my-0" onclick="location.href='/login';">
<i class="fas fa-arrow-alt-down mx-0" aria-hidden="true"></i> <i class="fas fa-arrow-alt-down mx-0" aria-hidden="true"></i>
@ -383,7 +383,7 @@
{% endif %} {% endif %}
<button class="btn caction nobackground p-0 m-0"> <button class="btn caction nobackground p-0 m-0">
<span data-bs-toggle="tooltip" data-bs-placement="top" title="+{{ups}} | -{{downs}}" class="comment-score-{{c.id}} score comment-score-{{c.id}} {% if voted==1 %}score-up{% elif voted==-1%}score-down{% endif %}{% if c.controversial %} controversial{% endif %}">{{score}}</span> <span data-bs-toggle="tooltip" data-bs-placement="top" title="+{{ups}} | -{{downs}}" class="comment-score-{{c.id}} score comment-score-{{c.id}} {% if voted==1 %}score-up{% elif voted==-1%}score-down{% endif %}{% if c.controversial %} controversial{% endif %}">{{score}}</span>
</button> </button>
{% if v and request.path.startswith('/@') and v.admin_level < 2 %} {% if v and request.path.startswith('/@') and v.admin_level < 2 %}
@ -436,7 +436,7 @@
<ul class="dropdown-menu"> <ul class="dropdown-menu">
{% if v.admin_level and v.id==c.author_id %} {% if v.admin_level and v.id==c.author_id %}
<button id="undistinguish-{{c.id}}" class="dropdown-item list-inline-item d-none {% if c.distinguish_level %}d-md-block{% endif %} text-info" onclick="post_toast(this,'/distinguish_comment/{{c.id}}','distinguish-{{c.id}}','undistinguish-{{c.id}}','d-md-block')"><i class="fas fa-id-badge text-info fa-fw"></i>Undistinguish</button> <button id="undistinguish-{{c.id}}" class="dropdown-item list-inline-item d-none {% if c.distinguish_level %}d-md-block{% endif %} text-info" onclick="post_toast(this,'/distinguish_comment/{{c.id}}','distinguish-{{c.id}}','undistinguish-{{c.id}}','d-md-block')"><i class="fas fa-id-badge text-info fa-fw"></i>Undistinguish</button>
<button id="distinguish-{{c.id}}" class="dropdown-item list-inline-item d-none {% if not c.distinguish_level %}d-md-block{% endif %} text-info" onclick="post_toast(this,'/distinguish_comment/{{c.id}}','distinguish-{{c.id}}','undistinguish-{{c.id}}','d-md-block')"><i class="fas fa-id-badge text-info fa-fw"></i>Distinguish</button> <button id="distinguish-{{c.id}}" class="dropdown-item list-inline-item d-none {% if not c.distinguish_level %}d-md-block{% endif %} text-info" onclick="post_toast(this,'/distinguish_comment/{{c.id}}','distinguish-{{c.id}}','undistinguish-{{c.id}}','d-md-block')"><i class="fas fa-id-badge text-info fa-fw"></i>Distinguish</button>
{% endif %} {% endif %}
@ -458,7 +458,7 @@
{% endif %} {% endif %}
{% if url != "" %} {% if url != "" %}
<button id="unpin-{{c.id}}" class="dropdown-item list-inline-item {% if c.stickied %}d-md-block{% endif %} text-muted d-none text-info" data-bs-dismiss="modal" data-bs-target="#actionsModal-{{c.id}}" onclick="post_toast(this,'/un{{url}}/{{c.id}}','pin-{{c.id}}','unpin-{{c.id}}','d-md-block')"><i class="fas fa-thumbtack fa-rotate--45 text-info fa-fw"></i>Unpin</button> <button id="unpin-{{c.id}}" class="dropdown-item list-inline-item {% if c.stickied %}d-md-block{% endif %} text-muted d-none text-info" data-bs-dismiss="modal" data-bs-target="#actionsModal-{{c.id}}" onclick="post_toast(this,'/un{{url}}/{{c.id}}','pin-{{c.id}}','unpin-{{c.id}}','d-md-block')"><i class="fas fa-thumbtack fa-rotate--45 text-info fa-fw"></i>Unpin</button>
<button id="pin-{{c.id}}" class="dropdown-item list-inline-item {% if not c.stickied %}d-md-block{% endif %} text-muted d-none text-info" data-bs-dismiss="modal" data-bs-target="#actionsModal-{{c.id}}" onclick="post_toast(this,'/{{url}}/{{c.id}}','pin-{{c.id}}','unpin-{{c.id}}','d-md-block')"><i class="fas fa-thumbtack fa-rotate--45 text-info fa-fw"></i>Pin</button> <button id="pin-{{c.id}}" class="dropdown-item list-inline-item {% if not c.stickied %}d-md-block{% endif %} text-muted d-none text-info" data-bs-dismiss="modal" data-bs-target="#actionsModal-{{c.id}}" onclick="post_toast(this,'/{{url}}/{{c.id}}','pin-{{c.id}}','unpin-{{c.id}}','d-md-block')"><i class="fas fa-thumbtack fa-rotate--45 text-info fa-fw"></i>Pin</button>
{% endif %} {% endif %}

View File

@ -104,7 +104,7 @@ Text 2
<tr> <tr>
<td>Pat Emojis</td> <td>Pat Emojis</td>
<td>:marseylovepat:</td> <td>:marseylovepat:</td>
<td><span alt=":marseylovepat:" data-bs-toggle="tooltip" title=":marseylovepat:"><img src="/i/hand.webp"><img alt=":marseylovepat:" b="" loading="lazy" pat="" src="/e/marseylove.webp"></span></td> <td><span alt=":marseylovepat:" data-bs-toggle="tooltip" title=":marseylovepat:"><img src="/i/hand.webp"><img alt=":marseylovepat:" b="" loading="lazy" pat="" src="/e/marseylove.webp"></span></td>
</tr> </tr>
<tr> <tr>
<td>Pat User</td> <td>Pat User</td>
@ -412,7 +412,7 @@ Text 2
Text in a pre element Text in a pre element
is displayed in a fixed-width is displayed in a fixed-width
font, and it preserves font, and it preserves
both spaces and both spaces and
line breaks line breaks
&lt;/pre&gt; &lt;/pre&gt;
</pre></td> </pre></td>
@ -421,7 +421,7 @@ Text 2
Text in a pre element Text in a pre element
is displayed in a fixed-width is displayed in a fixed-width
font, and it preserves font, and it preserves
both spaces and both spaces and
line breaks line breaks
</pre> </pre>
</td> </td>

View File

@ -106,7 +106,7 @@
{% if v.notifications_count %} {% if v.notifications_count %}
<a class="mobile-nav-icon d-md-none" href="/notifications{% if v.notifications_do %}/{{v.notifications_do}}{% endif %}" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Notifications"><i class="fas fa-bell align-middle" style="color: {{v.notifications_color}}"></i><span class="notif-count ml-1" style="padding-left: 4.5px;{% if v.notifications_do %}background:{{v.notifications_color}}{% endif %}">{{v.notifications_count}}</span></a> <a class="mobile-nav-icon d-md-none" href="/notifications{% if v.notifications_do %}/{{v.notifications_do}}{% endif %}" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Notifications"><i class="fas fa-bell align-middle" style="color: {{v.notifications_color}}"></i><span class="notif-count ml-1" style="padding-left: 4.5px;{% if v.notifications_do %}background:{{v.notifications_color}}{% endif %}">{{v.notifications_count}}</span></a>
{% else %} {% else %}
<a class="mobile-nav-icon d-md-none" href="/notifications" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Notifications"><i class="fas fa-bell align-middle text-gray-500 black"></i></a> <a class="mobile-nav-icon d-md-none" href="/notifications" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Notifications"><i class="fas fa-bell align-middle text-gray-500 black"></i></a>
{% endif %} {% endif %}
{% endif %} {% endif %}

View File

@ -1,189 +1,189 @@
<div> <div>
<div class="lottery-page--wrapper"> <div class="lottery-page--wrapper">
<div class="lottery-page--image"> <div class="lottery-page--image">
<img src="/i/{{SITE_NAME}}/lottery.webp?v=2000" /> <img src="/i/{{SITE_NAME}}/lottery.webp?v=2000" />
<img <img
id="lotteryTicketPulled" id="lotteryTicketPulled"
src="/i/{{SITE_NAME}}/lottery_active.webp?v=2000" src="/i/{{SITE_NAME}}/lottery_active.webp?v=2000"
style="display: none" style="display: none"
/> />
</div> </div>
<div class="lottery-page--stats"> <div class="lottery-page--stats">
{% if v.admin_level > 2 %} {% if v.admin_level > 2 %}
<div <div
class="lottery-page--stat" class="lottery-page--stat"
style="position: relative; padding-top: 1rem; overflow: hidden" style="position: relative; padding-top: 1rem; overflow: hidden"
> >
<i <i
class="fas fa-broom" class="fas fa-broom"
style=" style="
position: absolute; position: absolute;
bottom: -4px; bottom: -4px;
right: -4px; right: -4px;
font-size: 50px; font-size: 50px;
color: var(--gray-600); color: var(--gray-600);
" "
> >
</i> </i>
<button <button
type="button" type="button"
class="btn btn-danger" class="btn btn-danger"
id="endLotterySession" id="endLotterySession"
style="display: none" style="display: none"
onclick="endLotterySession()" onclick="endLotterySession()"
> >
End Current Session End Current Session
</button> </button>
<button <button
type="button" type="button"
class="btn btn-success" class="btn btn-success"
id="startLotterySession" id="startLotterySession"
style="display: none" style="display: none"
onclick="startLotterySession()" onclick="startLotterySession()"
> >
Start New Session Start New Session
</button> </button>
</div> </div>
{% endif %} {% endif %}
<div class="lottery-page--stat"> <div class="lottery-page--stat">
<div class="lottery-page--stat-keys"> <div class="lottery-page--stat-keys">
<div>Prize</div> <div>Prize</div>
<div>Time Left</div> <div>Time Left</div>
<div>Tickets Sold</div> <div>Tickets Sold</div>
<div>Participants</div> <div>Participants</div>
</div> </div>
<div class="lottery-page--stat-values"> <div class="lottery-page--stat-values">
<div> <div>
<img <img
id="prize-image" id="prize-image"
alt="coins" alt="coins"
class="mr-1 ml-1" class="mr-1 ml-1"
data-bs-toggle="tooltip" data-bs-toggle="tooltip"
data-bs-placement="bottom" data-bs-placement="bottom"
height="13" height="13"
src="{{asset_siteimg('coins.webp')}}" src="{{asset_siteimg('coins.webp')}}"
aria-label="coins" aria-label="coins"
title="coins" title="coins"
style="display: none; position: relative; top: -2px" style="display: none; position: relative; top: -2px"
/> />
<span id="prize">-</span> <span id="prize">-</span>
</div> </div>
<div id="timeLeft">-</div> <div id="timeLeft">-</div>
<div id="ticketsSoldThisSession">-</div> <div id="ticketsSoldThisSession">-</div>
<div id="participantsThisSession">-</div> <div id="participantsThisSession">-</div>
</div> </div>
</div> </div>
<div class="lottery-page--stat"> <div class="lottery-page--stat">
<div class="lottery-page--stat-keys"> <div class="lottery-page--stat-keys">
<div>Your Held Tickets</div> <div>Your Held Tickets</div>
<div>Lifetime Held Tickets</div> <div>Lifetime Held Tickets</div>
<div>Lifetime Winnings</div> <div>Lifetime Winnings</div>
</div> </div>
<div class="lottery-page--stat-values"> <div class="lottery-page--stat-values">
<div id="ticketsHeldCurrent">-</div> <div id="ticketsHeldCurrent">-</div>
<div id="ticketsHeldTotal">-</div> <div id="ticketsHeldTotal">-</div>
<div id="winnings">-</div> <div id="winnings">-</div>
</div> </div>
</div> </div>
<div class="lottery-page--stat"> <div class="lottery-page--stat">
<div class="lottery-page--stat-keys"> <div class="lottery-page--stat-keys">
<div>Purchase Quantity</div> <div>Purchase Quantity</div>
</div> </div>
<div class="lottery-page--stat-values"> <div class="lottery-page--stat-values">
<div> <div>
<input <input
id="ticketPurchaseQuantity" id="ticketPurchaseQuantity"
class="form-control" class="form-control"
autocomplete="off" autocomplete="off"
value="1" value="1"
min="1" min="1"
step="1" step="1"
aria-label="Quantity" aria-label="Quantity"
name="ticketPurchaseQuantity" name="ticketPurchaseQuantity"
type="number" type="number"
style="flex: 1; max-width: 100px; text-align: center" style="flex: 1; max-width: 100px; text-align: center"
/> />
</div> </div>
</div> </div>
</div> </div>
<button <button
type="button" type="button"
class="btn btn-success lottery-page--action" class="btn btn-success lottery-page--action"
id="purchaseTicket" id="purchaseTicket"
onclick="purchaseLotteryTicket()" onclick="purchaseLotteryTicket()"
> >
Purchase <span id="totalQuantityOfTickets">1</span> for Purchase <span id="totalQuantityOfTickets">1</span> for
<img <img
alt="coins" alt="coins"
class="mr-1 ml-1" class="mr-1 ml-1"
data-bs-toggle="tooltip" data-bs-toggle="tooltip"
data-bs-placement="bottom" data-bs-placement="bottom"
height="13" height="13"
src="{{asset_siteimg('coins.webp')}}" src="{{asset_siteimg('coins.webp')}}"
aria-label="coins" aria-label="coins"
title="coins" title="coins"
/> />
<span id="totalCostOfTickets">12</span> <span id="totalCostOfTickets">12</span>
</button> </button>
</div> </div>
<!-- Success --> <!-- Success -->
<div <div
class="toast" class="toast"
id="lottery-post-success" id="lottery-post-success"
style=" style="
position: fixed; position: fixed;
bottom: 1.5rem; bottom: 1.5rem;
margin: 0 auto; margin: 0 auto;
left: 0; left: 0;
right: 0; right: 0;
width: unset; width: unset;
z-index: 1000; z-index: 1000;
height: auto !important; height: auto !important;
" "
role="alert" role="alert"
aria-live="assertive" aria-live="assertive"
aria-atomic="true" aria-atomic="true"
data-bs-animation="true" data-bs-animation="true"
data-bs-autohide="true" data-bs-autohide="true"
data-bs-delay="5000" data-bs-delay="5000"
> >
<div class="toast-body bg-success text-center text-white"> <div class="toast-body bg-success text-center text-white">
<i class="fas fa-comment-alt-smile mr-2"></i <i class="fas fa-comment-alt-smile mr-2"></i
><span id="lottery-post-success-text"></span> ><span id="lottery-post-success-text"></span>
</div> </div>
</div> </div>
<!-- Error --> <!-- Error -->
<div <div
class="toast" class="toast"
id="lottery-post-error" id="lottery-post-error"
style=" style="
position: fixed; position: fixed;
bottom: 1.5rem; bottom: 1.5rem;
margin: 0 auto; margin: 0 auto;
left: 0; left: 0;
right: 0; right: 0;
width: unset; width: unset;
z-index: 1000; z-index: 1000;
height: auto !important; height: auto !important;
" "
role="alert" role="alert"
aria-live="assertive" aria-live="assertive"
aria-atomic="true" aria-atomic="true"
data-bs-animation="true" data-bs-animation="true"
data-bs-autohide="true" data-bs-autohide="true"
data-bs-delay="5000" data-bs-delay="5000"
> >
<div class="toast-body bg-danger text-center text-white"> <div class="toast-body bg-danger text-center text-white">
<i class="fas fa-exclamation-circle mr-2"></i <i class="fas fa-exclamation-circle mr-2"></i
><span id="lottery-post-error-text"></span> ><span id="lottery-post-error-text"></span>
</div> </div>
</div> </div>
</div> </div>
<script src="{{asset('js/lottery.js')}}"></script> <script src="{{asset('js/lottery.js')}}"></script>
</div> </div>

View File

@ -13,7 +13,7 @@
{% block content %} {% block content %}
<div class="row border-bottom bg-white w-200 pr-0" style="overflow: visible;"> <div class="row border-bottom bg-white w-200 pr-0" style="overflow: visible;">
<div class="col p-0 w-100"> <div class="col p-0 w-100">
<ul class="nav settings-nav" style="padding:0 0 0 20px" id="notifications--nav-list"> <ul class="nav settings-nav" style="padding:0 0 0 20px" id="notifications--nav-list">
<li class="nav-item"> <li class="nav-item">
<a class="nav-link py-3{% if request.path == '/notifications' %} active{% endif %}" href="/notifications"> <a class="nav-link py-3{% if request.path == '/notifications' %} active{% endif %}" href="/notifications">
@ -52,7 +52,7 @@
</li> </li>
{% endif %} {% endif %}
</ul> </ul>
</div> </div>
</div> </div>
<a class="btn btn-primary btn-rainbow ml-3 mt-4" role="button" onclick="post_toast(this,'/clear', true)">Mark all notifications as read</a> <a class="btn btn-primary btn-rainbow ml-3 mt-4" role="button" onclick="post_toast(this,'/clear', true)">Mark all notifications as read</a>

View File

@ -3,7 +3,7 @@
<img loading="lazy" class="pop-banner w-100 h-64 object-cover"> <img loading="lazy" class="pop-banner w-100 h-64 object-cover">
<div class="d-flex align-items-end px-3 mt-n6 mb-3"> <div class="d-flex align-items-end px-3 mt-n6 mb-3">
<div class="profile-pic-72-wrapper"> <div class="profile-pic-72-wrapper">
<img loading="lazy" class="pop-picture avatar-72 rounded img-thumbnail shadow-sm"> <img loading="lazy" class="pop-picture avatar-72 rounded img-thumbnail shadow-sm">
<img class="profile-pic-72-hat hat" loading="lazy" data-bs-toggle="tooltip" data-bs-placement="bottom"> <img class="profile-pic-72-hat hat" loading="lazy" data-bs-toggle="tooltip" data-bs-placement="bottom">
</div> </div>
<div class="px-3 text-truncate"> <div class="px-3 text-truncate">

View File

@ -36,7 +36,7 @@
<button id="undelete-{{p.id}}" class="{% if not p.deleted_utc %}d-none{% endif %} nobackground btn btn-link btn-block btn-lg text-left text-success" role="button" onclick="post_toast(this,'/undelete_post/{{p.id}}', 'delete-{{p.id}}', 'undelete-{{p.id}}','d-none');document.getElementById('post-{{p.id}}').classList.remove('deleted')" data-bs-dismiss="modal"><i class="far fa-trash-alt text-center mr-2"></i>Undelete</button> <button id="undelete-{{p.id}}" class="{% if not p.deleted_utc %}d-none{% endif %} nobackground btn btn-link btn-block btn-lg text-left text-success" role="button" onclick="post_toast(this,'/undelete_post/{{p.id}}', 'delete-{{p.id}}', 'undelete-{{p.id}}','d-none');document.getElementById('post-{{p.id}}').classList.remove('deleted')" data-bs-dismiss="modal"><i class="far fa-trash-alt text-center mr-2"></i>Undelete</button>
<button id="delete-{{p.id}}" class="{% if p.deleted_utc %}d-none{% endif %} nobackground btn btn-link btn-block btn-lg text-left text-danger" data-bs-toggle="modal" data-bs-dismiss="modal" data-bs-target="#deletePostModal" onclick="delete_postModal('{{p.id}}')"><i class="far fa-trash-alt mr-2"></i>Delete</button> <button id="delete-{{p.id}}" class="{% if p.deleted_utc %}d-none{% endif %} nobackground btn btn-link btn-block btn-lg text-left text-danger" data-bs-toggle="modal" data-bs-dismiss="modal" data-bs-target="#deletePostModal" onclick="delete_postModal('{{p.id}}')"><i class="far fa-trash-alt mr-2"></i>Delete</button>
{% if FEATURES['COUNTRY_CLUB'] -%} {% if FEATURES['COUNTRY_CLUB'] -%}

View File

@ -28,14 +28,14 @@
<button id="pin2-{{p.id}}" class="{% if p.stickied %}d-none{% endif %} nobackground btn btn-link btn-block btn-lg text-left text-primary" role="button" onclick="post_toast(this,'/sticky/{{p.id}}','pin2-{{p.id}}','unpin2-{{p.id}}','d-none')" data-bs-dismiss="modal"><i class="fas fa-thumbtack fa-rotate--45 text-center text-primary mr-2"></i>Pin</button> <button id="pin2-{{p.id}}" class="{% if p.stickied %}d-none{% endif %} nobackground btn btn-link btn-block btn-lg text-left text-primary" role="button" onclick="post_toast(this,'/sticky/{{p.id}}','pin2-{{p.id}}','unpin2-{{p.id}}','d-none')" data-bs-dismiss="modal"><i class="fas fa-thumbtack fa-rotate--45 text-center text-primary mr-2"></i>Pin</button>
<button id="unpin2-{{p.id}}" class="{% if not p.stickied %}d-none{% endif %} nobackground btn btn-link btn-block btn-lg text-left text-primary" role="button" onclick="post_toast(this,'/unsticky/{{p.id}}','pin2-{{p.id}}','unpin2-{{p.id}}','d-none')" data-bs-dismiss="modal"><i class="fas fa-thumbtack fa-rotate--45 text-center text-primary mr-2"></i>Unpin</button> <button id="unpin2-{{p.id}}" class="{% if not p.stickied %}d-none{% endif %} nobackground btn btn-link btn-block btn-lg text-left text-primary" role="button" onclick="post_toast(this,'/unsticky/{{p.id}}','pin2-{{p.id}}','unpin2-{{p.id}}','d-none')" data-bs-dismiss="modal"><i class="fas fa-thumbtack fa-rotate--45 text-center text-primary mr-2"></i>Unpin</button>
{% if "/reported/" in request.path %} {% if "/reported/" in request.path %}
<button class="nobackground btn btn-link btn-block btn-lg text-danger text-left" role="button" onclick="post_toast(this,'/remove_post/{{p.id}}')" data-bs-dismiss="modal"><i class="far fa-ban text-center mr-2"></i>Remove</button> <button class="nobackground btn btn-link btn-block btn-lg text-danger text-left" role="button" onclick="post_toast(this,'/remove_post/{{p.id}}')" data-bs-dismiss="modal"><i class="far fa-ban text-center mr-2"></i>Remove</button>
<button class="nobackground btn btn-link btn-block btn-lg text-success text-left" role="button" onclick="post_toast(this,'/approve_post/{{p.id}}')" data-bs-dismiss="modal"><i class="far fa-check text-center mr-2"></i>Approve</button> <button class="nobackground btn btn-link btn-block btn-lg text-success text-left" role="button" onclick="post_toast(this,'/approve_post/{{p.id}}')" data-bs-dismiss="modal"><i class="far fa-check text-center mr-2"></i>Approve</button>
{% else %} {% else %}
<button id="remove2-{{p.id}}" class="{% if p.is_banned %}d-none{% endif %} nobackground btn btn-link btn-block btn-lg text-danger text-left" role="button" onclick="post_toast(this,'/remove_post/{{p.id}}','remove2-{{p.id}}','approve2-{{p.id}}','d-none')" data-bs-dismiss="modal"><i class="far fa-ban text-center mr-2"></i>Remove</button> <button id="remove2-{{p.id}}" class="{% if p.is_banned %}d-none{% endif %} nobackground btn btn-link btn-block btn-lg text-danger text-left" role="button" onclick="post_toast(this,'/remove_post/{{p.id}}','remove2-{{p.id}}','approve2-{{p.id}}','d-none')" data-bs-dismiss="modal"><i class="far fa-ban text-center mr-2"></i>Remove</button>
<button id="approve2-{{p.id}}" class="{% if not p.is_banned %}d-none{% endif %} nobackground btn btn-link btn-block btn-lg text-success text-left" role="button" onclick="post_toast(this,'/approve_post/{{p.id}}','remove2-{{p.id}}','approve2-{{p.id}}','d-none')" data-bs-dismiss="modal"><i class="far fa-check text-center mr-2"></i>Approve</button> <button id="approve2-{{p.id}}" class="{% if not p.is_banned %}d-none{% endif %} nobackground btn btn-link btn-block btn-lg text-success text-left" role="button" onclick="post_toast(this,'/approve_post/{{p.id}}','remove2-{{p.id}}','approve2-{{p.id}}','d-none')" data-bs-dismiss="modal"><i class="far fa-check text-center mr-2"></i>Approve</button>
{% endif %} {% endif %}
{% if p.oauth_app %} {% if p.oauth_app %}

View File

@ -181,10 +181,10 @@
<a class="nav-link{% if '/posts' in request.path %} active{% endif %}" href="/search/posts/?sort={{sort}}&q={{query | urlencode}}&t={{t}}">Posts</a> <a class="nav-link{% if '/posts' in request.path %} active{% endif %}" href="/search/posts/?sort={{sort}}&q={{query | urlencode}}&t={{t}}">Posts</a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link{% if '/comments' in request.path %} active{% endif %}" href="/search/comments/?sort={{sort}}&q={{query | urlencode}}&t={{t}}">Comments</a> <a class="nav-link{% if '/comments' in request.path %} active{% endif %}" href="/search/comments/?sort={{sort}}&q={{query | urlencode}}&t={{t}}">Comments</a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link{% if '/users' in request.path %} active{% endif %}" href="/search/users/?sort={{sort}}&q={{query | urlencode}}&t={{t}}">Users</a> <a class="nav-link{% if '/users' in request.path %} active{% endif %}" href="/search/users/?sort={{sort}}&q={{query | urlencode}}&t={{t}}">Users</a>
</li> </li>
</ul> </ul>
</div> </div>

View File

@ -166,7 +166,7 @@
</div> </div>
{% if request.path == '/settings/security' %} {% if request.path == '/settings/security' %}
<div class="modal fade" id="2faModal" tabindex="-1" role="dialog" aria-labelledby="2faModalTitle" aria-hidden="true"> <div class="modal fade" id="2faModal" tabindex="-1" role="dialog" aria-labelledby="2faModalTitle" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document"> <div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content"> <div class="modal-content">

View File

@ -44,7 +44,7 @@
{% endif %} {% endif %}
<div class="input-group mb2"> <div class="input-group mb2">
<select {% if v.coins < cost and v.procoins < cost or v.bite %}disabled{% endif %} autocomplete="off" id='house' class="form-control" form="profile-settings" name="house" onchange="post_toast(this,'/settings/profile?house='+document.getElementById('house').value, true)"> <select {% if v.coins < cost and v.procoins < cost or v.bite %}disabled{% endif %} autocomplete="off" id='house' class="form-control" form="profile-settings" name="house" onchange="post_toast(this,'/settings/profile?house='+document.getElementById('house').value, true)">
{% for entry in ("None","Furry","Femboy","Vampire","Racist") %} {% for entry in ("None","Furry","Femboy","Vampire","Racist") %}
<option value="{{entry}}" {% if v.house==entry %} selected {% endif %}> <option value="{{entry}}" {% if v.house==entry %} selected {% endif %}>
{{entry}} {{entry}}
</option> </option>
@ -490,7 +490,7 @@
<input type="hidden" name="formkey" value="{{v.formkey}}"> <input type="hidden" name="formkey" value="{{v.formkey}}">
<input maxlength=100 {% if v.flairchanged %}disabled{% endif %} autocomplete="off" id="customtitlebody" type="text" name="title" class="form-control" placeholder='Enter a flair here' value="{% if v.customtitleplain %}{{v.customtitleplain}}{% endif %}"> <input maxlength=100 {% if v.flairchanged %}disabled{% endif %} autocomplete="off" id="customtitlebody" type="text" name="title" class="form-control" placeholder='Enter a flair here' value="{% if v.customtitleplain %}{{v.customtitleplain}}{% endif %}">
<div class="d-flex mt-2"> <div class="d-flex mt-2">
<a class="format" role="button"><i class="btn btn-secondary format d-inline-block m-0 fas fa-kiss-wink-heart" onclick="loadEmojis('customtitlebody')" aria-hidden="true" data-bs-toggle="modal" data-bs-target="#emojiModal" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Add Emoji"></i></a> <a class="format" role="button"><i class="btn btn-secondary format d-inline-block m-0 fas fa-kiss-wink-heart" onclick="loadEmojis('customtitlebody')" aria-hidden="true" data-bs-toggle="modal" data-bs-target="#emojiModal" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Add Emoji"></i></a>
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;
<small>Limit of 100 characters</small> <small>Limit of 100 characters</small>
<input {% if v.flairchanged %}disabled{% endif %} autocomplete="off" class="btn btn-primary ml-auto" id="titleSave" type="submit" onclick="disable(this)" value="Change Flair"> <input {% if v.flairchanged %}disabled{% endif %} autocomplete="off" class="btn btn-primary ml-auto" id="titleSave" type="submit" onclick="disable(this)" value="Change Flair">
@ -610,7 +610,7 @@
<div class="w-lg-100"> <div class="w-lg-100">
<form id="profile-bio" action="/settings/profile" method="post" enctype="multipart/form-data"> <form id="profile-bio" action="/settings/profile" method="post" enctype="multipart/form-data">
<input type="hidden" name="formkey" value="{{v.formkey}}"> <input type="hidden" name="formkey" value="{{v.formkey}}">
<textarea autocomplete="off" id="bio-text" class="form-control rounded" aria-label="With textarea" placeholder="Tell the community a bit about yourself." rows="3" name="bio" form="profile-bio" maxlength="1500">{% if v.bio %}{{v.bio}}{% endif %}</textarea> <textarea autocomplete="off" id="bio-text" class="form-control rounded" aria-label="With textarea" placeholder="Tell the community a bit about yourself." rows="3" name="bio" form="profile-bio" maxlength="1500">{% if v.bio %}{{v.bio}}{% endif %}</textarea>
<div class="d-flex"> <div class="d-flex">
<pre style="padding-top:0.7rem" class="btn btn-secondary format d-inline-block m-0 fas fa-bold" aria-hidden="true" onclick="makeBold('bio-text')" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Bold"></pre> <pre style="padding-top:0.7rem" class="btn btn-secondary format d-inline-block m-0 fas fa-bold" aria-hidden="true" onclick="makeBold('bio-text')" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Bold"></pre>
&nbsp; &nbsp;
@ -645,7 +645,7 @@
<div class="w-lg-100"> <div class="w-lg-100">
<form id="profile-friends" action="/settings/profile" method="post" enctype="multipart/form-data"> <form id="profile-friends" action="/settings/profile" method="post" enctype="multipart/form-data">
<input type="hidden" name="formkey" value="{{v.formkey}}"> <input type="hidden" name="formkey" value="{{v.formkey}}">
<textarea autocomplete="off" id="friends-text" class="form-control rounded" aria-label="With textarea" placeholder="Enter your friends on the site..." rows="3" name="friends" form="profile-friends" maxlength="1500">{% if v.friends %}{{v.friends}}{% endif %}</textarea> <textarea autocomplete="off" id="friends-text" class="form-control rounded" aria-label="With textarea" placeholder="Enter your friends on the site..." rows="3" name="friends" form="profile-friends" maxlength="1500">{% if v.friends %}{{v.friends}}{% endif %}</textarea>
<pre></pre> <pre></pre>
<div class="d-flex"> <div class="d-flex">
<small>Limit of 500 characters</small> <small>Limit of 500 characters</small>
@ -664,7 +664,7 @@
<div class="w-lg-100"> <div class="w-lg-100">
<form id="profile-enemies" action="/settings/profile" method="post" enctype="multipart/form-data"> <form id="profile-enemies" action="/settings/profile" method="post" enctype="multipart/form-data">
<input type="hidden" name="formkey" value="{{v.formkey}}"> <input type="hidden" name="formkey" value="{{v.formkey}}">
<textarea autocomplete="off" id="enemies-text" class="form-control rounded" aria-label="With textarea" placeholder="Enter your enemies on the site..." rows="3" name="enemies" form="profile-enemies" maxlength="1500">{% if v.enemies %}{{v.enemies}}{% endif %}</textarea> <textarea autocomplete="off" id="enemies-text" class="form-control rounded" aria-label="With textarea" placeholder="Enter your enemies on the site..." rows="3" name="enemies" form="profile-enemies" maxlength="1500">{% if v.enemies %}{{v.enemies}}{% endif %}</textarea>
<pre></pre> <pre></pre>
<div class="d-flex"> <div class="d-flex">
<small>Limit of 500 characters</small> <small>Limit of 500 characters</small>
@ -684,7 +684,7 @@
<div class="w-lg-100"> <div class="w-lg-100">
<form id="profile-sig" action="/settings/profile" method="post" enctype="multipart/form-data"> <form id="profile-sig" action="/settings/profile" method="post" enctype="multipart/form-data">
<input type="hidden" name="formkey" value="{{v.formkey}}"> <input type="hidden" name="formkey" value="{{v.formkey}}">
<textarea autocomplete="off" id="sig-text" class="form-control rounded" aria-label="With textarea" placeholder="Enter a signature..." rows="3" name="sig" form="profile-sig" maxlength="200">{% if v.sig %}{{v.sig}}{% endif %}</textarea> <textarea autocomplete="off" id="sig-text" class="form-control rounded" aria-label="With textarea" placeholder="Enter a signature..." rows="3" name="sig" form="profile-sig" maxlength="200">{% if v.sig %}{{v.sig}}{% endif %}</textarea>
<div class="d-flex"> <div class="d-flex">
<pre style="padding-top:0.7rem" class="btn btn-secondary format d-inline-block m-0 fas fa-bold" aria-hidden="true" onclick="makeBold('sig-text')" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Bold"></pre> <pre style="padding-top:0.7rem" class="btn btn-secondary format d-inline-block m-0 fas fa-bold" aria-hidden="true" onclick="makeBold('sig-text')" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Bold"></pre>
&nbsp; &nbsp;

View File

@ -807,7 +807,7 @@
<small class="format btn btn-secondary"><i class="fas fa-bold" aria-hidden="true" onclick="makeBold('post-edit-box-{{p.id}}')" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Bold"></i></small> <small class="format btn btn-secondary"><i class="fas fa-bold" aria-hidden="true" onclick="makeBold('post-edit-box-{{p.id}}')" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Bold"></i></small>
<a class="format btn btn-secondary" role="button"><i class="fas fa-italic" aria-hidden="true" onclick="makeItalics('post-edit-box-{{p.id}}')" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Italicize"></i></a> <a class="format btn btn-secondary" role="button"><i class="fas fa-italic" aria-hidden="true" onclick="makeItalics('post-edit-box-{{p.id}}')" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Italicize"></i></a>
<a class="format btn btn-secondary" role="button"><i class="fas fa-quote-right" aria-hidden="true" onclick="makeQuote('post-edit-box-{{p.id}}')" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Quote"></i></a> <a class="format btn btn-secondary" role="button"><i class="fas fa-quote-right" aria-hidden="true" onclick="makeQuote('post-edit-box-{{p.id}}')" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Quote"></i></a>
<a class="format btn btn-secondary" role="button"><span class="font-weight-bolder text-uppercase" onclick="commentForm('post-edit-box-{{p.id}}');getGif()" aria-hidden="true" data-bs-toggle="modal" data-bs-target="#gifModal" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Add GIF">GIF</span></a> <a class="format btn btn-secondary" role="button"><span class="font-weight-bolder text-uppercase" onclick="commentForm('post-edit-box-{{p.id}}');getGif()" aria-hidden="true" data-bs-toggle="modal" data-bs-target="#gifModal" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Add GIF">GIF</span></a>
<div onclick="loadEmojis('post-edit-box-{{p.id}}')" class="format btn btn-secondary" role="button" aria-hidden="true" data-bs-toggle="modal" data-bs-target="#emojiModal" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Add Emoji"><i class="fas fa-kiss-wink-heart"></i></div> <div onclick="loadEmojis('post-edit-box-{{p.id}}')" class="format btn btn-secondary" role="button" aria-hidden="true" data-bs-toggle="modal" data-bs-target="#emojiModal" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Add Emoji"><i class="fas fa-kiss-wink-heart"></i></div>
<label class="format btn btn-secondary m-0 ml-1 {% if v %}d-inline-block{% else %}d-none{% endif %}" for="file-upload-edit-{{p.id}}"> <label class="format btn btn-secondary m-0 ml-1 {% if v %}d-inline-block{% else %}d-none{% endif %}" for="file-upload-edit-{{p.id}}">
@ -868,7 +868,7 @@
<div id="voting" class="voting d-none d-md-block mb-auto"> <div id="voting" class="voting d-none d-md-block mb-auto">
<div tabindex="0" role="button" onclick="vote('post', '{{p.id}}', '1')" class="post-{{p.id}}-up mx-auto arrow-up upvote-button post-{{p.id}}-up {% if voted==1 %}active{% endif %}"></div> <div tabindex="0" role="button" onclick="vote('post', '{{p.id}}', '1')" class="post-{{p.id}}-up mx-auto arrow-up upvote-button post-{{p.id}}-up {% if voted==1 %}active{% endif %}"></div>
<span class="post-score-{{p.id}} score post-score-{{p.id}} {% if voted==1 %}score-up{% elif voted==-1%}score-down{% endif %}{% if p.controversial %} controversial{% endif %}" data-bs-toggle="tooltip" data-bs-placement="right" title="+{{ups}} | -{{downs}}">{{score}}</span> <span class="post-score-{{p.id}} score post-score-{{p.id}} {% if voted==1 %}score-up{% elif voted==-1%}score-down{% endif %}{% if p.controversial %} controversial{% endif %}" data-bs-toggle="tooltip" data-bs-placement="right" title="+{{ups}} | -{{downs}}">{{score}}</span>
<div {% if DISABLE_DOWNVOTES %}style="display:None!important"{% endif %} tabindex="0" role="button" onclick="vote('post', '{{p.id}}', '-1')" class="post-{{p.id}}-down text-muted mx-auto arrow-down downvote-button post-{{p.id}}-down {% if voted==-1 %}active{% endif %}"></div> <div {% if DISABLE_DOWNVOTES %}style="display:None!important"{% endif %} tabindex="0" role="button" onclick="vote('post', '{{p.id}}', '-1')" class="post-{{p.id}}-down text-muted mx-auto arrow-down downvote-button post-{{p.id}}-down {% if voted==-1 %}active{% endif %}"></div>
</div> </div>
@ -877,7 +877,7 @@
<div id="voting" class="voting d-none d-md-block mb-auto"> <div id="voting" class="voting d-none d-md-block mb-auto">
<div tabindex="0" role="button" class="post-{{p.id}}-up arrow-up mx-auto" onclick="location.href='/login?redirect={{request.path | urlencode}}';"> <div tabindex="0" role="button" class="post-{{p.id}}-up arrow-up mx-auto" onclick="location.href='/login?redirect={{request.path | urlencode}}';">
</div> </div>
<span class="post-{{p.id}}-score-none score text-muted{% if p.controversial %} controversial{% endif %}"{% if not p.is_banned %} data-bs-toggle="tooltip" data-bs-placement="right" title="+{{ups}} | -{{downs}}"{% endif %}>{{score}}</span> <span class="post-{{p.id}}-score-none score text-muted{% if p.controversial %} controversial{% endif %}"{% if not p.is_banned %} data-bs-toggle="tooltip" data-bs-placement="right" title="+{{ups}} | -{{downs}}"{% endif %}>{{score}}</span>
<div {% if DISABLE_DOWNVOTES %}style="display:None!important"{% endif %} tabindex="0" role="button" onclick="vote('post', '{{p.id}}', '-1')" class="post-{{p.id}}-down arrow-down mx-auto" onclick="location.href='/login?redirect={{request.path | urlencode}}';"></div> <div {% if DISABLE_DOWNVOTES %}style="display:None!important"{% endif %} tabindex="0" role="button" onclick="vote('post', '{{p.id}}', '-1')" class="post-{{p.id}}-down arrow-down mx-auto" onclick="location.href='/login?redirect={{request.path | urlencode}}';"></div>
</div> </div>
@ -929,7 +929,7 @@
</span> </span>
{% endif %} {% endif %}
<span class="post-mobile-score-{{p.id}} score post-score-{{p.id}} {% if voted==1 or v.id == AEVANN_ID %}score-up{% elif voted==-1%}score-down{% endif %}{% if p.controversial %} controversial{% endif %}" data-bs-toggle="tooltip" data-bs-placement="top" title="+{{ups}} | -{{downs}}">{{score}}</span> <span class="post-mobile-score-{{p.id}} score post-score-{{p.id}} {% if voted==1 or v.id == AEVANN_ID %}score-up{% elif voted==-1%}score-down{% endif %}{% if p.controversial %} controversial{% endif %}" data-bs-toggle="tooltip" data-bs-placement="top" title="+{{ups}} | -{{downs}}">{{score}}</span>
{% if v %} {% if v %}
<span {% if DISABLE_DOWNVOTES %}style="display:None!important"{% endif %} tabindex="0" role="button" onclick="vote('post-mobile', '{{p.id}}', '-1')" class="post-mobile-{{p.id}}-down mx-0 pl-1 my-0 arrow-down downvote-button post-{{p.id}}-down {% if voted==-1 %}active{% endif %}"></span> <span {% if DISABLE_DOWNVOTES %}style="display:None!important"{% endif %} tabindex="0" role="button" onclick="vote('post-mobile', '{{p.id}}', '-1')" class="post-mobile-{{p.id}}-down mx-0 pl-1 my-0 arrow-down downvote-button post-{{p.id}}-down {% if voted==-1 %}active{% endif %}"></span>
@ -987,11 +987,11 @@
<div class="text-small font-weight-bold mt-1" id="charcount-reply" style="right: 1rem; bottom: 0.5rem; z-index: 3;"></div> <div class="text-small font-weight-bold mt-1" id="charcount-reply" style="right: 1rem; bottom: 0.5rem; z-index: 3;"></div>
<div class="comment-format"> <div class="comment-format">
<a class="btn btn-secondary format d-inline-block m-0" role="button"><i class="fas fa-bold" onclick="makeBold('reply-form-body-{{p.fullname}}')" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Bold"></i></a> <a class="btn btn-secondary format d-inline-block m-0" role="button"><i class="fas fa-bold" onclick="makeBold('reply-form-body-{{p.fullname}}')" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Bold"></i></a>
&nbsp; &nbsp;
<a class="btn btn-secondary format d-inline-block m-0" role="button"><i class="fas fa-italic" onclick="makeItalics('reply-form-body-{{p.fullname}}')" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Italicize"></i></a> <a class="btn btn-secondary format d-inline-block m-0" role="button"><i class="fas fa-italic" onclick="makeItalics('reply-form-body-{{p.fullname}}')" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Italicize"></i></a>
&nbsp; &nbsp;
<a class="btn btn-secondary format d-inline-block m-0" role="button"><i class="fas fa-quote-right" onclick="makeQuote('reply-form-body-{{p.fullname}}')" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Quote"></i></a> <a class="btn btn-secondary format d-inline-block m-0" role="button"><i class="fas fa-quote-right" onclick="makeQuote('reply-form-body-{{p.fullname}}')" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Quote"></i></a>
&nbsp; &nbsp;
<label class="btn btn-secondary format d-inline-block m-0" for="gif-reply-btn-{{p.fullname}}"> <label class="btn btn-secondary format d-inline-block m-0" for="gif-reply-btn-{{p.fullname}}">
<span id="gif-reply-btn-{{p.fullname}}" class="font-weight-bolder text-uppercase" onclick="commentForm('reply-form-body-{{p.fullname}}');getGif()" aria-hidden="true" data-bs-toggle="modal" data-bs-target="#gifModal" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Add GIF">GIF</span> <span id="gif-reply-btn-{{p.fullname}}" class="font-weight-bolder text-uppercase" onclick="commentForm('reply-form-body-{{p.fullname}}');getGif()" aria-hidden="true" data-bs-toggle="modal" data-bs-target="#gifModal" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Add GIF">GIF</span>

View File

@ -33,12 +33,12 @@
<div id="voting" class="d-md-block my-auto mr-3 text-center"> <div id="voting" class="d-md-block my-auto mr-3 text-center">
<div class="post-{{p.id}}-up arrow-up mx-auto"> <div class="post-{{p.id}}-up arrow-up mx-auto">
</div> </div>
<span class="post-{{p.id}}-score-up score-up text-muted{% if voted!=1 %} d-none{% endif %}"></span> <span class="post-{{p.id}}-score-up score-up text-muted{% if voted!=1 %} d-none{% endif %}"></span>
<span class="post-{{p.id}}-score-none score text-muted{% if voted!=0 and voted!=-2 %} d-none{% endif %}"></span> <span class="post-{{p.id}}-score-none score text-muted{% if voted!=0 and voted!=-2 %} d-none{% endif %}"></span>
<span class="post-{{p.id}}-score-down score-down text-muted{% if voted!=-1 %} d-none{% endif %}"></span> <span class="post-{{p.id}}-score-down score-down text-muted{% if voted!=-1 %} d-none{% endif %}"></span>
<div class="post-{{p.id}}-down arrow-down mx-auto"> <div class="post-{{p.id}}-down arrow-down mx-auto">
</div> </div>
</div> </div>
@ -119,17 +119,17 @@
<div class="post-actions mx-auto"> <div class="post-actions mx-auto">
<ul class="list-inline"> <ul class="list-inline">
<li id="voting-mobile" class="voting list-inline-item{% if voted==1 %} upvoted{% elif voted==-1 %} downvoted{% endif %}"> <li id="voting-mobile" class="voting list-inline-item{% if voted==1 %} upvoted{% elif voted==-1 %} downvoted{% endif %}">
<span class="arrow-mobile-up mr-2 arrow-mobile-up"> <span class="arrow-mobile-up mr-2 arrow-mobile-up">
<i class="fas fa-arrow-alt-up mx-0"></i> <i class="fas fa-arrow-alt-up mx-0"></i>
</span> </span>
<span class="post-{{p.id}}-score-mobile-up score-up text-muted{% if voted!=1 %} d-none{% endif %}"></span> <span class="post-{{p.id}}-score-mobile-up score-up text-muted{% if voted!=1 %} d-none{% endif %}"></span>
<span class="post-{{p.id}}-score-mobile-none score text-muted{% if voted!=0 and voted!=-2 %} d-none{% endif %}"></span> <span class="post-{{p.id}}-score-mobile-none score text-muted{% if voted!=0 and voted!=-2 %} d-none{% endif %}"></span>
<span class="post-{{p.id}}-score-mobile-down score-down text-muted{% if voted!=-1 %} d-none{% endif %}"></span> <span class="post-{{p.id}}-score-mobile-down score-down text-muted{% if voted!=-1 %} d-none{% endif %}"></span>
<span class="arrow-mobile-down arrow-mobile-down ml-2 my-0"> <span class="arrow-mobile-down arrow-mobile-down ml-2 my-0">
<i class="fas fa-arrow-alt-down mx-0"></i> <i class="fas fa-arrow-alt-down mx-0"></i>
</span> </span>
</li> </li>

View File

@ -17,7 +17,7 @@
{% include "popover.html" %} {% include "popover.html" %}
{% for p in listing if p.can_see(v) %} {% for p in listing if p.can_see(v) %}
{% set ups=p.upvotes %} {% set ups=p.upvotes %}
{% set downs=p.downvotes %} {% set downs=p.downvotes %}

View File

@ -59,7 +59,7 @@
{% endblock %} {% endblock %}
</head> </head>
<body id="submit" {% if SITE_NAME == 'rDrama' and v and (v.is_banned or v.agendaposter) %}style="overflow-x: hidden;background:url(/assets/images/backgrounds/anime/1.webp?v=3) center center fixed; background-color: var(--background)"{% elif v and v.background %}style="overflow-x: hidden; background:url(/assets/images/backgrounds/{{v.background}}?v=3) center center fixed; background-color: var(--background)display: block{% if 'anime' not in v.background %};background-size: cover{% endif %}"{% endif %}> <body id="submit" {% if SITE_NAME == 'rDrama' and v and (v.is_banned or v.agendaposter) %}style="overflow-x: hidden;background:url(/assets/images/backgrounds/anime/1.webp?v=3) center center fixed; background-color: var(--background)"{% elif v and v.background %}style="overflow-x: hidden; background:url(/assets/images/backgrounds/{{v.background}}?v=3) center center fixed; background-color: var(--background)display: block{% if 'anime' not in v.background %};background-size: cover{% endif %}"{% endif %}>
{% include "header.html" %} {% include "header.html" %}
@ -127,7 +127,7 @@
<label for="body" class="mt-3">Text<i class="fas fa-info-circle text-gray-400 ml-1" data-bs-toggle="tooltip" data-bs-placement="top" title="Uses markdown. Limited to 20000 characters."></i></label> <label for="body" class="mt-3">Text<i class="fas fa-info-circle text-gray-400 ml-1" data-bs-toggle="tooltip" data-bs-placement="top" title="Uses markdown. Limited to 20000 characters."></i></label>
<textarea form="submitform" id="post-text" class="form-control rounded" aria-label="With textarea" placeholder="Optional if you have a link or an image." rows="7" name="body" data-preview="preview" oninput="markdown(this);charLimit('post-text','character-count-submit-text-form');checkForRequired();savetext()" {% if v.longpost %}minlength="280"{% endif %} maxlength="{% if v.bird %}140{% else %}20000{% endif %}" required></textarea> <textarea form="submitform" id="post-text" class="form-control rounded" aria-label="With textarea" placeholder="Optional if you have a link or an image." rows="7" name="body" data-preview="preview" oninput="markdown(this);charLimit('post-text','character-count-submit-text-form');checkForRequired();savetext()" {% if v.longpost %}minlength="280"{% endif %} maxlength="{% if v.bird %}140{% else %}20000{% endif %}" required></textarea>
<div class="ghostdiv" style="display:none;"></div> <div class="ghostdiv" style="display:none;"></div>
<div class="text-small font-weight-bold mt-1" id="character-count-submit-text-form" style="right: 1rem; bottom: 0.5rem; z-index: 3;"></div> <div class="text-small font-weight-bold mt-1" id="character-count-submit-text-form" style="right: 1rem; bottom: 0.5rem; z-index: 3;"></div>
@ -137,7 +137,7 @@
Toggle preview Toggle preview
</div> </div>
<small onclick="makeBold('post-text')" class="btn btn-secondary format d-inline-block m-0"> <small onclick="makeBold('post-text')" class="btn btn-secondary format d-inline-block m-0">
<i class="fas fa-bold" aria-hidden="true" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Bold"></i> <i class="fas fa-bold" aria-hidden="true" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Bold"></i>
</small> </small>
&nbsp; &nbsp;
@ -149,7 +149,7 @@
<i class="fas fa-quote-right" aria-hidden="true" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Quote"></i> <i class="fas fa-quote-right" aria-hidden="true" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Quote"></i>
</small> </small>
&nbsp; &nbsp;
<small class="btn btn-secondary format d-inline-block m-0"><span class="font-weight-bolder text-uppercase" aria-hidden="true" onclick="getGif();commentForm('post-text')" data-bs-toggle="modal" data-bs-target="#gifModal" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Add GIF">GIF</span></small> <small class="btn btn-secondary format d-inline-block m-0"><span class="font-weight-bolder text-uppercase" aria-hidden="true" onclick="getGif();commentForm('post-text')" data-bs-toggle="modal" data-bs-target="#gifModal" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Add GIF">GIF</span></small>
&nbsp; &nbsp;
<div onclick="loadEmojis('post-text')" class="btn btn-secondary format d-inline-block m-0" id="emoji-reply-btn" aria-hidden="true" data-bs-toggle="modal" data-bs-target="#emojiModal" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Add Emoji"><i class="fas fa-kiss-wink-heart"></i></div> <div onclick="loadEmojis('post-text')" class="btn btn-secondary format d-inline-block m-0" id="emoji-reply-btn" aria-hidden="true" data-bs-toggle="modal" data-bs-target="#emojiModal" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Add Emoji"><i class="fas fa-kiss-wink-heart"></i></div>

View File

@ -272,7 +272,7 @@
<input type="hidden" name="formkey" value="{{v.formkey}}"> <input type="hidden" name="formkey" value="{{v.formkey}}">
<input maxlength=100 autocomplete="off" id="customtitlebody" type="text" name="title" class="form-control" placeholder='Enter a flair here' value="{% if u.customtitleplain %}{{u.customtitleplain}}{% endif %}"> <input maxlength=100 autocomplete="off" id="customtitlebody" type="text" name="title" class="form-control" placeholder='Enter a flair here' value="{% if u.customtitleplain %}{{u.customtitleplain}}{% endif %}">
<div class="d-flex mt-2"> <div class="d-flex mt-2">
<a class="format" role="button"><i class="btn btn-secondary format d-inline-block m-0 fas fa-kiss-wink-heart" onclick="loadEmojis('customtitlebody')" aria-hidden="true" data-bs-toggle="modal" data-bs-target="#emojiModal" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Add Emoji"></i></a> <a class="format" role="button"><i class="btn btn-secondary format d-inline-block m-0 fas fa-kiss-wink-heart" onclick="loadEmojis('customtitlebody')" aria-hidden="true" data-bs-toggle="modal" data-bs-target="#emojiModal" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Add Emoji"></i></a>
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;
<div class="custom-control custom-checkbox"> <div class="custom-control custom-checkbox">
<input autocomplete="off" type="checkbox" class="custom-control-input" id="locked" name="locked" {% if u.flairchanged %}checked{% endif %}> <input autocomplete="off" type="checkbox" class="custom-control-input" id="locked" name="locked" {% if u.flairchanged %}checked{% endif %}>
@ -479,7 +479,7 @@
<br><span id="profile-mobile--based">Based count: {{u.basedcount}}</span> <br><span id="profile-mobile--based">Based count: {{u.basedcount}}</span>
{% endif %} {% endif %}
<br><span id="profile-mobile--joined">joined <span id="profile-mobile--joined--time" data-bs-toggle="tooltip" data-bs-placement="bottom" onmouseover="timestamp('profile-mobile--joined--time','{{u.created_utc}}')" class="font-weight-bold">{{u.created_date}}</span></span> <br><span id="profile-mobile--joined">joined <span id="profile-mobile--joined--time" data-bs-toggle="tooltip" data-bs-placement="bottom" onmouseover="timestamp('profile-mobile--joined--time','{{u.created_utc}}')" class="font-weight-bold">{{u.created_date}}</span></span>
{% if v and v.admin_level >= 2 -%} {% if v and v.admin_level >= 2 -%}
<br><span id="profile-mobile--lastactive">last active <span id="profile-mobile--lastactive--time" data-bs-toggle="tooltip" data-bs-placement="bottom" onmouseover="timestamp('profile-mobile--lastactive--time','{{u.last_active}}')" class="font-weight-bold">{{u.last_active_date}}</span></span> <br><span id="profile-mobile--lastactive">last active <span id="profile-mobile--lastactive--time" data-bs-toggle="tooltip" data-bs-placement="bottom" onmouseover="timestamp('profile-mobile--lastactive--time','{{u.last_active}}')" class="font-weight-bold">{{u.last_active_date}}</span></span>
@ -620,7 +620,7 @@
<input type="hidden" name="formkey" value="{{v.formkey}}"> <input type="hidden" name="formkey" value="{{v.formkey}}">
<input maxlength=100 autocomplete="off" id="customtitlebody-mobile" type="text" name="title" class="form-control" placeholder='Enter a flair here' value="{% if u.customtitleplain %}{{u.customtitleplain}}{% endif %}"> <input maxlength=100 autocomplete="off" id="customtitlebody-mobile" type="text" name="title" class="form-control" placeholder='Enter a flair here' value="{% if u.customtitleplain %}{{u.customtitleplain}}{% endif %}">
<div class="d-flex mt-2"> <div class="d-flex mt-2">
<a class="format" role="button"><i class="btn btn-secondary format d-inline-block m-0 fas fa-kiss-wink-heart" onclick="loadEmojis('customtitlebody-mobile')" aria-hidden="true" data-bs-toggle="modal" data-bs-target="#emojiModal" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Add Emoji"></i></a> <a class="format" role="button"><i class="btn btn-secondary format d-inline-block m-0 fas fa-kiss-wink-heart" onclick="loadEmojis('customtitlebody-mobile')" aria-hidden="true" data-bs-toggle="modal" data-bs-target="#emojiModal" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Add Emoji"></i></a>
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;
<div class="custom-control custom-checkbox"> <div class="custom-control custom-checkbox">
<input autocomplete="off" type="checkbox" class="custom-control-input" id="locked-mobile" name="locked" {% if u.flairchanged %}checked{% endif %}> <input autocomplete="off" type="checkbox" class="custom-control-input" id="locked-mobile" name="locked" {% if u.flairchanged %}checked{% endif %}>