// ==UserScript==
// @name ldr smooth scroll
// @namespace http://relaxedcolumn.blog8.fc2.com/
// @description smoothing scroll for livedoor reader (ldr) and fastladder
// @include http://reader.livedoor.com/reader/
// @include http://fastladder.com/reader/
// ==/UserScript==
//
(function() {
var w = unsafeWindow;
w.addEventListener('load', function() {
// config
var config = {
type: "always",
velocity: 30,
threshold: 250
};
// load config
for(var val in config) {
config[val] = GM_getValue(val, config[val]);
}
// smooth scroll
var timer = null;
var smooth_scroll_by = function(px) {
if(timer != null) return;
var step = (px < 0 ? -1 : 1) * config.velocity
var times = Math.floor(px / step);
var remain = px - step * times;
var container = $("right_container");
timer = setInterval(function() {
container.scrollTop += step;
if(--times <= 0) {
stop_scroll();
container.scrollTop += remain;
}
}, 10);
};
var stop_scroll = function() {
if(timer != null) {
clearInterval(timer);
timer = null;
}
};
// override original functions
var $ = w.$;
var Control = w.Control;
var Config = w.Config;
var should_smooth = function(px) {
return config.type == "always"
|| (config.type == "auto" && Math.abs(px) > config.threshold);
};
Control.scroll_page = function(num) {
var h = $("right_container").offsetHeight - 40;
var c =
(Config.scroll_type == "page") ? h:
(Config.scroll_type == "half") ? h / 2 :
(Config.scroll_px || 100);
if(should_smooth(c)) {
smooth_scroll_by(c * num);
}
else {
$("right_container").scrollTop += c * num;
}
};
Control.scroll_to_zero = function() {
var container = $("right_container");
var current = container.scrollTop;
if(should_smooth(current)) {
smooth_scroll_by(- current);
}
else {
container.scrollTop = 0;
}
};
Control.scroll_to_px = function(top) {
var container = $("right_container");
// for opera9 beta
var top_offset = $("right_body").offsetTop;
var current = $("right_container").scrollTop;
var target = top - top_offset;
if(should_smooth(target - current)) {
smooth_scroll_by(target - current);
}
else {
container.scrollTop = target;
}
};
var register_hook = w.register_hook;
register_hook("before_printfeed", stop_scroll);
// add config
register_hook("after_init_config", function(){
var document = w.document;
// get DOM nodes
var form = $("config_form");
var config_detail = $("config_detail");
var tbody = config_detail.getElementsByTagName("tbody")[0];
var insert_target = tbody.childNodes[8];
// localization
var labels = {};
// fastladder
if(location.href.match(/^http:\/\/fastladder/)) {
labels.title = "Smoothing scroll";
labels.description = "Smooth scroll mode and settings";
labels.none = "none";
labels.always = "always";
labels.auto = "auto (smoothing only when scroll length is longer than threshold)";
labels.velocity = "velocity";
labels.velocity_description = "px (more than 1)";
labels.threshold = "threshold";
labels.threshold_description = "px (only for auto)";
}
// livedoor reader
else {
labels.title = "スムーズスクロール";
labels.description = "スムーズのタイプと動作の設定ができます";
labels.none = "なし";
labels.always = "常に";
labels.auto = "自動(スクロール距離が閾値より長いときだけスムージング)";
labels.velocity = "速さ";
labels.velocity_description = "px (1以上)";
labels.threshold = "閾値";
labels.threshold_description = "px (自動のときのみ)";
}
// create config nodes
var smooth_tr = document.createElement("tr");
var smooth_th = document.createElement("th");
smooth_th.textContent = labels.title;
smooth_th.style.background = "#ffa";
smooth_tr.appendChild(smooth_th);
var smooth_td = document.createElement("td");
smooth_td.style.background = "#ffa";
smooth_td.innerHTML = <>
{labels.description}
{labels.none}
{labels.always}
{labels.auto}
{labels.velocity}
{labels.velocity_description}
{labels.threshold}
{labels.threshold_description}
>.toString();
smooth_tr.appendChild(smooth_td);
tbody.insertBefore(smooth_tr, insert_target);
// fill value
var fillConfig = {};
for(var val in config) {
fillConfig["smooth_" + val] = config[val];
}
w.Form.fill(form, fillConfig);
// add submit event
form.addEventListener("submit", function() {
for(var i = 0, len = form.smooth_type.length; i < len; ++i) {
var radio = form.smooth_type[i];
if(radio.checked) {
config.type = radio.value;
break;
}
}
var v = parseInt(form.smooth_velocity.value);
config.velocity = (v > 0) ? v : 30;
config.threshold = parseInt(form.smooth_threshold.value);
for(var val in config) {
GM_setValue(val, config[val]);
}
}, false);
});
// Keybind
var Keybind = w.Keybind;
// toggle smooth type
var next_type = {
none: "always",
always: "auto",
auto: "none"
};
Keybind.add("S", function() {
config.type = next_type[config.type];
GM_setValue("type", config.type);
w.message('set smooth: "' + config.type + '"');
});
// scroll to top
Keybind.add("t", Control.scroll_to_zero);
// scroll to bottom
Keybind.add("b", function() {
var container = $("right_container");
var divs = container.getElementsByTagName("h2");
var last_item = divs[divs.length - 1];
var target = last_item.offsetTop
+ last_item.parentNode.parentNode.offsetHeight
- container.offsetHeight + 40;
Control.scroll_to_px(target);
});
}, false);
})();