// ==UserScript==
// @version 19.11.11
// @name Smart Scroll
// @description Provides buttons to scroll web pages up and down
// @license MIT
// @author S-Marty
// @compatible firefox
// @compatible chrome
// @namespace https://github.com/s-marty/SmartScroll
// @homepageURL https://github.com/s-marty/SmartScroll
// @supportURL https://github.com/s-marty/SmartScroll/wiki
// @icon https://raw.githubusercontent.com/s-marty/SmartScroll/master/images/smartScroll.png
// @contributionURL https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=QHFFSLZ7ENUQN&source=url
// @include /^https?://.*$/
// @exclude /[^\s]+\.(jpe?g|png|gif|bmp|svg)(\?[^\s]+)?$/
// @run-at document-end
// @grant GM.getValue
// @grant GM.setValue
// @grant GM_getValue
// @grant GM_setValue
// @noframes
// @downloadURL none
// ==/UserScript==
/* greasyfork.org jshint syntax checking hacks */
/* jshint asi: true */
/* jshint boss: true */
/* jshint esversion: 6 */
/* jshint loopfunc: true */
/** ****************** Features ******************
*** Able to leap tall pages in a single click
*** Up and down, slow or faster than a speeding bullet
*** More robust than a locomotive
*** Slow scroll by middle mouse button or mouse hover
*** 26 Slow scrolling speeds
*** Doesn't interfere with the page's native onScroll event
*** Conquers bottomless or never-ending pages in most cases
*** Disableable per site
*** No extra @require files (jquery et.al.)
*** Double-click on either button for settings ***
*** Compatable with Firefox 43 to 63+ ***
*** Compatable with Chrome 62 to 71 ***
*** Compatable with Violentmonkey ? to 2.10 ***
*** Compatable with Greasemonkey 3.9 to 4.7 ***
*** Compatable with Tampermonkey 4.7 to 4.8 ***/
(function () {
"use strict";
var buttons = {
monkey: function () {
let style;
if (this.user.settings.ignore.indexOf(host) !=-1) return;
if (this.body && this.is_userscript) {
style = document.createElement("style");
style.setAttribute('id', 'smartscrollstyle');
style.type = "text/css";
style.innerHTML = this.buttonCss();
this.head.appendChild(style);
this.up_ctn = document.createElement("span");
this.dn_ctn = document.createElement("span");
this.up_ctn.setAttribute("id","up_btn");
this.dn_ctn.setAttribute("id","dn_btn");
this.up_ctn.className = "updn_btn";
this.dn_ctn.className = "updn_btn";
this.body.appendChild(this.up_ctn);
this.body.appendChild(this.dn_ctn);
if (this.user.settings.crawl_trigger == 'middleclick') {
this.up_ctn.addEventListener('mousedown', function(e) { if(e.which==2) {buttons.creepUp(1)} }, false);
this.up_ctn.addEventListener('mouseup', function(e) { if(e.which==2) {buttons.creep = !1} }, false);
this.dn_ctn.addEventListener('mousedown', function(e) { if(e.which==2) {buttons.creepDn(1)} }, false);
this.dn_ctn.addEventListener('mouseup', function(e) { if(e.which==2) {buttons.creep = !1} }, false);
}
else {
this.up_ctn.addEventListener('mouseover', function(e) { buttons.creepUp(1) }, false);
this.up_ctn.addEventListener('mouseout', function(e) { buttons.creep = !1 }, false);
this.dn_ctn.addEventListener('mouseover', function(e) { buttons.creepDn(1) }, false);
this.dn_ctn.addEventListener('mouseout', function(e) { buttons.creep = !1 }, false);
}
this.up_ctn.addEventListener('dblclick', function(e) { if(e.which===1) {buttons.smartScroll_Settings(e)} }, false);
this.dn_ctn.addEventListener('dblclick', function(e) { if(e.which===1) {buttons.smartScroll_Settings(e)} }, false);
this.up_ctn.addEventListener('click', function(e) { if(e.which===1) {buttons.scrollToTop()} }, false);
this.dn_ctn.addEventListener('click', function(e) { if(e.which===1) {buttons.scrollToBottom()} }, false);
if (root !== null && this.user.settings.bottomless_pages) {
this.resize = document.createElement("iframe");
this.resize.setAttribute("name","resize_frame");
this.resize.setAttribute("tabindex","-1");
this.resize.className = "resize_frame";
root.appendChild(this.resize);
this.resize.contentWindow.addEventListener('resize', function(e) { buttons.getDocumentHeight(e) }, false);
}
window.addEventListener('scroll', buttons.onScroll, {passive : true});
window.addEventListener('resize', buttons.onResize, false);
this.body.addEventListener('mouseleave', buttons.saveAccrued, false);
window.addEventListener('beforeunload', function(e) { buttons.settings_close('unload')}, false);
document.addEventListener('readystatechange', function(e) { if(document.readyState === "complete") { buttons.getDocumentHeight(e)} }, false);
if (this.user.settings.dimButtons) {
setTimeout(buttons.fadeOut, 3000);
}
}
else return
},
areOverVid: function(e) {
var vid = (typeof e === 'object' && e.target.tagName == 'VIDEO') ? e.target : false;
if (! vid) return false;
else {
var isOverV = false;
var isOverH = false;
var vidRect = vid.getBoundingClientRect();
var butRect = buttons.up_ctn.getBoundingClientRect();
if (vidRect.right - vidRect.left > window.innerWidth / 2) {
isOverH = true;
}
else {
isOverH = vidRect.left < window.innerWidth - 33 && vidRect.right > window.innerWidth - 33;
}
isOverV = vidRect.top < butRect.top && vidRect.bottom > butRect.top;
return isOverH && isOverV;
}
},
fadeOut: function(e) {
var up_btn = buttons.up_ctn;
var dn_btn = buttons.dn_ctn;
if (buttons.fading) return;
else if (buttons.areOverVid(e)) {
if (buttons.opacity_timer) clearInterval(buttons.opacity_timer);
up_btn.style.visibility = 'visible';
dn_btn.style.visibility = 'visible';
up_btn.style.opacity = 0.65;
dn_btn.style.opacity = 0.65;
buttons.fading = setTimeout( function(e) {buttons.fading = null}, 3000);
setTimeout( function(e) {
buttons.opacity_timer = setInterval(function() {
if (up_btn.style.opacity <= 0) {
clearInterval(buttons.opacity_timer);
buttons.opacity_timer = null;
up_btn.style.visibility = 'hidden';
dn_btn.style.visibility = 'hidden';
}
else {
up_btn.style.opacity -= 0.025;
dn_btn.style.opacity -= 0.025;
}
}, 100);
}, 3000);
}
else {
if (buttons.opacity_timer) {
clearInterval(buttons.opacity_timer);
buttons.opacity_timer = null;
}
up_btn.style.visibility = 'visible';
dn_btn.style.visibility = 'visible';
up_btn.style.opacity = 0.65;
dn_btn.style.opacity = 0.65;
}
},
reLoadStart: function(e) {
setTimeout(function() {
buttons.getDocumentHeight();
buttons.getVideo();
setTimeout(buttons.fadeOut, 3000);
}, 500);
},
hideOnFullScreen: function() {
document.addEventListener("fullscreenchange", () => { buttons.onFullScreen()});
document.addEventListener("mozfullscreenchange", () => { buttons.onFullScreen()});
document.addEventListener("webkitfullscreenchange", () => { buttons.onFullScreen()});
},
onFullScreen: function(e) {
if (document.fullscreenElement || document.mozFullScreenElement || document.webkitFullscreenElement) {
buttons.body.className = buttons.body.className + " isfullscreen";
}
else {
buttons.body.className = buttons.body.className.replace(/\sisfullscreen/g, "");
window.setTimeout(buttons.resetButtons,100);
}
},
hasScrollbar: function() {
let x,
rootElem,
overflowShown,
overflowStyle,
overflowYStyle,
contentOverflows,
alwaysShowScroll;
if (typeof window.innerHeight === 'number') {
x = window.innerHeight - document.documentElement.clientHeight;
return [window.innerHeight > document.documentElement.clientHeight, x+1];
}
rootElem = document.documentElement || document.body;
if (typeof rootElem.currentStyle !== 'undefined') {
overflowStyle = rootElem.currentStyle.overflow;
}
overflowStyle = overflowStyle || window.getComputedStyle(rootElem, '').overflow
if (typeof rootElem.currentStyle !== 'undefined') {
overflowYStyle = rootElem.currentStyle.overflowY;
}
overflowYStyle = overflowYStyle || window.getComputedStyle(rootElem, '').overflowY;
contentOverflows = rootElem.scrollHeight > rootElem.clientHeight;
overflowShown = /^(visible|auto)$/.test(overflowStyle) || /^(visible|auto)$/.test(overflowYStyle);
alwaysShowScroll = overflowStyle === 'scroll' || overflowYStyle === 'scroll';
if ((contentOverflows && overflowShown) || (alwaysShowScroll)) {
return true}
else {
return false}
},
getScrollTop: function() {
if (typeof pageYOffset != 'undefined') {
buttons._x = pageXOffset;
return pageYOffset;
}
},
getDocumentHeight: function(e) {
let docHeight = Math.max(
document.documentElement.clientHeight,
document.body.scrollHeight, document.documentElement.scrollHeight,
document.body.offsetHeight, document.documentElement.offsetHeight
) - ( buttons.hasScrollbar() ? buttons.scrollbar : 0 );
if (docHeight > window.innerHeight) {
if(docHeight - window.innerHeight - buttons.scrollable < 6) return;
buttons.getVideo();
buttons.scrollable = docHeight - window.innerHeight;
buttons.onScroll();
}
},
scrollToTop: function(e) {
var y, start = buttons.scrolled;
if (start > 0) {
buttons.animate({
duration: 1000,
timing: function(timeFraction) {
return Math.pow(timeFraction, 5);
},
draw: function(progress) {
y = start - (progress * start);
if (y < 0) y = 0;
if (progress < 1) {
window.scrollTo(buttons._x, y);
}
else {
window.scrollTo(buttons._x, 0);
buttons.count_accru += start;
buttons.getVideo();
}
}
});
}
},
scrollToBottom: function(e) {
var y, start = buttons.scrolled;
var maxscroll = buttons.scrollable;
if (maxscroll > start) {
buttons.animate({
duration: 1000,
timing: function(timeFraction) {
return Math.pow(timeFraction, 5);
},
draw: function(progress) {
y = (progress * maxscroll) + ((1 - progress) * start);
if (y > maxscroll) y = maxscroll;
if (progress < 1) {
window.scrollTo(buttons._x, y);
}
else {
window.scrollTo(buttons._x, maxscroll + 2);
buttons.count_accru += maxscroll - start;
buttons.getVideo();
}
}
});
}
},
creepUp: function(e) {
if (e === 1) {
buttons.creep = 1;
buttons.killCreeper();
buttons.creeper = setInterval(buttons.creepUp, buttons.crawl_speed_ms);
}
buttons.scrolled = buttons.getScrollTop();
if (buttons.creep && buttons.scrolled > 0) {
window.scrollTo(buttons._x, buttons.scrolled - buttons.step);
buttons.count_accru += buttons.step;
}
else buttons.killCreeper()
},
creepDn: function(e) {
if (e === 1) {
buttons.creep = 1;
buttons.killCreeper();
buttons.creeper = setInterval(buttons.creepDn, buttons.crawl_speed_ms);
}
buttons.scrolled = buttons.getScrollTop();
if (buttons.creep && buttons.scrollable > buttons.scrolled) {
window.scrollTo(buttons._x, buttons.scrolled + buttons.step);
buttons.count_accru += buttons.step;
}
else buttons.killCreeper()
},
onResize: function(e) {
buttons.getDocumentHeight(e);
window.setTimeout(buttons.resetButtons,500);
},
onScroll: function(e) {
buttons.scrolled = buttons.getScrollTop();
if (! buttons.scrollable) { buttons.getDocumentHeight(e) }
if (buttons.scrolled > 0) {
buttons.toggle_up_btn("show");
}
else {
buttons.toggle_up_btn("hide");
}
if (buttons.scrollable > buttons.scrolled) {
buttons.toggle_dn_btn("show");
}
else {
buttons.toggle_dn_btn("hide");
}
},
toggle_up_btn: function(action) {
if (action == "show" && buttons.up_btn_show != "show") {
buttons.up_btn_show = "show";
buttons.animate({
duration: 400,
timing: function(timeFraction) {
return Math.pow(timeFraction, 2);
},
draw: function(progress) {
buttons.up_ctn.style.right = -33 + (progress * 33) + 'px';
}
});
}
else if (action == "hide" && buttons.up_btn_show != "hide") {
buttons.up_btn_show = "hide";
buttons.animate({
duration: 500,
timing: function back(x, timeFraction) {
return Math.pow(timeFraction, 2) * ((x + 1) * timeFraction - x)
}.bind(null, 2.8),
draw: function(progress) {
buttons.up_ctn.style.right = 0 - (progress * 33) + 'px';
}
});
}
},
toggle_dn_btn: function(action) {
if (action == "show" && buttons.dn_btn_show != "show") {
buttons.dn_btn_show = "show";
buttons.animate({
duration: 400,
timing: function(timeFraction) {
return Math.pow(timeFraction, 2);
},
draw: function(progress) {
buttons.dn_ctn.style.right = -33 + (progress * 33) + 'px';
}
});
}
else if (action == "hide" && buttons.dn_btn_show != "hide") {
buttons.dn_btn_show = "hide";
buttons.animate({
duration: 500,
timing: function back(x, timeFraction) {
return Math.pow(timeFraction, 2) * ((x + 1) * timeFraction - x)
}.bind(null, 2.8),
draw: function(progress) {
buttons.dn_ctn.style.right = 0 - (progress * 33) + 'px';
}
});
}
},
killCreeper: function() {
if (this.creeper !== null) {
clearInterval(this.creeper);
this.creeper = null;
}
},
getVideo: function() {
if (buttons.user.settings.dimButtons) {
var vid = document.querySelectorAll('video');
if (vid.length) {
for (let i = 0; i < vid.length; i++) {
if (vid[i].scrollWidth > 100 && vid[i].scrollHeight > 50) {
vid[i].addEventListener('mousemove', buttons.fadeOut, false);
}
}
document.addEventListener("keydown", function(e) {
if ((e.which == 38 || e.which == 40) && ["INPUT", "TEXTAREA"].indexOf(document.activeElement.tagName) < 0) {
buttons.fadeOut()
}
}, false);
}
}
},
animate: function ({timing, draw, duration}) {
let start = performance.now();
requestAnimationFrame(function animate(time) {
let timeFraction = (time - start) / duration;
if (timeFraction > 1) timeFraction = 1;
let progress = timing(timeFraction);
draw(progress);
if (timeFraction < 1 && buttons.allowScroll) {
requestAnimationFrame(animate);
}
});
},
smartScroll_Settings: function(e) {
let modal = document.querySelector(".smartScroll_Settings"), form;
if (! buttons.settings_id && modal === null) {
buttons.allowScroll = false;
buttons.killCreeper();
var s, i, n;
var sel = '';
var plus_sel = '';
var minus_sel = '';
var crawl_sel = '';
let hostname = window.location.hostname;
let m = hostname.match(/^([\w\-]*\.)?([\w\-]+\.((\w{3,4}$)|(\w{2}\.\w{2}$)))/i);
if (m !== null && m.length > 2) {
if (typeof m[1] == 'undefined') {m[1] = '';}
hostname = m[1] + m[2];
}
let hex = ['0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'],
div = document.createElement("div"),
div2 = div,
html = '',
id = 'a',
doctype = document.createElement("br").outerHTML.indexOf('/') > 0 ? 'XHTML' : 'HTML';
for (n = 0; n < 16; n++) {
id += hex[Math.floor(Math.random()*16)];
}
var selected = doctype == 'HTML' ? 'selected' : 'selected="selected"';
let rangetype = document.createElement("input");
rangetype.setAttribute("type", "range");
rangetype = rangetype.type != "text";
if (! rangetype) {
let opts = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,16,17,19,21,23,26,30,36,44,57,80,100];
crawl_sel = '';
}
plus_sel = '';
let checked = doctype == 'HTML' ? 'checked' : 'checked="checked"';
let inputclose = doctype == 'HTML' ? '' : '';
let APOS = '\u0027';
let NBSP = '\u00A0';
let MIDDOT = '\u00B7';
let CDATA1 = doctype == 'HTML' ? '' : '/**/';
let ignoredHTML = '', ignored = this.user.settings.ignore.split(",");
let padding = [60, 40, 65];
let scrolledToDateTitle = 'That'+APOS+'s ';
switch (true) {
case this.user.count > 32602522:
padding = [0,160,5];
s = ((Math.round(this.user.count * 8.825569e-6)) / 10000);
scrolledToDateTitle += (s == 1 ? 'The ' : s +' ')+'Light Second'+(s == 1 ? '' : 's')+'.';
break;
case this.user.count > 2676326:
padding = [20,120,25];
s = ((Math.round(this.user.count * 6.134495e-6)) / 100)
scrolledToDateTitle += (s == 1 ? 'The ' : s +' Times the ')+'Width of the U.S.';
break;
case this.user.count > 669082:
padding = [30,100,35];
s = ((Math.round(this.user.count * 7.747293e-5)) / 100)
scrolledToDateTitle += (s == 1 ? '' : s +' times ')+'the Altitude of the ISS.';
break;
case this.user.count > 3041:
padding = [40,80,45];
s = ((Math.round(this.user.count * 1.644e-2)) / 100);
scrolledToDateTitle += s +' Mile'+(s == 1 ? '' : 's')+'.';
break;
default:
s = ((Math.round(this.user.count * 8.68)) / 10);
scrolledToDateTitle += s +' F'+(s == 1 ? 'oo' : 'ee')+'t';
}
let scrolledToDate = this.user.count.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",") +',000 Pixels Smart Scrolled.';
let addable = false;
for (n = 0; n < ignored.length; n++) {
if (ignored[n].length < 5) continue;
addable = addable || hostname == ignored[n];
ignoredHTML += ''+ignored[n]+'
';
}
addable = addable ? '' : (''+hostname+'
');
html = '