CB User Quick Bio

lets you view a users bio content by clicking on the name in any room

Tính đến 16-10-2025. Xem phiên bản mới nhất.

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(Tôi đã có Trình quản lý tập lệnh người dùng, hãy cài đặt nó!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name         CB User Quick Bio
// @namespace    aravvn.tools
// @author       aravvn
// @license      CC-BY-NC-SA-4.0
// @version      3.2.2
// @description  lets you view a users bio content by clicking on the name in any room
// @author       aravvn
// @match        https://chaturbate.com/*
// @match        https://*.chaturbate.com/*
// @run-at       document-idle
// @grant        none
// @noframes
// ==/UserScript==

(() => {
  'use strict';

  const MENU_SEL = '#user-context-menu[data-testid="user-context-menu"]';
  const LINK_SEL = 'a[data-testid="username"][href]';
  const PANEL_ID = 'cb-biox-phone';
  const BODY_ID  = 'cb-biox-body';
  const CACHE_TTL = 5 * 60 * 1000;

  let lastUser = '';
  let debTimer = 0;
  const cache = new Map();

  const debounce = (fn, ms=70) => (...a)=>{ clearTimeout(debTimer); debTimer=setTimeout(()=>fn(...a), ms); };
  const getUserFromHref = (href) => { try { const u=new URL(href, location.origin); return u.pathname.split('/').filter(Boolean)[0]||''; } catch { return (href||'').replace(/^\/+|\/+$/g,'').split('/')[0]||''; } };
  const isVisible = (el) => { if(!el) return false; const cs=getComputedStyle(el); if(cs.display==='none'||cs.visibility==='hidden'||+cs.opacity===0) return false; const r=el.getBoundingClientRect(); return r.width>0&&r.height>0; };
  const esc = (s) => String(s ?? '').replace(/[&<>"']/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c]));
  const absUrl = (u) => { try { return new URL(u, location.origin).href; } catch { return u; } };

  const sanitizeAndRestyleHTML = (dirty) => {
    const tmp = document.createElement('div');
    tmp.innerHTML = String(dirty || '');
    tmp.querySelectorAll('script,style,link,object,embed,meta,noscript').forEach(n=>n.remove());
    tmp.querySelectorAll('*').forEach(el=>{
      [...el.attributes].forEach(a=>{ if(/^on/i.test(a.name)) el.removeAttribute(a.name); });
      ['style','align','bgcolor','border','cellpadding','cellspacing','color','face','size','id'].forEach(attr=>el.removeAttribute(attr));
      if (el.tagName === 'FONT') { const p=el.parentNode; if(p){ while(el.firstChild) p.insertBefore(el.firstChild, el); el.remove(); } return; }
      if (el.tagName === 'A') { el.target = '_blank'; el.rel = 'noopener noreferrer'; }
      if (el.tagName === 'IMG' || el.tagName === 'VIDEO') { el.removeAttribute('width'); el.removeAttribute('height'); }
      if (el.tagName === 'IFRAME') { el.removeAttribute('width'); if(!el.getAttribute('height')) el.setAttribute('height','360'); }
    });
    tmp.innerHTML = tmp.innerHTML.replace(/(?:<br\s*\/?>\s*){3,}/gi, '<br><br>');
    tmp.querySelectorAll('h1,h2,h3,h4,h5,h6').forEach(h=>h.classList.add('hc-h'));
    tmp.querySelectorAll('p,li').forEach(e=>e.classList.add('hc-p'));
    tmp.querySelectorAll('blockquote').forEach(e=>e.classList.add('hc-quote'));
    tmp.querySelectorAll('pre,code').forEach(e=>e.classList.add('hc-code'));
    tmp.querySelectorAll('table').forEach(t=>t.classList.add('hc-table'));
    tmp.querySelectorAll('a').forEach(a=>a.classList.add('hc-a'));
    tmp.querySelectorAll('img').forEach(img=>img.classList.add('hc-img'));
    tmp.querySelectorAll('video').forEach(v=>{ v.classList.add('hc-video'); v.setAttribute('controls',''); });
    tmp.querySelectorAll('iframe').forEach(f=>f.classList.add('hc-iframe'));
    tmp.querySelectorAll('table, pre').forEach(el=>{
      if (!el.parentElement) return;
      if (!el.parentElement.classList.contains('hc-wrap-scroll')) {
        const w = document.createElement('div');
        w.className = 'hc-wrap-scroll';
        el.parentElement.insertBefore(w, el);
        w.appendChild(el);
      }
    });
    tmp.querySelectorAll('p, li, blockquote, code, pre, div').forEach(el=>{
      el.style.wordBreak = 'break-word';
      el.style.overflowWrap = 'anywhere';
    });
    const wrap = document.createElement('div');
    wrap.className = 'html-clean';
    wrap.append(...[...tmp.childNodes]);
    return wrap.outerHTML;
  };

  const ensurePanel = () => {
    let panel = document.getElementById(PANEL_ID);
    if (panel) return panel;
    panel = document.createElement('div');
    panel.id = PANEL_ID;
    panel.innerHTML = `
      <style>
        #${PANEL_ID}{
          position:fixed; right:16px; bottom:16px;
          width:min(420px,92vw); height:min(86vh,820px);
          z-index:2147483646; border-radius:22px;
          background:#0f1217; color:#e9eef5;
          box-shadow:0 20px 50px rgba(0,0,0,.55);
          border:1px solid rgba(255,255,255,.08);
          display:none; overflow:hidden;
        }
        #${PANEL_ID} *{ box-sizing:border-box }
        .ph-head{ height:56px; display:flex; align-items:center; gap:10px; padding:0 14px;
          border-bottom:1px solid rgba(255,255,255,.08);
          background:linear-gradient(0deg, rgba(255,255,255,.02), rgba(255,255,255,.06));
          user-select:none; cursor:move;
        }
        .ph-username{ font:800 16px/1.15 system-ui,-apple-system,Segoe UI,Roboto,Arial }
        .ph-body{ height:calc(100% - 56px - 48px); overflow:auto; padding:14px }
        .ph-tabs{ height:48px; display:flex; gap:8px; align-items:center; padding:0 10px 8px;
          border-top:1px solid rgba(255,255,255,.08); background:linear-gradient(180deg, rgba(255,255,255,.02), rgba(255,255,255,.06));
        }
        .ph-tab{ flex:1; text-align:center; padding:8px 10px; border-radius:12px;
          border:1px solid rgba(255,255,255,.10); cursor:pointer; user-select:none;
          font:600 12px/1 system-ui,-apple-system,Segoe UI,Roboto,Arial; opacity:.9;
        }
        .ph-tab.active{ background:#141924; border-color:rgba(255,255,255,.16) }

        .card{ background:#11161f; border:1px solid rgba(255,255,255,.10); border-radius:14px; padding:12px; margin-bottom:12px }
        .card h3{ margin:0 0 8px; font:700 13px/1.2 system-ui,-apple-system,Segoe UI,Roboto,Arial; color:#f3f6fb }

        .btns{ display:flex; gap:8px; flex-wrap:wrap; margin-top:8px }
        .btn{ padding:8px 12px; border-radius:10px; border:1px solid rgba(255,255,255,.14); background:#0f141e; color:#e9eef5; cursor:pointer; text-decoration:none; font-weight:600 }
        .btn:hover{ background:#141c29 }

        .chips{ display:flex; flex-wrap:wrap; gap:8px; margin-top:10px }
        .chip{ padding:4px 10px; border:1px solid rgba(255,255,255,.16); border-radius:999px; font-size:12px; opacity:.95 }

        .json{ white-space:pre; font:12px/1.35 ui-monospace,SFMono-Regular,Consolas,Menlo,monospace;
          background:#0f141c; border:1px solid rgba(255,255,255,.10); border-radius:12px; padding:10px; overflow:auto; max-height:50vh;
        }

        /* Clean HTML Theme */
        .html-clean{ line-height:1.55; font-size:13px; color:#e9eef5 }
        .html-clean .hc-h{ margin:10px 0 6px; font-weight:800; color:#f3f6fb }
        .html-clean .hc-p{ margin:8px 0 }
        .html-clean .hc-a{ color:#9ac7ff; text-decoration:underline }
        .html-clean .hc-img, .html-clean .hc-video{ max-width:100% !important; height:auto !important; display:block; border-radius:8px; }
        .html-clean .hc-iframe{ width:100% !important; border:none; border-radius:8px; }
        .html-clean .hc-code{ background:#0b111a; border:1px solid rgba(255,255,255,.10); padding:10px; border-radius:10px; overflow-x:auto }
        .html-clean .hc-table{ width:100%; border-collapse:collapse; display:block; overflow-x:auto; }
        .html-clean .hc-table th, .html-clean .hc-table td{ padding:6px 8px; border:1px solid rgba(255,255,255,.12); vertical-align:top }
        .html-clean .hc-quote{ margin:10px 0; padding:8px 10px; border-left:3px solid #3a7bd5; background:#0f141c; border-radius:8px }
        .hc-wrap-scroll{ overflow-x:auto; max-width:100% }

        /* Media (phone style) */
        .ps-grid{ display:grid; grid-template-columns:1fr; gap:10px }
        .ps-card{ display:flex; gap:10px; border:1px solid rgba(255,255,255,.12); background:#0f141c; border-radius:12px; padding:8px }
        .ps-cover{ width:96px; height:72px; border-radius:8px; overflow:hidden; background:#0b0f15; border:1px solid rgba(255,255,255,.08) }
        .ps-cover img{ width:100%; height:100%; object-fit:cover }
        .ps-info{ flex:1; min-width:0 }
        .ps-name{ font-weight:800; white-space:nowrap; overflow:hidden; text-overflow:ellipsis }
        .ps-meta{ display:flex; gap:6px; flex-wrap:wrap; margin-top:4px }
        .badge{ padding:2px 6px; border-radius:999px; border:1px solid rgba(255,255,255,.20); font-size:11px }
        .price{ margin-left:auto; font-weight:800 }

        .flags{ margin-top:4px; display:flex; gap:6px; flex-wrap:wrap }

        /* prettier "Access" pill */
        .flag-access{
          display:inline-flex; align-items:center; gap:6px;
          padding:3px 10px; border-radius:999px;
          background:linear-gradient(135deg,#1e6b3c,#2fae62);
          color:#ecfff3; border:1px solid rgba(255,255,255,.12);
          box-shadow:0 2px 10px rgba(47,174,98,.35), inset 0 0 0 1px rgba(255,255,255,.08);
          font-size:12px; font-weight:800; letter-spacing:.02em;
          text-shadow:0 1px 0 rgba(0,0,0,.35);
        }
        .flag-access .ico{
          width:16px; height:16px; border-radius:50%;
          background:rgba(255,255,255,.18); display:inline-flex;
          align-items:center; justify-content:center; font-size:12px;
        }
        .flag-access .ico::before{ content:"✓"; line-height:1; }

        .flag-bought{
          padding:2px 8px; border:1px solid rgba(108,199,144,.45);
          color:#98e3b6; border-radius:8px; font-size:11px; background:rgba(108,199,144,.08);
        }

        .muted{ opacity:.75 }
      </style>
      <div class="ph-head" id="${PANEL_ID}-drag">
        <div class="ph-username">Quick Profile</div>
      </div>
      <div class="ph-body" id="${BODY_ID}"></div>
      <div class="ph-tabs" id="${PANEL_ID}-tabs"></div>
    `;
    document.body.appendChild(panel);

    // drag
    const handle = document.getElementById(`${PANEL_ID}-drag`);
    let sx,sy,sl,st,drag=false;
    handle.addEventListener('pointerdown',(e)=>{ drag=true; sx=e.clientX; sy=e.clientY; const r=panel.getBoundingClientRect(); sl=r.left; st=r.top; panel.setPointerCapture(e.pointerId); e.preventDefault(); });
    window.addEventListener('pointermove',(e)=>{ if(!drag) return; const nl=sl+(e.clientX-sx), nt=st+(e.clientY-sy); panel.style.left=Math.max(8,Math.min(window.innerWidth-panel.offsetWidth-8,nl))+'px'; panel.style.top=Math.max(8,Math.min(window.innerHeight-panel.offsetHeight-8,nt))+'px'; panel.style.right='auto'; panel.style.bottom='auto'; });
    window.addEventListener('pointerup',()=>drag=false);

    window.addEventListener('keydown',(e)=>{ if(e.key==='Escape' && panel.style.display!=='none') closePanel(); });

    return panel;
  };

  const closePanel = () => {
    const panel = document.getElementById(PANEL_ID);
    const body  = document.getElementById(BODY_ID);
    if (panel) panel.style.display='none';
    if (body) body.innerHTML='';
    lastUser='';
  };

  const fetchBio = async (user) => {
    const url = new URL(`/api/biocontext/${encodeURIComponent(user)}/`, location.origin).href;
    const resp = await fetch(url, { credentials:'include' });
    if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
    return resp.json();
  };
  const getBioCached = async (user) => {
    const now = Date.now();
    const hit = cache.get(user);
    if (hit && (now - hit.t) < CACHE_TTL) return hit.data;
    const data = await fetchBio(user);
    cache.set(user, { t: now, data });
    return data;
  };

  const chip = (t) => `<span class="chip">${esc(t)}</span>`;

  const renderTabs = (tabsEl, panes) => {
    tabsEl.innerHTML = panes.map(p => `<div class="ph-tab${p.active?' active':''}" data-tab="${p.id}">${p.label}</div>`).join('');
    tabsEl.querySelectorAll('.ph-tab').forEach(tab=>{
      tab.addEventListener('click', ()=>{
        const target = tab.dataset.tab;
        tabsEl.querySelectorAll('.ph-tab').forEach(t=>t.classList.toggle('active', t===tab));
        document.querySelectorAll('[data-pane]').forEach(p=> p.style.display = (p.dataset.pane===target)?'':'none');
        document.getElementById(BODY_ID)?.scrollTo({ top:0, behavior:'instant' });
      });
    });
  };

  const renderPhotoSets = (sets=[]) => {
    if (!Array.isArray(sets) || sets.length===0) return '<div class="muted">No photo/video sets.</div>';
    return `
      <div class="ps-grid">
        ${sets.map(s=>{
          const cover=s.cover_url?absUrl(s.cover_url):'';
          const name=s.name||`Set #${s.id??''}`;
          const tokens=Number.isFinite(s.tokens)?`${s.tokens} tokens`:'';
          const badges=[
            s.is_video?'VIDEO':'PHOTO',
            s.video_ready?'READY':'',
            s.video_has_sound?'SOUND':'',
            s.fan_club_only?'FC ONLY':(s.fan_club_unlock?'FC UNLOCK':''),
            s.label_text||''
          ].filter(Boolean);
          const canAccess = !!s.user_can_access;
          const purchased = !!s.user_has_purchased;
          return `
            <div class="ps-card">
              <div class="ps-cover">${cover?`<img src="${esc(cover)}" alt="">`:''}</div>
              <div class="ps-info">
                <div class="ps-name" title="${esc(name)}">${esc(name)}</div>
                <div class="ps-meta">
                  ${badges.map(b=>`<span class="badge">${esc(b)}</span>`).join('')}
                  ${tokens?`<span class="price">${esc(tokens)}</span>`:''}
                </div>
                <div class="flags">
                  ${canAccess ? `<span class="flag-access" title="You can access"><span class="ico" aria-hidden="true"></span><span>Access</span></span>` : ''}
                  ${purchased ? `<span class="flag-bought">Purchased ✓</span>` : ''}
                </div>
              </div>
            </div>
          `;
        }).join('')}
      </div>
    `;
  };

  const renderSocials = (socials=[]) => {
    if (!Array.isArray(socials) || socials.length===0) return '<div class="muted">No social offers.</div>';
    return `
      <div class="soc-list">
        ${socials.map(s=>{
          const title=s.title_name||s.name||'Social';
          const img=s.image_url?absUrl(s.image_url):'';
          const price=Number.isFinite(s.tokens)?`${s.tokens} tokens`:'';
          const link=s.link?absUrl(s.link):'#';
          const label=s.label_text?`<span class="soc-label"${s.label_color?` style="border-color:${esc(s.label_color)};color:${esc(s.label_color)}"`:''}>${esc(s.label_text)}</span>`:'';
          const flag=s.purchased?`<span class="soc-label" style="border-color:#6cc790;color:#6cc790">Purchased ✓</span>`:'';
          return `
            <a class="soc-item" href="${esc(link)}" target="_blank" rel="noopener">
              ${img?`<img class="soc-ico" src="${esc(img)}" alt="">`:`<div class="soc-ico"></div>`}
              <div>
                <div class="soc-title">${esc(title)}</div>
                <div class="soc-meta">
                  ${price?`<span class="soc-price">${esc(price)}</span>`:''}
                  ${label}${flag}
                </div>
              </div>
            </a>
          `;
        }).join('')}
      </div>
    `;
  };

  const timeAgo = (isoOrText) => {
    if (!isoOrText) return '';
    if (typeof isoOrText==='string' && /\bago\b/i.test(isoOrText)) return isoOrText;
    const d = new Date(isoOrText); if (isNaN(d)) return String(isoOrText);
    let s = Math.max(0,(Date.now()-d.getTime())/1000);
    for (const [lab,sec] of [['y',31536000],['mo',2592000],['d',86400],['h',3600],['m',60],['s',1]]) {
      const v = Math.floor(s/sec); if (v>=1) return `${v}${lab} ago`;
    }
    return 'just now';
  };

  const renderView = (username, data) => {
    const body = document.getElementById(BODY_ID);
    const panel= document.getElementById(PANEL_ID);
    const tabs = document.getElementById(`${PANEL_ID}-tabs`);
    const head = document.getElementById(`${PANEL_ID}-drag`);
    if (!body || !panel || !tabs || !head) return;

    head.querySelector('.ph-username').innerHTML = `@${esc(username)}`;

    const chips = [
      data.sex || data.subgender ? `⚧ ${data.sex || data.subgender}` : '',
      data.languages ? `🗣 ${data.languages}` : '',
      Array.isArray(data.interested_in) && data.interested_in.length ? `❤️ ${data.interested_in.join(', ')}` : '',
      data.room_status ? `● ${data.room_status}` : '',
      data.follower_count!=null ? `👥 ${data.follower_count.toLocaleString?.() ?? data.follower_count}` : '',
      data.time_since_last_broadcast ? `⏱ ${data.time_since_last_broadcast}` : (data.last_broadcast?`⏱ ${timeAgo(data.last_broadcast)}`:'')
    ].filter(Boolean).map(chip).join('');

    const filtered = { ...data };
    delete filtered.photo_sets;
    delete filtered.social_medias;
    delete filtered.about_me;
    delete filtered.wish_list;

    const profileUrl = `/${encodeURIComponent(username)}/`;
    const hasFanclub  = data.performer_has_fanclub === true;
    const fanLink     = data.fan_club_join_url || '';
    const fanCost     = data.fan_club_cost;

    const aboutHTML    = sanitizeAndRestyleHTML(data.about_me || '');
    const wishlistHTML = sanitizeAndRestyleHTML(data.wish_list || '');
    const photoSets    = Array.isArray(data.photo_sets) ? data.photo_sets : [];
    const socialsArr   = Array.isArray(data.social_medias) ? data.social_medias : [];
    const showMedia    = (photoSets && photoSets.length) || (socialsArr && socialsArr.length);

    body.innerHTML = `
      <div class="card">
        <div class="chips">${chips}</div>
        <div class="btns" style="margin-top:10px">
          <a class="btn" href="${esc(profileUrl)}" target="_blank" rel="noopener">Open profile ↗</a>
          ${ (hasFanclub && fanLink) ? `<a class="btn" href="${esc(fanLink)}" target="_blank" rel="noopener">Join Fan Club${Number.isFinite(fanCost)?` (${fanCost})`:''}</a>` : '' }
          <button class="btn" id="cb-biox-copy">⧉ Copy username</button>
        </div>
      </div>

      <div class="card" data-pane="overview">
        <h3>Overview (Raw)</h3>
        <div class="json" id="cb-biox-json">${esc(JSON.stringify(filtered, null, 2))}</div>
      </div>

      ${data.about_me ? `
      <div class="card" data-pane="about" style="display:none">
        <h3>About Me</h3>
        ${aboutHTML}
      </div>`:''}

      ${data.wish_list ? `
      <div class="card" data-pane="wishlist" style="display:none">
        <h3>Wishlist</h3>
        ${wishlistHTML}
      </div>`:''}

      ${showMedia ? `
      <div class="card" data-pane="media" style="display:none">
        <h3>Photo/Video Sets</h3>
        ${renderPhotoSets(photoSets)}
        <div style="height:10px"></div>
        <h3>Social Offers</h3>
        ${renderSocials(socialsArr)}
      </div>`:''}
    `; // <-- WICHTIG: richtiges Backtick schließt das Template

    const panes = [{ id:'overview', label:'Overview', active:true }];
    if (data.about_me)    panes.push({ id:'about',    label:'About',    active:false });
    if (data.wish_list)   panes.push({ id:'wishlist', label:'Wishlist', active:false });
    if (showMedia)        panes.push({ id:'media',    label:'Media',    active:false });
    renderTabs(tabs, panes);

    const copyBtn = body.querySelector('#cb-biox-copy');
    if (copyBtn) {
      copyBtn.addEventListener('click', async ()=>{
        try { await navigator.clipboard.writeText(username); copyBtn.textContent='✓ Copied'; }
        catch { copyBtn.textContent=username; }
        setTimeout(()=> copyBtn.textContent='⧉ Copy username', 900);
      });
    }
  };

  const processMenu = async () => {
    const menu = document.querySelector(MENU_SEL);
    if (!menu || !isVisible(menu)) { closePanel(); return; }
    const a = menu.querySelector(LINK_SEL);
    if (!a) return;

    const user = getUserFromHref(a.getAttribute('href')||'');
    if (!user || user === lastUser) return;
    lastUser = user;

    const panel = ensurePanel();
    const body  = document.getElementById(BODY_ID);

    const r = menu.getBoundingClientRect();
    panel.style.display = 'block';
    const left = Math.min(window.innerWidth - panel.offsetWidth - 8, Math.max(8, r.right + 12));
    const top  = Math.min(window.innerHeight - panel.offsetHeight - 8, Math.max(8, r.top));
    panel.style.left = left + 'px';
    panel.style.top  = top  + 'px';
    panel.style.right='auto'; panel.style.bottom='auto';

    if (body) body.innerHTML = `<div class="muted">Loading…</div>`;

    try {
      const data = await getBioCached(user);
      renderView(user, data);
      document.getElementById(BODY_ID)?.scrollTo({ top:0, behavior:'instant' });
    } catch (err) {
      if (body) body.innerHTML = `<div class="muted">Failed to load (${esc(String(err))}).</div>`;
    }
  };

  const debouncedProcess = debounce(processMenu, 80);

  const mo = new MutationObserver((muts)=>{
    let menuAdded=false, menuRemoved=false, menuChanged=false;
    for (const m of muts){
      if (m.type==='childList'){
        for (const n of m.addedNodes){ if(n instanceof HTMLElement && (n.matches?.(MENU_SEL)||n.querySelector?.(MENU_SEL))) menuAdded=true; }
        for (const n of m.removedNodes){ if(n instanceof HTMLElement && (n.matches?.(MENU_SEL)||n.querySelector?.(MENU_SEL))) menuRemoved=true; }
      } else if (m.type==='attributes'){
        const t=m.target; if(t instanceof HTMLElement && t.matches(MENU_SEL)) menuChanged=true;
      }
    }
    if (menuAdded || menuChanged){ const menu=document.querySelector(MENU_SEL); if(menu && isVisible(menu)) debouncedProcess(); }
    if (menuRemoved){ closePanel(); } else { const menu=document.querySelector(MENU_SEL); if(!menu || !isVisible(menu)) closePanel(); }
  });
  mo.observe(document.documentElement||document.body, {
    childList:true, subtree:true, attributes:true, attributeFilter:['style','class','data-testid','id']
  });

  const existing = document.querySelector(MENU_SEL);
  if (existing && isVisible(existing)) debouncedProcess();
})();