// ==UserScript==
// @name Restore Right/Middle-Click New Tab Links for manko.fun / solji.kim
// @namespace http://tampermonkey.net/
// @version 1.3.1
// @description Restore right/middle-click new tab function on manko.fun / solji.kim.
// Note: This script works properly in Tampermonkey when Developer Mode is enabled.
//
// @author VanillaMilk
// @license MIT
// @match https://manko.fun/*
// @match https://solji.kim/*
// @run-at document-end
// @grant GM_registerMenuCommand
// @grant GM_setValue
// @grant GM_getValue
// ==/UserScript==
(async function () {
'use strict'
const ID_ATTR='data-id'
const DEST_BASE='https://solji.kim/movie-info/'
const KEY_OPEN_BLANK='mf_linkifier_open_blank'
const KEY_SIDE_NAV='mf_linkifier_side_nav'
const Rt = {
en: { popular:"Popular","most popular":"Most Popular","top rated":"Top Rated",top_rated:"Top Rated",new:"New",subtitle:"Subtitle",random:"Random",category:"Category",actor:"Actor",genre:"Genre",country:"Country",usecase:"Download app",censored:"Censored",uncensored:"Uncensored",fc2:"FC2" },
zh: { popular:"热门作品","most popular":"热门作品","top rated":"最佳评分",top_rated:"最佳评分",new:"最新作品",subtitle:"字幕",random:"随机",category:"类别",actor:"演员",genre:"类型",country:"国家",usecase:"下载应用程序",censored:"马赛克",uncensored:"无马赛克",fc2:"FC2" },
tw: { popular:"受歡迎的","most popular":"受歡迎的","top rated":"最高評分",top_rated:"最高評分",new:"新的",subtitle:"字幕",random:"隨機的",category:"類別",actor:"演員",genre:"類型",country:"國家",usecase:"下載應用程式",censored:"審查",uncensored:"未經審查",fc2:"FC2" },
ja: { popular:"人気","most popular":"人気","top rated":"最高評価",top_rated:"最高評価",new:"新作",subtitle:"字幕",random:"ランダム",category:"カテゴリー",actor:"俳優",genre:"ジャンル",country:"国名",usecase:"アプリをダウンロード",censored:"修正",uncensored:"無修正",fc2:"FC2" },
ko: { popular:"인기작","most popular":"인기작","top rated":"최고평점",top_rated:"최고평점",new:"신작",subtitle:"자막",random:"랜덤",category:"카테고리",actor:"배우",genre:"장르",country:"국가",usecase:"앱 다운로드",censored:"유모",uncensored:"노모",fc2:"FC2" },
id: { popular:"Populer","most popular":"Populer","top rated":"Peringkat Teratas",top_rated:"Peringkat Teratas",new:"Baru",subtitle:"Subjudul",random:"Acak",category:"Kategori",actor:"Aktor",genre:"Genre",country:"Negara",usecase:"Unduh aplikasi",censored:"Disensor",uncensored:"Tanpa Sensor",fc2:"FC2" },
ms: { popular:"Popular","most popular":"Popular","top rated":"Tertinggi",top_rated:"Tertinggi",new:"baru",subtitle:"Sari kata",random:"rawak",category:"kategori",actor:"pelakon",genre:"Genre",country:"Negara",usecase:"Muat turun aplikasi",censored:"ditapis",uncensored:"Tidak ditapis",fc2:"FC2" },
th: { popular:"เป็นที่นิยม","most popular":"เป็นที่นิยม","top rated":"อันดับสูงสุด",top_rated:"อันดับสูงสุด",new:"ใหม่",subtitle:"คำบรรยาย",random:"สุ่ม",category:"หมวดหมู่",actor:"นักแสดงชาย",genre:"ประเภท",country:"ประเทศ",usecase:"ดาวน์โหลดแอป",censored:"เซ็นเซอร์",uncensored:"ไม่เซ็นเซอร์",fc2:"เอฟซี2" },
vi: { popular:"Phổ biến","most popular":"Phổ biến","top rated":"Đánh giá cao nhất",top_rated:"Đánh giá cao nhất",new:"Mới",subtitle:"Phụ đề",random:"Ngẫu nhiên",category:"Loại",actor:"Diễn viên",genre:"Thể loại",country:"Quốc gia",usecase:"Tải ứng dụng",censored:"Bị kiểm duyệt",uncensored:"Không kiểm duyệt",fc2:"FC2" }
}
function englishParamForKey(key){
if(!key) return null
if(key==='popular') return Rt.en['most popular'] || 'Most Popular'
return Rt.en[key] || key
}
function cleanText(t){
return (t||'').replace(/[\p{Extended_Pictographic}\p{Emoji_Presentation}\uFE0F\u200D\u2600-\u27BF]/gu,'').trim()
}
function labelToKey(label){
const t = cleanText(label).toLowerCase()
for(const map of Object.values(Rt)){
for(const [k,v] of Object.entries(map)){
if(String(v).toLowerCase()===t) return k
}
}
if(t==='top rated') return 'top rated'
if(t==='most popular') return 'most popular'
return null
}
async function gv(k,d='0'){try{return String(await GM_getValue(k,d))==='1'}catch{return false}}
async function sv(k,v){try{await GM_setValue(k,v?'1':'0')}catch{}}
let useBlank=await gv(KEY_OPEN_BLANK,'0')
let sideEnable=await gv(KEY_SIDE_NAV,'0')
if(typeof GM_registerMenuCommand==='function'){
GM_registerMenuCommand((useBlank?'☑ ':'☐ ')+'Left-click opens in new tab',async()=>{useBlank=!useBlank;await sv(KEY_OPEN_BLANK,useBlank);location.reload()})
GM_registerMenuCommand((sideEnable?'☑ ':'☐ ')+'Side buttons: page ±1',async()=>{sideEnable=!sideEnable;await sv(KEY_SIDE_NAV,sideEnable);location.reload()})
}
(function normalizePageForPagination() {
try {
const u = new URL(location.href);
const path = u.pathname;
const isEligible = /^\/(movie-list|cate-list|actor-list|home)/.test(path);
const isExcluded = /^\/(genre|maker|usecase)/.test(path);
if (isEligible && !isExcluded && !u.searchParams.has('page')) {
u.searchParams.set('page', '1');
history.replaceState(null, '', u.toString());
}
} catch {}
})();
if (sideEnable) {
let last = 0;
const side = (e) => {
if (e.button !== 3 && e.button !== 4) return;
const u = new URL(location.href);
const path = u.pathname;
const isEligible = /^\/(movie-list|cate-list|actor-list)/.test(path);
const isExcluded = /^\/(genre|maker|usecase)/.test(path);
const hasQ = u.searchParams.has('page');
const hs = u.hash.startsWith('#') ? u.hash.slice(1) : u.hash;
const sp = new URLSearchParams(hs);
const hasH = sp.has('page');
if (isEligible && !isExcluded && !hasQ && !hasH) {
u.searchParams.set('page','1');
e.stopImmediatePropagation?.(); e.stopPropagation?.(); e.preventDefault?.();
location.assign(u.toString());
return;
}
if (!hasQ && !hasH) return;
const now = Date.now(); if (now - last < 120) return; last = now;
const delta = (e.button === 3) ? -1 : 1;
let p = hasQ ? parseInt(u.searchParams.get('page') || '1', 10)
: parseInt(sp.get('page') || '1', 10);
if (!Number.isFinite(p) || p < 1) p = 1;
let n = p + delta; if (n < 1) n = 1;
if (hasQ) {
u.searchParams.set('page', String(n));
} else {
sp.set('page', String(n));
u.hash = '#' + sp.toString();
}
e.stopImmediatePropagation?.(); e.stopPropagation?.(); e.preventDefault?.();
location.assign(u.toString());
};
addEventListener('mouseup', side, true);
addEventListener('pointerup', side, true);
}
const style=document.createElement('style')
style.textContent=`
.tm-wrap{position:relative!important;}
.tm-ol{position:absolute!important;inset:0!important;width:100%!important;height:100%!important;display:block!important;background:transparent!important;text-decoration:none!important;outline:none!important;pointer-events:auto!important;}
`
document.documentElement.appendChild(style)
function wrap(el, forceNew){
if(!forceNew){
const p=el.parentElement
if(p){const cs=getComputedStyle(p);if(cs.position!=='static')return p}
}
const w=document.createElement('span');w.className='tm-wrap';w.style.position='relative'
el.parentNode?.insertBefore(w,el);w.appendChild(el);return w
}
function setOverlay(el,href,forceNew){
const w=wrap(el,forceNew)
let a=w.querySelector(':scope > a.tm-ol')
if(!a){a=document.createElement('a');a.className='tm-ol';a.rel='noopener noreferrer';w.appendChild(a)}
a.href=href
a.target=useBlank?'_blank':'_self'
return a
}
function linkifyCard(card){
if(!(card instanceof Element))return
if(!card.hasAttribute(ID_ATTR))return
if(card.__tm_done__)return
const raw=card.getAttribute(ID_ATTR)||''
const id=raw.split('?')[0].trim()
if(!id)return
setOverlay(card,DEST_BASE+encodeURIComponent(id),true)
card.__tm_done__=true
}
function scanCards(root){
root.querySelectorAll(`[${ID_ATTR}]`).forEach(linkifyCard)
linkifyCard(root)
}
scanCards(document.body||document.documentElement)
const moCards=new MutationObserver(muts=>{
for(const m of muts){
if(m.type==='childList')m.addedNodes.forEach(n=>n.nodeType===1&&scanCards(n))
else if(m.type==='attributes'&&m.attributeName===ID_ATTR)linkifyCard(m.target)
}
})
moCards.observe(document.documentElement,{childList:true,subtree:true,attributes:true,attributeFilter:[ID_ATTR]})
function hrefForTopLabel(txt){
const key = labelToKey(txt)
if(!key) return null
if(key==='category' || key==='actor') return null
if(key==='genre') return '/genre'
if(key==='country') return '/maker'
if(key==='usecase') return '/usecase'
const cat = englishParamForKey(key) || cleanText(txt)
return `/movie-list?category=${encodeURIComponent(cat)}`
}
function linkifyTopNav(root){
root.querySelectorAll('nav button').forEach(btn=>{
if(btn.__tm_top__)return
const txt=cleanText(btn.textContent||'');if(!txt)return
const href=hrefForTopLabel(txt)
if(!href) return
setOverlay(btn,href,true)
btn.__tm_top__=true
})
}
const CATEGORY_FIXED_KEYS=new Set(['censored','uncensored','popular','top rated','top_rated','fc2'])
const ACTOR_FIXED_KEYS =new Set(['censored','uncensored'])
function resolveDropdownMode(pop){
let n=pop.previousElementSibling||pop.parentElement?.previousElementSibling
if(n){
const k = labelToKey(cleanText(n.textContent||''))
if(k==='actor') return 'actor'
if(k==='category') return 'category'
}
return 'category'
}
function linkifyDropdown(pop){
const mode=resolveDropdownMode(pop)
pop.querySelectorAll('button').forEach(btn=>{
if(btn.__tm_dd__)return
const label=cleanText(btn.textContent||'');if(!label)return
const key = labelToKey(label)
let href
if(mode==='actor'){
if(key && ACTOR_FIXED_KEYS.has(key)){
const cat = englishParamForKey(key) || key
href = `/actor-list?category=${encodeURIComponent(cat)}&page=1`
}else{
href = `/actor-list?category=${encodeURIComponent(label)}&page=1`
}
}else{
if(key && CATEGORY_FIXED_KEYS.has(key)){
const cat = englishParamForKey(key) || key
href = `/cate-list?category=${encodeURIComponent(cat)}&page=1`
}else{
href = `/cate-list?category=${encodeURIComponent(label)}&page=1`
}
}
setOverlay(btn,href,true)
btn.__tm_dd__=true
})
}
function fullScan(root){
linkifyTopNav(root)
root.querySelectorAll('div.absolute').forEach(div=>{
if(div.querySelector('button'))linkifyDropdown(div)
})
}
fullScan(document.body||document.documentElement)
const moMenus=new MutationObserver(muts=>{
for(const m of muts){
if(m.type==='childList'){
m.addedNodes.forEach(n=>{if(n.nodeType===1)fullScan(n)})
}else if(m.type==='attributes'&&(m.attributeName==='class'||m.attributeName==='style'||m.attributeName==='aria-expanded')){
if(m.target instanceof Element)fullScan(m.target)
}
}
})
moMenus.observe(document.documentElement,{childList:true,subtree:true,attributes:true,attributeFilter:['class','style','aria-expanded']})
})()