// ==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://t1.gstatic.com/faviconV2?client=SOCIAL&url=http://prnt.sc&size=32
// @version 1.3.1
// @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 {
/* fix header glitching */
overflow-y: scroll;
}
.randsshot-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;
}
.randsshot-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',
'script[src*="image-helper.js"]',
].join(', '),
needed: {
mainImg: 'img.no-click.screenshot-image',
headerLogo: 'a.header-logo',
headerLogoParent: 'div.header > div.page-constrain',
}
};
const neededNodes = {
mainImg: null,
headerLogo: null,
headerLogoParent: null,
}
document.arrive(qs.garbage, { existing: true }, n => n.remove());
let injected = false;
for (const nodeId in qs.needed) {
const query = qs.needed[nodeId];
document.arrive(query, { existing: true }, (aNode) => {
// unreachable?
if (injected) return;
neededNodes[nodeId] = aNode;
document.unbindArrive(query);
for (const nodeId in neededNodes) {
const node = neededNodes[nodeId];
if (node === null) return;
}
injected = true;
main();
});
}
function main() {
GM_addStyle(css);
const b = document.createElement('button');
const bParent = neededNodes.headerLogoParent;
const bNeighbour = neededNodes.headerLogo;
b.innerText = 'R';
b.title = `${i18n.getNewRandImg}\n${i18n.hotkey}: R`;
b.className = 'randsshot-icon';
bParent.insertBefore(b, bNeighbour);
b.addEventListener('click', loadNewSshot);
document.addEventListener('keydown', ev => {
if (ev.code === 'KeyR' && !ev.ctrlKey) loadNewSshot();
});
const parser = new DOMParser();
let failsStreak = 0;
let isFetching = false;
function closeFetchUX() {
isFetching = false;
b.className = 'randsshot-icon';
}
async function loadNewSshot() {
if (isFetching) return;
isFetching = true;
b.className = 'randsshot-icon randsshot-icon--loading';
const newSshotUrl = `https://prnt.sc/${makeSshotId()}`;
try {
const newSshotPage = await fetch(newSshotUrl);
const newSshotPageDOM = parser.parseFromString(
await newSshotPage.text(), 'text/html'
);
const fetchedImgNode = newSshotPageDOM.querySelector(
qs.needed.mainImg
);
if (!fetchedImgNode || !fetchedImgNode.src) {
throw new Error(
'Failed to find a new image in fetched webpage. ' +
'URL: ' + newSshotUrl
);
}
neededNodes.mainImg.src = fetchedImgNode.src;
await new Promise((resolve, reject) => {
const listeners = {
load: () => {
turnListeners('off');
history.pushState(null, null, newSshotUrl);
closeFetchUX();
resolve();
},
error: () => {
turnListeners('off');
reject(
`Failed to load ${fetchedImgNode.src} ` +
`that was fetched from ${newSshotUrl}`
);
},
};
const turnListeners = (to) => {
for (const type in listeners) {
const listener = listeners[type];
if (to === 'on') {
neededNodes.mainImg.addEventListener(type, listener);
} else {
neededNodes.mainImg.removeEventListener(type, listener);
}
}
};
turnListeners('on');
});
failsStreak = 0;
} catch (e) {
failsStreak += 1;
console.error(e);
closeFetchUX();
if (failsStreak < 20) {
// retry immediately (almost)
setTimeout(loadNewSshot, 250);
} else {
alert(`${GM_info.script.name}:\n${i18n.scriptGotFailStreak}`);
failsStreak = 0;
}
}
}
window.addEventListener('popstate', reloadPage);
}
// utils ---------------------------------------------
function reloadPage() {
window.location.reload();
}
function makeSshotId() {
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;
}
// ---------------------------------------------------
})();