Skip to content

Instantly share code, notes, and snippets.

@TSedlar
Last active October 30, 2019 21:53
Show Gist options
  • Save TSedlar/379d1e9260ab41e25437df3868bc9302 to your computer and use it in GitHub Desktop.
Save TSedlar/379d1e9260ab41e25437df3868bc9302 to your computer and use it in GitHub Desktop.
PageScrollHistory UserScript
// ==UserScript==
// @name Page Scroll History
// @namespace http://tampermonkey.net/
// @version 1.2.2
// @description Page Scroll History
// @author Tyler Sedlar
// @match *://www.tdev.top/eBooks/*
// @grant GM.registerMenuCommand
// @grant GM.getValue
// @grant GM.setValue
// @grant GM.listValues
// @grant GM.xmlHttpRequest
// ==/UserScript==
(function() {
'use strict';
var storageService = 'https://keyvalue.immanuel.co/api/KeyVal';
var storageApiKey = 'YOUR_KEY_HERE, GET @ https://keyvalue.immanuel.co/api/KeyVal/GetAppKey';
function getPageID() {
var minTitle = document.title
.toLowerCase()
.replace(/ /g, '_')
.replace(/-/g, '_')
.replace(/:/g, '_')
.replace(/__/g, '_')
.replace(/\./g, '')
.replace(/,/g, '');
var divCount = document.querySelectorAll('div').length;
var pCount = document.querySelectorAll('p').length;
var imgCount = document.querySelectorAll('img').length;
var spanCount = document.querySelectorAll('span').length;
var blockquoteCount = document.querySelectorAll('blockquote').length;
var aCount = document.querySelectorAll('a').length;
return minTitle + `_${divCount}d${pCount}p${imgCount}i${spanCount}s${blockquoteCount}b${aCount}a`;
}
function contentElement(callback) {
callback(document.body);
}
function updateBookmark(element) {
if (element) {
var pageID = getPageID();
var currentParagraph = topmostContent();
GM.setValue(pageID, currentParagraph);
GM.xmlHttpRequest({
url: `${storageService}/UpdateValue/${storageApiKey}/${pageID}/${currentParagraph}`,
method: 'POST',
onload: function(response) {
if (response.readyState == 4 && response.status == 200) {
alert(`Bookmark updated to ${currentParagraph}`);
} else {
alert('Bookmaked locally, but failed to set remotely.');
}
},
onerror: function(response) {
alert('Bookmaked locally, but failed to set remotely.');
}
});
}
}
function scrollToBookmark(element) {
if (element) {
var pageID = getPageID();
console.log(`scrollToBookmark(${pageID})`);
GM.getValue(pageID).then(function(localParagraph) {
Promise.resolve(new Promise(function(resolve, reject) {
GM.xmlHttpRequest({
url: `${storageService}/GetValue/${storageApiKey}/${pageID}`,
method: 'GET',
onload: function(response) {
if (response.readyState == 4 && response.status == 200 && response.responseText.replace(/"/g, '').length > 0) {
resolve(parseInt(response.responseText.replace(/"/g, '')));
} else {
resolve(-1);
}
},
onerror: function(response) {
resolve(-1);
}
});
})).then(function(remoteParagraph) {
var latestParagraph = localParagraph;
if (latestParagraph) {
if (remoteParagraph > latestParagraph) {
latestParagraph = remoteParagraph;
GM.setValue(pageID, latestParagraph);
} else if (latestParagraph > remoteParagraph && remoteParagraph != -1) {
contentElement(function(e) { updateBookmark(e); });
}
} else if (remoteParagraph >= 0) {
latestParagraph = remoteParagraph;
GM.setValue(pageID, latestParagraph);
}
if (latestParagraph !== undefined && latestParagraph >= 0) {
console.log('scrolling to bookmark ' + latestParagraph);
scrollToPos(latestParagraph);
}
});
});
}
}
function checkVisible(elm) {
var rect = elm.getBoundingClientRect();
var viewHeight = Math.max(document.documentElement.clientHeight, window.innerHeight);
return !(rect.bottom < 0 || rect.top - viewHeight >= 0);
}
function topmostContent() {
var paragraphs = document.querySelectorAll('p');
var idx = 0;
for (var p of paragraphs) {
if (checkVisible(p)) {
return idx;
}
idx++;
}
return 0;
}
function scrollToPos(pos) {
var paragraphs = document.querySelectorAll('p');
if (paragraphs.length >= pos) {
paragraphs[pos].scrollIntoView({ behavior: "smooth", block: "start", inline: "nearest" });
}
}
GM.registerMenuCommand('Go To Bookmark', function() { contentElement(function(e) { scrollToBookmark(e); }); }, 'g');
GM.registerMenuCommand('Update Bookmark', function() { contentElement(function(e) { updateBookmark(e); }); }, 'u');
GM.registerMenuCommand('Download Bookmarks', function() {
function downloadString(text, fileType, fileName) {
var blob = new Blob([text], { type: fileType });
var a = document.createElement('a');
a.download = fileName;
a.href = URL.createObjectURL(blob);
a.dataset.downloadurl = [fileType, a.download, a.href].join(':');
a.style.display = "none";
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
setTimeout(function() { URL.revokeObjectURL(a.href); }, 1500);
}
GM.listValues().then(function(keys) {
Promise.all(keys.map(function(k) {
return GM.getValue(k).then(function(v) {
return { key: k, value: v };
});
})).then(function(dataList) {
var bookmarks = {};
for (let data of dataList) {
bookmarks[data.key] = data.value;
}
downloadString(JSON.stringify(bookmarks, null, 2), 'json', 'page-scroll-history-storage.json');
});
});
}, 'd');
window.onbeforeunload = function(e) {
if (confirm('Would you like to save your position?')) {
contentElement(function(e) { updateBookmark(e); });
}
};
setTimeout(function() {
contentElement(function(e) { scrollToBookmark(e); });
}, 2000);
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment