DCInside 갤러리 세로모니터 폭맞춤 토글 (기본 OFF)
// ==UserScript==
// @name DCInside Vertical Fit Toggle
// @namespace https://chat.openai.com/
// @version 0.0.2
// @description DCInside 갤러리 세로모니터 폭맞춤 토글 (기본 OFF)
// @match *://gall.dcinside.com/*
// @run-at document-idle
// @grant none
// ==/UserScript==
(() => {
if (location.hostname !== "gall.dcinside.com") return;
const STYLE_ID = "dc-fit-toggle-style";
const BTN_ID = "dc-fit-toggle-btn";
const KEY = "dc_fit_mode_enabled";
let observer = null;
let timer = null;
let burstTimers = [];
let lastUrl = "";
let isApplying = false;
function isEnabled() {
return localStorage.getItem(KEY) === "1";
}
function setEnabled(v) {
localStorage.setItem(KEY, v ? "1" : "0");
}
function getRoot() {
return document.querySelector(
"#top.dcwrap.width1160.list_wrap, #top.dcwrap.width1160, #top.dcwrap, .dcwrap.width1160.list_wrap, .dcwrap.width1160"
);
}
function isTypingTarget(el) {
if (!el) return false;
const tag = el.tagName;
return (
el.isContentEditable ||
tag === "INPUT" ||
tag === "TEXTAREA" ||
tag === "SELECT"
);
}
function injectBaseStyle() {
let style = document.getElementById(STYLE_ID);
if (!style) {
style = document.createElement("style");
style.id = STYLE_ID;
document.head.appendChild(style);
}
style.textContent = `
#${BTN_ID} {
position: fixed;
right: 12px;
bottom: 12px;
z-index: 2147483647;
border: 1px solid #666;
background: #222;
color: #fff;
padding: 8px 12px;
border-radius: 8px;
font-size: 13px;
cursor: pointer;
opacity: 0.92;
}
#${BTN_ID}:hover {
opacity: 1;
}
html.dc-fit-on footer.dcfoot.type1 .dc_all {
display: none !important;
}
html.dc-fit-on,
html.dc-fit-on body {
margin: 0 !important;
min-width: 0 !important;
overflow-x: auto !important;
}
html.dc-fit-on [data-dc-fit-root="1"] {
box-sizing: border-box !important;
width: 100% !important;
max-width: 100% !important;
min-width: 0 !important;
padding-left: 8px !important;
padding-right: 8px !important;
margin-left: 0 !important;
margin-right: 0 !important;
left: 0 !important;
transform: none !important;
}
html.dc-fit-on [data-dc-fit-root="1"] [data-dc-kill-minw="1"] {
min-width: 0 !important;
}
html.dc-fit-on [data-dc-fit-root="1"] [data-dc-kill-width="1"] {
width: 100% !important;
max-width: 100% !important;
margin-left: 0 !important;
margin-right: 0 !important;
left: 0 !important;
transform: none !important;
}
html.dc-fit-on [data-dc-fit-root="1"] .right_content,
html.dc-fit-on [data-dc-fit-root="1"] .content_right,
html.dc-fit-on [data-dc-fit-root="1"] .rank_wrap,
html.dc-fit-on [data-dc-fit-root="1"] .issue_wrap {
display: none !important;
}
html.dc-fit-on .comment_wrap,
html.dc-fit-on .comment_box,
html.dc-fit-on .allcomment_box,
html.dc-fit-on .cmt_list,
html.dc-fit-on .comment_list,
html.dc-fit-on .reply_list,
html.dc-fit-on .view_comment {
width: auto !important;
max-width: none !important;
}
html.dc-fit-on .comment_wrap table,
html.dc-fit-on .comment_wrap tbody,
html.dc-fit-on .comment_wrap tr,
html.dc-fit-on .comment_wrap td,
html.dc-fit-on .comment_wrap th,
html.dc-fit-on .comment_box table,
html.dc-fit-on .comment_box tbody,
html.dc-fit-on .comment_box tr,
html.dc-fit-on .comment_box td,
html.dc-fit-on .comment_box th,
html.dc-fit-on .allcomment_box table,
html.dc-fit-on .allcomment_box tbody,
html.dc-fit-on .allcomment_box tr,
html.dc-fit-on .allcomment_box td,
html.dc-fit-on .allcomment_box th,
html.dc-fit-on .cmt_list table,
html.dc-fit-on .cmt_list tbody,
html.dc-fit-on .cmt_list tr,
html.dc-fit-on .cmt_list td,
html.dc-fit-on .cmt_list th {
width: auto !important;
max-width: none !important;
min-width: 0 !important;
}
html.dc-fit-on .date_time,
html.dc-fit-on .comment_date,
html.dc-fit-on .reply_date,
html.dc-fit-on [class*="date"] {
white-space: nowrap !important;
}
`;
}
function clearMarksFromRoot(root) {
if (!root) return;
root.removeAttribute("data-dc-fit-root");
root.querySelectorAll("[data-dc-kill-minw='1']").forEach((el) => {
el.removeAttribute("data-dc-kill-minw");
el.style.removeProperty("min-width");
});
root.querySelectorAll("[data-dc-kill-width='1']").forEach((el) => {
el.removeAttribute("data-dc-kill-width");
el.style.removeProperty("width");
el.style.removeProperty("max-width");
el.style.removeProperty("margin-left");
el.style.removeProperty("margin-right");
el.style.removeProperty("left");
el.style.removeProperty("transform");
});
}
function clearAllMarks() {
document.querySelectorAll("[data-dc-fit-root='1']").forEach(clearMarksFromRoot);
}
function shouldSkipFit(el) {
if (!el || el === document.documentElement || el === document.body) return false;
if (
el.closest(
".comment_wrap, .comment_box, .allcomment_box, .cmt_list, .comment_list, .reply_list, .view_comment"
)
) {
return true;
}
return ["TABLE", "TBODY", "TR", "TD", "TH", "IMG", "VIDEO", "IFRAME"].includes(el.tagName);
}
function applyFit() {
const root = getRoot();
if (!root) return false;
clearMarksFromRoot(root);
root.setAttribute("data-dc-fit-root", "1");
const all = [root, ...root.querySelectorAll("*")];
const vw = window.innerWidth;
for (const el of all) {
if (shouldSkipFit(el)) continue;
const cs = getComputedStyle(el);
const minw = parseFloat(cs.minWidth) || 0;
const width = parseFloat(cs.width) || 0;
const rect = el.getBoundingClientRect();
if (minw >= 1100) {
el.setAttribute("data-dc-kill-minw", "1");
el.style.setProperty("min-width", "0", "important");
}
if (width >= 1100 || rect.width >= 1100 || rect.width > vw + 80) {
el.setAttribute("data-dc-kill-width", "1");
el.style.setProperty("width", "100%", "important");
el.style.setProperty("max-width", "100%", "important");
el.style.setProperty("margin-left", "0", "important");
el.style.setProperty("margin-right", "0", "important");
el.style.setProperty("left", "0", "important");
el.style.setProperty("transform", "none", "important");
}
}
document.documentElement.classList.add("dc-fit-on");
return true;
}
function disableFit() {
document.documentElement.classList.remove("dc-fit-on");
clearAllMarks();
}
function updateButton() {
const btn = document.getElementById(BTN_ID);
if (!btn) return;
btn.textContent = isEnabled() ? "폭맞춤 ON" : "폭맞춤 OFF";
btn.title = "단축키: Ctrl + Space";
}
function toggleFit(reason = "toggle") {
const next = !isEnabled();
setEnabled(next);
runNow(reason);
}
function renderButton() {
let btn = document.getElementById(BTN_ID);
if (!btn) {
btn = document.createElement("button");
btn.id = BTN_ID;
document.body.appendChild(btn);
btn.addEventListener("click", () => {
toggleFit("toggle-button");
});
}
updateButton();
}
function runNow(reason = "") {
injectBaseStyle();
renderButton();
const sameUrl = location.href === lastUrl;
const prevX = window.scrollX;
const prevY = window.scrollY;
if (!isEnabled()) {
disableFit();
updateButton();
lastUrl = location.href;
return false;
}
const root = getRoot();
if (!root) {
updateButton();
lastUrl = location.href;
return false;
}
isApplying = true;
const applied = applyFit();
if (sameUrl) {
requestAnimationFrame(() => {
window.scrollTo(prevX, prevY);
});
}
updateButton();
lastUrl = location.href;
requestAnimationFrame(() => {
isApplying = false;
});
return applied;
}
function scheduleRun(reason = "") {
clearTimeout(timer);
timer = setTimeout(() => runNow(reason), 150);
}
function scheduleBurst(reason = "") {
burstTimers.forEach(clearTimeout);
burstTimers = [];
const delays = [0, 180];
for (const delay of delays) {
burstTimers.push(setTimeout(() => runNow(`${reason}+${delay}`), delay));
}
}
function setupRouteWatchers() {
if (window.__dcFitRouteWatchersInstalled) return;
window.__dcFitRouteWatchersInstalled = true;
const wrapHistoryMethod = (name) => {
const original = history[name];
history[name] = function (...args) {
const ret = original.apply(this, args);
window.dispatchEvent(new Event("dc-fit-routechange"));
return ret;
};
};
wrapHistoryMethod("pushState");
wrapHistoryMethod("replaceState");
window.addEventListener("dc-fit-routechange", () => {
if (isEnabled()) scheduleBurst("history");
});
window.addEventListener("popstate", () => {
if (isEnabled()) setTimeout(() => scheduleBurst("popstate"), 0);
});
window.addEventListener("hashchange", () => {
if (isEnabled()) scheduleBurst("hashchange");
});
window.addEventListener("pageshow", () => {
renderButton();
if (isEnabled()) scheduleBurst("pageshow");
else updateButton();
});
window.addEventListener("resize", () => {
if (isEnabled()) scheduleRun("resize");
});
document.addEventListener(
"keydown",
(e) => {
if (e.repeat) return;
if (isTypingTarget(e.target)) return;
if (e.ctrlKey && !e.altKey && !e.shiftKey && !e.metaKey && e.code === "Space") {
e.preventDefault();
e.stopPropagation();
toggleFit("toggle-hotkey");
}
},
true
);
document.addEventListener(
"click",
(e) => {
const a = e.target.closest("a");
if (!a || !isEnabled()) return;
setTimeout(() => scheduleBurst("click"), 0);
},
true
);
observer = new MutationObserver(() => {
if (isApplying || !isEnabled()) return;
if (location.href !== lastUrl) {
scheduleBurst("mutation-url");
return;
}
const root = getRoot();
if (!root) return;
if (!root.hasAttribute("data-dc-fit-root")) {
scheduleRun("mutation-root");
}
});
observer.observe(document.documentElement, {
childList: true,
subtree: true
});
}
setupRouteWatchers();
injectBaseStyle();
renderButton();
updateButton();
if (isEnabled()) {
scheduleBurst("init");
} else {
disableFit();
}
})();