gmx - show new unread

Mark folders with new unread messages (gmx / web.de)

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

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

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         gmx - show new unread
// @name:fr      gmx - marquer nouveaux messages
// @name:de      gmx - zeige neues ungelesenes
// @name:es      gmx - mostrar nuevos no leídos
// @namespace    https://github.com/Procyon-b
// @version      0.6.2
// @description  Mark folders with new unread messages (gmx / web.de)
// @description:fr Marque les dossiers contenant de nouveaux messages (gmx / web.de)
// @description:de Markieren Sie Ordner mit neuen ungelesenen Nachrichten (gmx / web.de)
// @description:es Marcar carpetas con nuevos mensajes no leídos (gmx / web.de)
// @author       Achernar

// @match        https://3c.gmx.net/mail/client/*
// @match        https://3c-bap.gmx.net/mail/client/*
// @include      https://3c-bs.gmx.tld/mail/client/*
// @match        https://3c.web.de/mail/client/*
// @match        https://3c-bap.web.de/mail/client/*

// @match        https://navigator.gmx.net/*
// @match        https://bap.navigator.gmx.net/*
// @include      https://navigator-bs.gmx.tld/*
// @match        https://navigator.web.de/*
// @match        https://bap.navigator.web.de/*

// @grant GM_setValue
// @grant GM_getValue
// ==/UserScript==

(function() {
"use strict";

var h=location.host.split('.');

window.addEventListener('blur', function(ev) {
  window.top.postMessage({blur:1},'*');
  });
window.addEventListener('focus', function(ev) {
  window.top.postMessage({focus:1},'*');
  });

let f=document.hasFocus();
window.top.postMessage({focus:f, NOblur:!f},'*');


// main frame (top)
if (location.host.startsWith('navigator') || location.host.startsWith('bap.navigator') ) {
  var TO, marked, TOb, focus=false, mailFrame, data=document.getElementById('user-data');
  try {
  if (data) data=JSON.parse(data.text);
  }catch(e){};


  window.addEventListener('message', function(ev) {
    if (typeof ev.data != 'object') return;

    if (ev.data.getUserId) {
      ev.source.window.postMessage({userId:data.hashedUasAccountId}, ev.origin);
      mailFrame={w:ev.source.window, origin:ev.origin};
      return;
      }

    if (ev.data.focus) {
      focus=true;
      if (TOb) {clearTimeout(TOb); TOb=null;}
      else if (marked) onF();
      }
    if (ev.data.blur) {
      focus=false;
      if (TO) {
        TOb=setTimeout(function(){
          TOb=null;
          onB();
          }, 1000);
        }
      }

    mailFrame && mailFrame.w.postMessage({focus:focus, blur:!focus}, mailFrame.origin);

    function onF() {
      onB();
      TO=setTimeout( function(){
        TO=null;
        marked=false;
        document.title=document.title.replace(/^\(.*?\) */,'');
        }, 3000);
      }
    function onB() {
      if (TO) {clearTimeout(TO); TO=null;}
      }

    if (!focus && ev.data.new) {
      marked=true;
      document.title='(*) '+document.title.replace(/^\(.*?\) */,'');
      }
    }, false);

  return;
  }


// folders frame
if (location.pathname.startsWith('/mail/client/') ) {
  if (window.parent === window) return;

  var e=document.querySelector('#navigation');
  if (!e) return;

  // call top to get userid
  window.top.postMessage({getUserId:true},'*');

  var focus=false, userId, folders={}, ignore=['vfol3','vfol2'];

  window.addEventListener('message', function(ev){
    if (typeof ev.data != 'object') return;

    if (ev.data.userId) {
      userId=ev.data.userId;
      let sFolders={};
      try{sFolders=GM_getValue(userId,{});}catch(e){}
      folders={userId:userId};

      // find all folders
      let a=document.querySelectorAll('#navigation NOul, #navigation li > .folder'),
        lvl=[], L, t, id, panel, New=false;
      for (let i=0,e; e=a[i]; i++) {
        L= parseInt( (L=/lvl(\d+)/.exec(e.parentNode.parentNode.classList)) && L[1] );
        lvl[L]=id=e.id;
        if (L==1) {
          panel= (panel=e.closest('.navigation')) && (panel=panel.querySelector(':scope > .panel-head'));
          if (panel && panel.title && panel.querySelector('.badge') ) panel=panel.title;
          else panel='';
          }

        let badge=e.querySelector('.badge');
        folders[id]={lvl:L, ur:badge && parseInt(badge.innerText)};
        if (ignore.includes(id)) folders[id].ignore=1;
        // the 3 default folders without badge
        if (!badge) folders[id].nobadge=1;

        let label=e.querySelector('.label');
        if (label) {
          folders[id].name=label.innerText.trim();
          // fix for closed folders
          if ( /\((\d+)\/\d+\)$/.exec(label.title) ) folders[id].ur=parseInt(RegExp.$1);
          }

        // is part of panel with badge
        if (panel) folders[id].panel=panel;

        // is subfolder
        if (t=lvl[L-1]) {
          folders[id].p=t;
          folders[t].sub=1;
          }

        if (sFolders[id]) {
          if (t=sFolders[id].mark) folders[id].mark=t;
          if (folders[id].ur > sFolders[id].ur) {
            folders[id].mark=1;
            New=true;
            }
          if (!folders[id].ur || !badge) delete folders[id].mark;
          }
        }
      try{GM_setValue(userId, folders);}catch(e){}
      buildCSS();
      if (!f && New) window.top.postMessage({new:1},'*');
      return;
      }

    if (ev.data.focus) focus=true;
    if (ev.data.blur) focus=false;
    }, false);

  function buildCSS(ret) {
    var s='/*userscript test*/', fol={}, pan={}, i;
    for (i in folders) {
      let f=folders[i];
      if (f.ignore) continue;
      if (f.mark || f.fmark) {
        s+='div.nav-item[id="'+i+'"] .badge{background-color: red !important; color: white !important;}';
        while (f.p) {
          if (!f.ignore) fol[f.p]=1;
          f=folders[f.p];
          }
        if (f.panel) {
          pan[f.panel]=1;
          }
        }
      }
    for (i in fol) { s+='div.nav-item[id="'+i+'"]:not(.open) .badge{background-color: red;}'; }
    for (i in pan) { s+='div.navigation > div.panel-head[title="'+i+'"] .badge{background-color: red; color: white;}'; }
    if (ret) return s;
    style.innerText=s;
    }

  var st={}, options={attributes: false, subtree: true, childList: true };

  var style=document.createElement('style');
  if (style.styleSheet) style.styleSheet.cssText = '';
  else style.appendChild(document.createTextNode(''));
  (document.head || document.documentElement).appendChild(style);
  buildCSS();

  const obs = new MutationObserver(function(mutL){
    let n, o, t, save=false, New=false;
    for (let mut of mutL) {
      if ( (t=mut.target) && (t.className=='badge') ) {
        if ( (n=mut.addedNodes[0]) && (n.nodeType==3) ) {
          var div=t.closest('div.nav-item.folder'), id=div && (id=div.id), q=parseInt(n.data);
          if (!id || !folders[id]) continue;
          if (folders[id].sub) {
            let cl=div.classList.contains('open') ? false:true;
            if (cl) {
              var tit= (tit=div.querySelector('.label')) && tit.title;
              let qt=-1;
              if ( /\((\d+)\/\d+\)$/.exec(tit) ) qt=parseInt(RegExp.$1);
              if (qt>=0) q=qt;
              }
            }
          if (folders[id].ur == q) continue;
          if (q <= folders[id].ur) delete folders[id].mark;
          else {
            folders[id].mark=1;
            New=true;
            }
          folders[id].ur=q;
          save=true;
          }
        }
      }

    if (save) {
      buildCSS();
      try{GM_setValue(userId, folders);}catch(e){}
      }
    if (!focus && New) window.top.postMessage({new:1},'*');
    });
  obs.observe(e, options);

  e.addEventListener('click', function(ev){
    if (ev.ctrlKey) {
      if (ev.target.classList.contains('folder-config') || ev.target.parentNode.classList.contains('folder-config') ) {
        let fol=ev.target.closest('div.nav-item.folder');
        if (!fol || fol.classList.contains('has-open-flyout') || (!fol.classList.contains('open') && fol.firstElementChild.classList.contains('toggle')) ) return;
        ev.stopPropagation();
        if (folders[fol.id].mark) {
          delete folders[fol.id].mark;
          buildCSS();
          try{GM_setValue(userId, folders);}catch(e){}
          }
        return false;
        }
      }
    },true);

  }

})();