Baslimbus Owned Toggle

패시브 페이지에서 '내 소유'만 보기 토글

Você precisará instalar uma extensão como Tampermonkey, Greasemonkey ou Violentmonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey ou Violentmonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey ou Userscripts para instalar este script.

Você precisará instalar uma extensão como o Tampermonkey para instalar este script.

Você precisará instalar um gerenciador de scripts de usuário para instalar este script.

(Eu já tenho um gerenciador de scripts de usuário, me deixe instalá-lo!)

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

(Eu já possuo um gerenciador de estilos de usuário, me deixar fazer a instalação!)

// ==UserScript==
// @name Baslimbus Owned Toggle
// @match https://baslimbus.info/passive
// @namespace https://dlwlsdn3642.github.io/
// @description  패시브 페이지에서 '내 소유'만 보기 토글
// @version   1.0.0
// @license      MIT
// @run-at document-idle
// @grant none
// @namespace
// ==/UserScript==

(function () {
  const SEASON_KIND = 6;
  const OWNED_LS_KEY = 'selectedIdentities';
  const BTN_LABEL_HTML = '<span class="text-xl text-white">내 소유</span>';
  const BTN_ID = 'owned-support-btn';

  function injectPageScript(js) {
    const s = document.createElement('script');
    s.textContent = js;
    (document.head || document.documentElement).appendChild(s);
    s.remove();
  }

  injectPageScript(`
    (function(){
      const SEASON_KIND=${SEASON_KIND};
      const OWNED_LS_KEY='${OWNED_LS_KEY}';
      const state={on:false,idx:null};
      const norm=s=>(s??'').toString().trim().replace(/\\s+/g,'').normalize('NFC');
      const loadOwned=()=>{try{return new Set(JSON.parse(localStorage.getItem(OWNED_LS_KEY)||'[]').map(Number).filter(Number.isFinite));}catch{return new Set();}};
      async function buildIndex(){
        if(state.idx) return state.idx;
        const map=new Map();
        for(let p=0;p<200;p++){
          const r=await fetch('https://baslimbus.store/dictionary/paginated/identity?size=100&page='+p,{cache:'no-store'});
          if(!r.ok) break;
          const d=await r.json();
          const list=d.list||d;
          for(const it of (list||[])){
            const id=Number(it?.id);
            if(Number.isFinite(id)) map.set(\`\${norm(it?.character)}|\${norm(it?.name)}|\${it?.season??''}\`,id);
          }
          if(d.last===true) break;
        }
        state.idx=map;
        return map;
      }
      function isSkill(input,init){
        try{
          const u=typeof input==='string'?new URL(input,location.origin):new URL(input.url,location.origin);
          const m=(init&&init.method)?String(init.method).toUpperCase():'GET';
          return m==='GET'&&u.pathname.startsWith('/dictionary/paginated/skill/')&&u.pathname.endsWith('/'+SEASON_KIND);
        }catch{return false;}
      }
      const _fetch=window.fetch;
      window.fetch=async function(input,init){
        const res=await _fetch(input,init);
        if(!state.on||!isSkill(input,init)) return res;
        const ct=res.headers.get('content-type')||'';
        if(!ct.includes('application/json')) return res;
        let data; try{data=await res.clone().json();}catch{return res;}
        const list=Array.isArray(data?.list)?data.list:(Array.isArray(data)?data:null);
        if(!Array.isArray(list)||list.length===0) return res;
        const idx=await buildIndex();
        const owned=loadOwned();
        const filtered=list.filter(it=>{
          const key=\`\${norm(it?.sinnerName)}|\${norm(it?.identityName)}|\${it?.season??''}\`;
          const id=idx.get(key);
          return id!=null&&owned.has(id);
        });
        const body=Array.isArray(data)?filtered:{...data,list:filtered};
        return new Response(JSON.stringify(body),{status:res.status,statusText:res.statusText,headers:{'Content-Type':'application/json'}});
      };
      window.addEventListener('message',e=>{
        const m=e?.data; if(!m||m.__ownedSupport!==true) return;
        if(m.type==='toggle'){state.on=!!m.enabled; if(state.on&&!state.idx) buildIndex();}
      });
    })();
  `);

  let btn=null, on=false;

  function setBtn() {
    if (!btn) return;
    btn.classList.remove('bg-primary-400','border','border-primary-100','bg-primary-450');
    if (on) btn.classList.add('bg-primary-400','border','border-primary-100');
    else btn.classList.add('bg-primary-450');
  }

  function mask(el, ms=100){
    if(!el) return ()=>{};
    const wrap=document.createElement('div');
    wrap.style.position='fixed';
    wrap.style.zIndex='2147483647';
    wrap.style.pointerEvents='none';
    const clone=el.cloneNode(true);
    clone.style.transition='none';
    clone.style.animation='none';
    clone.style.margin='0';
    wrap.appendChild(clone);
    document.body.appendChild(wrap);
    let stop=false;
    const place=()=>{if(stop)return;const r=el.getBoundingClientRect();wrap.style.left=r.left+'px';wrap.style.top=r.top+'px';wrap.style.width=r.width+'px';wrap.style.height=r.height+'px';requestAnimationFrame(place);};
    place();
    const off=()=>{if(stop)return;stop=true;wrap.remove();};
    setTimeout(off,ms);
    return off;
  }

  function fire(el){
    ['pointerdown','mousedown','mouseup','click'].forEach(t=>el.dispatchEvent(new MouseEvent(t,{bubbles:true,cancelable:true,view:window})));
  }

  function anchor(){
    const g=document.querySelector('div.grid.grid-cols-3');
    return g?.querySelector('button:nth-of-type(2)')||null;
  }

  function reload(){
    const el=anchor(); if(!el) return;
    mask(el,100); fire(el); setTimeout(()=>fire(el),0);
  }

  function toggle(){
    on=!on; setBtn();
    window.postMessage({__ownedSupport:true,type:'toggle',enabled:on},'*');
    let i=0;(function loop(){if(anchor()){reload();return;} if(++i<10)setTimeout(loop,120);})();
  }

  function gridNode(){
    return document.querySelector('div.bg-primary-500.w-full.rounded.p-4.flex.flex-col.gap-2 div.grid.grid-cols-3')||document.querySelector('div.grid.grid-cols-3');
  }

  function upsert(){
    const g=gridNode(); if(!g) return;
    let exist=document.getElementById(BTN_ID);
    if(exist && exist.parentElement!==g) g.appendChild(exist);
    if(!exist){
      const s=g.querySelector('button, a[role="button"], a[href]');
      exist=document.createElement('button');
      exist.id=BTN_ID;
      exist.type='button';
      exist.className=s?s.className:'inline-flex items-center justify-center rounded-md border px-3 py-1 text-sm font-medium transition bg-primary-450';
      exist.style.whiteSpace='nowrap';
      exist.innerHTML=BTN_LABEL_HTML;
      exist.addEventListener('click',toggle);
      g.appendChild(exist);
    }
    btn=exist; setBtn();
  }

  let scheduled=false;
  const schedule=()=>{ if(scheduled) return; scheduled=true; requestAnimationFrame(()=>{scheduled=false; upsert();}); };

  const mo=new MutationObserver(schedule);
  mo.observe(document.documentElement,{childList:true,subtree:true});

  window.addEventListener('load',schedule);
  document.addEventListener('visibilitychange',schedule,{passive:true});
})();