(() => { const FAV_KEY = "fanza_widget_favs_v1"; function loadFavs() { try { const raw = localStorage.getItem(FAV_KEY); if (!raw) return new Set(); const arr = JSON.parse(raw); if (!Array.isArray(arr)) return new Set(); return new Set(arr.map(String)); } catch (e) { return new Set(); } } function saveFavs(set) { try { localStorage.setItem(FAV_KEY, JSON.stringify(Array.from(set))); } catch (e) {} } function num(v, def = 0) { const x = parseFloat(v); return Number.isFinite(x) ? x : def; } function getTextAttr(el, name) { return (el.getAttribute(name) || "").trim(); } function parseCSV(s) { if (!s) return []; return s.split(",").map(x => x.trim()).filter(Boolean); } function uniq(arr) { return Array.from(new Set(arr)); } function initWidget(root) { const favs = loadFavs(); const grid = root.querySelector("#item-grid") || root.querySelector(".item-grid"); if (!grid) return; const cards = Array.from(grid.querySelectorAll(".item-card")); // mark fav cards.forEach(card => { const cid = getTextAttr(card, "data-content-id"); if (cid && favs.has(cid)) card.classList.add("is-fav"); const btn = card.querySelector(".fav-btn"); if (btn) { btn.addEventListener("click", (e) => { e.preventDefault(); e.stopPropagation(); if (!cid) return; if (favs.has(cid)) { favs.delete(cid); card.classList.remove("is-fav"); } else { favs.add(cid); card.classList.add("is-fav"); } saveFavs(favs); updateFavInfo(); apply(); }); } }); // toolbar might not exist in fragment views const sortSel = root.querySelector("#sort-select"); const labelSel = root.querySelector("#label-select"); const priceSel = root.querySelector("#price-filter"); const reviewSel = root.querySelector("#review-filter"); const searchInp = root.querySelector("#keyword-search"); const soloOnly = root.querySelector("#solo-only"); const favOnly = root.querySelector("#fav-only"); const genreWrap = root.querySelector("#genre-wrap"); const countInfo = root.querySelector("#count-info"); const favInfo = root.querySelector("#fav-info"); const moreBtn = root.querySelector("#more-button"); // build label options if (labelSel) { const labels = uniq(cards.map(c => getTextAttr(c, "data-label-name")).filter(Boolean)).sort((a,b)=>a.localeCompare(b,'ja')); labels.forEach(l => { const opt = document.createElement("option"); opt.value = l; opt.textContent = l; labelSel.appendChild(opt); }); } // build genre buttons (top frequency) let activeGenre = ""; if (genreWrap) { const freq = new Map(); cards.forEach(c => { parseCSV(getTextAttr(c, "data-genre-names")).forEach(g => { freq.set(g, (freq.get(g) || 0) + 1); }); }); const sorted = Array.from(freq.entries()).sort((a,b)=>b[1]-a[1]).slice(0, 14); if (sorted.length) { const allBtn = document.createElement("button"); allBtn.type = "button"; allBtn.className = "genre-btn is-active"; allBtn.textContent = "すべて"; allBtn.addEventListener("click", () => { activeGenre = ""; Array.from(genreWrap.querySelectorAll(".genre-btn")).forEach(x => x.classList.remove("is-active")); allBtn.classList.add("is-active"); apply(); }); genreWrap.appendChild(allBtn); sorted.forEach(([g]) => { const b = document.createElement("button"); b.type = "button"; b.className = "genre-btn"; b.textContent = g; b.addEventListener("click", () => { activeGenre = g; Array.from(genreWrap.querySelectorAll(".genre-btn")).forEach(x => x.classList.remove("is-active")); b.classList.add("is-active"); apply(); }); genreWrap.appendChild(b); }); } } // pagination let pageSize = 24; let visibleLimit = pageSize; if (moreBtn) { moreBtn.addEventListener("click", () => { visibleLimit += pageSize; apply(true); }); } function updateCount(visible, total) { if (!countInfo) return; countInfo.textContent = `表示: ${visible} / ${total}`; } function updateFavInfo() { if (!favInfo) return; favInfo.textContent = `お気に入り: ${favs.size}`; } updateFavInfo(); function matchPrice(card) { if (!priceSel) return true; const v = priceSel.value || "all"; if (v === "all") return true; const p = num(getTextAttr(card, "data-price"), 0); if (v === "~1000") return p > 0 && p <= 1000; if (v === "1000-2000") return p >= 1000 && p <= 2000; if (v === "2000-3000") return p >= 2000 && p <= 3000; if (v === "3000~") return p >= 3000; return true; } function matchReview(card) { if (!reviewSel) return true; const v = reviewSel.value || "all"; const r = num(getTextAttr(card, "data-review"), 0); if (v === "all") return true; if (v === "has") return r > 0; if (v === "4.0") return r >= 4.0; if (v === "4.5") return r >= 4.5; return true; } function matchLabel(card) { if (!labelSel) return true; const v = labelSel.value || "all"; if (v === "all") return true; return getTextAttr(card, "data-label-name") === v; } function matchSearch(card) { if (!searchInp) return true; const q = (searchInp.value || "").trim().toLowerCase(); if (!q) return true; const title = (card.querySelector(".title")?.textContent || "").trim().toLowerCase(); const actress = getTextAttr(card, "data-actress-names").toLowerCase(); const maker = getTextAttr(card, "data-maker-name").toLowerCase(); const series = getTextAttr(card, "data-series-name").toLowerCase(); const genre = getTextAttr(card, "data-genre-names").toLowerCase(); const label = getTextAttr(card, "data-label-name").toLowerCase(); const blob = [title, actress, maker, series, genre, label].join(" "); return blob.includes(q); } function matchSolo(card) { if (!soloOnly) return true; if (!soloOnly.checked) return true; const n = parseInt(getTextAttr(card, "data-actress-count") || "0", 10); return n === 1; } function matchFav(card) { if (!favOnly) return true; if (!favOnly.checked) return true; const cid = getTextAttr(card, "data-content-id"); return cid && favs.has(cid); } function matchGenre(card) { if (!activeGenre) return true; const genres = parseCSV(getTextAttr(card, "data-genre-names")); return genres.includes(activeGenre); } function sortCards(list) { if (!sortSel) return list; const mode = sortSel.value || "rank"; const copy = list.slice(); copy.sort((a,b) => { if (mode === "rank") { return num(getTextAttr(a,"data-rank"), 999999) - num(getTextAttr(b,"data-rank"), 999999); } if (mode === "date") { // date string compare (YYYY-MM-DD ...) return getTextAttr(b,"data-date").localeCompare(getTextAttr(a,"data-date")); } if (mode === "review") { return num(getTextAttr(b,"data-review"), 0) - num(getTextAttr(a,"data-review"), 0); } if (mode === "price-asc") { return num(getTextAttr(a,"data-price"), 0) - num(getTextAttr(b,"data-price"), 0); } if (mode === "price-desc") { return num(getTextAttr(b,"data-price"), 0) - num(getTextAttr(a,"data-price"), 0); } return 0; }); return copy; } function apply(keepLimit = false) { if (!keepLimit) visibleLimit = pageSize; // filter let filtered = cards.filter(card => matchLabel(card) && matchPrice(card) && matchReview(card) && matchSearch(card) && matchSolo(card) && matchFav(card) && matchGenre(card) ); // sort filtered = sortCards(filtered); // show/hide with pagination const total = filtered.length; const visible = Math.min(total, visibleLimit); // hide all first cards.forEach(c => { c.style.display = "none"; }); filtered.slice(0, visible).forEach(c => { c.style.display = ""; }); updateCount(visible, total); if (moreBtn) { moreBtn.disabled = visible >= total; moreBtn.style.display = total > pageSize ? "" : (total > 0 ? "none" : "none"); } } // listeners [sortSel,labelSel,priceSel,reviewSel,searchInp,soloOnly,favOnly].forEach(el => { if (!el) return; const evt = el.tagName === "INPUT" ? (el.type === "search" ? "input" : "change") : "change"; el.addEventListener(evt, () => apply()); }); apply(); } function boot() { document.querySelectorAll(".fanza-widget").forEach(initWidget); } if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", boot); } else { boot(); } })();
Warning: Cannot modify header information - headers already sent by (output started at /home/wp669230/fresh-shorouto.com/public_html/wp-content/plugins/fanza-actress-widget-loader-unified/fanza-actress-widget-loader-unified.php:1) in /home/wp669230/fresh-shorouto.com/public_html/wp-includes/sitemaps/class-wp-sitemaps-renderer.php on line 126
https://fresh-shorouto.com/wp-sitemap-posts-post-1.xmlhttps://fresh-shorouto.com/wp-sitemap-posts-page-1.xmlhttps://fresh-shorouto.com/wp-sitemap-taxonomies-category-1.xmlhttps://fresh-shorouto.com/wp-sitemap-taxonomies-actress_name-1.xmlhttps://fresh-shorouto.com/wp-sitemap-users-1.xml