Nekrocuck

This commit is contained in:
2025-09-28 12:59:09 +02:00
commit a2d093954d
402 changed files with 13763 additions and 0 deletions

52
static/js/autofill.js Normal file
View File

@@ -0,0 +1,52 @@
$(function () {
let name = get_cookie("name");
let password = get_cookie("password");
let email = get_cookie("email");
if (password === "") {
password = generate_password();
set_cookie("password", password);
}
$('input[name="post_name"]').attr("value", name);
$('input[name="post_password"]').attr("value", password);
$('input[name="email"]').attr("value", email);
function generate_password() {
let chars =
"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
let password_length = 8;
let password = "";
for (let i = 0; i <= password_length; i++) {
let random_number = Math.floor(Math.random() * chars.length);
password += chars.substring(random_number, random_number + 1);
}
return password;
}
function get_cookie(cname) {
let name = cname + "=";
let decodedCookie = decodeURIComponent(document.cookie);
let ca = decodedCookie.split(";");
for (let i = 0; i < ca.length; i++) {
let c = ca[i];
while (c.charAt(0) == " ") {
c = c.substring(1);
}
if (c.indexOf(name) == 0) {
return c.substring(name.length, c.length);
}
}
return "";
}
function set_cookie(cname, cvalue) {
document.cookie = `${cname}=${cvalue};path=/`;
}
});

25
static/js/captcha.js Normal file
View File

@@ -0,0 +1,25 @@
$(function () {
$("#get-captcha").click(function () {
let btn = $(this);
let board = btn.attr("data-board");
let reply = btn.attr("data-reply");
let req_url = `/captcha?board=${board}&reply=${reply}`;
btn.text("Získat CAPTCHA");
btn.attr("disabled", true);
btn.addClass("loading");
$.get(req_url, function (data, _) {
try {
$("#captcha-id").attr("value", data.id);
$("#captcha").html(`<img src="data:image/png;base64,${data.png}">`);
} catch {
btn.append(" [Chyba]");
}
btn.attr("disabled", false);
btn.removeClass("loading");
});
});
});

103
static/js/expand.js Normal file
View File

@@ -0,0 +1,103 @@
$(function () {
$(window).on("setup_post_events", function (event) {
setup_events($(`#${event.id}`).find(".expandable"));
});
setup_events($(".expandable"));
function setup_events(elements) {
elements.each(function() {
$(this).click(function() {
let src_link = $(this).attr("href");
let is_video = [".mpeg", ".mov", ".mp4", ".webm", ".mkv", ".ogg"].some(
(ext) => src_link.endsWith(ext)
);
if (!is_video) {
toggle_image($(this), src_link);
} else {
toggle_video($(this), src_link);
}
$(this).toggleClass("expanded");
return false;
})
})
}
function toggle_image(parent, src_link) {
let thumb = parent.find(".thumb");
let src = parent.find(".src");
if (src.length === 0) {
thumb.addClass("loading");
parent.append(`<img class="src" src="${src_link}">`);
let src = parent.find(".src");
src.hide();
src.on("load", function () {
thumb.removeClass("loading");
thumb.hide();
src.show();
parent.closest(".post-files").addClass("float-none-b");
});
return;
}
thumb.toggle();
src.toggle();
parent.closest(".post-files").toggleClass("float-none-b");
}
function toggle_video(parent, src_link) {
let expanded = parent.hasClass("expanded");
let thumb = parent.find(".thumb");
let src = parent.parent().find(".src");
if (src.length === 0) {
thumb.addClass("loading");
parent.append('<b class="closer">[Zavřít]<br></b>');
parent
.parent()
.append(
`<video class="src" src="${src_link}" controls="" loop=""></video>`
);
let src = parent.parent().find(".src");
src.hide();
src.on("loadstart", function () {
thumb.removeClass("loading");
thumb.hide();
src.show();
src.get(0).play();
parent.closest(".post-files").addClass("float-none-b");
});
return;
}
thumb.toggle();
src.toggle();
if (expanded) {
src.get(0).pause();
src.get(0).currentTime = 0;
} else {
src.get(0).play();
}
parent.closest(".post-files").toggleClass("float-none-b");
parent.find(".closer").toggle();
}
});

138
static/js/hover.js Normal file
View File

@@ -0,0 +1,138 @@
$.fn.isInViewport = function () {
let element_top = $(this).offset().top;
let element_bottom = element_top + $(this).outerHeight();
let viewport_top = $(window).scrollTop();
let viewport_bottom = viewport_top + $(window).height();
return element_bottom > viewport_top && element_top < viewport_bottom;
};
$(function () {
let cache = {};
let hovering = false;
let preview_w = 0;
let preview_h = 0;
$(window).on("setup_post_events", function (event) {
setup_events($(`#${event.id}`).find(".quote"));
});
setup_events($(".quote"));
function setup_events(elements) {
elements.on("mouseover", function (event) {
toggle_hover($(this), event);
});
elements.on("mouseout", function (event) {
toggle_hover($(this), event);
});
elements.on("click", function (event) {
toggle_hover($(this), event);
});
elements.on("mousemove", move_preview);
}
function toggle_hover(quote, event) {
hovering = event.type === "mouseover";
if ($("#preview").length !== 0 && !hovering) {
remove_preview();
return;
}
let path_segments = quote.prop("pathname").split("/");
let board = path_segments[2];
let thread = path_segments[3];
let id = quote.prop("hash").slice(1);
let post = $(`#${id}[data-board="${board}"]`);
if (post.length !== 0 && post.isInViewport()) {
post.toggleClass("highlighted", hovering);
return;
}
if (post.length !== 0 && hovering) {
create_preview(post.clone(), event.clientX, event.clientY);
return;
}
let html;
let cached_thread = cache[`${board}/${thread}`];
if (cached_thread) {
html = cached_thread[id];
post = $($.parseHTML(html));
create_preview(post, event.clientX, event.clientY);
return;
}
quote.css("cursor", "wait");
try {
$.get(`/thread-json/${board}/${thread}`, function (data) {
quote.css("cursor", "");
cache[`${board}/${thread}`] = data;
html = data[id];
post = $($.parseHTML(html));
create_preview(post, event.clientX, event.clientY);
});
} catch (e) {
quote.css("cursor", "");
console.error(e);
}
}
function move_preview(event) {
position_preview($("#preview"), event.clientX, event.clientY);
}
function create_preview(preview, x, y) {
if (!hovering) {
return;
}
preview.attr("id", "preview");
preview.addClass("box");
preview.removeClass("highlighted");
preview.css("position", "fixed");
let existing = $("#preview");
if (existing.length !== 0) {
existing.replaceWith(preview);
} else {
preview.appendTo("body");
}
preview_w = preview.outerWidth();
preview_h = preview.outerHeight();
position_preview(preview, x, y);
$(window).trigger({ type: "setup_post_events", id: "preview" });
}
function remove_preview() {
$("#preview").remove();
}
function position_preview(preview, x, y) {
let ww = $(window).width();
let wh = $(window).height();
preview.css("left", `${Math.min(x + 4, ww - preview_w)}px`);
if (preview_h + y < wh) {
preview.css("top", `${y + 4}px`);
preview.css("bottom", "");
} else {
preview.css("bottom", `${wh - y + 4}px`);
preview.css("top", "");
}
}
});

2
static/js/jquery.min.js vendored Normal file

File diff suppressed because one or more lines are too long

73
static/js/live.js Normal file
View File

@@ -0,0 +1,73 @@
$(function () {
let main = $(".thread + hr + .pagination + hr");
main.after(
'<div id="live-info" class="box inline-block"><span id="live-indicator"></span> <span id="live-status"></span></div><br>'
);
$("#live-indicator").css("background-color", "orange");
$("#live-status").text("Připojování...");
let thread = window.location.pathname.split("/").slice(-2);
let protocol;
if (window.location.protocol === "https:") {
protocol = "wss:";
} else {
protocol = "ws:";
}
let last_post = $(".thread").find(".post").last().attr("id");
let ws_location = `${protocol}//${window.location.host}/live/${thread[0]}/${thread[1]}/${last_post}`;
let ws = new WebSocket(ws_location);
let interval;
ws.addEventListener("open", function (_) {
$("#live-indicator").css("background-color", "lime");
$("#live-status").text("Připojeno pro nové příspěvky");
interval = setInterval(function () {
ws.send('{"type":"ping"}');
}, 10000);
});
ws.addEventListener("message", function (msg) {
let data = JSON.parse(msg.data);
switch (data.type) {
case "created":
$(".thread").append(data.html + "<br>");
$(window).trigger({
type: "setup_post_events",
id: data.id,
});
break;
case "updated":
$(`#${data.id}`).replaceWith(data.html);
$(window).trigger({
type: "setup_post_events",
id: data.id,
});
break;
case "removed":
$(`#${data.id}`).next("br").remove();
$(`#${data.id}`).remove();
break;
case "thread_removed":
setTimeout(function () {
$("#live-indicator").css("background-color", "red");
$("#live-status").text("Vlákno bylo odstraněno");
}, 100);
break;
default:
break;
}
});
ws.addEventListener("close", function (_) {
$("#live-indicator").css("background-color", "red");
$("#live-status").text("Odpojeno, obnov stránku");
clearInterval(interval);
});
});

173
static/js/post-form.js Normal file
View File

@@ -0,0 +1,173 @@
$(function () {
$(".open-post-form").click(function () {
$("#post-form").attr("data-visible", true);
return false;
});
$(".close-post-form").click(function () {
if (document.location.hash == "#post-form") {
document.location.hash = "";
}
$("#post-form").attr("data-visible", false);
return false;
});
});
// Stolen and modified code from jschan
$(function () {
let dragging = false;
let x_offset = 0;
let y_offset = 0;
let form = $("#post-form");
let handle = $("#post-form-handle");
let saved_top = window.localStorage.getItem("post_form_top");
let saved_left = window.localStorage.getItem("post_form_left");
if (saved_top) {
form.css("top", saved_top);
}
if (saved_left) {
form.css("left", saved_left);
form.css("right", "auto");
}
handle.on("mousedown", start);
handle.get(0).addEventListener("touchstart", start, { passive: true });
$(document).on("mouseup", stop);
$(document).on("touchend", stop);
$(window).on("resize", update_max);
$(window).on("orientationchange", update_max);
function start(event) {
dragging = true;
const rect = form.get(0).getBoundingClientRect();
switch (event.type) {
case "mousedown":
x_offset = event.clientX - rect.left;
y_offset = event.clientY - rect.top;
$(window).on("mousemove", drag);
break;
case "touchstart":
event.preventDefault();
event.stopPropagation();
x_offset = event.targetTouches[0].clientX - rect.left;
y_offset = event.targetTouches[0].clientY - rect.top;
$(window).on("touchmove", drag);
break;
default:
break;
}
}
function drag(event) {
if (!dragging) {
return;
}
update_max(event);
switch (event.type) {
case "mousemove":
form.css(
"left",
`${in_bounds(
event.clientX,
x_offset,
form.outerWidth(),
document.documentElement.clientWidth
)}px`
);
form.css(
"top",
`${in_bounds(
event.clientY,
y_offset,
form.outerHeight(),
document.documentElement.clientHeight
)}px`
);
break;
case "touchmove":
form.css(
"left",
`${in_bounds(
event.targetTouches[0].clientX,
x_offset,
form.outerWidth(),
document.documentElement.clientWidth
)}px`
);
form.css(
"top",
`${in_bounds(
event.targetTouches[0].clientY,
y_offset,
form.outerHeight(),
document.documentElement.clientHeight
)}px`
);
break;
default:
break;
}
form.css("right", "auto");
window.localStorage.setItem("post_form_top", form.css("top"));
window.localStorage.setItem("post_form_left", form.css("left"));
}
function stop() {
if (dragging) {
dragging = false;
$(window).off("mousemove");
$(window).off("touchmove");
}
}
function update_max() {
let rect = form.get(0).getBoundingClientRect();
if (rect.width === 0) {
return;
}
if (Math.floor(rect.right) > document.documentElement.clientWidth) {
form.css("left", 0);
form.css("right", "auto");
}
if (Math.floor(rect.bottom) > document.documentElement.clientHeight) {
form.css("top", 0);
}
rect = form.get(0).getBoundingClientRect();
form.css(
"max-height",
`${document.documentElement.clientHeight - rect.top}px`
);
form.css(
"max-width",
`${document.documentElement.clientWidth - rect.left}px`
);
}
function in_bounds(pos, offset, size, limit) {
if (pos - offset <= 0) {
return 0;
} else if (pos - offset + size > limit) {
return limit - size;
} else {
return pos - offset;
}
}
});

36
static/js/quote.js Normal file
View File

@@ -0,0 +1,36 @@
$(function () {
let quoted_post = window.localStorage.getItem("quoted_post");
if (quoted_post) {
$("#post-form").attr("data-visible", true);
$("#content").append(`&gt;&gt;${quoted_post}\n`);
window.localStorage.removeItem("quoted_post");
}
$(window).on("setup_post_events", function (event) {
setup_events($(`#${event.id}`).find(".quote-link"));
});
setup_events($(".quote-link"));
function setup_events(elements) {
elements.each(function () {
$(this).click(function () {
let post_id = $(this).text();
let thread_url = $(this).attr("data-thread-url");
let current_url = window.location.pathname;
if (current_url !== thread_url) {
window.localStorage.setItem("quoted_post", post_id);
window.location.href = `${thread_url}#${post_id}`;
return false;
}
$("#post-form").attr("data-visible", true);
$("#content").append(`&gt;&gt;${post_id}\n`);
return false;
});
});
}
});

125
static/js/time.js Normal file
View File

@@ -0,0 +1,125 @@
const MINUTE = 60000,
HOUR = 3600000,
DAY = 86400000,
WEEK = 604800000,
MONTH = 2592000000,
YEAR = 31536000000;
$(function () {
$(window).on("setup_post_events", function (event) {
setup_events($(`#${event.id}`).find("time"));
});
setup_events($("time"));
setInterval(() => {
setup_events($("time"));
}, 60000);
function setup_events(elements) {
elements.each(function () {
let title = $(this).attr("title");
if (!title) {
$(this).attr("title", $(this).text());
}
let rel = reltime($(this).attr("datetime"));
$(this).text(rel);
});
}
function reltime(date) {
let delta = Date.now() - Date.parse(date);
let fut = false;
if (delta < 0) {
delta = Math.abs(delta);
fut = true;
}
let minutes = Math.floor(delta / MINUTE);
let hours = Math.floor(delta / HOUR);
let days = Math.floor(delta / DAY);
let weeks = Math.floor(delta / WEEK);
let months = Math.floor(delta / MONTH);
let years = Math.floor(delta / YEAR);
let rt = "Teď";
if (minutes > 0) {
if (fut) {
rt = `za ${minutes} ${plural("minutu|minuty|minut", minutes)}`;
} else {
rt = `před ${minutes} ${plural(
"minutou|minutami|minutami",
minutes
)}`;
}
}
if (hours > 0) {
if (fut) {
rt = `za ${hours} ${plural("hodinu|hodiny|hodin", hours)}`;
} else {
rt = `před ${hours} ${plural(
"hodinou|hodinami|hodinami",
hours
)}`;
}
}
if (days > 0) {
if (fut) {
rt = `za ${days} ${plural("den|dny|dnů", days)}`;
} else {
rt = `před ${days} ${plural("dnem|dny|dny", days)}`;
}
}
if (weeks > 0) {
if (fut) {
rt = `za ${weeks} ${plural("týden|týdny", weeks)}`;
} else {
rt = `před ${weeks} ${plural("týdnem|týdny", weeks)}`;
}
}
if (months > 0) {
if (fut) {
rt = `za ${months} ${plural("měsíc|měsíce|měsíců", months)}`;
} else {
rt = `před ${months} ${plural(
"měsícem|měsíci|měsíci",
months
)}`;
}
}
if (years > 0) {
if (fut) {
rt = `za ${years} ${plural("rok|roky|let", years)}`;
} else {
rt = `před ${years} ${plural("rokem|lety|lety", years)}`;
}
}
return rt;
}
function plural(plurals, count) {
let plurals_arr = plurals.split("|");
let one = plurals_arr[0];
let few = plurals_arr[1];
let other = plurals_arr[2];
if (count === 1) {
return one;
} else if (count < 5 && count !== 0) {
return few;
} else {
return other;
}
}
});