// ==UserScript==
// @name Thread Watcher
// @namespace com.kongregate.resterman
// @author resterman
// @version 1.0.1
// @include http://www.kongregate.com/community*
// @include http://www.kongregate.com/forums/*
// @description Watch threads for new posts.
// ==/UserScript==
function Thread(threadId, threadTitle, lastPostId, lastPostAuthor, forumId) {
this.threadId = threadId;
this.threadTitle = threadTitle;
this.lastPostId = lastPostId;
this.lastPostAuthor = lastPostAuthor;
this.forumId = forumId;
}
Thread.prototype = {
THREADS_KEY: "thread",
save: function () {
var threadsWatched = localStorage.getItem(this.THREADS_KEY);
if (threadsWatched === null)
threadsWatched = {};
else
threadsWatched = JSON.parse(threadsWatched);
threadsWatched[this.threadId] = {
threadId: this.threadId,
forumId: this.forumId,
lastPostId: this.lastPostId,
threadTitle: this.threadTitle,
lastPostAuthor: this.lastPostAuthor
};
localStorage.setItem(this.THREADS_KEY, JSON.stringify(threadsWatched));
},
watch: function () {
this.save();
},
unwatch: function () {
var threadsWatched = JSON.parse(localStorage.getItem(this.THREADS_KEY));
if (threadsWatched === null || !this.isWatched())
return;
delete threadsWatched[this.threadId];
localStorage.setItem(this.THREADS_KEY, JSON.stringify(threadsWatched));
},
wasUpdated: function () {
if (!this.isWatched())
return false;
var storedThread = Thread.get(this.threadId);
return storedThread ? storedThread.lastPostId < this.lastPostId : false;
},
isOlder: function (aThread) {
return this.lastPostId < aThread.lastPostId;
},
isWatched: function () {
return !!Thread.get(this.threadId);
},
getUrl: function () {
return '/forums/'+ this.forumId +'/topics/'+ this.threadId;
},
createWatchButton: function () {
var link = new Element('a', {href: 'javascript:void(0);'})
.update(this.isWatched() ? Thread.UNWATCH : Thread.WATCH);
link.setAttribute('class', this.isWatched() ? 'unwatch-btn' : 'watch-btn');
var self = this;
link.observe('click', function (e) {
e.stop();
if (self.isWatched()) {
self.unwatch();
link.update(Thread.WATCH)
.setAttribute('class', 'watch-btn');
} else {
self.watch();
link.update(Thread.UNWATCH)
.setAttribute('class', 'unwatch-btn');
}
});
return link;
}
};
Thread.WATCH = 'watch';
Thread.UNWATCH = 'unwatch';
Thread.getPostIdFromUrl = function (url) {
var matches = url.match(/posts-([0-9]+)-row/);
return matches !== null ? parseInt(matches[1]) : null;
};
Thread.getThreadIdFromUrl = function (url) {
var matches = url.match(/topics\/([0-9]+)-/);
return matches !== null ? parseInt(matches[1]) : null;
};
Thread.getForumIdFromUrl = function (url) {
var matches = url.match(/forums\/([0-9]+)-/);
return matches !== null ? parseInt(matches[1]) : null;
};
Thread.create = function (threadId, threadTitle, lastPostId, lastPostAuthor, forumId) {
return new Thread(threadId, threadTitle, lastPostId, lastPostAuthor, forumId);
};
Thread.createFromUrl = function (url) {
return Thread.create(
Thread.getThreadIdFromUrl(url),
null,
Thread.getPostIdFromUrl(url),
null,
Thread.getForumIdFromUrl(url)
);
};
Thread.get = function (threadId) {
var threadsWatched = Thread.getAllWatched();
if (threadsWatched === null)
return null;
return threadsWatched[threadId];
};
Thread.getAllWatched = function () {
var threadsWatched = localStorage.getItem(Thread.prototype.THREADS_KEY);
if (threadsWatched === null)
return null;
threadsWatched = JSON.parse(threadsWatched);
for (var i in threadsWatched) {
var obj = threadsWatched[i];
threadsWatched[i] = Thread.create(
obj.threadId,
obj.threadTitle,
obj.lastPostId,
obj.lastPostAuthor,
obj.forumId
);
}
return threadsWatched;
};
/* url: http://www.kongregate.com/forums/ */
function threads(){
var css = document.createElement('style');
css.innerHTML = 'td.lp span a.unwatch-btn { color: #336699; } td.lp span a.unwatch-btn { color: #900; }';
document.head.appendChild(css);
var threads = $$('.hentry');
threads.each(function (thread) {
var links = thread.select('a');
var url = links[links.length - 1].href;
var t = Thread.createFromUrl(url);
t.threadTitle = thread.select('.entry-title')[0].innerText;
t.lastPostAuthor = thread.select('.author')[0].firstChild.innerText;
var actionLink = t.createWatchButton();
actionLink.setStyle({
'margin-left': '2px'
});
thread.select('.lp')[0]
.insert(new Element('span').insert(actionLink));
if (t.isWatched() && t.wasUpdated()) {
thread.select('.icon')[0].setStyle({
transition: 'all ease 0.5s',
backgroundColor: 'deepskyblue'
});
}
});
}
// url: http://www.kongregate.com/forums/*/topics/*
function thread() {
var id = Thread.getThreadIdFromUrl(location.href),
thread = Thread.get(id);
var titleClone = $$('.forum_header h1')[0].clone(true);
var threadTools = titleClone.select('#topic_mod')[0];
if (threadTools)
threadTools.remove();
var threadTitle = titleClone.innerText.match(/(.*?)(\s+page\s+[0-9]+|$)/m)[1];
if (!thread) {
thread = Thread.createFromUrl(location.href);
// Avoid fetching real last id, setting to negative id
thread.lastPostId = -1;
thread.lastPostAuthor = null; // Doesn't matter
thread.threadTitle = threadTitle;
}
if (thread.isWatched() && thread.threadTitle !== threadTitle) {
thread.threadTitle = threadTitle;
thread.save();
}
var lastPost = $$('.post:last')[0];
if (!lastPost)
return;
var lastId = lastPost.getAttribute('id').match(/posts-([0-9]+)-row/)[1];
if (thread.isWatched() && lastId > thread.lastPostId) {
thread.lastPostId = lastId;
thread.save();
}
var watchButton = thread.createWatchButton().setStyle({ marginLeft: '10px' });
$$('.media.mbs').each(function (i) {
i.select('.utility').each(function (j) {
j.insert({
after: watchButton
});
});
});
}
/* url: http://www.kongregate.com/community/ */
function community() {
var containerTitle = new Element('h3', {
id: 'watched_threads_title',
class: 'forum_group_title h2 mtl'
}).update('Watched Threads');
var threadsTable = new Element('table');
$('forums_title').parentNode.insert({ bottom: containerTitle });
$('forums_title').parentNode.insert({ bottom: threadsTable });
var threadsTableBody = new Element('tbody');
threadsTable.insert(threadsTableBody);
var onUnwatchClick = function (thread, row) {
return function(e) {
e.stop();
thread.unwatch();
row.remove();
};
};
var threads = Thread.getAllWatched();
for (var i in threads) {
var t = threads[i];
var row = new Element('tr');
threadsTableBody.insert(row);
var titleContainer = new Element('td', {
class: 'c2 pts'
});
row.insert(titleContainer);
var title = new Element('a', {
class: 'title h3',
href: t.getUrl()
}).update(t.threadTitle);
titleContainer.insert(title);
var unwatchButton = new Element('a', {
href: 'javascript:void(0);'
}).update('unwatch')
.setStyle({
'float': 'right'
});
unwatchButton.observe('click', onUnwatchClick(t, row));
titleContainer.insert(unwatchButton);
}
}
(function() {
'use strict';
if (/\.com\/forums\/.*\/topics/.test(location.href))
thread();
else if (/\.com\/forums/.test(location.href))
threads();
else if (/\.com\/community/.test(location.href))
community();
})();