$.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", ""); } } });