// ==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 0.0.1.20160528163032
// ==/UserScript==
//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) (default = 30)
var smoothness = 30;
//Scroll sensitivity
//values: anything? (default 1.00)
var sensitivity = 1.00;
//Acceleration sensitivity
//values: anything? (default 1.00)
var acceleration = 1.00;
//Refreshrate setting
//values: 30-144 (default = 60/72/120/144 = same as your monitor hz)
var baserefreshrate = 60;
//Alternative scrolling multiplier
//values: true/false (try to set this to true if scrolling is too slow/doesn't work)
var alternative_sesitivity_multiplier = false;
//CODE STARTS HERE
var minimal_jquery_version = 200;
var DEBUG = false;
var WEBKIT = false;
//this.$ = this.jQuery = jQuery.noConflict(true);
if (window.top != window.self) //don't run on frames or iframes
return;
if (smoothness>100)
{
smoothness = 100;
}
if (baserefreshrate <= 30 || baserefreshrate>144)
{
baserefreshrate = 144;
}
refreshrate = baserefreshrate;
var animationduration = Math.round(1000/refreshrate);
//var relativeratio = Math.round(51-smoothness/2)/100;
var relativeratio = Math.round(1/(1+smoothness)*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.01;
refreshrate = Math.round(refreshrate);
if (DEBUG)
{
console.log(refreshrate);
}
animationduration = Math.round(1000/(refreshrate));
//var relativeratio = Math.round(51-smoothness/2)/100;
relativeratio = Math.round(1/(1+smoothness*refreshrate/baserefreshrate)*100)/100;
}
gameLoop();
function InitSmoothscroll()
{
//LoadConfig();
InitConfigmenu();
var startposition = false;
var targetposition = 0;
var position = 0;
var scrollfocus = ss$('body');
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);
var parentelement = ss$(scrollfocus).parent();
if ( ss$(parentelement))
{
//if (ss$(parentelement).is("textarea") || ss$(scrollfocus).is("textarea"))
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
{
//scrollfocus = ss$('body');
//maxposition = ss$(scrollfocus).height();
return hasvisiblescrollbars;
}
return false;
}
function UpdatePosition(element)
{
gameLoop();
var positiondelta = ss$(element)[0].getAttribute( "positiondelta" );
//var smoothdelta = Math.sqrt(positiondelta*positiondelta*relativeratio);
var smoothdelta = Math.sqrt(positiondelta*positiondelta*relativeratio);
if (positiondelta<0)
{
smoothdelta = smoothdelta*(-1);
}
//var relative = position - ss$(element).scrollTop();
//console.log("smoothdelta:" + smoothdelta);
if (Math.abs( (positiondelta-smoothdelta)) <= 1 )
{
ss$(element).stop();
ss$(element).animate({
scrollTop: '+=' + Math.round(positiondelta)
}, animationduration, "linear", function() {
ss$(element).attr( "positiondelta",0 );
ss$(element)[0].setAttribute( "positiondelta",0 );
if (DEBUG)
{
ss$(element).css( "border", "1px solid red" );
}
});
}
else
{
ss$(element).stop();
ss$(element).animate({
scrollTop: '+=' + Math.round(smoothdelta)
}, animationduration, "linear", function() {
ss$(element).attr( "positiondelta",positiondelta-smoothdelta );
ss$(element)[0].setAttribute("positiondelta",positiondelta-smoothdelta );
UpdatePosition(element);
if (DEBUG)
{
ss$(element).css( "border", "1px solid red" );
}
});
}
}
function MouseScroll (x,y,e) {
scrollfocus = UpdateFocus(x,y);
var positiondelta = 0;
var lastscrolltop = 0;
var parentelement = ss$(scrollfocus).parent();
if ( ss$(parentelement))
{
if (ss$(parentelement).is("textarea") || ss$(scrollfocus).is("textarea"))
{
//return true;
}
else
{
if ( ss$(parentelement).height() < ss$(scrollfocus).height())
{
//maxposition = ss$(scrollfocus).height() - ss$(parentelement).height();
}
else
{
if (ss$(scrollfocus).height()==0)
{
//scrollfocus = ss$('body');
//maxposition = ss$(scrollfocus).height();
}
//scrollfocus = ss$('body');
//return MouseScroll (event);
//return true;
}
}
}
else
{
//scrollfocus = ss$('html');
//maxposition = ss$(scrollfocus).height();
}
var rolled = y;
//console.log("rolled: " + rolled);
//if (ss$(scrollfocus).data("positiondelta" )==undefined)
//$embellishment.data("embellishmentid",1)
var lastscrolltop = ss$(scrollfocus).scrollTop();
ss$(scrollfocus).scrollTop(lastscrolltop+rolled);
if (ss$(scrollfocus).scrollTop()==lastscrolltop)
{
if (!ss$(scrollfocus).is("html"))
{
focus = parentelement;
//focus = UpdateFocus(event);
//return MouseScroll (event);
//console.log("false");
//return false;
}
else
{
//console.log("true");
//return false;
}
}
else
{
e.preventDefault();
}
ss$(scrollfocus).scrollTop(lastscrolltop);
//console.log(scrollfocus);
if (ss$(scrollfocus)[0].getAttribute("positiondelta")==undefined)
{
positiondelta = 0;
//console.log("positiondelta: undefined");
}
else
{
positiondelta = ss$(scrollfocus)[0].getAttribute("positiondelta");
//console.log("positiondelta: " + positiondelta);
}
positiondelta = positiondelta*1;
var direction = rolled/Math.abs(rolled);
//var positiondeltadelta = rolled*sensitivity + Math.sqrt(Math.abs(positiondelta/rolled))*acceleration*rolled;
//var positiondeltadelta = rolled*(sensitivity+Math.sqrt(Math.abs(positiondelta))*acceleration);
//
var positiondeltadelta = Math.round(rolled*sensitivity + Math.sqrt(Math.abs(positiondelta-rolled*sensitivity))*acceleration*rolled*0.03/sensitivity);
positiondelta = positiondelta + positiondeltadelta;
ss$(scrollfocus)[0].setAttribute("positiondelta",positiondelta );
UpdatePosition(ss$(scrollfocus));
//element.innerHTML = "";
//console.log("pos:" + position);
//event.preventDefault();
return true;
}
function canscroll(element, dir)
{
var scrollable = ss$(element);
var lastscrolltop = ss$(scrollable).scrollTop();
ss$(scrollable).scrollTop(lastscrolltop+dir);
if (ss$(scrollable).scrollTop()==lastscrolltop)
{
ss$(scrollable).scrollTop(lastscrolltop);
return false;
}
else
{
ss$(scrollable).scrollTop(lastscrolltop);
return true;
}
}
function UpdateFocus(x,y) {
/*var dir = 0;
if ('wheelDelta' in event) {
dir = event.wheelDelta;
}
else { // Firefox
// The measurement units of the detail and wheelDelta properties are different.
dir = event.detail*(-120);
}*/
var dir = y;
//console.log(dir);
//dir = dir*(-1);
//
var nodelist = document.querySelectorAll( ":hover" );
ss$(nodelist).stop();
//console.log(nodelist);
if (WEBKIT)
{
scrollfocus = ss$('body');
}
else
{
scrollfocus = ss$('html');
}
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);
return newfocus;
}
}
return scrollfocus;
}
ss$('body').bind({
/*
mousewheel: function(e) {
if (DEBUG)
{
console.log(scrollfocus);
}
console.log("scrolling");
MouseScroll(e.originalEvent);
},
*/
mousedown: function(e) {
if (DEBUG)
{
console.log(scrollfocus);
}
if (scrollfocus)
{
ss$(scrollfocus)[0].setAttribute( "positiondelta",0 );
ss$(scrollfocus).stop();
}
//scrollfocus = UpdateFocus(e.originalEvent);
//console.log("click");
}
});
//Init(window);
(function(window,document) {
var prefix = "", _addEventListener, onwheel, support;
// detect event model
if ( window.addEventListener ) {
_addEventListener = "addEventListener";
} else {
_addEventListener = "attachEvent";
prefix = "on";
}
// detect available wheel event
support = "onwheel" in document.createElement("div") ? "wheel" : // Modern browsers support "wheel"
document.onmousewheel !== undefined ? "mousewheel" : // Webkit and IE support at least "mousewheel"
"DOMMouseScroll"; // let's assume that remaining browsers are older Firefox
window.addWheelListener = function( elem, callback, useCapture ) {
_addWheelListener( elem, support, callback, useCapture );
// handle MozMousePixelScroll in older Firefox
if( support == "DOMMouseScroll" ) {
_addWheelListener( elem, "MozMousePixelScroll", callback, useCapture );
}
};
function _addWheelListener( elem, eventName, callback, useCapture ) {
elem[ _addEventListener ]( prefix + eventName, support == "wheel" ? callback : function( originalEvent ) {
!originalEvent && ( originalEvent = window.event );
// create a normalized event object
var event = {
// keep a ref to the original event object
originalEvent: originalEvent,
target: originalEvent.target || originalEvent.srcElement,
type: "wheel",
deltaMode: originalEvent.type == "MozMousePixelScroll" ? 0 : 1,
deltaX: 0,
deltaZ: 0,
preventDefault: function() {
originalEvent.preventDefault ?
originalEvent.preventDefault() :
originalEvent.returnValue = false;
}
};
// calculate deltaY (and deltaX) according to the event
if ( support == "mousewheel" ) {
event.deltaY = - 1/40 * originalEvent.wheelDelta;
// Webkit also support wheelDeltaX
originalEvent.wheelDeltaX && ( event.deltaX = - 1/40 * originalEvent.wheelDeltaX );
} else {
event.deltaY = originalEvent.detail;
}
// it's time to fire the callback
return callback( event );
}, useCapture || false );
}
})(window,document);
addWheelListener( window, function( e ) {
var mul = 1;
if (!WEBKIT || alternative_sesitivity_multiplier)
mul = 40;
//console.log( e.deltaY );
MouseScroll(e.deltaX*mul,e.deltaY*mul, e);
});
/*var oldpos = ss$('body').scrollTop();
ss$('body').scrollTop(1);
var iswebkit = ss$('body').scrollTop()>0;
ss$('body').scrollTop(oldpos);*/
//WEBKIT = 'webkitRequestAnimationFrame' in window;
WEBKIT = document.compatMode == 'CSS1Compat'
//console.log("window: " + window);
console.log("Smoothscroll initiated! Webkit: " + WEBKIT);
}
var JQUERY = false;
var OWNJQUERY = false;
var OLDJQUERY = false;
var old$;
var oldjQuery;
//max retries
var r_max = 10;
var r_count = 0;
function Init(LEGACYWORKAROUND) {
//if (typeof jQuery == 'function') {
//if (typeof jQuery == 'undefined' || typeof $ == 'undefined' )
JQUERY = true;
if (typeof $ == 'undefined')
{
JQUERY = false;
}
else
{
if (typeof old$ == 'undefined') {
old$ = $;
}
}
if (typeof jQuery == 'undefined' )
{
JQUERY = false;
}
else
{
if (typeof oldjQuery == 'undefined') {
oldjQuery = jQuery;
}
}
OWNJQUERY = true;
if (typeof ss$ == 'undefined' )
{
OWNJQUERY = false;
}
if (!OWNJQUERY){
if (JQUERY) {
var versiontable = $.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];
}
if (version<=minimal_jquery_version && LEGACYWORKAROUND)
{
console.log("Page jQuery OLD! Version: " + version);
OLDJQUERY = true;
}
else
{
ss$ = $;
OWNJQUERY = true;
}
}
else
{
console.log("Page jQuery Missing!");
}
}
if (!OWNJQUERY && LEGACYWORKAROUND)
{
console.log("Loading new jQuery...");
//this.$ = this.jQuery = jQuery.noConflict(true);
//var $ = jQuery.noConflict();
//this.$ = this.jQuery = jQuery.noConflict(true);
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);
}
if (r_count<r_max)
{
setTimeout(function() {
Init(LEGACYWORKAROUND);
}, 300);
return 0;
}
else
{
console.log("Failed to load smoothscroll!");
}
r_count++;
}
if (OWNJQUERY) {
var versiontable = ss$.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];
}
console.log("Script jQuery OK! Version: " + version);
InitSmoothscroll();
}
if (LEGACYWORKAROUND) {
console.log("Restoring original jQuery...");
if (typeof old$ != 'undefined') {
$ = old$;
}
if (typeof oldjQuery != 'undefined') {
jQuery = oldjQuery;
}
if (OLDJQUERY)
{
var versiontable = $.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];
}
console.log("Page jQuery OK! Version: " + version);
}
}
console.log("Finished loading smoothscroll!");
}
function LoadConfig()
{
smoothness = GM_getValue( 'smoothness', smoothness );
sensitivity = GM_getValue( 'sensitivity', sensitivity );
acceleration = GM_getValue( 'acceleration', acceleration );
baserefreshrate = GM_getValue( 'baserefreshrate', baserefreshrate );
//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( 'baserefreshrate', document.getElementById('ss-baserefreshrate').value)
//console.log(document.getElementById('ss-alternative_sesitivity_multiplier').checked)
console.log("Config for smoothscroll saved!")
}
function InitConfigmenu()
{
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:</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-baserefreshrate" min="1" max="100" value="' + baserefreshrate + '"></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 = '99';
configbar.style.backgroundColor = 'white';
configparent.insertBefore(configbar, configparent.childNodes[0]);
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 (!OWNJQUERY){
alert("Smoothscroll is not running properly on this page!");
return;
}
//ss$("#ss-config").animate({
// height: '100px'
//}, 100);
ss$("#ss-configbar").show("slow");
}
GM_registerMenuCommand("Configurate smoothscroll", ConfigSmoothscroll);
console.log("Loading smoothscroll...");
Init(true);
//InitSmoothscroll();