// ==UserScript==
// @name NeoGAF Live Thread (modified by sharkiller)
// @namespace http://www.neogaf.com
// @version 2.1
// @description Automatically update a thread with new posts
// @include http://www.neogaf.com/forum/showthread*
// @include http://www.neogaf.net/forum/showthread*
// @grant unsafeWindow
// ==/UserScript==
(function () {
"use strict";
var css = document.createElement("style");
css.textContent = '@keyframes spin{from{transform:rotate(0)}to{transform:rotate(360deg)}}@-webkit-keyframes spin{from{-webkit-transform:rotate(0)}to{-webkit-transform:rotate(360deg)}}.live-toggle{background:transparent url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2aWV3Qm94PSIwIDAgNTYgMjgiIHByZXNlcnZlQXNwZWN0UmF0aW89Im5vbmUiPjxkZWZzPjxwYXRoIGlkPSJpY29uIiBkPSJNMTAuOTkyLDguNzkxYzEuOTc0LTEuMTQsNC4zMjYtMS4wMzIsNi4xNDQsMC4wNzdsLTAuNjEyLDIuMjg3IGwxLjA0Mi0wLjYwMmwwLjc0NC0wLjQyOWwwLjE5My0wLjExMmwyLjEwNS0xLjIxNXYwbDAuMDgzLTAuMDQ4bC0yLjQwNi00LjE2N2wtMC41MDgsMS44OTYgYy0yLjQ0Ny0xLjIyNi01LjQ0MS0xLjI0MS03Ljk4NywwLjIyOWMtMy42NzIsMi4xMi01LjEzMyw2LjU4OC0zLjYsMTAuNDE0bDIuMTA1LTEuMjE2QzcuNDA1LDEzLjI0OCw4LjQ3MywxMC4yNDYsMTAuOTkyLDguNzkxeiBNMjEuODExLDEwLjg4bC0yLjEwNSwxLjIxNWMwLjg5LDIuNjU3LTAuMTc5LDUuNjU5LTIuNjk4LDcuMTE0Yy0xLjk3NCwxLjE0MS00LjMyNiwxLjAzMy02LjE0NC0wLjA3NmwwLjYxMi0yLjI4N2wtMS4wNDIsMC42MDIgIGgwTDcuMzEsMTkuMjUybDIuNDA2LDQuMTY4bDAuNTA4LTEuODk3YzIuNDQ3LDEuMjI2LDUuNDQxLDEuMjQxLDcuOTg3LTAuMjI5QzIxLjg4MywxOS4xNzMsMjMuMzQ0LDE0LjcwNiwyMS44MTEsMTAuODh6Ii8+PC9kZWZzPjx1c2UgeGxpbms6aHJlZj0iI2ljb24iIGZpbGw9IiM5OTkiLz48dXNlIHhsaW5rOmhyZWY9IiNpY29uIiBmaWxsPSIjZjkwIiB4PSIyOCIvPjwvc3ZnPg==") 0 0 / 56px 28px no-repeat;border:0;display:inline-block;float:right;font-size:0;height:28px;margin:0 0 0 8px;outline:0;padding:0;width:28px}.live-toggle.on{animation:spin 1s linear infinite;-webkit-animation:spin 1s linear infinite;background-position:-28px 0}.live-spacer{background:#01518e;text-align:center;overflow:auto;padding:4px}.live-spacer-page{color:#fff}.live-spacer-close{background:transparent url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyMCAyMCIgcHJlc2VydmVBc3BlY3RSYXRpbz0ibm9uZSI+PHBvbHlnb24gZmlsbD0iI2ZmZiIgcG9pbnRzPSIxNiw2LjQwMSAxMy41OTgsNCAxMCw3LjU5OSA2LjQwMiw0IDQsNi40MDEgNy41OTgsMTAgNCwxMy41OTkgNi40MDIsMTYuMDAxIDEwLDEyLjQwMSAxMy41OTgsMTYuMDAxIDE2LDEzLjU5OSAxMi40LDEwICIvPjwvc3ZnPg==") 0 0 / 20px 20px no-repeat;display:block;float:right;font-size:0;height:20px;opacity:.4;width:20px}.live-spacer-close:focus,.live-spacer-close:hover{opacity:1}.live-menu[hidden]{display:none !important}.live-menu,.live-info:hover .live-options{background:#eee;border-radius:3px}.live-menu{margin:auto;text-align:center;padding:8px 0 4px 0;width:220px}.live-info{position:relative}.live-info:hover .live-options{display:block}.live-options{position:absolute;bottom:100%;width:100%;display:none;padding:8px 0}.live-option{background:#ddd;border:0;font-weight:bold}.live-option.on{background:#bbb}.live-message{padding:8px 0}.live-actions{padding:8px 0}.live-action{background:#1b639a;border:0;border-radius:3px;color:#eee;font-weight:bold;margin:0 2px;padding:8px;text-transform:capitalize;width:70px}.live-action:focus,.live-action:hover{background:#01518e}.live-action:active{background:#444}.live-dark .live-spacer{background:#414141}.live-dark .live-menu,.live-dark .live-info:hover .live-options{background:#282828}.live-dark .live-option{background:#373737;color:#e6e6e6}.live-dark .live-option.on{background:#474747;color:#e6e6e6}.live-dark .live-action{background:#505050;color:#e6e6e6}.live-dark .live-action:focus,.live-dark .live-action:hover{background:#5a5a5a}.live-dark .live-action:active{background:#414141}';
document.head.appendChild(css);
var OPTION_FAIL_LIMIT = Number(localStorage.LiveThread_FAIL_LIMIT) || 8,
OPTION_CONFIRM_EXIT = (localStorage.LiveThread_CONFIRM_EXIT === "true") ? true : false,
OPTION_CHECk_EDITS = (localStorage.LiveThread_CHECk_EDITS !== "false") ? true : false,
OPTION_USE_HISTORY = (localStorage.LiveThread_USE_HISTORY !== "false") ? true : false;
var TEXT_RESPONSE_ERROR = "Response error",
TEXT_PARSE_ERROR = "Parse error",
TEXT_NO_POSTS = "No new posts",
TEXT_POST_ADDED = " new post",
TEXT_POSTS_ADDED = " new posts",
TEXT_PENDING = "Pending",
TEXT_WAIT = "Wait",
TEXT_SPAM = "\u0CA0_\u0CA0",
TEXT_SECOND = " second",
TEXT_SECONDS = " seconds",
TEXT_MINUTE = " minute",
TEXT_MINUTES = " minutes",
TEXT_STOPPED = "Stopped",
TEXT_STARTING = "Starting",
TEXT_START = "Start",
TEXT_STOP = "Stop",
TEXT_UPDATE = "Update",
TEXT_CLOSE = "Close",
TEXT_SLOW = "Slow",
TEXT_NORMAL = "Normal",
TEXT_FAST = "Fast",
TEXT_PAGE = "Page ",
TEXT_CONFIRM_EXIT = "NeoGAF Live Thread is on",
TEXT_SPACER_CLOSE = "Delete posts above this";
var STATE_LIVE = false,
STATE_PENDING = false,
STATE_INTERVAL = 60,
STATE_FAIL_COUNT = 0,
STATE_URL = window.location.href,
STATE_NEXTPAGE = false,
STATE_TIMER = null,
STATE_DELAY = null;
var GAF_pagenavHead = document.querySelectorAll("a.large-button[href^=newreply]")[0].parentNode.nextElementSibling,
GAF_pagenavFoot = document.querySelectorAll("a.large-button[href^=newreply]")[1].parentNode.nextElementSibling,
GAF_postsContainer = document.querySelector("#posts"),
GAF_footerWrap = document.querySelector("#footer > .wrap"),
GAF_next = document.querySelector(".pagenav a[rel=next]");
var UI_toggleHead = document.createElement("button"),
UI_toggleFoot = document.createElement("button"),
UI_optionSlow = document.createElement("button"),
UI_optionNorm = document.createElement("button"),
UI_optionFast = document.createElement("button"),
UI_actionStart = document.createElement("button"),
UI_actionStop = document.createElement("button"),
UI_options = document.createElement("div"),
UI_message = document.createElement("div"),
UI_info = document.createElement("div"),
UI_actions = document.createElement("div"),
UI_menu = document.createElement("div");
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// UI Elements
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
UI_toggleHead.classList.add("live-toggle");
UI_toggleFoot.classList.add("live-toggle");
UI_optionSlow.classList.add("live-option");
UI_optionSlow.textContent = TEXT_SLOW;
UI_optionNorm.classList.add("live-option");
UI_optionNorm.classList.add("on");
UI_optionNorm.textContent = TEXT_NORMAL;
UI_optionFast.classList.add("live-option");
UI_optionFast.textContent = TEXT_FAST;
UI_options.classList.add("live-options");
UI_options.appendChild(UI_optionSlow);
UI_options.appendChild(UI_optionNorm);
UI_options.appendChild(UI_optionFast);
UI_message.classList.add("live-message");
UI_info.classList.add("live-info");
UI_info.appendChild(UI_options);
UI_info.appendChild(UI_message);
UI_actionStart.classList.add("live-action");
UI_actionStart.textContent = TEXT_START;
UI_actionStop.classList.add("live-action");
UI_actionStop.textContent = TEXT_STOP;
UI_actions.classList.add("live-actions");
UI_actions.appendChild(UI_actionStart);
UI_actions.appendChild(UI_actionStop);
UI_menu.setAttribute("hidden", "");
UI_menu.classList.add("live-menu");
UI_menu.appendChild(UI_info);
UI_menu.appendChild(UI_actions);
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Page changes
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
function checkReponse(response) {
if (!response || !response.body) {
STATE_FAIL_COUNT += 1;
UI_message.textContent = TEXT_RESPONSE_ERROR;
return;
}
function updatePage(posts, spacer) {
function fullImage(img) {
img.addEventListener("click", function() {
this.classList.toggle("full-image");
}, false);
}
function spoiler(el) {
el.addEventListener("click", function() {
this.classList.toggle("show");
}, false);
}
var fragment = document.createDocumentFragment();
if (spacer) {
fragment.appendChild(spacer);
}
posts.forEach(function(post) {
[].forEach.call(post.querySelectorAll("img"), fullImage);
[].forEach.call(post.querySelectorAll(".spoiler"), spoiler);
fragment.appendChild(post);
});
GAF_postsContainer.insertBefore(fragment, GAF_postsContainer.lastElementChild);
STATE_FAIL_COUNT = 0;
UI_message.textContent = posts.length + ((posts.length > 1) ? TEXT_POSTS_ADDED : TEXT_POST_ADDED);
unsafeWindow.mq_init(fetch_object("posts"));
try {
document.dispatchEvent(new CustomEvent("LiveThreadUpdate"));
} catch (ignore) {}
}
function createSpacer(pagenav, url) {
var title = pagenav.querySelector("li[class=current] span").getAttribute("title"),
number = pagenav.querySelector("li[class=current] strong").textContent,
spacer = document.createElement("div"),
page = document.createElement("a"),
close = document.createElement("a");
spacer.classList.add("live-spacer");
spacer.appendChild(page);
spacer.appendChild(close);
page.classList.add("live-spacer-page");
page.setAttribute("href", url);
page.setAttribute("title", title);
page.textContent = TEXT_PAGE + number;
close.classList.add("live-spacer-close");
close.setAttribute("title", TEXT_SPACER_CLOSE);
close.textContent = TEXT_SPACER_CLOSE;
close.addEventListener("click", function() {
[].some.call(document.querySelectorAll("#posts > *"), function(node) {
if (node !== spacer) {
node.parentNode.removeChild(node);
return false;
}
window.scrollTo(0, 0);
spacer.removeChild(close);
return true;
});
}, false);
return spacer;
}
var newposts, newpagenav, newspacer, curposts, curpagenav, nextpage;
try {
newposts = [].slice.call(response.querySelectorAll("#posts > div[id^=edit]"), 0);
newpagenav = response.querySelectorAll(".pagenav");
if (STATE_NEXTPAGE) {
if (newpagenav.length) {
newspacer = createSpacer(newpagenav[0], STATE_URL);
}
} else {
curposts = document.querySelectorAll("#posts > div[id^=edit]");
curposts = [].slice.call(curposts, Math.max(curposts.length - 100, 0));
newposts = newposts.filter(function (a) {
var isnew = true;
curposts.some(function (b, i) {
if (a.id === b.id) {
if (OPTION_CHECk_EDITS
&& !a.querySelector("form") // quick edit open
&& !a.querySelector(".post").isEqualNode(b.querySelector(".post"))) {
try {
GAF_postsContainer.replaceChild(a, b);
} catch (ignore) {}
}
curposts.splice(curposts[i], 1);
isnew = false;
return true;
}
return false;
});
return isnew;
});
}
if (newpagenav.length) {
curpagenav = document.querySelectorAll(".pagenav");
if (curpagenav.length) {
if (STATE_NEXTPAGE) {
GAF_pagenavHead.replaceChild(newpagenav[0], curpagenav[0]);
GAF_pagenavFoot.replaceChild(newpagenav[1], curpagenav[1]);
}
} else {
GAF_pagenavHead.appendChild(newpagenav[0]);
GAF_pagenavFoot.appendChild(newpagenav[1]);
}
nextpage = newpagenav[0].querySelector("a[rel=next]");
if (nextpage) {
STATE_NEXTPAGE = true;
STATE_URL = nextpage.href;
} else {
STATE_NEXTPAGE = false;
}
}
if (!newposts.length) {
STATE_FAIL_COUNT += 1;
UI_message.textContent = TEXT_NO_POSTS;
} else {
updatePage(newposts, newspacer);
}
} catch (e) {
STATE_FAIL_COUNT += 1;
UI_message.textContent = TEXT_PARSE_ERROR;
return;
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Upate
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
function confirmExit(e) {
(e || window.event).returnValue = TEXT_CONFIRM_EXIT;
return TEXT_CONFIRM_EXIT;
}
function updateComplete() {
setTimeout(function() {
STATE_PENDING = false;
UI_message.textContent = TEXT_STOPPED;
if (STATE_LIVE) {
updateStart();
}
}, 5000);
}
function updateRequest() {
clearInterval(STATE_TIMER);
STATE_TIMER = null;
STATE_PENDING = true;
UI_actionStart.textContent = TEXT_PENDING;
UI_message.textContent = TEXT_WAIT;
try {
var r = new XMLHttpRequest();
r.open("GET", STATE_URL, true);
r.responseType = "document";
r.onload = function() {
checkReponse(r.response);
updateComplete();
};
r.onerror = function() {
STATE_FAIL_COUNT += 1;
updateComplete();
};
r.send(null);
} catch (e) {
STATE_FAIL_COUNT += 1;
updateComplete();
}
}
function updateStop() {
if (!STATE_LIVE) {
STATE_PENDING = false;
UI_menu.setAttribute("hidden", "");
return;
}
clearInterval(STATE_TIMER);
STATE_TIMER = null;
STATE_LIVE = false;
STATE_FAIL_COUNT = 0;
UI_toggleHead.classList.remove("on");
UI_toggleFoot.classList.remove("on");
UI_actionStart.textContent = TEXT_START;
UI_actionStop.textContent = TEXT_CLOSE;
UI_message.textContent = TEXT_STOPPED;
window.removeEventListener("beforeunload", confirmExit, false);
}
function updateTick() {
function time(n) {
var t = "",
s = Math.floor(n % 60),
m = Math.floor((n / 60) % 60);
if (m > 1) {
t = m + TEXT_MINUTES + " ";
} else {
if (m === 1) {
t = m + TEXT_MINUTE + " ";
}
}
if (s > 1) {
t += s + TEXT_SECONDS;
} else {
if (s === 1) {
t += s + TEXT_SECOND;
}
}
return t;
}
STATE_DELAY -= 1;
UI_message.textContent = time(STATE_DELAY);
if (STATE_DELAY <= 0) {
return updateRequest();
}
}
function updateStart() {
if (STATE_PENDING) {
STATE_LIVE = true;
UI_message.textContent = TEXT_SPAM;
return;
}
if (STATE_FAIL_COUNT > OPTION_FAIL_LIMIT) {
return updateStop();
}
if (STATE_TIMER) {
STATE_FAIL_COUNT = 0;
return updateRequest();
}
STATE_LIVE = true;
STATE_DELAY = STATE_INTERVAL * STATE_FAIL_COUNT || STATE_INTERVAL;
STATE_TIMER = setInterval(updateTick, 1000);
UI_toggleHead.classList.add("on");
UI_toggleFoot.classList.add("on");
UI_actionStart.textContent = TEXT_UPDATE;
UI_actionStop.textContent = TEXT_STOP;
UI_message.textContent = TEXT_STARTING;
UI_menu.removeAttribute("hidden");
if (OPTION_CONFIRM_EXIT) {
window.addEventListener("beforeunload", confirmExit, false);
}
}
function updateToggle() {
if (STATE_LIVE) {
updateStop();
} else {
updateStart();
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Actions
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
UI_toggleHead.addEventListener("click", updateToggle, false);
UI_toggleFoot.addEventListener("click", updateToggle, false);
UI_actionStart.addEventListener("click", updateStart, false);
UI_actionStop.addEventListener("click", updateStop, false);
UI_optionSlow.addEventListener("click", function () {
STATE_INTERVAL = 120;
UI_optionSlow.classList.add("on");
UI_optionNorm.classList.remove("on");
UI_optionFast.classList.remove("on");
}, false);
UI_optionNorm.addEventListener("click", function () {
STATE_INTERVAL = 60;
UI_optionSlow.classList.remove("on");
UI_optionNorm.classList.add("on");
UI_optionFast.classList.remove("on");
}, false);
UI_optionFast.addEventListener("click", function () {
STATE_INTERVAL = 30;
UI_optionSlow.classList.remove("on");
UI_optionNorm.classList.remove("on");
UI_optionFast.classList.add("on");
}, false);
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// init
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
if (window.getComputedStyle(document.body, null).getPropertyValue("background-color") === "rgb(30, 30, 30)") {
document.body.classList.add("live-dark");
}
GAF_pagenavHead.parentNode.insertBefore(UI_toggleHead, GAF_pagenavHead.parentNode.firstElementChild);
GAF_pagenavFoot.parentNode.insertBefore(UI_toggleFoot, GAF_pagenavFoot.parentNode.firstElementChild);
GAF_footerWrap.insertBefore(UI_menu, GAF_footerWrap.firstElementChild);
if (GAF_next) {
STATE_NEXTPAGE = true;
STATE_URL = GAF_next.href;
}
}());