').addclass('scroll-wrapper').addclass(c.attr('class'))
.css('position', c.css('position') == 'absolute' ? 'absolute' : 'relative')
.insertbefore(c).append(c);
if (c.is('textarea')) {
this.containerwrapper = cw = $('
').insertbefore(c).append(c);
w.addclass('scroll-textarea');
}
cw.addclass('scroll-content').css({
"height": "auto",
"margin-bottom": browser.scroll.height * -1 + 'px',
"margin-right": browser.scroll.width * -1 + 'px',
"max-height": ""
});
c.on('scroll' + namespace, function (event) {
if ($.isfunction(o.onscroll)) {
o.onscroll.call(s, {
"maxscroll": s.y.maxscrolloffset,
"scroll": c.scrolltop(),
"size": s.y.size,
"visible": s.y.visible
}, {
"maxscroll": s.x.maxscrolloffset,
"scroll": c.scrollleft(),
"size": s.x.size,
"visible": s.x.visible
});
}
s.x.isvisible && s.x.scroll.bar.css('left', c.scrollleft() * s.x.kx + 'px');
s.y.isvisible && s.y.scroll.bar.css('top', c.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 {
cw.css({
"height": "auto",
"margin-bottom": browser.scroll.height * -1 + 'px',
"margin-right": browser.scroll.width * -1 + 'px',
"max-height": ""
});
}
// 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;
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;
} else {
scrollforward = (data.eventoffset > (data.scrollbaroffset + data.scrollbarsize) ? 1
: (data.eventoffset < data.scrollbaroffset ? -1 : 0));
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);
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', 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);
}
}));