// ==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() {
// shorthand
var shorthands = "document $ Control Form Keybind register_hook message".split(" ");
for(var i = 0, len = shorthands.length; i < len; ++i) {
eval("var " + shorthands[i] + " = w." + shorthands[i]);
}
var container = $("right_container");
// 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;
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;
}
};
var should_smooth = function(px) {
return config.type == "always"
|| config.type == "auto" && Math.abs(px) > config.threshold;
};
register_hook("before_printfeed", stop_scroll);
// override scrollTop
container.__defineGetter__("_scrollTop", container.__lookupGetter__("scrollTop"));
container.__defineSetter__("_scrollTop", container.__lookupSetter__("scrollTop"));
container.__defineGetter__("scrollTop", function() container._scrollTop);
container.__defineSetter__("scrollTop", function(px) {
var diff = px - container._scrollTop;
if(should_smooth(diff))
smooth_scroll_by(diff);
else
container._scrollTop = px;
});
// add config
register_hook("after_init_config", function(){
// 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];
}
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
// toggle smooth type
var next_type = {
none: "always",
always: "auto",
auto: "none"
};
Keybind.add("S", function() {
config.type = next_type[config.type];
message('set smooth: "' + config.type + '"');
window.setTimeout(function() {
GM_setValue("type", config.type);
}, 0);
});
// scroll to top
Keybind.add("t", Control.scroll_to_zero);
// scroll to bottom
Keybind.add("b", function() {
var divs = container.getElementsByTagName("h2");
var last_item = divs[divs.length - 1].parentNode.parentNode;
var target = last_item.offsetTop + last_item.offsetHeight
- container.offsetHeight + 40;
container.scrollTop = target;
});
}, false);
})();