// ==UserScript==
// @name LightShot (prnt.sc) random screenshot
// @description Press R on prnt.sc website to load some random screenshot
//
// @name:ru Lightshot (prnt.sc) случайный скриншот
// @description:ru Нажми R на сайте prnt.sc чтобы загрузить какой-то случайный скриншот
//
// @author Konf
// @namespace https://greasyfork.org/users/424058
// @icon https://www.google.com/s2/favicons?domain=prnt.sc&sz=32
// @version 1.2.0
// @match https://prnt.sc/*
// @compatible Chrome
// @compatible Opera
// @compatible Firefox
// @require https://cdnjs.cloudflare.com/ajax/libs/arrive/2.4.1/arrive.min.js
// @run-at document-body
// @grant GM_addStyle
// @noframes
// ==/UserScript==
/* jshint esversion: 8 */
(function() {
'use strict';
const langStrings = {
en: {
getNewRandImg: 'Load new random screenshot',
hotkey: 'Hotkey',
scriptGotFailStreak:
'Oops! Script just have got a huge fail streak. ' +
'If your internet connection is fine, maybe the script ' +
'is broken. Please consider to notify the script author ' +
'about the problem. Also it would be great if you check ' +
'your browser console and save all the info messages ' +
'from there. They are contain the errors details',
},
ru: {
getNewRandImg: 'Загрузить новый случайный скриншот',
hotkey: 'Горячая клавиша',
scriptGotFailStreak:
'Упс! Скрипт много пытался, но так и не смог ' +
'сработать. Если у тебя всё в порядке с интернетом, ' +
'то возможно скрипт просто сломан. Пожалуйста, сообщи ' +
'о проблеме автору скрипта. А ещё было бы супер если ' +
'бы ты открыл(а) консоль браузера и сохранил(а) оттуда ' +
'все сообщения. Они содержат описания ошибок',
},
};
const userLang = navigator.language.slice(0, 2);
const i18n = langStrings[userLang || 'en'];
const css = [`
body {
overflow-y: scroll;
}
.parser-icon {
float: left;
width: 28px;
height: 28px;
margin: 11px 25px 0 0;
color: white;
font-weight: bold;
user-select: none;
cursor: pointer;
border: 2px solid lightgray;
border-radius: 100%;
outline: none;
background: none;
}
.parser-icon--loading {
/* hide text */
text-indent: -9999em;
white-space: nowrap;
overflow: hidden;
border-top: 5px solid rgba(255, 255, 255, 0.2);
border-right: 5px solid rgba(255, 255, 255, 0.2);
border-bottom: 5px solid rgba(255, 255, 255, 0.2);
border-left: 5px solid #ffffff;
transform: translateZ(0);
animation: loading 1.1s infinite linear;
}
@keyframes loading {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
`].join();
// node queries
const qs = {
garbage: [
'div.image__title.image-info-item', 'div.additional',
'div.header-downloads', 'div.social', 'div.image-info',
].join(', '),
needed: {
mainImg: {
q: 'img.no-click.screenshot-image',
node: null,
},
headerLogo: {
q: 'a.header-logo',
node: null,
},
headerLogoParent: {
q: 'div.header > div.page-constrain',
node: null,
},
}
};
document.arrive(qs.garbage, { existing: true }, n => n.remove());
let injected = false;
for (const nodeId in qs.needed) {
const nodeObj = qs.needed[nodeId];
// eslint-disable-next-line no-loop-func
document.arrive(nodeObj.q, { existing: true }, (aNode) => {
nodeObj.node = aNode;
const neededNodesAmount = Object.keys(qs.needed).length;
let arrivedCount = 0;
for (const nodeId in qs.needed) {
const nodeObj = qs.needed[nodeId];
if (nodeObj.node) arrivedCount += 1;
}
if (
injected === false &&
arrivedCount === neededNodesAmount
) {
injected = true;
main();
}
});
}
function main() {
GM_addStyle(css);
const b = document.createElement('button'); // fetch button
const bParent = qs.needed.headerLogoParent.node;
const bNeighbour = qs.needed.headerLogo.node;
b.innerText = 'R';
b.title = `${i18n.getNewRandImg}\n${i18n.hotkey}: R`;
b.className = 'parser-icon';
bParent.insertBefore(b, bNeighbour);
b.addEventListener('click', loadNewImg);
document.addEventListener('keydown', ev => {
if (ev.code === 'KeyR') loadNewImg();
});
let failsStreak = 0;
let isFetching = false;
function closeFetchUX() {
isFetching = false;
b.className = 'parser-icon';
}
async function loadNewImg() {
if (isFetching) return;
isFetching = true;
b.className = 'parser-icon parser-icon--loading';
const newSshotUrl = `https://prnt.sc/${makeImgId()}`;
try {
const newSshotPage = await fetch(newSshotUrl);
const tempDiv = document.createElement('div');
tempDiv.innerHTML = await newSshotPage.text();
const mainImg = qs.needed.mainImg.node;
const fetchedImgNode = tempDiv.querySelector(qs.needed.mainImg.q);
if (!fetchedImgNode || !fetchedImgNode.src) {
throw new Error(
'Failed to find a new image in fetched webpage. ' +
'URL: ' + newSshotUrl
);
}
let newSshotBlob;
try {
const newSshotImg = await fetch(fetchedImgNode.src);
newSshotBlob = await newSshotImg.blob();
} catch (e) {
console.error(e);
throw new Error(
`Failed to load ${fetchedImgNode.src} ` +
`that was fetched from ${newSshotUrl}`
);
}
mainImg.src = URL.createObjectURL(newSshotBlob);
closeFetchUX();
history.pushState(null, null, newSshotUrl);
failsStreak = 0;
} catch (e) {
failsStreak += 1;
console.error(e);
closeFetchUX();
if (failsStreak < 20) {
// retry immediately (almost)
setTimeout(loadNewImg, 250);
} else {
alert(`${GM_info.script.name}:\n${i18n.scriptGotFailStreak}`);
failsStreak = 0;
}
}
}
window.addEventListener('popstate', reloadPage);
}
// utils ---------------------------------------------
function reloadPage() {
window.location.reload();
}
function makeImgId() {
const chars = {
first: 'abcdefghijklmnopqrstuvwxyz123456789',
rest: 'abcdefghijklmnopqrstuvwxyz0123456789',
};
return makeId(chars.first, 1) + makeId(chars.rest, 5);
}
function makeId(charset, length) {
let result = '';
for (let i = 0, randNum; i < length; i++) {
randNum = Math.floor(Math.random() * charset.length);
result += charset.charAt(randNum);
}
return result;
}
// ---------------------------------------------------
})();