// ==UserScript==
// @name Kidoku for Reddit
// @namespace kidoku-for-reddit
// @include http://www.reddit.com*
// @include https://www.reddit.com*
// @description redditに既読つけます。http://redd.it/2yrx0l
// @version 0.505.5
// @grant none
// ==/UserScript==
var url = location.href;
if( url.indexOf("http://www.reddit.com/tb/") != -1)return;
if( url.indexOf("http://www.reddit.com/toolbar/") != -1)return;
if( url.indexOf("https://www.reddit.com/live/") != -1)return;
//プロトコルが別だとストレージも別になってしまうので移動
if( url.indexOf("http://") === 0){
location.href = "https://" + url.substring(7);
return;
}
// 携帯版なら「読んだ」ボタンを埋め込んで終了
var timeForMobile = 0;
if( url.indexOf(".compact") != -1 ){
if( document.body.getAttribute("class").indexOf("comments-page") == -1 )return; // コメントページ以外でも終了
timeForMobile = Math.floor(Date.now()/1000);
var button = document.createElement("button");
button.innerHTML = "読んだ";
button.style.float = "right";
button.addEventListener("click", function(){
var sComment, c, count = 0;
var temp = document.querySelectorAll(".title-button");
var temp2 = document.querySelectorAll("span.title");
if( temp[0] ){
sComment = temp[0].innerHTML;
count = sComment.match(/\d+/); // コメントがすべて表示されていない場合
}
else if( temp2[0] ){
sComment = temp2[0].innerHTML;
count = sComment.match(/\d+/); // コメントがすべて表示されている場合
if(!count)count = 0;
}
else count = 0;
count = parseInt(count);
var json = {
"count": count,
"time": timeForMobile,
};
var key = "kidoku_" + document.querySelectorAll("link[rel=shorturl]")[0].getAttribute("href").split("/")[3];
localStorage.setItem(key, JSON.stringify(json));
// 保存
button.style.display = "none";
});
document.querySelectorAll(".tabmenu")[0].appendChild( button );
return;
}
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// URLからIDを取り出す
var getKey = function(arglink){
var list = arglink.split("/");
var key = "kidoku_" + list[6]; // サブミID
if(!list[6])return false;
return key;
};
// NEW!をクリックすると次へジャンプ
var jumpNextNew = function(){
var targ, i;
var list = document.querySelectorAll(".kidoku_new");
for(i = 0; i< list.length; i++){
if(list[i] == this)break;
}
targ = list[i+1] || list[0];
$('html, body').animate({
scrollTop: $( targ ).offset().top - 200
}, 200);
};
// コメント数を開くときlimitを指定して開くボタン
var popupLimit = {
element: null,
nowElement: null,
setup:function(){
var div = document.createElement("DIV");
var button0 = document.createElement("BUTTON");
div.setAttribute("style", "display:block;position:absolute;width:60px;");
button0.setAttribute("style", "width:30px;height:15px;font-size:10px;padding:0px;");
button1 = button0.cloneNode();
button0.innerHTML = "50";
button1.innerHTML = "500";
div.appendChild( button0 );
div.appendChild( button1 );
this.element = div;
// クリックで表示を消す
document.body.addEventListener("click", function(){
if( !popupLimit.nowElement )return;
popupLimit.nowElement.style.display = "none";
});
},
append: function( targetEl ){
var url = targetEl.getAttribute("href");
var bounds = targetEl.getBoundingClientRect();
var l = Math.max( document.body.scrollLeft, document.documentElement.scrollLeft);
var t = Math.max( document.body.scrollTop, document.documentElement.scrollTop);
var x = bounds.left + l;
var y = bounds.bottom + t;
if(this.nowElement)document.body.removeChild( this.nowElement );
var div = this.element.cloneNode(true);
div.addEventListener("click", function(){
targetEl.parentNode.style.textDecoration = "line-through";
});
document.body.appendChild( div );
var buttons = div.querySelectorAll("button");
if(url.indexOf("?") == -1)url += "?";
buttons[0].addEventListener("click", function(){
window.open( url + "&limit=50");
});
buttons[1].addEventListener("click", function(){
window.open( url + "&limit=500");
});
div.style.left = x + "px";
div.style.top = y + "px";
this.nowElement = div;
},
};
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ソート
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ リストページの既読sort
var submiList = []; // num, bKidoku, bSintyaku, node 既読スレのみ
var sortKidoku = {
sorted: false,
sort : function (){
if(document.querySelectorAll(".thing.link, .thing.comment").length > submiList.length)listPageFunc(); // 全部取得できていなければ取得
sortedListSintyaku = [];
sortedList = [];
sortedListMidoku = [];
var container = submiList[submiList.length-1].node.parentNode;
var navi = document.querySelectorAll(".nav-buttons")[0];
if(this.sorted){
submiList.sort(function(a,b){ return(a.num>b.num)? 1 : -1; });// 元の順に戻す
}
else{
// 既読を上にソート
for(var i = 0; i< submiList.length; i++){
if(submiList[i].bKidoku){
if(submiList[i].bSintyaku) sortedListSintyaku.push(submiList[i]);
else sortedList.push(submiList[i]);
}
else sortedListMidoku.push(submiList[i]);
}
submiList = sortedListSintyaku.concat(sortedList, sortedListMidoku);
}
for(var i = 0; i< submiList.length; i++){
container.appendChild(submiList[i].node);
}
if(navi)container.appendChild(navi);
this.sorted = !this.sorted;
var sepalist = document.querySelectorAll(".NERPageMarker");
if(sepalist){
for(var i = 0; i < sepalist.length; i++){
sepalist[i].style.display = "none";
}
}
}
};
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ コメントページの新着2ソート
var commentPage = {
"commentArea":null,
"pjson":null,
"comments":[], // obj, time, num, nest, nestTime
"sorted":false,
"sorter":function(){
var box = this.commentArea.querySelectorAll(".sitetable")[0];
if(box.lastChild.getAttribute("class") == "clearleft")box.removeChild(box.lastChild);
var last = box.lastChild;
if(!this.sorted){
//新着2
this.comments.sort(function(a,b){
var x = a.nestTime || a.time;
var y = b.nestTime || b.time;
if( x > y ) return -1;
else return 1 ;
});
}
else{
this.comments.sort(function(a,b){return(a.num>b.num)?1:-1;});
}
for(var i = 0; i < this.comments.length; i++){
if(this.comments[i].nest)continue;
box.appendChild(this.comments[i].obj);
}
if(last.getAttribute("class").indexOf("morechildren") != -1)box.appendChild(last);
this.sorted = !this.sorted;
}
};
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 長さが変わるかチェック。never ending reddit用。
var neverEnding = {
pSubmiListLength: 0,
waiting: false,
checkTime: Date.now(),
check: function(){
// マウスが動くと一秒に一回チェック
if(!this.waiting){
this.checkTime = Date.now() + 1000;
this.waiting = true;
}
else if(Date.now() > this.checkTime){
if(!commentPage.commentArea){
if(document.querySelectorAll(".thing.link, .thing.comment").length > submiList.length)listPageFunc();
}
else if(document.querySelectorAll(".thing.comment").length > commentPage.comments.length){
commentFunc();
}
this.waiting = false;
}
},
};
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 読み込み時、エンドレスRESで表示数が伸びる度に使う関数
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ コメントページにいろいろ追加してソート用に保存
var commentFunc = function(){
// 新着コメントを確認するため時間を確認
var commentElList = document.querySelectorAll(".thing.comment");
var newMark = function(){
if(timeEl.parentNode.lastChild.getAttribute("class") == "kidoku_new")return;
var newEl = document.createElement("span");
newEl.innerHTML = "NEW!";
newEl.setAttribute("class", "kidoku_new");
newEl.setAttribute("style", "color:red; text-decoration:underline; cursor:pointer");
newEl.addEventListener("click",jumpNextNew );
timeEl.parentNode.appendChild(newEl);
};
commentPage.comments = [];
for(var i = commentPage.comments.length; i < commentElList.length; i++){
var timeEl = commentElList[i].querySelectorAll("time")[0];
var time = Date.parse(timeEl.getAttribute("datetime"))/1000;
//入れ子の中ならtrue
var nest = false;
if(commentElList[i].parentNode.getAttribute("class") == "sitetable listing")nest = true;
//一番外なら一番早い時間を取得
var nestTime = false;
if(!nest){
var nestTimeElList = commentElList[i].querySelectorAll("time");
if(nestTimeElList.length != 1){
var array = [];
for(var j = 0; j< nestTimeElList.length; j++){
array.push( Date.parse(nestTimeElList[j].getAttribute("datetime")) );
}
nestTime = array.sort().reverse()[0]/1000;
}
}
commentPage.comments.push({"obj":commentElList[i], "time":time, "num": i, "nest":nest, "nestTime":nestTime });
if(commentPage.pjson){
var jsonobj = JSON.parse(commentPage.pjson);
if(jsonobj.time < time)newMark();// 印をつける
}
else newMark();
}
};
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ リストページにいろいろ追加してソート用に保存
var listPageFunc = function(){
popupLimit.setup();
var temp = document.querySelectorAll(".first");
var linklist = [];
for(var i = 0; i< temp.length; i++){
var el = temp[i].querySelectorAll(".may-blank")[0];
if(el)linklist.push( el );
}
var submiNodeList = document.querySelectorAll(".thing.link, .thing.comment");//既読ソート用
for(var i = neverEnding.pSubmiListLength; i< linklist.length ; i++ ){
//既読コメント数をストレージから取得
var value = null;
var key = getKey(linklist[i].getAttribute("href"));
var json = localStorage.getItem(key);
if(json){
var obj = JSON.parse(json);
value =obj.count;
}
//ソート用
var subm ={
num:i,
node:submiNodeList[i],
bKidoku:false,
bSintyaku:false
};
submiList.push(subm);
// 現在のコメント数と比べて増えていれば赤線を引く
if(value !== null){
subm.bKidoku = true;
var element = document.createElement('span');
element.innerHTML = "既読" + value + "/" ;
linklist[i].parentNode.insertBefore(element, linklist[i]);
linklist[i].parentNode.style.borderBottom = "1px solid gray";
var nowValue = linklist[i].innerHTML.match(/\d+/);
value = parseInt(value);
nowValue = parseInt(nowValue);
if( !nowValue );
else if(nowValue <= value);
else{
subm.bSintyaku = true;
linklist[i].parentNode.style.borderBottom = "1px solid red";
}
}
linklist[i].setAttribute("target", "_blank"); // key にすればターゲット固定
// ベストで開いたりnewで開いたり
var addLink = function( txt, type ){
var link = linklist[i].cloneNode();
link.innerHTML = txt;
link.setAttribute("href", link.getAttribute("href") + type );
linklist[i].parentNode.appendChild( link );
link.addEventListener( "mouseover", function( event ){
popupLimit.append( event.target );
});
};
addLink(" best", "?sort=confidence" );
addLink(" new", "?sort=new" );
addLink(" 小", ".compact?sort=new" );
// 削除したり
if( subm.bKidoku ){
var delf = (function() {
var key2 = key;
return function(){
localStorage.removeItem(key2);
};
})();
var delkey = document.createElement("button");
delkey.innerHTML = "del";
delkey.setAttribute("style", "margin:0px 0px 0px 10px; padding:0px;max-height:15px; vertical-align:bottom; font-size:12px; line-height:100%;");
delkey.addEventListener("click", delf);
linklist[i].parentNode.appendChild(delkey);
}
// クリックイベントで雑に打ち消し線をつける
var f = (function() {
var ii = i;
return function(){
linklist[ii].parentNode.style.textDecoration = "line-through";
};
})();
linklist[i].parentNode.addEventListener("click",f);
}
neverEnding.pSubmiListLength = submiList.length;
};
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 以下読み込み時に一度
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
var bSearchPage = false, bCommentsPage = false;
var pageTypeCss = document.body.getAttribute("class");
if( pageTypeCss ){
if( pageTypeCss.indexOf("search-page") != -1 )bSearchPage = true;
if( pageTypeCss.indexOf("comments-page") != -1 )bCommentsPage = true;
//if(document.body.getAttribute("class").indexOf("related-page") != -1)return;
}
if( bCommentsPage ){
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ コメントページの場合
commentPage.commentArea = document.querySelectorAll(".commentarea")[0];
var key = getKey(url);
commentPage.pjson = localStorage.getItem(key);
document.body.addEventListener("mousemove", neverEnding.check);
commentFunc();
// 既読コメント数と時間をストレージに保存
var setStorage = function (postComment){
if(key){
var sComment = document.querySelectorAll(".comments.may-blank")[0].innerHTML;
var count = sComment.match(/\d+/);
if(count === null)count = 0;
count = parseInt( count );
var json = {
"count": count,
"time": Math.floor(Date.now()/1000),
};
localStorage.setItem(key, JSON.stringify(json));
}
};
setStorage(false);
// 新着ソートボタン
var newElem = document.createElement("button");
newElem.setAttribute("style", "margin:10px 5px 0px 0px; padding:0px; width: 60px;vertical-align:bottom; font-size:11px; line-height:100%;");
newElem.innerHTML = "新着2";
newElem.addEventListener("click", function(){
commentPage.sorter();
if(this.innerHTML == "新着2")this.innerHTML = "戻す";
else this.innerHTML = "新着2";
});
var drop = document.querySelectorAll(".dropdown-title.lightdrop")[0];
drop.parentNode.appendChild(newElem);
// ニューにジャンプ
if(document.querySelectorAll(".kidoku_new")[0]){
var newEl = document.createElement("span");
newEl.innerHTML = "NEW!へジャンプ";
newEl.setAttribute("style", "color:red; text-decoration:underline; cursor:pointer; font-size:11px");
newEl.addEventListener("click",jumpNextNew );
drop.parentNode.appendChild(newEl);
}
}
else if( document.querySelectorAll(".first")[1] ){
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ リストページの場合
listPageFunc();
document.body.addEventListener("mousemove", neverEnding.check);
function setTabButton( button ){
if( !bSearchPage ){
var tab1 = document.querySelectorAll(".tabmenu > li")[0];
tab1.parentNode.insertBefore( button , tab1);
}
else document.querySelectorAll(".menuarea")[0].appendChild(button);
}
// 既読ソートボタン
var newElem = document.createElement("button");
newElem.setAttribute("style", "font-size:11px;line-height:100%;padding:0px;max-height:15px;min-width:65px; vertical-align:bottom;");
newElem.innerHTML = "既読sort";
newElem.addEventListener("click",function(){
sortKidoku.sort();
if(this.innerHTML == "既読sort")this.innerHTML = "戻す";
else this.innerHTML = "既読sort";
});
setTabButton( newElem );
}
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 全ページ
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 右上 既読 最近開いたスレをリストで開く
var getRecentButton = document.createElement("span");
getRecentButton.innerHTML = " 既読";
getRecentButton.style.cursor = "pointer";
getRecentButton.addEventListener("mouseup", function(event){
var array =[], idnames = "";
for(var i = localStorage.length-1 ; i > 0; i--){
var k = localStorage.key(i);
if(k.indexOf("kidoku_") === 0){
array.push( { time:JSON.parse(localStorage[k]).time, key:k.split("kidoku_")[1] } );
}
}
array.sort(function(a,b){return (a.time < b.time)? 1: -1;});
for(var i = 0; i < array.length; i++){
idnames += "t3_" + array[i].key + ",";
if (idnames.length > 5000)break; // 多すぎると拒否される?ので500スレ分くらいだけにする
}
if( event.button === 0 )location.href = "https://www.reddit.com/by_id/" + idnames;
else if( event.button == 2 )setTimeout(function(){alert( array.length + "記憶中" );},1);
else if( event.button == 1 || true )window.open( "https://www.reddit.com/by_id/" + idnames );
event.preventDefault();
});
var topbar = document.querySelector("#header-bottom-right");
//if( !topbar )topbar = document.body;
if( topbar )topbar.appendChild( getRecentButton );
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 下部 削除ボタン
var s = document.querySelectorAll(".bottommenu")[0];
if(s){
var newElem = document.createElement("button");
newElem.innerHTML = "削除";
newElem.style.margin = "10px 0px";
var inputEl = document.createElement("input");
inputEl.setAttribute("type", "number");
inputEl.setAttribute("id", "kidoku_delete_day");
inputEl.setAttribute("style", "text-align:right;width:40px;");
inputEl.setAttribute("value", "0");
s.parentNode.appendChild(inputEl);
s.parentNode.appendChild(document.createTextNode("日以上前の既読履歴を"));
s.parentNode.appendChild(newElem);
newElem.addEventListener("click", function(){
var day = document.querySelector("#kidoku_delete_day").value;
var pDay = Math.floor( ( Date.now() - (day * 24 * 60 * 60 * 1000) ) / 1000);
var delCount = 0;
var count = 0;
if(window.confirm( "既読データを消します" )){
for(var i = localStorage.length-1 ; i > 0; i--){
var k = localStorage.key(i);
if(k.indexOf("kidoku_") === 0){
count++;
if(JSON.parse(localStorage[k]).time < pDay){
delCount++;
localStorage.removeItem(k);
}
}
}
alert(delCount + "/" + count + "削除しました");
}
});
}