🏠 Home 

Heise.de Forum: all comments on one page v2

All comments on one page, iReply, quick-vote, user-scores.


Install this script?
// ==UserScript==
// @name          Heise.de Forum: all comments on one page v2
// @namespace     Vorticon
// @author        marrr, edited by commander_keen, jn
// @version       2.5.0
// @description   All comments on one page, iReply, quick-vote, user-scores.
// @include       http://www.heise.de/*/foren/*
// @include       https://www.heise.de/*/foren/*
// @include       http://www.heise.de/foren/*
// @include       https://www.heise.de/foren/*
// @include       http://www.heise.de/forum/*
// @include       https://www.heise.de/forum/*
// @grant         GM_getValue
// @grant         GM_setValue
// @grant         GM_xmlhttpRequest
// @grant         GM_log
// @grant         GM_addStyle
// ==/UserScript==
(function() { // Opera wrapper
/********************************************
* USER SETTINGS
********************************************/
// show article ratings by default https://www.heise.de/forum/heise-online/Meinungen-zu-heise-online/Bewertungsanzeige-im-Forum-kann-ab-sofort-ausgeblendet-werden/posting-30356078/show/
var showRatings = 1;
// don't fetch postings whose title ends in (kT), n.T. etc.
var skipNoTextPostings = 0;
var overviewPagePostCount  = 16;
var maxJoinedPosts         = overviewPagePostCount * 10; // in overview, add x posts * y pages
var maxJoinedPostsInThread = 80;
// user score is scaled by this value and then fitted into 0-10
var posterScoreScaleFactor = 0.3;
// here you can enable joining on different overview levels
/*var joinTopLevelForen = 1; this is nowhere to be found*/
var joinSubLevelForen = 1;
var enableThreadView = 1;
// reply stuff
var enableIReply    = 1;
var enableAutoQuote = 1;
var enableQuickVote = 1;
// "plain" for disabling colored bar indentation; or "colored" to enable
var displayMode = "colored";
// defines the colors used for colored indentation
var branchBorderStyle = "1px dashed";
function getBranchColor(lvl) {
switch(lvl % 8) {
case 0:  return "#999999";
case 1:  return "#445599";
case 2:  return "#995544";
case 3:  return "#449955";
case 4:  return "#994455";
case 5:  return "#554499";
case 6:  return "#CC77CC";
case 7:  return "#554499";
}
}
function getQuoteColor(lvl) {
switch(lvl) {
case 2:  return "#668811";
case 3:  return "#445599";
case 4:  return "#995544";
case 5:  return "#449955";
case 6:  return "#994455";
case 7:  return "#554499";
case 8:  return "#CC77CC";
case 9:  return "#554499";
// ...
default: return "";
}
}
/********************************************
* BROWSER DEPENDENT
********************************************/
function isOpera() {
return typeof(opera) != "undefined";
}
function isSafari() {
return typeof(safari) != "undefined" || /apple/i.test(navigator.vendor) || /safari/i.test(navigator.userAgent);
}
function isChrome() {
return typeof(chrome) != "undefined";
}
function log(msg) {
if (isOpera()) opera.postError(msg);
// else if (isChrome()) console.log(msg); // chrome supports GM_log
else if(isSafari())
{
// according to the docs, GM_log is supported
if(typeof(GM_log) != undefined)
GM_log(msg);
}
else GM_log(msg);
}
function requestHTML(fileUrl, callback, nr, div) {
fileUrl = ensureAbsoluteUrl(fileUrl);
if (isOpera() || isSafari()) {
var xmlHttp = new XMLHttpRequest();
xmlHttp.open('GET', fileUrl, true);
xmlHttp.onreadystatechange = function () {
if(xmlHttp.readyState == 4 &&
xmlHttp.status     == 200)
callback(xmlHttp.responseText, nr, div, fileUrl);
};
xmlHttp.send(null);
}
else { // maybe the opera way works here, but this one contains more GM_s
GM_xmlhttpRequest(
{
method: 'GET',
url: fileUrl,
onload: function(resp) {
if(resp.status == 200)
callback(resp.responseText, nr, div, fileUrl);
}
});
}
}
// TODO: this is limited to simple values, yet sufficient
function setLocalValue(name, val) {
if (isOpera() || isChrome() || isSafari()) {
var lifeTime = 31536000;
document.cookie = escape(name) + "=" + escape(val) +
";expires=" + (new Date((new Date()).getTime() + (1000 * lifeTime))).toGMTString() + ";path=/";
}
else
GM_setValue(name, val);
}
function getLocalValue(name, def ) {
if(isOpera() || isChrome() || isSafari()) {
var cookieJar = document.cookie.split("; ");
for(var x = 0; x < cookieJar.length; x++) {
var oneCookie = cookieJar[x].split( "=" );
if( oneCookie[0] == escape(name) ) {
try {
eval('var footm = '+unescape(oneCookie[1]));
return footm;
} catch(e) { return def; }
}
}
return def;
}
else
return GM_getValue(name, def);
}
/********************************************
* THE CODE
********************************************/
var baseUrl = document.location.protocol + '//' + document.location.host;
var postingRegExp;
var isNewForum = /^\/forum\//.test(document.location.pathname) ? true : false;
if(isNewForum)
postingRegExp = /((<div class="userbar[\s\S]*?)(?=(<div class="metabar">))\3[\s\S]*?<\/div>\s*<\/div>)(?=\s*<div class="thread_view_switch">)/;
else
postingRegExp = /((<div class="vote_posting">[\s\S]*?)?(?=(<div class="posting_date">))\3[\s\S]*?(?=(<div class="tovote_links">))\4[\s\S]*?<\/div>)/;
var searchUrl;
if(isNewForum)
searchUrl = baseUrl + '/forum/suche/?rm=search&q=';
else
searchUrl = baseUrl + '/foren/suche?q=';
function xpath(xp, root)
{
if(root === null) return null;
if(root === undefined) root = document;
return document.evaluate(xp, root, null, XPathR###lt.ORDERED_NODE_SNAPSHOT_TYPE, null);
}
function xpath1(xp, root)
{
var res = xpath(xp, root);
return res ? ((res.snapshotLength > 0) ? res.snapshotItem(0) : null) : null;
}
function ensureAbsoluteUrl(url)
{
if(url.match(/^\//))
url = baseUrl + url;
else if(url.match(/^\?/))
url = document.location.href.replace(document.location.search, "") + url;
return url;
}
function defineScriptInPageContext(code)
{
var script = document.createElement("script");
script.type = "application/javascript";
script.innerHTML = code;
document.body.appendChild(script);
}
function getElementsByClassName(oElm, strTagName, strClassName) {
var arrElements = (strTagName == "*" && document.all) ?
document.all : oElm.getElementsByTagName(strTagName);
var arrReturnElements = new Array();
strClassName = strClassName.replace(/\-/g, "\\-");
var oRegExp = new RegExp("(^|\\s)" + strClassName + "(\\s|$)");
for(var i = 0; i < arrElements.length; i++) {
var oElement = arrElements[i];
if(oRegExp.test(oElement.className))
arrReturnElements.push(oElement);
}
return (arrReturnElements);
}
function grepTitleLinkURL(html)
{
if(isNewForum)
var res = html.match(/<a[^>]*? href="([^"]*?)"[^>]*? class="posting_subject">/);
else
var res = html.match(/<a[^>]*? href="([\s\S]*?)"[^>]*? title=(["'])[\s\S]*?\2>/i);
if(!res) return null;
return res[1];
}
function grepTitle(html)
{
var res = html.match(/>([^<]*)<\/a>/i);
if(!res) return null;
return res[1];
}
function joinThreadPosts()
{
if(isNewForum) {
joinThreadPostsNew();
return;
}
// do it this way to respect priorities
var rootPostDiv = xpath1("//div[@class='forum_content']");
if(rootPostDiv == null)
rootPostDiv = xpath1("//div[@id='mitte_forum']");
if(rootPostDiv == null)
rootPostDiv = xpath1("//div[@id='mitte']");
if(rootPostDiv == null)
rootPostDiv = xpath1("//td[@class='f-content']");
if(rootPostDiv == null)
rootPostDiv = xpath1("//*[@id='parttime-forum']");
if(rootPostDiv == null)
log("Root div not found!");
var rootPostText = xpath1("p[@class='posting_text']", rootPostDiv);
appendReplyFrame(rootPostText, -1, rootPostDiv);
var replyLinkA = xpath1(".//a[text() = 'Beantworten']", rootPostDiv);
var replyLink = replyLinkA == null ? "" : replyLinkA.href;
processMessageDiv(rootPostDiv, document.location.href, replyLink);
var threadsList = xpath1("//ul[@class='thread_tree']");
if(threadsList == null)
{
log("Thread tree not found - activate it!");
return;
}
var divStack  = new Array();
divStack.peek = function() { return this[this.length - 1]; }
var answerDiv = document.createElement('div');
threadsList.parentNode.insertBefore(answerDiv, threadsList);
divStack.push(answerDiv);
var threadMsgs = xpath(".//div[@class='thread_title']", threadsList);
var maxJoinCnt  = Math.min(threadMsgs.snapshotLength, maxJoinedPostsInThread);
var afterActive = 0;
var cntJoined   = 0;
var rootAbsoluteDepth = 0;
for(var i = 0; i < threadMsgs.snapshotLength && cntJoined <= maxJoinCnt; i++) {
var msgDiv   = threadMsgs.snapshotItem(i);
var isActive = 0;
// search the currently selected beitrag
if(msgDiv.innerHTML.match("beitrag_aktiv") ||
msgDiv.innerHTML.match("active_post")) {
afterActive = 1;
isActive = 1;
rootAbsoluteDepth = countLevel(msgDiv);
continue;
}
else if(afterActive == 0) continue;
// count the number of next_levels upwards
var curRelativeDepth = countLevel(msgDiv) - rootAbsoluteDepth;
// only show current subnode
if(curRelativeDepth <= 0) break;
// find the URL
var url = grepTitleLinkURL(msgDiv.innerHTML);
if(!url) {
log("Error parsing: " + msgDiv.innerHTML);
continue;
}
// create div for the branch
var divBranch = document.createElement('div');
if(!isActive)
divBranch.style.marginLeft = "20px";
if(displayMode == "colored")
divBranch.style.borderLeft = branchBorderStyle + " " + getBranchColor(curRelativeDepth);
//		divBranch.innerHTML = '<a style="display: block; font-size: 6px">^^^</a>';
// create div for the post
var divPost = document.createElement('div');
divPost.style.border = "1px dashed #DDDDDD";
divPost.style.marginLeft = "8px"; // some space from the border
// decend down to current level
while(curRelativeDepth < divStack.length) divStack.pop();
// add it
divStack.peek().appendChild(divBranch);
divBranch.appendChild(divPost);
// create the ireply frame
appendReplyFrame(divBranch, cntJoined, divPost);
// remember current branch
divStack.push(divBranch);
// grep it
requestHTML(url, callbackThread, i, divPost);
cntJoined++;
}
if(afterActive == 0) {
log("found no active post!");
return;
}
}
function joinThreadPostsNew()
{
// do it this way to respect priorities
var rootPostDiv = xpath1(".//div[@id='forum_wrap']");
if(rootPostDiv == null)
rootPostDiv = xpath1("//div[@id='mitte']");
if(rootPostDiv == null)
log("Root div not found! (joinThreadPostsNew)");
if(enableIReply === 1) {
var rootPostText = xpath1(".//div[@class='post']", rootPostDiv);
appendReplyFrame(rootPostText, -1, rootPostDiv);
}
var replyLinkA = xpath1(".//a[@class='reply']", rootPostDiv);
var replyLink = replyLinkA === null ? "" : replyLinkA.href;
var quoteLinkA = xpath1(".//a[@class='quote']", rootPostDiv);
var quoteLink = quoteLinkA === null ? "" : quoteLinkA.href;
processMessageDiv(rootPostDiv, document.location.href, replyLink, quoteLink);
var threadsList = document.getElementById('tree_thread_list');
if(threadsList == null) {
// don't complain so often
if(xpath1(".//div[@class='thread_view_switch']", document.getElementById("forum_wrap")))
log("Thread tree not found - activate it! (joinThreadPostsNew)");
return;
}
// apply stylesheet once
addStyles();
if(showRatings === 1) {
addStyle('span.rating { display: inline !important /* Rating for each post*/ }\
.tree_thread_list--rating { display: inline !important /* Rating in post list at the bottom of the thread, and in thread list*/ }');
var votebuttonLoggedOutSpan = xpath1(".//span[@title='Bitte loggen Sie sich ein, um den Beitrag zu bewerten.']", document.getElementById("forum_wrap"));
if(votebuttonLoggedOutSpan === null) {
addStyle(".first_posting + .rating_buttons_wrapper { display: inline !important /* Show voting buttons */}\
.post + .rating_buttons_wrapper { bottom: 60px; display: inline; float: right; position: relative }\
#tree_thread_list { clear: right } /* sonst verschieben sich Bewertung+User+Datum (verursacht durch .ratings_button_wrapper{position != absolute}) */ \
");
} else
addStyle(".rating_buttons_wrapper { display: none !important /* Dont show voting buttons if voting is not posible */ }");
}
var divStack  = new Array();
divStack.peek = function() { return this[this.length - 1]; }
var answerDiv = document.createElement('div');
threadsList.parentNode.insertBefore(answerDiv, threadsList);
divStack.push(answerDiv);
var threadMsgs  = xpath(".//li[contains(@class, 'posting_element')]", threadsList);
var maxJoinCnt  = Math.min(threadMsgs.snapshotLength, maxJoinedPostsInThread);
var afterActive = false;
var cntJoined   = 0;
var rootAbsoluteDepth     = 0;
var skippedEntryTitles    = [];
skippedEntryTitles[1] = xpath1(".//a[@class='posting_subject']", threadsList).textContent.trim();
for(var i = 0; i < threadMsgs.snapshotLength && cntJoined <= maxJoinCnt; i++) {
var msgDiv   = threadMsgs.snapshotItem(i);
var isActive = false;
// search the currently selected beitrag
if(msgDiv.className.match("current_posting")) {
afterActive       = true;
isActive          = true;
rootAbsoluteDepth = countLevel(msgDiv);
continue;
}
else if(afterActive === false) continue;
// count the number of next_levels upwards
var curRelativeDepth = countLevel(msgDiv) - rootAbsoluteDepth;
// only show current subnode
if(curRelativeDepth <= 0) break;
// find the URL
var url = grepTitleLinkURL(msgDiv.innerHTML);
if(!url) {
// don't complain about locked posts
if(!msgDiv.innerHTML.match(/<span [^>]*class=(["'])posting_subject locked\1>/))
log("Error parsing (joinThreadPostsNew): " + msgDiv.innerHTML);
continue;
}
// create div for the branch
var divBranch = document.createElement('div');
if(!isActive)
divBranch.style.marginLeft = "20px";
if(displayMode == "colored")
divBranch.style.borderLeft = branchBorderStyle + " " + getBranchColor(curRelativeDepth);
// create div for the post
var divPost = document.createElement('div');
divPost.style.border = "1px dashed #DDDDDD";
divPost.style.marginLeft = "8px"; // some space from the border
// descend down to current level
while(curRelativeDepth < divStack.length) divStack.pop();
// add it
divStack.peek().appendChild(divBranch);
divBranch.appendChild(divPost);
// create the ireply frame
appendReplyFrame(divBranch, cntJoined, divPost);
// remember current branch
divStack.push(divBranch);
// don't retrieve (KeinText) contributions from the server
skippedEntryTitles[countLevel(msgDiv)] = grepTitle(msgDiv.innerHTML).trim().replace(/^Re:\s*/i, "");
if(skipNoTextPostings === 1
&& isNoTextEntry(skippedEntryTitles[countLevel(msgDiv)])
&& skippedEntryTitles[countLevelNew(msgDiv)-1] != skippedEntryTitles[countLevel(msgDiv)]) {
processMessageDivNew_NoText(msgDiv, url, divPost);
} else {
// grep it
requestHTML(url, callbackThread, i, divPost);
}
cntJoined++;
}
if(afterActive === false) {
log("found no active post! (joinThreadPostsNew)");
return;
}
}
function appendReplyFrame(div, id, divPost)
{
if(div == null)
{
log("div to attach reply frame to is null");
return;
}
var iReplyFrame = document.createElement('iframe');
iReplyFrame.id = "reply" + id;
iReplyFrame.style.display = 'none';
iReplyFrame.style.width   = '100%';
iReplyFrame.style.height  = '35em';
divPost.setAttribute("replyFrameID", iReplyFrame.id);
var divReply = document.createElement('div');
divReply.style.marginLeft = divPost.style.marginLeft;
divReply.appendChild(iReplyFrame);
div.appendChild(divReply);
}
function callbackThread(txt, nr, div, url)
{
if(isNewForum) {
callbackThreadNew(txt, nr, div, url);
return;
}
try
{
// pre-check the posting for performance issues
// else match can lock up
if(txt.indexOf('<div class="posting_date">') == -1 ||
txt.indexOf('<div class="tovote_links">') == -1) {
log("No known posting: " + url);
return;
}
var mtchs = txt.match(postingRegExp);
if(mtchs == null) {
div.innerHTML = "<i>Could not load comment</i>";
return;
}
var html = mtchs[1];
div.innerHTML = html;
var replyLinks = txt.match(/<a href="([^"]*)"\s*>Beantworten<\/a>/);
var replyLink  = (replyLinks != null) ? replyLinks[1] : "";
processMessageDiv(div, url, replyLink);
}
catch(e)
{
log('Error processing post: ' + e);
}
}
function callbackThreadNew(txt, nr, div, url)
{
try
{
// pre-check the posting for performance issues
// else match can lock up
if(txt.indexOf('<div class="thread_view_switch">') === -1) {
log("No known posting (callbackThreadNew): " + url);
return;
}
var mtchs = txt.match(postingRegExp);
if(mtchs == null) {
div.innerHTML = "<i>Could not load comment (new forum)</i>";
return;
}
var html = mtchs[1];
div.innerHTML = html;
var replyLinks = txt.match(/<a href="([^"]*)" class="reply"[^>]*>[\s]*Antworten[\s]*<\/a>/);
var replyLink  = (replyLinks !== null) ? replyLinks[1] : "";
var quoteLinks = txt.match(/<a href="([^"]*)" class="quote"[^>]*>[\s]*Zitieren[\s]*<\/a>/);
var quoteLink  = (quoteLinks !== null) ? quoteLinks[1] : "";
processMessageDiv(div, url, replyLink, quoteLink);
}
catch(e)
{
log('Error processing post (callbackThreadNew): ' + e);
}
}
function processMessageDiv(div, messageUrl, replyLink, quoteLink) {
// parameter quoteLink is only used in the new forum
if(isNewForum) {
processMessageDivNew(div, messageUrl, replyLink, quoteLink);
return;
}
// link username to search
var userI = xpath1(".//div[@class='user_info']/i", div);
var userName;
if(userI != null)
{
userName = userI.innerHTML.replace(/^(.*?),&nbsp;.*$/, "$1");
userI.innerHTML = "<a href=\"" + searchUrl + escape(userName) + "\">" + userI.innerHTML + "</a>";
userI.innerHTML += getPosterScoreBarCode(userName);
}
else
{
userName = "?";
log("Could not find username div!");
}
// colorize quote
var quotes = xpath(".//p/span[@class='quote']", div);
for(var i = 0; i < quotes.snapshotLength; i++) {
var e = quotes.snapshotItem(i);
var patternLength = 10;
var m = e.innerHTML.match(/^((?:&gt;&nbsp;)+)/);
if(m != null) {
var color = getQuoteColor(m[0].length / patternLength);
if(color != "")
e.innerHTML = "<span style=\"color: " + color + "\">" + e.innerHTML + "</span>";
}
}
// set the reply links
if(replyLink != "") {
replyLink       = ensureAbsoluteUrl(replyLink);
replyLinkInline = "<a style=\"color: #6673DD\" onclick=\"iReply('" + div.getAttribute ("replyFrameID") +
"', '" + replyLink + "')\">iReply</a>";
replyLink       = "<a href=\"" + replyLink + "\">Beantworten</a>";
if(userI != null)
userI.innerHTML += " --- " + replyLink + (enableIReply ? " / " + replyLinkInline : "");
}
// link posting title to posting
var postingSubject = xpath1(".//h3[@class='posting_subject']", div);
if(postingSubject != null)
postingSubject.innerHTML = "<a href=\"" + messageUrl + "\">" + postingSubject.innerHTML + "</a>";
else
log("Posting subject not found!");
// relink voting buttons
if(enableQuickVote) {
var voteLinks = xpath(".//div[@class='tovote_links']/a", div);
for(var i = 0; i < voteLinks.snapshotLength; i++) {
var voteLink = voteLinks.snapshotItem(i);
var url = voteLink.href;
voteLink.removeAttribute("href");
voteLink.addEventListener("click",
quickVoteFunctionBuilder(voteLink, url, userName), true);
voteLink.setAttribute("onclick", "sendVote(this, '" + url + "');");
}
}
}
function processMessageDivNew_NoText(div, messageUrl, divPost) {
var userH;
var userName = div.getElementsByTagName("span")[0].getElementsByTagName("span")[0].innerText;
if(userName !== "") {
userH = "<a href=\"" + searchUrl + escape(userName) + "\">" + userName + "</a>";
userH+= getPosterScoreBarCode(userName);
}
var replyLink = ensureAbsoluteUrl(messageUrl).replace(/\/show\/$/, "/reply/");
var quoteLink = ensureAbsoluteUrl(messageUrl).replace(/\/show\/$/, "/quote-1/reply");
replyLink =    '<a href="' + replyLink + '" class="reply">Beantworten<\/a>/';
quoteLink = ' | <a href="' + quoteLink + '" class="quote">Zitieren<\/a>';
replyLink+=quoteLink;
var replyLinkInline = "<a style=\"color: #6673DD\" id=\"iReplyLink_" + divPost.getAttribute("replyFrameID") +"\">iReply</a>";
userH += " --- " + replyLink + (enableIReply ? " / " + replyLinkInline : "");
var ntTime = div.getElementsByTagName("time")[0].outerHTML;
ntTime = ntTime.replace(/class="[^"]*"/, 'class="posting_timestamp"');
var ntRatingInner = div.getElementsByTagName("span")[0].getElementsByTagName("span")[1].innerHTML;
var ntLinkText    = div.getElementsByTagName("span")[0].getElementsByTagName("a")[0].innerText.trim();
var ntPostingID = messageUrl.match(/posting-(\d+)\/show\//)[1];
var ntDiv = '<div class="userbar clearfix"><ul class="author forum_userbar--author userbar_list"><li><span class="full_user_string"><span class="pseudonym">' + userH + '</span></span></li></ul><p><!--           xxx Beiträge seit xx.xx.xxxx  --></p><div class="user_interaction"><!-- <a href="Link nicht vorhanden /user-xxxxxx/posting-xxxxxxxx/ignore/" class="ignore" rel="nofollow">ignorieren</a>  --></div></div>';
ntDiv+= '<div class="metabar">' + ntTime;
ntDiv+= '<ul><li><span class="rating js-rating-chart__wrapper">'+ntRatingInner + '</span></li>';
ntDiv+= '<li><a href="https://www.heise.de/forum/p-' + ntPostingID + '/" class="perma" rel="nofollow">Permalink</a></li>';
ntDiv+= '<li><a href="https://www.heise.de/forum/posting-' + ntPostingID + '/feedback-links/" class="feedback" rel="nofollow">Melden</a></li>';
ntDiv+= '</ul></div><h1 class="thread_title"><a href="' + messageUrl +'">' + ntLinkText + '</a></h1>';
divPost.innerHTML = ntDiv;
if(enableIReply === 1)
document.getElementById("iReplyLink_" + divPost.getAttribute("replyFrameID"))
.addEventListener("click", function() {
iReplyNew(div.getAttribute ("replyFrameID"), replyURL)
}, false);
}
function processMessageDivNew(div, messageUrl, replyURL, quoteLink) {
// link username to search
var userI = xpath1(".//span[@class='realname']|.//span[@class='pseudonym']/a|.//span[@class='pseudonym']", div);
var userName;
var replyLink;
if(userI !== null) {
userName = userI.textContent;
userI.innerHTML = "<a href=\"" + searchUrl + escape(userName) + "\">" + userI.innerHTML + "</a>";
userI.innerHTML += getPosterScoreBarCode(userName);
}
else {
userName = "?";
log("Could not find username div! (processMessageDivNew)");
}
// set the reply links
if(replyURL != "") {
replyURL            = ensureAbsoluteUrl(replyURL);
var replyLinkInline = "<a style=\"color: #6673DD\" id=\"iReplyLink_" + div.getAttribute("replyFrameID") +"\">iReply</a>";
replyLink           = "<a href=\"" + replyURL + "\">Beantworten</a>";
if(quoteLink != "") {
quoteLink  = ensureAbsoluteUrl(quoteLink);
quoteLink  = " | <a href=\"" + quoteLink + "\">Zitieren</a>";
replyLink += quoteLink;
}
if(userI !== null) {
userI.innerHTML += " --- " + replyLink + (enableIReply ? " / " + replyLinkInline : "");
if(enableIReply === 1)
document.getElementById("iReplyLink_" + div.getAttribute("replyFrameID"))
.addEventListener("click", function() {
iReplyNew(div.getAttribute ("replyFrameID"), replyURL)
}, false);
}
}
// link posting title to posting
var postingSubject = xpath1(".//h1[@class='thread_title']", div);
if(postingSubject !== null)
postingSubject.innerHTML = "<a href=\"" + messageUrl + "\">" + postingSubject.innerHTML + "</a>";
else
log("Posting subject not found!(processMessageDivNew)");
// relink voting buttons
if(enableQuickVote) {
// not necessary when you cannot vote
var votebuttonLoggedOutSpan = xpath1(".//span[@title='Bitte loggen Sie sich ein, um den Beitrag zu bewerten.']", document.getElementById("forum_wrap"));
if(votebuttonLoggedOutSpan !== null) {
return;
}
var voteLinks = xpath(".//a[contains(@class, 'rating_post')]", div);
for(var i = 0; i < voteLinks.snapshotLength; i++) {
var voteLink = voteLinks.snapshotItem(i);
//var url = voteLink.href;	// always points to positive rating?!
//voteLink.removeAttribute("href");	// modifying the element will invalidate the list element and the event listener will match nothing
voteLink.addEventListener("click", function(event) {
if(GM_xmlhttpRequest || XMLHttpRequest)
event.preventDefault();
quickVoteFunctionBuilderNew(this.href, userName);
sendVoteNew(this, this.href);
}
, false);
}
}
}
function quickVoteFunctionBuilder(voteLink, url, author) {
return function() {
log("voted for author: " + author);
// mark as voted
voteLink.style.backgroundColor = "yellow";
// extract score
var matches = url.match(/postvote-(-?\d)/);
if(!matches) return;
var score = parseInt(matches[1]);
log("score: " + score);
// score the author
scorePoster(author, score);
};
}
function quickVoteFunctionBuilderNew(url, author) {
// extract score
var matches = url.match(/\/rate\/(positive|negative)\/$/);
if(!matches) {
log("vote url not found");
return;
}
var score = 0;
if(     matches[1] === "positive") score = 1;
else if(matches[1] === "negative") score = -1;
log("voting score: " + score + " added for author " + author);
// score the author
scorePoster(author, score);
}
function sendVoteNew(target, voteUrl) {
//prefer the GM_ method because XMLHttpRequest requires XHR permission in uMatrix
if(GM_xmlhttpRequest) {
GM_xmlhttpRequest({
method: 'GET',
url: voteUrl,
onload: function(resp) {
if(resp.status == 200) {
target.style.backgroundColor = "yellow";
}
}
});
}
else if(XMLHttpRequest) {
var xmlHttp = new XMLHttpRequest();
xmlHttp.open('GET', voteUrl, true);
xmlHttp.onreadystatechange = function () {
if(xmlHttp.readyState == 4 &&
xmlHttp.status     == 200) {
target.style.backgroundColor = "yellow";
}
};
xmlHttp.send(null);
}
else target.click();
}
function addStyle(styleText) {
if(GM_addStyle) {
GM_addStyle(styleText);
}
else {
var copyStyles = document.createElement("style");
copyStyles.setAttribute("type", "text/css");
copyStyles.appendChild(document.createTextNode(styleText));
document.getElementsByTagName("head")[0].appendChild(copyStyles);
}
}
function addStyles() {
if(top !== self) return;
// apply style rules from the first post to all posts
// fixes look and positioning of the voting buttons
// this is very messy but I want to avoid an external file
var styleText = "\
/*#thread #replys li .post .posting_options,#thread .first_posting*/ .post .posting_options{\
line-height:10px;\
position:absolute;\
right:0\
}\
/*#thread #replys li .post .posting_options .thanks,#thread .first_posting*/ .post .posting_options .thanks{\
display:inline-block;\
margin:-22px 0 0 10px;\
float:left;\
line-height:120%;\
text-align:center\
}\
/*#thread #replys li .post .posting_options .thanks .rate_post,#thread .first_posting*/ .post .posting_options .thanks .rate_post{\
float:left;\
width:12px;\
height:12px;\
overflow:hidden;\
color:#039;\
cursor:pointer;\
text-align:center;\
font-size:18px;\
line-height:14px;\
border:1px solid #ccc;\
background:#fff;\
padding:4px\
}\
/*#thread #replys li .post .posting_options .thanks .rate_post:hover,#thread .first_posting*/ .post .posting_options .thanks .rate_post:hover{\
opacity:.8;\
text-decoration:none\
}\
/*#thread #replys li .post .posting_options .thanks .rate_post.negative,#thread .first_posting*/ .post .posting_options .thanks .rate_post.negative{\
color:#c30000;\
margin-right:5px;\
border:1px solid #c30000;\
line-height:10px\
}\
/*#thread #replys li .post .posting_options .thanks .rate_post.negative:hover,#thread #replys li .post .posting_options .thanks .rate_post.negative:visited,#thread .first_posting*/ .post .posting_options .thanks .rate_post.negative:hover,/*#thread .first_posting*/ .post .posting_options .thanks .rate_post.negative:visited{\
background-color:#c30000;\
color:#fff\
}\
/*#thread #replys li .post .posting_options .thanks .positive,#thread .first_posting*/ .post .posting_options .thanks .positive{\
color:#2b8f00;\
border:1px solid #2b8f00\
}\
/*#thread #replys li .post .posting_options .thanks .positive:hover,#thread #replys li .post .posting_options .thanks .positive:visited,#thread .first_posting*/ .post .posting_options .thanks .positive:hover,/*#thread .first_posting*/ .post .posting_options .thanks .positive:visited{\
background-color:#2b8f00;\
color:#fff\
}\
div[style] > h1.thread_title {\
font-size: 1.2em;\
margin-bottom: 10px;\
line-height: 22px;\
}\
";
addStyle(styleText);
}
function getPosterScoreBarCode(author) {
var absScore = getPosterScore(author);
if (absScore === undefined) return "";
// TODO: think about making it logarithmical
var score = absScore * posterScoreScaleFactor;
score += 5;
if(score >= 4 && score <= 6) return "";
score = Math.min(score, 10);
score = Math.max(score, 0);
score += 1;
score = Math.round(score);
return "&nbsp;&nbsp;<img src=\"/icons/forum/wertung_" + score + ".gif\" title=\"User-Score: " + absScore + "\"/>";
}
function getPosterScore(author) {
return getLocalValue("score_" + author, 0);
}
function scorePoster(author, score) {
var oldScore = getPosterScore(author);
setLocalValue("score_" + author, oldScore + score);
}
function countLevel(el)
{
if(isNewForum) {
return countLevelNew(el);
}
var lvl = 1;
// limit loop, just to be sure
for(var i=0; i < 10000; i++) {
var par = el.parentNode;
el = par;
if(par == null) break;
if(par.className == "thread_tree") break;
if(par.className == "nextlevel" ||
par.className == "nextlevel_line")
lvl++;
}
return lvl;
}
function countLevelNew(el)
{
var lvl = 1;
// limit loop, just to be sure
for(var i=0; i < 10000; i++) {
var par = el.parentNode;
el = par;
if(par == null) break;
if(!par.className) continue;
if(par.className == "tree_thread_list") break;
if(par.className.indexOf("posting_element") === 0)
lvl++;
}
return lvl;
}
function insertPostStart(url, nr)
{
if(isNewForum)
return url.replace(/(\/page-|_page=)\d+/, "$1" + nr);
else {
var eall = "";
if(document.location.href.match(/\/e-all/)) eall = "/e-all";
return url.replace(/(\/(list|foren)\/hs)-\d+/, "$1-" + nr + eall);
}
}
function extractPostStart(url)
{
if(isNewForum)
return extractPostStartNew(url);
var matches = url.match(/\/hs-(\d+)/);
if(!matches) return -1;
return parseInt(matches[1]);
}
function extractPostStartNew(url)
{
var matches = url.match(/\/page-(\d+)/);
if(!matches) {
matches = url.match(/\?forum_page=(\d+)/);
}
if(!matches) return -1;
return parseInt(matches[1]);
}
function joinOverviewPages()
{
if(isNewForum) {
joinOverviewPagesNew();
return;
}
showOverviewPosterScores(document);
var pageNrUls   = getElementsByClassName(document, "ul", "forum_navi");
var threadTrees = getElementsByClassName(document, "ul", "thread_tree");
if(threadTrees.length == 0)
threadTrees = getElementsByClassName(document, "ul", "fora_list");
if(pageNrUls.length   == 0 ||
threadTrees.length == 0) {
log("no forum_navi or thread_tree");
return;
}
var firstPageURL = "";
var lastPageURL  = "";
// find the first and last of the page URLs
var pageLinks = pageNrUls[0].getElementsByTagName("li");
for(var i = 0; i < pageLinks.length; i++) {
var pageLink = pageLinks[i];
if(pageLink.innerHTML.match(/>Neuere</)) break;
if(pageLink.innerHTML.match(/^\d+$/))    firstPageURL = "";
// find the URL
var url = grepTitleLinkURL(pageLink.innerHTML);
if(!url) continue;
if(firstPageURL == "")
firstPageURL = url;
lastPageURL = url;
}
if(firstPageURL == "" || lastPageURL == "") {
log("found no page URLs");
return;
}
// extract the post numbers
var firstPostNr = extractPostStart(firstPageURL);
var lastPostNr  = extractPostStart(lastPageURL);
if(firstPostNr == -1 || lastPostNr == -1) {
log("found no post numbers");
return;
}
// limit pages to users setting
var limited = false;
if(lastPostNr - firstPostNr > maxJoinedPosts) {
lastPostNr = firstPostNr + maxJoinedPosts;
limited = true;
}
// add list items and load the overview pages into them
var threadTree = threadTrees[0];
for(var j = firstPostNr; j <= lastPostNr; j += overviewPagePostCount) {
var url = ensureAbsoluteUrl(insertPostStart(lastPageURL, j));
var li = document.createElement('li');
li.innerHTML = "<b>Beitr&auml;ge ab Nr. " + j + "</b>";
threadTree.appendChild(li);
li = document.createElement('li');
li.innerHTML = "<i>Lade...</i>";
threadTree.appendChild(li);
requestHTML(url, callbackOverviewPage, j, li);
}
// add links to navigate
if(firstPostNr > overviewPagePostCount) {
var li = document.createElement('li');
li.innerHTML = "<a href=\"" + ensureAbsoluteUrl(insertPostStart(lastPageURL, firstPostNr - maxJoinedPosts - 3 * overviewPagePostCount)) + "\"><b>Vorwärts...</b></a>";
threadTree.insertBefore(li, threadTree.childNodes[0]);
}
if(limited) {
var li = document.createElement('li');
li.innerHTML = "<a href=\"" + ensureAbsoluteUrl(insertPostStart(lastPageURL, lastPostNr + overviewPagePostCount)) + "\"><b>Weiter...</b></a>";
threadTree.appendChild(li);
}
}
function joinOverviewPagesNew()
{
showOverviewPosterScores(document);
var pageNrElmFirst = xpath1(".//span[@class='seiten']/a[position()=1]",      document.getElementById("thread_sortierung"));
var pageNrElmLast  = xpath1(".//span[@class='seiten']/a[position()=last()]", document.getElementById("thread_sortierung"));
if(pageNrElmFirst == null)
pageNrElmFirst = xpath1(".//a[@class='page'][starts-with(@href, '?forum_page=')][position()=1]",      document.getElementById("forum_wrap"));
if(pageNrElmLast  == null)
pageNrElmLast  = xpath1(".//a[@class='page'][starts-with(@href, '?forum_page=')][position()=last()]", document.getElementById("forum_wrap"));
// only one page, nothing to join
if(pageNrElmFirst == null || pageNrElmLast == null)
return;
var pageNrElmCurr  = xpath1(".//span[@class='seiten']/em/text()", document.getElementById("thread_sortierung"));
if(pageNrElmCurr === null)
pageNrElmCurr  = xpath1("./span[@class='active']/text()", document.getElementById("paginierung"));
if(pageNrElmCurr === null) {
log("current page element not found joinOverviewPagesNew()");
return;
}
var threadTree = document.getElementById("tree_thread_list");
if(!threadTree)
var threadTree = xpath1(".//div[@class='inner_forum_list']/ul[@class='forum_list']", document.getElementById("forum_wrap"));
if(threadTree == null) {
log("no tree_thread_list or forum_list (joinOverviewPagesNew)");
return;
}
// the "first page" is actually the first with a link in the navigation bar
// so when you are on the first page, firstPage is 2 ("Seite 2")
var firstPageURL = "";
var lastPageURL  = "";
// find the first and last of the page URLs
firstPageURL = ensureAbsoluteUrl(pageNrElmFirst.getAttribute("href"));
lastPageURL  = ensureAbsoluteUrl(pageNrElmLast.getAttribute("href"));
if(firstPageURL == "" || lastPageURL == "") {
log("found no page URLs (joinOverviewPagesNew)");
return;
}
// extract the post numbers
var firstPageNr   = extractPostStart(firstPageURL);
var lastPageNr    = extractPostStart(lastPageURL);
var currentPageNr = parseInt(pageNrElmCurr.textContent);
if(firstPageNr == -1 || lastPageNr == -1) {
log("found no page numbers (joinOverviewPagesNew)");
return;
}
if(Number.isNaN(currentPageNr)) {
log("could not find current page number");
return;
}
// limit pages to users setting
var limited = false;
if(lastPageNr - currentPageNr > maxJoinedPosts / overviewPagePostCount) {
lastPageNr = currentPageNr + maxJoinedPosts / overviewPagePostCount;
limited = true;
}
// add list items and load the overview pages into them
for(var j = currentPageNr+1; j <= lastPageNr; j++) {
var url = ensureAbsoluteUrl(insertPostStart(lastPageURL, j));
var li = document.createElement('li');
li.innerHTML = "<b>Beitr&auml;ge ab Seite " + j + "</b>";
threadTree.appendChild(li);
li = document.createElement('li');
li.innerHTML = "<i>Lade...</i>";
threadTree.appendChild(li);
requestHTML(url, callbackOverviewPage, j, li);
}
// add links to navigate
if(currentPageNr > 1) {
var li = document.createElement('li');
li.innerHTML = "<a href=\"" + ensureAbsoluteUrl(insertPostStart(lastPageURL, currentPageNr > (maxJoinedPosts / overviewPagePostCount) ? currentPageNr - maxJoinedPosts / overviewPagePostCount - 1 : 1)) + "\"><b>Vorwärts...</b></a>";
threadTree.insertBefore(li, threadTree.childNodes[0]);
}
if(limited) {
var li = document.createElement('li');
li.innerHTML = "<a href=\"" + ensureAbsoluteUrl(insertPostStart(lastPageURL, lastPageNr + 1)) + "\"><b>Weiter...</b></a>";
threadTree.appendChild(li);
}
}
function callbackOverviewPage(txt, nr, startli, url)
{
if(isNewForum) {
callbackOverviewPageNew(txt, nr, startli, url);
return;
}
var matches = txt.match(/<ul class=\"(thread_tree|fora_list)\">([\s\S]*)<\/ul>[\s\S]*?<ul class="forum_navi">/i);
if(!matches) {
startli.innerHTML = "<b><i>Fehler beim Laden</i></b>";
return;
}
var lis = matches[2];
lis = lis.replace(/\/read(?!\/showthread-1)/g, "/read/showthread-1");
startli.innerHTML = "<ul style=\"padding-left: 0px; list-style-type: none\">" + lis + "</ul>";
showOverviewPosterScores(startli);
}
function callbackOverviewPageNew(txt, nr, startli, url)
{
var matches = txt.match(/<ol id="tree_thread_list">([\s\S]*)<\/ol>[\s\S]*?<!-- Seitenzahl [\d ]+ -->[\s\S]*?<div class="paginierung(?:_thread)?">/i);
if(!matches)
matches = txt.match(/<div class="inner_forum_list" id="inner_forum_list_\d+">\s*<ul class="forum_list">\s*(<li class="forum[\s\S]*)<\/ul>\s*<\/div>\s*<div id="paginierung">/i);
if(!matches) {
startli.innerHTML = "<b><i>Fehler beim Laden der Seite</i></b>";
return;
}
var lis = matches[1];
startli.innerHTML = "" + lis + "";
showOverviewPosterScores(startli);
}
function showOverviewPosterScores(root) {
var userdivs;
if(isNewForum)
userdivs = xpath(".//span[contains(@class, 'written_by_user')]", root);
else
userdivs = xpath(".//div[@class='thread_user']", root);
for(var i = 0; i < userdivs.snapshotLength; i++) {
var div = userdivs.snapshotItem(i);
div.innerHTML += getPosterScoreBarCode(div.innerHTML.trim());
}
}
function cleanUpReplyPage()
{
if(isNewForum) {
cleanUpReplyPageNew();
return;
}
if(enableAutoQuote && document.getElementsByName("message")[0].value == "") {
// select the right button the ultra hacky way
document.getElementsByName("quote")[0].click();
return;
}
var form = xpath1("//div[@class='forum_content' or @id='mitte_forum']");
var html = form.innerHTML;
// messy but working
html = html.replace(/(?:Unsere Foren|Dieses Forum)[\s\S]*<textarea/i, "<textarea");
html = html.replace(/<i>\([^)]+\)<\/i>/ig, "");
document.body.innerHTML = html;
}
function cleanUpReplyPageNew()
{
// do not crop the regular answer page (e.g. after click on "Zitat einfügen")
if(top === self && /\/save\/$/.test(document.location.href))
return;
if(enableAutoQuote && document.getElementById("msg_body").value == "") {
// select the right button the ultra hacky way
document.getElementsByName("insert_quote")[0].click();
return;
}
/*var noticep = xpath1(".//p[@class='forum__hinweis']", document.getElementById("composer"));
noticep.parentNode.removeChild(noticep);*/
var form = document.getElementById("reply_create_posting");
document.body.innerHTML = form.innerHTML;
}
function isWriteUrl(url)
{
if(isNewForum)
return url.match(/\/reply\/$/);
return url.match(/\/write\/$/);
}
function isNoTextEntry(titleText)
{
if(titleText.match(/[[/(](?:[kno](?:\.?w)?[./]?t\.?)[\])]\s*$/i)) return true;
return false;
}
function ensureShowThreadLinks()
{
//there is no equivalent for this in the new forum?
if(isNewForum) return;
var links = xpath("//a[contains(@href, '/read/') and not(contains(@href, '/read/showthread-1'))]");
// we need the tree enabled on all links
for (var i = 0; i < links.snapshotLength; i++) {
var link = links.snapshotItem(i);
link.href = link.href.replace(/\/read\/(?!showthread-1)/, "/read/showthread-1/");
}
}
function iReplyNew(frameId, replyUrl) {
var frm = document.getElementById(frameId);
frm.src = replyUrl;
frm.style.display = "";
}
function main()
{
if (!String.prototype.trim) {
String.prototype.trim = function() {
return this.replace(/^\s+/, '').replace(/\s+$/, '');
};
}
ensureShowThreadLinks();
if(enableIReply) {
if(!isNewForum)
defineScriptInPageContext(
'function iReply(frameId, replyUrl) {' +
'var frm = document.getElementById(frameId);' +
'frm.src = replyUrl;' +
'frm.style.display = ""' +
'}');
// is reply page?
if((isWriteUrl(document.location.href) || document.body.innerHTML.match(/<textarea name="message"/i) || document.body.innerHTML.match(/<textarea name="body" id="msg_body"/i)) &&
(top === undefined || !isWriteUrl(top.location.href))) {
cleanUpReplyPage();
return;
}
}
if(enableQuickVote && !isNewForum) {
defineScriptInPageContext(
'function sendVote(target, voteUrl) {' +
'var xmlHttp = new XMLHttpRequest();' +
'xmlHttp.open(\'GET\', voteUrl, true);' +
'xmlHttp.send(null);' +
'target.style.backgroundColor = "yellow";' +
'}');
}
// is board overview?
if(document.location.href.match(/\/forum-\d+\/list/) ||
document.location.href.match(/\/foren\/(?:hs-\d+\/)?$/) ||
document.location.href.match(/\/forum-\d+(?:\/comment)?\/$/) ||
document.location.href.match(/\/forum-\d+\/page-\d+\/$/) ||
document.location.href.match(/\/forum-\d+\/\?$/) ||
document.location.href.match(/\?forum_page=\d+$/)) {
if(showRatings === 1)
addStyle(".tree_thread_list--rating { display: inline !important; }"); // Rating in post list at the bottom of the thread, and in thread list);
if(joinSubLevelForen === 1)
joinOverviewPages();
return;
}
// else must be a thread view
// except the reply site has no thread tree but has comments embedded
if(!/\/reply\/$/.test(document.location.href))
joinThreadPosts();
}
main();
})();