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("awardTarget").action = event.relatedTarget.dataset.url;
document.getElementById("awardTarget").action = event.relatedTarget.dataset.url;
});
// 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);
for (let i=0; i<upvotes.length; i++) {
const upvote = upvotes[i]
const downvote = downvotes[i]
const scoretext = scoretexts[i]
@ -90,7 +90,7 @@ function vote(type, id, dir) {
}
}
}
const xhr = new XMLHttpRequest();
xhr.open("POST", "/vote/" + type.replace('-mobile','') + "/" + id + "/" + votedirection);
xhr.setRequestHeader('xhr', 'xhr');

View File

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

View File

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

View File

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

View File

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

View File

@ -1,7 +1,7 @@
function pinned_timestamp(id) {
const el = document.getElementById(id)
const time = new Date(parseInt(el.dataset.timestamp)*1000)
const pintooltip = el.getAttribute("data-bs-original-title")
const time = new Date(parseInt(el.dataset.timestamp)*1000)
const pintooltip = el.getAttribute("data-bs-original-title")
if (!pintooltip.includes('until')) el.setAttribute("data-bs-original-title", `${pintooltip} until ${time}`)
}
@ -12,21 +12,21 @@ popClickBadgeTemplateDOM.loading = "lazy";
popClickBadgeTemplateDOM.alt = "badge";
/**
* @param {MouseEvent} e
*/
* @param {MouseEvent} e
*/
function popclick(e) {
// We let through those methods
if(e.ctrlKey || e.metaKey || e.shiftKey || e.altKey)
return true;
e.preventDefault();
if(e.currentTarget.dataset.popInfo === undefined)
throw new SyntaxError("ill formed HTML! data-pop-info not present!!!");
const author = JSON.parse(e.currentTarget.dataset.popInfo);
if(!(author instanceof Object))
throw new SyntaxError("data-pop-info was not valid!");
// 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
// when the DOM is ready.

View File

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

View File

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

View File

@ -1,5 +1,5 @@
function delete_postModal(id) {
document.getElementById("deletePostButton").onclick = function() {
document.getElementById("deletePostButton").onclick = function() {
const xhr = new XMLHttpRequest();
xhr.open("POST", `/delete_post/${id}`);
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.
This program is distributed in the hope that it will be useful,
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.
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,
2022 Nekobit, king autist
2022 Nekobit, king autist
*/
// Status
@ -50,14 +50,14 @@ const EMOIJ_SEARCH_ENGINE_MIN_INTERVAL = 350;
let emojiSearcher = {
working: false,
queries: [],
addQuery: function(query)
{
this.queries.push(query);
if(!this.working)
this.work();
},
work: async function work() {
this.working = true;
@ -82,7 +82,7 @@ let emojiSearcher = {
// Search
const resultSet = emojisSearchDictionary.completeSearch(query);
// update stuff
// update stuff
for(const [emojiName, emojiDOM] of Object.entries(emojiDOMs))
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.
*
*
* 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.
* @param {String} tag
@ -159,11 +159,11 @@ const emojisSearchDictionary = {
for(let i = target; i >= 0 && this.dict[i].tag.startsWith(query); i--)
for(let j = 0; j < this.dict[i].emojiNames.length; 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 j = 0; j < this.dict[i].emojiNames.length; j++)
result.add(this.dict[i].emojiNames[j]);
return result;
},
@ -179,7 +179,7 @@ const emojisSearchDictionary = {
if(this.dict[i].tag.includes(query))
for(let j = 0; j < this.dict[i].emojiNames.length; j++)
result.add(this.dict[i].emojiNames[j])
return result;
}
};
@ -188,7 +188,7 @@ const emojisSearchDictionary = {
const emojiRequest = new XMLHttpRequest();
emojiRequest.open("GET", '/marsey_list.json');
emojiRequest.onload = async (e) => {
let emojis = JSON.parse(emojiRequest.response);
let emojis = JSON.parse(emojiRequest.response);
if(! (emojis instanceof 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.emojiName = emoji.name;
emojiDOM.onclick = emojiAddToInput;
emojiDOM.hidden = true;
emojiDOM.hidden = true;
const emojiIMGDOM = emojiDOM.children[0];
emojiIMGDOM.src = "/e/" + emoji.name + ".webp";
emojiIMGDOM.alt = emoji.name;
/** Disableing lazy loading seems to reduce cpu usage somehow (?)
* idk it is difficult to benchmark */
* idk it is difficult to benchmark */
emojiIMGDOM.loading = "lazy";
// Save reference
@ -257,8 +257,8 @@ emojiRequest.onload = async (e) => {
// Show favorite for start.
await classesSelectorDOM.children[0].children[0].click();
// Send it to the render machine!
// Send it to the render machine!
emojiResultsDOM.appendChild(bussyDOM);
emojiResultsDOM.hidden = false;
@ -267,9 +267,9 @@ emojiRequest.onload = async (e) => {
}
/**
*
* @param {Event} e
*/
*
* @param {Event} e
*/
function switchEmojiTab(e)
{
const className = e.currentTarget.dataset.className;
@ -292,14 +292,14 @@ function switchEmojiTab(e)
const favs = emojiFirstBoot ? emojisSearchDictionary.searchFor("anton-d") : Object.keys(Object.fromEntries(
Object.entries(favorite_emojis).sort(([,a],[,b]) => b-a)
)).slice(0, 25);
for (const emoji of favs)
if(emojiDOMs[emoji] instanceof HTMLElement)
emojiDOMs[emoji].hidden = false;
return;
}
emojiNewUserDOM.hidden = true;
for(const emojiDOM of Object.values(emojiDOMs))
@ -315,53 +315,53 @@ async function start_search() {
}
/**
* Add the selected emoji to the targeted text area
* @param {Event} event
*/
* Add the selected emoji to the targeted text area
* @param {Event} event
*/
function emojiAddToInput(event)
{
// This should not happen if used properly but whatever
if(!(emojiInputTargetDOM instanceof HTMLTextAreaElement) && !(emojiInputTargetDOM instanceof HTMLInputElement))
return;
// 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?"))
return;
let strToInsert = event.currentTarget.dataset.emojiName;
let strToInsert = event.currentTarget.dataset.emojiName;
for(let i = 0; i < emojiSelectPostfixDOMs.length; i++)
if(emojiSelectPostfixDOMs[i].checked)
strToInsert = strToInsert + emojiSelectPostfixDOMs[i].value;
for(let i = 0; i < emojiSelectSuffixDOMs.length; i++)
if(emojiSelectSuffixDOMs[i].checked)
strToInsert = emojiSelectSuffixDOMs[i].value + strToInsert;
strToInsert = ":" + strToInsert + ":"
const newPos = emojiInputTargetDOM.selectionStart + strToInsert.length;
const newPos = emojiInputTargetDOM.selectionStart + strToInsert.length;
emojiInputTargetDOM.setRangeText(strToInsert);
// Sir, come out and drink your Chromium complaint web
// I HATE CHROME. I HATE CHROME
if(window.chrome !== undefined)
setTimeout(function(){
console.warn("Chrome detected, r-slured mode enabled.");
// AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
// JUST WORK STUPID CHROME PIECE OF SHIT
emojiInputTargetDOM.focus();
for(let i = 0; i < 2; i++)
emojiInputTargetDOM.setSelectionRange(newPos, newPos);
emojiInputTargetDOM.focus();
for(let i = 0; i < 2; i++)
emojiInputTargetDOM.setSelectionRange(newPos, newPos);
}, 1);
else
emojiInputTargetDOM.setSelectionRange(newPos, newPos);
// kick-start the preview
emojiInputTargetDOM.dispatchEvent(new Event('input'));
@ -374,190 +374,190 @@ function emojiAddToInput(event)
}
(function() {
const insertAt = (str, sub, pos) => `${str.slice(0, pos)}${sub}${str.slice(pos)}`;
let emoji_typing_state = false;
const insertAt = (str, sub, pos) => `${str.slice(0, pos)}${sub}${str.slice(pos)}`;
function update_ghost_div_textarea(text)
{
let ghostdiv = text.parentNode.querySelector(".ghostdiv");
if (!ghostdiv) return;
let emoji_typing_state = false;
ghostdiv.innerText = text.value.substring(0, text.selectionStart);
ghostdiv.innerHTML += "<span></span>";
function update_ghost_div_textarea(text)
{
let ghostdiv = text.parentNode.querySelector(".ghostdiv");
if (!ghostdiv) return;
// Now lets get coordinates
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 };
}
ghostdiv.innerText = text.value.substring(0, text.selectionStart);
ghostdiv.innerHTML += "<span></span>";
// 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);
// Now lets get coordinates
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 emoji_index = 0;
let emoji_index = 0;
function curr_word_is_emoji()
{
return current_word && current_word.charAt(0) == ":" &&
current_word.charAt(current_word.length-1) != ":";
}
function curr_word_is_emoji()
{
return current_word && current_word.charAt(0) == ":" &&
current_word.charAt(current_word.length-1) != ":";
}
function populate_speed_emoji_modal(results, textbox)
{
function populate_speed_emoji_modal(results, textbox)
{
selecting = true;
if (!results || results.size === 0)
{
speed_carot_modal.style.display = "none";
return -1;
}
if (!results || results.size === 0)
{
speed_carot_modal.style.display = "none";
return -1;
}
emoji_index = 0;
speed_carot_modal.innerHTML = "";
const MAXXX = 25;
// Not sure why the results is a Set... but oh well
let i = 0;
for (let result of results)
{
if (i++ > MAXXX) return i;
let emoji_option = document.createElement("div");
emoji_option.className = "speed-modal-option emoji-option " + (i === 1 ? "selected" : "");
emoji_option.tabIndex = 0;
let emoji_option_img = document.createElement("img");
emoji_option_img.className = "speed-modal-image emoji-option-image";
// This is a bit
emoji_option_img.src = `/e/${result}.webp`;
let emoji_option_text = document.createElement("span");
emoji_option_text.title = result;
emoji_option_text.innerText = result;
emoji_index = 0;
speed_carot_modal.innerHTML = "";
const MAXXX = 25;
// Not sure why the results is a Set... but oh well
let i = 0;
for (let result of results)
{
if (i++ > MAXXX) return i;
let emoji_option = document.createElement("div");
emoji_option.className = "speed-modal-option emoji-option " + (i === 1 ? "selected" : "");
emoji_option.tabIndex = 0;
let emoji_option_img = document.createElement("img");
emoji_option_img.className = "speed-modal-image emoji-option-image";
// This is a bit
emoji_option_img.src = `/e/${result}.webp`;
let emoji_option_text = document.createElement("span");
emoji_option_text.title = result;
emoji_option_text.innerText = result;
if (current_word.includes("#")) result = `#${result}`
if (current_word.includes("!")) result = `!${result}`
emoji_option.onclick = (e) => {
emoji_option.onclick = (e) => {
selecting = false;
speed_carot_modal.style.display = "none";
textbox.value = textbox.value.replace(new RegExp(current_word+"(?=\\s|$)", "g"), `:${result}:`)
speed_carot_modal.style.display = "none";
textbox.value = textbox.value.replace(new RegExp(current_word+"(?=\\s|$)", "g"), `:${result}:`)
markdown(textbox)
};
// Pack
emoji_option.appendChild(emoji_option_img);
emoji_option.appendChild(emoji_option_text);
speed_carot_modal.appendChild(emoji_option);
}
if (i === 0) speed_carot_modal.style.display = "none";
else speed_carot_modal.style.display = "initial";
return i;
}
};
// Pack
emoji_option.appendChild(emoji_option_img);
emoji_option.appendChild(emoji_option_text);
speed_carot_modal.appendChild(emoji_option);
}
if (i === 0) speed_carot_modal.style.display = "none";
else speed_carot_modal.style.display = "initial";
return i;
}
function update_speed_emoji_modal(event)
{
const box_coords = update_ghost_div_textarea(event.target);
function update_speed_emoji_modal(event)
{
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
switch (event.data)
{
case ':':
emoji_typing_state = true;
break;
case ' ':
emoji_typing_state = false;
break;
default:
break;
}
// Unused, but left incase anyone wants to use this more efficient method for emojos
switch (event.data)
{
case ':':
emoji_typing_state = true;
break;
case ' ':
emoji_typing_state = false;
break;
default:
break;
}
// Get current word at string, such as ":marse" or "word"
let coords = text.indexOf(' ',box_coords.pos);
current_word = /\S+$/.exec(text.slice(0, coords === -1 ? text.length : coords));
if (current_word) current_word = current_word.toString();
// Get current word at string, such as ":marse" or "word"
let coords = text.indexOf(' ',box_coords.pos);
current_word = /\S+$/.exec(text.slice(0, coords === -1 ? text.length : coords));
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
* kept it unless someone wants to provide an option to toggle it for performance */
if (curr_word_is_emoji() && current_word != ":")
{
loadEmojis(null);
let modal_pos = event.target.getBoundingClientRect();
modal_pos.x += window.scrollX;
modal_pos.y += window.scrollY;
/* 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 */
if (curr_word_is_emoji() && current_word != ":")
{
loadEmojis(null);
let modal_pos = event.target.getBoundingClientRect();
modal_pos.x += window.scrollX;
modal_pos.y += window.scrollY;
speed_carot_modal.style.display = "initial";
speed_carot_modal.style.left = box_coords.x - 35 + "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);
speed_carot_modal.style.display = "initial";
speed_carot_modal.style.left = box_coords.x - 35 + "px";
speed_carot_modal.style.top = modal_pos.y + box_coords.y + 14 + "px";
}
else {
speed_carot_modal.style.display = "none";
}
}
// Do the search (and do something with it)
populate_speed_emoji_modal(emojisSearchDictionary.searchFor(current_word.substr(1).replace(/#/g, "").replace(/!/g, "")), event.target);
function speed_carot_navigate(e)
{
}
else {
speed_carot_modal.style.display = "none";
}
}
function speed_carot_navigate(e)
{
if (!selecting) return;
let select_items = speed_carot_modal.querySelectorAll(".speed-modal-option");
if (!select_items || !curr_word_is_emoji()) return false;
// Up or down arrow or enter
if (e.keyCode == 38 || e.keyCode == 40 || e.keyCode == 13)
{
if (emoji_index > select_items.length)
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;
let select_items = speed_carot_modal.querySelectorAll(".speed-modal-option");
if (!select_items || !curr_word_is_emoji()) return false;
// Up or down arrow or enter
if (e.keyCode == 38 || e.keyCode == 40 || e.keyCode == 13)
{
if (emoji_index > select_items.length)
emoji_index = select_items;
case 13:
select_items[emoji_index].click();
default:
break;
}
select_items[emoji_index].classList.remove("selected");
switch (e.keyCode)
{
case 38: // Up arrow
if (emoji_index)
emoji_index--;
break;
select_items[emoji_index].classList.add("selected");
e.preventDefault();
}
}
case 40: // Down arrow
if (emoji_index < select_items.length-1) emoji_index++;
break;
// 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);
});
case 13:
select_items[emoji_index].click();
default:
break;
}
select_items[emoji_index].classList.add("selected");
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();
}
if (inputTargetIDName)
emojiInputTargetDOM = document.getElementById(inputTargetIDName);
if (inputTargetIDName)
emojiInputTargetDOM = document.getElementById(inputTargetIDName);
}
document.getElementById('emojiModal').addEventListener('shown.bs.modal', function () {

View File

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

View File

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

View File

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

View File

@ -1,64 +1,9 @@
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")`;
}
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);
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")`}
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:!0});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=!0}
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)

View File

@ -1,200 +1,200 @@
let purchaseQuantity = 1;
var lotteryOnReady = function () {
checkLotteryStats();
checkLotteryStats();
// Show ticket being pulled.
const ticketPulled = document.getElementById("lotteryTicketPulled");
const purchaseTicket = document.getElementById("purchaseTicket");
// Show ticket being pulled.
const ticketPulled = document.getElementById("lotteryTicketPulled");
const purchaseTicket = document.getElementById("purchaseTicket");
purchaseTicket.addEventListener("click", () => {
purchaseTicket.addEventListener("click", () => {
ticketPulled.style.display = "block";
setTimeout(() => {
ticketPulled.style.display = "none";
ticketPulled.src =
ticketPulled.style.display = "none";
ticketPulled.src =
"/i/rDrama/lottery_active.webp?v=2000&t=" +
new Date().getTime();
purchaseTicket.disabled = false;
purchaseTicket.disabled = false;
}, 1780);
});
});
// Update the quantity field
const purchaseQuantityField = document.getElementById(
// Update the quantity field
const purchaseQuantityField = document.getElementById(
"totalQuantityOfTickets"
);
const purchaseTotalCostField = document.getElementById("totalCostOfTickets");
const ticketPurchaseQuantityInput = document.getElementById(
);
const purchaseTotalCostField = document.getElementById("totalCostOfTickets");
const ticketPurchaseQuantityInput = document.getElementById(
"ticketPurchaseQuantity"
);
);
ticketPurchaseQuantityInput.addEventListener("change", (event) => {
ticketPurchaseQuantityInput.addEventListener("change", (event) => {
const value = Math.max(1, parseInt(event.target.value))
purchaseQuantity = value
purchaseQuantityField.innerText = value
purchaseTotalCostField.innerText = value * 12
});
});
};
if (
document.readyState === "complete" ||
(document.readyState !== "loading" && !document.documentElement.doScroll)
document.readyState === "complete" ||
(document.readyState !== "loading" && !document.documentElement.doScroll)
) {
lotteryOnReady();
lotteryOnReady();
} else {
document.addEventListener("DOMContentLoaded", lotteryOnReady);
document.addEventListener("DOMContentLoaded", lotteryOnReady);
}
function purchaseLotteryTicket() {
return handleLotteryRequest("buy", "POST");
return handleLotteryRequest("buy", "POST");
}
function checkLotteryStats() {
return handleLotteryRequest("active", "GET");
return handleLotteryRequest("active", "GET");
}
// Admin
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() {
checkLotteryStats();
checkLotteryStats();
if (ensureIntent()) {
if (ensureIntent()) {
return handleLotteryRequest("start", "POST", () =>
window.location.reload()
window.location.reload()
);
}
}
}
function endLotterySession() {
checkLotteryStats();
checkLotteryStats();
if (ensureIntent()) {
if (ensureIntent()) {
return handleLotteryRequest("end", "POST", () => window.location.reload());
}
}
}
// Composed
function handleLotteryRequest(uri, method, callback = () => {}) {
const xhr = new XMLHttpRequest();
const url = `/lottery/${uri}`;
xhr.open(method, url);
xhr.onload = handleLotteryResponse.bind(null, xhr, method, callback);
const xhr = new XMLHttpRequest();
const url = `/lottery/${uri}`;
xhr.open(method, url);
xhr.onload = handleLotteryResponse.bind(null, xhr, method, callback);
const form = new FormData();
form.append("formkey", formkey());
form.append("quantity", purchaseQuantity)
const form = new FormData();
form.append("formkey", formkey());
form.append("quantity", purchaseQuantity)
xhr.send(form);
xhr.send(form);
}
function handleLotteryResponse(xhr, method, callback) {
let response;
let response;
try {
try {
response = JSON.parse(xhr.response);
} catch (error) {
} catch (error) {
console.error(error);
}
}
if (method === "POST") {
if (method === "POST") {
const succeeded =
xhr.status >= 200 && xhr.status < 300 && response && response.message;
xhr.status >= 200 && xhr.status < 300 && response && response.message;
if (succeeded) {
// Display success.
const toast = document.getElementById("lottery-post-success");
const toastMessage = document.getElementById("lottery-post-success-text");
// Display success.
const toast = document.getElementById("lottery-post-success");
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 {
// Display error.
const toast = document.getElementById("lottery-post-error");
const toastMessage = document.getElementById("lottery-post-error-text");
// Display error.
const toast = document.getElementById("lottery-post-error");
const toastMessage = document.getElementById("lottery-post-error-text");
toastMessage.innerText =
toastMessage.innerText =
(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;
const { user, lottery, participants } = response.stats;
const [
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";
[
prizeImage,
prizeField,
timeLeftField,
ticketsSoldThisSessionField,
participantsThisSessionField,
ticketsHeldCurrentField,
].forEach((e) => (e.textContent = "-"));
purchaseTicketButton.disabled = true;
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,
timeLeftField,
ticketsSoldThisSessionField,
participantsThisSessionField,
ticketsHeldCurrentField,
].forEach((e) => (e.textContent = "-"));
purchaseTicketButton.disabled = true;
}
ticketsHeldTotalField.textContent = user.ticketsHeld.total;
winningsField.textContent = user.winnings;
const [endButton, startButton] = [
"endLotterySession",
"startLotterySession",
"endLotterySession",
"startLotterySession",
].map((id) => document.getElementById(id));
if (response.stats.lottery) {
endButton.style.display = "block";
startButton.style.display = "none";
endButton.style.display = "block";
startButton.style.display = "none";
} else {
endButton.style.display = "none";
startButton.style.display = "block";
endButton.style.display = "none";
startButton.style.display = "block";
}
}
}
}
function formatTimeLeft(secondsLeft) {
const minutesLeft = Math.floor(secondsLeft / 60);
const seconds = secondsLeft % 60;
const minutes = minutesLeft % 60;
const hours = Math.floor(minutesLeft / 60);
const minutesLeft = Math.floor(secondsLeft / 60);
const seconds = secondsLeft % 60;
const minutes = 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;
},
tokenizer: function(src) {
const rule = /^@[a-zA-Z0-9_\-]+/;
const rule = /^@[a-zA-Z0-9_\-]+/;
const match = rule.exec(src);
if(match){
return {
type: 'mention',
raw: match[0],
raw: match[0],
text: match[0].trim().slice(1),
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 = marked(input)
input = input.replace(/\n\n/g, '<br>')

View File

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

File diff suppressed because one or more lines are too long

View File

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

View File

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

View File

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

View File

@ -1,26 +1,26 @@
let sortAscending = {};
function sort_table(n) {
const table = this.event.target.parentElement.parentElement.parentElement
const rows = table.rows;
let items = [];
for (let i = 1; i < rows.length; i++) {
const ele = rows[i];
let x = rows[i].getElementsByTagName("TD")[n];
x = x.getElementsByTagName('a')[0] || x;
const attr = x.dataset.time ? parseInt(x.dataset.time) : parseInt(x.innerHTML);
console.log(attr);
items.push({ ele, attr });
}
if (sortAscending[n]) {
items.sort((a, b) => a.attr - b.attr);
sortAscending[n] = false;
} else {
items.sort((a, b) => b.attr - a.attr);
sortAscending[n] = true;
}
const table = this.event.target.parentElement.parentElement.parentElement
const rows = table.rows;
let items = [];
for (let i = 1; i < rows.length; i++) {
const ele = rows[i];
let x = rows[i].getElementsByTagName("TD")[n];
x = x.getElementsByTagName('a')[0] || x;
const attr = x.dataset.time ? parseInt(x.dataset.time) : parseInt(x.innerHTML);
console.log(attr);
items.push({ ele, attr });
}
if (sortAscending[n]) {
items.sort((a, b) => a.attr - b.attr);
sortAscending[n] = false;
} else {
items.sort((a, b) => b.attr - a.attr);
sortAscending[n] = true;
}
for (let i = items.length - 1; i--;) {
items[i].ele.parentNode.insertBefore(items[i].ele, items[i + 1].ele);
}
for (let i = items.length - 1; i--;) {
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();
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();
})
@ -150,7 +150,7 @@ function draft(t) {
followers.disabled = true;
} else {
followers.disabled = false;
}
}
}
function checkRepost() {
@ -168,7 +168,7 @@ function checkRepost() {
xhr.onload=function(){
try {data = JSON.parse(xhr.response)}
catch(e) {console.log(e)}
if (data && data["permalink"]) {
const permalink = data["permalink"]
if (permalink) {
@ -196,7 +196,7 @@ function updateCategories() {
document.getElementById("submit-categories").innerHTML = '';
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}">` +
`<label for="category-${c.id}" class="post--category-tag" ` +
`style="color:${c.color_text}; background-color:${c.color_bg};">` +
@ -207,12 +207,12 @@ function updateCategories() {
}
document.addEventListener('keydown', (e) => {
if(!((e.ctrlKey || e.metaKey) && e.key === "Enter"))
return;
if(!((e.ctrlKey || e.metaKey) && e.key === "Enter"))
return;
const submitButton = document.getElementById('create_button')
const submitButton = document.getElementById('create_button')
submitButton.click();
submitButton.click();
});
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();
document.getElementById('userpage').addEventListener('click', () => {
if (audio.paused) audio.play();
if (audio.paused) audio.play();
}, {once : true});
}
else
@ -28,7 +28,7 @@ else
let audio = new Audio(`/@${v_username}/song`);
audio.loop=true;
function toggle() {
if (audio.paused)
{
@ -41,12 +41,12 @@ else
localStorage.setItem("paused", "1")
}
}
if (!paused)
{
audio.play();
window.addEventListener('click', () => {
if (audio.paused) audio.play();
if (audio.paused) audio.play();
}, {once : true});
}
}
@ -54,7 +54,7 @@ else
function badge_timestamp(t) {
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.removeAttribute("onmouseover")
}

View File

@ -92,7 +92,7 @@ function transferCoins(mobile=false) {
let transferred = amount - Math.ceil(amount*TRANSFER_TAX);
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,
"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-preview').classList.add('d-none');
document.getElementById('message-preview-mobile').classList.add('d-none');
const form = e.target;
const xhr = new XMLHttpRequest();
e.preventDefault();

View File

@ -26,7 +26,7 @@ class Badge(Base):
__tablename__ = "badges"
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)
url = Column(String)
created_utc = Column(Integer)

View File

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

View File

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

View File

@ -78,7 +78,7 @@ class ModAction(Base):
@lazy
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>"
@ -106,7 +106,7 @@ class ModAction(Base):
@property
@lazy
def permalink(self):
return f"{SITE_FULL}/log/{self.id}"
return f"{SITE_FULL}/log/{self.id}"
ACTIONTYPES = {
'agendaposter': {

View File

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

View File

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

View File

@ -23,7 +23,7 @@ def get_active_lottery_stats():
active_lottery = get_active_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():

View File

@ -81,33 +81,33 @@ def allowed_attributes(tag, name, value):
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
and stomp on the existing ``url_re``::
If you want a different set of tlds or allowed protocols, pass those in
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(
r"""\(* # Match any opening parentheses.
\b(?<![@.])(?:(?:{0}):/{{0,3}}(?:(?:\w+:)?\w+@)?)? # http://
([\w-]+\.)+(?:{1})(?:\:[0-9]+)?(?!\.\w)\b # xx.yy.tld(:##)?
(?:[/?][^#\s\{{\}}\|\\\^\[\]`<>"]*)?
# /path/zz (excluding "unsafe" chars from RFC 1738,
# except for ~, which happens in practice)
(?:\#[^#\s\|\\\^\[\]`<>"]*)?
# #hash (excluding "unsafe" chars from RFC 1738,
# except for ~, which happens in practice)
""".format(
"|".join(sorted(protocols)), "|".join(sorted(tlds))
),
re.IGNORECASE | re.VERBOSE | re.UNICODE,
)
"""
return re.compile(
r"""\(*# Match any opening parentheses.
\b(?<![@.])(?:(?:{0}):/{{0,3}}(?:(?:\w+:)?\w+@)?)?# http://
([\w-]+\.)+(?:{1})(?:\:[0-9]+)?(?!\.\w)\b# xx.yy.tld(:##)?
(?:[/?][^#\s\{{\}}\|\\\^\[\]`<>"]*)?
# /path/zz (excluding "unsafe" chars from RFC 1738,
# except for ~, which happens in practice)
(?:\#[^#\s\|\\\^\[\]`<>"]*)?
# #hash (excluding "unsafe" chars from RFC 1738,
# except for ~, which happens in practice)
""".format(
"|".join(sorted(protocols)), "|".join(sorted(tlds))
),
re.IGNORECASE | re.VERBOSE | re.UNICODE,
)
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
if SITE_NAME == 'rDrama':
minimum_bet = 100
minimum_bet = 100
else:
minimum_bet = 10
minimum_bet = 10
maximum_bet = INFINITY
payout_to_symbols = {
2: ["👣", "🍀", "🌈", "⭐️"],
3: ["🍎", "🔞", "⚛️", "☢️"],
5: ["✡️", "⚔️", "🍆", "🍒"],
12: ["🐱"]
2: ["👣", "🍀", "🌈", "⭐️"],
3: ["🍎", "🔞", "⚛️", "☢️"],
5: ["✡️", "⚔️", "🍆", "🍒"],
12: ["🐱"]
}
def casino_slot_pull(gambler, wager_value, currency):
over_min = wager_value >= minimum_bet
under_max = wager_value <= maximum_bet
using_dramacoin = currency == "dramacoin"
using_marseybux = not using_dramacoin
has_proper_funds = (using_dramacoin and gambler.coins >= wager_value) or (
using_marseybux and gambler.procoins >= wager_value)
currency_prop = "coins" if using_dramacoin else "procoins"
currency_value = getattr(gambler, currency_prop, 0)
over_min = wager_value >= minimum_bet
under_max = wager_value <= maximum_bet
using_dramacoin = currency == "dramacoin"
using_marseybux = not using_dramacoin
has_proper_funds = (using_dramacoin and gambler.coins >= wager_value) or (
using_marseybux and gambler.procoins >= wager_value)
currency_prop = "coins" if using_dramacoin else "procoins"
currency_value = getattr(gambler, currency_prop, 0)
if (over_min and under_max and has_proper_funds):
setattr(gambler, currency_prop, currency_value - wager_value)
gambler.winnings -= wager_value
if (over_min and under_max and has_proper_funds):
setattr(gambler, currency_prop, currency_value - wager_value)
gambler.winnings -= wager_value
payout = determine_payout()
reward = wager_value * payout
payout = determine_payout()
reward = wager_value * payout
currency_value = getattr(gambler, currency_prop, 0)
setattr(gambler, currency_prop, currency_value + reward)
gambler.winnings += reward
currency_value = getattr(gambler, currency_prop, 0)
setattr(gambler, currency_prop, currency_value + reward)
gambler.winnings += reward
symbols = build_symbols(payout)
text = build_text(wager_value, payout, currency)
symbols = build_symbols(payout)
text = build_text(wager_value, payout, currency)
game_state = {
"symbols": symbols,
"text": text
}
casino_game = Casino_Game()
casino_game.active = False
casino_game.user_id = gambler.id
casino_game.currency = currency_prop
casino_game.wager = wager_value
casino_game.winnings = reward - wager_value
casino_game.kind = 'slots'
casino_game.game_state = json.dumps(game_state)
g.db.add(casino_game)
game_state = {
"symbols": symbols,
"text": text
}
casino_game = Casino_Game()
casino_game.active = False
casino_game.user_id = gambler.id
casino_game.currency = currency_prop
casino_game.wager = wager_value
casino_game.winnings = reward - wager_value
casino_game.kind = 'slots'
casino_game.game_state = json.dumps(game_state)
g.db.add(casino_game)
return True, casino_game.game_state
else:
return False, "{}"
return True, casino_game.game_state
else:
return False, "{}"
def build_symbols(for_payout):
all_symbols = []
all_symbols = []
for payout in payout_to_symbols:
for symbol in payout_to_symbols[payout]:
all_symbols.append(symbol)
for payout in payout_to_symbols:
for symbol in payout_to_symbols[payout]:
all_symbols.append(symbol)
shuffle(all_symbols)
shuffle(all_symbols)
if for_payout == 0:
return "".join([all_symbols[0], ",", all_symbols[1], ",", all_symbols[2]])
elif for_payout == 1:
indices = shuffle([0, 1, 2])
symbol_set = ["", "", ""]
match_a = indices[0]
match_b = indices[1]
nonmatch = indices[2]
matching_symbol = all_symbols[0]
other_symbol = all_symbols[1]
symbol_set[match_a] = matching_symbol
symbol_set[match_b] = matching_symbol
symbol_set[nonmatch] = other_symbol
if for_payout == 0:
return "".join([all_symbols[0], ",", all_symbols[1], ",", all_symbols[2]])
elif for_payout == 1:
indices = shuffle([0, 1, 2])
symbol_set = ["", "", ""]
match_a = indices[0]
match_b = indices[1]
nonmatch = indices[2]
matching_symbol = all_symbols[0]
other_symbol = all_symbols[1]
symbol_set[match_a] = matching_symbol
symbol_set[match_b] = matching_symbol
symbol_set[nonmatch] = other_symbol
return "".join([symbol_set[0], ",", symbol_set[1], ",", symbol_set[2]])
else:
relevantSymbols = shuffle(payout_to_symbols[for_payout])
symbol = relevantSymbols[0]
return "".join([symbol_set[0], ",", symbol_set[1], ",", symbol_set[2]])
else:
relevantSymbols = shuffle(payout_to_symbols[for_payout])
symbol = relevantSymbols[0]
return "".join([symbol, ",", symbol, ",", symbol])
return "".join([symbol, ",", symbol, ",", symbol])
def build_text(wager_value, result, currency):
if result == 0:
return f'Lost {wager_value} {currency}'
elif result == 1:
return 'Broke Even'
elif result == 12:
return f'Jackpot! Won {wager_value * (result-1)} {currency}'
else:
return f'Won {wager_value * (result-1)} {currency}'
if result == 0:
return f'Lost {wager_value} {currency}'
elif result == 1:
return 'Broke Even'
elif result == 12:
return f'Jackpot! Won {wager_value * (result-1)} {currency}'
else:
return f'Won {wager_value * (result-1)} {currency}'
def determine_payout():
value = random.randint(1, 100)
if value == 100:
return 12
elif value >= 96:
return 5
elif value >= 88:
return 3
elif value >= 72:
return 2
elif value >= 61:
return 1
else:
return 0
value = random.randint(1, 100)
if value == 100:
return 12
elif value >= 96:
return 5
elif value >= 88:
return 3
elif value >= 72:
return 2
elif value >= 61:
return 1
else:
return 0
def shuffle(stuff):
random.shuffle(stuff)
return stuff
random.shuffle(stuff)
return stuff

View File

@ -13,7 +13,7 @@ from files.classes.award import AwardRelationship
from files.helpers.const import *
def generate_charts_task(site):
chart(kind='daily', site=site)
chart(kind='daily', site=site)
chart(kind='weekly', site=site)
stats(site=site)
@ -63,14 +63,14 @@ def chart(kind, site):
plt.rcParams['figure.figsize'] = (chart_width, 20)
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)
signup_chart.grid(), posts_chart.grid(), comments_chart.grid()
signup_chart.plot (daily_times, daily_signups, color='red')
posts_chart.plot (daily_times, post_stats, color='blue')
comments_chart.plot(daily_times, comment_stats, color='purple')
signup_chart.plot(daily_times, daily_signups, color='red')
posts_chart.plot(daily_times, post_stats, color='blue')
comments_chart.plot(daily_times, comment_stats, color='purple')
signup_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
send_mail(to_address=email,
html=render_template("email/email_verify.html",
action_url=link,
v=user),
subject=f"Validate your {SITE_NAME} account email."
)
html=render_template("email/email_verify.html",
action_url=link,
v=user),
subject=f"Validate your {SITE_NAME} account email."
)
@app.post("/verify_email")
@ -66,7 +66,7 @@ def activate(v):
if int(time.time()) - timestamp > 3600:
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)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -866,7 +866,7 @@ def submit_post(v, sub=None):
send_repeatable_notification(v.id, text)
v.ban(reason="Spamming.",
days=1)
days=1)
for post in similar_posts + similar_urls:
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]}
return render_template("search.html",
v=v,
query=query,
total=total,
page=page,
listing=posts,
sort=sort,
t=t,
next_exists=next_exists
)
v=v,
query=query,
total=total,
page=page,
listing=posts,
sort=sort,
t=t,
next_exists=next_exists
)
@app.get("/search/comments")
@auth_required

View File

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

View File

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

View File

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

View File

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

View File

@ -89,17 +89,17 @@
</div>
<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>
</div>
<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>
</div>
<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>
</div>

View File

@ -4,127 +4,250 @@
<!-- New -->
<div class="casino-games">
<!-- Slots -->
<div id="slots-block" class="casino-block">
<div class="casino-block-title">
Slots
<hr style="flex: 1; margin-left: 1rem" />
</div>
<div class="casino-block-inner">
<div class="casino-block-left">
<!-- Game -->
<div class="casino-block-game">
<div>
<div class="casino-slots-results" style="flex: 1">
<div class="reel">
<img src="/i/rDrama/coins.webp?v=3009" alt="coin" />
</div>
<div class="reel">
<img src="/i/rDrama/coins.webp?v=3009" alt="coin" />
</div>
<div class="reel">
<img src="/i/rDrama/coins.webp?v=3009" alt="coin" />
</div>
</div>
<div class="casino-slots-outcome" id="casinoSlotsResult">
&nbsp;
</div>
</div>
</div>
<!-- Bet -->
<div class="casino-block-bet">
<div class="lottery-page--stat">
<div class="lottery-page--stat-keys" style="margin-right: 1rem">
<div>Enter Bet</div>
<div>
<input
id="casinoSlotsBet"
class="form-control"
autocomplete="off"
value="100"
min="100"
step="1"
aria-label="Bet"
name="casinoSlotsBet"
type="number"
style="flex: 1; max-width: 200px; text-align: right"
/>
</div>
</div>
<!-- Slots -->
<div id="slots-block" class="casino-block">
<div class="casino-block-title">
Slots
<hr style="flex: 1; margin-left: 1rem" />
</div>
<div class="casino-block-inner">
<div class="casino-block-left">
<!-- Game -->
<div class="casino-block-game">
<div>
<div class="casino-slots-results" style="flex: 1">
<div class="reel">
<img src="/i/rDrama/coins.webp?v=3009" alt="coin" />
</div>
<div class="reel">
<img src="/i/rDrama/coins.webp?v=3009" alt="coin" />
</div>
<div class="reel">
<img src="/i/rDrama/coins.webp?v=3009" alt="coin" />
</div>
</div>
<div class="casino-slots-outcome" id="casinoSlotsResult">
&nbsp;
</div>
</div>
</div>
<!-- Bet -->
<div class="casino-block-bet">
<div class="lottery-page--stat">
<div class="lottery-page--stat-keys" style="margin-right: 1rem">
<div>Enter Bet</div>
<div>
<input
id="casinoSlotsBet"
class="form-control"
autocomplete="off"
value="100"
min="100"
step="1"
aria-label="Bet"
name="casinoSlotsBet"
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="casinoSlotsCurrency"
id="casinoSlotsCurrencyDramacoin"
value="dramacoin"
checked
/>
<label
class="form-check-label"
for="casinoSlotsCurrencyDramacoin"
>
<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="casinoSlotsCurrency"
id="casinoSlotsCurrencyMarseybux"
value="marseybux"
/>
<label
class="form-check-label"
for="casinoSlotsCurrencyMarseybux"
>
<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 class="lottery-page--stat-values">
<div class="form-check">
<input
class="form-check-input"
type="radio"
name="casinoSlotsCurrency"
id="casinoSlotsCurrencyDramacoin"
value="dramacoin"
checked
/>
<label
class="form-check-label"
for="casinoSlotsCurrencyDramacoin"
>
<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="casinoSlotsCurrency"
id="casinoSlotsCurrencyMarseybux"
value="marseybux"
/>
<label
class="form-check-label"
for="casinoSlotsCurrencyMarseybux"
>
<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>
<button
type="button"
class="btn btn-success lottery-page--action"
id="casinoSlotsPull"
style="width: 100%"
onclick="pullSlots()"
>
Pull
</button>
</div>
</div>
</div>
</div>
<button
type="button"
class="btn btn-success lottery-page--action"
id="casinoSlotsPull"
style="width: 100%"
onclick="pullSlots()"
>
Pull
</button>
</div>
</div>
</div>
</div>
<!-- Blackjack -->
<div id="blackjack-block" class="casino-block">
<div class="casino-block-title">
Blackjack
<hr style="flex: 1; margin-left: 1rem" />
</div>
<div>Give us a moment to fix things.</div>
</div>
<!-- Blackjack -->
<div id="blackjack-block" class="casino-block">
<div class="casino-block-title">
Blackjack
<hr style="flex: 1; margin-left: 1rem" />
</div>
<div class="casino-block-inner">
<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 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>
{% 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 %}
<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>
<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>
@ -348,7 +348,7 @@
<i class="fas fa-arrow-alt-up mx-0" aria-hidden="true"></i>
</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';">
<i class="fas fa-arrow-alt-down mx-0" aria-hidden="true"></i>
@ -383,7 +383,7 @@
{% endif %}
<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>
{% if v and request.path.startswith('/@') and v.admin_level < 2 %}
@ -436,7 +436,7 @@
<ul class="dropdown-menu">
{% 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>
{% endif %}
@ -458,7 +458,7 @@
{% endif %}
{% 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>
{% endif %}

View File

@ -104,7 +104,7 @@ Text 2
<tr>
<td>Pat Emojis</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>
<td>Pat User</td>
@ -412,7 +412,7 @@ Text 2
Text in a pre element
is displayed in a fixed-width
font, and it preserves
both spaces and
both spaces and
line breaks
&lt;/pre&gt;
</pre></td>
@ -421,7 +421,7 @@ Text 2
Text in a pre element
is displayed in a fixed-width
font, and it preserves
both spaces and
both spaces and
line breaks
</pre>
</td>

View File

@ -106,7 +106,7 @@
{% 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>
{% 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 %}

View File

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

View File

@ -13,7 +13,7 @@
{% block content %}
<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">
<li class="nav-item">
<a class="nav-link py-3{% if request.path == '/notifications' %} active{% endif %}" href="/notifications">
@ -52,7 +52,7 @@
</li>
{% endif %}
</ul>
</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>

View File

@ -3,7 +3,7 @@
<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="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">
</div>
<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="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'] -%}

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="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 %}
<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>
{% 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="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 %}
{% 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>
</li>
<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 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>
</ul>
</div>

View File

@ -166,7 +166,7 @@
</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-dialog modal-dialog-centered" role="document">
<div class="modal-content">

View File

@ -44,7 +44,7 @@
{% endif %}
<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)">
{% 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 %}>
{{entry}}
</option>
@ -490,7 +490,7 @@
<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 %}">
<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;
<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">
@ -610,7 +610,7 @@
<div class="w-lg-100">
<form id="profile-bio" action="/settings/profile" method="post" enctype="multipart/form-data">
<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">
<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;
@ -645,7 +645,7 @@
<div class="w-lg-100">
<form id="profile-friends" action="/settings/profile" method="post" enctype="multipart/form-data">
<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>
<div class="d-flex">
<small>Limit of 500 characters</small>
@ -664,7 +664,7 @@
<div class="w-lg-100">
<form id="profile-enemies" action="/settings/profile" method="post" enctype="multipart/form-data">
<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>
<div class="d-flex">
<small>Limit of 500 characters</small>
@ -684,7 +684,7 @@
<div class="w-lg-100">
<form id="profile-sig" action="/settings/profile" method="post" enctype="multipart/form-data">
<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">
<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;

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>
<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"><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>
<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 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>
@ -877,7 +877,7 @@
<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>
<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>
@ -929,7 +929,7 @@
</span>
{% 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 %}
<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="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;
<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;
<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;
<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>

View File

@ -33,12 +33,12 @@
<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>
<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-down score-down text-muted{% if voted!=-1 %} d-none{% endif %}"></span>
<div class="post-{{p.id}}-down arrow-down mx-auto">
<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-down score-down text-muted{% if voted!=-1 %} d-none{% endif %}"></span>
<div class="post-{{p.id}}-down arrow-down mx-auto">
</div>
</div>
@ -119,17 +119,17 @@
<div class="post-actions mx-auto">
<ul class="list-inline">
<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>
</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-down score-down 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-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>
</span>
</li>

View File

@ -17,7 +17,7 @@
{% 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 downs=p.downvotes %}

View File

@ -59,7 +59,7 @@
{% endblock %}
</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" %}
@ -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>
<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>
@ -137,7 +137,7 @@
Toggle preview
</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>
</small>
&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>
</small>
&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;
<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 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">
<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;
<div class="custom-control custom-checkbox">
<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>
{% 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 -%}
<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 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">
<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;
<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 %}>