').addClass('scroll-wrapper').addClass(c.attr('class'))
+ .css('position', c.css('position') === 'absolute' ? 'absolute' : 'relative')
+ .insertBefore(c).append(c);
+
+ if (o.isRtl) {
+ w.addClass('scroll--rtl');
+ }
+
+ if (c.is('textarea')) {
+ this.containerWrapper = cw = $('
').insertBefore(c).append(c);
+ w.addClass('scroll-textarea');
+ }
+
+ cssOptions = {
+ "height": "auto",
+ "margin-bottom": browser.scroll.height * -1 + 'px',
+ "max-height": ""
+ };
+ cssOptions[o.isRtl ? 'margin-left' : 'margin-right'] = browser.scroll.width * -1 + 'px';
+
+ cw.addClass('scroll-content').css(cssOptions);
+
+ c.on('scroll' + namespace, function (event) {
+ var scrollLeft = c.scrollLeft();
+ var scrollTop = c.scrollTop();
+ if (o.isRtl) {
+ // webkit 0:100
+ // ie/edge 100:0
+ // firefox -100:0
+ switch (true) {
+ case browser.firefox:
+ scrollLeft = Math.abs(scrollLeft);
+ case browser.msedge || browser.msie:
+ scrollLeft = c[0].scrollWidth - c[0].clientWidth - scrollLeft;
+ break;
+ }
+ }
+ if ($.isFunction(o.onScroll)) {
+ o.onScroll.call(S, {
+ maxScroll: s.y.maxScrollOffset,
+ scroll: scrollTop,
+ size: s.y.size,
+ visible: s.y.visible
+ }, {
+ maxScroll: s.x.maxScrollOffset,
+ scroll: scrollLeft,
+ size: s.x.size,
+ visible: s.x.visible
+ });
+ }
+ s.x.isVisible && s.x.scroll.bar.css('left', scrollLeft * s.x.kx + 'px');
+ s.y.isVisible && s.y.scroll.bar.css('top', scrollTop * s.y.kx + 'px');
+ });
+
+ /* prevent native scrollbars to be visible on #anchor click */
+ w.on('scroll' + namespace, function () {
+ w.scrollTop(0).scrollLeft(0);
+ });
+
+ if (o.disableBodyScroll) {
+ var handleMouseScroll = function (event) {
+ isVerticalScroll(event) ?
+ s.y.isVisible && s.y.mousewheel(event) :
+ s.x.isVisible && s.x.mousewheel(event);
+ };
+ w.on('MozMousePixelScroll' + namespace, handleMouseScroll);
+ w.on('mousewheel' + namespace, handleMouseScroll);
+
+ if (browser.mobile) {
+ w.on('touchstart' + namespace, function (event) {
+ var touch = event.originalEvent.touches && event.originalEvent.touches[0] || event;
+ var originalTouch = {
+ pageX: touch.pageX,
+ pageY: touch.pageY
+ };
+ var originalScroll = {
+ left: c.scrollLeft(),
+ top: c.scrollTop()
+ };
+ $(document).on('touchmove' + namespace, function (event) {
+ var touch = event.originalEvent.targetTouches && event.originalEvent.targetTouches[0] || event;
+ c.scrollLeft(originalScroll.left + originalTouch.pageX - touch.pageX);
+ c.scrollTop(originalScroll.top + originalTouch.pageY - touch.pageY);
+ event.preventDefault();
+ });
+ $(document).on('touchend' + namespace, function () {
+ $(document).off(namespace);
+ });
+ });
+ }
+ }
+ if ($.isFunction(o.onInit)) {
+ o.onInit.apply(this, [c]);
+ }
+ } else {
+ cssOptions = {
+ "height": "auto",
+ "margin-bottom": browser.scroll.height * -1 + 'px',
+ "max-height": ""
+ };
+ cssOptions[o.isRtl ? 'margin-left' : 'margin-right'] = browser.scroll.width * -1 + 'px';
+ cw.css(cssOptions);
+ }
+
+ // init scrollbars & recalculate sizes
+ $.each(s, function (d, scrollx) {
+
+ var scrollCallback = null;
+ var scrollForward = 1;
+ var scrollOffset = (d === 'x') ? 'scrollLeft' : 'scrollTop';
+ var scrollStep = o.scrollStep;
+ var scrollTo = function () {
+ var currentOffset = c[scrollOffset]();
+ c[scrollOffset](currentOffset + scrollStep);
+ if (scrollForward == 1 && (currentOffset + scrollStep) >= scrollToValue)
+ currentOffset = c[scrollOffset]();
+ if (scrollForward == -1 && (currentOffset + scrollStep) <= scrollToValue)
+ currentOffset = c[scrollOffset]();
+ if (c[scrollOffset]() == currentOffset && scrollCallback) {
+ scrollCallback();
+ }
+ }
+ var scrollToValue = 0;
+
+ if (!scrollx.scroll) {
+
+ scrollx.scroll = S._getScroll(o['scroll' + d]).addClass('scroll-' + d);
+
+ if (o.showArrows) {
+ scrollx.scroll.addClass('scroll-element_arrows_visible');
+ }
+
+ scrollx.mousewheel = function (event) {
+
+ if (!scrollx.isVisible || (d === 'x' && isVerticalScroll(event))) {
+ return true;
+ }
+ if (d === 'y' && !isVerticalScroll(event)) {
+ s.x.mousewheel(event);
+ return true;
+ }
+
+ var delta = event.originalEvent.wheelDelta * -1 || event.originalEvent.detail;
+ var maxScrollValue = scrollx.size - scrollx.visible - scrollx.offset;
+
+ // fix new mozilla
+ if (!delta) {
+ if (d === 'x' && !!event.originalEvent.deltaX) {
+ delta = event.originalEvent.deltaX * 40;
+ } else if (d === 'y' && !!event.originalEvent.deltaY) {
+ delta = event.originalEvent.deltaY * 40;
+ }
+ }
+
+ if ((delta > 0 && scrollToValue < maxScrollValue) || (delta < 0 && scrollToValue > 0)) {
+ scrollToValue = scrollToValue + delta;
+ if (scrollToValue < 0)
+ scrollToValue = 0;
+ if (scrollToValue > maxScrollValue)
+ scrollToValue = maxScrollValue;
+
+ S.scrollTo = S.scrollTo || {};
+ S.scrollTo[scrollOffset] = scrollToValue;
+ setTimeout(function () {
+ if (S.scrollTo) {
+ c.stop().animate(S.scrollTo, 240, 'linear', function () {
+ scrollToValue = c[scrollOffset]();
+ });
+ S.scrollTo = null;
+ }
+ }, 1);
+ }
+
+ event.preventDefault();
+ return false;
+ };
+
+ scrollx.scroll
+ .on('MozMousePixelScroll' + namespace, scrollx.mousewheel)
+ .on('mousewheel' + namespace, scrollx.mousewheel)
+ .on('mouseenter' + namespace, function () {
+ scrollToValue = c[scrollOffset]();
+ });
+
+ // handle arrows & scroll inner mousedown event
+ scrollx.scroll.find('.scroll-arrow, .scroll-element_track')
+ .on('mousedown' + namespace, function (event) {
+
+ if (event.which != 1) // lmb
+ return true;
+
+ scrollForward = 1;
+
+ var data = {
+ eventOffset: event[(d === 'x') ? 'pageX' : 'pageY'],
+ maxScrollValue: scrollx.size - scrollx.visible - scrollx.offset,
+ scrollbarOffset: scrollx.scroll.bar.offset()[(d === 'x') ? 'left' : 'top'],
+ scrollbarSize: scrollx.scroll.bar[(d === 'x') ? 'outerWidth' : 'outerHeight']()
+ };
+ var timeout = 0, timer = 0;
+
+ if ($(this).hasClass('scroll-arrow')) {
+ scrollForward = $(this).hasClass("scroll-arrow_more") ? 1 : -1;
+ scrollStep = o.scrollStep * scrollForward;
+ scrollToValue = scrollForward > 0 ? data.maxScrollValue : 0;
+ if (o.isRtl) {
+ switch(true){
+ case browser.firefox:
+ scrollToValue = scrollForward > 0 ? 0: data.maxScrollValue * -1;
+ break;
+ case browser.msie || browser.msedge:
+ break;
+ }
+ }
+ } else {
+ scrollForward = (data.eventOffset > (data.scrollbarOffset + data.scrollbarSize) ? 1
+ : (data.eventOffset < data.scrollbarOffset ? -1 : 0));
+ if(d === 'x' && o.isRtl && (browser.msie || browser.msedge))
+ scrollForward = scrollForward * -1;
+ scrollStep = Math.round(scrollx.visible * 0.75) * scrollForward;
+ scrollToValue = (data.eventOffset - data.scrollbarOffset -
+ (o.stepScrolling ? (scrollForward == 1 ? data.scrollbarSize : 0)
+ : Math.round(data.scrollbarSize / 2)));
+ scrollToValue = c[scrollOffset]() + (scrollToValue / scrollx.kx);
+ }
+
+ S.scrollTo = S.scrollTo || {};
+ S.scrollTo[scrollOffset] = o.stepScrolling ? c[scrollOffset]() + scrollStep : scrollToValue;
+
+ if (o.stepScrolling) {
+ scrollCallback = function () {
+ scrollToValue = c[scrollOffset]();
+ clearInterval(timer);
+ clearTimeout(timeout);
+ timeout = 0;
+ timer = 0;
+ };
+ timeout = setTimeout(function () {
+ timer = setInterval(scrollTo, 40);
+ }, o.duration + 100);
+ }
+
+ setTimeout(function () {
+ if (S.scrollTo) {
+ c.animate(S.scrollTo, o.duration);
+ S.scrollTo = null;
+ }
+ }, 1);
+
+ return S._handleMouseDown(scrollCallback, event);
+ });
+
+ // handle scrollbar drag'n'drop
+ scrollx.scroll.bar.on('mousedown' + namespace, function (event) {
+
+ if (event.which != 1) // lmb
+ return true;
+
+ var eventPosition = event[(d === 'x') ? 'pageX' : 'pageY'];
+ var initOffset = c[scrollOffset]();
+
+ scrollx.scroll.addClass('scroll-draggable');
+
+ $(document).on('mousemove' + namespace, function (event) {
+ var diff = parseInt((event[(d === 'x') ? 'pageX' : 'pageY'] - eventPosition) / scrollx.kx, 10);
+ if (d === 'x' && o.isRtl && (browser.msie || browser.msedge))
+ diff = diff * -1;
+ c[scrollOffset](initOffset + diff);
+ });
+
+ return S._handleMouseDown(function () {
+ scrollx.scroll.removeClass('scroll-draggable');
+ scrollToValue = c[scrollOffset]();
+ }, event);
+ });
+ }
+ });
+
+ // remove classes & reset applied styles
+ $.each(s, function (d, scrollx) {
+ var scrollClass = 'scroll-scroll' + d + '_visible';
+ var scrolly = (d == "x") ? s.y : s.x;
+
+ scrollx.scroll.removeClass(scrollClass);
+ scrolly.scroll.removeClass(scrollClass);
+ cw.removeClass(scrollClass);
+ });
+
+ // calculate init sizes
+ $.each(s, function (d, scrollx) {
+ $.extend(scrollx, (d == "x") ? {
+ offset: parseInt(c.css('left'), 10) || 0,
+ size: c.prop('scrollWidth'),
+ visible: w.width()
+ } : {
+ offset: parseInt(c.css('top'), 10) || 0,
+ size: c.prop('scrollHeight'),
+ visible: w.height()
+ });
+ });
+
+ // update scrollbar visibility/dimensions
+ this._updateScroll('x', this.scrollx);
+ this._updateScroll('y', this.scrolly);
+
+ if ($.isFunction(o.onUpdate)) {
+ o.onUpdate.apply(this, [c]);
+ }
+
+ // calculate scroll size
+ $.each(s, function (d, scrollx) {
+
+ var cssOffset = (d === 'x') ? 'left' : 'top';
+ var cssFullSize = (d === 'x') ? 'outerWidth' : 'outerHeight';
+ var cssSize = (d === 'x') ? 'width' : 'height';
+ var offset = parseInt(c.css(cssOffset), 10) || 0;
+
+ var AreaSize = scrollx.size;
+ var AreaVisible = scrollx.visible + offset;
+
+ var scrollSize = scrollx.scroll.size[cssFullSize]() + (parseInt(scrollx.scroll.size.css(cssOffset), 10) || 0);
+
+ if (o.autoScrollSize) {
+ scrollx.scrollbarSize = parseInt(scrollSize * AreaVisible / AreaSize, 10);
+ scrollx.scroll.bar.css(cssSize, scrollx.scrollbarSize + 'px');
+ }
+
+ scrollx.scrollbarSize = scrollx.scroll.bar[cssFullSize]();
+ scrollx.kx = ((scrollSize - scrollx.scrollbarSize) / (AreaSize - AreaVisible)) || 1;
+ scrollx.maxScrollOffset = AreaSize - AreaVisible;
+ });
+
+ c.scrollLeft(initScroll.scrollLeft).scrollTop(initScroll.scrollTop).trigger('scroll');
+ },
+ /**
+ * Get scrollx/scrolly object
+ *
+ * @param {Mixed} scroll
+ * @returns {jQuery} scroll object
+ */
+ _getScroll: function (scroll) {
+ var types = {
+ advanced: [
+ '
'
+ ].join(''),
+ simple: [
+ '
'
+ ].join('')
+ };
+ if (types[scroll]) {
+ scroll = types[scroll];
+ }
+ if (!scroll) {
+ scroll = types['simple'];
+ }
+ if (typeof (scroll) == 'string') {
+ scroll = $(scroll).appendTo(this.wrapper);
+ } else {
+ scroll = $(scroll);
+ }
+ $.extend(scroll, {
+ bar: scroll.find('.scroll-bar'),
+ size: scroll.find('.scroll-element_size'),
+ track: scroll.find('.scroll-element_track')
+ });
+ return scroll;
+ },
+ _handleMouseDown: function (callback, event) {
+
+ var namespace = this.namespace;
+
+ $(document).on('blur' + namespace, function () {
+ $(document).add('body').off(namespace);
+ callback && callback();
+ });
+ $(document).on('dragstart' + namespace, function (event) {
+ event.preventDefault();
+ return false;
+ });
+ $(document).on('mouseup' + namespace, function () {
+ $(document).add('body').off(namespace);
+ callback && callback();
+ });
+ $('body').on('selectstart' + namespace, function (event) {
+ event.preventDefault();
+ return false;
+ });
+
+ event && event.preventDefault();
+ return false;
+ },
+ _updateScroll: function (d, scrollx) {
+
+ var container = this.container,
+ containerWrapper = this.containerWrapper || container,
+ scrollClass = 'scroll-scroll' + d + '_visible',
+ scrolly = (d === 'x') ? this.scrolly : this.scrollx,
+ offset = parseInt(this.container.css((d === 'x') ? 'left' : 'top'), 10) || 0,
+ wrapper = this.wrapper;
+
+ var AreaSize = scrollx.size;
+ var AreaVisible = scrollx.visible + offset;
+
+ scrollx.isVisible = (AreaSize - AreaVisible) > 1; // bug in IE9/11 with 1px diff
+ if (scrollx.isVisible) {
+ scrollx.scroll.addClass(scrollClass);
+ scrolly.scroll.addClass(scrollClass);
+ containerWrapper.addClass(scrollClass);
+ } else {
+ scrollx.scroll.removeClass(scrollClass);
+ scrolly.scroll.removeClass(scrollClass);
+ containerWrapper.removeClass(scrollClass);
+ }
+
+ if (d === 'y') {
+ if (container.is('textarea') || AreaSize < AreaVisible) {
+ containerWrapper.css({
+ "height": (AreaVisible + browser.scroll.height) + 'px',
+ "max-height": "none"
+ });
+ } else {
+ containerWrapper.css({
+ //"height": "auto", // do not reset height value: issue with height:100%!
+ "max-height": (AreaVisible + browser.scroll.height) + 'px'
+ });
+ }
+ }
+
+ if (scrollx.size != container.prop('scrollWidth')
+ || scrolly.size != container.prop('scrollHeight')
+ || scrollx.visible != wrapper.width()
+ || scrolly.visible != wrapper.height()
+ || scrollx.offset != (parseInt(container.css('left'), 10) || 0)
+ || scrolly.offset != (parseInt(container.css('top'), 10) || 0)
+ ) {
+ $.extend(this.scrollx, {
+ offset: parseInt(container.css('left'), 10) || 0,
+ size: container.prop('scrollWidth'),
+ visible: wrapper.width()
+ });
+ $.extend(this.scrolly, {
+ offset: parseInt(container.css('top'), 10) || 0,
+ size: this.container.prop('scrollHeight'),
+ visible: wrapper.height()
+ });
+ this._updateScroll(d === 'x' ? 'y' : 'x', scrolly);
+ }
+ }
+ };
+
+ var CustomScrollbar = BaseScrollbar;
+
+ /*
+ * Extend jQuery as plugin
+ *
+ * @param {Mixed} command to execute
+ * @param {Mixed} arguments as Array
+ * @return {jQuery}
+ */
+ $.fn.scrollbar = function (command, args) {
+ if (typeof command !== 'string') {
+ args = command;
+ command = 'init';
+ }
+ if (typeof args === 'undefined') {
+ args = [];
+ }
+ if (!$.isArray(args)) {
+ args = [args];
+ }
+ this.not('body, .scroll-wrapper').each(function () {
+ var element = $(this),
+ instance = element.data(browser.data.name);
+ if (instance || command === 'init') {
+ if (!instance) {
+ instance = new CustomScrollbar(element);
+ }
+ if (instance[command]) {
+ instance[command].apply(instance, args);
+ }
+ }
+ });
+ return this;
+ };
+
+ /**
+ * Connect default options to global object
+ */
+ $.fn.scrollbar.options = defaults;
+
+
+ /**
+ * Check if scroll content/container size is changed
+ */
+
+ var updateScrollbars = (function () {
+ var timer = 0,
+ timerCounter = 0;
+
+ return function (force) {
+ var i, container, options, scroll, wrapper, scrollx, scrolly;
+ for (i = 0; i < browser.scrolls.length; i++) {
+ scroll = browser.scrolls[i];
+ container = scroll.container;
+ options = scroll.options;
+ wrapper = scroll.wrapper;
+ scrollx = scroll.scrollx;
+ scrolly = scroll.scrolly;
+ if (force || (options.autoUpdate && wrapper && wrapper.is(':visible') &&
+ (container.prop('scrollWidth') != scrollx.size || container.prop('scrollHeight') != scrolly.size || wrapper.width() != scrollx.visible || wrapper.height() != scrolly.visible))) {
+ scroll.init();
+
+ if (options.debug) {
+ window.console && console.log({
+ scrollHeight: container.prop('scrollHeight') + ':' + scroll.scrolly.size,
+ scrollWidth: container.prop('scrollWidth') + ':' + scroll.scrollx.size,
+ visibleHeight: wrapper.height() + ':' + scroll.scrolly.visible,
+ visibleWidth: wrapper.width() + ':' + scroll.scrollx.visible
+ }, true);
+ timerCounter++;
+ }
+ }
+ }
+ if (debug && timerCounter > 10) {
+ window.console && console.log('Scroll updates exceed 10');
+ updateScrollbars = function () {};
+ } else {
+ clearTimeout(timer);
+ timer = setTimeout(updateScrollbars, 300);
+ }
+ };
+ })();
+
+ /* ADDITIONAL FUNCTIONS */
+ /**
+ * Get native browser scrollbar size (height/width)
+ *
+ * @param {Boolean} actual size or CSS size, default - CSS size
+ * @returns {Object} with height, width
+ */
+ function getBrowserScrollSize(actualSize) {
+
+ if (browser.webkit && !actualSize) {
+ return {
+ height: 0,
+ width: 0
+ };
+ }
+
+ if (!browser.data.outer) {
+ var css = {
+ "border": "none",
+ "box-sizing": "content-box",
+ "height": "200px",
+ "margin": "0",
+ "padding": "0",
+ "width": "200px"
+ };
+ browser.data.inner = $("
").css($.extend({}, css));
+ browser.data.outer = $("
").css($.extend({
+ "left": "-1000px",
+ "overflow": "scroll",
+ "position": "absolute",
+ "top": "-1000px"
+ }, css)).append(browser.data.inner).appendTo("body");
+ }
+
+ browser.data.outer.scrollLeft(1000).scrollTop(1000);
+
+ return {
+ height: Math.ceil((browser.data.outer.offset().top - browser.data.inner.offset().top) || 0),
+ width: Math.ceil((browser.data.outer.offset().left - browser.data.inner.offset().left) || 0)
+ };
+ }
+
+ /**
+ * Check if native browser scrollbars overlay content
+ *
+ * @returns {Boolean}
+ */
+ function isScrollOverlaysContent() {
+ var scrollSize = getBrowserScrollSize(true);
+ return !(scrollSize.height || scrollSize.width);
+ }
+
+ function isVerticalScroll(event) {
+ var e = event.originalEvent;
+ if (e.axis && e.axis === e.HORIZONTAL_AXIS)
+ return false;
+ if (e.wheelDeltaX)
+ return false;
+ return true;
+ }
+
+
+ /**
+ * Extend AngularJS as UI directive
+ * and expose a provider for override default config
+ *
+ */
+ if (window.angular) {
+ (function (angular) {
+ angular.module('jQueryScrollbar', [])
+ .provider('jQueryScrollbar', function () {
+ var defaultOptions = defaults;
+ return {
+ setOptions: function (options) {
+ angular.extend(defaultOptions, options);
+ },
+ $get: function () {
+ return {
+ options: angular.copy(defaultOptions)
+ };
+ }
+ };
+ })
+ .directive('jqueryScrollbar', ['jQueryScrollbar', '$parse', function (jQueryScrollbar, $parse) {
+ return {
+ restrict: "AC",
+ link: function (scope, element, attrs) {
+ var model = $parse(attrs.jqueryScrollbar),
+ options = model(scope);
+ element.scrollbar(options || jQueryScrollbar.options)
+ .on('$destroy', function () {
+ element.scrollbar('destroy');
+ });
+ }
+ };
+ }]);
+ })(window.angular);
+ }
+}));