// ==UserScript==
// @name Smoothscroll
// @include http*
// @author Creec Winceptor
// @description Smooth scrolling on pages using javascript and jquery
// @namespace https://greasyfork.org/users/3167
// @run-at document-idle
// @grant none
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_registerMenuCommand
// @version 2.0
// ==/UserScript==
if (window.top != window.self) //don't run on frames or iframes
return;
//DEFAULT SETTINGS HERE
//DO NOT CHANGE ANYTHING HERE ANYMORE, USE SCRIPT COMMANDS -> CONFIGURE SMOOTHSCROLL
//Smoothness factor value (how strong the smoothing effect is)
//values: 1-(infinite)
var smoothness = 30;
//Scroll sensitivity
//values: anything? (default 1.00)
var sensitivity = 1;
//Acceleration sensitivity
//values: anything? (default 1.50)
var acceleration = 1;
//Refreshrate setting
//values: 30-144 (default = 60/72/120/144 = same as your monitor hz)
var refreshrate = 60;
//Alternative scrolling multiplier
//values: true/false (try to set this to true if scrolling is too slow/doesn't work)
var alternative_sensitivity_multiplier = false;
//CODE STARTS HERE
var minimal_jquery_version = 200;
//max retries
//var jquery_retry_max = 5;
//var jquery_retry_count = 0;
var DEBUG = false;
var WEBKIT = false;
//this.$ = this.jQuery = jQuery.noConflict(true);
console.log("Loading smoothscroll...");
var baserefreshrate = 60; //DO NOT CHANGE THIS EVER
if (smoothness>100)
{
smoothness = 100;
}
if (refreshrate <= 30 || refreshrate>144)
{
refreshrate = 144;
}
//var animationduration = Math.round(1000/refreshrate*3);
//var relativeratio = Math.round(51-smoothness/2)/100;
//var relativeratio = Math.round(1/(1+smoothness*baserefreshrate/refreshrate)*100)/100;
//var relativeratio = relativeratio;
/*
var lastLoop = new Date;
//var lastrefreshrate = 0;
function gameLoop() {
var thisLoop = new Date;
var refreshrate0 = 1000 / (thisLoop - lastLoop + 1);
lastLoop = thisLoop;
refreshrate = refreshrate + (refreshrate0-refreshrate)*0.1;
refreshrate = Math.round(refreshrate);
if (DEBUG)
{
console.log(refreshrate);
}
//animationduration = Math.round(1000/(refreshrate*3));
//console.log(animationduration);
//var relativeratio = Math.round(51-smoothness/2)/100;
relativeratio = Math.round(1/(1+smoothness*baserefreshrate/refreshrate)*100)/100;
}
gameLoop();*/
function InitSmoothscroll()
{
LoadConfig();
InitConfigmenu();
var startposition = false;
var targetposition = 0;
var position = 0;
//var scrollfocus = //ss$('body');
var scrollfocus = document.body;
var mousemoved = true;
function hasScrollBarVisible(element)
{
//return (document.documentElement.scrollHeight !== document.documentElement.clientHeight);
// Get the computed style of the body element
var cStyle = element.currentStyle||window.getComputedStyle(element, "");
// Check the overflow and overflowY properties for "auto" and "visible" values
var scrollbar1 = cStyle.overflow == "scroll" || cStyle.overflowY == "scroll";
var scrollbar2 = cStyle.overflow == "auto" || cStyle.overflowY == "auto";
var scrollbar = scrollbar1 || scrollbar2;
return scrollbar;
}
function hasscrollbars(scrollfocus)
{
var hasvisiblescrollbars = hasScrollBarVisible(scrollfocus);
return hasvisiblescrollbars;
/*
var parentelement = //ss$(scrollfocus).parent();
if ( //ss$(parentelement))
{
if (//ss$(parentelement).is("textarea") || //ss$(scrollfocus).is("textarea") || //ss$(parentelement).is("article") || //ss$(parentelement).is("article"))
{
return true;
}
else
{
if (//ss$(parentelement).hasClass( "yt-scrollbar" ) || //ss$(scrollfocus).hasClass( "yt-scrollbar" ))
{
return true;
}
return hasvisiblescrollbars;
}
}
else
{
return hasvisiblescrollbars;
}
return false;
*/
}
var scroll_timeout = null;
function UpdatePosition(element)
{
//gameLoop();
var interpolation = 2;
var animationduration = Math.round(1000/(refreshrate*interpolation));
var relativeratio = Math.round(( 1/(1+smoothness)/interpolation*refreshrate/baserefreshrate )*1000)/1000;
var positiondelta = scrollpositiondelta(element);
//console.log(positiondelta);
var smoothdelta = Math.sqrt(positiondelta*positiondelta*relativeratio);
if (positiondelta<0)
{
smoothdelta = smoothdelta*(-1);
}
//console.log(smoothdelta);
//console.log(positiondelta-smoothdelta);
if (Math.abs( (positiondelta-smoothdelta) ) <= 1 )
//if (Math.abs( smoothdelta ) < 1 )
{
/*
//ss$(element).stop();
ss$(element).animate({
scrollTop: '+=' + Math.round(positiondelta)
}, animationduration, "linear", function() {
scrollpositiondelta(element, 0);
if (DEBUG)
{
//ss$(element).css( "border", "1px solid red" );
}
});
*/
scroll_timeout = setTimeout(function() {
element.scrollTop = element.scrollTop + smoothdelta;
scrollpositiondelta(element, 0);
}, animationduration);
}
else
{
/*
//ss$(element).stop();
ss$(element).animate({
scrollTop: '+=' + Math.round(smoothdelta)
}, animationduration, "linear", function() {
scrollpositiondelta(element, positiondelta-smoothdelta);
UpdatePosition(element);
if (DEBUG)
{
//ss$(element).css( "border", "1px solid red" );
}
});
*/
scroll_timeout = setTimeout(function() {
element.scrollTop = element.scrollTop + smoothdelta;
scrollpositiondelta(element, positiondelta - smoothdelta);
UpdatePosition(element);
}, animationduration);
}
}
function MouseScroll (e) {
//gameLoop();
var mul = 1;
if (!WEBKIT || alternative_sensitivity_multiplier)
mul = 40;
var x = e.deltaX*mul;
var y = e.deltaY*mul;
if (mousemoved)
{
scrollfocus = UpdateFocus(x,y);
mousemoved = false;
}
var positiondelta = 0;
var lastscrolltop = 0;
//var parentelement = //ss$(scrollfocus).parent();
var rolled = y;
//var lastscrolltop = //ss$(scrollfocus).scrollTop();
if (!canscroll(scrollfocus, rolled))
{
//if (!//ss$(scrollfocus).is("html"))
if (scrollfocus != document.documentElement)
{
scrollfocus = UpdateFocus(x,y);
return false;
}
else
{
//console.log("true");
return false;
}
}
else
{
e.preventDefault();
}
positiondelta = scrollpositiondelta(scrollfocus);
//console.log("positiondelta = " + positiondelta);
var direction = rolled/Math.abs(rolled);
var positiondeltadelta = Math.round(rolled*sensitivity + Math.sqrt(Math.abs(positiondelta-rolled*sensitivity))*acceleration*rolled*0.09/sensitivity);
positiondelta = positiondelta*1 + positiondeltadelta*1;
//console.log("positiondelta + " + positiondeltadelta + " = " + positiondelta);
scrollpositiondelta(scrollfocus, positiondelta)
//UpdatePosition(//ss$(scrollfocus));
clearTimeout(scroll_timeout);
UpdatePosition(scrollfocus);
return true;
}
function canscroll(element, dir)
{
//console.log(dir);
if (dir>0)
{
dir = 1;
}
if (dir<0)
{
dir = -1;
}
var checkradius = 3; //pixels to try scrolling
var canscroll0 = false;
//var scrollable = //ss$(element);
var scrollable = element;
//var lastscrolltop = //ss$(scrollable).scrollTop();
var lastscrolltop = scrollable.scrollTop;
//ss$(scrollable).scrollTop(lastscrolltop+dir*checkradius);
scrollable.scrollTop = lastscrolltop+dir*checkradius;
//if (//ss$(scrollable).scrollTop()!=lastscrolltop)
if (scrollable.scrollTop!=lastscrolltop)
{
canscroll0 = true;
}
//ss$(scrollable).scrollTop(lastscrolltop);
scrollable.scrollTop = lastscrolltop;
//console.log("canscroll0: " + //ss$(element).prev().prop('nodeName') + " : " + canscroll0);
return canscroll0;
}
function scrollpositiondelta(element, newdelta)
{
//var target = //ss$(element);
var target = element;
var delta = 0;
if (newdelta!=undefined)
{
//console.log(dir);
var dir = 0;
if (newdelta>0)
{
dir = 1;
}
if (newdelta<0)
{
dir = -1;
}
target.setAttribute("positiondelta", newdelta );
//console.log(newdelta);
////ss$(target)[0].setAttribute("positiondelta", newdelta );
//ss$( target ).data( "positiondelta", newdelta );
delta = newdelta;
}
else
{
var olddelta = target.getAttribute("positiondelta");
//var olddelta = //ss$(target)[0].getAttribute("positiondelta");
//var olddelta = //ss$( target ).data( "positiondelta" );
if (olddelta!=undefined)
{
delta = olddelta;
}
}
return delta*1;
}
function UpdateFocus(x,y) {
if (scrollfocus)
{
//scrollpositiondelta(scrollfocus, 0);
//ss$(scrollfocus).stop();
}
var dir = y;
var nodelist = document.querySelectorAll( ":hover" );
////ss$(nodelist).stop();
//console.log(nodelist);
if (WEBKIT)
{
//scrollfocus = //ss$('body');
scrollfocus = document.body;
}
else
{
//scrollfocus = //ss$('html');
scrollfocus = document.documentElement;
}
for (var i = nodelist.length-1; i >= 0 ; i--) {
//var parent = nodelist[i-1];
var newfocus = nodelist[i];
if (DEBUG)
{
//ss$(newfocus).css( "border", "1px solid blue" );
//var debugtimer = setTimeout(function(){ //ss$(newfocus).css( "border", "0px solid white" ); }, 1000);
}
//if (//ss$(newfocus).hasScrollBar3() && hasScrollBarVisible(newfocus) && canscroll(newfocus, dir) && hasscrollbars(newfocus))
if (canscroll(newfocus, dir) && hasscrollbars(newfocus))
{
//scrollfocus = ss$(newfocus);
scrollfocus = newfocus;
return newfocus;
}
}
return scrollfocus;
}
/*ss$('html').bind({
mousemove: function(e) {
mousemoved = true;
},
mousedown: function(e) {
if (DEBUG)
{
console.log(scrollfocus);
}
if (scrollfocus)
{
scrollpositiondelta(scrollfocus, 0);
////ss$(scrollfocus).stop();
}
scrollfocus = UpdateFocus(0,0);
//console.log("click");
}
});
*/
document.documentElement.addEventListener("wheel", function(e){
MouseScroll(e);
mousemoved = true;
//console.log("scrolling");
});
//WEBKIT = 'webkitRequestAnimationFrame' in window;
WEBKIT = document.compatMode == 'CSS1Compat' || document.compatMode == "BackCompat";
//console.log("window: " + window);
console.log("Smoothscroll initiated! Webkit: " + WEBKIT);
$smoothscroll$ = true;
}
/*
var JQUERY = false;
var OWNJQUERY = false;
var OLDJQUERY = false;
var old$;
var oldjQuery;
var ss$;
*/
/*
function jQueryVersion(temp$)
{
if (typeof temp$ == 'undefined' || typeof temp$.fn == 'undefined' || typeof temp$.fn.jquery == 'undefined')
{
return 0;
}
var versiontable = temp$.fn.jquery.split('.');
var version = 0;
for (var i = versiontable.length-1; i >= 0; i--) {
var power = versiontable.length-i;
version += Math.pow(10,power-1)*versiontable[i];
}
return version;
}
*/
function LoadConfig()
{
smoothness = GM_getValue( 'smoothness', smoothness );
sensitivity = GM_getValue( 'sensitivity', sensitivity );
acceleration = GM_getValue( 'acceleration', acceleration );
refreshrate = GM_getValue( 'refreshrate', refreshrate );
//alternative_sesitivity_multiplier = GM_getValue( 'alternative_sesitivity_multiplier', alternative_sesitivity_multiplier );
console.log("Config for smoothscroll loaded!")
}
function SaveConfig()
{
GM_setValue( 'smoothness', document.getElementById('ss-smoothness').value)
GM_setValue( 'sensitivity', document.getElementById('ss-sensitivity').value)
GM_setValue( 'acceleration', document.getElementById('ss-acceleration').value)
GM_setValue( 'refreshrate', document.getElementById('ss-refreshrate').value)
//console.log(document.getElementById('ss-alternative_sesitivity_multiplier').checked)
console.log("Config for smoothscroll saved!")
}
function CloseConfig()
{
var configbar = document.getElementById("ss-configbar");
configbar.style.display = 'none';
}
function InitConfigmenu()
{
console.log("Initiating smoothscroll config...");
var configbar = document.createElement('div');
configbar.setAttribute("id","ss-configbar");
configbar.innerHTML = '<div style="display:block; width: 100%; height: auto; border: 0px solid #aaaaaa; background-color: grey;">Config page for smoothscroll (refresh page for changes to apply!) Made by: Creec Winceptor</div><div id="ss-config" style="margin:3px; display:block; width: auto; height: auto; border: 0px solid #554433;"><table style="width:auto;"><tr><td>Smoothness</td><td><input type="number" id="ss-smoothness" min="0" max="100" value="' + smoothness + '"></td><td> Smoothness factor value (how strong the smoothing effect is)</td></tr><tr><td>Sensitivity</td><td><input type="number" id="ss-sensitivity" min="0" max="100" value="' + sensitivity + '"></td><td> Scroll sensitivity (duh)</td></tr><tr><td>Acceleration</td><td><input type="number" id="ss-acceleration" min="0" max="100" value="' + acceleration + '"></td><td> Acceleration of continuous scroll action (saves your finger)</td></tr><tr><td>Refreshrate</td><td><input type="number" id="ss-refreshrate" min="1" max="100" value="' + refreshrate + '"></td><td>Refreshrate of scrollanimation (60Hz default)</td></tr></table></div><div style="width: 100%; height: auto; text-align: center; background-color: grey;"><input id="ss-save" type="button" value="Save config" style="width: 44%; height: auto; border: 0px solid #aaaaaa; margin: 3px"/><input id="ss-close" type="button" value="Close config" style="width: 44%; height: auto; border: 0px solid #aaaaaa; margin: 3px"/><div>';
var configparent = document.getElementsByTagName("body")[0];
configbar.style.width = '100%';
configbar.style.display = 'none';
configbar.style.position = 'absolute';
configbar.style.zIndex = '9999';
configbar.style.backgroundColor = 'white';
configparent.insertBefore(configbar, configparent.childNodes[0]);
document.getElementById("ss-close").onclick = function() {CloseConfig()};
document.getElementById("ss-save").onclick = function() {SaveConfig()};
//ss$("#ss-close").click(function() {
////ss$("#ss-config").animate({
// height: '0'
//}, 100);
//ss$("#ss-configbar").hide("slow");
//});
//ss$("#ss-save").click(function() {
//SaveConfig();
//});
////ss$("#ss-configbar").hide();
}
function ConfigSmoothscroll()
{
if (typeof $smoothscroll$ == 'undefined'){
alert("Smoothscroll is not running properly on this page!");
return;
}
////ss$("#ss-config").animate({
//height: '100px'
//}, 100);
//ss$("#ss-configbar").show("slow");
var configbar = document.getElementById("ss-configbar");
configbar.style.display = 'block';
//ss$("html, body").animate({ scrollTop: 0 }, "slow");
console.log("opening config...");
}
/*
function LoadjQuery(loading)
{
if (loading)
{
console.log("Waiting for jQuery...");
if (typeof $ == 'undefined' || typeof jQuery == 'undefined' || jQueryVersion($)<minimal_jquery_version)
{
if (jquery_retry_count<jquery_retry_max)
{
jquery_retry_count++;
setTimeout(function() {
LoadjQuery(true);
}, 100);
}
else
{
console.log("Failed to load smoothscroll!");
if (typeof old$ != 'undefined') {
$ = old$;
}
if (typeof oldjQuery != 'undefined') {
jQuery = oldjQuery;
}
}
}
else
{
//ss$ = $;
//ssjQuery = jQuery;
console.log("jQuery loaded!");
if (typeof old$ != 'undefined') {
$ = old$;
}
if (typeof oldjQuery != 'undefined') {
jQuery = oldjQuery;
}
console.log("Page jQuery version: " + jQueryVersion(old$));
console.log("Script jQuery version: " + jQueryVersion(//ss$));
InitSmoothscroll();
}
}
else
{
console.log("Loading own jQuery...");
jquery_retry_count = 0;
var filename = "https://code.jquery.com/jquery-2.2.3.js";
var fileref = document.createElement('script');
fileref.setAttribute("type","text/javascript");
fileref.setAttribute("src", filename);
if (typeof fileref!="undefined")
{
var scriptparent = document.getElementsByTagName("head")[0];
if (typeof scriptparent=="undefined")
{
var scriptparent = document.createElement('head');
document.getElementsByTagName("html")[0].appendChild(scriptparent);
}
scriptparent.appendChild(fileref);
}
LoadjQuery(true);
}
}
*/
/*
function Init()
{
if (typeof $smoothscroll$ == 'undefined' )
{
var sitejquery = !(typeof $ == 'undefined' || typeof jQuery == 'undefined');
if (sitejquery && jQueryVersion($)>=minimal_jquery_version)
{
//ss$ = $;
//ssjQuery = jQuery;
console.log("Reusing page jQuery...");
console.log("Page jQuery version: " + jQueryVersion($));
InitSmoothscroll();
}
else
{
if (typeof $ != 'undefined' && typeof old$ == 'undefined')
{
old$ = $;
}
if (typeof jQuery != 'undefined' && typeof oldjQuery == 'undefined')
{
oldjQuery = jQuery;
}
LoadjQuery(false);
}
}
}
*/
if (typeof $smoothscroll$ == 'undefined'){
console.log("Initiating smoothscroll...");
//Init();
InitSmoothscroll();
GM_registerMenuCommand("Configurate smoothscroll", ConfigSmoothscroll);
}
else
{
console.log("Smoothscroll already loaded!");
}