/*!
* jQuery floatingScroll Plugin v2.3.3
* https://github.com/Amphiluke/floating-scroll
* (c) 2017 Amphiluke
*/
(function (global, factory) {
"use strict";
if (typeof define === "function" && define.amd) {
define(["jquery"], factory);
} else if (typeof module === "object" && module.exports) {
factory(require("jquery"));
} else {
factory(global.jQuery);
}
}(this, function ($) {
"use strict";
function FScroll(cont) {
var inst = this,
scrollBody = cont.closest(".fl-scrolls-body");
inst.cont = cont[0];
if (scrollBody.length) {
inst.scrollBody = scrollBody;
}
inst.sbar = inst.initScroll();
inst.visible = true;
inst.updateAPI(); // recalculate floating scrolls and hide those of them whose containers are out of sight
inst.syncSbar(inst.cont);
inst.addEventHandlers();
}
$.extend(FScroll.prototype, {
initScroll: function () {
var flscroll = $("");
$("").appendTo(flscroll).css({width: this.cont.scrollWidth + "px"});
return flscroll.appendTo(this.cont);
},
addEventHandlers: function () {
var inst = this,
handlers,
i, len;
handlers = inst.eventHandlers = [
{
$el: inst.scrollBody || $(window),
handlers: {
// Don't use `$.proxy()` since it makes impossible event unbinding individually per instance
// (see the warning at http://api.jquery.com/unbind/)
scroll: function () {
inst.checkVisibility();
},
resize: function () {
inst.updateAPI();
}
}
},
{
$el: inst.sbar,
handlers: {
scroll: function () {
inst.visible && inst.syncCont(this, true);
}
}
},
{
$el: $(inst.cont),
handlers: {
scroll: function () {
inst.syncSbar(this, true);
},
focusin: function () {
setTimeout(function () {
inst.syncSbar(inst.cont);
}, 0);
},
// The `adjustScroll` event type is kept for backward compatibility only.
"update.fscroll adjustScroll": function (e) {
// Check event namespace to ensure that this is not an extraneous event in a bubbling phase
if (e.namespace === "fscroll" || e.type === "adjustScroll") {
inst.updateAPI();
}
},
"destroy.fscroll": function (e) {
if (e.namespace === "fscroll") {
inst.destroyAPI();
}
}
}
}
];
for (i = 0, len = handlers.length; i < len; i++) {
handlers[i].$el.bind(handlers[i].handlers);
}
},
checkVisibility: function () {
var inst = this,
mustHide = (inst.sbar[0].scrollWidth <= inst.sbar[0].offsetWidth),
contRect,
maxVisibleY;
if (!mustHide) {
contRect = inst.cont.getBoundingClientRect();
maxVisibleY = inst.scrollBody ?
inst.scrollBody[0].getBoundingClientRect().bottom :
window.innerHeight || document.documentElement.clientHeight;
mustHide = ((contRect.bottom <= maxVisibleY) || (contRect.top > maxVisibleY));
}
if (inst.visible === mustHide) {
inst.visible = !mustHide;
// we cannot simply hide a floating scroll bar since its scrollLeft property will not update in that case
inst.sbar.toggleClass("fl-scrolls-hidden");
}
},
syncCont: function (sender, preventSyncSbar) {
// Prevents next syncSbar function from changing scroll position
if (this.preventSyncCont === true) {
this.preventSyncCont = false;
return;
}
this.preventSyncSbar = !!preventSyncSbar;
this.cont.scrollLeft = sender.scrollLeft;
},
syncSbar: function (sender, preventSyncCont) {
// Prevents next syncCont function from changing scroll position
if (this.preventSyncSbar === true) {
this.preventSyncSbar = false;
return;
}
this.preventSyncCont = !!preventSyncCont;
this.sbar[0].scrollLeft = sender.scrollLeft;
},
// Recalculate scroll width and container boundaries
updateAPI: function () {
var inst = this,
cont = inst.cont,
pos = cont.getBoundingClientRect();
inst.sbar.width($(cont).outerWidth());
if (!inst.scrollBody) {
inst.sbar.css("left", pos.left + "px");
}
$("div", inst.sbar).width(cont.scrollWidth);
inst.checkVisibility(); // fixes issue #2
},
// Remove a scrollbar and all related event handlers
destroyAPI: function () {
var handlers = this.eventHandlers,
i, len;
for (i = 0, len = handlers.length; i < len; i++) {
handlers[i].$el.unbind(handlers[i].handlers);
}
this.sbar.remove();
}
});
// `attachScroll` is the old alias used in v1.X. Temporally kept for backward compatibility.
$.fn.attachScroll = $.fn.floatingScroll = function (method) {
if (!arguments.length || method === "init") {
this.each(function () {
new FScroll($(this));
});
} else if (FScroll.prototype.hasOwnProperty(method + "API")) {
this.trigger(method + ".fscroll");
}
return this;
};
}));