USO - Display additional infos & fixes

Display additional information. Total installs, update date, initial release date. Fix site's navigability.

À partir de 2023-03-20. Voir la dernière version.

Vous devrez installer une extension telle que Tampermonkey, Greasemonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Userscripts pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey pour installer ce script.

Vous devrez installer une extension de gestionnaire de script utilisateur pour installer ce script.

(J'ai déjà un gestionnaire de scripts utilisateur, laissez-moi l'installer !)

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

(J'ai déjà un gestionnaire de style utilisateur, laissez-moi l'installer!)

// ==UserScript==
// @name         USO - Display additional infos & fixes
// @namespace    https://github.com/Procyon-b 
// @version      0.3
// @description  Display additional information. Total installs, update date, initial release date. Fix site's navigability.
// @author       Achernar
// @match        https://userstyles.org/*
// @run-at       document-start
// @grant        none
// ==/UserScript==

(function() {
"use strict";

const DEBUG={
  xhr: 0,
  fetch: 0,
  json: 0,
  fJson: 0,
  pushS: 1,
  styles: false,
  mutPop: 1,
  addData: 1,
  jsonIgnoreEmpty: 1,
  hist: 1,
  };

var initial=true, firstPage=true;

// catch site errors
if (location.pathname.startsWith('/styles/[styleId]/')) {
  let u=location.pathname.substr(17);
  history.replaceState({}, null, u);
  location.pathname=u;
  return;
  }


// XHR -- XML
self.XMLHttpRequest = class extends self.XMLHttpRequest {
  open(...args) {
    let t=this;
    // intercept and source code
    if ( arguments[1].startsWith('https://gateway.userstyles.org/styles/getStyleCss/') ) {
      styleLoaded=styleID || -1;
      loadCSS=1;
      this.addEventListener('load', function(){
        var id=t.responseURL.split('/').pop();
        if (styles[id] && !styles[id].sourceCode) styles[id].sourceCode=JP(t.response).result;
        });
      }
    return super.open(...args);
    }
  }

// JSON.parse
var JP=JSON.parse;
JSON.parse=function(){
  var r=JP(...arguments);
  getStyles(r);
  return r;
}

// stop svg js animation
window.requestAnimationFrame=function(){}

// handle data
var stylesA=[], styles={};
var total=0, styleLoaded;
var closeBut;

function getStyles(o) {
  stylesA=parseObj(o, [], ['styles', 'stylesList', 'style']);
  stylesA.forEach((e) => {
    if (e.id && !styles[e.id]) {
      styles[e.id]=Object.assign({}, e);
      total++;
      }
    });
  if (stylesA.length) {
    if (DEBUG.styles) console.info('%c styles found ','background:green;color:white',{styles, stylesA});
    }
  }

function parseObj(o, A, n=[]) {
  var k, v;
  if ( (typeof o != 'object') || !Array.isArray(A)) return;
  for (k in o) {
    v=o[k];
    if ((typeof v == 'object') && n.includes(k)) A=A.concat(v);
    if (typeof v == 'object') A=parseObj(v, A, n);
    }
  return A;
  }

var done, newL, Tit, title, title0='Website Themes & Skins by Stylish | Userstyles.org',
    userID, cancelNextRS=0, dontCancel='', site='',
    bgPage='/', blockTitle;

if (document.readyState != 'loading') init('"already done"', 1);
else {
  document.addEventListener('DOMContentLoaded', (ev) => { init('DOM');} );
  window.addEventListener('load', (ev) => { init('wLoad', 1);} );
  }


function chkState(a) {
  var u=a[2];
  if (u == '/styles/browse') {
    if (bgPage.startsWith(u)) u=bgPage;
    } 
  else if (u.startsWith('/styles/[styleId]/')) {
    title='';
    document.title=title0;
    u=bgPage;
    }
  else if (u.startsWith('/user-profile/[...userId]')) {
    if (userID || __NEXT_DATA__) {
      u='/user-profile/'+(userID || __NEXT_DATA__.query.userId);
      if (DEBUG.pushS) console.info('changed --> replaceState: '+u);
      }
    }
  a[2]=u;
  }

// hide logo when scrolled. (saves cpu on old hardware)
var Logo, LHidden=false;
function hideLogo() {
  function toggle(set) {
    if ( Logo=(Logo && Logo.parentNode && Logo) || document.querySelector('[class^="welcome-banner_top_"] svg') )
      LHidden=Logo.classList.toggle('hideMe',set);
    else LHidden=set;
    }
  window.addEventListener('scroll', function() {
    if (window.scrollY > 200) {
      if (LHidden) return;
      toggle(true);
      }
    else if (LHidden) toggle(false);
    });
  }

var reactID;

function init(v, old) {
  if (done) return;
  newL=document.querySelector('#__next');
  if (!newL) {
    if (old && ST) ST.remove(); 
    return;
    }
  addSt();
  hideLogo();
  let t;
  done=true;
  reactID= (t=Object.keys(newL).find( (v) => v.startsWith('__react') )) && t.split('$')[1];
  
  if (__NEXT_DATA__.page == '/styles/[styleId]/[[...styleParams]]') bgPage='/';
  else if ( (__NEXT_DATA__.page == '/styles/browse/[[...categoryParams]]') || (__NEXT_DATA__.page == '/user-profile/[...userId]') )
    bgPage='/'+__NEXT_DATA__.props.metaTagsData.og.url.split('/').slice(3).join('/');
  
  let pushState=history.pushState;
  history.pushState=function(){
    let u=arguments[2];
    // ignore if same state
    if (cancelNextRS && (dontCancel == u) ) {
      dontCancel='';
      }
    else if (cancelNextRS || (u == location.pathname) ) {
      //cancelNextRS=false;
      cancelNextRS && cancelNextRS--;
      dontCancel='';
      selfTitle=blockTitle=true;
      return;
      }
    initial=false;
    firstPage=false;
    chkState(arguments);
    if (u != arguments[2]) if (DEBUG.pushS) console.info('state fixed', u, '=>', arguments[2]);
    
    if (u.startsWith('/user-profile/') && (u[13] != '[') ) userID=u.split('/')[2];
    if ( (u == '/') || u.startsWith('/user-profile/') || u.startsWith('/styles/browse/') ) bgPage=u;
    
    pushState.apply(history, arguments);
    if (location.pathname.startsWith('/styles/')) {
      addData('from pushState (path)');
      }
    }

  var hback=history.back,
      hforward=history.forward,
      hgo=history.go,
      hreplaceState=history.replaceState;
  
  history.replaceState=function() {
    if (cancelNextRS && (dontCancel == arguments[2]) ) {
      dontCancel='';
      }
    else if (cancelNextRS || (arguments[2] == location.pathname) ) {
      cancelNextRS && cancelNextRS--;
      dontCancel='';
      selfTitle=blockTitle=true;
      return;
      }
    initial=false;
    firstPage=false;
    return hreplaceState.apply(history, arguments);
    }


  window.addEventListener('popstate', (ev) => {
    if (location.pathname.startsWith('/user-profile/')) {
      if (newL.querySelector(':scope > [class^="style_mainWrapper_"]')) {
        let e=newL.querySelector(':scope > [class^="style_mainWrapper_"] a[data-stylish="close-style-page-button"]');
        if (e) {
          e.click();
          }
        }
      }
    else if (location.pathname.startsWith('/styles/')) {
      let i, r, e=document.querySelector('a[href^="'+location.pathname+'"]');

      if (!e) {
        r=newL.querySelectorAll('[class^="styles-list_styleRow_"]');
        let st, ost;
        
        let ID=location.pathname.split('/')[2]
        
        for (i=0; i < r.length; i++) {
          if (r[i]['__reactFiber$'+reactID] && (ID == r[i]['__reactFiber$'+reactID].key.split('-').pop()) ) {
            e=r[i];
            break;
            }
          }
        }

      if (!e) {
        let A=document.querySelector('a[href^="/styles/"][href*="1"]');
        if (A) {
          let react=Object.keys(A).find( (v) => v.startsWith('__reactFiber') );
          e=document.createElement('a');
          e.href=location.pathname;
          e.style='display: none !important;';
          let r=newL.querySelector(':scope > div > [class^="Home_homepageWrapper_"]')
          if (r) r.appendChild(e);
          }
        }
      if (e) e.click();
      }
    else if (location.pathname == '/') {
      let e=newL.querySelector(':scope > [class^="style_mainWrapper_"] a[data-stylish="close-style-page-button"]');
      if (e) {
        cancelNextRS=1;
        e.click();
        }
      }
    });

  
  // check Title
  var Tit = document.querySelector('title'), selfTitle=false;
  new MutationObserver(function(mutations) {
    if (selfTitle) { selfTitle=false; return; }
    if (title && !Tit.textContent.startsWith(title) ) {
      document.title=title;
      selfTitle=true;
      }
    }).observe(Tit, { attributes: false, subtree: false, childList: true });

  site=document.title.split('|')[1] || '';
  if (site) site=' |'+site;

  // detect (no) popup
  new MutationObserver(function(mutations) {
    if (!newL.querySelector(':scope > [class^="style_mainWrapper_"]')) {
      title='';
      document.title=title0;
      if (location.pathname.startsWith('/styles/')) {
        if (firstPage && !initial) {
          }
        }
      }
    else {
      addData('from is popup');
      }
    
    }).observe(newL, { attributes: false, subtree: false, childList: true });

  // mutation add-er
  new MutationObserver(function(muts) {
    let mut;
    for (mut of muts) {
      
      // new body
      if (mut.addedNodes.length && mut.previousSibling && (mut.previousSibling.className == 'Toastify') ) {
        addDataTiles();

        // new list
        let r=newL.querySelector(':scope > .Toastify ~ div[class=""]');
        if (r) console.info('adding mutation obs on', {r});
        if (r) {new MutationObserver(function(muts) {
          for (mut of muts) {
            if (mut.addedNodes.length && mut.previousSibling && (mut.previousSibling.localName == 'header') ) {
              addDataTiles();
              watchGrid();
              break;
              }
            }          
          }).observe(r, { attributes: true, subtree: false, childList: true });
          r.setAttribute('obs', 'tiles');
          }

        // added cards ?
        watchGrid();
        function watchGrid() {
          r=newL.querySelector('[class^="styles-grid_gridItems_"]');
          if (r) console.info('adding mutation obs on', {r});
          if (r) {new MutationObserver(function(muts) {
            for (mut of muts) {
              if (mut.addedNodes.length) { 
                addDataTiles();
                break;
                }
              }          
            }).observe(r, { attributes: true, subtree: false, childList: true });
            r.setAttribute('obs', 'grid');
            }
          }

        break;
        }
      
      } 
    }).observe(newL, { attributes: false, subtree: false, childList: true });
    newL.setAttribute('obs', null);

  if (location.pathname.startsWith('/styles/')) {
        setTimeout((e) => {addData('from ini '+v)}, 0);
        }
  }

var totalI, styleID, showCSS, loadCSS;

function getCSS(id, callback) {
  if (id && (typeof callback == 'function') ) {
    fetch('https://gateway.userstyles.org/styles/getStyleCss/'+id)
      .then((response) => response.json())
      .then((data) => callback(data));
    }
  }

function addData() {
  var id=styleID=location.pathname.split('/')[2],
      s=styles[id];

  if (initial) {
    // this is the only case when this has to be done.
    var t=newL.querySelector('[class^="style-header_close_"]')
    if (!closeBut || (closeBut !== t) ) {
      closeBut=t;
      if (closeBut) {
        closeBut.addEventListener('click', function() {
          cancelNextRS=1;
          dontCancel='/';
          title='';
          document.title=title0;
          history.pushState({}, null, '/');
          });
        }
      }
    }
  
  if (!s) return;
  var a=document.querySelector('#weekly-installs');
  if (!a) return;

  if (!totalI || !totalI._root.parentNode) {
    totalI=a.cloneNode(true);
    totalI.id='totalInstalls';
    totalI.dataset.tooltipContent='';
    totalI._v=totalI.querySelector(':scope div span');
    totalI._v.textContent='';
    var tt=a.nextElementSibling, ttTI;
    if ( tt.attributes.role && (tt.attributes.role.value == 'tooltip') ) ttTI=tt.cloneNode(true);
    if (ttTI) {
      a.parentNode.insertBefore(ttTI, tt.nextSibling);
      if (ttTI.childNodes.length == 1) {
        let T=document.createTextNode('Total installs');
        ttTI.insertBefore(T, ttTI.firstChild);
        }
      else ttTI.childNodes[0].textContent='Total installs';
      totalI.onmouseenter=function(){
        ttTI.style.opacity='0.9';
        ttTI.style.visibility='visible';
        ttTI.style.left=(totalI.offsetLeft - a.offsetLeft + tt.offsetLeft)+'px';
        if (ttTI._init) return;
        ttTI._init=true;
        ttTI.style.top=tt.style.top;
        ttTI.firstElementChild.setAttribute('style', tt.firstElementChild.attributes.style.value);
        };
      totalI.onmouseleave=function(){ttTI.style.opacity='0'; ttTI.style.visibility='hidden';};
      }
    var i=totalI.querySelector(':scope > svg'), i2;
    if (i) {
      i2=i.cloneNode(true);
      totalI.insertBefore(i2, i.nextSibling);
      i2.style='margin-left: -17px';
      }
    a.parentNode.insertBefore(totalI, (tt || a).nextSibling);
    totalI._root=totalI.closest('[class^="style_mainWrapper_"]');

    showCSS=totalI._root.querySelector('[class*="style-info_showCss_"]');
    if (showCSS) {
      new MutationObserver(function(mutations) {
        let e;
        if (e=showCSS.parentNode.querySelector('[class^="Popup_modalWrapper_"] textarea')) {
          if (loadCSS) {loadCSS=0; return;}
          if (styles[styleID].sourceCode) e.value=styles[styleID].sourceCode;
          else {
            e.value='';
            getCSS(styleID, function(v){e.value=styles[styleID].sourceCode=v.result;} );
            }
          }
        }).observe(showCSS.parentNode, { attributes: false, subtree: false, childList: true });
      }
    }
  else {
    let e=totalI._root.querySelector('[class^="Popup_modalWrapper_"] textarea');
    if (e) {
      if (styles[styleID].sourceCode) e.value=styles[styleID].sourceCode;
      else {
        e.value='';
        getCSS(styleID, function(v){e.value=styles[styleID].sourceCode=v.result;} );
        }
      }
    }

  let v=parseInt(s.totalInstallsCount);
  let vd=a.querySelector('span'), vdv=vd.innerText;
  if (vdv.endsWith('k')) vdv=Math.floor(parseFloat(vdv) * 1000);
  else vdv=parseInt(vdv);
  if (vdv > v) {vd.style='text-decoration: line-through; text-decoration-color: red;';}
  else vd.style='';
  if (v > 1000) v=(v/1000).toFixed(1)+'k';
  totalI._v.textContent=v;
  document.title=title=s.name+site;

  setTimeout(function(){
    if (!totalI._root.parentNode) addData('after');
    },10);
  }

function addDataTiles() {
  var a=document.querySelectorAll('[data-stylish^="strip-cube-styles"]:not(._done), [data-stylish^="grid-cube-styles"]:not(._done)');
  
  a.forEach((e) => e.classList.add('_done'));
  var i=0;
  add2Tiles();
  
  function add2Tiles() {
    var max=Date.now() + 100;
    for (; i < a.length; i++) {
      if (Date.now() > max) {
        setTimeout(add2Tiles, 0);
        return;
        }
      
      let e=a[i].querySelector('[class*="style-cube_activeUsers_"]');
      if (!e) continue;
      let h=a[i].querySelector('a[href^="/styles/"]');
      if (!h) continue;
      let s=styles[h.pathname.split('/')[2]];
      if (!s) continue;
  
      let r=a[i].querySelector('[class^="style-cube_styleDetails_"]');

      let tot=e.cloneNode(true);
      e.title='Weekly installs';
      tot.classList.add('_totalInstalls');
      tot.title='Total installs';
      let _v=tot.querySelector(':scope div span');
      _v.textContent='';
      
      let I=tot.querySelector(':scope > svg'), i2;
      if (I) {
        i2=I.cloneNode(true);
        tot.insertBefore(i2, I.nextSibling);
        i2.style='margin-left: -9px';
        }
      
      // insert total
      e.parentNode.insertBefore(tot, e);
  
      let v=parseInt(s.totalInstallsCount);
      if (v > 1000) v=(v/1000).toFixed(1)+'k';
      _v.textContent=v;
      
      // add dates
      e=document.createElement('div');
      e.className='_dates_';
      let C=s.created.split('T')[0], U=s.updated.split('T')[0];
      e.innerHTML=(C == U ? '':'<span title="Last updated">'+U+'</span>')+'<span title="Date created">'+C+'</span>';
      r.appendChild(e);
      
      // add user name
      e=a[i].querySelector('[class^="style-cube_authorAvatar_"]');
      if (e) e.title=s.user.name;
      }
    }
  }


var iST=`
.hideMe {
  display: none;
}
div[class^="style_mainWrapper__"] {
  padding-top: 0;
}

[data-stylish^="strip-cube-styles"] [class^="style-cube_topWrapper_"],
[data-stylish^="grid-cube-styles"] [class^="style-cube_topWrapper_"] {
  margin-bottom: 0;
}
[data-stylish^="strip-cube-styles"] [class^="style-cube_styleDetails_"],
[data-stylish^="grid-cube-styles"] [class^="style-cube_styleDetails_"] {
  flex-direction: row wrap;
  background: var(--bg, gray);
  margin-top: 2px;
  transition: unset !important;
  transition-delay: unset !important;
  padding-top: 2px;
  color: white;
}
[data-stylish^="strip-cube-styles"] [class^="style-cube_styleDetails_"] > *,
[data-stylish^="grid-cube-styles"] [class^="style-cube_styleDetails_"] > * {
  text-overflow: ellipsis;
  overflow: hidden;
  word-break: break-all;
}
[data-stylish^="strip-cube-styles"] [class^="style-cube_styleDetails_"] [class^="style-cube_activeUsers_"],
[data-stylish^="grid-cube-styles"] [class^="style-cube_styleDetails_"] [class^="style-cube_activeUsers_"] {
  width: auto !important;
  margin-left: auto;
}
[data-stylish^="strip-cube-styles"] [class^="style-cube_styleDetails_"] [class^="style-cube_activeUsers_"] + [class^="style-cube_activeUsers_"],
[data-stylish^="grid-cube-styles"] [class^="style-cube_styleDetails_"] [class^="style-cube_activeUsers_"] + [class^="style-cube_activeUsers_"] {
  margin-left: 1em;
}
[data-stylish^="strip-cube-styles"] [class^="style-cube_styleDetails_"] [class^="style-cube_activeUsers_"] svg,
[data-stylish^="grid-cube-styles"] [class^="style-cube_styleDetails_"] [class^="style-cube_activeUsers_"] svg {
  fill: white;
  display: block;
}
  
[data-stylish^="strip-cube-styles"] [class^="style-cube_styleDetails_"] [class^="style-cube_name_"] *,
[data-stylish^="grid-cube-styles"] [class^="style-cube_styleDetails_"] [class^="style-cube_name_"] * {
  text-overflow: ellipsis;
  overflow: hidden;
}
[data-stylish^="strip-cube-styles"] [class^="style-cube_styleDetails_"] > [class^="style-cube_styleName_"],
[data-stylish^="grid-cube-styles"] [class^="style-cube_styleDetails_"] > [class^="style-cube_styleName_"] {
  oline-height: normal;
  flex-basis: calc(100% - 40px);
}
[data-stylish^="strip-cube-styles"] [class^="style-cube_styleDetails_"] > [class^="style-cube_details_"],
[data-stylish^="grid-cube-styles"] [class^="style-cube_styleDetails_"] > [class^="style-cube_details_"] {
  width: auto !important;
  margin-left: auto;
  ooutline: 2px solid red !important;
}

[data-stylish^="strip-cube-styles"] [class*="style-cube_withHover_"]:hover,
[data-stylish^="grid-cube-styles"] [class*="style-cube_withHover_"]:hover {
  transform: unset !important;
  transition: unset !important;
  transition-delay: unset !important;
  background-color: unset !important;
  --bg: DarkSlateGrey;
}

._dates_._dates_ {
  flex-basis: 100%;
  margin-top: 2px;
  text-align: right;
}
._dates_ span {
  color: lightgray;
  font-size: 11px;
}
._dates_ span + span {
  margin-left: 1em;
}

[class^="styles-strip_stripItemsWrapper_"] > button {
  display: none !important;
}
[class^="styles-strip_stripItemsWrapper_"] {
  overflow-x: scroll;
  padding-bottom: 4px;
}
[class^="styles-strip_stripItemsWrapper_"]::-webkit-scrollbar {
  height: 8px !important;
}
[class^="styles-strip_items_"] {
  transform: unset !important;
}

:not(g):not(button):not([class^="style_mainWrapper_"])) {
  transform: unset !important;
  transition: unset !important;
  transition-delay: unset !important;
}
svg[transform*="rotate(-180)"] {
  transform: rotate(-180deg) !important;
}

[class^="category-filter_borderRadios_"] {
  display: none;
}

[class^="styles-list_styleName_"] {
  word-break: break-all;
}
`;

addSt();

var ST;
function addSt() {
  if (!iST) {
    document.documentElement.appendChild(ST);
    return;
    }
  try {
    ST=document.createElement('style');
    document.documentElement.appendChild(ST);
    ST.textContent=iST;
    iST='';
  }catch(e){
    setTimeout(addSt,0); }
}


})();