Repository: gblazex/smoothscroll
Branch: master
Commit: 5098123b2bcb
Files: 8
Total size: 48.8 KB
Directory structure:
gitextract_6eefbn6w/
├── LICENSE
├── README
├── manifest.json
├── pages/
│ ├── background.js
│ ├── options.html
│ └── options.js
└── src/
├── middlemouse.js
└── sscr.js
================================================
FILE CONTENTS
================================================
================================================
FILE: LICENSE
================================================
MIT License
----
Copyright (c) 2010-2018 Balazs Galambosi
The only restriction is to not publish any extension for browsers or
native application without getting a written permission first. Otherwise:
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
================================================
FILE: README
================================================
A Google Chrome extension for smooth scrolling with the mouse wheel and keyboard buttons.
Also available as a Mac app
http://www.smoothscroll.net/mac/
Embedding it into your website
https://github.com/gblazex/smoothscroll-for-websites
Chrome extension
https://chrome.google.com/webstore/detail/smoothscroll/nbokbjkabcmbfdlbddjidfmibcpneigj
Opera extension
https://addons.opera.com/extensions/details/smoothscroll-3/
Features
- Picasa-like smooth scrolling
- Mouse wheel, middle mouse and keyboard support
- Arrow keys, PgUp/PgDown, Spacebar, Home/End
- Customizable step sizes, frames per second and more...
- Works with embedded content (PDF, flash)
- Full touchpad support
- Excluded pages list
People involved
- Balazs Galambosi (maintainer)
- Michael Herf (pulse algorithm)
================================================
FILE: manifest.json
================================================
{
"background": {
"scripts": [ "pages/background.js" ],
"persistent": false
},
"content_scripts": [ {
"all_frames": true,
"js": [ "src/sscr.js", "src/middlemouse.js" ],
"matches": [ "http://*/*", "https://*/*", "ftp://*/*" ],
"exclude_globs": ["*.pdf*"],
"run_at": "document_start"
} ],
"description": "Scroll smoothly on all websites with your mouse and keyboard.",
"icons": {
"128": "img/128n.png",
"16": "img/16n.png",
"32": "img/32n.png",
"48": "img/48n.png"
},
"key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQD1YCUodXSIZuDNa4PJGDCzSWnJAsPymJLQNj+fbxNHbE8BJ4x062dCB0rg0ovXXjNjGJW5FUX+aIEdhh1oNpouWkfu0GP6D6VXCrArXS1hKa7mV8jrBSuMLQo/aU3X7iieqkDzeSFRwUaAEp54C62J22sJ06EHI1QMLuCJ6C9lVQIDAQAB",
"name": "SmoothScroll",
"options_page": "pages/options.html",
"version": "1.5.10",
"manifest_version": 2,
"permissions": ["storage", "http://*/*", "https://*/*"],
"web_accessible_resources": [ "img/cursor.png" ]
}
================================================
FILE: pages/background.js
================================================
var defaultOptions = {
// Plugin
middleMouse : true,
// Scrolling Core
framerate : 150, // [Hz]
animationTime : 400, // [px]
stepSize : 120, // [px]
// Pulse (less tweakable)
// ratio of "tail" to "acceleration"
pulseAlgorithm : true,
pulseScale : 4,
pulseNormalize : 1,
// Acceleration
accelerationDelta : 20, // 20
accelerationMax : 1, // 1
// Keyboard Settings
keyboardSupport : true, // option
arrowScroll : 50, // [px]
// Other
touchpadSupport : true,
fixedBackground : true,
excluded : "example.com, another.example.com"
}
// Fired when the extension is first installed,
// when the extension is updated to a new version,
// and when Chrome is updated to a new version.
chrome.runtime.onInstalled.addListener(init);
function init(details) {
if (details.reason == "install") {
chrome.storage.sync.set(defaultOptions);
var optionsPage = chrome.runtime.getURL("pages/options.html");
chrome.tabs.create({ url: optionsPage });
chrome.tabs.query({}, function (tabs) {
tabs.forEach(addSmoothScrollToTab);
});
}
}
function addSmoothScrollToTab(tab) {
chrome.tabs.executeScript(tab.id, {
file: "src/sscr.js",
allFrames: true
});
chrome.tabs.executeScript(tab.id, {
file: "src/middlemouse.js",
allFrames: true
});
}
================================================
FILE: pages/options.html
================================================
<!DOCTYPE html>
<html>
<head><title>SmoothScroll Options</title>
<meta charset=utf-8 />
<style type="text/css">
body {
background:#fdfdfd;
font-size: 13.4px;
font-family: Verdana, sans-serif;
color: #333333;
margin: 0;
zoom:1.35;
}
a { text-decoration: none; color: #333333; border-bottom:1px dotted #ccc; }
a:hover { text-decoration: none; color: #0CF; border-bottom:1px solid #0CF; }
hr { margin:0; }
h1 { text-shadow: transparent 0 0 1px; }
h2 { font-size: 16px; color: #00CCFF; margin:20px 0 15px 0; }
li { padding: 2px 0; }
#page {
/*border: 3px solid #28AEA8;*/
background: #fff;
margin:0 auto 10px;
width: 800px;
text-align: left;
padding: 30px;
-webkit-border-radius: 20px;
/*-webkit-box-shadow: #BBB 0px 0px 20px;*/
-webkit-box-shadow: #ddd -2px 2px 10px;
}
#title {
margin: 30px auto;
font-size: 40px;
background-image:url(../img/128n.png);
width: 400px;
padding-left: 140px;
background-repeat: no-repeat;
background-position: 0% 50%;
text-align: left;
font-weight: bold;
text-shadow: transparent 0 0 1px; /*anit-alias*/
}
#title a { color: #30BCFF /*#3c78d7*/; border:0; }
#title a:hover { color:#0CF; }
input[type=text] {
color: #010101;
background-color: transparent;
padding: 3px;
outline: none;
text-align: center;
border: 1px solid #bbb;
width: 38px;
margin-right:10px;
-webkit-border-radius: 4px;
-webkit-box-shadow: #CCC 0px 0px 2px;
}
textarea {
padding:10px;
width: 770px;
border: 1px solid #DDD;
-webkit-border-radius: 8px;
color: #777;
}
textarea:focus { color:#222; }
#save {
font-size:16px;
cursor:pointer;
width:160px;
margin:0 auto;
display:block;
}
button {
font-weight:bold;
border:1px solid #ccc;
background:#f6f6f6;
color:#777;
text-shadow:1px 1px 0 white;
padding:5px 10px;
border-radius:5px;
background-image:-webkit-gradient(linear,left top,left bottom,from(white),to(#EFEFEF));
-webkit-transition: all 0.15s ease-out;
}
button:hover {
color:#555;
box-shadow:0 0 3px #999;
}
button:active {
background: #DDD;
background-image: -webkit-gradient(linear,left top,left bottom,from(#CCC),to(white));
}
/*.default_value { display:none; }*/
.note {
font-size: 80%;
color: #666;
padding-left: 10px;
cursor:help;
}
label[title], .help { cursor:help; }
.setting-name { width: 150px; }
.dialog {
text-align:center;
display:none;
opacity: 0;
-webkit-transition: opacity 1s ease-in-out;
background-color: #111;
color: #fff;
position: fixed;
padding: 30px;
bottom: 0;
left: 50%;
margin-left:-260px;
width:460px;
border-radius: 8px 8px 0 0;
}
.dialog-header {
color: #999;
font: bold 20px 'arial black', arial;
}
.dialog a {
color: white;
font-weight: bold;
text-decoration: underline;
border:0;
}
#footer {
margin-top: 40px;
border-top: 20px solid #f6f6f6;
}
#footer li {
margin:5px 0;
}
#test { width:800px; margin:0 auto; }
#test div { margin:20px 0; }
#version-container {
color: #222;
font-size: 22px;
font-weight: normal;
margin-top: 5px; font-style:italic;
width:300px;
height:10px;
background:#ececec;
text-align:center;
}
#version {
background:#fdfcfc;
color:#999;
padding:0 8px;
position:relative;
top:-8px;
}
#thanks {
text-align: center;
margin: 30px 0 0;
}
</style>
<script src="../src/sscr.js"></script>
<script src="../src/middlemouse.js"></script>
</head>
<body>
<div id="title">
<a href="https://chrome.google.com/webstore/detail/smoothscroll/nbokbjkabcmbfdlbddjidfmibcpneigj" target="_blank">SmoothScroll</a>
<div id="version-container"><span id="version"></span></div>
</div>
<!-- Fork me on GitHub -->
<a href="https://github.com/galambalazs/smoothscroll"><img style="position: absolute; top: 0; left: 0; border: 0;" src="../img/forkme.png" alt="Fork me on GitHub"></a>
<div id="page">
<h2 style="margin-top:0;">Scroll Settings</h2>
<!-- -->
<div id="profiles">
<i class="help "title="Don't forget to hit 'Save' if you want to use them!">Load profile:</i>
<button id="custom">Custom</button>
<button id="default">Default</button>
<button id="iphone">iPhone</button>
<button id="opera">Opera</button>
<button id="ie9">IE</button>
</div>
<br />
<table border="0" cellpadding="0" cellspacing="2" style="font-size: 12px; margin-left: 6px">
<tr>
<td class="setting-name"><label for="stepSize" title="higher means more scrolling for each mouse event">Step size [px]</label></td>
<td><input id="stepSize" type="text" size="1" maxlength="3"> </td>
<td class="default_value"><span style="color: #00CCFF; font-size: 12px;"> <i>Default value: 100</i></span></td>
<td class="note" title="higher means more scrolling for each mouse event">(<b>how</b> much)</td>
</tr>
<tr>
<td class="setting-name"><label for="animationTime" title="higher number stretches animation">Animation time [ms]</label></td>
<td><input id="animationTime" type="text" size="1" maxlength="3"> </td>
<td class="default_value"><span style="color: #00CCFF; font-size: 12px;"> <i>Default value: 400</i></span></td>
<td class="note" title="higher number stretches animation">(<b>how</b> quick)</td>
</tr>
<tr>
<td class="setting-name"><label for="accelerationMax" title="how much scrolling can accelerate">Acceleration scale</label></td>
<td><input id="accelerationMax" type="text" size="1" maxlength="3"></td>
<td class="default_value"><span style="color: #00CCFF; font-size: 12px;"> <i>Default value: 3</i></span></td>
<td class="note" title="how much scrolling can accelerate">(<b>how </b>swift)</td>
</tr>
<tr>
<td class="setting-name"><label for="accelerationDelta" title="lower means acceleration kicks in more frequently">Acceleration delta [ms]</label></td>
<td><input id="accelerationDelta" type="text" size="1" maxlength="2"></td>
<td class="default_value"><span style="color: #00CCFF; font-size: 12px;"> <i>Default value: 50</i></span></td>
<td class="note" title="lower means acceleration kicks in more frequently">(<b>time</b> between scroll events)</td>
</tr>
<tr>
<td class="setting-name"><label for="pulseScale" title="it affects how long the decceleration part is">Pulse Scale</label></td>
<td><input id="pulseScale" type="text" size="1" maxlength="2"></td>
<td class="default_value"><span style="color: #00CCFF; font-size: 12px;"> <i>Default value: 4</i></span></td>
<td class="note" title="it affects how long the decceleration part is">(<b>ratio </b>of "tail" to "acceleration")</td>
</tr>
<tr>
<td style="width:180px;"><label for="arrowScroll" title="higher means more scrolling for each arrow key press">Arrow key step size [px]</label></td>
<td><input id="arrowScroll" type="text" size="1" maxlength="3"> </td>
<td class="default_value" ><span style="color: #00CCFF; font-size: 12px;"> <i>Default value: 50</i></span></td>
</tr>
</table>
<br>
<h2>Features</h2>
<input type="checkbox" id="keyboardSupport"><label for="keyboardSupport">Enable <b>keyboard support</b></label><br><br>
<input type="checkbox" id="touchpadSupport"><label for="touchpadSupport">Enable <b>touchpad support</b></label><br><br>
<input type="checkbox" id="pulseAlgorithm"><label for="pulseAlgorithm" title="adds easing to the animations">Enable <b>pulse algorithm</b></label><br><br>
<input type="checkbox" id="middleMouse"><label for="middleMouse" title="scroll smooth with the middle mouse button">Enable <b>middle mouse</b> scrolling</label><br><br>
<input type="checkbox" id="fixedBackground"><label for="fixedBackground" title="disabling it improves performance on sites like twitter.com">Enable <b>fixed backgrounds</b></label><br><br>
<table border="0" cellpadding="0" cellspacing="2" style="font-size: 12px; ">
<!--
<tr>
<td style="width:200px;"><label for="pgscroll">PgUp/PgDown step size [px]</label></td>
<td><input id="pgscroll" type="text" size="1" maxlength="4"> </td>
<td><span style="color: #00CCFF; font-size: 12px;"> <i>Default value: 800</i></span></td>
</tr>
-->
</table>
<br>
<h2><label for="excluded">Excluded pages</label></h2>
<textarea id="excluded" cols="70" rows="5"></textarea>
<br><br>
<button id="save">Save Settings</button>
<div id="thanks">If you <strong><i>love</i></strong> this extension please consider
<a href="https://chrome.google.com/webstore/detail/smoothscroll/nbokbjkabcmbfdlbddjidfmibcpneigj/reviews" style="color:#00CCFF;font-weight:bold" target="_blank">writing a great review</a>
</div>
<!--
<div style="text-align:center; height: 30px;margin:20px 0 5px;">
<div id="saving" style="color: #00CCFF; font-size: 12px; font-weight: bold;"> </div>
<div id="message" style="color: #00CCFF; font-size: 11px; font-style:italic;"></div>
</div>
-->
<div id="footer">
<div>
<h2>Links</h2>
<ul>
<li><a href="https://github.com/galambalazs/smoothscroll/"
target="_blank">Bug Report, Known Issues, Source Code...</a></li>
<li><a href="https://chrome.google.com/webstore/detail/smoothscroll/nbokbjkabcmbfdlbddjidfmibcpneigj"
target="_blank"> Extension Homepage</a></li>
</ul>
</div>
<div>
<h2>People involved</h2>
<ul>
<li><a href="mailto:galambalazs@yahoo.co.uk">Balázs Galambosi <b>(</b>developer<b>)</b></a></li>
<li><a href="http://stereopsis.com/stopping/">Michael Herf's pulse algorithm is used</a></li>
</ul>
</div>
</div>
</div> <!-- /#page -->
<br><br><br><br><br><br>
<div class="dialog">
<div class="dialog-header">Settings saved</div>
<div class="dialog-content">You can now test the new scroll settings!</div>
</div>
<div id="test">
<h1>Test your settings!</h1>
<div>
<h2>By scrolling on this long page.</h2>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum non orci ipsum, eu laoreet nisl. Suspendisse ut dictum neque. Pellentesque nulla ipsum, posuere ut mattis vel, dapibus fringilla arcu. Donec fermentum purus in risus convallis semper. Nullam tempor urna eu libero vulputate vitae pulvinar enim accumsan. Donec vitae leo a enim faucibus consectetur. Nullam lectus justo, eleifend quis condimentum ut, pharetra et risus. Ut luctus molestie nibh sit amet vestibulum. Vestibulum tempor luctus scelerisque. Duis posuere elementum mollis. Cras sit amet varius massa. Nulla condimentum feugiat aliquam. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Aenean id ante et diam lacinia convallis vel eu magna.</p>
<p>In congue, risus sed commodo molestie, tortor sem dictum neque, in viverra sapien arcu vulputate velit. Pellentesque nec dignissim neque. Proin non felis quis lectus fermentum lobortis ut nec purus. Nam quam turpis, sodales in pellentesque vel, tempor gravida lacus. Pellentesque euismod aliquet pulvinar. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Sed tempus velit in lacus tristique facilisis. Mauris commodo, nunc et tempor pharetra, lectus dui posuere odio, eu auctor lorem augue non neque. Aenean auctor velit ac erat lobortis iaculis. Nullam in nibh et sem porttitor malesuada non non urna. Nulla lacus est, aliquet vel egestas a, accumsan a ante. Sed semper molestie dictum. Morbi at quam non enim porttitor varius. Vivamus ut metus et enim egestas ornare malesuada at augue.</p>
<p>Morbi laoreet velit sit amet metus tincidunt hendrerit. Aliquam augue ante, venenatis a rutrum sit amet, tristique non urna. Suspendisse et euismod nunc. Donec tempus luctus convallis. Proin blandit justo in nulla volutpat imperdiet. Integer dapibus lectus vitae libero hendrerit commodo. Praesent felis metus, pretium id tempus sed, porttitor ut justo. Praesent dapibus massa a erat congue malesuada. Fusce at erat in augue lobortis ultrices. Fusce vitae nunc sed arcu viverra convallis. Etiam nec magna turpis, id iaculis dui. Etiam at interdum est. Ut quis eros justo, vel consequat dolor. Proin pulvinar tristique congue. Mauris consectetur aliquam ipsum lobortis tempor. In non ligula orci, a commodo turpis. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Integer non tempus lectus. Praesent tristique ante a purus lobortis viverra.</p>
<p>Curabitur eget leo velit, ac sagittis arcu. Mauris pulvinar orci et libero ultricies non iaculis velit gravida. Nulla facilisi. Pellentesque vel tellus nisl, et eleifend turpis. Vestibulum accumsan tincidunt tellus ac posuere. Nunc et orci metus. Cras quis lacus at tortor semper sollicitudin. Sed mi velit, semper et eleifend eget, faucibus mattis velit. Quisque interdum, risus et vulputate cursus, mauris elit fermentum nunc, in imperdiet ipsum odio sed magna. Vivamus porttitor ornare lacus in congue. Morbi et justo nulla, non tincidunt eros. Nulla scelerisque, tortor eget pulvinar volutpat, ante justo dapibus arcu, eu lacinia quam ante ut libero. Integer porta libero eu est scelerisque malesuada. Sed risus diam, pellentesque eu euismod sit amet, rhoncus quis velit. Aliquam et vehicula odio. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Pellentesque nec magna nec odio venenatis sollicitudin non id massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Ut in eleifend mauris.</p>
<p>Sed ac scelerisque nisl. Vivamus ac diam et nibh pulvinar gravida vitae a magna. Fusce ipsum ligula, suscipit sit amet ullamcorper a, fringilla sed neque. Curabitur ipsum dolor, consectetur condimentum luctus nec, molestie eu nulla. Curabitur eros nunc, varius at vestibulum sed, tincidunt aliquam risus. Aenean ultricies iaculis augue. Sed tempor scelerisque velit, et dignissim erat hendrerit gravida. Mauris consectetur porta lacus vel posuere. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut diam diam, tempus mattis interdum nec, tincidunt in ante. Pellentesque lacus ipsum, eleifend non imperdiet id, dapibus vel metus. Proin egestas sagittis urna facilisis congue. Fusce nec dapibus nibh.</p>
<div>
</div>
<script src="options.js"></script>
</body>
</html>
================================================
FILE: pages/options.js
================================================
/**
* Options page logic.
*/
(function(window, undefined){
/**
* List of available options
*/
var optionsList = [
'animationTime',
'stepSize',
'arrowScroll',
'middleMouse',
'accelerationMax',
'accelerationDelta',
'pulseAlgorithm',
'pulseScale',
'keyboardSupport',
'touchpadSupport',
'excluded',
'fixedBackground'
];
var options = {};
function byId(id) { return document.getElementById(id); }
function byClass(cname) { return document.getElementsByClassName(cname); }
function byTag(tag,base) { return (base||document).getElementsByTagName(tag||'*'); }
function isNodeName(el, tag) {
return el.nodeName.toLowerCase() === tag.toLowerCase();
}
function show(elem, newop) {
elem.style.display = "block";
elem.style.webkitTransition = "opacity 0.2s ease-in-out";
setTimeout(function(){
elem.style.opacity = (newop || 1);
}, 0);
}
function hide(elem, newop) {
elem.style.webkitTransition = "opacity 1s ease-in-out";
elem.style.opacity = (newop || 0);
setTimeout(function(){
elem.style.display = "none";
}, 1000);
}
function isCheckbox(key) {
var re = /^(?:keyboardSupport|touchpadSupport|middleMouse|pulseAlgorithm|fixedBackground)$/;
return re.test(key);
}
function init() {
chrome.storage.sync.get(optionsList, initWithOptions);
}
/**
* Fills up the form with the saved values from local storage.
*/
function initWithOptions(optionsSynced) {
options = optionsSynced;
// settings were updated -> show dialog
if (localStorage.saved == 'true') {
var dialog = byClass('dialog')[0];
show(dialog, 0.9);
setTimeout(function () {
hide(dialog);
}, 3000);
}
// updated complete
localStorage.saved = 'false';
// fill the form fields from storage
for (var key in options) {
if (isCheckbox(key)) {
byId(key).checked = options[key];
} else if (options[key]) {
byId(key).value = options[key];
}
}
}
/**
* Saves the values from the form to local storage.
*/
function save() {
var i, key, opt, elem, error, options = {};
// save options to the local storage
optionsList.forEach(function(key, i) {
// <input type="text"> and <textarea>
if (!isCheckbox(key)) {
elem = byId(key);
opt = elem.value;
// every <input>
if (isNodeName(elem, "input")) {
// should be a number
opt = parseFloat(opt, 10);
if (isNaN(opt)) {
error = "Numeric Values Only!";
return; // stop iteration
}
}
options[key] = opt;
} else { // checkbox
options[key] = byId(key).checked;
}
});
// update message
if (!error) {
chrome.storage.sync.set(options, function(){
localStorage.saved = 'true';
reload();
});
}
// error message
else {
alert(error);
}
}
function get_manifest(callback) {
var xhr = new XMLHttpRequest();
xhr.onload = function () {
callback(JSON.parse(xhr.responseText));
};
xhr.open('GET', '../manifest.json', true);
xhr.send(null);
}
get_manifest(function (manifest) {
version = manifest.version;
byId("version").innerHTML = version;
});
function reload() {
window.location.reload();
}
var profiles = {
'_custom': {
'animationTime': 160,
'stepSize': 120,
'pulseAlgorithm': 'true',
'pulseScale': 4
},
'_default': {
'animationTime': 400,
'stepSize': 100,
'pulseAlgorithm': 'true',
'pulseScale': 4
},
'_iphone': {
'animationTime': 600,
'stepSize': 120,
'pulseAlgorithm': 'true',
'pulseScale': 3
},
'_opera': {
'animationTime': 120,
'stepSize': 120,
'pulseAlgorithm': 'false'
},
'_ie9': {
'animationTime': 60,
'stepSize': 120,
'pulseAlgorithm': 'false'
}
};
// TODO: merge with init
function setProfile(profile) {
if ('custom' == profile){
init();
return;
}
profile = profiles['_'+profile];
// set
for (var key in profile) {
if (isCheckbox(key)) {
byId(key).checked = (profile[key] == "true");
} else if (options[key]) {
byId(key).value = profile[key];
}
};
}
// Restores select box state to saved value from storage.
// function restore_options() {}
function generateTest() {
var test = byId('test');
var el = byTag('div', test)[0];
for (var i = 5; i--;) {
test.appendChild(el.cloneNode(true));
}
}
byId('profiles').onclick = function(e) {
if (e.target.id && e.target.nodeName == 'BUTTON') {
setProfile(e.target.id);
}
};
byId('save').onclick = save;
// public interface
init();
window.addEventListener("DOMContentLoaded", generateTest, false);
window.reload = reload;
window.save = save;
window.setProfile = setProfile;
})(window);
================================================
FILE: src/middlemouse.js
================================================
//
// SmoothScroll (Balazs Galambosi)
// Licensed under the terms of the MIT license.
// The only restriction would be not to publish any
// extension for browsers or native application
// without getting a written permission first.
//
/**
* A module for middle mouse scrolling.
*/
(function (window) {
var defaultOptions = {
middleMouse : false,
frameRate : 200
};
var options = defaultOptions;
var img = document.createElement("div"); // img at the reference point
var scrolling = false; // guards one phase
// we check the OS for default middle mouse behavior only!
var isLinux = (navigator.platform.indexOf("Linux") != -1);
// get global settings
chrome.storage.sync.get(defaultOptions, function (syncedOptions) {
options = syncedOptions;
// leave time for the main script to check excluded pages
setTimeout(function() {
// if we shouldn't run, stop listening to events
if (isExcluded && !options.middleMouse) {
cleanup();
}
}, 10);
});
/**
* Initializes the image at the reference point.
*/
function init() {
var url = chrome.extension.getURL("../img/cursor.png");
var style = img.style;
style.background = "url("+url+") no-repeat";
style.position = "fixed";
style.zIndex = "1000";
style.width = "20px";
style.height = "20px";
new Image().src = url; // force download
}
/**
* Removes event listeners and other traces left on the page.
*/
function cleanup() {
removeEvent("mousedown", mousedown);
}
/**
* Shows the reference image, and binds event listeners for scrolling.
* It also manages the animation.
* @param {Object} event
*/
function mousedown(e) {
// use default action if we're disabled
// or it's not the midde mouse button
if (!options.middleMouse || e.button !== 1) {
return true;
}
var isLink = false;
var elem = e.target;
// linux middle mouse shouldn't be overwritten (paste)
var isLinuxInput = (isLinux && (/input|textarea/i.test(elem.nodeName) ||
elem.isContentEditable));
do {
isLink = isNodeName(elem, "a");
if (isLink) break;
} while ((elem = elem.parentNode));
elem = overflowingAncestor(e.target);
// if it's being used on an <a> element
// take the default action
if (!elem || isLink || isLinuxInput) {
return true;
}
// we don't want the default by now
e.preventDefault();
// quit if there's an ongoing scrolling
if (scrolling) {
return false;
}
// set up a new scrolling phase
scrolling = true;
// reference point
img.style.left = e.clientX - 10 + "px";
img.style.top = e.clientY - 10 + "px";
document.body.appendChild(img);
var refereceX = e.clientX;
var refereceY = e.clientY;
var speedX = 0;
var speedY = 0;
// animation loop
var last = dateNow();
var delay = 1000 / options.frameRate;
var finished = false;
window.requestAnimationFrame(function step(time) {
var now = dateNow();
var elapsed = now - last;
// NOTE: later can also use document.scrollingElement
if (elem == document.body) {
window.scrollBy(speedX * elapsed, speedY * elapsed);
} else {
elem.scrollLeft += (speedX * elapsed) >> 0;
elem.scrollTop += (speedY * elapsed) >> 0;
}
last = now;
if (!finished) {
window.requestAnimationFrame(step);
}
}, elem, delay);
var firstMove = true;
function mousemove(e) {
var deltaX = Math.abs(refereceX - e.clientX);
var deltaY = Math.abs(refereceY - e.clientY);
var movedEnough = Math.max(deltaX, deltaY) > 10;
if (firstMove && movedEnough) {
addEvent("mouseup", remove);
firstMove = false;
}
speedX = (e.clientX - refereceX) * 10 / 1000;
speedY = (e.clientY - refereceY) * 10 / 1000;
}
function remove(e) {
removeEvent("mousemove", mousemove);
removeEvent("mousedown", remove);
removeEvent("mouseup", remove);
removeEvent("keydown", remove);
document.body.removeChild(img);
scrolling = false;
finished = true;
}
addEvent("mousemove", mousemove);
addEvent("mousedown", remove);
addEvent("keydown", remove);
}
/**
* performance.now with fallback
*/
var dateNow = (function () {
return (window.performance && performance.now)
? function () { return performance.now(); }
: function () { return Date.now(); };
})();
addEvent("mousedown", mousedown);
addEvent("DOMContentLoaded", init);
})(window);
================================================
FILE: src/sscr.js
================================================
//
// SmoothScroll (Balazs Galambosi)
// Licensed under the terms of the MIT license.
// The only restriction would be not to publish any
// extension for browsers or native application
// without getting a written permission first.
//
// Scroll Variables (tweakable)
var defaultOptions = {
// Scrolling Core
frameRate : 150, // [Hz]
animationTime : 400, // [px]
stepSize : 100, // [px]
// Pulse (less tweakable)
// ratio of 'tail' to 'acceleration'
pulseAlgorithm : true,
pulseScale : 4,
pulseNormalize : 1,
// Acceleration
accelerationDelta : 50, // 20
accelerationMax : 3, // 1
// Keyboard Settings
keyboardSupport : true, // option
arrowScroll : 50, // [px]
// Other
touchpadSupport : false,
fixedBackground : true,
excluded : ''
};
var options = defaultOptions;
// Other Variables
var isExcluded = false;
var isFrame = false;
var direction = { x: 0, y: 0 };
var initDone = false;
var root = document.documentElement;
var activeElement;
var observer;
var deltaBuffer = [];
var deltaBufferTimer;
var isMac = /^Mac/.test(navigator.platform);
var isWin = /Windows/i.test(navigator.userAgent);
var key = { left: 37, up: 38, right: 39, down: 40, spacebar: 32,
pageup: 33, pagedown: 34, end: 35, home: 36 };
var arrowKeys = { 37: 1, 38: 1, 39: 1, 40: 1 };
/***********************************************
* SETTINGS
***********************************************/
chrome.storage.sync.get(defaultOptions, function (syncedOptions) {
options = syncedOptions;
// it seems that sometimes settings come late
// and we need to test again for excluded pages
initTest();
});
/***********************************************
* INITIALIZE
***********************************************/
/**
* Tests if smooth scrolling is allowed. Shuts down everything if not.
*/
function initTest() {
// disable keyboard support if the user said so
if (!options.keyboardSupport) {
removeEvent('keydown', keydown);
}
// disable everything if the page is blacklisted
if (options.excluded) {
var domains = options.excluded.split(/[,\n] ?/);
domains.push('mail.google.com'); // exclude Gmail for now
domains.push('play.google.com/music'); // problem with Polymer elements
for (var i = domains.length; i--;) {
if (document.URL.indexOf(domains[i]) > -1) {
isExcluded = true;
cleanup();
return;
}
}
}
}
/**
* Sets up scrolls array, determines if frames are involved.
*/
function init() {
if (initDone || isExcluded || !document.body) {
return;
}
initDone = true;
var body = document.body;
var html = document.documentElement;
var windowHeight = window.innerHeight;
var scrollHeight = body.scrollHeight;
// check compat mode for root element
root = (document.compatMode.indexOf('CSS') >= 0) ? html : body;
activeElement = body;
// Checks if this script is running in a frame
if (top != self) {
isFrame = true;
}
// TODO: check if clearfix is still needed
else if (scrollHeight > windowHeight &&
(body.clientHeight + 1 < body.scrollHeight &&
html.clientHeight + 1 < html.scrollHeight)) {
if (root.offsetHeight <= windowHeight) {
var clearfix = document.createElement('div');
clearfix.style.clear = 'both';
body.appendChild(clearfix);
}
}
// disable fixed background
if (!options.fixedBackground && !isExcluded) {
body.style.backgroundAttachment = 'scroll';
html.style.backgroundAttachment = 'scroll';
}
if (!isFrame) {
addEvent('message', function (e) {
if (e.data.SS == 'SmoothScroll') {
var wheelEvent = e.data;
wheelEvent.target = getFrameByEvent(e);
wheel(wheelEvent);
}
});
}
}
function getFrameByEvent(event) {
var iframes = document.getElementsByTagName('iframe');
return [].filter.call(iframes, function(iframe) {
return iframe.contentWindow === event.source;
})[0];
}
/**
* Removes event listeners and other traces left on the page.
*/
function cleanup() {
observer && observer.disconnect();
removeEvent(wheelEvent, wheel);
removeEvent('mousedown', mousedown);
removeEvent('keydown', keydown);
}
/**
* Make sure we are the last listener on the page so special
* key event handlers (e.g for <video>) can come before us
*/
function loaded() {
setTimeout(function () {
init();
if (options.keyboardSupport) {
removeEvent('keydown', keydown);
addEvent('keydown', keydown);
}
}, 1);
}
/************************************************
* SCROLLING
************************************************/
var que = [];
var pending = null;
var lastScroll = Date.now();
/**
* Pushes scroll actions to the scrolling queue.
*/
function scrollArray(elem, left, top) {
directionCheck(left, top);
if (options.accelerationMax != 1) {
var now = Date.now();
var elapsed = now - lastScroll;
if (elapsed < options.accelerationDelta) {
var factor = (1 + (50 / elapsed)) / 2;
if (factor > 1) {
factor = Math.min(factor, options.accelerationMax);
left *= factor;
top *= factor;
}
}
lastScroll = Date.now();
}
// push a scroll command
que.push({
x: left,
y: top,
lastX: (left < 0) ? 0.99 : -0.99,
lastY: (top < 0) ? 0.99 : -0.99,
start: Date.now()
});
// don't act if there's a pending frame loop
if (pending) {
return;
}
var scrollRoot = getScrollRoot();
var isWindowScroll = (elem === scrollRoot || elem === document.body);
// if we haven't already fixed the behavior,
// and it needs fixing for this sesh
if (elem.$scrollBehavior == null && isScrollBehaviorSmooth(elem)) {
elem.$scrollBehavior = elem.style.scrollBehavior;
elem.style.scrollBehavior = 'auto';
}
var step = function (time) {
var now = Date.now();
var scrollX = 0;
var scrollY = 0;
for (var i = 0; i < que.length; i++) {
var item = que[i];
var elapsed = now - item.start;
var finished = (elapsed >= options.animationTime);
// scroll position: [0, 1]
var position = (finished) ? 1 : elapsed / options.animationTime;
// easing [optional]
if (options.pulseAlgorithm) {
position = pulse(position);
}
// only need the difference
var x = (item.x * position - item.lastX) >> 0;
var y = (item.y * position - item.lastY) >> 0;
// add this to the total scrolling
scrollX += x;
scrollY += y;
// update last values
item.lastX += x;
item.lastY += y;
// delete and step back if it's over
if (finished) {
que.splice(i, 1); i--;
}
}
if (window.devicePixelRatio) {
//scrollX /= (window.devicePixelRatio;
//scrollY /= window.devicePixelRatio;
}
// scroll left and top
if (isWindowScroll) {
window.scrollBy(scrollX, scrollY);
}
else {
if (scrollX) elem.scrollLeft += scrollX;
if (scrollY) elem.scrollTop += scrollY;
}
// clean up if there's nothing left to do
if (!left && !top) {
que = [];
}
if (que.length) {
pending = window.requestAnimationFrame(step);
} else {
pending = null;
// restore default behavior at the end of scrolling sesh
if (elem.$scrollBehavior != null) {
elem.style.scrollBehavior = elem.$scrollBehavior;
elem.$scrollBehavior = null;
}
}
};
// start a new queue of actions
pending = window.requestAnimationFrame(step);
}
/***********************************************
* EVENTS
***********************************************/
/**
* Mouse wheel handler.
* @param {Object} event
*/
function wheel(event) {
if (!initDone) {
init();
}
var target = event.target;
// leave early if default action is prevented
// or it's a zooming event with CTRL
if (event.defaultPrevented || event.ctrlKey) {
return true;
}
// leave embedded content alone (flash & pdf)
if (isNodeName(activeElement, 'embed') ||
(isNodeName(target, 'embed') && /\.pdf/i.test(target.src)) ||
isNodeName(activeElement, 'object') ||
target.shadowRoot) {
return true;
}
var deltaX = -event.wheelDeltaX || event.deltaX || 0;
var deltaY = -event.wheelDeltaY || event.deltaY || 0;
if (isMac) {
if (event.wheelDeltaX && isDivisible(event.wheelDeltaX, 120)) {
deltaX = -120 * (event.wheelDeltaX / Math.abs(event.wheelDeltaX));
}
if (event.wheelDeltaY && isDivisible(event.wheelDeltaY, 120)) {
deltaY = -120 * (event.wheelDeltaY / Math.abs(event.wheelDeltaY));
}
}
// use wheelDelta if deltaX/Y is not available
if (!deltaX && !deltaY) {
deltaY = -event.wheelDelta || 0;
}
// line based scrolling
if (event.deltaMode === 1) {
deltaX *= 40;
deltaY *= 40;
}
// check if it's a touchpad scroll that should be ignored
if (!options.touchpadSupport && isTouchpad(deltaY)) {
return true;
}
var xOnly = (deltaX && !deltaY);
var overflowing = overflowingAncestor(target, xOnly);
// nothing to do if there's no element that's scrollable
if (!overflowing) {
// Chrome iframes seem to eat wheel events, which we need to
// propagate up if the iframe has nothing overflowing to scroll
if (isFrame) {
event.preventDefault();
postScrollToParent(deltaX, deltaY);
// change target to iframe element itself for the parent frame
//Object.defineProperty(event, "target", {value: window.frameElement});
//return parent.wheel(event);
}
return true;
}
// scale by step size
// delta is 120 most of the time
// synaptics seems to send 1 sometimes
if (Math.abs(deltaX) > 1.2) {
deltaX *= options.stepSize / 120;
}
if (Math.abs(deltaY) > 1.2) {
deltaY *= options.stepSize / 120;
}
scrollArray(overflowing, deltaX, deltaY);
if (event.preventDefault) event.preventDefault();
scheduleClearCache();
}
/**
* Keydown event handler.
* @param {Object} event
*/
function keydown(event) {
var target = event.target;
var modifier = event.ctrlKey || event.altKey || event.metaKey ||
(event.shiftKey && event.keyCode !== key.spacebar);
// our own tracked active element could've been removed from the DOM
if (!document.contains(activeElement)) {
activeElement = document.activeElement;
}
// do nothing if user is editing text
// or using a modifier key (except shift)
// or in a dropdown
// or inside interactive elements
var inputNodeNames = /^(textarea|select|embed|object)$/i;
var buttonTypes = /^(button|submit|radio|checkbox|file|color|image)$/i;
if ( event.defaultPrevented ||
inputNodeNames.test(target.nodeName) ||
isNodeName(target, 'input') && !buttonTypes.test(target.type) ||
isNodeName(activeElement, 'video') ||
isInsideYoutubeVideo(event) ||
target.isContentEditable ||
modifier ) {
return true;
}
// [spacebar] should trigger button press, leave it alone
if ((isNodeName(target, 'button') ||
isNodeName(target, 'input') && buttonTypes.test(target.type)) &&
event.keyCode === key.spacebar) {
return true;
}
// [arrwow keys] on radio buttons should be left alone
if (isNodeName(target, 'input') && target.type == 'radio' &&
arrowKeys[event.keyCode]) {
return true;
}
var xOnly = (event.keyCode == key.left || event.keyCode == key.right);
var overflowing = overflowingAncestor(activeElement, xOnly);
if (!overflowing) {
// iframes seem to eat key events, which we need to propagate up
// if the iframe has nothing overflowing to scroll
return isFrame ? parent.keydown(event) : true;
}
var clientHeight = overflowing.clientHeight;
var shift, x = 0, y = 0;
if (overflowing == document.body) {
clientHeight = window.innerHeight;
}
switch (event.keyCode) {
case key.up:
y = -options.arrowScroll;
break;
case key.down:
y = options.arrowScroll;
break;
case key.spacebar: // (+ shift)
shift = event.shiftKey ? 1 : -1;
y = -shift * clientHeight * 0.9;
break;
case key.pageup:
y = -clientHeight * 0.9;
break;
case key.pagedown:
y = clientHeight * 0.9;
break;
case key.home:
if (overflowing == document.body && document.scrollingElement)
overflowing = document.scrollingElement;
y = -overflowing.scrollTop;
break;
case key.end:
var scroll = overflowing.scrollHeight - overflowing.scrollTop;
var scrollRemaining = scroll - clientHeight;
y = (scrollRemaining > 0) ? scrollRemaining+10 : 0;
break;
case key.left:
x = -options.arrowScroll;
break;
case key.right:
x = options.arrowScroll;
break;
default:
return true; // a key we don't care about
}
scrollArray(overflowing, x, y);
event.preventDefault();
scheduleClearCache();
}
/**
* Mousedown event only for updating activeElement
*/
function mousedown(event) {
activeElement = event.target;
}
/***********************************************
* OVERFLOW
***********************************************/
var uniqueID = (function () {
var i = 0;
return function (el) {
return el.uniqueID || (el.uniqueID = i++);
};
})();
var cacheX = {}; // cleared out after a scrolling session
var cacheY = {}; // cleared out after a scrolling session
var clearCacheTimer;
var smoothBehaviorForElement = {};
//setInterval(function () { cache = {}; }, 10 * 1000);
function scheduleClearCache() {
clearTimeout(clearCacheTimer);
clearCacheTimer = setInterval(function () {
cacheX = cacheY = smoothBehaviorForElement = {};
}, 1*1000);
}
function setCache(elems, overflowing, x) {
var cache = x ? cacheX : cacheY;
for (var i = elems.length; i--;)
cache[uniqueID(elems[i])] = overflowing;
return overflowing;
}
function getCache(el, x) {
return (x ? cacheX : cacheY)[uniqueID(el)];
}
// (body) (root)
// | hidden | visible | scroll | auto |
// hidden | no | no | YES | YES |
// visible | no | YES | YES | YES |
// scroll | no | YES | YES | YES |
// auto | no | YES | YES | YES |
function overflowingAncestor(el, x) {
var elems = [];
var body = document.body;
var rootScrollHeight = root.scrollHeight;
var rootScrollWidth = root.scrollWidth;
do {
var cached = getCache(el, x);
if (cached) {
return setCache(elems, cached, x);
}
elems.push(el);
if (x && rootScrollWidth === el.scrollWidth ||
!x && rootScrollHeight === el.scrollHeight) {
var topOverflowsNotHidden = overflowNotHidden(root, x) && overflowNotHidden(body, x);
var isOverflowCSS = topOverflowsNotHidden || overflowAutoOrScroll(root, x);
if (isFrame && isContentOverflowing(root, x) ||
!isFrame && isOverflowCSS) {
return setCache(elems, getScrollRoot(), x);
}
} else if (isContentOverflowing(el, x) && overflowAutoOrScroll(el, x)) {
return setCache(elems, el, x);
}
} while ((el = el.parentElement));
}
function isContentOverflowing(el, x) {
return x ? (el.clientWidth + 10 < el.scrollWidth)
: (el.clientHeight + 10 < el.scrollHeight);
}
function computedOverflow(el, x) {
var property = x ? 'overflow-x' : 'overflow-y';
return getComputedStyle(el, '').getPropertyValue(property);
}
// typically for <body> and <html>
function overflowNotHidden(el, x) {
return (computedOverflow(el, x) != 'hidden');
}
// for all other elements
function overflowAutoOrScroll(el, x) {
return /^(scroll|auto)$/.test(computedOverflow(el, x));
}
// for all other elements
function isScrollBehaviorSmooth(el) {
var id = uniqueID(el);
if (smoothBehaviorForElement[id] == null) {
var scrollBehavior = getComputedStyle(el, '')['scroll-behavior'];
smoothBehaviorForElement[id] = ('smooth' == scrollBehavior);
}
return smoothBehaviorForElement[id];
}
function postScrollToParent(deltaX, deltaY) {
parent.postMessage({
deltaX: deltaX,
deltaY: deltaY,
SS: 'SmoothScroll'
}, '*');
}
/***********************************************
* HELPERS
***********************************************/
function addEvent(type, fn) {
window.addEventListener(type, fn, false);
}
function removeEvent(type, fn) {
window.removeEventListener(type, fn, false);
}
function isNodeName(el, tag) {
return el && (el.nodeName||'').toLowerCase() === tag.toLowerCase();
}
function directionCheck(x, y) {
x = (x > 0) ? 1 : -1;
y = (y > 0) ? 1 : -1;
if (direction.x !== x || direction.y !== y) {
direction.x = x;
direction.y = y;
que = [];
lastScroll = 0;
window.cancelAnimationFrame(pending);
pending = null;
}
}
function isTouchpad(deltaY) {
if (!deltaY) return;
if (!deltaBuffer.length) {
deltaBuffer = [deltaY, deltaY, deltaY];
}
deltaY = Math.abs(deltaY);
deltaBuffer.push(deltaY);
deltaBuffer.shift();
clearTimeout(deltaBufferTimer);
deltaBufferTimer = setTimeout(function () {
chrome.storage.local.set({ deltaBuffer: deltaBuffer });
}, 1000);
var dpiScaledWheelDelta = deltaY > 120 && allDeltasDivisableBy(deltaY); // win64
return !allDeltasDivisableBy(120) && !allDeltasDivisableBy(100) && !dpiScaledWheelDelta;
}
function isDivisible(n, divisor) {
return (Math.floor(n / divisor) == n / divisor);
}
function allDeltasDivisableBy(divisor) {
return (isDivisible(deltaBuffer[0], divisor) &&
isDivisible(deltaBuffer[1], divisor) &&
isDivisible(deltaBuffer[2], divisor));
}
chrome.storage.local.get('deltaBuffer', function (stored) {
if (stored.deltaBuffer) {
deltaBuffer = stored.deltaBuffer;
}
});
function isInsideYoutubeVideo(event) {
var elem = event.target;
var isControl = false;
if (document.URL.indexOf ('www.youtube.com/watch') != -1) {
do {
isControl = (elem.classList &&
elem.classList.contains('html5-video-controls'));
if (isControl) break;
} while ((elem = elem.parentNode));
}
return isControl;
}
function getScrollRoot() {
return document.scrollingElement || document.body; // scrolling root in WebKit
}
/***********************************************
* PULSE (by Michael Herf)
***********************************************/
/**
* Viscous fluid with a pulse for part and decay for the rest.
* - Applies a fixed force over an interval (a damped acceleration), and
* - Lets the exponential bleed away the velocity over a longer interval
* - Michael Herf, http://stereopsis.com/stopping/
*/
function pulse_(x) {
var val, start, expx;
// test
x = x * options.pulseScale;
if (x < 1) { // acceleartion
val = x - (1 - Math.exp(-x));
} else { // tail
// the previous animation ended here:
start = Math.exp(-1);
// simple viscous drag
x -= 1;
expx = 1 - Math.exp(-x);
val = start + (expx * (1 - start));
}
return val * options.pulseNormalize;
}
function pulse(x) {
if (x >= 1) return 1;
if (x <= 0) return 0;
if (options.pulseNormalize == 1) {
options.pulseNormalize /= pulse_(1);
}
return pulse_(x);
}
// new standard wheel event from Chrome 31+
var wheelEvent = 'onwheel' in document.createElement('div') ? 'wheel' : 'mousewheel';
addEvent(wheelEvent, wheel);
addEvent('mousedown', mousedown);
addEvent('keydown', keydown);
addEvent('load', loaded);
gitextract_6eefbn6w/
├── LICENSE
├── README
├── manifest.json
├── pages/
│ ├── background.js
│ ├── options.html
│ └── options.js
└── src/
├── middlemouse.js
└── sscr.js
SYMBOL INDEX (49 symbols across 4 files)
FILE: pages/background.js
function init (line 38) | function init(details) {
function addSmoothScrollToTab (line 49) | function addSmoothScrollToTab(tab) {
FILE: pages/options.js
function byId (line 27) | function byId(id) { return document.getElementById(id); }
function byClass (line 28) | function byClass(cname) { return document.getElementsByClassName(cname); }
function byTag (line 29) | function byTag(tag,base) { return (base||document).getElementsByTagName(...
function isNodeName (line 31) | function isNodeName(el, tag) {
function show (line 35) | function show(elem, newop) {
function hide (line 43) | function hide(elem, newop) {
function isCheckbox (line 51) | function isCheckbox(key) {
function init (line 56) | function init() {
function initWithOptions (line 63) | function initWithOptions(optionsSynced) {
function save (line 92) | function save() {
function get_manifest (line 130) | function get_manifest(callback) {
function reload (line 144) | function reload() {
function setProfile (line 186) | function setProfile(profile) {
function generateTest (line 208) | function generateTest() {
FILE: src/middlemouse.js
function init (line 45) | function init() {
function cleanup (line 59) | function cleanup() {
function mousedown (line 68) | function mousedown(e) {
FILE: src/sscr.js
function initTest (line 79) | function initTest() {
function init (line 104) | function init() {
function getFrameByEvent (line 154) | function getFrameByEvent(event) {
function cleanup (line 164) | function cleanup() {
function loaded (line 175) | function loaded() {
function scrollArray (line 197) | function scrollArray(elem, left, top) {
function wheel (line 320) | function wheel(event) {
function keydown (line 406) | function keydown(event) {
function mousedown (line 507) | function mousedown(event) {
function scheduleClearCache (line 530) | function scheduleClearCache() {
function setCache (line 537) | function setCache(elems, overflowing, x) {
function getCache (line 544) | function getCache(el, x) {
function overflowingAncestor (line 555) | function overflowingAncestor(el, x) {
function isContentOverflowing (line 580) | function isContentOverflowing(el, x) {
function computedOverflow (line 585) | function computedOverflow(el, x) {
function overflowNotHidden (line 591) | function overflowNotHidden(el, x) {
function overflowAutoOrScroll (line 596) | function overflowAutoOrScroll(el, x) {
function isScrollBehaviorSmooth (line 601) | function isScrollBehaviorSmooth(el) {
function postScrollToParent (line 610) | function postScrollToParent(deltaX, deltaY) {
function addEvent (line 623) | function addEvent(type, fn) {
function removeEvent (line 627) | function removeEvent(type, fn) {
function isNodeName (line 631) | function isNodeName(el, tag) {
function directionCheck (line 635) | function directionCheck(x, y) {
function isTouchpad (line 648) | function isTouchpad(deltaY) {
function isDivisible (line 664) | function isDivisible(n, divisor) {
function allDeltasDivisableBy (line 668) | function allDeltasDivisableBy(divisor) {
function isInsideYoutubeVideo (line 680) | function isInsideYoutubeVideo(event) {
function getScrollRoot (line 693) | function getScrollRoot() {
function pulse_ (line 707) | function pulse_(x) {
function pulse (line 724) | function pulse(x) {
Condensed preview — 8 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (52K chars).
[
{
"path": "LICENSE",
"chars": 1253,
"preview": "MIT License\r\n----\r\n\r\nCopyright (c) 2010-2018 Balazs Galambosi\r\n\r\nThe only restriction is to not publish any extension fo"
},
{
"path": "README",
"chars": 797,
"preview": "A Google Chrome extension for smooth scrolling with the mouse wheel and keyboard buttons.\n\nAlso available as a Mac app\n "
},
{
"path": "manifest.json",
"chars": 1015,
"preview": "{\n \"background\": {\n \"scripts\": [ \"pages/background.js\" ],\n \"persistent\": false\n },\n \"content_scripts\": ["
},
{
"path": "pages/background.js",
"chars": 1493,
"preview": "\nvar defaultOptions = {\n\n // Plugin\n middleMouse : true, \n\n // Scrolling Core\n framerate : 150"
},
{
"path": "pages/options.html",
"chars": 14278,
"preview": "<!DOCTYPE html>\n<html>\n<head><title>SmoothScroll Options</title>\n<meta charset=utf-8 />\n<style type=\"text/css\">\n\nbody {\n"
},
{
"path": "pages/options.js",
"chars": 5000,
"preview": "\n/**\n * Options page logic.\n */\n(function(window, undefined){\n\n/**\n * List of available options\n */\nvar optionsList = [\n"
},
{
"path": "src/middlemouse.js",
"chars": 4777,
"preview": "\n//\n// SmoothScroll (Balazs Galambosi)\n// Licensed under the terms of the MIT license.\n// The only restriction would be "
},
{
"path": "src/sscr.js",
"chars": 21358,
"preview": "\n//\n// SmoothScroll (Balazs Galambosi)\n// Licensed under the terms of the MIT license.\n// The only restriction would be "
}
]
About this extraction
This page contains the full source code of the gblazex/smoothscroll GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 8 files (48.8 KB), approximately 13.1k tokens, and a symbol index with 49 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.