You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
441 lines
12 KiB
441 lines
12 KiB
/**
|
|
<b>Ace custom scroller</b>. It is not as feature-rich as plugins such as NiceScroll but it's good enough for most cases.
|
|
*/
|
|
(function($ , undefined) {
|
|
var Ace_Scroll = function(element , _settings) {
|
|
var self = this;
|
|
var settings = $.extend({}, $.fn.ace_scroll.defaults, _settings);
|
|
|
|
this.size = 0;
|
|
this.$element = $(element);
|
|
this.element = element;
|
|
|
|
var vertical = true;
|
|
|
|
var disabled = false;
|
|
var active = false;
|
|
var created = false;
|
|
|
|
var $content_wrap = null, content_wrap = null;
|
|
var $track = null, $bar = null, track = null, bar = null;
|
|
var bar_style = null;
|
|
|
|
var bar_size = 0, bar_pos = 0, bar_max_pos = 0, bar_size_2 = 0, move_bar = true;
|
|
var reset_once = false;
|
|
|
|
var css_pos,
|
|
css_size,
|
|
max_css_size,
|
|
client_size,
|
|
scroll_direction,
|
|
scroll_size;
|
|
|
|
var ratio = 1;
|
|
var inline_style = false;
|
|
var mouse_track = false;
|
|
var mouse_release_target = 'onmouseup' in window ? window : 'html';
|
|
var dragEvent = settings.dragEvent || false;
|
|
|
|
var trigger_scroll = _settings.scrollEvent || false;
|
|
|
|
this.create = function(_settings) {
|
|
if(created) return;
|
|
//if(disabled) return;
|
|
if(_settings) settings = $.extend({}, $.fn.ace_scroll.defaults, _settings);
|
|
|
|
this.size = parseInt(this.$element.attr('data-size')) || settings.size || 200;
|
|
vertical = !settings['horizontal'];
|
|
|
|
css_pos = vertical ? 'top' : 'left';//'left' for horizontal
|
|
css_size = vertical ? 'height' : 'width';//'width' for horizontal
|
|
max_css_size = vertical ? 'maxHeight' : 'maxWidth';
|
|
|
|
client_size = vertical ? 'clientHeight' : 'clientWidth';
|
|
scroll_direction = vertical ? 'scrollTop' : 'scrollLeft';
|
|
scroll_size = vertical ? 'scrollHeight' : 'scrollWidth';
|
|
|
|
|
|
|
|
this.$element.addClass('ace-scroll '+((vertical? '' : ' scroll-hz') + (settings.styleClass ? ' '+settings.styleClass : '')));
|
|
if(this.$element.css('position') == 'static') {
|
|
inline_style = this.element.style.position;
|
|
this.element.style.position = 'relative';
|
|
} else inline_style = false;
|
|
|
|
this.$element.wrapInner('<div class="scroll-content" />');
|
|
this.$element.prepend('<div class="scroll-track"><div class="scroll-bar"></div></div>');
|
|
|
|
$content_wrap = this.$element.find('.scroll-content').eq(0);
|
|
if(!vertical) $content_wrap.wrapInner('<div />');
|
|
|
|
content_wrap = $content_wrap.get(0);
|
|
|
|
$track = this.$element.find('.scroll-track').eq(0);
|
|
$bar = $track.find('.scroll-bar').eq(0);
|
|
track = $track.get(0);
|
|
bar = $bar.get(0);
|
|
bar_style = bar.style;
|
|
|
|
$track.hide();
|
|
|
|
|
|
//if(!touchDrag) {
|
|
$track.on('mousedown', mouse_down_track);
|
|
$bar.on('mousedown', mouse_down_bar);
|
|
//}
|
|
|
|
$content_wrap.on('scroll', function() {
|
|
if(move_bar) {
|
|
bar_pos = parseInt(Math.round(this[scroll_direction] * ratio));
|
|
bar_style[css_pos] = bar_pos + 'px';
|
|
}
|
|
move_bar = false;
|
|
if(trigger_scroll) this.$element.trigger('scroll', [content_wrap]);
|
|
})
|
|
|
|
if(settings.mouseWheel) {
|
|
var lock = settings.mouseWheelLock;
|
|
var lock_anyway = !settings.lockAnyway;
|
|
|
|
this.$element.on('mousewheel.ace_scroll DOMMouseScroll.ace_scroll', function(event) {
|
|
if(disabled) return;
|
|
if(!active) return lock_anyway;
|
|
|
|
if(mouse_track) {
|
|
mouse_track = false;
|
|
$('html').off('.ace_scroll')
|
|
$(mouse_release_target).off('.ace_scroll');
|
|
if(dragEvent) self.$element.trigger('drag.end');
|
|
}
|
|
|
|
var delta = event.originalEvent.detail < 0 || event.originalEvent.wheelDelta > 0 ? 1 : -1
|
|
var scrollEnd = false//have we reached the end of scrolling?
|
|
|
|
var clientSize = content_wrap[client_size], scrollAmount = content_wrap[scroll_direction];
|
|
if( !lock ) {
|
|
if(delta == -1) scrollEnd = (content_wrap[scroll_size] <= scrollAmount + clientSize);
|
|
else scrollEnd = (scrollAmount == 0);
|
|
}
|
|
|
|
self.move_bar(true);
|
|
|
|
var step = parseInt(Math.round(Math.min(Math.max(clientSize / 8 , 54)) , self.size )) + 1;
|
|
content_wrap[scroll_direction] = scrollAmount - (delta * step);
|
|
|
|
return scrollEnd && lock_anyway;
|
|
})
|
|
}
|
|
|
|
|
|
//swipe not available yet
|
|
var touchDrag = ace.vars['touch'] && 'ace_drag' in $.event.special && settings.touchDrag //&& !settings.touchSwipe;
|
|
//add drag event for touch devices to scroll
|
|
if(touchDrag/** || ($.fn.swipe && settings.touchSwipe)*/) {
|
|
var dir = '', event_name = touchDrag ? 'ace_drag' : 'swipe';
|
|
this.$element.on(event_name + '.ace_scroll', function(event) {
|
|
dir = event.direction;
|
|
if( (vertical && (dir == 'up' || dir == 'down'))
|
|
||
|
|
(!vertical && (dir == 'left' || dir == 'right'))
|
|
)
|
|
{
|
|
var distance = vertical ? event.dy : event.dx;
|
|
|
|
if(distance != 0) {
|
|
if(Math.abs(distance) > 20 && touchDrag) distance = distance * 2;
|
|
|
|
self.move_bar(true);
|
|
content_wrap[scroll_direction] = content_wrap[scroll_direction] + distance;
|
|
}
|
|
|
|
//if(event.retval) event.retval['cancel'] = true;//prevents document scroll
|
|
}
|
|
//else if(event.retval) event.retval['cancel'] = false;//allows document scroll
|
|
|
|
})
|
|
}
|
|
|
|
if(settings.hoverReset) {
|
|
//some mobile browsers don't have mouseenter
|
|
this.$element.on('mouseenter.ace_scroll touchstart.ace_scroll', function() {
|
|
self.reset();
|
|
})
|
|
}
|
|
|
|
if(!vertical) $content_wrap.children(0).css(css_size, this.size);//the extra wrapper
|
|
$content_wrap.css(max_css_size , this.size);
|
|
|
|
disabled = false;
|
|
created = true;
|
|
}
|
|
this.is_active = function() {
|
|
return active;
|
|
}
|
|
this.is_enabled = function() {
|
|
return !disabled;
|
|
}
|
|
this.move_bar = function($move) {
|
|
move_bar = $move;
|
|
}
|
|
|
|
this.reset = function() {
|
|
if(disabled) return;// this;
|
|
if(!created) this.create();
|
|
|
|
var content_size = vertical ? content_wrap[scroll_size] : this.size;
|
|
if( (vertical && content_size == 0) || (!vertical && this.element.scrollWidth == 0) ) {
|
|
//elemen is hidden
|
|
//this.$element.addClass('scroll-hidden');
|
|
this.$element.removeClass('scroll-active')
|
|
return;// this;
|
|
}
|
|
|
|
|
|
var available_space = vertical ? this.size : content_wrap.clientWidth;
|
|
|
|
if(!vertical) $content_wrap.children(0).css(css_size, this.size);//the extra wrapper
|
|
$content_wrap.css(max_css_size , this.size);
|
|
|
|
if(content_size > available_space) {
|
|
active = true;
|
|
$track.css(css_size, available_space).show();
|
|
|
|
ratio = parseFloat((available_space / content_size).toFixed(5))
|
|
|
|
bar_size = parseInt(Math.round(available_space * ratio));
|
|
bar_size_2 = parseInt(Math.round(bar_size / 2));
|
|
|
|
bar_max_pos = available_space - bar_size;
|
|
bar_pos = parseInt(Math.round(content_wrap[scroll_direction] * ratio));
|
|
|
|
bar_style[css_size] = bar_size + 'px';
|
|
bar_style[css_pos] = bar_pos + 'px';
|
|
|
|
this.$element.addClass('scroll-active')
|
|
|
|
if(!reset_once) {
|
|
//this.$element.removeClass('scroll-hidden');
|
|
if(settings.reset) {
|
|
//reset scrollbar to zero position at first
|
|
content_wrap[scroll_direction] = 0;
|
|
bar_style[css_pos] = 0;
|
|
}
|
|
reset_once = true;
|
|
}
|
|
} else {
|
|
active = false;
|
|
$track.hide();
|
|
this.$element.removeClass('scroll-active');
|
|
$content_wrap.css(max_css_size , '');
|
|
}
|
|
|
|
return;// this;
|
|
}
|
|
this.disable = function() {
|
|
content_wrap[scroll_direction] = 0;
|
|
bar_style[css_pos] = 0;
|
|
|
|
disabled = true;
|
|
active = false;
|
|
$track.hide();
|
|
|
|
this.$element.removeClass('scroll-active');
|
|
$content_wrap.css(max_css_size , '');
|
|
|
|
return this;
|
|
}
|
|
this.enable = function() {
|
|
disabled = false;
|
|
this.reset();
|
|
|
|
return this;
|
|
}
|
|
this.destroy = function() {
|
|
active = false;
|
|
disabled = false;
|
|
created = false;
|
|
|
|
this.$element.removeClass('ace-scroll scroll-hz'+(settings.extraClass ? ' '+settings.extraClass : ''));
|
|
this.$element.off('.ace_scroll')
|
|
|
|
if(!vertical) {
|
|
//remove the extra wrapping div
|
|
$content_wrap.find('> div').children().unwrap();
|
|
}
|
|
$content_wrap.children().unwrap();
|
|
$content_wrap.remove();
|
|
|
|
$track.remove();
|
|
|
|
if(inline_style !== false) this.element.style.position = inline_style;
|
|
|
|
return this;
|
|
}
|
|
this.modify = function(_settings) {
|
|
if(_settings) settings = $.extend({}, $.fn.ace_scroll.defaults, _settings);
|
|
|
|
this.destroy();
|
|
this.create();
|
|
this.reset();
|
|
|
|
return this;
|
|
}
|
|
this.update = function(_settings) {
|
|
//if(_settings) settings = $.extend({}, $.fn.ace_scroll.defaults, _settings);
|
|
this.size = _settings.size;
|
|
return this;
|
|
}
|
|
|
|
|
|
this.update_scroll = function() {
|
|
move_bar = false;
|
|
bar_style[css_pos] = bar_pos + 'px';
|
|
content_wrap[scroll_direction] = parseInt(Math.round(bar_pos / ratio));
|
|
}
|
|
|
|
function mouse_down_track(e) {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
|
|
var track_offset = $track.offset();
|
|
var track_pos = track_offset[css_pos];//top for vertical, left for horizontal
|
|
var mouse_pos = vertical ? e.pageY : e.pageX;
|
|
|
|
if(mouse_pos > track_pos + bar_pos) {
|
|
bar_pos = mouse_pos - track_pos - bar_size + bar_size_2;
|
|
if(bar_pos > bar_max_pos) {
|
|
bar_pos = bar_max_pos;
|
|
}
|
|
}
|
|
else {
|
|
bar_pos = mouse_pos - track_pos - bar_size_2;
|
|
if(bar_pos < 0) bar_pos = 0;
|
|
}
|
|
|
|
self.update_scroll()
|
|
}
|
|
|
|
var mouse_pos1 = -1, mouse_pos2 = -1;
|
|
function mouse_down_bar(e) {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
|
|
if(vertical) {
|
|
mouse_pos2 = mouse_pos1 = e.pageY;
|
|
} else {
|
|
mouse_pos2 = mouse_pos1 = e.pageX;
|
|
}
|
|
|
|
mouse_track = true;
|
|
$('html').off('mousemove.ace_scroll').on('mousemove.ace_scroll', mouse_move_bar)
|
|
$(mouse_release_target).off('mouseup.ace_scroll').on('mouseup.ace_scroll', mouse_up_bar);
|
|
|
|
$track.addClass('active');
|
|
if(dragEvent) self.$element.trigger('drag.start');
|
|
}
|
|
function mouse_move_bar(e) {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
|
|
if(vertical) {
|
|
mouse_pos2 = e.pageY;
|
|
} else {
|
|
mouse_pos2 = e.pageX;
|
|
}
|
|
|
|
|
|
if(mouse_pos2 - mouse_pos1 + bar_pos > bar_max_pos) {
|
|
mouse_pos2 = mouse_pos1 + bar_max_pos - bar_pos;
|
|
} else if(mouse_pos2 - mouse_pos1 + bar_pos < 0) {
|
|
mouse_pos2 = mouse_pos1 - bar_pos;
|
|
}
|
|
bar_pos = bar_pos + (mouse_pos2 - mouse_pos1);
|
|
|
|
mouse_pos1 = mouse_pos2;
|
|
|
|
if(bar_pos < 0) {
|
|
bar_pos = 0;
|
|
}
|
|
else if(bar_pos > bar_max_pos) {
|
|
bar_pos = bar_max_pos;
|
|
}
|
|
|
|
self.update_scroll()
|
|
}
|
|
function mouse_up_bar(e) {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
|
|
mouse_track = false;
|
|
$('html').off('.ace_scroll')
|
|
$(mouse_release_target).off('.ace_scroll');
|
|
|
|
$track.removeClass('active');
|
|
if(dragEvent) self.$element.trigger('drag.end');
|
|
}
|
|
|
|
this.create();
|
|
this.reset();
|
|
|
|
return this;
|
|
}
|
|
|
|
|
|
$.fn.ace_scroll = function (option,value) {
|
|
var retval;
|
|
|
|
var $set = this.each(function () {
|
|
var $this = $(this);
|
|
var data = $this.data('ace_scroll');
|
|
var options = typeof option === 'object' && option;
|
|
|
|
if (!data) $this.data('ace_scroll', (data = new Ace_Scroll(this, options)));
|
|
//else if(typeof options == 'object') data['modify'](options);
|
|
if (typeof option === 'string') retval = data[option](value);
|
|
});
|
|
|
|
return (retval === undefined) ? $set : retval;
|
|
};
|
|
|
|
|
|
$.fn.ace_scroll.defaults = {
|
|
'size' : 200,
|
|
'horizontal': false,
|
|
'mouseWheel': true,
|
|
'mouseWheelLock': false,
|
|
'lockAnyway': false,
|
|
'styleClass' : false,
|
|
|
|
'hoverReset': true //reset scrollbar sizes on mouse hover because of possible sizing changes
|
|
,
|
|
'reset': false //true= set scrollTop = 0
|
|
,
|
|
'dragEvent': false
|
|
,
|
|
'touchDrag': true
|
|
,
|
|
'touchSwipe': false
|
|
,
|
|
'scrollEvent': false //trigger scroll event
|
|
/**
|
|
,
|
|
'track' : true,
|
|
'show' : false,
|
|
'dark': false,
|
|
'alwaysVisible': false,
|
|
'margin': false,
|
|
'thin': false,
|
|
'position': 'right'
|
|
*/
|
|
}
|
|
|
|
/**
|
|
$(document).on('ace.settings.ace_scroll', function(e, name) {
|
|
if(name == 'sidebar_collapsed') $('.ace-scroll').scroller('reset');
|
|
});
|
|
$(window).on('resize.ace_scroll', function() {
|
|
$('.ace-scroll').scroller('reset');
|
|
});
|
|
*/
|
|
|
|
})(window.jQuery);
|