Baslimbus Owned Toggle

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

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey, Greasemonkey или Violentmonkey.

Для установки этого скрипта вам необходимо установить расширение, такое как Tampermonkey.

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey или Violentmonkey.

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey или Userscripts.

Чтобы установить этот скрипт, сначала вы должны установить расширение браузера, например Tampermonkey.

Чтобы установить этот скрипт, вы должны установить расширение — менеджер скриптов.

(у меня уже есть менеджер скриптов, дайте мне установить скрипт!)

Чтобы установить этот стиль, сначала вы должны установить расширение браузера, например Stylus.

Чтобы установить этот стиль, сначала вы должны установить расширение браузера, например Stylus.

Чтобы установить этот стиль, сначала вы должны установить расширение браузера, например Stylus.

Чтобы установить этот стиль, сначала вы должны установить расширение — менеджер стилей.

Чтобы установить этот стиль, сначала вы должны установить расширение — менеджер стилей.

Чтобы установить этот стиль, сначала вы должны установить расширение — менеджер стилей.

(у меня уже есть менеджер стилей, дайте мне установить скрипт!)

// ==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});
})();