// ==UserScript==
// @name xkcd Tweaks
// @description Some tweaks to xkcd.com and what-if.xkcd.com
// @include /^(https?:)?(\/\/)?(www\.)?(what-?if\.)?xkcd\.com/
// @author Mital Ashok
// @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAABX0lEQVRYhcWVMZIEIQhFOYrhHM8baGjoEbyRYYcehY2cchXwt7O9Q1VXzQDKAxSJiPjLH/G3xARwzi20zrnnAaTAT4GIAGjvJAkhLH4hBBxgzl5ZIFbBqpxWsQXAyk4CQYJbEH8GcNo2EQAVC2CU1to9gE8rIJW6t2gLoDlKC8dAyPWEAOZslAWLDYWGAE5ONBFxa00N3s8BBGCBbO6z2f/bAPPmiM/H11AjtzL03sMA3nsM4M5YfQRgLJn2WxK09CZAz75nOm4y206rpgJIAeYsJJ9T6AVAGzKW7hR6AZDKNZZV0xsb/9IZoPIB211DLbgGPdpEgFlmoN2JZmautS7QtVbVfzsHXq/XorNevJQSExGXUriUwkTEKaX7AJ3+ui5Rv9mQY4wcYzybA1YgFGD+r7UBGkSorWecc37rcs5mG0SAu1nO+nHej+/EvwGgehFgvD47AHQdYMOe0qe+Hzm8hknb8QYpAAAAAElFTkSuQmCC
// @namespace https://greasyfork.org/en/users/59570-mital-ashok
// @license http://creativecommons.org/licenses/by-sa/4.0/
// @version 1.0.1
// @grant none
// @noframes
// ==/UserScript==
(function() {
var border = true,
hr = true,
titleText = true,
snap = true,
explainButton = true,
transcript = true;
var location = window.location.href.toLowerCase().replace(/^(?:https?:)?(?:\/\/)?(?:www\.)?/, 'https://')
.replace(/^(?:https:\/\/)whatif/, 'https://what-if')
.replace(/(?:^https(?=:\/\/xkcd\.com\/1663(\/|$)))/, 'http');
// Comic 1663 (Garden) does not work over https.
if (location !== window.location.href) {
window.location.replace(location);
return;
}
function repeat(interval, func, onCompletion) {
if (typeof onCompletion === 'undefined') {
onCompletion = function() {};
}
if (func()) {
onCompletion();
return;
}
var intervalId = setInterval(function() {if (func()) { clearInterval(intervalId); onCompletion(); }}, interval);
}
function disable(nodeList) {
return function disable() {
Array.prototype.forEach.call(nodeList, function(currentValue) {
currentValue.style.visibility = 'hidden';
currentValue.removeAttribute('href');
currentValue.style.cursor = 'default';
currentValue.parentElement.style.MozUserSelect = 'none';
currentValue.parentElement.style.webkitUserSelect = 'none';
currentValue.parentElement.style.userSelect = 'none';
});
return Array.prototype.every.call(nodeList, function(currentValue) {
return currentValue.style.visibility === 'hidden' &&
!currentValue.hasAttribute('href') &&
currentValue.style.cursor === 'default';
});
};
}
function activeIsEditable() {
var node = document.activeElement;
var name = node.nodeName.toLowerCase();
return node.isContentEditable || node.nodeType == 1 && (name == "textarea" ||
name == "input" && /^(?:text|email|number|search|tel|url|password|)$/i.test(node.type));
}
var isWhatIf = /^(https?:)?(\/\/)?(www\.)?what-if/i.test(window.location.href),
blackList = {
'xkcd': {
1193: ['titleText'],
1506: ['p', 'n', 'r', 'titleText'],
1525: ['p', 'n', 'r', 'titleText'],
1608: ['left', 'right', 'titleText']
}, 'whatIf': {
}
},
number, first, last, xhttp,
left = 37, right = 39, n = 78, p = 80, r = 82;
if (isWhatIf) {
last = false;
var setRandom = function(create) {
if (!receivedResponse.received) {
if (receivedResponse.onreceive !== false) {
receivedResponse.onreceive = !!create;
}
return;
}
if (typeof random === 'undefined') {
random = Math.floor(Math.random() * (lastNumber - 1)) + 1;
}
if (random >= number) {
random++;
}
if (create) {
var addRandom = function(ulTag) {
var liTag = document.createElement('li'),
aTag = document.createElement('a');
aTag.href = '/' + random + '/';
aTag.appendChild(document.createTextNode('Random'));
liTag.appendChild(aTag);
if (last) {
ulTag.insertBefore(liTag, ulTag.lastChild);
} else {
ulTag.insertBefore(liTag, ulTag.lastChild.previousSibling);
}
if (first) {
liTag.style.marginLeft = '8.57552em';
}
};
navs = document.getElementsByClassName('main-nav');
addRandom(navs[0].firstElementChild);
addRandom(navs[1].firstElementChild);
} else {
window.location.pathname = '/' + random + '/';
}
},
receivedResponse = {'received': false, 'onreceive': null},
lastNumber, navs, addButtons, localLatest, time, expired, random;
localLatest = localStorage.latestComic;
time = new Date().getTime();
if (typeof localLatest === 'undefined') {
expired = true;
} else {
localLatest = localLatest.split(',');
expired = localLatest[1] <= time;
}
if (expired) {
xhttp = new XMLHttpRequest();
xhttp.open('get', '/', true);
xhttp.onreadystatechange = function() {
if (xhttp.readyState == XMLHttpRequest.DONE) {
lastNumber = +xhttp.response.match(/<a href="\/\/what-if.xkcd.com\/(\d+)\/"><h1>/)[1];
receivedResponse.received = true;
localStorage.latestComic = lastNumber + ',' + (time + 6.048e+8);
if (receivedResponse.onreceive !== null) {
setRandom(receivedResponse.onreceive);
}
}
};
xhttp.send();
} else {
lastNumber = localLatest[0];
receivedResponse.received = true;
}
document.addEventListener('keydown', function(event) {
var keyCode = event.keyCode;
if ((keyCode === right || keyCode === n) && !last) {
Array.prototype.forEach.call(document.getElementsByTagName('a'), function(currentValue) {
if (currentValue.firstChild !== null) {
if (currentValue.firstChild.textContent === 'Next') {
currentValue.click();
}
}
});
} else if ((keyCode === left || keyCode === p) && !first) {
Array.prototype.forEach.call(document.getElementsByTagName('a'), function(currentValue) {
if (currentValue.firstChild !== null) {
if (currentValue.firstChild.textContent === 'Prev') {
currentValue.click();
}
}
});
} else if (keyCode === r) {
setRandom();
}
});
var headATag = document.getElementsByTagName('h1')[0].parentElement;
number = +headATag.getAttribute('href').slice(19, -1);
first = true;
last = true;
Array.prototype.forEach.call(document.getElementsByTagName('a'), function(currentValue) {
if (currentValue.firstChild) {
if (currentValue.firstChild.textContent === 'Prev') {
first = false;
}
if (currentValue.firstChild.textContent === 'Next') {
last = false;
}
}
});
if (/^(https?:)?(\/\/)?(www\.)?what-if.xkcd.com\/\d+/i.test(window.location.href)) {
repeat(100, function() {
headATag.removeAttribute('href');
headATag.style.textDecoration = 'underline';
return !headATag.hasAttribute('href') && headATag.style.textDecoration === 'underline';
});
}
setRandom(true);
addButtons = function(navTag) {
var ulTag = navTag.firstElementChild,
aTag;
navTag.style.width = '23.5em';
if (!first) {
var firstTag = document.createElement('li');
aTag = document.createElement('a');
aTag.appendChild(document.createTextNode('First'));
aTag.href = '/1/';
firstTag.appendChild(aTag);
firstTag.className = 'nav-prev';
ulTag.insertBefore(firstTag, ulTag.firstChild);
}
if (!last) {
var lastTag = document.createElement('li');
aTag = document.createElement('a');
aTag.appendChild(document.createTextNode('Last'));
aTag.href = '/';
lastTag.appendChild(aTag);
lastTag.className = 'nav-next';
ulTag.insertBefore(lastTag, ulTag.lastElementChild);
}
Array.prototype.forEach.call(ulTag.children, function(currentValue) {
if (currentValue.firstChild.firstChild.textContent !== 'Last') {
currentValue.style.marginRight = '0.5em';
}
});
};
navs = document.getElementsByClassName('main-nav');
addButtons(navs[0]);
addButtons(navs[1]);
Array.prototype.forEach.call(document.getElementsByTagName('img'), function(currentValue) {
if (currentValue.hasAttribute('title')) {
if (border) {
currentValue.style.border = '2px solid rgba(127, 127, 127, 0.22)';
}
if (titleText) {
currentValue.style.marginTop = currentValue.style.paddingTop;
currentValue.style.marginBottom = 0;
var oldPadding = currentValue.style.paddingBottom;
currentValue.style.paddingTop = 0;
currentValue.style.paddingBottom = 0;
var title = document.createElement('p');
title.appendChild(document.createTextNode(currentValue.title));
title.style.textAlign = 'center';
title.style.paddingLeft = '5em';
title.style.paddingRight = '5em';
title.style.paddingTop = 0;
title.style.paddingBottom = oldPadding;
title.style.fontSize = '85%';
currentValue.parentElement.insertBefore(title, currentValue.nextSibling);
currentValue.removeAttribute('title');
}
}
});
return;
}
var comic = document.getElementById('comic'),
title = document.createElement('p'),
previousButtons, nextButtons, intervalId, top, nav, link, oldNav;
if (snap) {
top = document.getElementById('middleContainer').offsetTop + 6;
window.scrollTo(0, top);
window.addEventListener('scroll', function() {
var scrollPosition = document.documentElement.scrollTop || document.body.scrollTop;
if (scrollPosition > top - 145 && top + 10 > scrollPosition) {
if (intervalId === false && scrollPosition !== top) {
intervalId = setInterval(function() {
if (scrollPosition > top - 145 && top + 10 > scrollPosition) {
window.scrollTo(0, top);
}
clearInterval(intervalId);
intervalId = false;
}, 500);
}
} else {
clearInterval(intervalId);
intervalId = false;
}
});
}
first = last = false;
repeat(100, function() {
nextButtons = Array.prototype.slice.call(document.querySelectorAll('[accesskey="n"], [href="/"]'), 1);
previousButtons = document.querySelectorAll('[accesskey="p"], [href="/1/"]');
return nextButtons.length === 4 && previousButtons.length === 4;
}, function() {
if (previousButtons[1].getAttribute('href') === '#') {
number = 1;
first = true;
repeat(100, disable(previousButtons));
} else {
number = +previousButtons[1].getAttribute('href').slice(1, -1) + 1;
if (nextButtons[0].getAttribute('href') === '#') {
last = true;
repeat(100, disable(nextButtons));
}
}
});
if (number in blackList.xkcd) {
blackList.xkcd[number].forEach(function(currentValue) {
switch (currentValue) {
case 'right':
right = null;
break;
case 'n':
n = null;
break;
case 'left':
left = null;
break;
case 'p':
p = null;
break;
case 'r':
r = null;
break;
case 'titleText':
titleText = false;
}
});
}
repeat(1000, function() {
if (comic.firstElementChild.tagName.toLowerCase() === 'script') {
return false;
}
try {
if (hr) {
comic.parentElement.insertBefore(document.createElement('hr'), comic.nextSibling);
comic.parentElement.insertBefore(document.createElement('hr'), comic);
}
if (titleText) {
title.appendChild(document.createTextNode(comic.firstElementChild.title));
title.style.fontVariant = 'normal';
title.style.paddingLeft = '80px';
title.style.paddingRight = '80px';
title.style.fontSize = '20px';
comic.parentElement.insertBefore(title, comic.nextSibling);
comic.firstElementChild.removeAttribute('title');
}
return true;
} catch (e) {
return true;
}
});
if (explainButton) {
link = document.createElement('a');
link.href = 'http://www.explainxkcd.com/' + number + '/';
link.style.fontFamily = 'xkcd-Regular';
link.style.fontVariant = 'normal';
link.style.marginLeft = '5px';
link.style.display = 'inline-block';
link.style.fontSize = '20px';
link.style.padding = '0px 5px 5px';
link.appendChild(document.createTextNode('Explain!'));
nav = document.createElement('ul');
nav.className = 'comicNav';
nav.style.marginBottom = 0;
nav.appendChild(document.createElement('li'));
nav.firstChild.appendChild(link);
oldNav = document.getElementsByClassName('comicNav')[1];
oldNav.parentElement.insertBefore(nav, oldNav.nextSibling);
}
document.addEventListener('keydown', function(event) {
if (activeIsEditable()) {
return;
}
var keyCode = event.keyCode;
if ((keyCode === right || keyCode === n) && !last) {
nextButtons[0].click();
} else if ((keyCode === left || keyCode === p) && !first) {
previousButtons[1].click();
} else if (keyCode === r) {
document.querySelector('[href="//c.xkcd.com/random/comic/"]').click();
}
});
if (transcript) {
transcript = document.getElementById('transcript');
if (transcript !== null && transcript.firstChild !== null && transcript.firstChild !== '') {
var node = document.createElement('p');
node.innerHTML = transcript.innerHTML.replace(/\n*{{(title text|alt):.+\n*$/i, '').replace(/\n/g, '<br style="display: block; margin: 3px 0;">');
node.style.fontVariant = 'normal';
node.style.paddingLeft = '80px';
node.style.paddingRight = '80px';
node.style.fontSize = '14px';
node.style.textAlign = 'left';
var ul = document.getElementsByTagName('ul');
ul = ul[ul.length - 1];
var parent = ul.parentElement;
ul = ul.nextSibling;
if (hr) {
parent.insertBefore(document.createElement('hr'), ul);
}
parent.insertBefore(node, ul);
if (hr) {
parent.insertBefore(document.createElement('hr'), ul);
}
}
}
})();