您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
For those users that enjoy odd hats and questionable writing. Adds a few reasonable features to the THP site.
// ==UserScript== // @name THPsupplemental // @namespace https://greasyfork.org/en/scripts/13233-thpsupplemental // @version 0.9 // @description For those users that enjoy odd hats and questionable writing. Adds a few reasonable features to the THP site. // @author kntei // @include http://www.touhou-project.com/* // @run-at document-end // @grant GM_listValues // @grant GM_deleteValue // @grant GM_getValue // @grant GM_setValue // @icon data:image/png;base64,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 // ==/UserScript== //GLOBAL VARIABLES var thps = { posts: [], threads: [], intervals: [], maxReplies: 3, timeThreshold: 1000 * 60 * 60 * 24 * 7, // 7 days refreshRSS: 1000 * 60 * 30, // 30 mins storyDomains: ["th", "eientei", "forest", "border", "shrine", "sdm", "youkai", "underground", "others", "shorts"], allBoards: [ "gensokyo", "i", "at", "th", "eientei", "forest", "border", "shrine", "sdm", "youkai", "underground", "others", "shorts", "blue" ], allFullBoards: [ "General Discussion", "Drawing", "18+ Touhou", "General", "Eientei & Lunar Capital", "Forest of Magic & Flower Fields", "Hakurei Border & Afterlife", "Hakurei Shrine, Human Village & Myouren Temple", "Scarlet Devil Mansion & Misty Lake", "Youkai Mountain & Heaven", "Underground & Makai", "Others", "Short Stories", "not a color we know" ] }; //FUNCTIONS ON GLOBAL VARIABLES function clearIntervals() { for( var i = 0; i < thps.intervals.length; i++ ) clearInterval(thps.intervals[i]); } function hasLocalPost( id, board ) { for( var i = 0; i < thps.posts.length; i++ ) if( thps.posts[i].postID == id && thps.posts[i].boardStr == board ) return true; return false; } function threadIndex( id ) { for( var i = 0; i < thps.threads.length; i++ ) if( thps.threads[i].topicID == id ) return i; return -1; } function updateThreads() { thps.threads = []; for( var i = 0; i < thps.posts.length; i++ ) { var index = threadIndex(thps.posts[i].topicID); if( index === -1 ) { var newthread = new Thread(); newthread.topicID = thps.posts[i].topicID; newthread.latestDate = thps.posts[i].date; if( thps.posts[i].topicID != thps.posts[i].postID ) newthread.numChild = newthread.numChild + 1; thps.threads.push( newthread ); } else { if( thps.threads[index].latestDate.stringToMilli() < thps.posts[i].date.stringToMilli() ) thps.threads[index].latestDate = thps.posts[i].date; if( thps.posts[i].topicID != thps.posts[i].postID ) thps.threads[index].numChild = thps.threads[index].numChild + 1; } } } function printThreads() { for( var i = 0; i < thps.threads.length; i++ ) { console.log('---'); console.log( "ID " + thps.threads[i].topicID); console.log( "Date " + thps.threads[i].latestDate); console.log( "Child " + thps.threads[i].numChild); } } function sortPostArrayByDate() { thps.posts.sort( function( a, b ) { return a.date.stringToMilli() - b.date.stringToMilli(); } ); } function sortPostArrayByPostNum() { updateThreads(); thps.posts.sort( function( a, b ) { if( thps.threads[threadIndex(a.topicID)].latestDate.stringToMilli() < thps.threads[threadIndex(b.topicID)].latestDate.stringToMilli() ) return 1; if( thps.threads[threadIndex(a.topicID)].latestDate.stringToMilli() > thps.threads[threadIndex(b.topicID)].latestDate.stringToMilli() ) return -1; else if( Number(a.topicID) > Number(b.topicID) ) return 1; else if( Number(a.topicID) < Number(b.topicID) ) return -1; else if( Number(a.postID) > Number(b.postID) ) return 1; else if( Number(a.postID) < Number(b.postID) ) return -1; else return 0; } ); } // TIME CONVERSIONS String.prototype.stringToMilli = function() { var replaced = this.replace(/[^\w\s]/gi, ' '); var parsed = replaced.split(" "); var d = new Date( parsed[0], Number(parsed[1])-1, parsed[2], parsed[4], parsed[5], 0, 0); return d.valueOf(); }; Number.prototype.milliToString = function() { var d = new Date( this ); var string = ""; var dayWeek = ["Sun", "Mon", "Tue", "Wen", "Thu", "Fri", "Sat"]; string += d.getFullYear(); string += '/'; string += numPad(d.getMonth()+1, 2); string += '/'; string += numPad(d.getDate(), 2); string += '('; string += dayWeek[d.getDay()]; string += ')'; string += numPad(d.getHours(), 2); string += ':'; string += numPad(d.getMinutes(), 2); return string; }; // XMLHttpRequest CLASS function RequestThread() { this.client = new XMLHttpRequest(); } function AJAXLike( url, success, failure ) { var client = new XMLHttpRequest(); var handler = function() { if (this.readyState === 4) { if (this.status === 200) //COMPLETE success(client);//FUNCTION else //ERROR failure(); } }; client.onreadystatechange = handler; client.open( "GET", url ); client.send(); } RequestThread.prototype.requestHTML = function( url, func, board ) { var handler = function() { if (this.readyState === 4) { if (this.status === 200) //COMPLETE { var doc = document.implementation.createHTMLDocument(""); doc.body.innerHTML = this.response; func( doc, board );//FUNCTION } else //ERROR console.log( "ERROR on page " + url ); } }; this.client.onreadystatechange = handler; this.client.open( "GET", url ); this.client.send(); }; function Thread(){ this.topicID = ""; this.latestDate = ""; this.numChild = 0; } // POST CLASS function Post() { this.boardStr = ""; this.imgNumber = ""; this.imgType = ""; this.imgDetails = ""; this.threadName = ""; this.posterName = ""; this.posterTrip = ""; this.date = ""; this.topicID = ""; this.postID = ""; this.text = ""; } Post.prototype.isBlank = function() { if( this.text === "" ) return true; return false; }; Post.prototype.isChild = function() { if( Number(this.topicID) === Number(this.postID) ) return false; return true; }; Post.prototype.clear = function() { this.boardStr = ""; this.imgNumber = ""; this.imgType = ""; this.imgDetails = ""; this.threadName = ""; this.posterName = ""; this.posterTrip = ""; this.date = ""; this.topicID = ""; this.postID = ""; this.text = ""; }; Post.prototype.fromSinglePost = function( doc, board ) { this.boardStr = board; var searchspan = doc.getElementsByTagName( "span" ); for( var i = 0; i < searchspan.length; i++ ) { if( searchspan[i].className == "filesize" ) { var file = searchspan[i].childNodes; var a = file[1].textContent.split("."); this.imgNumber = a[0]; this.imgType = a[1]; var b = file[2].textContent; b.removeWhiteSpace(); b = b.popFront(); b = b.popBack(); this.imgDetails = b; } if( searchspan[i].className == "filetitle" ) this.threadName = searchspan[i].innerHTML; if( searchspan[i].className == "postername" ) this.posterName = searchspan[i].innerHTML; if( searchspan[i].className == "postertrip" ) this.posterTrip = searchspan[i].innerHTML; if( searchspan[i].className == "reflink" ) { var ref = searchspan[i].innerHTML; var breakdown = ref.split('"'); breakdown = breakdown[1].split(/[.\/#]/g); this.topicID = breakdown[breakdown.length-3]; this.postID = breakdown[breakdown.length-1]; } } var searchblock = doc.getElementsByTagName( "blockquote" ); if( searchblock[0] !== undefined ) this.text = searchblock[0].innerHTML; var searchlabel = doc.getElementsByTagName( "label" ); if( searchlabel[0] !== undefined ) { var lab = searchlabel[0].childNodes; this.date = lab[lab.length-1].textContent.replace(/\s/g, ""); } }; function createBreakString( arr ) { var s = ""; var sLength = 0; for( var i = 0; i < arr.length; i++ ) { s += "!" + (arr[i].length + sLength); sLength += arr[i].length; } return s; } function createDataString( arr ) { var s = ""; for( var i = 0; i < arr.length; i++ ) s += arr[i]; return s; } Post.prototype.savePost = function() { //breaksSTRING id + board + breaks, "!2!135!18469849!651651" //dataSTRING id + board + data, "al;sidjf;aloskjd;falksjdf;lasjkd" //GM_deleteValue( id + board + "breaks" ); //GM_deleteValue( id + board + "data" ); var id = this.postID; var board = this.boardStr; var brks = "0" + createBreakString([this.boardStr, this.imgNumber, this.imgType, this.imgDetails, this.threadName, this.posterName, this.posterTrip, this.date, this.topicID, this.postID, this.text]); var data = createDataString([this.boardStr, this.imgNumber, this.imgType, this.imgDetails, this.threadName, this.posterName, this.posterTrip, this.date, this.topicID, this.postID, this.text]); //console.log( brks ); //console.log( data ); var savB = brks; //var savB = totalencode(brks); GM_setValue( id + '!' + board + '!' + "breaks", savB ); var savD = data; //var savD = totalencode(data); GM_setValue( id + '!' + board + '!' + "data", savD ); }; function parsePostData( pos, breaks, data ) { var string = ""; var arrBreaks = breaks.split("!"); string += data.substring(Number(arrBreaks[pos]),Number(arrBreaks[pos+1])); return string; } Post.prototype.loadPost = function( id, board ) { var loadB = GM_getValue( id + '!' + board + '!' + "breaks" ); var loadD = GM_getValue( id + '!' + board + '!' + "data" ); //var brks = totaldecode(loadB); var brks = loadB; //var data = totaldecode(loadD); var data = loadD; this.boardStr = parsePostData( 0, brks, data ); this.imgNumber = parsePostData( 1, brks, data ); this.imgType = parsePostData( 2, brks, data ); this.imgDetails = parsePostData( 3, brks, data ); this.threadName = parsePostData( 4, brks, data ); this.posterName = parsePostData( 5, brks, data ); this.posterTrip = parsePostData( 6, brks, data ); this.date = parsePostData( 7, brks, data ); this.topicID = parsePostData( 8, brks, data ); this.postID = parsePostData( 9, brks, data ); this.text = parsePostData( 10, brks, data ); }; function makeOpeningPost( oldhtml ) { var html = ''; html += '<table>'; html += '<tbody>'; html += '<tr>'; html += '<td>'; html += oldhtml; html += '</span>'; html += '</td>'; html += '</tr>'; html += '</tbody>'; html += '</table>'; return html; } function makeChildPost( oldhtml ) { var html = ''; html += '<table>'; html += '<tbody>'; html += '<tr>'; html += '<td class="reply" style=\'padding:0px 7.5px 0px 7.5px\'">'; html += oldhtml; html += '</span>'; html += '</td>'; html += '</tr>'; html += '</tbody>'; html += '</table>'; return html; } ////////////////////////////////////////////// // BORROWED SOME KUSABA CODE TO WORK IN FRAMES ////////////////////////////////////////////// function addPreview(e) { var c; var d = "srcElement"; var f = "href"; this[f] ? c = this : c = e[d]; ainfo = c.className.split('|'); var g = getCurrentFrameDoc().createElement('div'); g.setAttribute("id", "preview" + c.className); g.setAttribute('class', 'reflinkpreview'); g.setAttribute('className', 'reflinkpreview'); if (e.pageX) { g.style.left = '' + (e.pageX + 50) + 'px'; } else { g.style.left = (e.clientX + 50); } var h = getCurrentFrameDoc().createTextNode(''); g.appendChild(h); var i = c.parentNode; var j = i.insertBefore(g, c); AJAXLike('http://www.touhou-project.com/read.php?b=' + ainfo[1] + '&t=' + ainfo[2] + '&p=' + ainfo[3] + '&single', function(a) { var b = a.responseText || _("something went wrong (blank response)"); j.innerHTML = b; }, function() { var response = _("something went wrong (blank response)"); newelement.innerHTML = response; } ); } function delPreview(e) { var a; var b = "srcElement"; var c = "href"; this[c] ? a = this : a = e[b]; var d = getCurrentFrameDoc().getElementById("preview" + a.className); if (d) { d.parentNode.removeChild(d); } } function parsePreview() { var a = getCurrentFrameDoc().getElementsByTagName('a'); var b; var c; for (var i = 0; i < a.length; i++) { b = a[i]; if (b.className) { if (b.className.substr(0, 4) == "ref|") { if (b.addEventListener) { b.addEventListener("mouseover", addPreview, false); b.addEventListener("mouseout", delPreview, false); } else if (b.attachEvent) { b.attachEvent("onmouseover", addPreview); b.attachEvent("onmouseout", delPreview); } } } } } // TEXT SHORTENER function getCurrentFrameDoc() { if( window.top.frames[1] !== undefined ) return window.top.frames[1].document; else return document; } function setStyles( style ) { var styles = getCurrentFrameDoc().getElementsByTagName('link'); for( var i = 0; i < styles.length; i++ ) if( styles[i].type == "text/css" ) if( styles[i].title == style ) styles[i].disabled = false; } function deleteStyles() { var styles = getCurrentFrameDoc().getElementsByTagName('link'); for( var i = 0; i < styles.length; i++ ) if( styles[i].type == "text/css" ) styles[i].parentNode.removeChild(styles[i]); } function disableStyles() { var styles = getCurrentFrameDoc().getElementsByTagName('link'); for( var i = 0; i < styles.length; i++ ) if( styles[i].type == "text/css" ) styles[i].disabled = true; } function addStyles() { var head = getCurrentFrameDoc().getElementsByTagName('head')[0]; var link = document.createElement('link'); link.rel = 'stylesheet'; link.type = 'text/css'; link.href = 'http://www.touhou-project.com/css/img_global.css'; head.appendChild(link); link = document.createElement('link'); link.rel = 'alternate stylesheet'; link.type = 'text/css'; link.href = 'http://www.touhou-project.com/css/burichan.css'; link.title = 'Burichan'; link.disabled = 'true'; head.appendChild(link); link = document.createElement('link'); link.rel = 'alternate stylesheet'; link.type = 'text/css'; link.href = 'http://www.touhou-project.com/css/futaba.css'; link.title = 'Futaba'; link.disabled = 'true'; head.appendChild(link); link = document.createElement('link'); link.rel = 'alternate stylesheet'; link.type = 'text/css'; link.href = 'http://www.touhou-project.com/css/photon.css'; link.title = 'Photon'; link.disabled = 'true'; head.appendChild(link); link = document.createElement('link'); link.rel = 'alternate stylesheet'; link.type = 'text/css'; link.href = 'http://www.touhou-project.com/css/chernobyl.css'; link.title = 'Chernobyl'; link.disabled = 'true'; head.appendChild(link); link = document.createElement('link'); link.rel = 'alternate stylesheet'; link.type = 'text/css'; link.href = 'http://www.touhou-project.com/css/darkish.css'; link.title = 'Darkish'; link.disabled = 'true'; head.appendChild(link); } function cookieStyle() { var style = cookieValue(' kustyle'); deleteStyles(); addStyles(); if( style !== '' ) setStyles( style ); else setStyles( "Photon" ); } function cookieValue(cname) { var name = cname + "="; var ca = document.cookie.split(';'); for( var i = 0; i < ca.length; i++ ) { var s = ca[i].split('='); if( cname == s[0] ) return s[1]; } return ""; } function toggle_visibility(id) { var arr = id.split('!'); var short = document.getElementById( arr[0] + arr[1] + "short" ); var long = document.getElementById( arr[0] + arr[1] + "long" ); if( short.style.display == 'block' ) { short.style.display = 'none'; long.style.display = 'block'; } else { short.style.display = 'block'; long.style.display = 'none'; } }; function cropString( string, delim, times ) { var cropstring = string; for( var i = 0; i < times; i++ ) { var loc = cropstring.lastIndexOf(delim); cropstring = cropstring.substring(0,loc); } return cropstring; } function makeMessageShort( id, board, string ) { var html = ''; var countMax = 12; var count = string.split("<br>").length - 1; if( count > countMax ) { html += '<div id=\'' + id + board + 'long\' style=\'display:none\'>'; html += '<blockquote>'; html += string; html += '</blockquote>'; html += '</div>'; html += '<div id=\'' + id + board + 'short\'>'; html += '<blockquote>'; html += cropString(string, "<br>", count - countMax ); html += '</blockquote>'; html += '</div>'; html += '<a href="javascript:void(0)" onclick="toggle_visibility(\'' + id + '!' + board + '\');">Message too long. Click here to toggle text.</a>'; return html; } else { html += '<blockquote>'; html += string; html += '</blockquote>'; return html; } } function getDims( string ) { var arr = [0,0]; var breakdown = string.split(','); breakdown = breakdown[1].replace(/\s/g, "").split('x'); arr[0] = Number(breakdown[0]); arr[1] = Number(breakdown[1]); return arr; } function getScaled( string, maxSize ) { var arrDim = getDims( string ); var arr = [0,0,0,0]; arr[2] = arrDim[0]; arr[3] = arrDim[1]; var scale = 0; if( arr[2] <= maxSize && arr[3] <= maxSize ) return arr; if( arr[2] > arr [3] ) { scale = maxSize / arr[2]; arr[0] = scale * arr[2]; arr[1] = scale * arr[3]; } else { scale = maxSize / arr[3]; arr[0] = scale * arr[2]; arr[1] = scale * arr[3]; } return arr; } function makePost( post, pos ) { var html = ''; if( post.topicID == post.postID ) { html += '<span class="reply" style=\'padding:0px 10px 0px 10px\'>/' + post.boardStr + '/</span><br><br>'; } // HAS IMAGE if( post.imgDetails !== "" ) { if( post.topicID == post.postID ) var thumbDim = getScaled( post.imgDetails, 200 ); else var thumbDim = getScaled( post.imgDetails, 125 ); // IMAGE html += '<a target="_blank" href="http://www.touhou-project.com/' + post.boardStr + '/src/' + post.imgNumber + '.' + post.imgType + '">'; html += '<span id="thumb' + post.postID + '">'; html += '<img src="http://www.touhou-project.com/' + post.boardStr + '/thumb/' + post.imgNumber + 's.' + post.imgType + '" alt="' + post.postID + '" class="thumb" height="' + thumbDim[1] + '" width="' + thumbDim[0] + '" />'; html += '</span>'; html += '</a>'; // FILE EXPANDLINK + DETAILS + IQDB html += '<span class="filesize">'; html += 'File '; html += '<a href="http://www.touhou-project.com/' + post.boardStr + '/src/' + Number(post.imgNumber) + '.' + post.imgType + '" onclick="javascript:expandimg(\'' + Number(post.postID) + '\', \'http://www.touhou-project.com/' + post.boardStr + '/src/' + Number(post.imgNumber) + '.' + post.imgType + '\', \'http://www.touhou-project.com/' + post.boardStr + '/thumb/' + Number(post.imgNumber) + 's.' + post.imgType + '\', \'' + thumbDim[2] + '\', \'' + thumbDim[3] + '\', \'' + thumbDim[0] + '\', \'' + thumbDim[1] + '\');return false;">' + post.imgNumber + '.' + post.imgType + '</a>'; html += post.imgDetails + ' '; html += '[<a href="http://iqdb.org/?url=http://www.touhou-project.com/' + post.boardStr + '/src/' + post.imgNumber + '.' + post.imgType + '" target="_blank">iqdb</a>]'; html += '</span>'; html += '<br/>'; } // TITLE html += '<a name="' + post.postID + '"></a>'; html += '<span class="filetitle">'; html += post.threadName + ' '; html += '</span>'; // USER html += '<span class="postername">' + post.posterName + '</span>'; html += '<span class="postertrip">' + post.posterTrip + '</span>'; html += ' '; // TIME+DATE html += post.date; html += ' '; // REFLINK html += '<span class="reflink">'; html += '<a href="/' + post.boardStr + '/res/' + post.topicID + '.html#' + post.postID + '" onclick="return highlight("' + post.postID + '");">No. </a>'; html += '<a href="/' + post.boardStr + '/res/' + post.topicID + '.html#i' + post.postID + '" onclick="return insert(\'>>' + post.postID + '\n\');">' + post.postID + '</a>'; html += '</span>'; if( post.topicID == post.postID ) { // REPLY html += ' ['; html += '<a href=\'http://www.touhou-project.com/' + post.boardStr + '/res/' + post.topicID + '.html\'>Reply</a>'; html += ']'; } // SOME KIND OF ID? html += '<span id="dnb-' + post.boardStr + '-' + post.postID + '-y"></span>'; html += '<br />'; // BLOCK QUOTE html += makeMessageShort( post.postID, post.boardStr, post.text); return html; } function NavigationBar() { var html = ''; html += '<div class="navbar">'; html += '[ '; html += '<a title="General Discussion" href="/gensokyo/">gensokyo</a> / '; html += '<a title="Drawing" href="/i/">i</a> / '; html += '<a title="18+ Touhou" href="/at/">at</a> / '; html += '<a title="Words Words Words" href="/words/">words</a>'; html += ' ] '; html += '[ '; html += '<a title="General" href="/th/">th</a> / '; html += '<a title="Eientei & Lunar Capital" href="/eientei/">eientei</a> / '; html += '<a title="Forest of Magic & Flower Fields" href="/forest/">forest</a> / '; html += '<a title="Hakurei Border & Afterlife" href="/border/">border</a> / '; html += '<a title="Hakurei Shrine, Human Village & Myouren Temple" href="/shrine/">shrine</a> / '; html += '<a title="Scarlet Devil Mansion & Misty Lake" href="/sdm/">sdm</a> / '; html += '<a title="Youkai Mountain & Heaven" href="/youkai/">youkai</a> / '; html += '<a title="Underground & Makai" href="/underground/">underground</a> / '; html += '<a title="Others" href="/others/">others</a> / '; html += '<a title="Short Stories" href="/shorts/">shorts</a>'; html += ' ] '; html += '[ '; html += '<a title="not a color we know" href="/blue/">blue</a> / '; html += '<a title="Downloads" href="/rs/">rs</a>'; html += ' ] '; html += '[ '; html += '<a title="Story List" href="/storylist.html">Story List</a>'; html += ' ] '; html += '</div>'; return html; } function ScriptInject() { var html = ''; html += '<script>'; //General Scripts html += getCurrentFrameDoc; //Cookie + Style Set Scipt html += cookieValue; html += cookieStyle; html += addStyles; html += deleteStyles; //html += disableStyles; html += setStyles; //AJAX request for preview html += AJAXLike; //Preview Script html += addPreview; html += delPreview; html += parsePreview; html += toggle_visibility; html += 'cookieStyle();'; html += 'parsePreview();'; html += '</script>'; return html; } function StyleSelect() { var html = ''; html += '<script type="text/javascript" src="http://www.touhou-project.com//lib/javascript/gettext.js"></script>'; html += '<script type="text/javascript" src="http://www.touhou-project.com/lib/javascript/protoaculous-compressed.js"></script>'; html += '<script type="text/javascript"><!--'; html += 'var ku_boardspath = "http://www.touhou-project.com";'; html += 'var ku_cgipath = "http://www.touhou-project.com";'; html += 'var style_cookie = "kustyle";'; html += 'var ispage = true;'; html += '//--></script>'; html += '<script type="text/javascript" src="http://www.touhou-project.com/lib/javascript/kusaba.js"></script>'; html += '<div class="adminbar">'; html += '<select onchange="javascript:if(selectedIndex != 0)set_stylesheet(options[selectedIndex].value);return false;">'; html += '<option>Styles</option>'; html += '<option value="Burichan">Burichan</option>;'; html += '<option value="Futaba">Futaba</option>;'; html += '<option value="Photon">Photon</option>;'; html += '<option value="Chernobyl">Chernobyl</option>;'; html += '<option value="Darkish">Darkish</option>;'; html += '</select>'; html += '</div>'; return html; } ///////////////////////////// // JAVASCRIPT CREATED PAGES ///////////////////////////// function sizeLocalStorage() { var arrNames = GM_listValues(); var size = 0; for( var i = 0; i < arrNames.length; i++ ) size += GM_getValue(arrNames[i]).length; return size; } function clearLocalStorage() { var arrNames = GM_listValues(); for( var i = 0; i < arrNames.length; i++ ) GM_deleteValue(arrNames[i]); } function printLocalPosts() { var arrNames = GM_listValues(); for( var i = 0; i < arrNames.length; i++ ) console.log( arrNames[i] ); } function saveLocalPosts( arr ) { for( var i = 0; i < arr.length; i++ ) arr[i].savePost(); } function loadLocalPosts() { var arr = []; var arrNames = GM_listValues(); var arrbreaks = []; var arrdata = []; for( var i = 0; i < arrNames.length; i++ ) { if( arrNames[i].indexOf("breaks") !== -1) arrbreaks.push(arrNames[i]); if( arrNames[i].indexOf("data") !== -1 ) arrdata.push(arrNames[i]); } for( var i = 0; i < arrbreaks.length; i++ ) { var breakbrk = arrbreaks[i].split('!'); for ( var j = 0; j < arrdata.length; j++ ) { var databrk = arrdata[j].split('!'); if( breakbrk[0] === databrk[0] && breakbrk[1] === databrk[1] ) { var post = new Post(); post.loadPost( breakbrk[0], breakbrk[1] ); arr.push(post); } } } return arr; } function threadSavePost( doc, board ) { //console.log( "SAVEPOST: " + board ); var post = new Post(); post.fromSinglePost( doc, board ); post.savePost(); //console.log(window.location.host); } function savePostsRSS( doc, board ) { //console.log( "RSS BOARD: " + board ); var items = doc.getElementsByTagName("item"); var requestArr = []; for( var i = 0; i < items.length; i++ ) { var cn = items[i].childNodes; var s = cn[4].textContent.replace(/\s/g,"").split(/[.\/#]/g); if( !hasLocalPost( s[s.length-3], board ) && requestArr.indexOf("" + s[s.length-3] + board) === -1 ) { requestArr.push("" + s[s.length-3] + board); var thread1 = new RequestThread(); var url1 = 'http://www.touhou-project.com/read.php?b=' + board + '&t=' + s[s.length-3] + '&p=' + s[s.length-3] + '&single'; thread1.requestHTML( url1, threadSavePost, board ); } if( !hasLocalPost( s[s.length-1], board ) && requestArr.indexOf("" + s[s.length-1] + board) === -1 ) { requestArr.push("" + s[s.length-1] + board); var thread2 = new RequestThread(); var url2 = 'http://www.touhou-project.com/read.php?b=' + board + '&t=' + s[s.length-3] + '&p=' + s[s.length-1] + '&single'; thread2.requestHTML( url2, threadSavePost, board ); } } } function currentTimeMilli() { var d = new Date(); return d.valueOf(); } function AllStoriesPageHTML() { thps.posts = loadLocalPosts(); sortPostArrayByPostNum(); //printThreads(); var html = ''; html += NavigationBar(); html += StyleSelect(); html += '<div class="logo"> RSS Feed </div>'; var currentthread = ""; var count = 0; for( var i = 0; i < thps.posts.length; i++ ) { var post = thps.posts[i]; if( !post.isBlank() ) { var thread = thps.threads[threadIndex(post.topicID)]; if( thread.latestDate.stringToMilli() - currentTimeMilli() + thps.timeThreshold > 0 ) { if( currentthread != post.topicID ) { currentthread = post.topicID; count = thread.numChild; } else { if( currentthread != post.postID ) count -= 1; } if( post.isChild() && count < (thread.numChild - (thread.numChild - thps.maxReplies)) ) html += makeChildPost(makePost( post, 1+i )); //else //console.log( 'Hidden post - ' + post.postID + ' ' + post.boardStr ); if( !post.isChild() ) html += "<hr>" + makeOpeningPost(makePost( post, 1+i )); } } } html += '<hr/>'; html += NavigationBar(); html += ScriptInject(); return html; } function WatchedThreadsPage( open ) { open.document.write( NavigationBar() ); open.document.write( StyleSelect() ); var html = '<div class="logo"> Watched Threads </div><hr /><u1>Work in progress...</u1><hr/>'; open.document.write( html ); open.document.write( NavigationBar() ); open.document.close(); } function OptionsPageHTML( open ) { var html = ''; html += NavigationBar(); html += StyleSelect(); html += '<div class="logo"> Options </div>'; html += '<hr />'; html += '<h2>RSS OPTIONS</h2>'; html += '<u1>Check Boards</u1><br>'; for( var i = 0; i < thps.allBoards.length; i++ ) html += '<label><input type="checkbox" value="' + thps.allBoards[i] + '" >/' + thps.allBoards[i] + '/ - ' + thps.allFullBoards[i] + '</label><br>'; html += '<br><input type="text" id="refreshrate" value="' + thps.refreshRSS/60000 + '" size="1" maxlength="4"><u1> Minutes - Refresh Rate</u1><br>'; html += '<hr />'; html += '<h2>THREAD DISPLAY OPTIONS</h2>'; html += '<input type="text" id="maxreplies" value="' + thps.maxReplies + '" size="1" maxlength="4"><u1> Max visible replies</u1><br><br>'; html += '<input type="text" id="oldthreshhold" value="' + thps.timeThreshold/86400000 + '" size="1" maxlength="4"><u1> Days - Old thread threshhold</u1><br>'; html += '<hr />'; html += '<h2>OFFLINE LOCAL STORAGE</h2>'; html += '<input type="button" id="ClearCache" value="Clear Cache"><u1> - ' + (sizeLocalStorage()/1048576).toFixed(2) +' MB</u1><br>'; html += '<hr />'; html += '<input type="button" id="Save" value="Save">'; html += '<hr />'; html += NavigationBar(); html += ScriptInject(); return html; } function exec_body_scripts(body_el) { // Finds and executes scripts in a newly added element's body. // Needed since innerHTML does not run scripts. // // Argument body_el is an element in the dom. function nodeName(elem, name) { return elem.nodeName && elem.nodeName.toUpperCase() === name.toUpperCase(); } function evalScript(elem) { var data = (elem.text || elem.textContent || elem.innerHTML || "" ), head = getCurrentFrameDoc().getElementsByTagName("head")[0] || getCurrentFrameDoc().documentElement, script = getCurrentFrameDoc().createElement("script"); script.type = "text/javascript"; try { // doesn't work on ie... script.appendChild(getCurrentFrameDoc().createTextNode(data)); } catch(e) { // IE has funky script nodes script.text = data; } head.insertBefore(script, head.firstChild); head.removeChild(script); } // main section of function var scripts = [], script, children_nodes = body_el.childNodes, child, i; for (i = 0; children_nodes[i]; i++) { child = children_nodes[i]; if (nodeName(child, "script" ) && (!child.type || child.type.toLowerCase() === "text/javascript")) { scripts.push(child); } } for (i = 0; scripts[i]; i++) { script = scripts[i]; if (script.parentNode) {script.parentNode.removeChild(script);} evalScript(scripts[i]); } } function parseTextNumber( text, min, multi, def ) { var num = def * multi; var tonum = Number(text); if( !isNaN(tonum) ) { if(tonum >= min ) num = tonum * multi; else num = min * multi; } return num; } function optionsLoad() { var s = GM_getValue("settings"); if( s !== undefined && s!== null ) { thps.storyDomains = []; var arr = s.split('!'); for( var i = 0; i < arr.length; i++ ) { if( i > 0 && i < arr.length - 3 ) thps.storyDomains.push(arr[i]); if( i == arr.length - 3 ) thps.refreshRSS = Number(arr[i]); if( i == arr.length - 2 ) thps.maxReplies = Number(arr[i]); if( i == arr.length - 1 ) thps.timeThreshold = Number(arr[i]); } } } function optionsSave( focus ) { var inputs = focus.getElementsByTagName("input"); thps.storyDomains = []; for( var i = 0; i < inputs.length; i++ ) { if( inputs[i].type == "checkbox" && inputs[i].checked ) thps.storyDomains.push(inputs[i].value); if( inputs[i].id == "refreshrate" ) thps.refreshRSS = parseTextNumber(inputs[i].value, 5, 60000, 30); if( inputs[i].id == "maxreplies" ) thps.maxReplies = parseTextNumber(inputs[i].value, 0, 1, 3); if( inputs[i].id == "oldthreshhold" ) thps.timeThreshold = parseTextNumber(inputs[i].value, 0, 86400000, 7); } } function optionsSaveString( focus ) { var s = ""; var inputs = focus.getElementsByTagName("input"); for( var i = 0; i < inputs.length; i++ ) { if( inputs[i].type == "checkbox" && inputs[i].checked ) s += '!' + inputs[i].value; if( inputs[i].id == "refreshrate" ) s += '!' + parseTextNumber(inputs[i].value, 5, 60000, 30); if( inputs[i].id == "maxreplies" ) s += '!' + parseTextNumber(inputs[i].value, 0, 1, 3); if( inputs[i].id == "oldthreshhold" ) s += '!' + parseTextNumber(inputs[i].value, 0, 86400000, 7); } GM_setValue( "settings", s ); } function OptionsPage( focus ) { clearIntervals(); optionsLoad(); var head = focus.getElementsByTagName("head")[0]; var body = focus.getElementsByTagName("body")[0]; body.innerHTML = OptionsPageHTML(); exec_body_scripts( head ); exec_body_scripts( body ); var evt = getCurrentFrameDoc().createEvent('Event'); evt.initEvent('load', false, false); window.dispatchEvent(evt); var bar = focus.getElementsByClassName("navbar"); for( var i = 0; i < bar.length; i++ ) { bar[i].appendChild(document.createTextNode("[ ")); bar[i].appendChild(NewScriptMenuLink(function(){AllStoriesPage(getCurrentFrameDoc());}, "RSS Feed")); //bar[i].appendChild(document.createTextNode(" / ")); //bar[i].appendChild(NewScriptMenuLink(function(){GetPage(WatchedThreadsPage,"_self");}, "Watched Threads")); bar[i].appendChild(document.createTextNode(" / ")); bar[i].appendChild(NewScriptMenuLink(function(){OptionsPage(getCurrentFrameDoc());}, "Options")); bar[i].appendChild(document.createTextNode(" ]")); } var inputs = focus.getElementsByTagName("input"); for( var i = 0; i < inputs.length; i++ ) if( inputs[i].type == "checkbox" && thps.storyDomains.indexOf(inputs[i].value) != -1 ) inputs[i].checked = true; focus.getElementById("ClearCache").onmousedown = function(){clearLocalStorage();}; focus.getElementById("Save").onmousedown = function(){optionsSaveString(focus); optionsLoad();}; } function AllStoriesPage( focus ) { clearIntervals(); thps.posts = loadLocalPosts(); for( var i = 0; i < thps.storyDomains.length; i++ ) { var thread = new RequestThread(); thread.requestHTML( 'http://www.touhou-project.com/' + thps.storyDomains[i] + '/rss.xml', savePostsRSS, thps.storyDomains[i] ); } var head = focus.getElementsByTagName("head")[0]; var body = focus.getElementsByTagName("body")[0]; body.innerHTML = AllStoriesPageHTML(); exec_body_scripts( head ); exec_body_scripts( body ); var evt = getCurrentFrameDoc().createEvent('Event'); evt.initEvent('load', false, false); window.dispatchEvent(evt); var bar = focus.getElementsByClassName("navbar"); for( var i = 0; i < bar.length; i++ ) { bar[i].appendChild(document.createTextNode("[ ")); bar[i].appendChild(NewScriptMenuLink(function(){AllStoriesPage(getCurrentFrameDoc());}, "RSS Feed")); //bar[i].appendChild(document.createTextNode(" / ")); //bar[i].appendChild(NewScriptMenuLink(function(){GetPage(WatchedThreadsPage,"_self");}, "Watched Threads")); bar[i].appendChild(document.createTextNode(" / ")); bar[i].appendChild(NewScriptMenuLink(function(){OptionsPage(getCurrentFrameDoc());}, "Options")); bar[i].appendChild(document.createTextNode(" ]")); } thps.intervals.push(setInterval( function(){ thps.posts = loadLocalPosts(); for( var i = 0; i < thps.storyDomains.length; i++ ) { var thread = new RequestThread(); thread.requestHTML( 'http://www.touhou-project.com/' + thps.storyDomains[i] + '/rss.xml', savePostsRSS, thps.storyDomains[i] ); } var offset = window.pageYOffset; var head = focus.getElementsByTagName("head")[0]; var body = focus.getElementsByTagName("body")[0]; body.innerHTML = AllStoriesPageHTML(); exec_body_scripts( head ); exec_body_scripts( body ); var evt = getCurrentFrameDoc().createEvent('Event'); evt.initEvent('load', false, false); window.dispatchEvent(evt); var bar = focus.getElementsByClassName("navbar"); for( var i = 0; i < bar.length; i++ ) { bar[i].appendChild(document.createTextNode("[ ")); bar[i].appendChild(NewScriptMenuLink(function(){AllStoriesPage(getCurrentFrameDoc());}, "RSS Feed")); //bar[i].appendChild(document.createTextNode(" / ")); //bar[i].appendChild(NewScriptMenuLink(function(){GetPage(WatchedThreadsPage,"_self");}, "Watched Threads")); bar[i].appendChild(document.createTextNode(" / ")); bar[i].appendChild(NewScriptMenuLink(function(){OptionsPage(getCurrentFrameDoc());}, "Options")); bar[i].appendChild(document.createTextNode(" ]")); } window.scrollTo(0, offset); }, thps.refreshRSS )); } function GetPage( page, focus ) { var open = window.open("", focus); page( open ); } ///////////////////////////// // HELPER FUNCTIONS ///////////////////////////// String.prototype.removeWhiteSpace = function() { this.replace(/\t|\r|\n|\s/g,""); return this; }; String.prototype.popFront = function() { var size = this.length; var remainder = ""; if( size > 0 ) remainder = this.substring(1,size); return remainder; }; String.prototype.popBack = function() { var size = this.length; var remainder = ""; if( size > 0 ) remainder = this.substring(0,size-1); return remainder; }; function nodeHTML( className, html ) { var node = document.createElement( "span" ); node.className = className; node.innerHTML = html; return node; } function numpad( num, pad ) { var string = ""; string += num; while (string.length < pad) string = "0" + string; return string; } function NewScriptMenuLinkBR( script, text ) { var line = document.createElement("li"); var link = document.createElement("a"); var t = document.createTextNode(text); line.appendChild(link); link.appendChild(t); link.href = "javascript:void(0)"; link.onclick = script; return line; } function NewScriptMenuLink( script, text ) { var link = document.createElement("a"); var t = document.createTextNode(text); link.appendChild(t); link.href = "javascript:void(0)"; link.onclick = script; return link; } function CheckForScript( name ) { var scripts = document.getElementsByTagName("script"); for( var i = 0; i < scripts.length; i++ ) if( scripts[i].src == name ) return true; return false; } function PrintByElementTag( doc, tag ) { var tags = doc.getElementsByTagName( tag ); for( var i = 0; i < tags.length; i++ ) console.log( tags[i] ); } ///////////////////////////// // MAIN EXECUTION SECTION ///////////////////////////// //clearLocalStorage(); if( CheckForScript("http://www.touhou-project.com/lib/javascript/menu.js") ) { var title = document.createElement("h2"); var links = document.createElement("u1"); title.innerHTML = 'THPsupplemental'; links.appendChild(NewScriptMenuLinkBR(function(){AllStoriesPage(window.top.frames[1].document);}, "RSS Feed")); //links.appendChild(NewScriptMenuLinkBR(function(){GetPage(WatchedThreadsPage,"main");}, "Watched Threads")); links.appendChild(NewScriptMenuLinkBR(function(){OptionsPage(window.top.frames[1].document);}, "Options")); document.body.appendChild(title); document.body.appendChild(links); } if( CheckForScript("http://www.touhou-project.com/banner.js") ) { var bar = document.getElementsByClassName("navbar"); for( var i = 0; i < bar.length; i++ ) { bar[i].appendChild(document.createTextNode("[ ")); bar[i].appendChild(NewScriptMenuLink(function(){AllStoriesPage(document);}, "RSS Feed")); //bar[i].appendChild(document.createTextNode(" / ")); //bar[i].appendChild(NewScriptMenuLink(function(){GetPage(WatchedThreadsPage,"_self");}, "Watched Threads")); bar[i].appendChild(document.createTextNode(" / ")); bar[i].appendChild(NewScriptMenuLink(function(){OptionsPage(document);}, "Options")); bar[i].appendChild(document.createTextNode(" ]")); } }