winter/modules/backend/assets/js/october.dragscroll.js
2014-08-11 13:57:42 +02:00

359 lines
12 KiB
JavaScript

/*
* Allows to scroll an element content in the horizontal or horizontal directions. This script doesn't use
* absolute positioning and rely on the scrollLeft/scrollTop DHTML properties. The element width should be
* fixed with the CSS or JavaScript.
*
* Events triggered on the element:
* - start.oc.dragScroll
* - drag.oc.dragScroll
* - stop.oc.dragScroll
*
* Options:
* - start - callback function to execute when the drag starts
* - drag - callback function to execute when the element is dragged
* - stop - callback function to execute when the drag ends
* - vertical - determines if the scroll direction is vertical, true by default
* - allowScroll - determines if the mouse wheel scrolling is allowed, true by default
* - scrollClassContainer - if specified, specifies an element or element selector to apply the 'scroll-before' and 'scroll-after' CSS classes,
* depending on whether the scrollable area is in its start or end
* - scrollMarkerContainer - if specified, specifies an element or element selector to inject scroll markers (span elements that con
* contain the ellipses icon, indicating whether scrolling is possible)
*
* Methods:
* - isStart - determines if the scrollable area is in its start (left or top)
* - isEnd - determines if the scrollable area is in its end (right or bottom)
* - goToStart - moves the scrollable area to the start (left or top)
* - goToElement - moves the scrollable area to an element
*
* Dependences:
* - Mouse Wheel plugin (mousewheel.js)
*/
+function ($) { "use strict";
var DragScroll = function (element, options) {
this.options = $.extend({}, DragScroll.DEFAULTS, options)
var
$el = $(element),
el = $el.get(0),
dragStart= 0,
startOffset = 0,
self = this,
dragging = false,
eventElementName = this.options.vertical ? 'pageY' : 'pageX';
this.el = $el
this.scrollClassContainer = this.options.scrollClassContainer ? $(this.options.scrollClassContainer) : $el
/*
* Inject scroll markers
*/
if (this.options.scrollMarkerContainer)
$(this.options.scrollMarkerContainer).append($('<span class="before scroll-marker"></span><span class="after scroll-marker"></span>'))
/*
* Bind events
*/
$el.mousewheel(function(event){
if (!self.options.allowScroll)
return;
var offset = self.options.vertical
? ((event.deltaFactor * event.deltaY) * -1)
: ((event.deltaFactor * event.deltaX) * -1)
return !scrollWheel(offset)
})
$el.on('mousedown', function(event){
startDrag(event)
return false
})
$el.on('touchstart', function(event){
var touchEvent = event.originalEvent;
if (touchEvent.touches.length == 1) {
startDrag(touchEvent.touches[0])
event.stopPropagation()
}
})
$el.on('click', function() {
// Do not handle item clicks while dragging
if ($(document.body).hasClass('drag'))
return false
})
$(document).on('ready', $.proxy(this.fixScrollClasses, this))
$(window).on('resize', $.proxy(this.fixScrollClasses, this))
/*
* Internal event, drag has started
*/
function startDrag(event) {
dragStart = event[eventElementName]
startOffset = self.options.vertical ? $el.scrollTop() : $el.scrollLeft()
if (Modernizr.touch) {
$(window).on('touchmove.dragScroll', function(event){
var touchEvent = event.originalEvent
moveDrag(touchEvent.touches[0])
event.preventDefault()
})
$(window).on('touchend.dragScroll', function(event) {
stopDrag()
})
}
else {
$(window).on('mousemove.dragScroll', function(event){
moveDrag(event)
$(document.body).addClass(self.options.dragClass)
return false
})
$(window).on('mouseup.dragScroll', function(mouseUpEvent){
var isClick = event.pageX == mouseUpEvent.pageX && event.pageY == mouseUpEvent.pageY
stopDrag(isClick)
return false
})
}
}
/*
* Internal event, drag is active
*/
function moveDrag(event) {
var current = event[eventElementName],
offset = dragStart - current
if (Math.abs(offset) > 2) {
if (!dragging) {
dragging = true
$el.trigger('start.oc.dragScroll')
self.options.start();
}
self.options.vertical
? $el.scrollTop(startOffset + offset)
: $el.scrollLeft(startOffset + offset)
$el.trigger('drag.oc.dragScroll')
self.options.drag()
}
}
/*
* Internal event, drag has ended
*/
function stopDrag(click) {
$(window).off('.dragScroll')
dragging = false;
if (click)
$(document.body).removeClass(self.options.dragClass)
else
self.fixScrollClasses()
window.setTimeout(function(){
if (!click) {
$(document.body).removeClass(self.options.dragClass)
$el.trigger('stop.oc.dragScroll')
self.options.stop()
self.fixScrollClasses()
}
}, 100)
}
/*
* Scroll wheel has moved by supplied offset
*/
function scrollWheel(offset) {
startOffset = self.options.vertical ? el.scrollTop : el.scrollLeft
self.options.vertical
? $el.scrollTop(startOffset + offset)
: $el.scrollLeft(startOffset + offset)
var scrolled = self.options.vertical
? el.scrollTop != startOffset
: el.scrollLeft != startOffset
$el.trigger('drag.oc.dragScroll')
self.options.drag()
if (scrolled) {
if (self.wheelUpdateTimer !== undefined && self.wheelUpdateTimer !== false)
window.clearInterval(self.wheelUpdateTimer);
self.wheelUpdateTimer = window.setTimeout(function() {
self.wheelUpdateTimer = false;
self.fixScrollClasses()
}, 100);
}
return scrolled
}
this.fixScrollClasses();
}
DragScroll.DEFAULTS = {
vertical: false,
allowScroll: true,
scrollClassContainer: false,
scrollMarkerContainer: false,
dragClass: 'drag',
start: function() {},
drag: function() {},
stop: function() {}
}
DragScroll.prototype.fixScrollClasses = function() {
this.scrollClassContainer.toggleClass('scroll-before', !this.isStart())
this.scrollClassContainer.toggleClass('scroll-after', !this.isEnd())
this.scrollClassContainer.toggleClass('scroll-active-before', this.isActiveBefore())
this.scrollClassContainer.toggleClass('scroll-active-after', this.isActiveAfter())
}
DragScroll.prototype.isStart = function() {
if (!this.options.vertical)
return this.el.scrollLeft() <= 0;
else
return this.el.scrollTop() <= 0;
}
DragScroll.prototype.isEnd = function() {
if (!this.options.vertical)
return (this.el[0].scrollWidth - (this.el.scrollLeft() + this.el.width())) <= 0
else
return (this.el[0].scrollHeight - (this.el.scrollTop() + this.el.height())) <= 0
}
DragScroll.prototype.goToStart = function() {
if (!this.options.vertical)
return this.el.scrollLeft(0)
else
return this.el.scrollTop(0)
}
/*
* Determines if the element with the class 'active' is hidden before the viewport -
* on the left or on the top, depending on whether the scrollbar is horizontal or vertical.
*/
DragScroll.prototype.isActiveAfter = function() {
var activeElement = $('.active', this.el);
if (activeElement.length == 0)
return false
if (!this.options.vertical)
return activeElement.get(0).offsetLeft > (this.el.scrollLeft() + this.el.width())
else
return activeElement.get(0).offsetTop > (this.el.scrollTop() + this.el.height())
}
/*
* Determines if the element with the class 'active' is hidden after the viewport -
* on the right or on the bottom, depending on whether the scrollbar is horizontal or vertical.
*/
DragScroll.prototype.isActiveBefore = function() {
var activeElement = $('.active', this.el);
if (activeElement.length == 0)
return false
if (!this.options.vertical)
return (activeElement.get(0).offsetLeft + activeElement.width()) < this.el.scrollLeft()
else
return (activeElement.get(0).offsetTop + activeElement.height()) < this.el.scrollTop()
}
DragScroll.prototype.goToElement = function(element, callback, options) {
var $el = $(element)
if (!$el.length)
return;
var self = this,
params = {
duration: 300,
queue: false,
complete: function(){
self.fixScrollClasses()
if (callback !== undefined)
callback()
}
}
params = $.extend(params, options || {})
var offset = 0,
animated = false
if (!this.options.vertical) {
offset = $el.get(0).offsetLeft - this.el.scrollLeft()
if (offset < 0) {
this.el.animate({'scrollLeft': $el.get(0).offsetLeft}, params)
animated = true
} else {
offset = $el.get(0).offsetLeft + $el.width() - (this.el.scrollLeft() + this.el.width())
if (offset > 0) {
this.el.animate({'scrollLeft': $el.get(0).offsetLeft + $el.width() - this.el.width()}, params)
animated = true
}
}
} else {
offset = $el.get(0).offsetTop - this.el.scrollTop()
if (offset < 0) {
this.el.animate({'scrollTop': $el.get(0).offsetTop}, params)
animated = true
} else {
offset = $el.get(0).offsetTop - (this.el.scrollTop() + this.el.height())
if (offset > 0) {
this.el.animate({'scrollTop': $el.get(0).offsetTop + $el.height() - this.el.height()}, params)
animated = true
}
}
}
if (!animated && callback !== undefined)
callback()
}
// DRAGSCROLL PLUGIN DEFINITION
// ============================
var old = $.fn.dragScroll
$.fn.dragScroll = function (option) {
var args = arguments;
return this.each(function () {
var $this = $(this)
var data = $this.data('oc.dragScroll')
var options = typeof option == 'object' && option
if (!data) $this.data('oc.dragScroll', (data = new DragScroll(this, options)))
if (typeof option == 'string') {
var methodArgs = [];
for (var i=1; i<args.length; i++)
methodArgs.push(args[i])
data[option].apply(data, methodArgs)
}
})
}
$.fn.dragScroll.Constructor = DragScroll
// DRAGSCROLL NO CONFLICT
// =================
$.fn.dragScroll.noConflict = function () {
$.fn.dragScroll = old
return this
}
}(window.jQuery);