// ==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
// ==/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 = '<select name="crawl_speed">';
opts.forEach( function(opt) {
sel = opt == buttons.user.settings.crawl_speed ? selected : '';
crawl_sel += '<option value="'+opt+'" '+sel+'>'+opt+'</option>';
});
crawl_sel += '</select>';
}
plus_sel = '<select name="top_plus" style="width: 52px;height: 20px;font-size: 12px;">';
minus_sel = '<select name="bot_minus" style="width: 52px;height: 20px;font-size: 12px;margin-left:36px;">';
[0,5,10,15,20,25,30,35,40,45,50,55,60,65,70,75,80,85,90,95,99].forEach( function(opt) {
sel = opt == buttons.user.settings.top_plus ? selected : '';
plus_sel += '<option value="'+opt+'" '+sel+'>'+opt+'</option>';
sel = opt == buttons.user.settings.bot_minus ? selected : '';
minus_sel += '<option value="'+opt+'" '+sel+'>'+opt+'</option>';
});
plus_sel += '</select>';
minus_sel += '</select>';
let checked = doctype == 'HTML' ? 'checked' : 'checked="checked"';
let inputclose = doctype == 'HTML' ? '' : '</input>';
let APOS = '\u0027';
let NBSP = '\u00A0';
let MIDDOT = '\u00B7';
let CDATA1 = doctype == 'HTML' ? '' : '/*<![CD'+'ATA[*/';
let CDATA2 = 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 += '<span class="ignores"><button style="padding:0px 3px 1px;margin-right:6px;background-color:#ff3333;color:#fff;border-radius:3px;-webkit-border-radius:3px;-moz-border-radius:3px" onclick="document.forms[\'sssettings\'].remove.value+=\''+ignored[n]+',\';this.disabled=\'disabled\'" title="Mark '+ignored[n]+' for deletion from this list">x</button><span>'+ignored[n]+'</span></span><br />';
}
addable = addable ? '' : ('<span class="addable"><button style="padding:0px 3px 1px;margin-right:6px;background-color:#33ff33;color:#000;font-weight:bold;border-radius:3px;-webkit-border-radius:3px;-moz-border-radius:3px" onclick="document.forms[\'sssettings\'].add.value+=\''+hostname+'\';this.disabled=\'disabled\'" title="Mark '+hostname+' for addition to this list">+</button><span>'+hostname+'</span></span><br />');
html = '<div id="'+id+'" style="width:500px;height:400px;margin:auto auto;border-radius:12px 54px 12px 100px;-webkit-border-radius:12px 54px 12px 100px;-moz-border-radius:12px 54px 12px 100px;border:3px solid #CCC;">'+
' <div style="width:500px;height:50px;background-color:#000;border-radius:12px 50px 0px 0px;-webkit-border-radius:12px 50px 0px 0px;-moz-border-radius:12px 50px 0px 0px;">'+
' <div style="width: 380px;height:40px;padding:12px 0 0 65px;color:#eee;font-size: 1.5em;font-weight: bold;background:transparent url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAkCAQAAABLCVATAAAAAmJLR0QA/vCI/CkAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQfiDBwSIh99fynEAAAD9klEQVRIx6XWzW9UVRjH8c+dzjBtAfsCUlIsKbSU8hZpijGRRAzhTY0Y4sqgiYQENVEXJiTEVUPQjSgr3Rhd+i+wKAYJSMJb5U2YAkljpSVAUlqgZZjOzHXR28tMKSSkZzKbc879nuc+z+/53RNsCc1oBEKPZCWft'+
'WFcTighQBj909NsD5GaDhQIjcups0aHf50yqtZ6dS7rMywlJRCWPZFgKmgC0q5TuwWuOewUeGiv9e7I6JGRknoqsimgx2ptstJ8Tc44oCeaPyrQZa0aba44YkT6eaCsNts1W6jeCV3Ol6wdlfOjVUbVa3JY7xRURUvXE0yHbZZoVeOyPc4pL2i/GzZoUmNcg5wBFXFWH0tM5iarzUYtWlX50y6XJk9SqQIBjtnlikrtmm3Q7nHJMYkJZl6t7ZZYoso/9jofRRN41Q6rozIXnfSpXmmtmm1VI18OCuVs0qxVtR479ShGy20O+F2Xpriqf/ncoCrLNNpsvBw0rt0qDWbhtgsxptX3Nil6x3eaY9RZD1Gp0QrtcUwJyOlUb7YQuSgftPjNuxKKknY6aEFclAJCNWp1yEW7'+
'E4yr064xKudanwgltTpovUARRaH3/GSxpCp7LBQKVVukVb2ccEJHOWvUmQ0KXrHfGwKLvSlUiM4rSNqh2oDANrXRS85RZ6mz0pOCXKlRZVSXQKPdRHEEcT8VBLZFgFBREOVpuTNPlL0o7p0g2jidXYiLID4gZVFpsueUbHmxEXqpFJSagbElS0H5GYDKdPRgsuVeeCQ8KAX1y5YYZxD9nrbUJythNPfIf6WvmHHLXJVRNcbcFag0vwyWELotK/CyymjlkVuuPVF2Wp9hDyPTGPGzDut86IKKEh0FLvrAOq/7xVg0/8CIPmnBBChpWK9BWQTO2Oe+ISd8rT+2rgrn7HbKPXftNyAQGHPLDUNR3aLy97hnVIBCFEPBSR+7IIkKJ3wZe1RWEYFhw/6OpROBMq64JYs6DTH'+
'quH3OSzrtG6fjbC1VjVGDMjKxjpIToacc0aSoXadffeZmtHxEymtOOl6COaTRmGsGdZdIOTL/CqPuaVBUb7l5etyPGrffOdflo/Qu84MtxmUM6DYgFZt//DlK642spM1HkvYZiDKSjXW0wgHvG5Ux4JirZk3/OUoaMGyeRyp0anDRvTJJtvnWZnfccFO3SzFmSkQwy1WDNrtvnrcNORTrluW+8pZeIzK63S+LZkpEE1FlXXJbQd5qc92IctXqCxtdd9kfjimU+cVERMF096O8nHotVuhx1EO1tmlxTZ8hqacuMIGCYcHW8Hn3o6CsZdPPuE7lPZAsPNXjk+ekn+M+QdkzedWSIzO7+QklVJvlf7qjThcu+vTfAAAAAElFTkSuQmCC) no-repeat 10px center;">'+
' Smart Scroll Settings</div>'+
' </div>'+
' <form id="'+id+'_settings_form" name="sssettings" class="sssettings" action="javascript:void(0)" onsubmit="return false" style="padding:10px;height:330px;border-radius:0px 0px 10px 96px;-webkit-border-radius:0px 0px 10px 96px;-moz-border-radius:0px 0px 10px 96px;">'+
' <div style="width:100%;height:240px;padding-bottom:0px;">'+
' <div style="width:50%;height:100%;padding:0px;float:left;border-radius:0px 0px 0px 96px;-webkit-border-radius:0px 0px 0px 96px;-moz-border-radius:0px 0px 0px 96px;">'+
' <span style="margin-left:30px;">Slow scrolling speed:</span><br />'+ (rangetype ?
' <input name ="crawl_speed" type="range" min="1" max="100" step="1" value="'+this.user.settings.crawl_speed+'" onchange="document.getElementById(\'crawl_speed_now\').innerHTML=this.value" style="width: 180px;">'+inputclose+' <span id="crawl_speed_now">'+this.user.settings.crawl_speed+'</span><br />'+
' <span style="font-size:11px;">'+NBSP+'Slow '+MIDDOT+NBSP+MIDDOT+NBSP+MIDDOT+NBSP+MIDDOT+' Fast '+MIDDOT+NBSP+MIDDOT+NBSP+MIDDOT+NBSP+MIDDOT+' Faster</span><br /><br />' :
' '+crawl_sel+'<br />'+
' <span style="font-size:11px;">1 = Slow thru 100 = Fast</span><br /><br />')+
' <input type="checkbox" name="bottomless_pages" id="bottomless1" '+(this.user.settings.bottomless_pages ? checked : "")+' >'+inputclose+' <label for="bottomless1" title="Recalculates when pages add content to bottom">Bottomless pages</label><br />'+
' <input type="checkbox" name="refresh" id="refresh1" '+(this.user.settings.refresh ? checked : "")+' >'+inputclose+' <label for="refresh1" title="Refresh current page upon critical settings changes made here">Auto page refresh</label><br />'+
' <input type="checkbox" name="dimButtons" id="dimButtons1" '+(this.user.settings.dimButtons ? checked : "")+' >'+inputclose+' <label for="dimButtons1" title="Buttons will disappear when over videos after 3 seconds">Fade out over video</label><br />'+
' <div style="margin: 6px 0 2px 30px;">Slow scroll trigger</div>'+
' <span><input type="radio" name="crawl_trigger" id="trigger1" value="middleclick"'+(this.user.settings.crawl_trigger == "middleclick" ? checked : "")+' >'+inputclose+' <label for="trigger1" title="Middle mouse button held down">Middleclick</label> '+
' <input type="radio" name="crawl_trigger" id="trigger2" value="hover" '+(this.user.settings.crawl_trigger == "hover" ? checked : "")+' style="margin-left:10px;">'+inputclose+' <label for="trigger2" title="Mouse pointer held over">Hover</label></span><br />'+
' <div style="margin: 6px 0 2px 30px;">Button Position</div>'+
' <span><input type="radio" name="position" id="position1" value="top" onmouseout="this.blur()" '+(this.user.settings.position == "top" ? checked : "")+'>'+inputclose+' <label for="position1" title="Always at top right">Top</label>'+
' <input type="radio" name="position" id="position2" value="middle" onmouseout="this.blur()" '+(this.user.settings.position == "middle" ? checked : "")+' style="margin-left:6px;">'+inputclose+' <label for="position2" title="Always at middle right">Middle</label>'+
' <input type="radio" name="position" id="position3" value="bottom" onmouseout="this.blur()" '+(this.user.settings.position == "bottom" ? checked : "")+' style="margin-left:6px;">'+inputclose+' <label for="position3" title="Always at bottom right">Bottom</label></span><br />'+
' <span>'+plus_sel+'<span title="Pixels down from top">:Plus</span>'+minus_sel+'<span title="Pixels up from bottom">:Less</span></span><br />'+
' </div>'+
' <div style="width:50%;height:100%;float:left;">'+
' <div style="width:100%;height:20px;text-align:center;" title="(Scroll buttons will not be used)">Ignored Sites</div>'+
' <div id="ssignored" style="width:224px;height:180px;overflow-y: auto;overflow-x: hidden;border: 1px solid #000;padding: 6px;line-height:1.3;border-radius:5px;-webkit-border-radius:5px;-moz-border-radius:5px;">'+
' '+addable+ignoredHTML+'</div>'+
' <div style="width:100%;height:12px;text-align:center;font-style: italic;color:blue;font-size: 12px;padding-top: 4px;cursor:help" title="'+scrolledToDateTitle+'">'+scrolledToDate+'</div>'+
' </div>'+
' </div>'+
' <div style="width:100%;height:90px;border-radius:0px 0px 10px 96px;-webkit-border-radius:0px 0px 10px 96px;-moz-border-radius:0px 0px 10px 96px;">'+
' <div style="width:fit-content;width: -moz-fit-content;height:80px;margin:10px auto 0px;background-color:transparent">'+
' <button id="'+id+'_settings_close" style="background-color:#ff3333;padding:30px '+padding[0]+'px;border-radius:0px 0px 0px 74px;-webkit-border-radius:0px 0px 0px 74px;-moz-border-radius:0px 0px 0px 74px;">Cancel</button> '+
' <button id="'+id+'_settings_donate" style="background-color:#ffff33;padding:30px '+padding[1]+'px;">Donate</button>'+
' <button id="'+id+'_settings_save" style="background-color:#33ff33;padding:30px '+padding[2]+'px;">Save</button>'+
' </div>'+
' </div>'+
' <input name ="remove" type="hidden" value="">'+inputclose+'<input name ="add" type="hidden" value="">'+inputclose+'<input name ="'+id+'" type="hidden" value="">'+inputclose+
' <input name ="submit" type="submit" value="" style="width:0px;height:0px;opacity:0;">'+inputclose+
' </form>'+
'</div>'+
'<style type="text/css">'+CDATA1+
' #pdus div, #pdus span, #pdus label, #pdus form, #pdus input, #pdus button {margin:0px;padding:0px;border:0px;color:#333;box-sizing:content-box;background-color:#EEE; font: 14px \'Titillium Web\', \'Helvetica Neue\', Helvetica, Arial, sans-serif; box-shadow: none;min-height:0px;line-height: 1;text-align:left; text-decoration:none;outline:none; }'+
' #pdus div, #pdus form {display:block;} #pdus span, #pdus label {display:inline;} #pdus input, #pdus button {display:inline-block;}'+CDATA2+
'</style>';
div.setAttribute('id', 'pdus');
div.className = "smartScroll_Settings";
div.style = 'width:100%;height:100%;margin:0;padding:0;position:fixed;top:0px;left:0px;background-color: rgba(0, 0, 0, 0.7);z-index:84000;';
div.innerHTML = html;
/* Some (unmentionable large) sites remove settings modal as soon as it is appended */
buttons.body.removeChild=function() {return div2}
buttons.body.replaceChild=function() {return div2}
if (e && e == 'removed') {
buttons.body.innerHTML += div.outerHTML
}
else {
buttons.body.appendChild(div);
setTimeout(function() {
if (document.querySelector(".smartScroll_Settings") === null) {
buttons.settings_id = "";
buttons.smartScroll_Settings('removed');
}
}, 300);
}
buttons.settings_id = id;
form = document.querySelector('form#'+id+'_settings_form');
form.querySelector('#'+id+'_settings_close').addEventListener('click', function(e) { buttons.settings_close() }, false);
form.querySelector('#'+id+'_settings_donate').addEventListener('click', function(e) { buttons.settings_donate() }, false);
form.querySelector('#'+id+'_settings_save').addEventListener('click', function(e) { buttons.onSettingsSave() }, false);
form.querySelector('#'+id+'_settings_save').addEventListener('change', function(e) { buttons.onSettingsSave() }, false);
form.querySelector('#position1').addEventListener('change', function(e) { buttons.updateButtonCss() }, false);
form.querySelector('#position2').addEventListener('change', function(e) { buttons.updateButtonCss() }, false);
form.querySelector('#position3').addEventListener('change', function(e) { buttons.updateButtonCss() }, false);
let options1 = form.top_plus.options;
let options2 = form.bot_minus.options;
for (i = 0;i < options1.length; i++) {
options1[i].addEventListener('mouseup', function(e) { buttons.updateButtonCss(e) }, false);
options2[i].addEventListener('mouseup', function(e) { buttons.updateButtonCss(e) }, false);
};
}
else {
buttons.settings_close()
}
},
onSettingsSave: function() {
var form = document.querySelector('form#'+buttons.settings_id+'_settings_form');
var inputs = form.querySelectorAll('input, select'),
settings = { ignore: this.user.settings.ignore },
input,
val,
i, n,
ignoreUpdated = false,
refreshNeeded = false;
if (form.querySelector('input[name="'+buttons.settings_id+'"]') !== null) {
for (i = 0; i < inputs.length; i++) {
val = null;
input = inputs[i];
switch (input.type) {
case 'checkbox':
val = input.checked;
if (this.user.settings[input.name] != val && input.name != 'refresh') {
refreshNeeded = true;
}
break;
case 'select':
case 'select-one':
val = parseInt(input[input.selectedIndex].value);
break;
case 'radio':
if (input.checked) {
val = input.value;
if (this.user.settings[input.name] != val) {
refreshNeeded = true;
}
}
else {continue;}
break;
case 'range':
val = parseInt(input.value);
break;
case 'hidden':
if (input.name == buttons.settings_id) {
continue;
}
else if (input.name == "remove" && input.value != "") {
let thisUrl = false;
let urls = input.value.split(",");
for (n = 0; n < urls.length; n++) {
if (urls[n].length < 5) continue;
if (urls[n].indexOf(window.location.hostname) !=-1) {
thisUrl = true;
}
var url = new RegExp(urls[n] +",", "gi");
settings.ignore = settings.ignore.replace(url, "");
}
refreshNeeded = refreshNeeded || thisUrl;
continue;
}
else if (input.name == "add" && input.value != "") {
settings.ignore += input.value.trim() +',';
refreshNeeded = ignoreUpdated = true;
continue;
}
else { continue; }
break;
default: continue;
}
if (val !== null) {
settings[input.name] = val;
}
}
if (ignoreUpdated) {
let ignore = settings.ignore.split(',');
ignore.pop();
if (ignore.length > 1) {
ignore.sort();
settings.ignore = ignore.join() +',';
}
}
buttons.saveSettings({name: 'user_settings', value: settings});
if (refreshNeeded && settings.refresh) {
window.location = window.location;
}
else {
this.resetButtons();
}
}
buttons.settings_close();
},
settings_close: function(e) {
let modal = document.querySelector(".smartScroll_Settings");
if (modal !== null) {
modal.remove();
}
buttons.settings_id = "";
if (typeof e != "undefined" && e == 'unload') {
buttons.toggle_up_btn("hide");
buttons.toggle_dn_btn("hide");
}
else { buttons.resetButtons(); }
},
saveAccrued: function(e) {
if (buttons.count_accru > 1000) {
buttons.initSettings({name: "count"});
}
},
saveSettings: function(settings) {
if (typeof settings == 'undefined' || typeof settings.name == 'undefined' || settings.name == '') {
console.error('saveSettings() input error');
return;
}
switch(settings.name) {
case 'user_settings':
this.GM.setValue('settings', JSON.stringify(settings.value));
break;
case 'count':
if (this.count_accru > 999) {
let totalCount = Math.round(this.count_accru / 1000) + settings.value;
this.GM.setValue('count', totalCount);
this.user.count = totalCount;
this.count_accru = 0;
}
else {
this.user.count = settings.value;
}
break;
}
},
settings_donate: function() {
let donate_url = "https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=QHFFSLZ7ENUQN&source=url";
window.open(donate_url, '_blank');
},
updateButtonCss: function(e) {
let style = document.querySelector('#smartscrollstyle');
let form = document.querySelector('form#'+buttons.settings_id+'_settings_form');
let position2 = form.position2.checked;
let position3 = form.position3.checked;
let position = 'top';
if (position2) position = 'middle';
else if (position3) position = 'bottom';
let settings = {
position: position,
top_plus: parseInt(form.top_plus[form.top_plus.selectedIndex].value),
bot_minus: parseInt(form.bot_minus[form.bot_minus.selectedIndex].value),
bottomless_pages: buttons.user.settings.bottomless_pages
};
style.innerHTML = buttons.buttonCss(settings);
},
buttonCss: function(settings) {
let btn_css = '',
dn_btn_css = '',
updn_btn_css = '';
settings = typeof settings === 'object' ? settings : this.user.settings;
if (settings.position == 'top') {
updn_btn_css = 'top:'+(settings.top_plus)+'px;';
dn_btn_css = 'margin-top:33px;';
} else if (settings.position == 'bottom') {
updn_btn_css = 'bottom:'+(settings.bot_minus+33)+'px;';
dn_btn_css = 'margin-bottom:-33px;';
} else if (settings.position == 'middle') {
updn_btn_css = 'bottom:50%;';
dn_btn_css = 'margin-bottom:-33px;';
}
btn_css += '#up_btn { position:fixed; right:-33px; z-index:54000; height:36px; width:33px; display:inline; cursor:pointer; background:url('+up_btn_src+') no-repeat scroll 50% 50% rgba(0, 0, 0, 0.7); }';
btn_css += '#dn_btn { position:fixed; right:-33px; z-index:54000; height:36px; width:33px; display:inline; cursor:pointer; '+dn_btn_css+'background:url('+dn_btn_src+') no-repeat scroll 50% 50% rgba(0, 0, 0, 0.7); }';
btn_css += '.updn_btn { '+updn_btn_css+'opacity:0.65; } .isfullscreen > .updn_btn { display:none;}';
btn_css += '.sssettings button:hover {opacity:0.85;} .updn_btn:hover { opacity:1; } .sssettings button[disabled], .sssettings button[disabled]:hover { opacity:0.35;} ';
btn_css += '.sssettings span button:hover:enabled {line-height: 1.4 !important; width: 10px !important;} .sssettings .ignores button[disabled] + span { opacity:0.5;} .sssettings .addable button + span { opacity:0.5;} .sssettings .addable button[disabled] + span { opacity:1;}';
if (settings.bottomless_pages) {
btn_css += 'iframe.resize_frame {position: absolute; display: block ;top: 0; bottom: 0; left: 0; height: 100%; opacity: 0; z-index: 0; width: 0; border: 0; background-color: transparent;}';
if ('contentType' in document) { // Firefox
if (document.contentType.indexOf("image") ==-1) {
btn_css += 'body {position:relative !important;} body.isfullscreen {position:initial !important;}';
}
}
else {
btn_css += 'body {position:relative !important;} body.isfullscreen {position:initial !important;}';
}
}
return btn_css;
},
scrolledUpdate: function(e) {
if (buttons.count_accru > 1000 || e === true) {
buttons.initSettings({name: "count"});
}
window.setTimeout(buttons.scrolledUpdate, 120000);
},
resetButtons: function (e) {
buttons.allowScroll = true;
buttons.up_btn_show = "";
buttons.dn_btn_show = "";
buttons.getDocumentHeight(e);
buttons.crawl_speed_ms = Math.round(200 / buttons.user.settings.crawl_speed);
},
syncGM_getValue: function (e) {
if (e.detail.setting == 'settings') {
buttons.initSettings(e.detail);
}
else if (e.detail.setting == 'count') {
if (e.detail.value && e.detail.value != "{}") {
buttons.saveSettings({name: "count", value: JSON.parse(e.detail.value)});
}
}
},
initSettings: function (detail) {
if (typeof detail.name != 'undefined') {
this.GM.getValue(detail.name, "{}").then(function (value) {
var details = {
detail: {
setting: detail.name,
value: value || "{}"
}
};
try{
buttons.syncGM_getValue(details);
}
catch (e) {
console.warn( 'Smart Scroll: UPDATE Your Browser ',e.name );
}
});
}
else {
var settings,
setting = detail.setting,
value = detail.value;
if (! value || value == "{}") {
settings = false;
console.warn( 'Smart Scroll: NO storage database');
}
else {
settings = JSON.parse(value);
//console.log( 'Smart Scroll: Initializing settings from storage database');
}
if (! settings) {
settings = this.user;
console.log( 'Smart Scroll: Installing storage database...' );
for (let k in settings) {
let jstr = JSON.stringify(settings[k]);
this.GM.setValue(k, jstr);
console.log( 'Smart Scroll: Inserting '+k+': '+ jstr);
}
settings = this.user.settings;
console.log( 'Smart Scroll: dONE' );
}
this.user.settings = settings;
this.scrolledUpdate(true);
this.monkey();
this.monkey = null;
}
},
init: function () {
if (typeof GM_info === "object" || typeof GM === "object") {
this.is_userscript = true;
if (typeof GM === "undefined") {
console.log( 'Smart Scroll: Legacy GM_setValue enabled' );
this.GM = {
info: GM_info,
setValue: GM_setValue,
getValue: function () {
return new Promise( (resolve, reject) => {
try {
resolve(GM_getValue.apply(this, arguments));
}
catch (e) { reject(e) }
});
}
};
}
else { this.GM = GM; }
var outerscroll = document.createElement("div");
var innerscroll = document.createElement("div");
outerscroll.style = "position:absolute;top:0px;background-color:transparent;";
innerscroll.style = "height:"+ (window.innerHeight - 100) +"px;width:100px;overflow:scroll;background-color:transparent;";
this.body.appendChild(outerscroll);
outerscroll.appendChild(innerscroll);
let h1 = innerscroll.scrollHeight;
innerscroll.style.overflow = 'hidden';
let h2 = innerscroll.scrollHeight;
this.scrollbar = h2 - h1;
innerscroll.remove();
outerscroll.remove();
this.initSettings({name: "settings"});
}
},
head: document.head || document.getElementsByTagName('head')[0],
body: document.body || document.documentElement,
user: {
settings: {
bottomless_pages: true,
crawl_trigger: 'hover',
position: 'bottom',
dimButtons: true,
crawl_speed: 21,
refresh: true,
bot_minus: 30,
top_plus: 0,
ignore: ''
},
count: 0
},
crawl_speed_ms: 10,
opacity_timer: null,
allowScroll: true,
settings_id: "",
up_btn_show: "",
dn_btn_show: "",
count_accru: 0,
scrollable: 0,
scrollbar: 0,
scrolled: 0,
creeper: null,
up_ctn: null,
dn_ctn: null,
creep: !1,
step: 5,
GM: null,
_x: 0
};
var _once = 0;
var up_btn_src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACEAAAAkCAQAAACtIJtXAAAAAmJLR0QA/vCI/CkAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQfiDBkQOCqvuzNgAAABWElEQVRIx6XWO7aDIBCA4V+OWYOaVbiDFGlccopkD/Sp0qiswcJb+ODhgNwjXXh8DpFhLKa558uVVvxmuF8iyi8PcWBkCHoaapmILTdA5fQZDJXIlPHl/vSlX2LKENDCcoCaemdab7Q8Am1kzxuj0d4clQ9sTAtoxiORB0jITgyZg'+
'EUGnxgxVJnAglSYNQ5lY2iEqR03bnTCSLPHoVIxdHwA+AiIjUPFY9iAGLLFsW7kGIMLyEi9JoCS/64QiEUSJVzgyfMEUWfAi9cJIhA+AARI1kZ8IEQihHHS5n0AXOTtpKWxyd5gGPbX+mASn/YKfm+nSYUnPrfZE63CE5/b7IlWx8z7XwzOG2mCuygNaCerVPxCSwP2elKpWzEHCG7wmhaNjpQcW2MSRWBB5JITK1FCNXNLjl8QyS2ILuO2KlWWe/HjoM6+z4vffO0DBco7V1sB09xfIv4A9ICG7qoP1KkAAAAASUVORK5CYII=';
var dn_btn_src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACEAAAAkCAQAAACtIJtXAAAAAmJLR0QA/vCI/CkAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQfiDBkRAC0ngnAPAAABV0lEQVRIx6XWO5KDMAyA4R8PnAHIKbjBFtkiR06TIjdwnyoN4DOkYAsClm2Z8Q4q/fhGgGxRfZaRF2eiei9wOUXUL37UiZkpGunpdCK33QGtGHM4WpWp89vD5eu4xtQxYJXtAB3dzgzBbJ0CQ+aZN8ZigzWmHNiYAbDMKVEGaMhOT'+
'IWAR6aQmHG0hcCKtLhvHsbn0P+rJvs9D5Pm8KSh4ZZsutHQ8EzyMGkOvwA8IuTGQ8zKPL4Por0HiWxA/D6iutjimiASuCbrFeIeISFwLyFi5BjIECFyDGSJFMkBO+HEsdEQDZhxnuhFxWuInsFWTbWstE5BcuEr2sQVXxq+ok168kpCniojK94WIuv11McfNb3QjgF/PZmjW7EEiG7wjgGLzbQc32MOmsCK6C0n16KUbiZbTtgQKW2IkpHRHrXlUf056Irv8+q9nPtBgfrC2ajgs4yniD+XioU/GQ3iSgAAAABJRU5ErkJggg==';
var root = buttons.body;
var host = window.location.hostname;
/* Not in Frames */
if (window.self == window.top) {
switch(true) {
/* Special case sites */
case host.indexOf('youtube') !=-1 :
buttons.hideOnFullScreen();
window.addEventListener("yt-navigate-finish", function(e) { buttons.reLoadStart(e) }, false);
/* body remains 0 height propter full-screen styling */
if (root = document.querySelector("ytd-app #content")) {
buttons.init();
}
else {
buttons.body.addEventListener('yt-page-data-updated', function(e) {
if (root = document.querySelector("ytd-app #content")) {
if (_once++ < 1) buttons.init();
}
}, false);
}
break;
default: buttons.init();
}
}
}());