RedditChart

Some data analysis for reddit

目前為 2017-04-07 提交的版本,檢視 最新版本

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         RedditChart
// @namespace    http://tampermonkey.net/
// @version      1.1
// @description  Some data analysis for reddit
// @author       wpatter6
// @match        *://*.reddit.com/*
// @grant        none
// ==/UserScript==

(()=>{
    'use strict';
    var main=()=>{
        var doLog=true,_canvasBoxSize = 290, _canvasMinSize = 60,_lastData=null, _lastColors=null,_noCollapse=!!localStorage.getItem("rc-noCollapse"),selectOpened=false,
        aggregates={
            "Post Count":1
            ,"Sum of Votes":2
            //,"Avg Votes/Post":3//uncomment for additional aggregates for those smart enough to be here
            //,"Avg Votes/Post/Minute":4
        },
        log=(a,b)=>{//log shortener
            if(!doLog) return;
            if(typeof(a)==="string") a="RedditChart:" + a;
            else if (!b){b=a; a="RedditChart:debug";}
            if(typeof(b)!=="undefined") return console.log(a,b);
            return console.log(a);
        },
        go=()=>{//method executed once Chart.js has loaded
            if(String(location).indexOf("reddit.com/r/")>-1 && String(location).indexOf("reddit.com/r/all") === -1 && String(location).indexOf("reddit.com/r/popular") === -1)
                return log("Inside subreddit. Initialization cancelled.");

            log("Chart.js namespace found.  Initializing...");
            var chart,aggregate=parseInt(localStorage.getItem("rc-agg")||"1"),exclude,
            curpage=execReg(/\/r\/([^\/]*)/,location),
            subpage=(()=>{
                var ret = $("div.menuarea div.dropdown.lightdrop>span").text(),
                    regstr = location.host+"/"+(curpage?"r/"+curpage+"/":"")+"([^/?#]*)";

                if(ret==="") return execReg(regstr,location);
                return execReg(regstr,location) + " " + ret;
            })(),
            //dom element creation
            el=$("<canvas>").addClass("rc-chart").attr({width:(_noCollapse?_canvasBoxSize:_canvasMinSize)+"px",height:(_noCollapse?_canvasBoxSize:_canvasMinSize)+"px",oncontextmenu:"return false;"})
                .mousemove((e)=>{
					var activePoints=chart.getElementsAtEvent(e.originalEvent),
						sub=activePoints.length>0?chart.data.labels[activePoints[0]._index]:null; 
					
					log("activePoints[0]", activePoints.length>0?_lastColors[activePoints[0]._index]:null);
					
					$(".thing").removeClass("rc-active").css("border-left",""); 
					
					if(sub){ 
						$(".thing[data-subreddit="+sub+"]").addClass("rc-active").css("border-left","5px solid "+_lastColors[activePoints[0]._index]); 
					}
				})
				.mouseout(()=>{$(".thing").removeClass("rc-active");})
                .mousedown((e)=>{if(e.button !== 2) return true; var activePoints=chart.getElementsAtEvent(e.originalEvent), sub=activePoints.length > 0 ? chart.data.labels[activePoints[0]._index] :null; exclude=sub; calculate(); return false;}),
            dd=$("<select>").addClass("rc-dd").change((e)=>{localStorage.setItem("rc-agg",aggregate=parseInt($(e.target).val())); calculate();}).click(()=>{selectOpened=!selectOpened}),
            ddspan=$("<span>").addClass("rc-aggspan").append(dd).append($("<span>").text((()=>{//set the text of which page we're on
                if(subpage.indexOf("user")===0) {//handles user multis as well
                    var username=execReg("/user/([^?#]*)",location);
                    if(username) return subpage.replace("user", username);
                }
				var def = $(".user").text()==="Want to join? Log in or sign up in seconds." ? "popular" : "front page";
                return (curpage?"/r/"+curpage:def) + " " + subpage;
            })()).css({paddingLeft:"10px"})).css({padding:"10px"}),
            div=$("<div>").addClass("rc-float").appendTo($("body")).append(ddspan).append(el).hover(()=>{
                ddspan.show();
                dochart(null, _canvasBoxSize, true);
            }, ()=>{
				if(!selectOpened){
					ddspan.hide();
					dochart(null, _canvasMinSize, true);
				}
            }),
            calculate=()=>{//calculate and set up the chart
				log("calculating...");
                var aggregateData=[],namecount={},rawdata=$(".thing:visible").not(".promoted").map((i,e)=>{
                    var diff=(Date.now()-new Date($(e).find("time").attr("datetime")).getTime())/60000,
                        votes=parseInt($(e).find(".score.unvoted").attr("title"));
                    return {
                        sub:$(e).attr("data-subreddit"),
                        votes:votes,
                        vpm:Math.round(votes/diff)};
                }).get();
                for(var i=0; i<rawdata.length; i++){
                    var sub=rawdata[i].sub;
                    if(!sub||sub===exclude) continue;
                    var idx=((d,s)=>{ for(var j=0; j<d.length; j++) if(d[j].sub.toLowerCase()===s) return j; return -1;})(aggregateData,sub.toLowerCase()),
                        curval=idx+1?aggregateData[idx].value:0;
                    switch(aggregate){
                        case 1: curval++; break;
                        case 2:case 3: curval+=rawdata[i].votes; break;
                        case 4: curval+=rawdata[i].vpm; break;
                    }
                    if(idx+1){
                        aggregateData[idx].value=curval;
                        namecount[sub]++;
                    }else{
                        aggregateData.push({sub:rawdata[i].sub,value:curval});
                        namecount[sub]=1;
                    }
                }
                if([3,4].indexOf(aggregate)+1)//averages need to be divided by post count
                    for(var j=0; j<aggregateData.length; j++){
                        var agg=aggregateData[j];
                        agg.value=Math.round(agg.value/namecount[agg.sub]);
                    }

                //sort from biggest to smallest
                aggregateData.sort((a,b)=>{
                    if(b.value-a.value===0) return a.sub.localeCompare(b.sub);
                    return b.value - a.value;
                });
				log("calculating complete.");
                dochart(aggregateData);
                return aggregateData;
            },
            dochart=(data, boxSize, noAnimate)=>{//take aggregated/sorted data and apply it to the chart & .subreddit elements
				log("charting...");
                var reuseColors = false;
                if(!data){
                    if(!_lastData){
                        return calculate;
                    }
                    data = _lastData;
                    reuseColors = true;
                } else _lastData = data;

                var names=[],dataitems=[],colors= reuseColors ? _lastColors : getRandomColors(data.length);
                _lastColors = colors;
                for(var i=0; i<data.length; i++){
                    var agg=data[i];
                    names.push(agg.sub);
                    dataitems.push(agg.value);
                }

                $(".subreddit:visible").each((i,e)=>{
                    var sub =$(e).text().replace(/\/?r\//gi,"");
                    if(sub===exclude){$(e).css({backgroundColor:"",color:""});return;}
                    var idx=names.indexOf(sub);if(idx===-1) return;
                    var color=colors[idx],fontColor=getColorLuma(color)<70?"white":"black";
                    $(e).css({backgroundColor:color,color:fontColor});
                });

                if(chart) chart.destroy();
				if(boxSize && boxSize > 0){
					var context = el[0].getContext("2d");
					context.canvas.width = boxSize;
					context.canvas.height = boxSize;
				}
                chart=new Chart(el,{//Chart.js initialize with data
                    type:"pie",
                    data:{
                        labels:names,
                        datasets:[{data:dataitems,backgroundColor:colors,borderColor:colors}]
                    },
                    options:{
                        responsive:true,
						animation:{duration: noAnimate?0:1000},
                        legend:{display:false},
                        onClick:(e)=>{
                            var activePoints=chart.getElementsAtEvent(e),sub=activePoints[0]?chart.data.labels[activePoints[0]._index]:null;
                            if(!sub) return; open(location.protocol+"//"+location.hostname+"/r/"+sub);
                        }
                    }
                });
                chart.update();
				log("chart complete.");
                return data;
            };
            //triggered when Never ending reddit fires (RES)
            document.body.addEventListener("DOMNodeInserted",(e)=>{if(e.target.tagName==="DIV" && ($(e.target).attr("id")||"").indexOf("siteTable")>-1)calculate();},true);
            //finish setting up dropdown
            for(var i in aggregates) dd.append($("<option>").text(i).attr("value",aggregates[i]));
            dd.val(aggregate);

            calculate();
            log("Initialization complete.");
        },
        getRandomColors=(c)=>{//get array of random colors, c = length of result array
            var ret=[]; for(var i=0;i<c;i++)ret.push("#000000".replace(/0/g,()=>{return (~~(Math.random()*16)).toString(16);})); return ret;
        },
        getColorLuma=(c)=>{//get color light/darkness
            if(c.indexOf("#")===0)c=c.substring(1); var rgb=parseInt(c,16),r=(rgb >> 16) & 0xff,g=(rgb >>  8) & 0xff,b=(rgb >>  0) & 0xff; return 0.2126 * r + 0.7152 * g + 0.0722 * b;
        },
        checkForChartJs=(c)=>{//recurring setTimeout check to wait for Chart.js to load into page.  Dies after 100 loops.
            if(c<100){ log("Checking for chart.js namespace ("+c+")..."); if(!window.Chart) return setTimeout(checkForChartJs,50,++c); go(); } else log("Chart.js namespace not found! Cannot initialize.");
        },
        execReg=(reg, str)=>{
            if(typeof(reg)==="string") reg = new RegExp(reg); return (reg.exec(str)||[0,null])[1];
        };
        checkForChartJs(0);
   };

    var css=".rc-float{position:fixed;bottom:20px;right:0px; z-index:2000;border-radius:20px;padding:10px;opacity:0.4;text-align:center;} .rc-float:hover{background-color:rgba(0,0,0,0.8);opacity:1} .rc-aggspan{display:none;color:white;font-size:small;} .rc-chart{cursor:pointer;} .subreddit{border-radius:2px;padding:2px;} .rc-active{background-color:rgba(66,66,66,0.3) !important;} .rcf{height:0px;position:fixed;border:0px} .rcx { }";
    $("body")
		.append($("<style>").html(css))
        .append($("<script>").attr({src:"https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.4.0/Chart.min.js"}))
		.append($("<script>").html("("+main.toString()+")();"));
})();