bangumi列表显示增强

在有条目列表的页面,显示条目的排名,站内评分和评分人数,好友评分和评分人数,并提供排序功能,鼠标移到排名处可查看历史记录

当前为 2018-01-10 提交的版本,查看 最新版本

// ==UserScript==
// @name         bangumi列表显示增强
// @namespace    https://github.com/bangumi/scripts/liaune
// @version      0.7
// @description  在有条目列表的页面,显示条目的排名,站内评分和评分人数,好友评分和评分人数,并提供排序功能,鼠标移到排名处可查看历史记录
// @author       Liaune,Yonjar
// @include      /^https?://(bangumi\.tv|bgm\.tv|chii\.in)/(.+?/list|.+?/tag|.+?/browser|subject_search|index)(/|\?).+$/
// @grant        GM_addStyle
// ==/UserScript==

(function() {
    GM_addStyle(`
.yonjar_bgm_userjs_rank_excellent{       background-color: #0033CC!important;}
.yonjar_bgm_userjs_rank_recommended{     background-color: #3ed715!important;}
.yonjar_bgm_userjs_rank_justsoso{        background-color: #FF6600!important;}
.yonjar_bgm_userjs_rank_refuse{          background-color: #f2050587!important;}
.yonjar_bgm_userjs_rank_undefined{       background-color: #0000001a!important;}
.friend_vote{                            font-size: 10px;   color:#4260cb;}
.yonjar_bgm_userjs_vote_0{               background-color: rgba(11, 12, 12, 0)!important;        color:black;}
.yonjar_bgm_userjs_vote_500{             background-color: rgba(128, 255, 255, 0)!important;     color:#eca609;}
.yonjar_bgm_userjs_vote_1000{            background-color: rgba(128, 144, 255, 0)!important;     color:#18a099;}
.yonjar_bgm_userjs_vote_2000{            background-color: rgba(209, 80, 205, 0)!important;      color:blue;}
.yonjar_bgm_userjs_vote_4000{            background-color: rgba(253, 62, 80, 0)!important;       color:red;}
.yonjar_bgm_userjs_rank_btn{             display: inline-block;   color: #666;   text-shadow: 0px 1px 2px #FFF;   text-decoration: none;   line-height: 20px;   margin: 0 5px 5px 0;   padding: 0 12px;   border: 1px solid #DDD;   background: -webkit-gradient(linear,left top,left bottom,from(#FCFCFC),to(#F1F1F1));   background: -moz-linear-gradient(top,#FCFCFC,#F1F1F1);   background: -o-linear-gradient(top,#FCFCFC,#F1F1F1);   -webkit-box-shadow: 0 1px 2px #EEE,inset 0 1px 1px #FFF;   -moz-box-shadow: 0 1px 2px #EEE,inset 0 1px 1px #FFF;   box-shadow: 0 1px 2px #EEE,inset 0 1px 1px #FFF;   -moz-border-radius: 4px;   -webkit-border-radius: 4px;   border-radius: 4px}
.yonjar_bgm_userjs_rank_btn:hover {      color: #FFF;   text-shadow: none;   background: #4F93CF;   background: -moz-linear-gradient(top,#6BA6D8,#4F93CF);   background: -o-linear-gradient(top,#6BA6D8,#4F93CF);   background: -webkit-gradient(linear,left top,left bottom,from(#5FA3DB),to(#72B6E3));    -webkit-box-shadow: 0 0 3px #EEE,inset 0 -1px 5px rgba(0,0,0,0.1);   -moz-box-shadow: 0 0 3px #EEE,inset 0 -1px 5px rgba(0,0,0,0.1);   box-shadow: 0 0 3px #EEE,inset 0 -1px 5px rgba(0,0,0,0.1)}
`);

    let itemsList = document.querySelectorAll('#browserItemList li.item');
    let sortstyle = -1, sortstyle1 = 1,sortstyle2 = 1,sortstyle3 = -1,sortstyle4 = 1,count=0,update=0;

    //按排名排序
    const showBtn = document.createElement('a');  showBtn.addEventListener('click', SortByRank);  showBtn.className = 'chiiBtn';  showBtn.href='javascript:;';   showBtn.textContent = '排名排序';
    //按评分人数排序
    const showBtn1 = document.createElement('a'); showBtn1.addEventListener('click', SortByVote);  showBtn1.className = 'chiiBtn'; showBtn1.href='javascript:;'; showBtn1.textContent = '人数排序';
    //按好友评价排序
    const showBtn2 = document.createElement('a'); showBtn2.addEventListener('click', SortByFriend); showBtn2.className = 'chiiBtn'; showBtn2.href='javascript:;'; showBtn2.textContent = '好友评价';
    //按自己评分排序
    const showBtn5 = document.createElement('a'); showBtn5.addEventListener('click', SortByMyPoint); showBtn5.className = 'chiiBtn'; showBtn5.href='javascript:;'; showBtn5.textContent = '我的评分';
    //按时间排序
    const showBtn3 = document.createElement('a'); showBtn3.addEventListener('click', SortByTime); showBtn3.className = 'chiiBtn'; showBtn3.href='javascript:;'; showBtn3.textContent = '时间排序';
    //更新缓存数据
    const showBtn4 = document.createElement('a'); showBtn4.addEventListener('click', Update); showBtn4.className = 'chiiBtn'; showBtn4.href='javascript:;'; showBtn4.textContent = '更新';
    //显示菜单按钮
    const showBtn0 = document.createElement('a'); showBtn0.addEventListener('click', ShowProcess); showBtn0.className = 'chiiBtn';  showBtn0.href='javascript:;';  showBtn0.textContent = 'Show';

    const You=document.querySelectorAll('#headerNeue2 .idBadgerNeue a.avatar')[0].href.split('/user/')[1];
    const User =window.location.href.match(/\/list\/(\S+)\//)? window.location.href.match(/\/list\/(\S+)\//)[1]: null;
    //为应对分页
    if(window.location.href.match(/\/index\//))     Process();
    else {document.querySelector('#browserTools').append(showBtn0);}

    function ShowProcess(){
        itemsList = document.querySelectorAll('#browserItemList li.item');
        showBtn0.style.display='none';
        Process();
    }
    //Main Program
    function Process(){
        itemsList.forEach( (elem, index) => {
            let href = elem.querySelector('a.subjectCover').href;
            let ID = href.split('/subject/')[1];

            //为每个条目添加单独刷新
            let showBtn5 = document.createElement('a');  showBtn5.className = 'l';   showBtn5.href='javascript:;';  showBtn5.textContent = '↺';  showBtn5.addEventListener('click', FetchStatus.bind(this,href,index),false);
            elem.querySelector('.inner h3').appendChild(showBtn5);
            if(localStorage.getItem(ID+'Point')){
                let info = {"rankNum": localStorage.getItem(ID+'Rank'),"Point": localStorage.getItem(ID+'Point'),"votes": localStorage.getItem(ID+'Votes'),"Point_f": localStorage.getItem(ID+'Point_f'),"Votes_f": localStorage.getItem(ID+'Votes_f')};
                DisplayStatus(ID,index,info);
            }
            else     FetchStatus(href,index);
        });
    }

    function Update(){
        update=1;
        count=0;
        itemsList.forEach( (elem, index) => {
            let href = elem.querySelector('a.subjectCover').href;
            let ID = href.split('/subject/')[1];
            //同一天只更新一次
            let date = new Date();
            let time = date.getFullYear()+"-" + (date.getMonth()+1) + "-" + date.getDate();
            let lastime = localStorage.getItem(ID+'Lastime');
            if(time != lastime)  FetchStatus(href,index);
            else {
                count+=1;
                showBtn4.textContent='更新中... (' + count + '/' + itemsList.length +')';
                if(count==itemsList.length){ location.reload(); showBtn4.textContent='更新完毕!';}}
        });
    }

    function FetchStatus(href,index){
        fetch(href,{credentials: "include"})
            .then(data => {
            return new Promise(function (resovle, reject) {
                let targetStr = data.text();
                resovle(targetStr);
            });
        })
            .then(targetStr => {
            let ID = href.split('/subject/')[1];
            //获取排名
            let canMatch = targetStr.match(/<small class="alarm">#(\S+?)<\/small>/);
            let rankNum =  canMatch ? parseInt(canMatch[1], 10) : null;
            if(canMatch)  localStorage.setItem(ID+'Rank',rankNum);

            //获取站内评分和评分人数
            let Match1 = targetStr.match(/<span class="number" property="v:average">(\S+?)<\/span>/);
            let Point = Match1? parseFloat(Match1[1]) : null;
            if(Match1)  localStorage.setItem(ID+'Point',Point);
            let Match2 = targetStr.match(/<span property="v:votes">(\S+?)<\/span>/);
            let votes = Match2? parseInt(Match2[1]) : null;
            if(Match2)  localStorage.setItem(ID+'Votes',votes);

            //获取好友评分和评分人数
            let Match3 = targetStr.match(/<span class="num">(\S+?)<\/span>/);
            let Point_f = Match3? parseFloat(Match3[1]).toFixed(1) : null;
            if(Match3)  localStorage.setItem(ID+'Point_f',Point_f);
            let Match4 = targetStr.match(/class="l">(\S+?) 人评分<\/a>/);
            let Votes_f = Match4? parseFloat(Match4[1]) : null;
            if(Match4) localStorage.setItem(ID+'Votes_f',Votes_f);

            //加入历史记录
            let date = new Date();
            let time = date.getFullYear()+"-" + (date.getMonth()+1) + "-" + date.getDate();
            let lastime = localStorage.getItem(ID+'Lastime');
            let Record = time + ' Rank #' + rankNum + ' 评分:'+ Point + ' '+ votes + ' 人评分'+ '\r\n';
            let History = localStorage.getItem(ID+'Records');
            if(History) Record = History + Record;
            if(Match1 && time!=lastime){
                localStorage.setItem(ID+'Lastime',time);
                localStorage.setItem(ID+'Records',Record);}

            let info = {"rankNum": rankNum,"Point": Point,"votes": votes,"Point_f": Point_f,"Votes_f": Votes_f};
            if(!update)  DisplayStatus(ID,index,info);
            else{
                count+=1;
                showBtn4.textContent='更新中... (' + count + '/' + itemsList.length +')';
                if(count==itemsList.length){ location.reload(); showBtn4.textContent='更新完毕!';}
            }
        });
    }

    function DisplayStatus(ID,index,info){
        let rankNum=info.rankNum,Point=info.Point,votes=info.votes,Point_f=info.Point_f,Votes_f=info.Votes_f;
        //显示排名
        let rankSp = document.createElement('span');
        rankSp.className = 'rank';
        if (rankNum >= 3000)      {rankSp.classList.add('yonjar_bgm_userjs_rank_refuse');     }
        else if (rankNum >= 2000) {rankSp.classList.add('yonjar_bgm_userjs_rank_justsoso');   }
        else if (rankNum >= 1000) { }
        else if (rankNum >= 100)  {rankSp.classList.add('yonjar_bgm_userjs_rank_recommended');}
        else if ( rankNum >0)     {rankSp.classList.add('yonjar_bgm_userjs_rank_excellent');  }
        else                      {rankSp.classList.add('yonjar_bgm_userjs_rank_undefined');  }

        rankSp.innerHTML = `<small>Rank </small>${rankNum}`;
        let note = localStorage.getItem(ID+'Records');
        rankSp.setAttribute('title', note);

        if(window.location.href.match(/\/(list|index)\//))
            document.querySelectorAll('#browserItemList .item .inner')[index].insertBefore(rankSp, document.querySelectorAll('#browserItemList .item .inner .info.tip')[index]);

        //显示站内评分和评分人数
        let rateInfo = document.createElement('p');  rateInfo.setAttribute("class","rateInfo");
        let PointSm = document.createElement('small');  PointSm.innerHTML = Point;  PointSm.setAttribute("class","fade");
        let sstars = document.createElement('span');
        let Point1 = Point ? parseInt(Point) : null;
        if(Point1)    sstars.setAttribute("class","sstars"+ Point1+ " starsinfo");
        let tip_j = document.createElement('span');  tip_j.setAttribute("class","tip_j");   tip_j.innerHTML = "("+ votes +"人评分)";
        if(votes>=4000)          tip_j.classList.add('yonjar_bgm_userjs_vote_4000');
        else if(votes>=2000)     tip_j.classList.add('yonjar_bgm_userjs_vote_2000');
        else if(votes>=1000)     tip_j.classList.add('yonjar_bgm_userjs_vote_1000');
        else if(votes>=500)      tip_j.classList.add('yonjar_bgm_userjs_vote_500');
        if(votes<500)            tip_j.classList.add('yonjar_bgm_userjs_vote_0');

        if(window.location.href.match(/\/(list|index)\//)){
            document.querySelectorAll('#browserItemList .item .inner')[index].insertBefore(rateInfo, document.querySelectorAll('#browserItemList .item .inner .info.tip')[index]);
            if(rateInfo){
                rateInfo.appendChild(sstars);
                rateInfo.appendChild(PointSm);
                rateInfo.appendChild(tip_j);}
        }
        else    rateInfo = document.querySelectorAll('#browserItemList .item .inner .rateInfo')[index];

        //显示好友评分和评分人数
        let Pointfr = document.createElement('small');
        Point_f = Point_f ? Point_f : '-';
        Votes_f = Votes_f ? Votes_f :'-';
        let Point_My = localStorage.getItem(You+'Point'+ID);
        if(Point_My=='null') Point_My = null;
        let Point_M = Point_My ? "我的评分:"+Point_My :'';
        Pointfr.innerHTML = "好友评分:"+Point_f+"  "+Votes_f+"人评分"+"  "+Point_M;    Pointfr.setAttribute("class","friend_vote");
        if(rateInfo) rateInfo.appendChild(Pointfr);


        //显示排序按钮
        count+=1;
        if(count==itemsList.length && document.querySelector('#indexCatBox ul.cat')){
            document.querySelector('#indexCatBox ul.cat').append(showBtn);
            document.querySelector('#indexCatBox ul.cat').append(showBtn1);
            document.querySelector('#indexCatBox ul.cat').append(showBtn2);
            document.querySelector('#indexCatBox ul.cat').append(showBtn5);
            document.querySelector('#indexCatBox ul.cat').append(showBtn3);
            document.querySelector('#indexCatBox ul.cat').append(showBtn4);
            $('.chiiBtn').css({padding:'0 5px'});
            $('ul.cat li').css({padding:'0 5px 0 0'});
        }
        else if(count==itemsList.length && document.querySelector('#browserTools')){
            document.querySelector('#browserTools').append(showBtn);
            document.querySelector('#browserTools').append(showBtn1);
            document.querySelector('#browserTools').append(showBtn2);
            document.querySelector('#browserTools').append(showBtn5);
            document.querySelector('#browserTools').append(showBtn3);
            document.querySelector('#browserTools').append(showBtn4);
        }
    }

    //各种排序
    function ParseRank(rankstring){
        let rank = rankstring.match(/Rank (\d{1,4})/)? rankstring.match(/Rank (\d{1,4})/)[1]: 9999;
        return rank;
    }
    function ParseVote(votestring){
        let vote = votestring.match(/(\d{1,5})人评分/)? votestring.match(/(\d{1,5})人评分/)[1]: 0;
        return vote;
    }
    function ParseFriendRank(rankstring){
        let rank = rankstring.match(/(\d+\.\d+?)/)? rankstring.match(/(\d+\.\d+?)/)[1]: 0;
        let votes = rankstring.match(/(\d+?)人/)? rankstring.match(/(\d+?)人/)[1]: 0;
        rank = parseFloat(rank);
        votes = parseInt(votes);
        let fixed = rank ? (votes / (votes+10))* rank + (10 / (votes+10)) * 6: 0;
        return parseInt(fixed*10);
    }
    function ParseDate(Datestring){
        let yy = Datestring.match(/(\d{4})/)? Datestring.match(/(\d{4})/)[1].toString():'1000';
        Datestring = Datestring.match(/(\d{4})(年|-)(\d{1,2})(月|-)(\d{1,2})/);
        let year = Datestring ? Datestring[1].toString(): yy;
        let month = Datestring ? Datestring[3].toString(): '01';
        let day = Datestring ?Datestring[5].toString(): '01';
        let date= new Date(year+'/'+month+'/'+day);
        let now = new Date();
        return now.getTime()-date.getTime();
    }
    function ParseMyRank(rankstring){
        let rank = rankstring.match(/我的评分:(\d+)/)? rankstring.match(/我的评分:(\d+)/)[1]: 0;
        return rank;
    }

    function SortByRank() {
        sortstyle = (sortstyle==1)? -1 :1;
        showBtn.textContent = (showBtn.textContent=='排名排序↑') ? '排名排序↓':'排名排序↑';
        let container = document.querySelector('ul#browserItemList');
        /*if (container) container.style.cssText = 'display: flex; flex-flow: row wrap;';
        [].slice.call(document.querySelectorAll('#browserItemList .item .inner .rank'), 0)
            .map(x => [x.textContent, x])
            .sort((x,y) => (ParseRank(x[0]) - ParseRank(y[0]))*sortstyle)
            .forEach((x,n) => {x[1].parentNode.parentNode.style.order = n; x[1].parentNode.parentNode.style.width = '100%';});*/
        let arr=[];
        for(i=0;i<itemsList.length;i++)   arr[i]=itemsList[i];
        arr.sort(function(li1,li2){
            let n1=li1.querySelector('.inner .rank')? ParseRank(li1.querySelector('.inner .rank').textContent): 9999;
            let n2=li2.querySelector('.inner .rank')? ParseRank(li2.querySelector('.inner .rank').textContent): 9999;
            return (n1-n2)*sortstyle;});
        for(i=0; i<arr.length; i++)     $('#browserItemList').append(arr[i]);
    }

    function SortByVote() {
        sortstyle1 = (sortstyle1==-1)? 1 :-1;
        showBtn1.textContent = (showBtn1.textContent=='人数排序↓') ? '人数排序↑':'人数排序↓';
        let container = document.querySelector('ul#browserItemList');
        let arr=[];
        for(i=0;i<itemsList.length;i++)   arr[i]=itemsList[i];
        arr.sort(function(li1,li2){
            let n1=li1.querySelector('.inner .rateInfo .tip_j')? ParseVote(li1.querySelector('.inner .rateInfo .tip_j').textContent): 0;
            let n2=li2.querySelector('.inner .rateInfo .tip_j')? ParseVote(li2.querySelector('.inner .rateInfo .tip_j').textContent): 0;
            return (n1-n2)*sortstyle1;});
        for(i=0; i<arr.length; i++)      $('#browserItemList').append(arr[i]);
    }

    function SortByFriend() {
        sortstyle2 = (sortstyle2==-1)? 1 :-1;
        showBtn2.textContent = (showBtn2.textContent=='好友评价↓') ? '好友评价↑':'好友评价↓';
        let container = document.querySelector('ul#browserItemList');
        let arr=[];
        for(i=0;i<itemsList.length;i++)   arr[i]=itemsList[i];
        arr.sort(function(li1,li2){
            let n1=li1.querySelector('.inner .rateInfo .friend_vote')? ParseFriendRank(li1.querySelector('.inner .rateInfo .friend_vote').textContent): 0;
            let n2=li2.querySelector('.inner .rateInfo .friend_vote')? ParseFriendRank(li2.querySelector('.inner .rateInfo .friend_vote').textContent): 0;
            return (n1-n2)*sortstyle2;
        });
        for(i=0; i<arr.length; i++)     $('#browserItemList').append(arr[i]);
    }

    function SortByTime() {
        sortstyle3 = (sortstyle3==-1)? 1 :-1;
        showBtn3.textContent = (showBtn3.textContent=='时间排序↓') ? '时间排序↑':'时间排序↓';
        let container = document.querySelector('ul#browserItemList');
        let arr=[];
        for(i=0;i<itemsList.length;i++)   arr[i]=itemsList[i];
        arr.sort(function(li1,li2){
            let n1=li1.querySelector('.inner .info')? ParseDate(li1.querySelector('.inner .info').textContent): 0;
            let n2=li2.querySelector('.inner .info')? ParseDate(li2.querySelector('.inner .info').textContent): 0;
            return (n1-n2)*sortstyle3;
        });
        for(i=0; i<arr.length; i++)     $('#browserItemList').append(arr[i]);
    }

    function SortByMyPoint() {
        sortstyle4 = (sortstyle4==-1)? 1 :-1;
        showBtn5.textContent = (showBtn5.textContent=='我的评分↓') ? '我的评分↑':'我的评分↓';
        let arr=[];
        for(i=0;i<itemsList.length;i++)   arr[i]=itemsList[i];
        arr.sort(function(li1,li2){
            let n1=li1.querySelector('.inner .rateInfo .friend_vote')? ParseMyRank(li1.querySelector('.inner .rateInfo .friend_vote').textContent): 0;
            let n2=li2.querySelector('.inner .rateInfo .friend_vote')? ParseMyRank(li2.querySelector('.inner .rateInfo .friend_vote').textContent): 0;
            if(n1==n2){
                let n11=li1.querySelector('.inner .rank')? ParseRank(li1.querySelector('.inner .rank').textContent): 9999;
                let n22=li2.querySelector('.inner .rank')? ParseRank(li2.querySelector('.inner .rank').textContent): 9999;
                return (n22-n11)*sortstyle4;
            }
            else return (n1-n2)*sortstyle4;
        });
        for(i=0; i<arr.length; i++)     $('#browserItemList').append(arr[i]);
    }

})();