// ==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() { // shorthand var _w = unsafeWindow; var $ = _w.$; 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_to = function(elem, px) { if(timer != null) return; var diff = px - elem._scrollTop; var step = (diff < 0 ? -1 : 1) * config.velocity var times = Math.floor(diff / step); timer = setInterval(function() { elem._scrollTop += step; if(--times <= 0) { stop_scroll(); elem._scrollTop = px; } }, 10); }; var stop_scroll = function() { if(timer != null) { clearInterval(timer); timer = null; } }; // override scrollTop var force_nonsmooth = false; var override_scrollTop = function(elem) { elem.__defineGetter__("_scrollTop", elem.__lookupGetter__("scrollTop")); elem.__defineSetter__("_scrollTop", elem.__lookupSetter__("scrollTop")); elem.__defineGetter__("scrollTop", function() elem._scrollTop); elem.__defineSetter__("scrollTop", function(px) { stop_scroll(); var diff = Math.abs(px - elem._scrollTop); if(!force_nonsmooth && ( config.type == "always" || config.type == "auto" && diff > config.threshold )) smooth_scroll_to(elem, px); else elem._scrollTop = px; }); }; override_scrollTop(container); // privent scroll when go to next feed // override scrollTop of each items for fastladder_fixed_item_height.js var register_hook = _w.register_hook; register_hook("BEFORE_PRINTFEED", function() force_nonsmooth = true); register_hook("BEFORE_SUBS_LOAD", function() force_nonsmooth = true); register_hook("AFTER_PRINTFEED", function() { force_nonsmooth = false; Array.slice(container.getElementsByClassName("item")).map(override_scrollTop); }); register_hook("AFTER_SUBS_LOAD", function() { force_nonsmooth = false; Array.slice(container.getElementsByClassName("item")).map(override_scrollTop); }); // create additional config // 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 div = document.createElement("div"); div.innerHTML =
{labels.title} {labels.description}
{labels.none}
{labels.always}
{labels.auto}
{labels.velocity} {labels.velocity_description}
{labels.threshold} {labels.threshold_description}
.toString(); var insert = div.getElementsByTagName("tr")[0]; register_hook("AFTER_INIT_CONFIG", function(){ // add additional config var tbody = $("config_detail").getElementsByTagName("tbody")[0]; tbody.insertBefore(insert.cloneNode(true), tbody.childNodes[8]); // fill value var form = $("config_form"); 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; i < form.smooth_type.length; i++) { var radio = form.smooth_type[i]; if(radio.checked) config.type = radio.value; } 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); }); window.addEventListener('load', function() { // 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]; _w.message('set smooth: "' + config.type + '"'); window.setTimeout(function() GM_setValue("type", config.type), 0); }); // scroll to top Keybind.add("t", function() container.scrollTop = 0); // scroll to bottom Keybind.add("b", function() { var divs = container.getElementsByClassName("item"); var last_item = divs[divs.length - 1]; var target = last_item.offsetTop + last_item.offsetHeight - container.offsetHeight + 40; container.scrollTop = target; }); }, false); })();