%PDF- %PDF-
| Direktori : /home1/lightco1/public_html/media/sigplus/engines/scrollplus/js/ |
| Current File : //home1/lightco1/public_html/media/sigplus/engines/scrollplus/js/scrollplus.js |
/**@license scrollplus: a custom scrollbar for your website
* @author Levente Hunyadi
* @version 1.0
* @remarks Copyright (C) 2017-2018 Levente Hunyadi
* @remarks Licensed under GNU/GPLv3, see http://www.gnu.org/licenses/gpl-3.0.html
* @see http://hunyadi.info.hu/projects/scrollplus
**/
/*
* scrollplus: a custom scrollbar for your website
* Copyright 2017-2018 Levente Hunyadi
*
* scrollplus is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* scrollplus is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with scrollplus. If not, see <http://www.gnu.org/licenses/>.
*/
/*{"compilation_level":"ADVANCED_OPTIMIZATIONS"}*/
'use strict';
/**
* Content type shown in the pop-up window.
* @enum {string}
*/
const ScrollPlusOrientation = {
Vertical: 'vertical',
Horizontal: 'horizontal'
};
/**
* Options for the scroll-bar.
*
* @typedef {{
* orientation: (ScrollPlusOrientation|undefined)
* }}
*/
let ScrollPlusOptions;
/** @type {!ScrollPlusOptions} */
const ScrollPlusDefaults = {
'orientation': ScrollPlusOrientation.Vertical
};
/**
* Sets all undefined properties on an object using a reference object.
* @param {Object|null|undefined} obj The original object whose keys are to be enumerated.
* @param {!Object} ref The reference object to provide a default value for unset keys.
* @return {!Object} The original object (or a new object is the original is null) with a value for all keys.
*/
function applyDefaults(obj, ref) {
/** @type {!Object} */
let ret = obj || {};
for (const prop in /** @type {!Object} */ (ref)) {
if (Object.prototype.hasOwnProperty.call(ref, prop) && !(prop in ret)) {
ret[prop] = ref[prop];
}
}
return ret;
}
/**
* Gets the outer height of an element, including margin, border and padding.
* @param {!HTMLElement} elem
* @return {string} A CSS length value, possibly involving `calc`.
*/
function getOuterHeight(elem) {
function isCSSLength(value) {
return /ch|e[mx]|rem|v([hw]|min|max)|px|[cm]m|in|p[ct]$/.test(value);
}
let style = window.getComputedStyle(elem);
let values = ['margin-top','margin-bottom'].map(function (key) {
return style[key];
}).filter(function (value) {
return isCSSLength(value);
});
let css = elem.offsetHeight + 'px';
if (values.length > 0) {
css = 'calc(' + css + ' + ' + values.join(' + ') + ')';
}
return css;
}
/**
* Adds scrollbar behavior to an HTML parent element.
* The following HTML structure is created where the root element corresponds to the original parent element:
* ```
* <div class="scrollplus-container">
* <div class="scrollplus-dock">
* <div class="scrollplus-view">
* <div class="scrollplus-content">
* <!-- original element content -->
* </div>
* </div>
* <div class="scrollplus-track">
* <div class="scrollplus-thumb"></div>
* </div>
* </div>
* </div>
* ```
*
* @constructor
* @param {HTMLElement} elem An element to be made scrollable.
* @param {ScrollPlusOptions=} options
*/
function ScrollPlus(elem, options) {
// element existence check to ensure element is within DOM, some content management
// systems may call the script even if the associated content is not on the page,
// which is the case e.g. with Joomla category list layout or multi-page layout
if (!elem) {
return;
}
let opts = /** @type {!ScrollPlusOptions} */ (applyDefaults(options, ScrollPlusDefaults));
let orientation = opts['orientation'];
elem.classList.add('scrollplus-container');
if (elem.classList.contains('scrollplus-vertical')) {
orientation = ScrollPlusOrientation.Vertical;
} else if (elem.classList.contains('scrollplus-horizontal')) {
orientation = ScrollPlusOrientation.Horizontal;
} else {
elem.classList.add('scrollplus-' + /** @type {string} */ (orientation));
}
/**
* @param {string} cls A CSS class name the newly created element takes.
* @return {!HTMLElement}
*/
function createElement(cls) {
let elem = /** @type {!HTMLElement} */ (document.createElement('div'));
elem.classList.add(cls);
return elem;
}
// create HTML DOM structure
let dock = createElement('scrollplus-dock');
let view = createElement('scrollplus-view');
let content = createElement('scrollplus-content');
let track = createElement('scrollplus-track');
let thumb = createElement('scrollplus-thumb');
// adopt all children of original element
while (elem.firstChild) {
content.appendChild(elem.firstChild); // causes element to be removed from original parent
}
view.appendChild(content);
track.appendChild(thumb);
dock.appendChild(view);
dock.appendChild(track);
elem.appendChild(dock);
/**
* @param {!MouseEvent} evt
*/
function scrollToMousePosition(evt) {
let trackClientRect = track.getBoundingClientRect();
view.scrollLeft = (view.scrollWidth - view.clientWidth) * (evt.clientX - trackClientRect.left - 0.5 * thumb.offsetWidth) / (track.offsetWidth - thumb.offsetWidth);
view.scrollTop = (view.scrollHeight - view.clientHeight) * (evt.clientY - trackClientRect.top - 0.5 * thumb.offsetHeight) / (track.offsetHeight - thumb.offsetHeight);
}
function updateThumbPosition() {
let thumbStyle = thumb.style;
let trackStyle = track.style;
/** @type {number} */
let scrollSize = 1;
/** @type {number} */
let clientSize = 1;
/** @type {number} */
let trackSize = 1;
/** @type {number} */
let offset = 0;
switch (orientation) {
case ScrollPlusOrientation.Vertical:
scrollSize = view.scrollHeight;
clientSize = view.clientHeight;
trackSize = track.offsetHeight;
offset = view.scrollTop;
break;
case ScrollPlusOrientation.Horizontal:
scrollSize = view.scrollWidth;
clientSize = view.clientWidth;
trackSize = track.offsetWidth;
offset = view.scrollLeft;
break;
}
/** @type {number} */
let ratioViewToContent = clientSize / scrollSize;
/** @type {number} */
let extent = Math.max(8, 0.5 * ratioViewToContent * trackSize); // multiply by 0.5 because thumb extends in both directions
/** @type {string} */
let position = scrollSize > clientSize ? 100 * offset / (scrollSize - clientSize) + '%' : '0';
switch (orientation) {
case ScrollPlusOrientation.Vertical:
thumbStyle.top = position;
thumbStyle.margin = (-extent) + 'px 0';
thumbStyle.padding = extent + 'px 0';
trackStyle.padding = extent + 'px 0';
break;
case ScrollPlusOrientation.Horizontal:
thumbStyle.left = position;
thumbStyle.margin = '0 ' + (-extent) + 'px';
thumbStyle.padding = '0 ' + extent + 'px';
trackStyle.padding = '0 ' + extent + 'px';
break;
}
let dockClass = dock.classList;
dockClass.remove('scrollplus-start');
dockClass.remove('scrollplus-end');
if (offset == 0) { // scrolled to top
dockClass.add('scrollplus-start');
}
if (offset == scrollSize - clientSize) { // scrolled to bottom
dockClass.add('scrollplus-end');
}
}
/**
* Prevents the default action associated with an event.
* @param {Event} evt
*/
function preventDefault(evt) {
evt.preventDefault();
}
view.addEventListener('scroll', updateThumbPosition);
window.addEventListener('resize', updateThumbPosition);
track.addEventListener('click', function (event) {
let mouseEvent = /** @type {!MouseEvent} */ (event);
scrollToMousePosition(mouseEvent);
preventDefault(mouseEvent);
});
/** @type {boolean} */
let depressed = false;
thumb.addEventListener('mousedown', function (event) {
let mouseEvent = /** @type {!MouseEvent} */ (event);
if (mouseEvent.buttons == 1) {
depressed = true;
document.addEventListener('selectstart', preventDefault);
scrollToMousePosition(mouseEvent);
}
});
thumb.addEventListener('dragstart', preventDefault);
document.addEventListener('mouseup', function () {
depressed = false;
document.removeEventListener('selectstart', preventDefault);
});
document.addEventListener('mousemove', function (event) {
if (depressed) {
scrollToMousePosition(/** @type {!MouseEvent} */ (event));
}
});
/** @type {!HTMLElement} */
this.view = view;
updateThumbPosition();
window.requestAnimationFrame(function () { // address an issue in Firefox
updateThumbPosition();
});
}
/**
* @param {number} pos_x
* @param {number} pos_y
*/
ScrollPlus.prototype.scrollTo = function (pos_x, pos_y) {
let view = this.view;
if (pos_x >= 0) {
view.scrollLeft = pos_x;
}
if (pos_y >= 0) {
view.scrollTop = pos_y;
}
view.dispatchEvent(new Event('scroll'));
}
ScrollPlus.prototype.scrollToTop = function () {
this.scrollTo(-1,0);
}
ScrollPlus.prototype.scrollToBottom = function () {
this.scrollTo(-1,this.view.scrollHeight);
}
// export symbols
window['ScrollPlus'] = ScrollPlus;
ScrollPlus.prototype['scrollToTop'] = ScrollPlus.prototype.scrollToTop;
ScrollPlus.prototype['scrollToBottom'] = ScrollPlus.prototype.scrollToBottom;