diff options
Diffstat (limited to 'rulemgt-frontend/src/assets/common/js/jquery.sly.js')
-rw-r--r-- | rulemgt-frontend/src/assets/common/js/jquery.sly.js | 1446 |
1 files changed, 1446 insertions, 0 deletions
diff --git a/rulemgt-frontend/src/assets/common/js/jquery.sly.js b/rulemgt-frontend/src/assets/common/js/jquery.sly.js new file mode 100644 index 0000000..ce3390f --- /dev/null +++ b/rulemgt-frontend/src/assets/common/js/jquery.sly.js @@ -0,0 +1,1446 @@ +/*! + * jQuery Sly v0.9.6 + * https://github.com/Darsain/sly + * + * Licensed under the MIT license. + * http://www.opensource.org/licenses/MIT + */ + +/*jshint eqeqeq: true, noempty: true, strict: true, undef: true, expr: true, smarttabs: true, browser: true */ +/*global jQuery:false */ + +;(function($, undefined){ +'use strict'; + +// Plugin names +var pluginName = 'sly', + namespace = 'plugin_' + pluginName; + +/** + * Plugin class + * + * @class + * @param {Element} frame DOM element of sly container + * @param {Object} o Object with plugin options + */ +function Plugin( frame, o ){ + + // Alias for this + var self = this, + + // Frame variables + $frame = $(frame), + $slidee = $frame.children().eq(0), + frameSize = 0, + slideeSize = 0, + pos = { + cur: 0, + max: 0, + min: 0 + }, + + // Scrollbar variables + $sb = $(o.scrollBar).eq(0), + $handle = $sb.length ? $sb.children().eq(0) : 0, + sbSize = 0, + handleSize = 0, + hPos = { + cur: 0, + max: 0, + min: 0 + }, + + // Pagesbar variables + $pb = $(o.pagesBar), + $pages = 0, + pages = [], + + // Navigation type booleans + basicNav = o.itemNav === 'basic', + smartNav = o.itemNav === 'smart', + forceCenteredNav = o.itemNav === 'forceCentered', + centeredNav = o.itemNav === 'centered' || forceCenteredNav, + itemNav = basicNav || smartNav || centeredNav || forceCenteredNav, + + // Other variables + $items = 0, + items = [], + rel = { + firstItem: 0, + lastItem: 1, + centerItem: 1, + activeItem: -1, + activePage: 0, + items: 0, + pages: 0 + }, + $scrollSource = o.scrollSource ? $( o.scrollSource ) : $frame, + $dragSource = o.dragSource ? $( o.dragSource ) : $frame, + $prevButton = $(o.prev), + $nextButton = $(o.next), + $prevPageButton = $(o.prevPage), + $nextPageButton = $(o.nextPage), + cycleIndex = 0, + cycleIsPaused = 0, + isDragging = 0, + callbacks = {}; + + + /** + * (Re)Loading function + * + * Populates arrays, sets sizes, binds events, ... + * + * @public + */ + var load = this.reload = function(){ + + // Local variables + var ignoredMargin = 0, + oldPos = $.extend({}, pos); + + // Clear cycling timeout + clearTimeout( cycleIndex ); + + // Reset global variables + frameSize = o.horizontal ? $frame.width() : $frame.height(); + sbSize = o.horizontal ? $sb.width() : $sb.height(); + slideeSize = o.horizontal ? $slidee.outerWidth() : $slidee.outerHeight(); + $items = $slidee.children(); + items = []; + pages = []; + + // Set position limits & relatives + pos.min = 0; + pos.max = slideeSize > frameSize ? slideeSize - frameSize : 0; + rel.items = $items.length; + + // Sizes & offsets logic, but only when needed + if( itemNav ){ + + var marginStart = getPx( $items, o.horizontal ? 'marginLeft' : 'marginTop' ), + marginEnd = getPx( $items.slice(-1), o.horizontal ? 'marginRight' : 'marginBottom' ), + centerOffset = 0, + paddingStart = getPx( $slidee, o.horizontal ? 'paddingLeft' : 'paddingTop' ), + paddingEnd = getPx( $slidee, o.horizontal ? 'paddingRight' : 'paddingBottom' ), + areFloated = $items.css('float') !== 'none'; + + // Update ignored margin + ignoredMargin = marginStart ? 0 : marginEnd; + + // Reset slideeSize + slideeSize = 0; + + // Iterate through items + $items.each(function(i,e){ + + // Item + var item = $(e), + itemSize = o.horizontal ? item.outerWidth(true) : item.outerHeight(true), + marginTop = getPx( item, 'marginTop' ), + marginBottom = getPx( item, 'marginBottom'), + marginLeft = getPx( item, 'marginLeft'), + marginRight = getPx( item, 'marginRight'), + itemObj = { + size: itemSize, + offStart: slideeSize - ( !i || o.horizontal ? 0 : marginTop ), + offCenter: slideeSize - Math.round( frameSize / 2 - itemSize / 2 ), + offEnd: slideeSize - frameSize + itemSize - ( marginStart ? 0 : marginRight ), + margins: { + top: marginTop, + bottom: marginBottom, + left: marginLeft, + right: marginRight + } + }; + + // Account for centerOffset & slidee padding + if( !i ){ + centerOffset = -( forceCenteredNav ? Math.round( frameSize / 2 - itemSize / 2 ) : 0 ) + paddingStart; + slideeSize += paddingStart; + } + + // Increment slidee size for size of the active element + slideeSize += itemSize; + + // Try to account for vertical margin collapsing in vertical mode + // It's not bulletproof, but should work in 99% of cases + if( !o.horizontal && !areFloated ){ + + // Subtract smaller margin, but only when top margin is not 0, and this is not the first element + if( marginBottom && marginTop && i > 0 ){ + slideeSize -= marginTop < marginBottom ? marginTop : marginBottom; + } + + } + + // Things to be done at last item + if( i === $items.length - 1 ){ + slideeSize += paddingEnd; + } + + // Add item object to items array + items.push(itemObj); + + }); + + // Resize slidee + $slidee.css( o.horizontal ? { width: slideeSize+'px' } : { height: slideeSize+'px' } ); + + // Adjust slidee size for last margin + slideeSize -= ignoredMargin; + + // Set limits + pos.min = centerOffset; + pos.max = forceCenteredNav ? items[items.length-1].offCenter : slideeSize > frameSize ? slideeSize - frameSize : 0; + + // Fix overflowing activeItem + rel.activeItem >= items.length && self.activate( items.length-1 ); + + } + + // Assign relative position indexes + assignRelatives(); + + // Scrollbar + if( $handle ){ + + // Stretch scrollbar handle to represent the visible area + handleSize = o.dynamicHandle ? Math.round( sbSize * frameSize / slideeSize ) : o.horizontal ? $handle.width() : $handle.height(); + handleSize = handleSize > sbSize ? sbSize : handleSize; + handleSize = handleSize < o.minHandleSize ? o.minHandleSize : handleSize; + hPos.max = sbSize - handleSize; + + // Resize handle + $handle.css( o.horizontal ? { width: handleSize+'px' } : { height: handleSize+'px' } ); + + } + + // Pages + var tempPagePos = 0, + pagesHtml = '', + pageIndex = 0; + + // Populate pages array + if( forceCenteredNav ){ + pages = $.map( items, function( o ){ return o.offCenter; } ); + } else { + while( tempPagePos - frameSize < pos.max ){ + + var pagePos = tempPagePos > pos.max ? pos.max : tempPagePos; + + pages.push( pagePos ); + tempPagePos += frameSize; + + // When item navigation, and last page is smaller than half of the last item size, + // adjust the last page position to pos.max and break the loop + if( tempPagePos > pos.max && itemNav && pos.max - pagePos < ( items[items.length-1].size - ignoredMargin ) / 2 ){ + + pages[pages.length-1] = pos.max; + break; + + } + + } + } + + // Pages bar + if( $pb.length ){ + + for( var i = 0; i < pages.length; i++ ){ + pagesHtml += o.pageBuilder( pageIndex++ ); + } + + // Bind page navigation, append to pagesbar, and save to $pages variable + $pages = $(pagesHtml).bind('click.' + namespace, function(){ + + self.activatePage( $pages.index(this) ); + + }).appendTo( $pb.empty() ); + + } + + // Bind activating to items + $items.unbind('.' + namespace).bind('mouseup.' + namespace, function(e){ + + e.which === 1 && !isDragging && self.activate( this ); + + }); + + // Fix overflowing + pos.cur < pos.min && slide( pos.min ); + pos.cur > pos.max && slide( pos.max ); + + // Extend relative variables object with some useful info + rel.pages = pages.length; + rel.slideeSize = slideeSize; + rel.frameSize = frameSize; + rel.sbSize = sbSize; + rel.handleSize = handleSize; + + // Synchronize scrollbar + syncBars(0); + + // Disable buttons + disableButtons(); + + // Automatic cycling + if( itemNav && o.cycleBy ){ + + var pauseEvents = 'mouseenter.' + namespace + ' mouseleave.' + namespace; + + // Pause on hover + o.pauseOnHover && $frame.unbind(pauseEvents).bind(pauseEvents, function(e){ + + !cycleIsPaused && self.cycle( e.type === 'mouseenter', 1 ); + + }); + + // Initiate cycling + self.cycle( o.startPaused ); + + } + + // Trigger :load event + $frame.trigger( pluginName + ':load', [ $.extend({}, pos, { old: oldPos }), $items, rel ] ); + + }; + + + /** + * Slide the slidee + * + * @private + * + * @param {Int} newPos New slidee position in relation to frame + * @param {Bool} align Whetner to Align elements to the frame border + * @param {Int} speed Animation speed in milliseconds + */ + function slide( newPos, align, speed ){ + + speed = isNumber( speed ) ? speed : o.speed; + + // Align items + if( align && itemNav ){ + + var tempRel = getRelatives( newPos ); + + if( centeredNav ){ + + newPos = items[tempRel.centerItem].offCenter; + self[ forceCenteredNav ? 'activate' : 'toCenter']( tempRel.centerItem, 1 ); + + } else if( newPos > pos.min && newPos < pos.max ){ + + newPos = items[tempRel.firstItem].offStart; + + } + + } + + // Fix overflowing position + if( !isDragging || !o.elasticBounds ){ + newPos = newPos < pos.min ? pos.min : newPos; + newPos = newPos > pos.max ? pos.max : newPos; + } + + // Stop if position has not changed + if( newPos === pos.cur ) { + return; + } else { + pos.cur = newPos; + } + + // Reassign relative indexes + assignRelatives(); + + // Add disabled classes + disableButtons(); + + // halt ongoing animations + stop(); + + // Trigger :move event + !isDragging && $frame.trigger( pluginName + ':move', [ pos, $items, rel ] ); + + var newProp = o.horizontal ? { left: -pos.cur+'px' } : { top: -pos.cur+'px' }; + + // Slidee move + if( speed > 16 ){ + + $slidee.animate( newProp, speed, isDragging ? 'swing' : o.easing, function(e){ + + // Trigger :moveEnd event + !isDragging && $frame.trigger( pluginName + ':moveEnd', [ pos, $items, rel ] ); + + }); + + } else { + + $slidee.css( newProp ); + + // Trigger :moveEnd event + !isDragging && $frame.trigger( pluginName + ':moveEnd', [ pos, $items, rel ] ); + + } + + } + + + /** + * Synchronizes scrollbar & pagesbar positions with the slidee + * + * @private + * + * @param {Int} speed Animation speed for scrollbar synchronization + */ + function syncBars( speed ){ + + // Scrollbar synchronization + if ($handle) { + + hPos.cur = Math.round( ( pos.cur - pos.min ) / ( pos.max - pos.min ) * hPos.max ); + hPos.cur = hPos.cur < hPos.min ? hPos.min : hPos.cur > hPos.max ? hPos.max : hPos.cur; + $handle.stop().animate( o.horizontal ? { left: hPos.cur+'px' } : { top: hPos.cur+'px' }, isNumber(speed) ? speed : o.speed, o.easing ); + + } + + // Pagesbar synchronization + syncPages(); + + } + + + /** + * Synchronizes pagesbar + * + * @private + */ + function syncPages(){ + + if (!$pages.length) { + return; + } + + // Classes + $pages.removeClass(o.activeClass).eq(rel.activePage).addClass(o.activeClass); + + } + + + /** + * Activate previous item + * + * @public + */ + this.prev = function(){ + + self.activate( rel.activeItem - 1 ); + + }; + + + /** + * Activate next item + * + * @public + */ + this.next = function(){ + + self.activate( rel.activeItem + 1 ); + + }; + + + /** + * Activate previous page + * + * @public + */ + this.prevPage = function(){ + + self.activatePage( rel.activePage - 1 ); + + }; + + + /** + * Activate next page + * + * @public + */ + this.nextPage = function(){ + + self.activatePage( rel.activePage + 1 ); + + }; + + + /** + * Stop ongoing animations + * + * @private + */ + function stop(){ + + $slidee.add($handle).stop(); + + } + + + /** + * Animate element or the whole slidee to the start of the frame + * + * @public + * + * @param {Element|Int} el DOM element, or index of element in items array + */ + this.toStart = function( el ){ + + if( itemNav ){ + + var index = getIndex( el ); + + if( el === undefined ){ + + slide( pos.min, 1 ); + + } else if( index !== -1 ){ + + // You can't align items to the start of the frame when centeredNav is enabled + if (centeredNav) { + return; + } + + index !== -1 && slide( items[index].offStart ); + + } + + } else { + + if( el === undefined ){ + + slide( pos.min ); + + } else { + + var $el = $slidee.find(el).eq(0); + + if( $el.length ){ + + var offset = o.horizontal ? $el.offset().left - $slidee.offset().left : $el.offset().top - $slidee.offset().top; + + slide( offset ); + + } + + } + + } + + syncBars(); + + }; + + + /** + * Animate element or the whole slidee to the end of the frame + * + * @public + * + * @param {Element|Int} el DOM element, or index of element in items array + */ + this.toEnd = function( el ){ + + if( itemNav ){ + + var index = getIndex( el ); + + if( el === undefined ){ + + slide( pos.max, 1 ); + + } else if( index !== -1 ){ + + // You can't align items to the end of the frame when centeredNav is enabled + if (centeredNav) { + return; + } + + slide( items[index].offEnd ); + + } + + } else { + + if( el === undefined ){ + + slide( pos.max ); + + } else { + + var $el = $slidee.find(el).eq(0); + + if( $el.length ){ + + var offset = o.horizontal ? $el.offset().left - $slidee.offset().left : $el.offset().top - $slidee.offset().top; + + slide( offset - frameSize + $el[o.horizontal ? 'outerWidth' : 'outerHeight']() ); + + } + + } + + } + + syncBars(); + + }; + + + /** + * Animate element or the whole slidee to the center of the frame + * + * @public + * + * @param {Element|Int} el DOM element, or index of element in items array + */ + this.toCenter = function( el ){ + + if( itemNav ){ + + var index = getIndex( el ); + + if( el === undefined ){ + + slide( Math.round( pos.max / 2 + pos.min / 2 ), 1 ); + + } else if( index !== -1 ){ + + slide( items[index].offCenter ); + forceCenteredNav && self.activate( index, 1 ); + + } + + } else { + + if( el === undefined ){ + + slide( Math.round( pos.max / 2 ) ); + + } else { + + var $el = $slidee.find(el).eq(0); + + if( $el.length ){ + + var offset = o.horizontal ? $el.offset().left - $slidee.offset().left : $el.offset().top - $slidee.offset().top; + + slide( offset - frameSize / 2 + $el[o.horizontal ? 'outerWidth' : 'outerHeight']() / 2 ); + + } + + } + + + } + + syncBars(); + + }; + + + /** + * Get an index of the element + * + * @private + * + * @param {Element|Int} el DOM element, or index of element in items array + */ + function getIndex( el ){ + + return isNumber(el) ? el < 0 ? 0 : el > items.length-1 ? items.length-1 : el : el === undefined ? -1 : $items.index( el ); + + } + + + /** + * Parse style to pixels + * + * @private + * + * @param {Object} $item jQuery object with element + * @param {Property} property Property to get the pixels from + */ + function getPx( $item, property ){ + + return parseInt( $item.css( property ), 10 ); + + } + + + /** + * Activates an element + * + * Element is positioned to one of the sides of the frame, based on it's current position. + * If the element is close to the right frame border, it will be animated to the start of the left border, + * and vice versa. This helps user to navigate through the elements only by clicking on them, without + * the need for navigation buttons, scrolling, or keyboard arrows. + * + * @public + * + * @param {Element|Int} el DOM element, or index of element in items array + * @param {Bool} noReposition Activate item without repositioning it + */ + this.activate = function( el, noReposition ){ + + if (!itemNav || el === undefined) { + return; + } + + var index = getIndex( el ), + oldActive = rel.activeItem; + + // Update activeItem index + rel.activeItem = index; + + // Add active class to the active element + $items.removeClass(o.activeClass).eq(index).addClass(o.activeClass); + + // Trigget :active event if a new element is being activated + index !== oldActive && $items.eq( index ).trigger( pluginName + ':active', [ $items, rel ] ); + + if( !noReposition ){ + + // When centeredNav is enabled, center the element + if( centeredNav ){ + + self.toCenter( index ); + + // Otherwise determine where to position the element + } else if( smartNav ) { + + // If activated element is currently on the far right side of the frame, assume that + // user is moving forward and animate it to the start of the visible frame, and vice versa + if (index >= rel.lastItem) { + self.toStart(index); + } else if (index <= rel.firstItem) { + self.toEnd(index); + } + + } + + } + + // Add disabled classes + disableButtons(); + + }; + + + /** + * Activates a page + * + * @public + * + * @param {Int} index Page index, starting from 0 + */ + this.activatePage = function( index ){ + + // Fix overflowing + index = index < 0 ? 0 : index >= pages.length ? pages.length-1 : index; + slide( pages[index], itemNav ); + + syncBars(); + + }; + + + /** + * Return relative positions of items based on their location within visible frame + * + * @private + * + * @param {Int} sPos Position of slidee + */ + function getRelatives( sPos ){ + + var newRel = {}, + centerOffset = forceCenteredNav ? 0 : frameSize / 2; + + // Determine active page + for( var p = 0; p < pages.length; p++ ){ + + if( sPos >= pos.max || p === pages.length - 1 ){ + newRel.activePage = pages.length - 1; + break; + } + + if( sPos <= pages[p] + centerOffset ){ + newRel.activePage = p; + break; + } + + } + + // Relative item indexes + if( itemNav ){ + + var first = false, + last = false, + center = false; + + /* From start */ + for( var i=0; i < items.length; i++ ){ + + // First item + if (first === false && sPos <= items[i].offStart) { + first = i; + } + + // Centered item + if (center === false && sPos - items[i].size / 2 <= items[i].offCenter) { + center = i; + } + + // Last item + if (i === items.length - 1 || (last === false && sPos < items[i + 1].offEnd)) { + last = i; + } + + // Terminate if all are assigned + if (last !== false) { + break; + } + + } + + // Safe assignment, just to be sure the false won't be returned + newRel.firstItem = isNumber( first ) ? first : 0; + newRel.centerItem = isNumber( center ) ? center : newRel.firstItem; + newRel.lastItem = isNumber( last ) ? last : newRel.centerItem; + + } + + return newRel; + + } + + + /** + * Assign element indexes to the relative positions + * + * @private + */ + function assignRelatives(){ + + $.extend( rel, getRelatives( pos.cur ) ); + + } + + + /** + * Disable buttons when needed + * + * Adds disabledClass, and when the button is <button> or <input>, + * activates :disabled state + * + * @private + */ + function disableButtons(){ + + // item navigation + if( itemNav ){ + + var isFirstItem = rel.activeItem === 0, + isLastItem = rel.activeItem >= items.length-1; + + if( $prevButton.is('button,input') ){ + $prevButton.prop('disabled', isFirstItem); + } + + if( $nextButton.is('button,input') ){ + $nextButton.prop('disabled', isLastItem); + } + + $prevButton[ isFirstItem ? 'removeClass' : 'addClass'](o.disabledClass); + $nextButton[ isLastItem ? 'removeClass' : 'addClass'](o.disabledClass); + + } + + // pages navigation + if( $pages.length ){ + + var isStart = pos.cur <= pos.min, + isEnd = pos.cur >= pos.max; + + if( $prevPageButton.is('button,input') ){ + $prevPageButton.prop('disabled', isStart); + } + + if( $nextPageButton.is('button,input') ){ + $nextPageButton.prop('disabled', isEnd); + } + + $prevPageButton[ isStart ? 'removeClass' : 'addClass'](o.disabledClass); + $nextPageButton[ isEnd ? 'removeClass' : 'addClass'](o.disabledClass); + + } + + } + + + /** + * Manage cycling + * + * @public + * + * @param {Bool} pause Pass true to pause cycling + * @param {Bool} soft Soft pause intended for pauseOnHover - won't set cycleIsPaused variable to true + */ + this.cycle = function( pause, soft ){ + + if (!itemNav || !o.cycleBy) { + return; + } + + if( !soft ){ + cycleIsPaused = !!pause; + } + + if( pause ){ + + if( cycleIndex ){ + + cycleIndex = clearTimeout( cycleIndex ); + + // Trigger :cyclePause event + $frame.trigger( pluginName + ':cyclePause', [ pos, $items, rel ] ); + + } + + } else { + + // Don't initiate more than one cycle + if (cycleIndex) { + return; + } + + // Trigger :cycleStart event + $frame.trigger( pluginName + ':cycleStart', [ pos, $items, rel ] ); + + // Cycling loop + (function loop(){ + + if( o.cycleInterval === 0 ){ + return; + } + + cycleIndex = setTimeout( function(){ + + if( !isDragging ){ + switch( o.cycleBy ){ + + case 'items': + var nextItem = rel.activeItem >= items.length-1 ? 0 : rel.activeItem + 1; + self.activate( nextItem ); + break; + + case 'pages': + var nextPage = rel.activePage >= pages.length-1 ? 0 : rel.activePage + 1; + self.activatePage( nextPage ); + break; + + } + } + + // Trigger :cycle event + $frame.trigger( pluginName + ':cycle', [ pos, $items, rel ] ); + + // Cycle the cycle! + loop(); + + }, o.cycleInterval ); + + }()); + + } + + }; + + + /** + * Crossbrowser reliable way to stop default event action + * + * @private + * + * @param {Event} e Event object + * @param {Bool} noBubbles Cancel event bubbling + */ + function stopDefault( e, noBubbles ){ + + var evt = e || window.event; + evt.preventDefault ? evt.preventDefault() : evt.returnValue = false; + noBubbles && evt.stopPropagation ? evt.stopPropagation() : evt.cancelBubble = true; + + } + + + /** + * Updates a signle or multiple option values + * + * @param {Mixed} property Option property name that should be updated, or object with options that will extend the current one + * @param {Mixed} value Option property value + * + * @public + */ + this.set = function( property, value ){ + + if( $.isPlainObject(property) ){ + + o = $.extend({}, o, property); + + } else if( typeof property === 'string' ) { + + o[property] = value; + + } + + }; + + + /** + * Destroys plugin instance and everything it created + * + * @public + */ + this.destroy = function(){ + + // Unbind all events + $frame.add(document).add($slidee).add($items).add($scrollSource).add($handle) + .add($prevButton).add($nextButton).add($prevPageButton).add($nextPageButton) + .unbind('.' + namespace); + + // Reset some styles + $slidee.add($handle).css( o.horizontal ? { left: 0 } : { top: 0 } ); + + // Remove plugin classes + $prevButton.add($nextButton).removeClass(o.disabledClass); + + // Remove page items + $pb.empty(); + + // Remove plugin from element data storage + $.removeData(frame, namespace); + + }; + + + /** + * Check if variable is a number + * + * @param {Mixed} n Any type of variable + * + * @return {Boolean} + */ + function isNumber( n ) { + return !isNaN(parseFloat(n)) && isFinite(n); + } + + + /** Constructor */ + (function(){ + + var doc = $(document), + dragEvents = 'mousemove.' + namespace + ' mouseup.' + namespace; + + // Extend options + o = $.extend( {}, $.fn[pluginName].defaults, o ); + + // Set required styles to elements + $frame.css({ overflow: 'hidden' }).css('position') === 'static' && $frame.css({ position: 'relative' }); + $sb.css('position') === 'static' && $sb.css({ position: 'relative' }); + $slidee.add($handle).css( o.horizontal ? { position: 'absolute', left: 0 } : { position: 'absolute', top: 0 } ); + + // Load + load(); + + // Activate requested position + itemNav ? self.activate( o.startAt ) : slide( o.startAt ); + + // Sync scrollbar & pages + syncBars(); + + // Scrolling navigation + o.scrollBy && $scrollSource.bind('DOMMouseScroll.' + namespace + ' mousewheel.' + namespace, function(e){ + + // If there is no scrolling to be done, leave the default event alone + if (pos.min === pos.max) { + return; + } + + stopDefault( e, 1 ); + + var orgEvent = e.originalEvent, + delta = 0, + isForward, nextItem; + + // Old school scrollwheel delta + if ( orgEvent.wheelDelta ){ delta = orgEvent.wheelDelta / 120; } + if ( orgEvent.detail ){ delta = -orgEvent.detail / 3; } + + isForward = delta < 0; + + if( itemNav ){ + + nextItem = getIndex( ( centeredNav ? forceCenteredNav ? rel.activeItem : rel.centerItem : rel.firstItem ) + ( isForward ? o.scrollBy : -o.scrollBy ) ); + + self[centeredNav ? forceCenteredNav ? 'activate' : 'toCenter' : 'toStart']( nextItem ); + + } else { + + slide( pos.cur + ( isForward ? o.scrollBy : -o.scrollBy ) ); + + } + + syncBars(); + + }); + + // Keyboard navigation + o.keyboardNav && doc.bind('keydown.' + namespace, function(e){ + + switch( e.keyCode || e.which ){ + + // Left or Up + case o.horizontal ? 37 : 38: + + stopDefault(e); + o.keyboardNavByPages ? self.prevPage() : self.prev(); + + break; + + // Right or Down + case o.horizontal ? 39 : 40: + + stopDefault(e); + o.keyboardNavByPages ? self.nextPage() : self.next(); + + break; + + } + + }); + + // Navigation buttons + o.prev && $prevButton.bind('click.' + namespace, function(e){ stopDefault(e); self.prev(); }); + o.next && $nextButton.bind('click.' + namespace, function(e){ stopDefault(e); self.next(); }); + o.prevPage && $prevPageButton.bind('click.' + namespace, function(e){ stopDefault(e); self.prevPage(); }); + o.nextPage && $nextPageButton.bind('click.' + namespace, function(e){ stopDefault(e); self.nextPage(); }); + + // Dragging navigation + o.dragContent && $dragSource.bind('mousedown.' + namespace, function(e){ + + // Ignore other than left mouse button + if (e.which !== 1) { + return; + } + + stopDefault(e); + + var leftInit = e.clientX, + topInit = e.clientY, + posInit = pos.cur, + start = +new Date(), + srcEl = e.target, + easeoff = 0, + isInitialized = 0; + + // Add dragging class + $slidee.addClass(o.draggedClass); + + // Stop potential ongoing animations + stop(); + + // Bind dragging events + doc.bind(dragEvents, function(e){ + + var released = e.type === 'mouseup', + path = o.horizontal ? e.clientX - leftInit : e.clientY - topInit, + newPos = posInit - path; + + // Initialized logic + if( !isInitialized && Math.abs( path ) > 10 ){ + + isInitialized = 1; + + // Trigger :dragStart event + $slidee.trigger( pluginName + ':dragStart', [ pos ] ); + + } + + // Limits & Elastic bounds + if( newPos > pos.max ){ + newPos = o.elasticBounds ? pos.max + ( newPos - pos.max ) / 6 : pos.max; + } else if( newPos < pos.min ){ + newPos = o.elasticBounds ? pos.min + ( newPos - pos.min ) / 6 : pos.min; + } + + // Adjust newPos with easing when content has been released + if( released ){ + + // Cleanup + doc.unbind(dragEvents); + $slidee.removeClass(o.draggedClass); + + // How long was the dragging + var time = +new Date() - start; + + // Calculate swing length + var swing = time < 300 ? Math.ceil( Math.pow( 6 / ( time / 300 ) , 2 ) * Math.abs( path ) / 120 ) : 0; + newPos += path > 0 ? -swing : swing; + + } + + // Drag only when isInitialized + if (!isInitialized) { + return; + } + + stopDefault(e); + + // Stop default click action on source element + if( srcEl ){ + + $(srcEl).bind('click.' + namespace, function stopMe(e){ + + stopDefault(e,true); + $(this).unbind('click.' + namespace, stopMe); + + }); + + srcEl = 0; + + } + + // Dragging state + isDragging = !released; + + // Animage, synch bars, & align + slide( newPos, released, released ? o.speed : 0 ); + syncBars( released ? null : 0 ); + + // Trigger :drag event + if (isInitialized) { + $slidee.trigger(pluginName + ':drag', [pos]); + } + + // Trigger :dragEnd event + if (released) { + $slidee.trigger(pluginName + ':dragEnd', [pos]); + } + + }); + + }); + + // Scrollbar navigation + $handle && o.dragHandle && $handle.bind('mousedown.' + namespace, function(e){ + + // Ignore other than left mouse button + if (e.which !== 1) { + return; + } + + stopDefault(e); + + var leftInit = e.clientX, + topInit = e.clientY, + posInit = hPos.cur, + pathMin = -hPos.cur, + pathMax = hPos.max - hPos.cur, + nextDrag = 0; + + // Add dragging class + $handle.addClass(o.draggedClass); + + // Stop potential ongoing animations + stop(); + + // Bind dragging events + doc.bind(dragEvents, function(e){ + + stopDefault(e); + + var released = e.type === 'mouseup', + path = o.horizontal ? e.clientX - leftInit : e.clientY - topInit, + newPos = posInit + path, + time = +new Date(); + + // Dragging state + isDragging = !released; + + // Unbind events and remove classes when released + if( released ){ + + doc.unbind(dragEvents); + $handle.removeClass(o.draggedClass); + + } + + // Execute only moves within path limits + if( path < pathMax+5 && path > pathMin-5 || released ){ + + // Fix overflows + hPos.cur = newPos > hPos.max ? hPos.max : newPos < hPos.min ? hPos.min : newPos; + + // Move handle + $handle.stop().css( o.horizontal ? { left: hPos.cur+'px' } : { top: hPos.cur+'px' } ); + + // Trigger :dragStart event + if (!nextDrag) { + $handle.trigger(pluginName + ':dragStart', [hPos]); + } + + // Trigger :drag event + $handle.trigger( pluginName + ':drag', [ hPos ] ); + + // Trigger :dragEnd event + if (released) { + $handle.trigger(pluginName + ':dragEnd', [hPos]); + } + + // Throttle sync interval -> smoother animations, lower CPU load + if( nextDrag <= time || released || path > pathMax || path < pathMin ){ + + nextDrag = time + 50; + + // Synchronize slidee position + slide( Math.round( hPos.cur / hPos.max * ( pos.max - pos.min ) ) + pos.min, released, released ? o.speed : 50 ); + + } + + // Sync pagesbar + syncPages(); + + } + + }); + + }); + + }()); + +} + + +// jQuery plugin extension +$.fn[pluginName] = function( options, returnInstance ){ + + var method = false, + methodArgs, + instances = []; + + // Basic attributes logic + if( typeof options !== 'undefined' && !$.isPlainObject( options ) ){ + method = options === false ? 'destroy' : options; + methodArgs = arguments; + Array.prototype.shift.call( methodArgs ); + } + + // Apply requested actions on all elements + this.each(function( i, element ){ + + // Plugin call with prevention against multiple instantiations + var plugin = $.data( element, namespace ); + + if( plugin && method ){ + + // Call plugin method + if( plugin[method] ){ + + plugin[method].apply( plugin, methodArgs ); + + } + + } else if( !plugin && !method ){ + + // Create a new plugin object if it doesn't exist yet + plugin = $.data( element, namespace, new Plugin( element, options ) ); + + } + + // Push plugin to instances + instances.push( plugin ); + + }); + + // Return chainable jQuery object, or plugin instance(s) + return returnInstance && !method ? instances.length > 1 ? instances : instances[0] : this; + +}; + + +// Default options +$.fn[pluginName].defaults = { + + // Sly direction + horizontal: 0, // set to 1 to change the sly direction to horizontal + + // Navigation by items; when using this, `scrollBy` option scrolls by items, not pixels + itemNav: 0, // enable type of item based navigation. when itemNav is enabled, items snap to frame edges or frame center + // itemNav also enables "item activation" functionality and methods associated with it + // + // itemNav can be: + // ------------------------------------------------------------------------------------ + // basic: items snap to edges (ideal if you don't care about "active item" functionality) + // smart: same as basic, but activated item close to, or outside of the visible edge will be positioned to the opposite edge + // centered: activated items are positioned to the center of visible frame if possible + // forceCentered: active items are always centered & centered items are always active (scrolling & dragging end activates centered item) + + // Scrollbar + scrollBar: null, // selector or DOM element for scrollbar container (scrollbar container should have one child element representing scrollbar handle) + dynamicHandle: 1, // resizes scrollbar handle to represent the relation between hidden and visible content. set to "0" to leave it as big as CSS made it + dragHandle: 1, // set to 0 to disable dragging of scrollbar handle with mouse + minHandleSize: 50, // minimal height or width (depends on sly direction) of a handle in pixels + + // Pagesbar (when centerActive is enabled, every item is considered to be a page) + pagesBar: null, // selector or DOM element for pages bar container + pageBuilder: // function with `index` (starting at 0) as argument that returns an HTML for one item + function( index ){ + return '<li>'+(index+1)+'</li>'; + }, + + // Navigation buttons + prev: null, // selector or DOM element for "previous item" button ; doesn't work when `itemsNav` is disabled + next: null, // selector or DOM element for "next item" button ; doesn't work when `itemsNav` is disabled + prevPage: null, // selector or DOM element for "previous page" button + nextPage: null, // selector or DOM element for "next page" button + + // Automated cycling + cycleBy: 0, // enable automatic cycling by 'items', or 'pages' + cycleInterval: 5000, // number of milliseconds between cycles + pauseOnHover: 1, // pause cycling when mouse hovers over frame + startPaused: 0, // set to "1" to start in paused sate. cycling can be than resumed with "cycle" method + + // Mixed options + scrollBy: 0, // how many pixels/items should one mouse scroll event go. leave "0" to disable mousewheel scrolling + dragContent: 0, // set to 1 to enable navigation by dragging the content with your mouse + elasticBounds: 0, // when dragging past limits, stretch them a little bit (like on spartphones) + speed: 300, // animations speed + easing: 'swing', // animations easing. build in jQuery options are "linear" and "swing". for more, install gsgd.co.uk/sandbox/jquery/easing/ + scrollSource: null, // selector or DOM element for catching the mouse wheel event for sly scrolling. default source is the frame + dragSource: null, // selector or DOM element for catching the mouse dragging events. default source is the frame + startAt: 0, // starting offset in pixels or items (depends on itemsNav option) + keyboardNav: 0, // whether to allow navigation by keyboard arrows (left & right for horizontal, up & down for vertical) + // NOTE! keyboard navigation will disable page scrolling with keyboard arrows in correspondent sly direction (vertical or horizontal) + keyboardNavByPages: 0, // whether the keyboard should navigate by pages instead of items (useful when not using `itemsNav` navigation) + + // Classes + draggedClass: 'dragged', // class that will be added to scrollbar handle, or content when they are being dragged + activeClass: 'active', // class that will be added to the active item, or page + disabledClass: 'disabled' // class that will be added to prev button when on start, or next button when on end + +}; + +}(jQuery));
\ No newline at end of file |