tui-calendar
Version:
TOAST UI Calendar
246 lines (205 loc) • 6.46 kB
JavaScript
/**
* @fileoverview Add autoscroll feature to elements that prevented text selection.
* @author NHN FE Development Lab <dl_javascript@nhn.com>
*/
'use strict';
var util = require('tui-code-snippet');
var domevent = require('../common/domevent');
var domutil = require('../common/domutil');
var Point = require('../common/point');
var SCROLL_INTERVAL = 30;
var SCROLL_MAX = 15;
var SCROLL_CLICK_INCREASED = 2; // In IE, the offset of the actual UI pixel when the scroll bar is clicked is offset.
/**
* Add autoscroll feature to elements that prevented text selection.
* @constructor
* @param {HTMLElement} container - HTMLElement to add autoscroll features.
*/
function AutoScroll(container) {
/**
* @type {HTMLElement}
*/
this.container = container;
/**
* @type {AutoScroll.DIRECTION}
*/
this._direction = AutoScroll.DIRECTION.INSIDE;
/**
* @type {number}
*/
this._offset = 0;
/**
* interval to scrolling
* @type {number}
*/
this._intervalID = 0;
domevent.on(container, {
'mousedown': this._onMouseDown
}, this);
}
/**
* @enum
*/
AutoScroll.DIRECTION = {
INSIDE: 0,
TOP: 1,
RIGHT: 2,
BOTTOM: 3,
LEFT: 4
};
/**
* Instance destroy method.
*/
AutoScroll.prototype.destroy = function() {
domevent.off(this.container, {
'mousedown': this._onMouseDown,
'mousemove': this._onMouseMove,
'mouseup': this._onMouseUp
}, this);
window.clearInterval(this._intervalID);
this._intervalID = this._direction = this.container = null;
};
/**
* Normalize ClientRect and calculate each position of edges.
* @param {ClientRect} clientRect - ClientRect object of element.
* @returns {object} edges.
*/
AutoScroll.prototype._getEdgePositions = function(clientRect) {
return {
top: clientRect.top,
right: clientRect.left + clientRect.width,
bottom: clientRect.bottom,
left: clientRect.left
};
};
/**
* Get element real size ("real size" -> size without scrollbar)
* @param {HTMLElement} el - element want to know real size ("real size" -> size without scrollbar)
* @returns {number[]} real size [width, height]
*/
AutoScroll.prototype.getRealSize = function(el) {
var computed = domutil.getComputedStyle(el),
border,
padding;
border = parseFloat(computed.getPropertyValue('border-top-width')) +
parseFloat(computed.getPropertyValue('border-bottom-width'));
padding = parseFloat(computed.getPropertyValue('padding-top')) +
parseFloat(computed.getPropertyValue('padding-bottom'));
return [el.clientWidth + border + padding, el.clientHeight + border + padding];
};
/**
* Check supplied element has scrollbar.
* @param {HTMLElement} el - element want to know has scrollbar.
* @returns {boolean[]} has scrollbar? [horizontal, vertical]
*/
AutoScroll.prototype.hasScrollbar = function(el) {
var realSize = this.getRealSize(el);
return [
el.offsetWidth > Math.ceil(realSize[0]),
el.offsetHeight > Math.ceil(realSize[1])
];
};
/**
* @param {HTMLElement} el - element want to know.
* @param {MouseEvent} mouseEvent - mouse event object.
* @returns {boolean} mouse pointer is on the scrollbar?
*/
AutoScroll.prototype.isOnScrollbar = function(el, mouseEvent) {
var realSize = this.getRealSize(el),
pos = domevent.getMousePosition(mouseEvent, el),
mouseInScrollbar = false;
mouseInScrollbar = (realSize[0] - SCROLL_CLICK_INCREASED < pos[0] ||
realSize[1] - SCROLL_CLICK_INCREASED < pos[1]);
return mouseInScrollbar;
};
/**
* MouseDown event handler
* @param {MouseEvent} mouseDownEvent - mouse down event
*/
AutoScroll.prototype._onMouseDown = function(mouseDownEvent) {
// only primary button can start drag.
if (domevent.getMouseButton(mouseDownEvent) !== 0) {
return;
}
// deactivate autoscroll feature when mouse is on the scrollbar. (IE)
if (util.browser.msie && this.isOnScrollbar(this.container, mouseDownEvent)) {
return;
}
window.clearInterval(this._intervalID);
this._intervalID = window.setInterval(this._onTick.bind(this), SCROLL_INTERVAL);
domevent.on(global, {
'mousemove': this._onMouseMove,
'mouseup': this._onMouseUp
}, this);
};
/**
* MouseMove event handler
* @param {MouseEvent} mouseEvent - mouse move event object.
*/
AutoScroll.prototype._onMouseMove = function(mouseEvent) {
var edge = this._getEdgePositions(this.container.getBoundingClientRect()),
pos = Point.n(domevent.getMousePosition(mouseEvent));
if (pos.y >= edge.top && pos.y <= edge.bottom &&
pos.x >= edge.left && pos.x <= edge.right) {
this._direction = AutoScroll.DIRECTION.INSIDE;
return;
}
if (pos.y < edge.top) {
this._direction = AutoScroll.DIRECTION.TOP;
this._offset = edge.top - pos.y;
return;
}
if (pos.y > edge.bottom) {
this._direction = AutoScroll.DIRECTION.BOTTOM;
this._offset = pos.y - edge.bottom;
return;
}
if (pos.x < edge.left) {
this._direction = AutoScroll.DIRECTION.LEFT;
this._offset = edge.left - pos.x;
return;
}
this._direction = AutoScroll.DIRECTION.RIGHT;
this._offset = pos.x - edge.right;
};
/**
* MouseUp event handler.
*/
AutoScroll.prototype._onMouseUp = function() {
window.clearInterval(this._intervalID);
this._intervalID = 0;
this._direction = AutoScroll.DIRECTION.INSIDE;
this._offset = 0;
domevent.off(global, {
'mousemove': this._onMouseMove,
'mouseup': this._onMouseUp
}, this);
};
/**
* Interval tick event handler
*/
AutoScroll.prototype._onTick = function() {
var direction = this._direction,
container,
factor;
if (!direction) {
return;
}
container = this.container;
factor = Math.min(this._offset, SCROLL_MAX);
switch (direction) {
case AutoScroll.DIRECTION.TOP:
container.scrollTop -= factor;
break;
case AutoScroll.DIRECTION.RIGHT:
container.scrollLeft += factor;
break;
case AutoScroll.DIRECTION.BOTTOM:
container.scrollTop += factor;
break;
default:
container.scrollLeft -= factor;
break;
}
};
module.exports = AutoScroll;