# vim:fileencoding=utf-8 # License: GPL v3 Copyright: 2018, Kovid Goyal from __python__ import bound_methods, hash_literals THRESHOLD = 5 FILTER_THRESHOLD = 25 MAX_SAMPLES = 256 class Timers: def __init__(self): self.reset_read_timer() self.rates = v'[]' self.average = self.stddev = 0 def start_book(self, book): self.reset_read_timer() self.rates = v'[]' def reset_read_timer(self): self.last_scroll_at = None def calculate(self): rates = self.rates rlen = rates.length if rlen >= THRESHOLD: avg = 0 for v'var i = 0; i < rlen; i++': avg += rates[i] avg /= rlen self.average = avg sq = 0 for v'var i = 0; i < rlen; i++': x = rates[i] - avg sq += x * x self.stddev = Math.sqrt(sq / (rlen - 1)) else: self.average = self.stddev = 0 def on_human_scroll(self, amt_scrolled): last_scroll_at = self.last_scroll_at self.last_scroll_at = now = window.performance.now() if last_scroll_at is None: return time_since_last_scroll = (now - last_scroll_at) / 1000 if time_since_last_scroll <= 0 or time_since_last_scroll >= 300: return if time_since_last_scroll < 2: return rate = amt_scrolled / time_since_last_scroll if self.rates.length >= FILTER_THRESHOLD and Math.abs(rate - self.average) > 2 * self.stddev: return if self.rates.length >= MAX_SAMPLES: self.rates.shift() self.rates.push(rate) self.calculate() def time_for(self, length): if length >= 0 and self.rates.length >= THRESHOLD and self.average > 0: return length / self.average return None