From 52a7c9f394f91530cd3ddc44ea2010f5210b89d0 Mon Sep 17 00:00:00 2001 From: Jamilson Date: Fri, 17 Feb 2017 14:50:25 -0200 Subject: [PATCH] Fix vertical scroll when multiple columns of contents --- dist/sly.js | 3280 ++++++++++++++++++++++++++------------------------- 1 file changed, 1651 insertions(+), 1629 deletions(-) diff --git a/dist/sly.js b/dist/sly.js index ad4e88e..5bdd4f7 100644 --- a/dist/sly.js +++ b/dist/sly.js @@ -6,49 +6,49 @@ * http://opensource.org/licenses/MIT */ -;(function ($, w, undefined) { - 'use strict'; - - var pluginName = 'sly'; - var className = 'Sly'; - var namespace = pluginName; - - // Local WindowAnimationTiming interface - var cAF = w.cancelAnimationFrame || w.cancelRequestAnimationFrame; - var rAF = w.requestAnimationFrame; - - // Support indicators - var transform, gpuAcceleration; - - // Other global values - var $doc = $(document); - var dragInitEvents = 'touchstart.' + namespace + ' mousedown.' + namespace; - var dragMouseEvents = 'mousemove.' + namespace + ' mouseup.' + namespace; - var dragTouchEvents = 'touchmove.' + namespace + ' touchend.' + namespace; - var wheelEvent = (document.implementation.hasFeature('Event.wheel', '3.0') ? 'wheel.' : 'mousewheel.') + namespace; - var clickEvent = 'click.' + namespace; - var mouseDownEvent = 'mousedown.' + namespace; - var interactiveElements = ['INPUT', 'SELECT', 'BUTTON', 'TEXTAREA']; - var tmpArray = []; - var time; - - // Math shorthands - var abs = Math.abs; - var sqrt = Math.sqrt; - var pow = Math.pow; - var round = Math.round; - var max = Math.max; - var min = Math.min; - - // Keep track of last fired global wheel event - var lastGlobalWheel = 0; - $doc.on(wheelEvent, function (event) { - var sly = event.originalEvent[namespace]; - var time = +new Date(); - // Update last global wheel time, but only when event didn't originate - // in Sly frame, or the origin was less than scrollHijack time ago - if (!sly || sly.options.scrollHijack < time - lastGlobalWheel) lastGlobalWheel = time; - }); +; (function ($, w, undefined) { + 'use strict'; + + var pluginName = 'sly'; + var className = 'Sly'; + var namespace = pluginName; + + // Local WindowAnimationTiming interface + var cAF = w.cancelAnimationFrame || w.cancelRequestAnimationFrame; + var rAF = w.requestAnimationFrame; + + // Support indicators + var transform, gpuAcceleration; + + // Other global values + var $doc = $(document); + var dragInitEvents = 'touchstart.' + namespace + ' mousedown.' + namespace; + var dragMouseEvents = 'mousemove.' + namespace + ' mouseup.' + namespace; + var dragTouchEvents = 'touchmove.' + namespace + ' touchend.' + namespace; + var wheelEvent = (document.implementation.hasFeature('Event.wheel', '3.0') ? 'wheel.' : 'mousewheel.') + namespace; + var clickEvent = 'click.' + namespace; + var mouseDownEvent = 'mousedown.' + namespace; + var interactiveElements = ['INPUT', 'SELECT', 'BUTTON', 'TEXTAREA']; + var tmpArray = []; + var time; + + // Math shorthands + var abs = Math.abs; + var sqrt = Math.sqrt; + var pow = Math.pow; + var round = Math.round; + var max = Math.max; + var min = Math.min; + + // Keep track of last fired global wheel event + var lastGlobalWheel = 0; + $doc.on(wheelEvent, function (event) { + var sly = event.originalEvent[namespace]; + var time = +new Date(); + // Update last global wheel time, but only when event didn't originate + // in Sly frame, or the origin was less than scrollHijack time ago + if (!sly || sly.options.scrollHijack < time - lastGlobalWheel) lastGlobalWheel = time; + }); /** * Sly. @@ -59,111 +59,111 @@ * @param {Object} options Object with options. * @param {Object} callbackMap Callbacks map. */ - function Sly(frame, options, callbackMap) { - if (!(this instanceof Sly)) return new Sly(frame, options, callbackMap); - - // Extend options - var o = $.extend({}, Sly.defaults, options); - - // Private variables - var self = this; - var parallax = isNumber(frame); - - // Frame - var $frame = $(frame); - var $slidee = o.slidee ? $(o.slidee).eq(0) : $frame.children().eq(0); - var frameSize = 0; - var slideeSize = 0; - var pos = { - start: 0, - center: 0, - end: 0, - cur: 0, - dest: 0 - }; - - // Scrollbar - var $sb = $(o.scrollBar).eq(0); - var $handle = $sb.children().eq(0); - var sbSize = 0; - var handleSize = 0; - var hPos = { - start: 0, - end: 0, - cur: 0 - }; - - // Pagesbar - var $pb = $(o.pagesBar); - var $pages = 0; - var pages = []; - - // Items - var $items = 0; - var items = []; - var rel = { - firstItem: 0, - lastItem: 0, - centerItem: 0, - activeItem: null, - activePage: 0 - }; - - // Styles - var frameStyles = new StyleRestorer($frame[0]); - var slideeStyles = new StyleRestorer($slidee[0]); - var sbStyles = new StyleRestorer($sb[0]); - var handleStyles = new StyleRestorer($handle[0]); - - // Navigation type booleans - var basicNav = o.itemNav === 'basic'; - var forceCenteredNav = o.itemNav === 'forceCentered'; - var centeredNav = o.itemNav === 'centered' || forceCenteredNav; - var itemNav = !parallax && (basicNav || centeredNav || forceCenteredNav); - - // Miscellaneous - var $scrollSource = o.scrollSource ? $(o.scrollSource) : $frame; - var $dragSource = o.dragSource ? $(o.dragSource) : $frame; - var $forwardButton = $(o.forward); - var $backwardButton = $(o.backward); - var $prevButton = $(o.prev); - var $nextButton = $(o.next); - var $prevPageButton = $(o.prevPage); - var $nextPageButton = $(o.nextPage); - var callbacks = {}; - var last = {}; - var animation = {}; - var move = {}; - var dragging = { - released: 1 - }; - var scrolling = { - last: 0, - delta: 0, - resetTime: 200 - }; - var renderID = 0; - var historyID = 0; - var cycleID = 0; - var continuousID = 0; - var i, l; - - // Normalizing frame - if (!parallax) { - frame = $frame[0]; - } - - // Expose properties - self.initialized = 0; - self.frame = frame; - self.slidee = $slidee[0]; - self.pos = pos; - self.rel = rel; - self.items = items; - self.pages = pages; - self.isPaused = 0; - self.options = o; - self.dragging = dragging; + function Sly(frame, options, callbackMap) { + if (!(this instanceof Sly)) return new Sly(frame, options, callbackMap); + + // Extend options + var o = $.extend({}, Sly.defaults, options); + + // Private variables + var self = this; + var parallax = isNumber(frame); + + // Frame + var $frame = $(frame); + var $slidee = o.slidee ? $(o.slidee).eq(0) : $frame.children().eq(0); + var frameSize = 0; + var slideeSize = 0; + var pos = { + start: 0, + center: 0, + end: 0, + cur: 0, + dest: 0 + }; + + // Scrollbar + var $sb = $(o.scrollBar).eq(0); + var $handle = $sb.children().eq(0); + var sbSize = 0; + var handleSize = 0; + var hPos = { + start: 0, + end: 0, + cur: 0 + }; + + // Pagesbar + var $pb = $(o.pagesBar); + var $pages = 0; + var pages = []; + + // Items + var $items = 0; + var items = []; + var rel = { + firstItem: 0, + lastItem: 0, + centerItem: 0, + activeItem: null, + activePage: 0 + }; + + // Styles + var frameStyles = new StyleRestorer($frame[0]); + var slideeStyles = new StyleRestorer($slidee[0]); + var sbStyles = new StyleRestorer($sb[0]); + var handleStyles = new StyleRestorer($handle[0]); + + // Navigation type booleans + var basicNav = o.itemNav === 'basic'; + var forceCenteredNav = o.itemNav === 'forceCentered'; + var centeredNav = o.itemNav === 'centered' || forceCenteredNav; + var itemNav = !parallax && (basicNav || centeredNav || forceCenteredNav); + + // Miscellaneous + var $scrollSource = o.scrollSource ? $(o.scrollSource) : $frame; + var $dragSource = o.dragSource ? $(o.dragSource) : $frame; + var $forwardButton = $(o.forward); + var $backwardButton = $(o.backward); + var $prevButton = $(o.prev); + var $nextButton = $(o.next); + var $prevPageButton = $(o.prevPage); + var $nextPageButton = $(o.nextPage); + var callbacks = {}; + var last = {}; + var animation = {}; + var move = {}; + var dragging = { + released: 1 + }; + var scrolling = { + last: 0, + delta: 0, + resetTime: 200 + }; + var renderID = 0; + var historyID = 0; + var cycleID = 0; + var continuousID = 0; + var i, l; + + // Normalizing frame + if (!parallax) { + frame = $frame[0]; + } + + // Expose properties + self.initialized = 0; + self.frame = frame; + self.slidee = $slidee[0]; + self.pos = pos; + self.rel = rel; + self.items = items; + self.pages = pages; + self.isPaused = 0; + self.options = o; + self.dragging = dragging; /** * Loading function. @@ -173,195 +173,217 @@ * @param {Boolean} [isInit] Whether load is called from within self.init(). * @return {Void} */ - function load(isInit) { - // Local variables - var lastItemsCount = 0; - var lastPagesCount = pages.length; - - // Save old position - pos.old = $.extend({}, pos); - - // Reset global variables - frameSize = parallax ? 0 : $frame[o.horizontal ? 'width' : 'height'](); - sbSize = $sb[o.horizontal ? 'width' : 'height'](); - slideeSize = parallax ? frame : $slidee[o.horizontal ? 'outerWidth' : 'outerHeight'](); - pages.length = 0; - - // Set position limits & relatives - pos.start = 0; - pos.end = max(slideeSize - frameSize, 0); - - // Sizes & offsets for item based navigations - if (itemNav) { - // Save the number of current items - lastItemsCount = items.length; - - // Reset itemNav related variables - $items = $slidee.children(o.itemSelector); - items.length = 0; - - // Needed variables - var paddingStart = getPx($slidee, o.horizontal ? 'paddingLeft' : 'paddingTop'); - var paddingEnd = getPx($slidee, o.horizontal ? 'paddingRight' : 'paddingBottom'); - var borderBox = $($items).css('boxSizing') === 'border-box'; - var areFloated = $items.css('float') !== 'none'; - var ignoredMargin = 0; - var lastItemIndex = $items.length - 1; - var lastItem; - - // Reset slideeSize - slideeSize = 0; - - // Iterate through items - $items.each(function (i, element) { - // Item - var $item = $(element); - var rect = element.getBoundingClientRect(); - var itemSize = round(o.horizontal ? rect.width || rect.right - rect.left : rect.height || rect.bottom - rect.top); - var itemMarginStart = getPx($item, o.horizontal ? 'marginLeft' : 'marginTop'); - var itemMarginEnd = getPx($item, o.horizontal ? 'marginRight' : 'marginBottom'); - var itemSizeFull = itemSize + itemMarginStart + itemMarginEnd; - var singleSpaced = !itemMarginStart || !itemMarginEnd; - var item = {}; - item.el = element; - item.size = singleSpaced ? itemSize : itemSizeFull; - item.half = item.size / 2; - item.start = slideeSize + (singleSpaced ? itemMarginStart : 0); - item.center = item.start - round(frameSize / 2 - item.size / 2); - item.end = item.start - frameSize + item.size; - - // Account for slidee padding - if (!i) { - slideeSize += paddingStart; - } - - // Increment slidee size for size of the active element - slideeSize += itemSizeFull; - - // 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 (itemMarginEnd && itemMarginStart && i > 0) { - slideeSize -= min(itemMarginStart, itemMarginEnd); - } - } - - // Things to be done on last item - if (i === lastItemIndex) { - item.end += paddingEnd; - slideeSize += paddingEnd; - ignoredMargin = singleSpaced ? itemMarginEnd : 0; - } - - // Add item object to items array - items.push(item); - lastItem = item; - }); - - // Resize SLIDEE to fit all items - $slidee[0].style[o.horizontal ? 'width' : 'height'] = (borderBox ? slideeSize: slideeSize - paddingStart - paddingEnd) + 'px'; - - // Adjust internal SLIDEE size for last margin - slideeSize -= ignoredMargin; - - // Set limits - if (items.length) { - pos.start = items[0][forceCenteredNav ? 'center' : 'start']; - pos.end = forceCenteredNav ? lastItem.center : frameSize < slideeSize ? lastItem.end : pos.start; - } else { - pos.start = pos.end = 0; - } - } - - // Calculate SLIDEE center position - pos.center = round(pos.end / 2 + pos.start / 2); - - // Update relative positions - updateRelatives(); - - // Scrollbar - if ($handle.length && sbSize > 0) { - // Stretch scrollbar handle to represent the visible area - if (o.dynamicHandle) { - handleSize = pos.start === pos.end ? sbSize : round(sbSize * frameSize / slideeSize); - handleSize = within(handleSize, o.minHandleSize, sbSize); - $handle[0].style[o.horizontal ? 'width' : 'height'] = handleSize + 'px'; - } else { - handleSize = $handle[o.horizontal ? 'outerWidth' : 'outerHeight'](); - } - - hPos.end = sbSize - handleSize; - - if (!renderID) { - syncScrollbar(); - } - } - - // Pages - if (!parallax && frameSize > 0) { - var tempPagePos = pos.start; - var pagesHtml = ''; - - // Populate pages array - if (itemNav) { - $.each(items, function (i, item) { - if (forceCenteredNav) { - pages.push(item.center); - } else if (item.start + item.size > tempPagePos && tempPagePos <= pos.end) { - tempPagePos = item.start; - pages.push(tempPagePos); - tempPagePos += frameSize; - if (tempPagePos > pos.end && tempPagePos < pos.end + frameSize) { - pages.push(pos.end); - } - } - }); - } else { - while (tempPagePos - frameSize < pos.end) { - pages.push(tempPagePos); - tempPagePos += frameSize; - } - } - - // Pages bar - if ($pb[0] && lastPagesCount !== pages.length) { - for (var i = 0; i < pages.length; i++) { - pagesHtml += o.pageBuilder.call(self, i); - } - $pages = $pb.html(pagesHtml).children(); - $pages.eq(rel.activePage).addClass(o.activeClass); - } - } - - // Extend relative variables object with some useful info - rel.slideeSize = slideeSize; - rel.frameSize = frameSize; - rel.sbSize = sbSize; - rel.handleSize = handleSize; - - // Activate requested position - if (itemNav) { - if (isInit && o.startAt != null) { - activate(o.startAt); - self[centeredNav ? 'toCenter' : 'toStart'](o.startAt); - } - // Fix possible overflowing - var activeItem = items[rel.activeItem]; - slideTo(centeredNav && activeItem ? activeItem.center : within(pos.dest, pos.start, pos.end)); - } else { - if (isInit) { - if (o.startAt != null) slideTo(o.startAt, 1); - } else { - // Fix possible overflowing - slideTo(within(pos.dest, pos.start, pos.end)); - } - } - - // Trigger load event - trigger('load'); - } - self.reload = function () { load(); }; + function load(isInit) { + // Local variables + var lastItemsCount = 0; + var lastPagesCount = pages.length; + + // Save old position + pos.old = $.extend({}, pos); + + // Reset global variables + frameSize = parallax ? 0 : $frame[o.horizontal ? 'width' : 'height'](); + sbSize = $sb[o.horizontal ? 'width' : 'height'](); + slideeSize = parallax ? frame : $slidee[o.horizontal ? 'outerWidth' : 'outerHeight'](); + pages.length = 0; + + // Set position limits & relatives + pos.start = 0; + pos.end = max(slideeSize - frameSize, 0); + + // Sizes & offsets for item based navigations + if (itemNav) { + // Save the number of current items + lastItemsCount = items.length; + + // Reset itemNav related variables + $items = $slidee.children(o.itemSelector); + items.length = 0; + + // Needed variables + var paddingStart = getPx($slidee, o.horizontal ? 'paddingLeft' : 'paddingTop'); + var paddingEnd = getPx($slidee, o.horizontal ? 'paddingRight' : 'paddingBottom'); + var borderBox = $($items).css('boxSizing') === 'border-box'; + var areFloated = $items.css('float') !== 'none'; + var ignoredMargin = 0; + var lastItemIndex = $items.length - 1; + var lastItem; + + // Reset slideeSize + slideeSize = 0; + + // Iterate through items + $items.each(function (i, element) { + // Item + var $item = $(element); + var rect = element.getBoundingClientRect(); + var itemSize = round(o.horizontal ? rect.width || rect.right - rect.left : rect.height || rect.bottom - rect.top); + var itemMarginStart = getPx($item, o.horizontal ? 'marginLeft' : 'marginTop'); + var itemMarginEnd = getPx($item, o.horizontal ? 'marginRight' : 'marginBottom'); + var itemSizeFull = itemSize + itemMarginStart + itemMarginEnd; + var singleSpaced = !itemMarginStart || !itemMarginEnd; + var item = {}; + item.el = element; + item.size = singleSpaced ? itemSize : itemSizeFull; + item.half = item.size / 2; + item.start = slideeSize + (singleSpaced ? itemMarginStart : 0); + item.center = item.start - round(frameSize / 2 - item.size / 2); + item.end = item.start - frameSize + item.size; + + // Account for slidee padding + if (!i) { + slideeSize += paddingStart; + } + + // Increment slidee size for size of the active element + slideeSize += itemSizeFull; + + // 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 (itemMarginEnd && itemMarginStart && i > 0) { + slideeSize -= min(itemMarginStart, itemMarginEnd); + } + } + + // Things to be done on last item + if (i === lastItemIndex) { + item.end += paddingEnd; + slideeSize += paddingEnd; + ignoredMargin = singleSpaced ? itemMarginEnd : 0; + } + + // Add item object to items array + items.push(item); + lastItem = item; + }); + + // Resize SLIDEE to fit all items + var _finalSize = (borderBox ? slideeSize : slideeSize - paddingStart - paddingEnd); + var _middleItem = null; + + /* + Actually when i try to apply Sly in a div with 2 or more columns of content, the slidee size is too big; + When 'o.verticalColumns' option is set, the slidee size will be calculated to show on the correct size; + We divide the '_finalSize' with the number of columns as necessary; + The '_middleItem' is item that appears in the bottom of the slidee box; + */ + if ((!o.horizontal) && (o.verticalColumns) && (_finalSize > 0)) { + + _finalSize = _finalSize / o.verticalColumns; + _middleItem = items[(Math.ceil($items.length / o.verticalColumns) - 1)]; + } + + $slidee[0].style[o.horizontal ? 'width' : 'height'] = _finalSize + 'px'; + + // Adjust internal SLIDEE size for last margin + slideeSize -= ignoredMargin; + + // Set limits + if (items.length) { + pos.start = items[0][forceCenteredNav ? 'center' : 'start']; + pos.end = forceCenteredNav ? lastItem.center : frameSize < slideeSize ? lastItem.end : pos.start; + + /* + We change the 'pos.end' to page navigation work correctly + */ + if ((!o.horizontal) && (o.verticalColumns)) { + pos.end = _middleItem.end; + + } else { + pos.start = pos.end = 0; + } + } + + // Calculate SLIDEE center position + pos.center = round(pos.end / 2 + pos.start / 2); + + // Update relative positions + updateRelatives(); + + // Scrollbar + if ($handle.length && sbSize > 0) { + // Stretch scrollbar handle to represent the visible area + if (o.dynamicHandle) { + handleSize = pos.start === pos.end ? sbSize : round(sbSize * frameSize / slideeSize); + handleSize = within(handleSize, o.minHandleSize, sbSize); + $handle[0].style[o.horizontal ? 'width' : 'height'] = handleSize + 'px'; + } else { + handleSize = $handle[o.horizontal ? 'outerWidth' : 'outerHeight'](); + } + + hPos.end = sbSize - handleSize; + + if (!renderID) { + syncScrollbar(); + } + } + + // Pages + if (!parallax && frameSize > 0) { + var tempPagePos = pos.start; + var pagesHtml = ''; + + // Populate pages array + if (itemNav) { + $.each(items, function (i, item) { + if (forceCenteredNav) { + pages.push(item.center); + } else if (item.start + item.size > tempPagePos && tempPagePos <= pos.end) { + tempPagePos = item.start; + pages.push(tempPagePos); + tempPagePos += frameSize; + if (tempPagePos > pos.end && tempPagePos < pos.end + frameSize) { + pages.push(pos.end); + } + } + }); + } else { + while (tempPagePos - frameSize < pos.end) { + pages.push(tempPagePos); + tempPagePos += frameSize; + } + } + + // Pages bar + if ($pb[0] && lastPagesCount !== pages.length) { + for (var i = 0; i < pages.length; i++) { + pagesHtml += o.pageBuilder.call(self, i); + } + $pages = $pb.html(pagesHtml).children(); + $pages.eq(rel.activePage).addClass(o.activeClass); + } + } + + // Extend relative variables object with some useful info + rel.slideeSize = slideeSize; + rel.frameSize = frameSize; + rel.sbSize = sbSize; + rel.handleSize = handleSize; + + // Activate requested position + if (itemNav) { + if (isInit && o.startAt != null) { + activate(o.startAt); + self[centeredNav ? 'toCenter' : 'toStart'](o.startAt); + } + // Fix possible overflowing + var activeItem = items[rel.activeItem]; + slideTo(centeredNav && activeItem ? activeItem.center : within(pos.dest, pos.start, pos.end)); + } else { + if (isInit) { + if (o.startAt != null) slideTo(o.startAt, 1); + } else { + // Fix possible overflowing + slideTo(within(pos.dest, pos.start, pos.end)); + } + } + + // Trigger load event + trigger('load'); + } + self.reload = function () { load(); }; /** * Animate to a position. @@ -372,163 +394,163 @@ * * @return {Void} */ - function slideTo(newPos, immediate, dontAlign) { - // Align items - if (itemNav && dragging.released && !dontAlign) { - var tempRel = getRelatives(newPos); - var isNotBordering = newPos > pos.start && newPos < pos.end; - - if (centeredNav) { - if (isNotBordering) { - newPos = items[tempRel.centerItem].center; - } - if (forceCenteredNav && o.activateMiddle) { - activate(tempRel.centerItem); - } - } else if (isNotBordering) { - newPos = items[tempRel.firstItem].start; - } - } - - // Handle overflowing position limits - if (dragging.init && dragging.slidee && o.elasticBounds) { - if (newPos > pos.end) { - newPos = pos.end + (newPos - pos.end) / 6; - } else if (newPos < pos.start) { - newPos = pos.start + (newPos - pos.start) / 6; - } - } else { - newPos = within(newPos, pos.start, pos.end); - } - - // Update the animation object - animation.start = +new Date(); - animation.time = 0; - animation.from = pos.cur; - animation.to = newPos; - animation.delta = newPos - pos.cur; - animation.tweesing = dragging.tweese || dragging.init && !dragging.slidee; - animation.immediate = !animation.tweesing && (immediate || dragging.init && dragging.slidee || !o.speed); - - // Reset dragging tweesing request - dragging.tweese = 0; - - // Start animation rendering - if (newPos !== pos.dest) { - pos.dest = newPos; - trigger('change'); - if (!renderID) { - render(); - } - } - - // Reset next cycle timeout - resetCycle(); - - // Synchronize states - updateRelatives(); - updateButtonsState(); - syncPagesbar(); - } + function slideTo(newPos, immediate, dontAlign) { + // Align items + if (itemNav && dragging.released && !dontAlign) { + var tempRel = getRelatives(newPos); + var isNotBordering = newPos > pos.start && newPos < pos.end; + + if (centeredNav) { + if (isNotBordering) { + newPos = items[tempRel.centerItem].center; + } + if (forceCenteredNav && o.activateMiddle) { + activate(tempRel.centerItem); + } + } else if (isNotBordering) { + newPos = items[tempRel.firstItem].start; + } + } + + // Handle overflowing position limits + if (dragging.init && dragging.slidee && o.elasticBounds) { + if (newPos > pos.end) { + newPos = pos.end + (newPos - pos.end) / 6; + } else if (newPos < pos.start) { + newPos = pos.start + (newPos - pos.start) / 6; + } + } else { + newPos = within(newPos, pos.start, pos.end); + } + + // Update the animation object + animation.start = +new Date(); + animation.time = 0; + animation.from = pos.cur; + animation.to = newPos; + animation.delta = newPos - pos.cur; + animation.tweesing = dragging.tweese || dragging.init && !dragging.slidee; + animation.immediate = !animation.tweesing && (immediate || dragging.init && dragging.slidee || !o.speed); + + // Reset dragging tweesing request + dragging.tweese = 0; + + // Start animation rendering + if (newPos !== pos.dest) { + pos.dest = newPos; + trigger('change'); + if (!renderID) { + render(); + } + } + + // Reset next cycle timeout + resetCycle(); + + // Synchronize states + updateRelatives(); + updateButtonsState(); + syncPagesbar(); + } /** * Render animation frame. * * @return {Void} */ - function render() { - if (!self.initialized) { - return; - } - - // If first render call, wait for next animationFrame - if (!renderID) { - renderID = rAF(render); - if (dragging.released) { - trigger('moveStart'); - } - return; - } - - // If immediate repositioning is requested, don't animate. - if (animation.immediate) { - pos.cur = animation.to; - } - // Use tweesing for animations without known end point - else if (animation.tweesing) { - animation.tweeseDelta = animation.to - pos.cur; - // Fuck Zeno's paradox - if (abs(animation.tweeseDelta) < 0.1) { - pos.cur = animation.to; - } else { - pos.cur += animation.tweeseDelta * (dragging.released ? o.swingSpeed : o.syncSpeed); - } - } - // Use tweening for basic animations with known end point - else { - animation.time = min(+new Date() - animation.start, o.speed); - pos.cur = animation.from + animation.delta * $.easing[o.easing](animation.time/o.speed, animation.time, 0, 1, o.speed); - } - - // If there is nothing more to render break the rendering loop, otherwise request new animation frame. - if (animation.to === pos.cur) { - pos.cur = animation.to; - dragging.tweese = renderID = 0; - } else { - renderID = rAF(render); - } - - trigger('move'); - - // Update SLIDEE position - if (!parallax) { - if (transform) { - $slidee[0].style[transform] = gpuAcceleration + (o.horizontal ? 'translateX' : 'translateY') + '(' + (-pos.cur) + 'px)'; - } else { - $slidee[0].style[o.horizontal ? 'left' : 'top'] = -round(pos.cur) + 'px'; - } - } - - // When animation reached the end, and dragging is not active, trigger moveEnd - if (!renderID && dragging.released) { - trigger('moveEnd'); - } - - syncScrollbar(); - } + function render() { + if (!self.initialized) { + return; + } + + // If first render call, wait for next animationFrame + if (!renderID) { + renderID = rAF(render); + if (dragging.released) { + trigger('moveStart'); + } + return; + } + + // If immediate repositioning is requested, don't animate. + if (animation.immediate) { + pos.cur = animation.to; + } + // Use tweesing for animations without known end point + else if (animation.tweesing) { + animation.tweeseDelta = animation.to - pos.cur; + // Fuck Zeno's paradox + if (abs(animation.tweeseDelta) < 0.1) { + pos.cur = animation.to; + } else { + pos.cur += animation.tweeseDelta * (dragging.released ? o.swingSpeed : o.syncSpeed); + } + } + // Use tweening for basic animations with known end point + else { + animation.time = min(+new Date() - animation.start, o.speed); + pos.cur = animation.from + animation.delta * $.easing[o.easing](animation.time / o.speed, animation.time, 0, 1, o.speed); + } + + // If there is nothing more to render break the rendering loop, otherwise request new animation frame. + if (animation.to === pos.cur) { + pos.cur = animation.to; + dragging.tweese = renderID = 0; + } else { + renderID = rAF(render); + } + + trigger('move'); + + // Update SLIDEE position + if (!parallax) { + if (transform) { + $slidee[0].style[transform] = gpuAcceleration + (o.horizontal ? 'translateX' : 'translateY') + '(' + (-pos.cur) + 'px)'; + } else { + $slidee[0].style[o.horizontal ? 'left' : 'top'] = -round(pos.cur) + 'px'; + } + } + + // When animation reached the end, and dragging is not active, trigger moveEnd + if (!renderID && dragging.released) { + trigger('moveEnd'); + } + + syncScrollbar(); + } /** * Synchronizes scrollbar with the SLIDEE. * * @return {Void} */ - function syncScrollbar() { - if ($handle.length) { - hPos.cur = pos.start === pos.end ? 0 : (((dragging.init && !dragging.slidee) ? pos.dest : pos.cur) - pos.start) / (pos.end - pos.start) * hPos.end; - hPos.cur = within(round(hPos.cur), hPos.start, hPos.end); - if (last.hPos !== hPos.cur) { - last.hPos = hPos.cur; - if (transform) { - $handle[0].style[transform] = gpuAcceleration + (o.horizontal ? 'translateX' : 'translateY') + '(' + hPos.cur + 'px)'; - } else { - $handle[0].style[o.horizontal ? 'left' : 'top'] = hPos.cur + 'px'; - } - } - } - } + function syncScrollbar() { + if ($handle.length) { + hPos.cur = pos.start === pos.end ? 0 : (((dragging.init && !dragging.slidee) ? pos.dest : pos.cur) - pos.start) / (pos.end - pos.start) * hPos.end; + hPos.cur = within(round(hPos.cur), hPos.start, hPos.end); + if (last.hPos !== hPos.cur) { + last.hPos = hPos.cur; + if (transform) { + $handle[0].style[transform] = gpuAcceleration + (o.horizontal ? 'translateX' : 'translateY') + '(' + hPos.cur + 'px)'; + } else { + $handle[0].style[o.horizontal ? 'left' : 'top'] = hPos.cur + 'px'; + } + } + } + } /** * Synchronizes pagesbar with SLIDEE. * * @return {Void} */ - function syncPagesbar() { - if ($pages[0] && last.page !== rel.activePage) { - last.page = rel.activePage; - $pages.removeClass(o.activeClass).eq(rel.activePage).addClass(o.activeClass); - trigger('activePage', last.page); - } - } + function syncPagesbar() { + if ($pages[0] && last.page !== rel.activePage) { + last.page = rel.activePage; + $pages.removeClass(o.activeClass).eq(rel.activePage).addClass(o.activeClass); + trigger('activePage', last.page); + } + } /** * Returns the position object. @@ -537,28 +559,28 @@ * * @return {Object} */ - self.getPos = function (item) { - if (itemNav) { - var index = getIndex(item); - return index !== -1 ? items[index] : false; - } else { - var $item = $slidee.find(item).eq(0); - - if ($item[0]) { - var offset = o.horizontal ? $item.offset().left - $slidee.offset().left : $item.offset().top - $slidee.offset().top; - var size = $item[o.horizontal ? 'outerWidth' : 'outerHeight'](); - - return { - start: offset, - center: offset - frameSize / 2 + size / 2, - end: offset - frameSize + size, - size: size - }; - } else { - return false; - } - } - }; + self.getPos = function (item) { + if (itemNav) { + var index = getIndex(item); + return index !== -1 ? items[index] : false; + } else { + var $item = $slidee.find(item).eq(0); + + if ($item[0]) { + var offset = o.horizontal ? $item.offset().left - $slidee.offset().left : $item.offset().top - $slidee.offset().top; + var size = $item[o.horizontal ? 'outerWidth' : 'outerHeight'](); + + return { + start: offset, + center: offset - frameSize / 2 + size / 2, + end: offset - frameSize + size, + size: size + }; + } else { + return false; + } + } + }; /** * Continuous move in a specified direction. @@ -568,97 +590,97 @@ * * @return {Void} */ - self.moveBy = function (speed) { - move.speed = speed; - // If already initiated, or there is nowhere to move, abort - if (dragging.init || !move.speed || pos.cur === (move.speed > 0 ? pos.end : pos.start)) { - return; - } - // Initiate move object - move.lastTime = +new Date(); - move.startPos = pos.cur; - // Set dragging as initiated - continuousInit('button'); - dragging.init = 1; - // Start movement - trigger('moveStart'); - cAF(continuousID); - moveLoop(); - }; + self.moveBy = function (speed) { + move.speed = speed; + // If already initiated, or there is nowhere to move, abort + if (dragging.init || !move.speed || pos.cur === (move.speed > 0 ? pos.end : pos.start)) { + return; + } + // Initiate move object + move.lastTime = +new Date(); + move.startPos = pos.cur; + // Set dragging as initiated + continuousInit('button'); + dragging.init = 1; + // Start movement + trigger('moveStart'); + cAF(continuousID); + moveLoop(); + }; /** * Continuous movement loop. * * @return {Void} */ - function moveLoop() { - // If there is nowhere to move anymore, stop - if (!move.speed || pos.cur === (move.speed > 0 ? pos.end : pos.start)) { - self.stop(); - } - // Request new move loop if it hasn't been stopped - continuousID = dragging.init ? rAF(moveLoop) : 0; - // Update move object - move.now = +new Date(); - move.pos = pos.cur + (move.now - move.lastTime) / 1000 * move.speed; - // Slide - slideTo(dragging.init ? move.pos : round(move.pos)); - // Normally, this is triggered in render(), but if there - // is nothing to render, we have to do it manually here. - if (!dragging.init && pos.cur === pos.dest) { - trigger('moveEnd'); - } - // Update times for future iteration - move.lastTime = move.now; - } + function moveLoop() { + // If there is nowhere to move anymore, stop + if (!move.speed || pos.cur === (move.speed > 0 ? pos.end : pos.start)) { + self.stop(); + } + // Request new move loop if it hasn't been stopped + continuousID = dragging.init ? rAF(moveLoop) : 0; + // Update move object + move.now = +new Date(); + move.pos = pos.cur + (move.now - move.lastTime) / 1000 * move.speed; + // Slide + slideTo(dragging.init ? move.pos : round(move.pos)); + // Normally, this is triggered in render(), but if there + // is nothing to render, we have to do it manually here. + if (!dragging.init && pos.cur === pos.dest) { + trigger('moveEnd'); + } + // Update times for future iteration + move.lastTime = move.now; + } /** * Stops continuous movement. * * @return {Void} */ - self.stop = function () { - if (dragging.source === 'button') { - dragging.init = 0; - dragging.released = 1; - } - }; + self.stop = function () { + if (dragging.source === 'button') { + dragging.init = 0; + dragging.released = 1; + } + }; /** * Activate previous item. * * @return {Void} */ - self.prev = function () { - self.activate(rel.activeItem == null ? 0 : rel.activeItem - 1); - }; + self.prev = function () { + self.activate(rel.activeItem == null ? 0 : rel.activeItem - 1); + }; /** * Activate next item. * * @return {Void} */ - self.next = function () { - self.activate(rel.activeItem == null ? 0 : rel.activeItem + 1); - }; + self.next = function () { + self.activate(rel.activeItem == null ? 0 : rel.activeItem + 1); + }; /** * Activate previous page. * * @return {Void} */ - self.prevPage = function () { - self.activatePage(rel.activePage - 1); - }; + self.prevPage = function () { + self.activatePage(rel.activePage - 1); + }; /** * Activate next page. * * @return {Void} */ - self.nextPage = function () { - self.activatePage(rel.activePage + 1); - }; + self.nextPage = function () { + self.activatePage(rel.activePage + 1); + }; /** * Slide SLIDEE by amount of pixels. @@ -668,18 +690,18 @@ * * @return {Void} */ - self.slideBy = function (delta, immediate) { - if (!delta) { - return; - } - if (itemNav) { - self[centeredNav ? 'toCenter' : 'toStart']( - within((centeredNav ? rel.centerItem : rel.firstItem) + o.scrollBy * delta, 0, items.length) - ); - } else { - slideTo(pos.dest + delta, immediate); - } - }; + self.slideBy = function (delta, immediate) { + if (!delta) { + return; + } + if (itemNav) { + self[centeredNav ? 'toCenter' : 'toStart']( + within((centeredNav ? rel.centerItem : rel.firstItem) + o.scrollBy * delta, 0, items.length) + ); + } else { + slideTo(pos.dest + delta, immediate); + } + }; /** * Animate SLIDEE to a specific position. @@ -689,9 +711,9 @@ * * @return {Void} */ - self.slideTo = function (pos, immediate) { - slideTo(pos, immediate); - }; + self.slideTo = function (pos, immediate) { + slideTo(pos, immediate); + }; /** * Core method for handling `toLocation` methods. @@ -702,28 +724,28 @@ * * @return {Void} */ - function to(location, item, immediate) { - // Optional arguments logic - if (type(item) === 'boolean') { - immediate = item; - item = undefined; - } - - if (item === undefined) { - slideTo(pos[location], immediate); - } else { - // You can't align items to sides of the frame - // when centered navigation type is enabled - if (centeredNav && location !== 'center') { - return; - } - - var itemPos = self.getPos(item); - if (itemPos) { - slideTo(itemPos[location], immediate, !centeredNav); - } - } - } + function to(location, item, immediate) { + // Optional arguments logic + if (type(item) === 'boolean') { + immediate = item; + item = undefined; + } + + if (item === undefined) { + slideTo(pos[location], immediate); + } else { + // You can't align items to sides of the frame + // when centered navigation type is enabled + if (centeredNav && location !== 'center') { + return; + } + + var itemPos = self.getPos(item); + if (itemPos) { + slideTo(itemPos[location], immediate, !centeredNav); + } + } + } /** * Animate element or the whole SLIDEE to the start of the frame. @@ -733,9 +755,9 @@ * * @return {Void} */ - self.toStart = function (item, immediate) { - to('start', item, immediate); - }; + self.toStart = function (item, immediate) { + to('start', item, immediate); + }; /** * Animate element or the whole SLIDEE to the end of the frame. @@ -745,9 +767,9 @@ * * @return {Void} */ - self.toEnd = function (item, immediate) { - to('end', item, immediate); - }; + self.toEnd = function (item, immediate) { + to('end', item, immediate); + }; /** * Animate element or the whole SLIDEE to the center of the frame. @@ -757,9 +779,9 @@ * * @return {Void} */ - self.toCenter = function (item, immediate) { - to('center', item, immediate); - }; + self.toCenter = function (item, immediate) { + to('center', item, immediate); + }; /** * Get the index of an item in SLIDEE. @@ -768,16 +790,16 @@ * * @return {Int} Item index, or -1 if not found. */ - function getIndex(item) { - return item != null ? - isNumber(item) ? - item >= 0 && item < items.length ? item : -1 : - $items.index(item) : - -1; - } - // Expose getIndex without lowering the compressibility of it, - // as it is used quite often throughout Sly. - self.getIndex = getIndex; + function getIndex(item) { + return item != null ? + isNumber(item) ? + item >= 0 && item < items.length ? item : -1 : + $items.index(item) : + -1; + } + // Expose getIndex without lowering the compressibility of it, + // as it is used quite often throughout Sly. + self.getIndex = getIndex; /** * Get index of an item in SLIDEE based on a variety of input types. @@ -786,9 +808,9 @@ * * @return {Int} Item index, or -1 if not found. */ - function getRelativeIndex(item) { - return getIndex(isNumber(item) && item < 0 ? item + items.length : item); - } + function getRelativeIndex(item) { + return getIndex(isNumber(item) && item < 0 ? item + items.length : item); + } /** * Activates an item. @@ -797,28 +819,28 @@ * * @return {Mixed} Activated item index or false on fail. */ - function activate(item, force) { - var index = getIndex(item); + function activate(item, force) { + var index = getIndex(item); - if (!itemNav || index < 0) { - return false; - } + if (!itemNav || index < 0) { + return false; + } - // Update classes, last active index, and trigger active event only when there - // has been a change. Otherwise just return the current active index. - if (last.active !== index || force) { - // Update classes - $items.eq(rel.activeItem).removeClass(o.activeClass); - $items.eq(index).addClass(o.activeClass); + // Update classes, last active index, and trigger active event only when there + // has been a change. Otherwise just return the current active index. + if (last.active !== index || force) { + // Update classes + $items.eq(rel.activeItem).removeClass(o.activeClass); + $items.eq(index).addClass(o.activeClass); - last.active = rel.activeItem = index; + last.active = rel.activeItem = index; - updateButtonsState(); - trigger('active', index); - } + updateButtonsState(); + trigger('active', index); + } - return index; - } + return index; + } /** * Activates an item and helps with further navigation when o.smart is enabled. @@ -828,26 +850,26 @@ * * @return {Void} */ - self.activate = function (item, immediate) { - var index = activate(item); - - // Smart navigation - if (o.smart && index !== false) { - // When centeredNav is enabled, center the element. - // Otherwise, determine where to position the element based on its current position. - // If the element is currently on the far end side of the frame, assume that user is - // moving forward and animate it to the start of the visible frame, and vice versa. - if (centeredNav) { - self.toCenter(index, immediate); - } else if (index >= rel.lastItem) { - self.toStart(index, immediate); - } else if (index <= rel.firstItem) { - self.toEnd(index, immediate); - } else { - resetCycle(); - } - } - }; + self.activate = function (item, immediate) { + var index = activate(item); + + // Smart navigation + if (o.smart && index !== false) { + // When centeredNav is enabled, center the element. + // Otherwise, determine where to position the element based on its current position. + // If the element is currently on the far end side of the frame, assume that user is + // moving forward and animate it to the start of the visible frame, and vice versa. + if (centeredNav) { + self.toCenter(index, immediate); + } else if (index >= rel.lastItem) { + self.toStart(index, immediate); + } else if (index <= rel.firstItem) { + self.toEnd(index, immediate); + } else { + resetCycle(); + } + } + }; /** * Activates a page. @@ -857,11 +879,11 @@ * * @return {Void} */ - self.activatePage = function (index, immediate) { - if (isNumber(index)) { - slideTo(pages[within(index, 0, pages.length - 1)], immediate); - } - }; + self.activatePage = function (index, immediate) { + if (isNumber(index)) { + slideTo(pages[within(index, 0, pages.length - 1)], immediate); + } + }; /** * Return relative positions of items based on their visibility within FRAME. @@ -870,60 +892,60 @@ * * @return {Void} */ - function getRelatives(slideePos) { - slideePos = within(isNumber(slideePos) ? slideePos : pos.dest, pos.start, pos.end); - - var relatives = {}; - var centerOffset = forceCenteredNav ? 0 : frameSize / 2; - - // Determine active page - if (!parallax) { - for (var p = 0, pl = pages.length; p < pl; p++) { - if (slideePos >= pos.end || p === pages.length - 1) { - relatives.activePage = pages.length - 1; - break; - } - - if (slideePos <= pages[p] + centerOffset) { - relatives.activePage = p; - break; - } - } - } - - // Relative item indexes - if (itemNav) { - var first = false; - var last = false; - var center = false; - - // From start - for (var i = 0, il = items.length; i < il; i++) { - // First item - if (first === false && slideePos <= items[i].start + items[i].half) { - first = i; - } - - // Center item - if (center === false && slideePos <= items[i].center + items[i].half) { - center = i; - } - - // Last item - if (i === il - 1 || slideePos <= items[i].end + items[i].half) { - last = i; - break; - } - } - - // Safe assignment, just to be sure the false won't be returned - relatives.firstItem = isNumber(first) ? first : 0; - relatives.centerItem = isNumber(center) ? center : relatives.firstItem; - relatives.lastItem = isNumber(last) ? last : relatives.centerItem; - } - - return relatives; - } + function getRelatives(slideePos) { + slideePos = within(isNumber(slideePos) ? slideePos : pos.dest, pos.start, pos.end); + + var relatives = {}; + var centerOffset = forceCenteredNav ? 0 : frameSize / 2; + + // Determine active page + if (!parallax) { + for (var p = 0, pl = pages.length; p < pl; p++) { + if (slideePos >= pos.end || p === pages.length - 1) { + relatives.activePage = pages.length - 1; + break; + } + + if (slideePos <= pages[p] + centerOffset) { + relatives.activePage = p; + break; + } + } + } + + // Relative item indexes + if (itemNav) { + var first = false; + var last = false; + var center = false; + + // From start + for (var i = 0, il = items.length; i < il; i++) { + // First item + if (first === false && slideePos <= items[i].start + items[i].half) { + first = i; + } + + // Center item + if (center === false && slideePos <= items[i].center + items[i].half) { + center = i; + } + + // Last item + if (i === il - 1 || slideePos <= items[i].end + items[i].half) { + last = i; + break; + } + } + + // Safe assignment, just to be sure the false won't be returned + relatives.firstItem = isNumber(first) ? first : 0; + relatives.centerItem = isNumber(center) ? center : relatives.firstItem; + relatives.lastItem = isNumber(last) ? last : relatives.centerItem; + } + + return relatives; + } /** * Update object with relative positions. @@ -932,9 +954,9 @@ * * @return {Void} */ - function updateRelatives(newPos) { - $.extend(rel, getRelatives(newPos)); - } + function updateRelatives(newPos) { + $.extend(rel, getRelatives(newPos)); + } /** * Disable navigation buttons when needed. @@ -943,63 +965,63 @@ * * @return {Void} */ - function updateButtonsState() { - var isStart = pos.dest <= pos.start; - var isEnd = pos.dest >= pos.end; - var slideePosState = (isStart ? 1 : 0) | (isEnd ? 2 : 0); - - // Update paging buttons only if there has been a change in SLIDEE position - if (last.slideePosState !== slideePosState) { - last.slideePosState = slideePosState; - - if ($prevPageButton.is('button,input')) { - $prevPageButton.prop('disabled', isStart); - } - - if ($nextPageButton.is('button,input')) { - $nextPageButton.prop('disabled', isEnd); - } - - $prevPageButton.add($backwardButton)[isStart ? 'addClass' : 'removeClass'](o.disabledClass); - $nextPageButton.add($forwardButton)[isEnd ? 'addClass' : 'removeClass'](o.disabledClass); - } - - // Forward & Backward buttons need a separate state caching because we cannot "property disable" - // them while they are being used, as disabled buttons stop emitting mouse events. - if (last.fwdbwdState !== slideePosState && dragging.released) { - last.fwdbwdState = slideePosState; - - if ($backwardButton.is('button,input')) { - $backwardButton.prop('disabled', isStart); - } - - if ($forwardButton.is('button,input')) { - $forwardButton.prop('disabled', isEnd); - } - } - - // Item navigation - if (itemNav && rel.activeItem != null) { - var isFirst = rel.activeItem === 0; - var isLast = rel.activeItem >= items.length - 1; - var itemsButtonState = (isFirst ? 1 : 0) | (isLast ? 2 : 0); - - if (last.itemsButtonState !== itemsButtonState) { - last.itemsButtonState = itemsButtonState; - - if ($prevButton.is('button,input')) { - $prevButton.prop('disabled', isFirst); - } - - if ($nextButton.is('button,input')) { - $nextButton.prop('disabled', isLast); - } - - $prevButton[isFirst ? 'addClass' : 'removeClass'](o.disabledClass); - $nextButton[isLast ? 'addClass' : 'removeClass'](o.disabledClass); - } - } - } + function updateButtonsState() { + var isStart = pos.dest <= pos.start; + var isEnd = pos.dest >= pos.end; + var slideePosState = (isStart ? 1 : 0) | (isEnd ? 2 : 0); + + // Update paging buttons only if there has been a change in SLIDEE position + if (last.slideePosState !== slideePosState) { + last.slideePosState = slideePosState; + + if ($prevPageButton.is('button,input')) { + $prevPageButton.prop('disabled', isStart); + } + + if ($nextPageButton.is('button,input')) { + $nextPageButton.prop('disabled', isEnd); + } + + $prevPageButton.add($backwardButton)[isStart ? 'addClass' : 'removeClass'](o.disabledClass); + $nextPageButton.add($forwardButton)[isEnd ? 'addClass' : 'removeClass'](o.disabledClass); + } + + // Forward & Backward buttons need a separate state caching because we cannot "property disable" + // them while they are being used, as disabled buttons stop emitting mouse events. + if (last.fwdbwdState !== slideePosState && dragging.released) { + last.fwdbwdState = slideePosState; + + if ($backwardButton.is('button,input')) { + $backwardButton.prop('disabled', isStart); + } + + if ($forwardButton.is('button,input')) { + $forwardButton.prop('disabled', isEnd); + } + } + + // Item navigation + if (itemNav && rel.activeItem != null) { + var isFirst = rel.activeItem === 0; + var isLast = rel.activeItem >= items.length - 1; + var itemsButtonState = (isFirst ? 1 : 0) | (isLast ? 2 : 0); + + if (last.itemsButtonState !== itemsButtonState) { + last.itemsButtonState = itemsButtonState; + + if ($prevButton.is('button,input')) { + $prevButton.prop('disabled', isFirst); + } + + if ($nextButton.is('button,input')) { + $nextButton.prop('disabled', isLast); + } + + $prevButton[isFirst ? 'addClass' : 'removeClass'](o.disabledClass); + $nextButton[isLast ? 'addClass' : 'removeClass'](o.disabledClass); + } + } + } /** * Resume cycling. @@ -1008,32 +1030,32 @@ * * @return {Void} */ - self.resume = function (priority) { - if (!o.cycleBy || !o.cycleInterval || o.cycleBy === 'items' && (!items[0] || rel.activeItem == null) || priority < self.isPaused) { - return; - } - - self.isPaused = 0; - - if (cycleID) { - cycleID = clearTimeout(cycleID); - } else { - trigger('resume'); - } - - cycleID = setTimeout(function () { - trigger('cycle'); - switch (o.cycleBy) { - case 'items': - self.activate(rel.activeItem >= items.length - 1 ? 0 : rel.activeItem + 1); - break; - - case 'pages': - self.activatePage(rel.activePage >= pages.length - 1 ? 0 : rel.activePage + 1); - break; - } - }, o.cycleInterval); - }; + self.resume = function (priority) { + if (!o.cycleBy || !o.cycleInterval || o.cycleBy === 'items' && (!items[0] || rel.activeItem == null) || priority < self.isPaused) { + return; + } + + self.isPaused = 0; + + if (cycleID) { + cycleID = clearTimeout(cycleID); + } else { + trigger('resume'); + } + + cycleID = setTimeout(function () { + trigger('cycle'); + switch (o.cycleBy) { + case 'items': + self.activate(rel.activeItem >= items.length - 1 ? 0 : rel.activeItem + 1); + break; + + case 'pages': + self.activatePage(rel.activePage >= pages.length - 1 ? 0 : rel.activePage + 1); + break; + } + }, o.cycleInterval); + }; /** * Pause cycling. @@ -1042,27 +1064,27 @@ * * @return {Void} */ - self.pause = function (priority) { - if (priority < self.isPaused) { - return; - } + self.pause = function (priority) { + if (priority < self.isPaused) { + return; + } - self.isPaused = priority || 100; + self.isPaused = priority || 100; - if (cycleID) { - cycleID = clearTimeout(cycleID); - trigger('pause'); - } - }; + if (cycleID) { + cycleID = clearTimeout(cycleID); + trigger('pause'); + } + }; /** * Toggle cycling. * * @return {Void} */ - self.toggle = function () { - self[cycleID ? 'pause' : 'resume'](); - }; + self.toggle = function () { + self[cycleID ? 'pause' : 'resume'](); + }; /** * Updates a signle or multiple option values. @@ -1072,13 +1094,13 @@ * * @return {Void} */ - self.set = function (name, value) { - if ($.isPlainObject(name)) { - $.extend(o, name); - } else if (o.hasOwnProperty(name)) { - o[name] = value; - } - }; + self.set = function (name, value) { + if ($.isPlainObject(name)) { + $.extend(o, name); + } else if (o.hasOwnProperty(name)) { + o[name] = value; + } + }; /** * Add one or multiple items to the SLIDEE end, or a specified position index. @@ -1088,28 +1110,28 @@ * * @return {Void} */ - self.add = function (element, index) { - var $element = $(element); - - if (itemNav) { - // Insert the element(s) - if (index == null || !items[0] || index >= items.length) { - $element.appendTo($slidee); - } else if (items.length) { - $element.insertBefore(items[index].el); - } - - // Adjust the activeItem index - if (rel.activeItem != null && index <= rel.activeItem) { - last.active = rel.activeItem += $element.length; - } - } else { - $slidee.append($element); - } - - // Reload - load(); - }; + self.add = function (element, index) { + var $element = $(element); + + if (itemNav) { + // Insert the element(s) + if (index == null || !items[0] || index >= items.length) { + $element.appendTo($slidee); + } else if (items.length) { + $element.insertBefore(items[index].el); + } + + // Adjust the activeItem index + if (rel.activeItem != null && index <= rel.activeItem) { + last.active = rel.activeItem += $element.length; + } + } else { + $slidee.append($element); + } + + // Reload + load(); + }; /** * Remove an item from SLIDEE. @@ -1119,36 +1141,36 @@ * * @return {Void} */ - self.remove = function (element) { - if (itemNav) { - var index = getRelativeIndex(element); - - if (index > -1) { - // Remove the element - $items.eq(index).remove(); - - // If the current item is being removed, activate new one after reload - var reactivate = index === rel.activeItem; - - // Adjust the activeItem index - if (rel.activeItem != null && index < rel.activeItem) { - last.active = --rel.activeItem; - } - - // Reload - load(); - - // Activate new item at the removed position - if (reactivate) { - last.active = null; - self.activate(rel.activeItem); - } - } - } else { - $(element).remove(); - load(); - } - }; + self.remove = function (element) { + if (itemNav) { + var index = getRelativeIndex(element); + + if (index > -1) { + // Remove the element + $items.eq(index).remove(); + + // If the current item is being removed, activate new one after reload + var reactivate = index === rel.activeItem; + + // Adjust the activeItem index + if (rel.activeItem != null && index < rel.activeItem) { + last.active = --rel.activeItem; + } + + // Reload + load(); + + // Activate new item at the removed position + if (reactivate) { + last.active = null; + self.activate(rel.activeItem); + } + } + } else { + $(element).remove(); + load(); + } + }; /** * Helps re-arranging items. @@ -1159,31 +1181,31 @@ * * @return {Void} */ - function moveItem(item, position, after) { - item = getRelativeIndex(item); - position = getRelativeIndex(position); - - // Move only if there is an actual change requested - if (item > -1 && position > -1 && item !== position && (!after || position !== item - 1) && (after || position !== item + 1)) { - $items.eq(item)[after ? 'insertAfter' : 'insertBefore'](items[position].el); - - var shiftStart = item < position ? item : (after ? position : position - 1); - var shiftEnd = item > position ? item : (after ? position + 1 : position); - var shiftsUp = item > position; - - // Update activeItem index - if (rel.activeItem != null) { - if (item === rel.activeItem) { - last.active = rel.activeItem = after ? (shiftsUp ? position + 1 : position) : (shiftsUp ? position : position - 1); - } else if (rel.activeItem > shiftStart && rel.activeItem < shiftEnd) { - last.active = rel.activeItem += shiftsUp ? 1 : -1; - } - } - - // Reload - load(); - } - } + function moveItem(item, position, after) { + item = getRelativeIndex(item); + position = getRelativeIndex(position); + + // Move only if there is an actual change requested + if (item > -1 && position > -1 && item !== position && (!after || position !== item - 1) && (after || position !== item + 1)) { + $items.eq(item)[after ? 'insertAfter' : 'insertBefore'](items[position].el); + + var shiftStart = item < position ? item : (after ? position : position - 1); + var shiftEnd = item > position ? item : (after ? position + 1 : position); + var shiftsUp = item > position; + + // Update activeItem index + if (rel.activeItem != null) { + if (item === rel.activeItem) { + last.active = rel.activeItem = after ? (shiftsUp ? position + 1 : position) : (shiftsUp ? position : position - 1); + } else if (rel.activeItem > shiftStart && rel.activeItem < shiftEnd) { + last.active = rel.activeItem += shiftsUp ? 1 : -1; + } + } + + // Reload + load(); + } + } /** * Move item after the target anchor. @@ -1193,9 +1215,9 @@ * * @return {Void} */ - self.moveAfter = function (item, position) { - moveItem(item, position, 1); - }; + self.moveAfter = function (item, position) { + moveItem(item, position, 1); + }; /** * Move item before the target anchor. @@ -1205,9 +1227,9 @@ * * @return {Void} */ - self.moveBefore = function (item, position) { - moveItem(item, position); - }; + self.moveBefore = function (item, position) { + moveItem(item, position); + }; /** * Registers callbacks. @@ -1217,30 +1239,30 @@ * * @return {Void} */ - self.on = function (name, fn) { - // Callbacks map - if (type(name) === 'object') { - for (var key in name) { - if (name.hasOwnProperty(key)) { - self.on(key, name[key]); - } - } - // Callback - } else if (type(fn) === 'function') { - var names = name.split(' '); - for (var n = 0, nl = names.length; n < nl; n++) { - callbacks[names[n]] = callbacks[names[n]] || []; - if (callbackIndex(names[n], fn) === -1) { - callbacks[names[n]].push(fn); - } - } - // Callbacks array - } else if (type(fn) === 'array') { - for (var f = 0, fl = fn.length; f < fl; f++) { - self.on(name, fn[f]); - } - } - }; + self.on = function (name, fn) { + // Callbacks map + if (type(name) === 'object') { + for (var key in name) { + if (name.hasOwnProperty(key)) { + self.on(key, name[key]); + } + } + // Callback + } else if (type(fn) === 'function') { + var names = name.split(' '); + for (var n = 0, nl = names.length; n < nl; n++) { + callbacks[names[n]] = callbacks[names[n]] || []; + if (callbackIndex(names[n], fn) === -1) { + callbacks[names[n]].push(fn); + } + } + // Callbacks array + } else if (type(fn) === 'array') { + for (var f = 0, fl = fn.length; f < fl; f++) { + self.on(name, fn[f]); + } + } + }; /** * Registers callbacks to be executed only once. @@ -1250,13 +1272,13 @@ * * @return {Void} */ - self.one = function (name, fn) { - function proxy() { - fn.apply(self, arguments); - self.off(name, proxy); - } - self.on(name, proxy); - }; + self.one = function (name, fn) { + function proxy() { + fn.apply(self, arguments); + self.off(name, proxy); + } + self.on(name, proxy); + }; /** * Remove one or all callbacks. @@ -1266,26 +1288,26 @@ * * @return {Void} */ - self.off = function (name, fn) { - if (fn instanceof Array) { - for (var f = 0, fl = fn.length; f < fl; f++) { - self.off(name, fn[f]); - } - } else { - var names = name.split(' '); - for (var n = 0, nl = names.length; n < nl; n++) { - callbacks[names[n]] = callbacks[names[n]] || []; - if (fn == null) { - callbacks[names[n]].length = 0; - } else { - var index = callbackIndex(names[n], fn); - if (index !== -1) { - callbacks[names[n]].splice(index, 1); - } - } - } - } - }; + self.off = function (name, fn) { + if (fn instanceof Array) { + for (var f = 0, fl = fn.length; f < fl; f++) { + self.off(name, fn[f]); + } + } else { + var names = name.split(' '); + for (var n = 0, nl = names.length; n < nl; n++) { + callbacks[names[n]] = callbacks[names[n]] || []; + if (fn == null) { + callbacks[names[n]].length = 0; + } else { + var index = callbackIndex(names[n], fn); + if (index !== -1) { + callbacks[names[n]].splice(index, 1); + } + } + } + } + }; /** * Returns callback array index. @@ -1295,25 +1317,25 @@ * * @return {Int} Callback array index, or -1 if isn't registered. */ - function callbackIndex(name, fn) { - for (var i = 0, l = callbacks[name].length; i < l; i++) { - if (callbacks[name][i] === fn) { - return i; - } - } - return -1; - } + function callbackIndex(name, fn) { + for (var i = 0, l = callbacks[name].length; i < l; i++) { + if (callbacks[name][i] === fn) { + return i; + } + } + return -1; + } /** * Reset next cycle timeout. * * @return {Void} */ - function resetCycle() { - if (dragging.released && !self.isPaused) { - self.resume(); - } - } + function resetCycle() { + if (dragging.released && !self.isPaused) { + self.resume(); + } + } /** * Calculate SLIDEE representation of handle position. @@ -1322,35 +1344,35 @@ * * @return {Int} */ - function handleToSlidee(handlePos) { - return round(within(handlePos, hPos.start, hPos.end) / hPos.end * (pos.end - pos.start)) + pos.start; - } + function handleToSlidee(handlePos) { + return round(within(handlePos, hPos.start, hPos.end) / hPos.end * (pos.end - pos.start)) + pos.start; + } /** * Keeps track of a dragging delta history. * * @return {Void} */ - function draggingHistoryTick() { - // Looking at this, I know what you're thinking :) But as we need only 4 history states, doing it this way - // as opposed to a proper loop is ~25 bytes smaller (when minified with GCC), a lot faster, and doesn't - // generate garbage. The loop version would create 2 new variables on every tick. Unexaptable! - dragging.history[0] = dragging.history[1]; - dragging.history[1] = dragging.history[2]; - dragging.history[2] = dragging.history[3]; - dragging.history[3] = dragging.delta; - } + function draggingHistoryTick() { + // Looking at this, I know what you're thinking :) But as we need only 4 history states, doing it this way + // as opposed to a proper loop is ~25 bytes smaller (when minified with GCC), a lot faster, and doesn't + // generate garbage. The loop version would create 2 new variables on every tick. Unexaptable! + dragging.history[0] = dragging.history[1]; + dragging.history[1] = dragging.history[2]; + dragging.history[2] = dragging.history[3]; + dragging.history[3] = dragging.delta; + } /** * Initialize continuous movement. * * @return {Void} */ - function continuousInit(source) { - dragging.released = 0; - dragging.source = source; - dragging.slidee = source === 'slidee'; - } + function continuousInit(source) { + dragging.released = 0; + dragging.source = source; + dragging.slidee = source === 'slidee'; + } /** * Dragging initiator. @@ -1359,68 +1381,68 @@ * * @return {Void} */ - function dragInit(event) { - var isTouch = event.type === 'touchstart'; - var source = event.data.source; - var isSlidee = source === 'slidee'; - - // Ignore when already in progress, or interactive element in non-touch navivagion - if (dragging.init || !isTouch && isInteractive(event.target)) { - return; - } - - // Handle dragging conditions - if (source === 'handle' && (!o.dragHandle || hPos.start === hPos.end)) { - return; - } - - // SLIDEE dragging conditions - if (isSlidee && !(isTouch ? o.touchDragging : o.mouseDragging && event.which < 2)) { - return; - } - - if (!isTouch) { - // prevents native image dragging in Firefox - stopDefault(event); - } - - // Reset dragging object - continuousInit(source); - - // Properties used in dragHandler - dragging.init = 0; - dragging.$source = $(event.target); - dragging.touch = isTouch; - dragging.pointer = isTouch ? event.originalEvent.touches[0] : event; - dragging.initX = dragging.pointer.pageX; - dragging.initY = dragging.pointer.pageY; - dragging.initPos = isSlidee ? pos.cur : hPos.cur; - dragging.start = +new Date(); - dragging.time = 0; - dragging.path = 0; - dragging.delta = 0; - dragging.locked = 0; - dragging.history = [0, 0, 0, 0]; - dragging.pathToLock = isSlidee ? isTouch ? 30 : 10 : 0; - - // Bind dragging events - $doc.on(isTouch ? dragTouchEvents : dragMouseEvents, dragHandler); - - // Pause ongoing cycle - self.pause(1); - - // Add dragging class - (isSlidee ? $slidee : $handle).addClass(o.draggedClass); - - // Trigger moveStart event - trigger('moveStart'); - - // Keep track of a dragging path history. This is later used in the - // dragging release swing calculation when dragging SLIDEE. - if (isSlidee) { - historyID = setInterval(draggingHistoryTick, 10); - } - } + function dragInit(event) { + var isTouch = event.type === 'touchstart'; + var source = event.data.source; + var isSlidee = source === 'slidee'; + + // Ignore when already in progress, or interactive element in non-touch navivagion + if (dragging.init || !isTouch && isInteractive(event.target)) { + return; + } + + // Handle dragging conditions + if (source === 'handle' && (!o.dragHandle || hPos.start === hPos.end)) { + return; + } + + // SLIDEE dragging conditions + if (isSlidee && !(isTouch ? o.touchDragging : o.mouseDragging && event.which < 2)) { + return; + } + + if (!isTouch) { + // prevents native image dragging in Firefox + stopDefault(event); + } + + // Reset dragging object + continuousInit(source); + + // Properties used in dragHandler + dragging.init = 0; + dragging.$source = $(event.target); + dragging.touch = isTouch; + dragging.pointer = isTouch ? event.originalEvent.touches[0] : event; + dragging.initX = dragging.pointer.pageX; + dragging.initY = dragging.pointer.pageY; + dragging.initPos = isSlidee ? pos.cur : hPos.cur; + dragging.start = +new Date(); + dragging.time = 0; + dragging.path = 0; + dragging.delta = 0; + dragging.locked = 0; + dragging.history = [0, 0, 0, 0]; + dragging.pathToLock = isSlidee ? isTouch ? 30 : 10 : 0; + + // Bind dragging events + $doc.on(isTouch ? dragTouchEvents : dragMouseEvents, dragHandler); + + // Pause ongoing cycle + self.pause(1); + + // Add dragging class + (isSlidee ? $slidee : $handle).addClass(o.draggedClass); + + // Trigger moveStart event + trigger('moveStart'); + + // Keep track of a dragging path history. This is later used in the + // dragging release swing calculation when dragging SLIDEE. + if (isSlidee) { + historyID = setInterval(draggingHistoryTick, 10); + } + } /** * Handler for dragging scrollbar handle or SLIDEE. @@ -1429,104 +1451,104 @@ * * @return {Void} */ - function dragHandler(event) { - dragging.released = event.type === 'mouseup' || event.type === 'touchend'; - dragging.pointer = dragging.touch ? event.originalEvent[dragging.released ? 'changedTouches' : 'touches'][0] : event; - dragging.pathX = dragging.pointer.pageX - dragging.initX; - dragging.pathY = dragging.pointer.pageY - dragging.initY; - dragging.path = sqrt(pow(dragging.pathX, 2) + pow(dragging.pathY, 2)); - dragging.delta = o.horizontal ? dragging.pathX : dragging.pathY; - - if (!dragging.released && dragging.path < 1) return; - - // We haven't decided whether this is a drag or not... - if (!dragging.init) { - // If the drag path was very short, maybe it's not a drag? - if (dragging.path < o.dragThreshold) { - // If the pointer was released, the path will not become longer and it's - // definitely not a drag. If not released yet, decide on next iteration - return dragging.released ? dragEnd() : undefined; - } - else { - // If dragging path is sufficiently long we can confidently start a drag - // if drag is in different direction than scroll, ignore it - if (o.horizontal ? abs(dragging.pathX) > abs(dragging.pathY) : abs(dragging.pathX) < abs(dragging.pathY)) { - dragging.init = 1; - } else { - return dragEnd(); - } - } - } - - stopDefault(event); - - // Disable click on a source element, as it is unwelcome when dragging - if (!dragging.locked && dragging.path > dragging.pathToLock && dragging.slidee) { - dragging.locked = 1; - dragging.$source.on(clickEvent, disableOneEvent); - } - - // Cancel dragging on release - if (dragging.released) { - dragEnd(); - - // Adjust path with a swing on mouse release - if (o.releaseSwing && dragging.slidee) { - dragging.swing = (dragging.delta - dragging.history[0]) / 40 * 300; - dragging.delta += dragging.swing; - dragging.tweese = abs(dragging.swing) > 10; - } - } - - slideTo(dragging.slidee ? round(dragging.initPos - dragging.delta) : handleToSlidee(dragging.initPos + dragging.delta)); - } + function dragHandler(event) { + dragging.released = event.type === 'mouseup' || event.type === 'touchend'; + dragging.pointer = dragging.touch ? event.originalEvent[dragging.released ? 'changedTouches' : 'touches'][0] : event; + dragging.pathX = dragging.pointer.pageX - dragging.initX; + dragging.pathY = dragging.pointer.pageY - dragging.initY; + dragging.path = sqrt(pow(dragging.pathX, 2) + pow(dragging.pathY, 2)); + dragging.delta = o.horizontal ? dragging.pathX : dragging.pathY; + + if (!dragging.released && dragging.path < 1) return; + + // We haven't decided whether this is a drag or not... + if (!dragging.init) { + // If the drag path was very short, maybe it's not a drag? + if (dragging.path < o.dragThreshold) { + // If the pointer was released, the path will not become longer and it's + // definitely not a drag. If not released yet, decide on next iteration + return dragging.released ? dragEnd() : undefined; + } + else { + // If dragging path is sufficiently long we can confidently start a drag + // if drag is in different direction than scroll, ignore it + if (o.horizontal ? abs(dragging.pathX) > abs(dragging.pathY) : abs(dragging.pathX) < abs(dragging.pathY)) { + dragging.init = 1; + } else { + return dragEnd(); + } + } + } + + stopDefault(event); + + // Disable click on a source element, as it is unwelcome when dragging + if (!dragging.locked && dragging.path > dragging.pathToLock && dragging.slidee) { + dragging.locked = 1; + dragging.$source.on(clickEvent, disableOneEvent); + } + + // Cancel dragging on release + if (dragging.released) { + dragEnd(); + + // Adjust path with a swing on mouse release + if (o.releaseSwing && dragging.slidee) { + dragging.swing = (dragging.delta - dragging.history[0]) / 40 * 300; + dragging.delta += dragging.swing; + dragging.tweese = abs(dragging.swing) > 10; + } + } + + slideTo(dragging.slidee ? round(dragging.initPos - dragging.delta) : handleToSlidee(dragging.initPos + dragging.delta)); + } /** * Stops dragging and cleans up after it. * * @return {Void} */ - function dragEnd() { - clearInterval(historyID); - dragging.released = true; - $doc.off(dragging.touch ? dragTouchEvents : dragMouseEvents, dragHandler); - (dragging.slidee ? $slidee : $handle).removeClass(o.draggedClass); + function dragEnd() { + clearInterval(historyID); + dragging.released = true; + $doc.off(dragging.touch ? dragTouchEvents : dragMouseEvents, dragHandler); + (dragging.slidee ? $slidee : $handle).removeClass(o.draggedClass); - // Make sure that disableOneEvent is not active in next tick. - setTimeout(function () { - dragging.$source.off(clickEvent, disableOneEvent); - }); + // Make sure that disableOneEvent is not active in next tick. + setTimeout(function () { + dragging.$source.off(clickEvent, disableOneEvent); + }); - // Normally, this is triggered in render(), but if there - // is nothing to render, we have to do it manually here. - if (pos.cur === pos.dest && dragging.init) { - trigger('moveEnd'); - } + // Normally, this is triggered in render(), but if there + // is nothing to render, we have to do it manually here. + if (pos.cur === pos.dest && dragging.init) { + trigger('moveEnd'); + } - // Resume ongoing cycle - self.resume(1); + // Resume ongoing cycle + self.resume(1); - dragging.init = 0; - } + dragging.init = 0; + } /** * Check whether element is interactive. * * @return {Boolean} */ - function isInteractive(element) { - return ~$.inArray(element.nodeName, interactiveElements) || $(element).is(o.interactive); - } + function isInteractive(element) { + return ~$.inArray(element.nodeName, interactiveElements) || $(element).is(o.interactive); + } /** * Continuous movement cleanup on mouseup. * * @return {Void} */ - function movementReleaseHandler() { - self.stop(); - $doc.off('mouseup', movementReleaseHandler); - } + function movementReleaseHandler() { + self.stop(); + $doc.off('mouseup', movementReleaseHandler); + } /** * Buttons navigation handler. @@ -1535,33 +1557,33 @@ * * @return {Void} */ - function buttonsHandler(event) { - /*jshint validthis:true */ - stopDefault(event); - switch (this) { - case $forwardButton[0]: - case $backwardButton[0]: - self.moveBy($forwardButton.is(this) ? o.moveBy : -o.moveBy); - $doc.on('mouseup', movementReleaseHandler); - break; - - case $prevButton[0]: - self.prev(); - break; - - case $nextButton[0]: - self.next(); - break; - - case $prevPageButton[0]: - self.prevPage(); - break; - - case $nextPageButton[0]: - self.nextPage(); - break; - } - } + function buttonsHandler(event) { + /*jshint validthis:true */ + stopDefault(event); + switch (this) { + case $forwardButton[0]: + case $backwardButton[0]: + self.moveBy($forwardButton.is(this) ? o.moveBy : -o.moveBy); + $doc.on('mouseup', movementReleaseHandler); + break; + + case $prevButton[0]: + self.prev(); + break; + + case $nextButton[0]: + self.next(); + break; + + case $prevPageButton[0]: + self.prevPage(); + break; + + case $nextPageButton[0]: + self.nextPage(); + break; + } + } /** * Mouse wheel delta normalization. @@ -1570,27 +1592,27 @@ * * @return {Int} */ - function normalizeWheelDelta(event) { - // wheelDelta needed only for IE8- - scrolling.curDelta = ((o.horizontal ? event.deltaY || event.deltaX : event.deltaY) || -event.wheelDelta); - scrolling.curDelta /= event.deltaMode === 1 ? 3 : 100; - if (!itemNav) { - return scrolling.curDelta; - } - time = +new Date(); - if (scrolling.last < time - scrolling.resetTime) { - scrolling.delta = 0; - } - scrolling.last = time; - scrolling.delta += scrolling.curDelta; - if (abs(scrolling.delta) < 1) { - scrolling.finalDelta = 0; - } else { - scrolling.finalDelta = round(scrolling.delta / 1); - scrolling.delta %= 1; - } - return scrolling.finalDelta; - } + function normalizeWheelDelta(event) { + // wheelDelta needed only for IE8- + scrolling.curDelta = ((o.horizontal ? event.deltaY || event.deltaX : event.deltaY) || -event.wheelDelta); + scrolling.curDelta /= event.deltaMode === 1 ? 3 : 100; + if (!itemNav) { + return scrolling.curDelta; + } + time = +new Date(); + if (scrolling.last < time - scrolling.resetTime) { + scrolling.delta = 0; + } + scrolling.last = time; + scrolling.delta += scrolling.curDelta; + if (abs(scrolling.delta) < 1) { + scrolling.finalDelta = 0; + } else { + scrolling.finalDelta = round(scrolling.delta / 1); + scrolling.delta %= 1; + } + return scrolling.finalDelta; + } /** * Mouse scrolling handler. @@ -1599,26 +1621,26 @@ * * @return {Void} */ - function scrollHandler(event) { - // Mark event as originating in a Sly instance - event.originalEvent[namespace] = self; - // Don't hijack global scrolling - var time = +new Date(); - if (lastGlobalWheel + o.scrollHijack > time && $scrollSource[0] !== document && $scrollSource[0] !== window) { - lastGlobalWheel = time; - return; - } - // Ignore if there is no scrolling to be done - if (!o.scrollBy || pos.start === pos.end) { - return; - } - var delta = normalizeWheelDelta(event.originalEvent); - // Trap scrolling only when necessary and/or requested - if (o.scrollTrap || delta > 0 && pos.dest < pos.end || delta < 0 && pos.dest > pos.start) { - stopDefault(event, 1); - } - self.slideBy(o.scrollBy * delta); - } + function scrollHandler(event) { + // Mark event as originating in a Sly instance + event.originalEvent[namespace] = self; + // Don't hijack global scrolling + var time = +new Date(); + if (lastGlobalWheel + o.scrollHijack > time && $scrollSource[0] !== document && $scrollSource[0] !== window) { + lastGlobalWheel = time; + return; + } + // Ignore if there is no scrolling to be done + if (!o.scrollBy || pos.start === pos.end) { + return; + } + var delta = normalizeWheelDelta(event.originalEvent); + // Trap scrolling only when necessary and/or requested + if (o.scrollTrap || delta > 0 && pos.dest < pos.end || delta < 0 && pos.dest > pos.start) { + stopDefault(event, 1); + } + self.slideBy(o.scrollBy * delta); + } /** * Scrollbar click handler. @@ -1627,14 +1649,14 @@ * * @return {Void} */ - function scrollbarHandler(event) { - // Only clicks on scroll bar. Ignore the handle. - if (o.clickBar && event.target === $sb[0]) { - stopDefault(event); - // Calculate new handle position and sync SLIDEE to it - slideTo(handleToSlidee((o.horizontal ? event.pageX - $sb.offset().left : event.pageY - $sb.offset().top) - handleSize / 2)); - } - } + function scrollbarHandler(event) { + // Only clicks on scroll bar. Ignore the handle. + if (o.clickBar && event.target === $sb[0]) { + stopDefault(event); + // Calculate new handle position and sync SLIDEE to it + slideTo(handleToSlidee((o.horizontal ? event.pageX - $sb.offset().left : event.pageY - $sb.offset().top) - handleSize / 2)); + } + } /** * Keyboard input handler. @@ -1643,25 +1665,25 @@ * * @return {Void} */ - function keyboardHandler(event) { - if (!o.keyboardNavBy) { - return; - } - - switch (event.which) { - // Left or Up - case o.horizontal ? 37 : 38: - stopDefault(event); - self[o.keyboardNavBy === 'pages' ? 'prevPage' : 'prev'](); - break; - - // Right or Down - case o.horizontal ? 39 : 40: - stopDefault(event); - self[o.keyboardNavBy === 'pages' ? 'nextPage' : 'next'](); - break; - } - } + function keyboardHandler(event) { + if (!o.keyboardNavBy) { + return; + } + + switch (event.which) { + // Left or Up + case o.horizontal ? 37 : 38: + stopDefault(event); + self[o.keyboardNavBy === 'pages' ? 'prevPage' : 'prev'](); + break; + + // Right or Down + case o.horizontal ? 39 : 40: + stopDefault(event); + self[o.keyboardNavBy === 'pages' ? 'nextPage' : 'next'](); + break; + } + } /** * Click on item activation handler. @@ -1670,22 +1692,22 @@ * * @return {Void} */ - function activateHandler(event) { - /*jshint validthis:true */ + function activateHandler(event) { + /*jshint validthis:true */ - // Ignore clicks on interactive elements. - if (isInteractive(this)) { - event.originalEvent[namespace + 'ignore'] = true; - return; - } + // Ignore clicks on interactive elements. + if (isInteractive(this)) { + event.originalEvent[namespace + 'ignore'] = true; + return; + } - // Ignore events that: - // - are not originating from direct SLIDEE children - // - originated from interactive elements - if (this.parentNode !== $slidee[0] || event.originalEvent[namespace + 'ignore']) return; + // Ignore events that: + // - are not originating from direct SLIDEE children + // - originated from interactive elements + if (this.parentNode !== $slidee[0] || event.originalEvent[namespace + 'ignore']) return; - self.activate(this); - } + self.activate(this); + } /** * Click on page button handler. @@ -1694,13 +1716,13 @@ * * @return {Void} */ - function activatePageHandler() { - /*jshint validthis:true */ - // Accept only events from direct pages bar children. - if (this.parentNode === $pb[0]) { - self.activatePage($pages.index(this)); - } - } + function activatePageHandler() { + /*jshint validthis:true */ + // Accept only events from direct pages bar children. + if (this.parentNode === $pb[0]) { + self.activatePage($pages.index(this)); + } + } /** * Pause on hover handler. @@ -1709,11 +1731,11 @@ * * @return {Void} */ - function pauseOnHoverHandler(event) { - if (o.pauseOnHover) { - self[event.type === 'mouseenter' ? 'pause' : 'resume'](2); - } - } + function pauseOnHoverHandler(event) { + if (o.pauseOnHover) { + self[event.type === 'mouseenter' ? 'pause' : 'resume'](2); + } + } /** * Trigger callbacks for event. @@ -1723,212 +1745,212 @@ * * @return {Void} */ - function trigger(name, arg1) { - if (callbacks[name]) { - l = callbacks[name].length; - // Callbacks will be stored and executed from a temporary array to not - // break the execution queue when one of the callbacks unbinds itself. - tmpArray.length = 0; - for (i = 0; i < l; i++) { - tmpArray.push(callbacks[name][i]); - } - // Execute the callbacks - for (i = 0; i < l; i++) { - tmpArray[i].call(self, name, arg1); - } - } - } + function trigger(name, arg1) { + if (callbacks[name]) { + l = callbacks[name].length; + // Callbacks will be stored and executed from a temporary array to not + // break the execution queue when one of the callbacks unbinds itself. + tmpArray.length = 0; + for (i = 0; i < l; i++) { + tmpArray.push(callbacks[name][i]); + } + // Execute the callbacks + for (i = 0; i < l; i++) { + tmpArray[i].call(self, name, arg1); + } + } + } /** * Destroys instance and everything it created. * * @return {Void} */ - self.destroy = function () { - // Remove the reference to itself - Sly.removeInstance(frame); - - // Unbind all events - $scrollSource - .add($handle) - .add($sb) - .add($pb) - .add($forwardButton) - .add($backwardButton) - .add($prevButton) - .add($nextButton) - .add($prevPageButton) - .add($nextPageButton) - .off('.' + namespace); - - // Unbinding specifically as to not nuke out other instances - $doc.off('keydown', keyboardHandler); - - // Remove classes - $prevButton - .add($nextButton) - .add($prevPageButton) - .add($nextPageButton) - .removeClass(o.disabledClass); - - if ($items && rel.activeItem != null) { - $items.eq(rel.activeItem).removeClass(o.activeClass); - } - - // Remove page items - $pb.empty(); - - if (!parallax) { - // Unbind events from frame - $frame.off('.' + namespace); - // Restore original styles - frameStyles.restore(); - slideeStyles.restore(); - sbStyles.restore(); - handleStyles.restore(); - // Remove the instance from element data storage - $.removeData(frame, namespace); - } - - // Clean up collections - items.length = pages.length = 0; - last = {}; - - // Reset initialized status and return the instance - self.initialized = 0; - return self; - }; + self.destroy = function () { + // Remove the reference to itself + Sly.removeInstance(frame); + + // Unbind all events + $scrollSource + .add($handle) + .add($sb) + .add($pb) + .add($forwardButton) + .add($backwardButton) + .add($prevButton) + .add($nextButton) + .add($prevPageButton) + .add($nextPageButton) + .off('.' + namespace); + + // Unbinding specifically as to not nuke out other instances + $doc.off('keydown', keyboardHandler); + + // Remove classes + $prevButton + .add($nextButton) + .add($prevPageButton) + .add($nextPageButton) + .removeClass(o.disabledClass); + + if ($items && rel.activeItem != null) { + $items.eq(rel.activeItem).removeClass(o.activeClass); + } + + // Remove page items + $pb.empty(); + + if (!parallax) { + // Unbind events from frame + $frame.off('.' + namespace); + // Restore original styles + frameStyles.restore(); + slideeStyles.restore(); + sbStyles.restore(); + handleStyles.restore(); + // Remove the instance from element data storage + $.removeData(frame, namespace); + } + + // Clean up collections + items.length = pages.length = 0; + last = {}; + + // Reset initialized status and return the instance + self.initialized = 0; + return self; + }; /** * Initialize. * * @return {Object} */ - self.init = function () { - if (self.initialized) { - return; - } - - // Disallow multiple instances on the same element - if (Sly.getInstance(frame)) throw new Error('There is already a Sly instance on this element'); - - // Store the reference to itself - Sly.storeInstance(frame, self); - - // Register callbacks map - self.on(callbackMap); - - // Save styles - var holderProps = ['overflow', 'position']; - var movableProps = ['position', 'webkitTransform', 'msTransform', 'transform', 'left', 'top', 'width', 'height']; - frameStyles.save.apply(frameStyles, holderProps); - sbStyles.save.apply(sbStyles, holderProps); - slideeStyles.save.apply(slideeStyles, movableProps); - handleStyles.save.apply(handleStyles, movableProps); - - // Set required styles - var $movables = $handle; - if (!parallax) { - $movables = $movables.add($slidee); - $frame.css('overflow', 'hidden'); - if (!transform && $frame.css('position') === 'static') { - $frame.css('position', 'relative'); - } - } - if (transform) { - if (gpuAcceleration) { - $movables.css(transform, gpuAcceleration); - } - } else { - if ($sb.css('position') === 'static') { - $sb.css('position', 'relative'); - } - $movables.css({ position: 'absolute' }); - } - - // Navigation buttons - if (o.forward) { - $forwardButton.on(mouseDownEvent, buttonsHandler); - } - if (o.backward) { - $backwardButton.on(mouseDownEvent, buttonsHandler); - } - if (o.prev) { - $prevButton.on(clickEvent, buttonsHandler); - } - if (o.next) { - $nextButton.on(clickEvent, buttonsHandler); - } - if (o.prevPage) { - $prevPageButton.on(clickEvent, buttonsHandler); - } - if (o.nextPage) { - $nextPageButton.on(clickEvent, buttonsHandler); - } - - // Scrolling navigation - $scrollSource.on(wheelEvent, scrollHandler); - - // Clicking on scrollbar navigation - if ($sb[0]) { - $sb.on(clickEvent, scrollbarHandler); - } - - // Click on items navigation - if (itemNav && o.activateOn) { - $frame.on(o.activateOn + '.' + namespace, '*', activateHandler); - } - - // Pages navigation - if ($pb[0] && o.activatePageOn) { - $pb.on(o.activatePageOn + '.' + namespace, '*', activatePageHandler); - } - - // Dragging navigation - $dragSource.on(dragInitEvents, { source: 'slidee' }, dragInit); - - // Scrollbar dragging navigation - if ($handle) { - $handle.on(dragInitEvents, { source: 'handle' }, dragInit); - } - - // Keyboard navigation - $doc.on('keydown', keyboardHandler); - - if (!parallax) { - // Pause on hover - $frame.on('mouseenter.' + namespace + ' mouseleave.' + namespace, pauseOnHoverHandler); - // Reset native FRAME element scroll - $frame.on('scroll.' + namespace, resetScroll); - } - - // Mark instance as initialized - self.initialized = 1; - - // Load - load(true); - - // Initiate automatic cycling - if (o.cycleBy && !parallax) { - self[o.startPaused ? 'pause' : 'resume'](); - } - - // Return instance - return self; - }; - } - - Sly.getInstance = function (element) { - return $.data(element, namespace); - }; - - Sly.storeInstance = function (element, sly) { - return $.data(element, namespace, sly); - }; - - Sly.removeInstance = function (element) { - return $.removeData(element, namespace); - }; + self.init = function () { + if (self.initialized) { + return; + } + + // Disallow multiple instances on the same element + if (Sly.getInstance(frame)) throw new Error('There is already a Sly instance on this element'); + + // Store the reference to itself + Sly.storeInstance(frame, self); + + // Register callbacks map + self.on(callbackMap); + + // Save styles + var holderProps = ['overflow', 'position']; + var movableProps = ['position', 'webkitTransform', 'msTransform', 'transform', 'left', 'top', 'width', 'height']; + frameStyles.save.apply(frameStyles, holderProps); + sbStyles.save.apply(sbStyles, holderProps); + slideeStyles.save.apply(slideeStyles, movableProps); + handleStyles.save.apply(handleStyles, movableProps); + + // Set required styles + var $movables = $handle; + if (!parallax) { + $movables = $movables.add($slidee); + $frame.css('overflow', 'hidden'); + if (!transform && $frame.css('position') === 'static') { + $frame.css('position', 'relative'); + } + } + if (transform) { + if (gpuAcceleration) { + $movables.css(transform, gpuAcceleration); + } + } else { + if ($sb.css('position') === 'static') { + $sb.css('position', 'relative'); + } + $movables.css({ position: 'absolute' }); + } + + // Navigation buttons + if (o.forward) { + $forwardButton.on(mouseDownEvent, buttonsHandler); + } + if (o.backward) { + $backwardButton.on(mouseDownEvent, buttonsHandler); + } + if (o.prev) { + $prevButton.on(clickEvent, buttonsHandler); + } + if (o.next) { + $nextButton.on(clickEvent, buttonsHandler); + } + if (o.prevPage) { + $prevPageButton.on(clickEvent, buttonsHandler); + } + if (o.nextPage) { + $nextPageButton.on(clickEvent, buttonsHandler); + } + + // Scrolling navigation + $scrollSource.on(wheelEvent, scrollHandler); + + // Clicking on scrollbar navigation + if ($sb[0]) { + $sb.on(clickEvent, scrollbarHandler); + } + + // Click on items navigation + if (itemNav && o.activateOn) { + $frame.on(o.activateOn + '.' + namespace, '*', activateHandler); + } + + // Pages navigation + if ($pb[0] && o.activatePageOn) { + $pb.on(o.activatePageOn + '.' + namespace, '*', activatePageHandler); + } + + // Dragging navigation + $dragSource.on(dragInitEvents, { source: 'slidee' }, dragInit); + + // Scrollbar dragging navigation + if ($handle) { + $handle.on(dragInitEvents, { source: 'handle' }, dragInit); + } + + // Keyboard navigation + $doc.on('keydown', keyboardHandler); + + if (!parallax) { + // Pause on hover + $frame.on('mouseenter.' + namespace + ' mouseleave.' + namespace, pauseOnHoverHandler); + // Reset native FRAME element scroll + $frame.on('scroll.' + namespace, resetScroll); + } + + // Mark instance as initialized + self.initialized = 1; + + // Load + load(true); + + // Initiate automatic cycling + if (o.cycleBy && !parallax) { + self[o.startPaused ? 'pause' : 'resume'](); + } + + // Return instance + return self; + }; + } + + Sly.getInstance = function (element) { + return $.data(element, namespace); + }; + + Sly.storeInstance = function (element, sly) { + return $.data(element, namespace, sly); + }; + + Sly.removeInstance = function (element) { + return $.removeData(element, namespace); + }; /** * Return type of the value. @@ -1937,17 +1959,17 @@ * * @return {String} */ - function type(value) { - if (value == null) { - return String(value); - } + function type(value) { + if (value == null) { + return String(value); + } - if (typeof value === 'object' || typeof value === 'function') { - return Object.prototype.toString.call(value).match(/\s([a-z]+)/i)[1].toLowerCase() || 'object'; - } + if (typeof value === 'object' || typeof value === 'function') { + return Object.prototype.toString.call(value).match(/\s([a-z]+)/i)[1].toLowerCase() || 'object'; + } - return typeof value; - } + return typeof value; + } /** * Event preventDefault & stopPropagation helper. @@ -1957,12 +1979,12 @@ * * @return {Void} */ - function stopDefault(event, noBubbles) { - event.preventDefault(); - if (noBubbles) { - event.stopPropagation(); - } - } + function stopDefault(event, noBubbles) { + event.preventDefault(); + if (noBubbles) { + event.stopPropagation(); + } + } /** * Disables an event it was triggered on and unbinds itself. @@ -1971,22 +1993,22 @@ * * @return {Void} */ - function disableOneEvent(event) { - /*jshint validthis:true */ - stopDefault(event, 1); - $(this).off(event.type, disableOneEvent); - } + function disableOneEvent(event) { + /*jshint validthis:true */ + stopDefault(event, 1); + $(this).off(event.type, disableOneEvent); + } /** * Resets native element scroll values to 0. * * @return {Void} */ - function resetScroll() { - /*jshint validthis:true */ - this.scrollLeft = 0; - this.scrollTop = 0; - } + function resetScroll() { + /*jshint validthis:true */ + this.scrollLeft = 0; + this.scrollTop = 0; + } /** * Check if variable is a number. @@ -1995,9 +2017,9 @@ * * @return {Boolean} */ - function isNumber(value) { - return !isNaN(parseFloat(value)) && isFinite(value); - } + function isNumber(value) { + return !isNaN(parseFloat(value)) && isFinite(value); + } /** * Parse style to pixels. @@ -2007,9 +2029,9 @@ * * @return {Int} */ - function getPx($item, property) { - return 0 | round(String($item.css(property)).replace(/[^\-0-9.]/g, '')); - } + function getPx($item, property) { + return 0 | round(String($item.css(property)).replace(/[^\-0-9.]/g, '')); + } /** * Make sure that number is within the limits. @@ -2020,9 +2042,9 @@ * * @return {Number} */ - function within(number, min, max) { - return number < min ? min : number > max ? max : number; - } + function within(number, min, max) { + return number < min ? min : number > max ? max : number; + } /** * Saves element styles for later restoration. @@ -2035,176 +2057,176 @@ * * @param {Element} element */ - function StyleRestorer(element) { - var self = {}; - self.style = {}; - self.save = function () { - if (!element || !element.nodeType) return; - for (var i = 0; i < arguments.length; i++) { - self.style[arguments[i]] = element.style[arguments[i]]; - } - return self; - }; - self.restore = function () { - if (!element || !element.nodeType) return; - for (var prop in self.style) { - if (self.style.hasOwnProperty(prop)) element.style[prop] = self.style[prop]; - } - return self; - }; - return self; - } - - // Local WindowAnimationTiming interface polyfill - (function (w) { - rAF = w.requestAnimationFrame - || w.webkitRequestAnimationFrame - || fallback; + function StyleRestorer(element) { + var self = {}; + self.style = {}; + self.save = function () { + if (!element || !element.nodeType) return; + for (var i = 0; i < arguments.length; i++) { + self.style[arguments[i]] = element.style[arguments[i]]; + } + return self; + }; + self.restore = function () { + if (!element || !element.nodeType) return; + for (var prop in self.style) { + if (self.style.hasOwnProperty(prop)) element.style[prop] = self.style[prop]; + } + return self; + }; + return self; + } + + // Local WindowAnimationTiming interface polyfill + (function (w) { + rAF = w.requestAnimationFrame + || w.webkitRequestAnimationFrame + || fallback; /** * Fallback implementation. */ - var prev = new Date().getTime(); - function fallback(fn) { - var curr = new Date().getTime(); - var ms = Math.max(0, 16 - (curr - prev)); - var req = setTimeout(fn, ms); - prev = curr; - return req; - } + var prev = new Date().getTime(); + function fallback(fn) { + var curr = new Date().getTime(); + var ms = Math.max(0, 16 - (curr - prev)); + var req = setTimeout(fn, ms); + prev = curr; + return req; + } /** * Cancel. */ - var cancel = w.cancelAnimationFrame - || w.webkitCancelAnimationFrame - || w.clearTimeout; - - cAF = function(id){ - cancel.call(w, id); - }; - }(window)); - - // Feature detects - (function () { - var prefixes = ['', 'Webkit', 'Moz', 'ms', 'O']; - var el = document.createElement('div'); - - function testProp(prop) { - for (var p = 0, pl = prefixes.length; p < pl; p++) { - var prefixedProp = prefixes[p] ? prefixes[p] + prop.charAt(0).toUpperCase() + prop.slice(1) : prop; - if (el.style[prefixedProp] != null) { - return prefixedProp; - } - } - } - - // Global support indicators - transform = testProp('transform'); - gpuAcceleration = testProp('perspective') ? 'translateZ(0) ' : ''; - }()); - - // Expose class globally - w[className] = Sly; - - // jQuery proxy - $.fn[pluginName] = function (options, callbackMap) { - var method, methodArgs; - - // Attributes logic - if (!$.isPlainObject(options)) { - if (type(options) === 'string' || options === false) { - method = options === false ? 'destroy' : options; - methodArgs = Array.prototype.slice.call(arguments, 1); - } - options = {}; - } - - // Apply to all elements - return this.each(function (i, element) { - // Call with prevention against multiple instantiations - var plugin = Sly.getInstance(element); - - if (!plugin && !method) { - // Create a new object if it doesn't exist yet - plugin = new Sly(element, options, callbackMap).init(); - } else if (plugin && method) { - // Call method - if (plugin[method]) { - plugin[method].apply(plugin, methodArgs); - } - } - }); - }; - - // Default options - Sly.defaults = { - slidee: null, // Selector, DOM element, or jQuery object with DOM element representing SLIDEE. - horizontal: false, // Switch to horizontal mode. - - // Item based navigation - itemNav: null, // Item navigation type. Can be: 'basic', 'centered', 'forceCentered'. - itemSelector: null, // Select only items that match this selector. - smart: false, // Repositions the activated item to help with further navigation. - activateOn: null, // Activate an item on this event. Can be: 'click', 'mouseenter', ... - activateMiddle: false, // Always activate the item in the middle of the FRAME. forceCentered only. - - // Scrolling - scrollSource: null, // Element for catching the mouse wheel scrolling. Default is FRAME. - scrollBy: 0, // Pixels or items to move per one mouse scroll. 0 to disable scrolling. - scrollHijack: 300, // Milliseconds since last wheel event after which it is acceptable to hijack global scroll. - scrollTrap: false, // Don't bubble scrolling when hitting scrolling limits. - - // Dragging - dragSource: null, // Selector or DOM element for catching dragging events. Default is FRAME. - mouseDragging: false, // Enable navigation by dragging the SLIDEE with mouse cursor. - touchDragging: false, // Enable navigation by dragging the SLIDEE with touch events. - releaseSwing: false, // Ease out on dragging swing release. - swingSpeed: 0.2, // Swing synchronization speed, where: 1 = instant, 0 = infinite. - elasticBounds: false, // Stretch SLIDEE position limits when dragging past FRAME boundaries. - dragThreshold: 3, // Distance in pixels before Sly recognizes dragging. - interactive: null, // Selector for special interactive elements. - - // Scrollbar - scrollBar: null, // Selector or DOM element for scrollbar container. - dragHandle: false, // Whether the scrollbar handle should be draggable. - dynamicHandle: false, // Scrollbar handle represents the ratio between hidden and visible content. - minHandleSize: 50, // Minimal height or width (depends on sly direction) of a handle in pixels. - clickBar: false, // Enable navigation by clicking on scrollbar. - syncSpeed: 0.5, // Handle => SLIDEE synchronization speed, where: 1 = instant, 0 = infinite. - - // Pagesbar - pagesBar: null, // Selector or DOM element for pages bar container. - activatePageOn: null, // Event used to activate page. Can be: click, mouseenter, ... - pageBuilder: // Page item generator. - function (index) { - return '
  • ' + (index + 1) + '
  • '; - }, - - // Navigation buttons - forward: null, // Selector or DOM element for "forward movement" button. - backward: null, // Selector or DOM element for "backward movement" button. - prev: null, // Selector or DOM element for "previous item" button. - next: null, // Selector or DOM element for "next item" button. - prevPage: null, // Selector or DOM element for "previous page" button. - nextPage: null, // Selector or DOM element for "next page" button. - - // Automated cycling - cycleBy: null, // Enable automatic cycling by 'items' or 'pages'. - cycleInterval: 5000, // Delay between cycles in milliseconds. - pauseOnHover: false, // Pause cycling when mouse hovers over the FRAME. - startPaused: false, // Whether to start in paused sate. - - // Mixed options - moveBy: 300, // Speed in pixels per second used by forward and backward buttons. - speed: 0, // Animations speed in milliseconds. 0 to disable animations. - easing: 'swing', // Easing for duration based (tweening) animations. - startAt: null, // Starting offset in pixels or items. - keyboardNavBy: null, // Enable keyboard navigation by 'items' or 'pages'. - - // Classes - draggedClass: 'dragged', // Class for dragged elements (like SLIDEE or scrollbar handle). - activeClass: 'active', // Class for active items and pages. - disabledClass: 'disabled' // Class for disabled navigation elements. - }; -}(jQuery, window)); + var cancel = w.cancelAnimationFrame + || w.webkitCancelAnimationFrame + || w.clearTimeout; + + cAF = function (id) { + cancel.call(w, id); + }; + }(window)); + + // Feature detects + (function () { + var prefixes = ['', 'Webkit', 'Moz', 'ms', 'O']; + var el = document.createElement('div'); + + function testProp(prop) { + for (var p = 0, pl = prefixes.length; p < pl; p++) { + var prefixedProp = prefixes[p] ? prefixes[p] + prop.charAt(0).toUpperCase() + prop.slice(1) : prop; + if (el.style[prefixedProp] != null) { + return prefixedProp; + } + } + } + + // Global support indicators + transform = testProp('transform'); + gpuAcceleration = testProp('perspective') ? 'translateZ(0) ' : ''; + }()); + + // Expose class globally + w[className] = Sly; + + // jQuery proxy + $.fn[pluginName] = function (options, callbackMap) { + var method, methodArgs; + + // Attributes logic + if (!$.isPlainObject(options)) { + if (type(options) === 'string' || options === false) { + method = options === false ? 'destroy' : options; + methodArgs = Array.prototype.slice.call(arguments, 1); + } + options = {}; + } + + // Apply to all elements + return this.each(function (i, element) { + // Call with prevention against multiple instantiations + var plugin = Sly.getInstance(element); + + if (!plugin && !method) { + // Create a new object if it doesn't exist yet + plugin = new Sly(element, options, callbackMap).init(); + } else if (plugin && method) { + // Call method + if (plugin[method]) { + plugin[method].apply(plugin, methodArgs); + } + } + }); + }; + + // Default options + Sly.defaults = { + slidee: null, // Selector, DOM element, or jQuery object with DOM element representing SLIDEE. + horizontal: false, // Switch to horizontal mode. + + // Item based navigation + itemNav: null, // Item navigation type. Can be: 'basic', 'centered', 'forceCentered'. + itemSelector: null, // Select only items that match this selector. + smart: false, // Repositions the activated item to help with further navigation. + activateOn: null, // Activate an item on this event. Can be: 'click', 'mouseenter', ... + activateMiddle: false, // Always activate the item in the middle of the FRAME. forceCentered only. + + // Scrolling + scrollSource: null, // Element for catching the mouse wheel scrolling. Default is FRAME. + scrollBy: 0, // Pixels or items to move per one mouse scroll. 0 to disable scrolling. + scrollHijack: 300, // Milliseconds since last wheel event after which it is acceptable to hijack global scroll. + scrollTrap: false, // Don't bubble scrolling when hitting scrolling limits. + + // Dragging + dragSource: null, // Selector or DOM element for catching dragging events. Default is FRAME. + mouseDragging: false, // Enable navigation by dragging the SLIDEE with mouse cursor. + touchDragging: false, // Enable navigation by dragging the SLIDEE with touch events. + releaseSwing: false, // Ease out on dragging swing release. + swingSpeed: 0.2, // Swing synchronization speed, where: 1 = instant, 0 = infinite. + elasticBounds: false, // Stretch SLIDEE position limits when dragging past FRAME boundaries. + dragThreshold: 3, // Distance in pixels before Sly recognizes dragging. + interactive: null, // Selector for special interactive elements. + + // Scrollbar + scrollBar: null, // Selector or DOM element for scrollbar container. + dragHandle: false, // Whether the scrollbar handle should be draggable. + dynamicHandle: false, // Scrollbar handle represents the ratio between hidden and visible content. + minHandleSize: 50, // Minimal height or width (depends on sly direction) of a handle in pixels. + clickBar: false, // Enable navigation by clicking on scrollbar. + syncSpeed: 0.5, // Handle => SLIDEE synchronization speed, where: 1 = instant, 0 = infinite. + + // Pagesbar + pagesBar: null, // Selector or DOM element for pages bar container. + activatePageOn: null, // Event used to activate page. Can be: click, mouseenter, ... + pageBuilder: // Page item generator. + function (index) { + return '
  • ' + (index + 1) + '
  • '; + }, + + // Navigation buttons + forward: null, // Selector or DOM element for "forward movement" button. + backward: null, // Selector or DOM element for "backward movement" button. + prev: null, // Selector or DOM element for "previous item" button. + next: null, // Selector or DOM element for "next item" button. + prevPage: null, // Selector or DOM element for "previous page" button. + nextPage: null, // Selector or DOM element for "next page" button. + + // Automated cycling + cycleBy: null, // Enable automatic cycling by 'items' or 'pages'. + cycleInterval: 5000, // Delay between cycles in milliseconds. + pauseOnHover: false, // Pause cycling when mouse hovers over the FRAME. + startPaused: false, // Whether to start in paused sate. + + // Mixed options + moveBy: 300, // Speed in pixels per second used by forward and backward buttons. + speed: 0, // Animations speed in milliseconds. 0 to disable animations. + easing: 'swing', // Easing for duration based (tweening) animations. + startAt: null, // Starting offset in pixels or items. + keyboardNavBy: null, // Enable keyboard navigation by 'items' or 'pages'. + + // Classes + draggedClass: 'dragged', // Class for dragged elements (like SLIDEE or scrollbar handle). + activeClass: 'active', // Class for active items and pages. + disabledClass: 'disabled' // Class for disabled navigation elements. + }; +}(jQuery, window)); \ No newline at end of file