sashimi-webapp/src/logic/inputHandler/DocumentNavigator/EventHandlerManager.js
import domUtils from 'src/helpers/domUtils';
import unitConverter from 'src/helpers/unitConverter';
const NOT_IMPLEMENTED = null;
const MARGIN_WIDTH = 60;
const TRANSITION_DURATION = 500;
const guard = {
translateY(translateY, parentEle) {
const bottomLimit = (() => {
const numberOfElement = parentEle.childNodes.length;
const pageHeight = unitConverter.get(parentEle.childNodes[0].style.height, 'px', false);
const pageMargin = unitConverter.get(getComputedStyle(parentEle.childNodes[0]).marginTop, 'px', false);
return -(numberOfElement) * (pageHeight + pageMargin);
})();
if (translateY < bottomLimit) translateY = bottomLimit;
if (translateY > 0) translateY = 0;
return translateY;
},
translateX(translateX, parentEle) {
const sideLimit = (() => {
const pageWidth = unitConverter.get(parentEle.childNodes[0].style.width, 'px', false);
return (pageWidth / 2);
})();
if (translateX < -sideLimit) translateX = -sideLimit;
if (translateX > sideLimit) translateX = sideLimit;
return translateX;
},
scale(scale) {
if (scale < 0.05) scale = 0.05;
if (scale > 15) scale = 15;
return scale;
}
};
const putDocumentBackToPlace = function putDocumentBackToPlace(navInstance) {
if (!navInstance.el.container.documentNavigator) {
return;
}
const tempContainerHeight = navInstance.el.container.documentNavigator.originalStyle.height;
const tempContainerWidth = navInstance.el.container.documentNavigator.originalStyle.width;
const renderHeight = tempContainerHeight * navInstance.transform.scale;
const renderWidth = tempContainerWidth * navInstance.transform.scale;
// Readjust scrollTop - [1] Cache original height of parent div
let oriHeight = domUtils.getComputedStyle(navInstance.el.parent).height;
if (oriHeight === 'auto') oriHeight = `${navInstance.el.parent.scrollHeight}px`;
const oriHeightPx = unitConverter.get(oriHeight, 'px', false);
// Readjust parent height and width to fix overall scrollbar problem
navInstance.el.parent.style.height = `${renderHeight}px`;
navInstance.el.parent.style.width = `${renderWidth + (MARGIN_WIDTH/2)}px`;
// Readjust scrollTop - [2] Compute height difference and adjust scrollTop
const newHeightPx = renderHeight;
const heightChange = newHeightPx / oriHeightPx;
navInstance.el.parent.parentNode.scrollTop *= heightChange;
};
export default function(navInstance) {
// Event handlers
this.eventFn = {
gesture: {
dragstart(event) {
if (navInstance.eventInstance.state.pointerType === 'touch') {
navInstance.eventInstance.state.action = 'panning';
return false;
} else {
// other input: Mouse, Pen will not change
// the eventInstance.state.action
return true;
}
},
dragmove(event) {
if (navInstance.eventInstance.state.action !== 'panning') {
return;
}
// translate the element
const translateY = (navInstance.el.parent.scrollTop - (event.dy));
navInstance.el.parent.scrollTop = translateY;
},
dragend(event) {
if (navInstance.eventInstance.state.action === 'panning') {
navInstance.eventInstance.state.action = null;
}
},
zoom(event) {
event.preventDefault();
if (event.type === 'gesturemove' && navInstance.eventInstance.numPointers < 2) {
// The event is a gesture, but is it not executed by at least two fingers
return;
}
if (event.ds == null) {
// event polyfill for interactjs gesturable
// deltaY is only available for mousewheel event
// detail is only available for DOMMouseWheel event
event.ds = (event.deltaY) ? (-event.deltaY / 1000) : (-event.detail / 30);
}
let scale = navInstance.transform.scale * (1 + event.ds) || navInstance.transform.scale;
scale = guard.scale(scale);
navInstance.transform.set({ scale });
putDocumentBackToPlace(navInstance);
},
},
pointer: {
down: (event) => {
// Set the state of event instance
navInstance.eventInstance.pointers[event.pointerId] = event;
navInstance.eventInstance.state.pointerType = event.pointerType || 'mouse';
navInstance.eventInstance.numPointers = Object.keys(navInstance.eventInstance.pointers).length;
if (navInstance.eventInstance.state.pointerType === 'touch') {
navInstance.interactable.draggable(this.settings.draggable);
} else {
navInstance.interactable.draggable(false);
}
},
up(event) {
// Set the state of event instance
delete navInstance.eventInstance.pointers[event.pointerId];
navInstance.eventInstance.state.pointerType = null;
navInstance.eventInstance.numPointers = Object.keys(navInstance.eventInstance.pointers).length;
},
move: NOT_IMPLEMENTED,
},
dom: {
resize(event) {
// Retrieve the resized width
// Resize the element's transformer
navInstance.el.container.style.transition = `transform ${TRANSITION_DURATION}ms`;
navInstance.width.element = navInstance.width.element || (navInstance.width.html - MARGIN_WIDTH);
navInstance.transform.set({ scale: (navInstance.width.html - MARGIN_WIDTH) / navInstance.width.element });
setTimeout(() => {
navInstance.el.container.style.transition = '';
}, TRANSITION_DURATION);
putDocumentBackToPlace(navInstance);
},
transitioned: NOT_IMPLEMENTED
},
mousewheel: (event) => {
if (event.ctrlKey) {
// event.preventDefault() is called so that MS Edge does not
// zoom the entire window.
event.preventDefault();
this.eventFn.gesture.zoom.call(navInstance, event);
}
}
};
this.settings = {
draggable: {
inertia: {
resistance: 5,
minSpeed: 400,
endSpeed: 20
},
onstart: this.eventFn.gesture.dragstart,
onmove: this.eventFn.gesture.dragmove,
onend: this.eventFn.gesture.dragend,
},
gesturable: {
onmove: this.eventFn.gesture.zoom,
}
};
}