Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Tweaks for various comics, such as visible alt and title texts, adding ctrl+left/right keyboard shortcuts for previous and next comic, prefetching prev/next link documents, and a kioskmode. Currently supported: xkcd and the (NSFW) comic "Oglaf".
// ==UserScript==
// @name Comics tweaks
// @author Filip H.F. "FiXato" Slagter
// @namespace http://code.fixato.org/userscripts
// @description Some tweaks for various comic sites, such as visible alt text, and kiosk mode. Currently supports: Oglaf, xkcd.
// @match https://oglaf.com/*
// @match https://www.oglaf.com/*
// @match https://xkcd.com/*
// @match https://www.xkcd.com/*
// @version 2.1
// @grant GM_setValue
// @grant GM_getValue
// @source https://gist.github.com/FiXato/b5434f66d2af50514bf84721fa249c22
// ==/UserScript==
/* TODO:
- Allow each site to be toggled individually, perhaps even per setting.
- Add a small settings panel? At least to toggle KioskMode on/off.
- Move this to an actual git repo / project.
*/
(function() {
'use strict';
/* Change the 'prev' meta-links (used by ctrl+left/right controls) to advance through a series before going to the following previous comic
Useful if you catch up on the comic by going backwards. */
const TraverseThroughSeriesWhileReversing = true;
// Enabled keyboard shortcuts to go to the previous and next comics with Ctrl + Left Arrow and Ctrl + Right Arrow respectively.
const PrevNextKeyControls = true;
// Make the comic image's alt and title attributes visible without having to hover over the image.
const DisplayImageDescription = true;
// Hide header images, as it helps show more of the actual image on screen
const HideHeaderImages = true;
// Try to prefetch cache the previous (and next) comics
const PrefetchPrevNextComics = true;
// Add a thumbnail next to the image descriptions?
const IncludeThumbnail = true;
// Cut down the contents of the page to the bare minimum, and load next and previous items via AJAX/XHR call. Assumes PrevNextKeyControls = true;
const KioskMode = true;
//If you want to reset states when you reload the page.
const ResetTraversal = false;
if (KioskMode && ResetTraversal) { reset_traversal(); }
var previous_href_before_series, next_href_before_series, current_series_base_href;
var page_title_element = document.querySelector('title');
var comic_image = get_comic_image(document);
var current_xhr_page_request = undefined;
var previous_link = get_link(document, 'previous');
var next_link = get_link(document, 'next');
check_series_traversal();
add_css_styles();
function abort(error_message, obj) {
console.error(error_message, obj);
throw new Error('Aborted: ' + error_message);
}
function get_link(doc, type) {
if (window.location.host.includes('xkcd.com')) {
if (type == 'previous') { return doc.querySelector('.comicNav a[rel*="prev"]'); }
else if (type == 'next') { return doc.querySelector('.comicNav a[rel*="next"]'); }
else { abort('Comic Tweaks.get_link(): Unsupported link type', type) }
}
else if (window.location.host.includes('oglaf.com')) {
if (type == 'previous') { return doc.querySelector('link[rel*="prev"]'); }
else if (type == 'next') { return doc.querySelector('link[rel*="next"]'); }
else { abort('Comic Tweaks.get_link(): Unsupported link type', type) }
} else { abort('Comic Tweaks.get_link(): Unsupported host', window.location.host); }
}
if (PrevNextKeyControls) {
window.addEventListener('keydown', (e) => {
if (e.code == "ArrowLeft" && e.ctrlKey) {
if (previous_link) {change_page(previous_link.href);}
} else if (e.code == "ArrowRight" && e.ctrlKey) {
if (next_link) {change_page(next_link.href);}
}
});
}
if (KioskMode) {
document.querySelector('html').classList.add('ComicsTweaksKioskMode');
build_kiosk();
//request_comic_via_xhr(window.location.href);
} else {
if (HideHeaderImages) {hide_header_images(document);}
}
if (DisplayImageDescription) {display_image_description();}
if (PrefetchPrevNextComics) {prefetch_prev_and_next_links();}
function current_page_href() {
if(KioskMode && current_xhr_page_request && current_xhr_page_request.responseURL) {
return current_xhr_page_request.responseURL;
}
return window.location.href;
}
function build_kiosk() {
const comics_tweaks_container = document.createElement('div');
comics_tweaks_container.id = 'comics_tweaks_container';
const comic_container = document.createElement('main');
comic_container.id = 'ctc_comic_container';
comic_container.prepend(comic_image);
comics_tweaks_container.prepend(comic_container);
const page_items_container = document.createElement('div');
page_items_container.id = 'ctc_page_items_container';
const source_document = document.body.cloneNode(true);
site_specific_kiosk_setup(comics_tweaks_container, page_items_container);
insertAfter(page_items_container, comic_container);
document.body.replaceChildren(comics_tweaks_container);
console.log('source doc', source_document);
}
function site_specific_kiosk_setup(comics_tweaks_container, page_items_container) {
let host = window.location.host;
if (host.includes('oglaf.com')) {
const random_image_quote = document.getElementById("ll");
random_image_quote.classList.add('random_image_quote');
const random_image = document.querySelector('#nav a:last-child img');
random_image.classList.add('random_image');
const random_image_container = document.createElement('div');
random_image_container.id = 'ctc_random_image_container';
random_image_container.append(random_image, random_image_quote);
page_items_container.append(random_image_container);
}
}
function request_comic_via_xhr(url) {
let request = new XMLHttpRequest();
request.open('GET', url);
request.responseType = "document";
request.send();
request.onload = function() {
process_xhr_comic(request);
}
request.onerror = function() {
process_xhr_comic_failure(request);
}
}
function process_xhr_comic(request) {
current_xhr_page_request = request;
let response = request.response.cloneNode(true);
previous_link.href = get_link(response, 'previous').href;
next_link.href = get_link(response, 'next').href
console.log("processed xhr. New links:", [previous_link.href, next_link.href]);
let new_image = get_comic_image(request.response);
comic_image.parentNode.replaceChild(new_image, comic_image);
comic_image = new_image;
window.history.replaceState({ additionalInformation: 'loaded via Comics Tweaks UserScript KioskMode' }, response.title, request.responseURL);
page_title_element.innerText = response.querySelector('title').innerText;
if (DisplayImageDescription) {display_image_description();}
check_series_traversal();
}
function process_xhr_comic_failure(request) {
console.error("process_xhr_comic_failure", request);
current_xhr_page_request = undefined;
}
function get_comic_image(doc) {
if (window.location.host.includes('xkcd.com')) {
return doc.querySelector('#comic img');
}
else if (window.location.host.includes('oglaf.com')) {
return doc.querySelector('#strip');
} else { abort('Comic Tweaks.get_comic_image(): Unsupported host', window.location.host); }
}
function change_page(url) {
console.log("change page", url);
if (!KioskMode) {
window.location.href = url;
} else {
request_comic_via_xhr(url);
}
}
function display_image_description() {
var alt_text = comic_image.alt;
var title_text = comic_image.title;
var thumbnail = '';
if(KioskMode && IncludeThumbnail) {
thumbnail = comic_image.cloneNode(true);
thumbnail.id = undefined;
thumbnail.classList.add('thumbnail');
}
const image_description = document.createElement('div');
image_description.classList.add("image_descriptions");
const page_title = document.createElement('h2');
page_title.classList.add('page_title');
page_title.textContent = page_title_element.innerText;
const image_title = document.createElement('b');
image_title.classList.add('image_title');
image_title.textContent = title_text;
const image_alt = document.createElement('i');
image_alt.classList.add('image_alt');
image_alt.textContent = alt_text;
let description_body = document.createElement('p');
description_body.classList.add('container');
description_body.classList.add('description_body');
description_body.prepend(image_title, image_alt);
image_description.prepend(page_title, thumbnail, description_body);
Array.from(document.querySelectorAll('.image_descriptions')).slice(2).slice(-3).forEach((el) => {
el.parentNode.removeChild(el);
});
document.querySelector('#ctc_page_items_container').prepend(image_description);
}
function hide_header_images(doc) {
doc.querySelectorAll('#tl, #tl + *, #tl + * + *').forEach((element) => {
element.style = 'display: none;';
});
}
function insertAfter(newNode, referenceNode) {
referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
}
function prefetch_prev_and_next_links() {
[previous_link, next_link].forEach((link) => {
if (link) {link.rel += " prerender";}
});
}
function load_series_variables() {
previous_href_before_series = GM_getValue('previous_href_before_series');
next_href_before_series = GM_getValue('next_href_before_series');
current_series_base_href = GM_getValue('current_series_base_href');
}
function reset_traversal() {
GM_setValue('previous_href_before_series', '');
GM_setValue('next_href_before_series', '');
GM_setValue('current_series_base_href', '');
}
function add_css_styles() {
let style_element = document.createElement('style');
style_element.innerText = `
html.ComicsTweaksKioskMode, html.ComicsTweaksKioskMode body {
width: 100%!important;
height: 100%!important;
margin: 0;
padding: 0;
position: relative;
left: 0;
top: 0;
right: 0;
bottom: 0;
}
img.thumbnail {
max-width: 150px;
max-height: 100px;
height: auto;
width: auto;
flex: 0 0 150px;
margin: 0.5em;
}
.image_descriptions ~ .image_descriptions {
opacity: 0.3;
}
.image_descriptions:nth-child(1) + .image_descriptions {
opacity: 0.5;
}
.image_descriptions {
display: flex;
flex-wrap: wrap;
align-items: center;
padding: 1em;
background-color: rgba(255,255,255, 0.5);
margin: 0;
padding: 0;
}
.page_title {
margin: 0;
margin-block-start: 0;
margin-block-end: 0;
padding: 0.5em;
font-size: 1.2em;
text-align: center;
font-weight: bold;
width: 100%;
flex-basis: 100%;
}
.image_title {
margin-bottom: 0.5em;
}
.container.description_body {
flex: 1;
display: flex;
flex-direction: column;
padding: 0.5em;
margin-block-start: 0;
margin-block-end: 0;
}
#nav a:last-child {
margin-top: 0!important;
}
#comics_tweaks_container {
display: flex;
align-items: flex-start;
}
#comics_tweaks_container #ctc_random_image_container {
display: flex;
background-color: #cccccc;
flex-wrap: wrap;
justify-content: space-between;
align-items: center;
margin: 0;
padding: 0;
}
#comics_tweaks_container .random_image_quote {
flex: 0 1;
width: initial;
height: min-content;
}
#comics_tweaks_container .random_image {
flex: 0 0 100px;
max-width: 81px;
width: auto;
height: auto;
}
html.ComicsTweaksKioskMode #ctc_comic_container img {
float: unset;
left: unset;
right: unset;
top: unset;
bottom: unset;
width: unset;
height: unset;
padding: 0;
margin: 0;
text-align: unset;
border: none;
}
`;
document.querySelector('head').prepend(style_element);
}
function check_series_traversal() {
if (TraverseThroughSeriesWhileReversing && window.location.host.includes('oglaf.com')) {
console.log('BEFORE', ['current_series_base_href', current_series_base_href], ['previous_link', previous_link.href], ['next_link', next_link.href], ['current_page_href', current_page_href()]);
load_series_variables();
if ((current_page_href() + '2/') == next_link.href) {
GM_setValue('previous_href_before_series', previous_link.href);
GM_setValue('next_href_before_series', next_link.href);
GM_setValue('current_series_base_href', current_page_href());
console.log('Start of series', current_page_href());
previous_link.href = next_link.href;
} else if (previous_link && previous_link.href.includes(current_series_base_href) && next_link && !next_link.href.includes(current_series_base_href) && current_page_href().includes(current_series_base_href)) {
console.log('End of series', current_series_base_href);
previous_link.href = previous_href_before_series;
console.log('Set prev link to ', previous_link.href, previous_link);
GM_setValue('previous_href_before_series', '');
GM_setValue('next_href_before_series', '');
GM_setValue('current_series_base_href', '');
} else if (current_series_base_href && current_page_href().includes(current_series_base_href) && next_link && next_link.href.includes(current_series_base_href)) {
console.log("We are still in the " + current_series_base_href + " series", next_link.href, next_link);
previous_link.href = next_link.href;
}
load_series_variables();
console.log('AFTER', ['current_series_base_href', current_series_base_href], ['previous_link', previous_link.href], ['next_link', next_link.href], ['current_page_href', current_page_href()]);
}
}
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment