202 lines
6.4 KiB
JavaScript
202 lines
6.4 KiB
JavaScript
|
|
/**
|
||
|
|
* @name Reload-in-place v2.0
|
||
|
|
* @desc A simple script to add to your pagedjs project. On reload, it will make the web browser scroll to the place it was before reload.
|
||
|
|
* Useful when styling or proof correcting your book. Multi docs compatible and doesn't wait for complete compilation to go.
|
||
|
|
* @author Nicolas Taffin
|
||
|
|
* @author Sameh Chafik
|
||
|
|
* @author (adapted by) Benjamin G. <ecrire@bnjm.eu>
|
||
|
|
* @license MIT
|
||
|
|
* @see { @link https://gitlab.com/csspageweaver/plugins/reloadInPlace }
|
||
|
|
*/
|
||
|
|
|
||
|
|
|
||
|
|
import { Handler } from '/csspageweaver/lib/paged.esm.js';
|
||
|
|
|
||
|
|
|
||
|
|
export default class reloadInPlace extends Handler {
|
||
|
|
constructor(chunker, polisher, caller) {
|
||
|
|
super(chunker, polisher, caller);
|
||
|
|
this.parameters = cssPageWeaver.features.reloadInPlace.parameters || {}
|
||
|
|
this.isScrollBlured = this.parameters.blur || true;
|
||
|
|
this.scrollBehavior = this.parameters.scrollBehavior || 'instant';
|
||
|
|
|
||
|
|
// set a "unique" filename based on title element, in case several books are opened
|
||
|
|
this.fileTitle = document.getElementsByTagName("title")[0].text.replace(/ /g, "");
|
||
|
|
}
|
||
|
|
|
||
|
|
beforeParsed() {
|
||
|
|
// separate human / machine scroll
|
||
|
|
this.parameters.machineScroll = false;
|
||
|
|
|
||
|
|
// check pagedJS ended compilation
|
||
|
|
this.parameters.cssPageWeaverEnd = false;
|
||
|
|
|
||
|
|
// Make it blur if needed
|
||
|
|
this.isBlur()
|
||
|
|
|
||
|
|
// Start saving scroll position early (don't wait for render to complete)
|
||
|
|
let _ = this;
|
||
|
|
var slowSave = this.debounce(() => {
|
||
|
|
if(!_.parameters.machineScroll) {
|
||
|
|
_.saveAmountScrolled();
|
||
|
|
}
|
||
|
|
}, 100);
|
||
|
|
|
||
|
|
window.addEventListener('scroll', slowSave);
|
||
|
|
}
|
||
|
|
|
||
|
|
afterParsed() {
|
||
|
|
this.moveFast();
|
||
|
|
}
|
||
|
|
|
||
|
|
afterRendered(pages) {
|
||
|
|
this.parameters.cssPageWeaverEnd = true;
|
||
|
|
}
|
||
|
|
|
||
|
|
getDocHeight() {
|
||
|
|
var D = document;
|
||
|
|
return Math.max(
|
||
|
|
D.body.scrollHeight, D.documentElement.scrollHeight,
|
||
|
|
D.body.offsetHeight, D.documentElement.offsetHeight,
|
||
|
|
D.body.clientHeight, D.documentElement.clientHeight
|
||
|
|
)
|
||
|
|
}
|
||
|
|
|
||
|
|
saveAmountScrolled(){
|
||
|
|
var scrollArray = [];
|
||
|
|
var scrollTop = window.pageYOffset || (document.documentElement || document.body.parentNode || document.body).scrollTop
|
||
|
|
if (!this.parameters.machineScroll) {
|
||
|
|
var scrollLeft = window.pageXOffset || (document.documentElement || document.body.parentNode || document.body).scrollLeft
|
||
|
|
scrollArray.push({ X: Math.round(scrollLeft), Y: Math.round(scrollTop) });
|
||
|
|
//console.log("Saved ", scrollArray);
|
||
|
|
localStorage['reloadInPlace-' + this.fileTitle] = JSON.stringify(scrollArray);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
isBlur(){
|
||
|
|
// Apply a blur effect if scroll blurring is enabled
|
||
|
|
if (this.isScrollBlured) {
|
||
|
|
var styleEl = document.createElement('style');
|
||
|
|
styleEl.setAttribute("data-reload-in-place", true)
|
||
|
|
document.head.appendChild(styleEl);
|
||
|
|
this.styleSheet = styleEl.sheet;
|
||
|
|
this.styleSheet.insertRule('.pagedjs_pages { filter: blur(3px); }', 0);
|
||
|
|
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
retrievePosition(){
|
||
|
|
// Retrieve saved scroll data from localStorage
|
||
|
|
var savedData = localStorage.getItem('reloadInPlace-' + this.fileTitle);
|
||
|
|
|
||
|
|
if (savedData) {
|
||
|
|
// Parse the saved data to get the scroll positions
|
||
|
|
var scrollArray = JSON.parse(savedData);
|
||
|
|
this.scrollTop = scrollArray[0].Y;
|
||
|
|
this.scrollLeft = scrollArray[0].X;
|
||
|
|
} else {
|
||
|
|
// Default scroll positions if no saved data is found
|
||
|
|
this.scrollTop = 0;
|
||
|
|
this.scrollLeft = 0;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Adjusts the scroll position of the window based on saved data.
|
||
|
|
* This function handles scrolling behavior when the document height changes,
|
||
|
|
* ensuring the view returns to a previously saved scroll position or the bottom of the document.
|
||
|
|
*
|
||
|
|
* @param {Object} _ - ReloadInPlace scope
|
||
|
|
*/
|
||
|
|
moveFast() {
|
||
|
|
// Set the machine scroll flag to true
|
||
|
|
this.parameters.machineScroll = true;
|
||
|
|
|
||
|
|
this.retrievePosition()
|
||
|
|
|
||
|
|
// Get the window height
|
||
|
|
var winheight = window.innerHeight || (document.documentElement || document.body).clientHeight;
|
||
|
|
|
||
|
|
let _ = this
|
||
|
|
|
||
|
|
// Track last scroll position to detect user scroll
|
||
|
|
let lastScrollTop = window.pageYOffset || document.documentElement.scrollTop;
|
||
|
|
|
||
|
|
// Detect user manual scroll and stop auto-scroll
|
||
|
|
const userScrollHandler = () => {
|
||
|
|
if (!_.parameters.machineScroll) return;
|
||
|
|
|
||
|
|
const currentScroll = window.pageYOffset || document.documentElement.scrollTop;
|
||
|
|
const expectedScroll = _.scrollTop > _.docheight - winheight ? _.docheight : _.scrollTop;
|
||
|
|
|
||
|
|
// If scroll position differs significantly from expected, user is scrolling
|
||
|
|
if (Math.abs(currentScroll - expectedScroll) > 100 && Math.abs(currentScroll - lastScrollTop) > 50) {
|
||
|
|
_.stopAutoScroll();
|
||
|
|
}
|
||
|
|
lastScrollTop = currentScroll;
|
||
|
|
};
|
||
|
|
|
||
|
|
window.addEventListener('wheel', () => _.stopAutoScroll(), { once: true });
|
||
|
|
window.addEventListener('touchstart', () => _.stopAutoScroll(), { once: true });
|
||
|
|
|
||
|
|
// Set up an interval to adjust the scroll position
|
||
|
|
_.currentInterval = setInterval(() => {
|
||
|
|
// Get the current document height
|
||
|
|
_.docheight = _.getDocHeight();
|
||
|
|
|
||
|
|
// Check if the saved scroll position is beyond the current document height
|
||
|
|
if ( _.scrollTop > 0 && _.scrollTop > _.docheight - winheight && !_.parameters.cssPageWeaverEnd) {
|
||
|
|
// Scroll to the bottom of the document
|
||
|
|
window.scrollTo({ left: _.scrollLeft, top: _.docheight, behavior: _.scrollBehavior });
|
||
|
|
} else {
|
||
|
|
// Scroll to the saved position
|
||
|
|
window.scrollTo({ left: _.scrollLeft, top: _.scrollTop, behavior: _.scrollBehavior });
|
||
|
|
|
||
|
|
// Clear interval
|
||
|
|
clearInterval(_.currentInterval);
|
||
|
|
|
||
|
|
// set a timeout to finalize the scroll position
|
||
|
|
setTimeout(function () {
|
||
|
|
window.scrollTo({ left: _.scrollLeft, top: _.scrollTop, behavior: _.scrollBehavior });
|
||
|
|
|
||
|
|
// Reset the machine scroll flag
|
||
|
|
_.parameters.machineScroll = false;
|
||
|
|
|
||
|
|
// Remove the blur effect if it was applied
|
||
|
|
if (_.isScrollBlured) {
|
||
|
|
_.styleSheet.deleteRule(0);
|
||
|
|
}
|
||
|
|
}, 50); // Delay to start
|
||
|
|
}
|
||
|
|
}, 50); // Refresh frequency
|
||
|
|
}
|
||
|
|
|
||
|
|
stopAutoScroll() {
|
||
|
|
if (this.currentInterval) {
|
||
|
|
clearInterval(this.currentInterval);
|
||
|
|
this.currentInterval = null;
|
||
|
|
}
|
||
|
|
this.parameters.machineScroll = false;
|
||
|
|
|
||
|
|
// Remove blur if applied
|
||
|
|
if (this.isScrollBlured && this.styleSheet && this.styleSheet.cssRules.length > 0) {
|
||
|
|
this.styleSheet.deleteRule(0);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
debounce(func, wait, immediate) {
|
||
|
|
var timeout;
|
||
|
|
return function() {
|
||
|
|
var context = this, args = arguments;
|
||
|
|
var later = function() {
|
||
|
|
timeout = null;
|
||
|
|
if (!immediate) func.apply(context, args);
|
||
|
|
};
|
||
|
|
var callNow = immediate && !timeout;
|
||
|
|
clearTimeout(timeout);
|
||
|
|
timeout = setTimeout(later, wait);
|
||
|
|
if (callNow) func.apply(context, args);
|
||
|
|
};
|
||
|
|
};
|
||
|
|
}
|