; (function (root, factory){ "use strict"; if (typeof exports === 'object') { module.exports = factory(require('jquery'), require('jquery.documentsize')); } else if (typeof define === 'function' && define.amd) { define(['jquery', 'jquery.documentsize'] , factory); } } (this, function (jQuery){ "use strict"; ; (function ($){ "use strict"; var mgr = { } , norm = { } , queue = { } , lib = { } , core = { } ; (function (mgr, norm){ "use strict"; $.fn.scrollable = function (){ return getScrollable(this); } ; $.fn.scrollRange = function (axis){ return getScrollRange(this, axis); } ; $.fn.scrollTo = function (position, options){ scrollTo(this, position, options); return this; } ; $.fn.stopScroll = function (options){ stopScroll(this, options); return this; } ; $.fn.notifyScrollCallbacks = function (message, callbackNames, queueName){ notifyScrollCallbacks(this, message, callbackNames, queueName); return this; } ; $.scrollable = { lockSpeedBelow: 400, defaultDuration: $.fx.speeds._default, userScrollThreshold: 10, _scrollDetectionThreshold: 5, _enableUserScrollDetection: !isIOS(), _enableClickAndTouchDetection: true , _useScrollHistoryForDetection: isIOS()} ; function getScrollable($container){ $container = norm.normalizeContainer($container); return mgr.getScrollable($container); } function getScrollRange($container, axis){ $container = norm.normalizeContainer($container); axis = axis? norm.normalizeAxisName(axis): norm.BOTH_AXES; return mgr.getScrollRange($container, axis); } function scrollTo($container, position, options){ options = norm.normalizeOptions(options, position); $container = norm.normalizeContainer($container); position = norm.normalizePosition(position, $container, mgr.getScrollable($container), options); mgr.scrollTo($container, position, options); } function stopScroll($container, options){ $container = norm.normalizeContainer($container); options = norm.normalizeOptions(options); mgr.stopScroll($container, options); } function notifyScrollCallbacks($container, message, callbackNames, queueName){ $container = norm.normalizeContainer($container); if (callbackNames !== undefined && !$.isArray(callbackNames)) callbackNames = [callbackNames] ; mgr.notifyScrollCallbacks($container, message, callbackNames, queueName); } function isIOS(){ return (/iPad|iPhone|iPod/gi).test(navigator.userAgent); } } )(mgr, norm); (function (mgr, lib, core){ "use strict"; mgr.getScrollable = function ($container){ return core.getScrollable($container); } ; mgr.getScrollRange = function ($container, axis){ return lib.getScrollMaximum($container, axis); } ; mgr.scrollTo = function ($container, position, options){ var stopOptions, notifyCancelled = extractNotifyCancelled(options) || { } ; if ($.isWindow($container[0])) options = lib.bindAnimationCallbacks(options, $container[0]); if (!lib.isRedundantTarget(position, lib.getScrollStartPosition($container, options))) { if (options.append) { options._history = lib.getLastStepHistory($container, options); } else { $.extend(notifyCancelled, { cancelled: options.merge? "merge": "replace"} ); stopOptions = $.extend({ notifyCancelled: notifyCancelled} , options); options._history = mgr.stopScroll($container, stopOptions); } core.animateScroll($container, position, options); } } ; mgr.stopScroll = function ($container, options){ var notifyCancelled = extractNotifyCancelled(options), $scrollable = mgr.getScrollable($container); return lib.stopScrollAnimation($scrollable, options, notifyCancelled); } ; mgr.notifyScrollCallbacks = function ($container, message, callbackNames, queueName){ var $scrollable = mgr.getScrollable($container); lib.notifyScrollCallbacks($scrollable, message, callbackNames, queueName); } ; function extractNotifyCancelled(options){ var notifyCancelled; if (options && options.notifyCancelled) { if (!$.isPlainObject(options.notifyCancelled)) throw new Error('Invalid notifyCancelled option. Expected a hash but got type ' + $.type(options.notifyCancelled)) notifyCancelled = $.extend({ } , options.notifyCancelled); delete options.notifyCancelled; } return notifyCancelled; } } )(mgr, lib, core); (function (norm, lib, queue){ "use strict"; var altAxisNamesV = ["v", "y", "top"] , altAxisNamesH = ["h", "x", "left"] , altAxisNamesBoth = ["vh", "hv", "xy", "yx", "all"] , altAxisNames = altAxisNamesV.concat(altAxisNamesH, altAxisNamesBoth); norm.VERTICAL = "vertical"; norm.HORIZONTAL = "horizontal"; norm.BOTH_AXES = "both"; norm.IGNORE_AXIS = -999; norm.defaults = { _jqScrollable: true , axis: norm.VERTICAL, queue: "internal.jquery.scrollable", ignoreUser: false } ; norm.IGNORE_USER_SCROLL_ONLY = "scroll"; norm.IGNORE_USER_CLICK_TOUCH_ONLY = "click"; norm.MODE_REPLACE = "replace"; norm.MODE_APPEND = "append"; norm.MODE_MERGE = "merge"; norm.normalizeContainer = function ($container){ var container = $container[0], tagName = container && container.tagName && container.tagName.toLowerCase(), isIframe = tagName === "iframe", isWindow = !tagName || tagName === "html" || tagName === "body"; return isWindow? $(lib.ownerWindow(container)): isIframe? $(container.contentWindow): $container; } ; norm.normalizePosition = function (position, $container, $scrollable, options){ var queueWrapper = new queue.QueueWrapper($scrollable, options.queue); if ($.isPlainObject(position)) { return normalizePositionForHash(position, $container, options, queueWrapper); } else { return normalizePositionForAxis(position, $container, options, queueWrapper); } } ; function normalizePositionForHash(position, $container, options, queueWrapper){ var axis = options.axis, pos = normalizeAxisProperty(position), posX = pos[norm.HORIZONTAL], posY = pos[norm.VERTICAL], ignoreX = axis === norm.VERTICAL, ignoreY = axis === norm.HORIZONTAL, optionsX = $.extend({ } , options, { axis: norm.HORIZONTAL} ), optionsY = $.extend({ } , options, { axis: norm.VERTICAL} ), normalized = { } ; normalized[norm.HORIZONTAL] = ignoreX? (options.merge? lib.getLastTarget_QW(queueWrapper, norm.HORIZONTAL): norm.IGNORE_AXIS): normalizePositionForAxis(posX, $container, optionsX, queueWrapper)[norm.HORIZONTAL]; normalized[norm.VERTICAL] = ignoreY? (options.merge? lib.getLastTarget_QW(queueWrapper, norm.VERTICAL): norm.IGNORE_AXIS): normalizePositionForAxis(posY, $container, optionsY, queueWrapper)[norm.VERTICAL]; return normalized; } function normalizePositionForAxis(position, $container, options, queueWrapper){ var otherAxis, prefix, origPositionArg = position, basePosition = 0, sign = 1, axis = options.axis, scrollMode = norm.getScrollMode(options), normalized = { } ; if (!(axis === norm.HORIZONTAL || axis === norm.VERTICAL)) throw new Error("Axis option not defined, or not defined unambiguously, with current value " + axis) if (lib.isString(position)) { position = position.toLowerCase(); prefix = position.slice(0, 2); if (prefix === "+=" || prefix === "-=") { position = position.slice(2); sign = prefix === "+="? 1: -1; basePosition = lib.getScrollStartPosition_QW($container, queueWrapper, axis, scrollMode); } if (position.slice(-2) === "px") { position = parseFloat(position.slice(0, -2)); } else if (position.slice(-1) === "%") { position = parseFloat(position.slice(0, -1)) * lib.getScrollMaximum($container, axis) / 100; } else { if (axis === norm.HORIZONTAL) { if (position === "left") position = 0; if (position === "right") position = lib.getScrollMaximum($container, axis); if (position === "top" || position === "bottom") throw new Error("Desired position " + position + "is inconsistent with axis option " + axis) } else { if (position === "top") position = 0; if (position === "bottom") position = lib.getScrollMaximum($container, axis); if (position === "left" || position === "right") throw new Error("Desired position " + position + "is inconsistent with axis option " + axis) } } if (lib.isString(position) && $.isNumeric(position)) position = parseFloat(position); } if (lib.isNumber(position)) { position = Math.round(basePosition + sign * position); normalized[axis] = limitToScrollRange(position, $container, axis); } else if (isUndefinedPositionValue(position)) { normalized[axis] = scrollMode === norm.MODE_MERGE? lib.getLastTarget_QW(queueWrapper, axis): norm.IGNORE_AXIS; } else { throw new Error("Invalid position argument " + origPositionArg) } otherAxis = axis === norm.HORIZONTAL? norm.VERTICAL: norm.HORIZONTAL; normalized[otherAxis] = norm.IGNORE_AXIS; return normalized; } norm.normalizeOptions = function (options, position){ var hasX, hasY, axisDefault = norm.defaults.axis; options = options? normalizeAxisProperty(options): { } ; if ($.isPlainObject(position)) { position = normalizeAxisProperty(position); hasX = !isUndefinedPositionValue(position[norm.HORIZONTAL]) && position[norm.HORIZONTAL] !== norm.IGNORE_AXIS; hasY = !isUndefinedPositionValue(position[norm.VERTICAL]) && position[norm.VERTICAL] !== norm.IGNORE_AXIS; axisDefault = (hasX && hasY)? norm.BOTH_AXES: hasX? norm.HORIZONTAL: norm.VERTICAL; } else if (lib.isString(position)) { position = position.toLowerCase(); if (position === "top" || position === "bottom") { axisDefault = norm.VERTICAL; } else if (position === "left" || position === "right") { axisDefault = norm.HORIZONTAL; } } options.lockSpeedBelow = normalizeSpeedLockThreshold(options); validateIgnoreUserOption(options); return $.extend({ } , norm.defaults, { axis: axisDefault, duration: $.scrollable.defaultDuration} , options); } ; norm.normalizeAxisName = function (name){ if (lib.isInArray(name, altAxisNamesV)) { name = norm.VERTICAL; } else if (lib.isInArray(name, altAxisNamesH)) { name = norm.HORIZONTAL; } else if (lib.isInArray(name, altAxisNamesBoth)) { name = norm.BOTH_AXES; } if (!(name === norm.VERTICAL || name === norm.HORIZONTAL || name === norm.BOTH_AXES)) throw new Error("Invalid axis name " + name) return name; } ; function validateIgnoreUserOption(options){ var ignoreUser = options && options.ignoreUser; if (ignoreUser && !(ignoreUser === true || ignoreUser === norm.IGNORE_USER_SCROLL_ONLY || ignoreUser === norm.IGNORE_USER_CLICK_TOUCH_ONLY)) throw new Error('Invalid ignoreUser option value "' + ignoreUser + '"') } norm.getScrollMode = function (animationOptions){ return animationOptions.append? norm.MODE_APPEND: animationOptions.merge? norm.MODE_MERGE: norm.MODE_REPLACE; } ; function limitToScrollRange(position, $container, axis){ position = Math.min(position, lib.getScrollMaximum($container, axis)); position = Math.max(position, 0); return position; } function normalizeSpeedLockThreshold(options){ var threshold = options.lockSpeedBelow !== undefined? options.lockSpeedBelow: $.scrollable.lockSpeedBelow; threshold = parseFloat(threshold); return isNaN(threshold)? 0: threshold; } function normalizeAxisProperty(inputHash){ var normalized = { } ; $.each(inputHash, function (key, value){ if (lib.isInArray(key, altAxisNames)) { normalized[norm.normalizeAxisName(key)] = value; } else if (key === "axis") { normalized[key] = norm.normalizeAxisName(value); } else { normalized[key] = value; } } ); return normalized; } function isUndefinedPositionValue(positionValue){ return positionValue === undefined || positionValue === null || positionValue === false || positionValue === ""; } } )(norm, lib, queue); (function (queue, lib, norm){ "use strict"; var jqEffectsWithOptionsArg1 = getJQueryFunctions(["fadeIn", "fadeOut", "fadeToggle", "hide", "show", "slideDown", "slideToggle", "slideUp", "toggle"] ), jqEffectsWithOptionsArg2 = getJQueryFunctions(["animate"] ), jqEffectsWithStringArg2 = getJQueryFunctions(["delay"] ), jqEffectsFxQueueOnly = getJQueryFunctions(["fadeTo"] ), jQueryEffects = jqEffectsWithOptionsArg1.concat(jqEffectsWithOptionsArg2, jqEffectsWithStringArg2, jqEffectsFxQueueOnly); queue.QueueWrapper = function ($elem, queueName){ this._$elem = $elem; this._queueName = queueName !== undefined? queueName: norm.defaults.queue; this._isInternalCustomQueue = isInternalCustomQueue(this._queueName); this._isOtherCustomQueue = isOtherCustomQueue(this._queueName); } ; queue.QueueWrapper.prototype.addToQueue = function (config){ var func = config.func, args = config.args, $elem = this._$elem, queueName = this._queueName, sentinel = function (next){ next(); } ; sentinel.isSentinel = true ; if (config.info) sentinel.info = config.info; if (isQueueable(func)) { if (lib.isInArray(func, jqEffectsWithOptionsArg1)) { $.extend(args[0], { queue: queueName} ); } else if (lib.isInArray(func, jqEffectsWithOptionsArg2)) { $.extend(args[1], { queue: queueName} ); } else if (lib.isInArray(func, jqEffectsWithStringArg2)) { args[1] = queueName; } else { if (queueName !== "fx") throw new Error("Can't use a custom queue (queue name: '" + queueName + "') with the provided animation function") } func.apply($elem, args); } else { $elem.queue(queueName, function (next){ func.apply($elem, args); next(); } ); } $elem.queue(queueName, sentinel); if (this._isInternalCustomQueue && this.getContent()[1] === sentinel) $elem.dequeue(queueName); } ; queue.QueueWrapper.prototype.getInfo = function (){ var info = [] , queueContent = this.getContent(); $.each(queueContent, function (index, entry){ if (entry.isSentinel && entry.info) info.push(entry.info); } ); return info; } ; queue.QueueWrapper.prototype.getFirstInfo = function (){ var info = this.getInfo(); return _AN_Read_length("length", info)? info[0]: undefined; } ; queue.QueueWrapper.prototype.getLastInfo = function (){ var info = this.getInfo(), lastIndex = _AN_Read_length("length", info) - 1; return _AN_Read_length("length", info)? info[lastIndex]: undefined; } ; queue.QueueWrapper.prototype.getContent = function (){ return this._$elem.queue(this._queueName); } ; function getJQueryFunctions(names){ return $.grep($.map(names, function (name){ return $.fn[name]; } ), function (func){ return !!func; } ); } function isQueueable(func){ return lib.isInArray(func, jQueryEffects); } function isInternalCustomQueue(queueName){ return queueName === norm.defaults.queue && queueName !== "fx"; } function isOtherCustomQueue(queueName){ return lib.isString(queueName) && queueName !== "" && queueName !== norm.defaults.queue && queueName !== "fx"; } } )(queue, lib, norm); (function (lib, norm, queue, core){ "use strict"; var animationExitCallbacks = ["complete", "done", "fail", "always"] , animationCallbacks = animationExitCallbacks.concat("start", "step", "progress"); lib.getScrollMaximum = function ($container, axis){ var max, containerSize, contentSize, container = $container[0], _document = container.ownerDocument || container.document, isWindow = $.isWindow(container); if (axis === norm.BOTH_AXES) { max = { } ; max[norm.HORIZONTAL] = lib.getScrollMaximum($container, norm.HORIZONTAL); max[norm.VERTICAL] = lib.getScrollMaximum($container, norm.VERTICAL); } else { if (axis === norm.HORIZONTAL) { containerSize = isWindow? $.windowWidth(): container.clientWidth; contentSize = isWindow? $.documentWidth(_document): container.scrollWidth; } else if (axis === norm.VERTICAL) { containerSize = isWindow? $.windowHeight(): container.clientHeight; contentSize = isWindow? $.documentHeight(_document): container.scrollHeight; } else { throw new Error("Unrecognized axis argument " + axis) } max = Math.max(contentSize - containerSize, 0); } return max; } ; lib.ownerWindow = function ($elem){ var elem = $elem instanceof $? $elem[0]: $elem, ownerDocument = elem && (elem.nodeType === 9? elem: elem.ownerDocument); return ownerDocument && (ownerDocument.defaultView || ownerDocument.parentWindow) || $.isWindow(elem) && elem || undefined; } ; lib.getCallbacks = function (animationOptions){ return pick(animationOptions, animationCallbacks); } ; lib.getExitCallbacks = function (animationOptions){ return pick(animationOptions, animationExitCallbacks); } ; lib.bindAnimationCallbacks = function (animationOptions, thisNode){ if (animationOptions) { animationOptions = $.extend({ } , animationOptions); $.each(animationCallbacks, function (index, name){ if (animationOptions[name]) animationOptions[name] = $.proxy(animationOptions[name], thisNode); } ); } return animationOptions; } ; lib.addScrollAnimation = function ($elem, position, options){ var posX = position[norm.HORIZONTAL], posY = position[norm.VERTICAL], hasPosX = posX !== norm.IGNORE_AXIS, hasPosY = posY !== norm.IGNORE_AXIS, animated = { } , history = options._history || { real: [] , expected: [] } , callbackMessageContainer = createOuterMessageContainer(), animationInfo = { position: position, history: history, callbackMessages: callbackMessageContainer} ; options = addUserScrollDetection(options, history); options = addUserClickTouchDetection($elem, options); options = addMessagingToCallbacks(options, callbackMessageContainer); if (hasPosX) animated.scrollLeft = posX; if (hasPosY) animated.scrollTop = posY; if (hasPosX || hasPosY) lib.addAnimation($elem, animated, options, animationInfo); } ; lib.addAnimation = function ($elem, properties, options, animationInfo){ var queueWrapper = new queue.QueueWrapper($elem, options.queue), config = { func: $.fn.animate, args: [properties, options] , info: animationInfo} ; queueWrapper.addToQueue(config); } ; lib.stopScrollAnimation = function ($scrollable, options, messages){ var history; options = $.extend({ jumpToTargetPosition: false } , options); if (options.queue === false ) { $scrollable.stop(true , options.jumpToTargetPosition); } else { history = getCurrentStepHistory($scrollable, options); if (messages) lib.notifyScrollCallbacks($scrollable, messages, animationExitCallbacks, options.queue); $scrollable.stop(options.queue, true , options.jumpToTargetPosition); } return history; } ; lib.notifyScrollCallbacks = function ($scrollable, message, callbackNames, queueName){ var callbackMessageContainers = getMessageContainers($scrollable, { queue: queueName} , callbackNames); if (!$.isPlainObject(message)) throw new Error('Invalid message argument. Expected a hash but got type ' + $.type(message)) $.each(callbackMessageContainers, function (index, messageContainer){ $.extend(messageContainer, message); } ); } ; lib.getScrollStartPosition = function ($container, options, axis){ var queueWrapper = new queue.QueueWrapper(core.getScrollable($container), options.queue), scrollMode = norm.getScrollMode(options); return lib.getScrollStartPosition_QW($container, queueWrapper, axis, scrollMode); } ; lib.getScrollStartPosition_QW = function ($container, queueWrapper, axis, scrollMode){ var append = scrollMode === norm.MODE_APPEND, merge = scrollMode === norm.MODE_MERGE, position; axis || (axis = norm.BOTH_AXES); position = (append || merge)? lib.getLastTarget_QW(queueWrapper, axis): lib.getCurrentScrollPosition($container, axis); if (axis === norm.BOTH_AXES) { if (position[norm.HORIZONTAL] === norm.IGNORE_AXIS) position[norm.HORIZONTAL] = lib.getCurrentScrollPosition($container, norm.HORIZONTAL); if (position[norm.VERTICAL] === norm.IGNORE_AXIS) position[norm.VERTICAL] = lib.getCurrentScrollPosition($container, norm.VERTICAL); } else if (position === norm.IGNORE_AXIS) { position = lib.getCurrentScrollPosition($container, axis); } return position; } ; lib.getLastStepHistory = function ($container, options){ return lib.getLastStepHistory_QW(new queue.QueueWrapper(core.getScrollable($container), options.queue)); } ; lib.getLastStepHistory_QW = function (queueWrapper){ var info = queueWrapper.getLastInfo(); return info? info.history: undefined; } ; lib.getLastTarget = function ($container, options, axis){ return lib.getLastTarget_QW(new queue.QueueWrapper(core.getScrollable($container), options.queue), axis); } ; lib.getLastTarget_QW = function (queueWrapper, axis){ var retrievedX, retrievedY, returnBothAxes = !axis || axis === norm.BOTH_AXES, last = { } , infoEntries = queueWrapper.getInfo(); last[norm.HORIZONTAL] = last[norm.VERTICAL] = norm.IGNORE_AXIS; $.each(infoEntries, function (index, info){ if (info.position) { retrievedX = info.position[norm.HORIZONTAL]; retrievedY = info.position[norm.VERTICAL]; if (retrievedX !== undefined && retrievedX !== norm.IGNORE_AXIS) last[norm.HORIZONTAL] = retrievedX; if (retrievedY !== undefined && retrievedY !== norm.IGNORE_AXIS) last[norm.VERTICAL] = retrievedY; } } ); return returnBothAxes? last: last[axis]; } ; lib.isRedundantTarget = function (target, compareTo){ var newX = target[norm.HORIZONTAL], newY = target[norm.VERTICAL], lastX = compareTo[norm.HORIZONTAL], lastY = compareTo[norm.VERTICAL], matchesX = newX === norm.IGNORE_AXIS || newX === lastX, matchesY = newY === norm.IGNORE_AXIS || newY === lastY; return matchesX && matchesY; } ; lib.getCurrentScrollPosition = function ($container, axis){ var coords = { } ; if (!axis || axis === norm.BOTH_AXES) { coords[norm.HORIZONTAL] = $container.scrollLeft(); coords[norm.VERTICAL] = $container.scrollTop(); } return axis === norm.HORIZONTAL? $container.scrollLeft(): axis === norm.VERTICAL? $container.scrollTop(): coords; } ; lib.isString = function (value){ return typeof value === 'string' || value instanceof String; } ; lib.isNumber = function (value){ return (typeof value === 'number' || value instanceof Number) && !isNaN(value); } ; lib.isInArray = function (value, arr){ return $.inArray(value, arr) !== -1; } ; function addUserScrollDetection(animationOptions, history){ var queueName = animationOptions.queue, enableDetection = $.scrollable._enableUserScrollDetection && animationOptions.ignoreUser !== true && animationOptions.ignoreUser !== norm.IGNORE_USER_SCROLL_ONLY, userScrollTriggerThreshold = animationOptions.userScrollThreshold !== undefined? parseInt(animationOptions.userScrollThreshold, 10): $.scrollable.userScrollThreshold, userScrollDetectionThreshold = $.scrollable._scrollDetectionThreshold, userStepCb = animationOptions.step, modifiedOptions = animationOptions? $.extend({ } , animationOptions): { } , lastExpected = { } , cumulativeDelta = { scrollTop: 0, scrollLeft: 0} ; if (enableDetection) { if (userScrollTriggerThreshold < userScrollDetectionThreshold) throw new Error("User scroll detection: threshold too low. The threshold for detecting user scroll movement must be set to at least " + userScrollDetectionThreshold) modifiedOptions.step = function (now, tween){ var animatedProp = tween.prop, otherProp = animatedProp === "scrollTop"? "scrollLeft": "scrollTop", lastReal = { } , currentDelta = { scrollTop: 0, scrollLeft: 0} , scrollDetected = { scrollTop: false , scrollLeft: false } ; lastReal[animatedProp] = Math.floor(tween.cur()); lastReal[otherProp] = Math.floor(tween.elem[otherProp]); if (lastExpected[animatedProp] !== undefined && lastExpected[otherProp] !== undefined) { currentDelta[animatedProp] = lastExpected[animatedProp] - lastReal[animatedProp]; currentDelta[otherProp] = lastExpected[otherProp] - lastReal[otherProp]; scrollDetected[animatedProp] = Math.abs(currentDelta[animatedProp]) > userScrollDetectionThreshold; scrollDetected[otherProp] = Math.abs(currentDelta[otherProp]) > userScrollDetectionThreshold; if ($.scrollable._useScrollHistoryForDetection) { if (scrollDetected[animatedProp]) scrollDetected[animatedProp] = !hasBrowserFailedToUpdate(animatedProp, lastReal, history); if (scrollDetected[otherProp]) scrollDetected[otherProp] = !hasBrowserFailedToUpdate(otherProp, lastReal, history); } if (scrollDetected[animatedProp] || scrollDetected[otherProp]) { cumulativeDelta[animatedProp] += currentDelta[animatedProp]; cumulativeDelta[otherProp] += currentDelta[otherProp]; } if (Math.abs(cumulativeDelta[animatedProp]) > userScrollTriggerThreshold || Math.abs(cumulativeDelta[otherProp]) > userScrollTriggerThreshold) { tween.now = lastReal[animatedProp]; lib.stopScrollAnimation($(tween.elem), { queue: queueName} , { cancelled: "scroll"} ); } } lastExpected[animatedProp] = Math.floor(tween.now); lastExpected[otherProp] = lastReal[otherProp]; if ($.scrollable._useScrollHistoryForDetection) { history.real.push(lastReal); history.expected.push($.extend({ } , lastExpected)); if (_AN_Read_length("length", history.real) > 6) history.real.shift(); if (_AN_Read_length("length", history.expected) > 6) history.expected.shift(); } return userStepCb && userStepCb.apply(this, $.makeArray(arguments)); } ; } return modifiedOptions; } function addUserClickTouchDetection($elem, animationOptions){ var handler, events = "mousedown touchstart pointerdown", enableDetection = $.scrollable._enableClickAndTouchDetection && animationOptions.ignoreUser !== true && animationOptions.ignoreUser !== norm.IGNORE_USER_CLICK_TOUCH_ONLY, queueName = animationOptions.queue, userStartCb = animationOptions.start, userAlwaysCb = animationOptions.always, modifiedOptions = animationOptions? $.extend({ } , animationOptions): { } , $clickable = $elem[0].tagName.toLowerCase() === "html"? $($elem[0].ownerDocument.body): $elem; if (enableDetection) { handler = function (){ lib.stopScrollAnimation($elem, { queue: queueName} , { cancelled: "click"} ); } ; modifiedOptions.start = function (){ $clickable.on(events, handler); return userStartCb && userStartCb.apply(this, $.makeArray(arguments)); } ; modifiedOptions.always = function (){ $clickable.off(events, handler); return userAlwaysCb && userAlwaysCb.apply(this, $.makeArray(arguments)); } ; } return modifiedOptions; } function addMessagingToCallbacks(animationOptions, outerMessageContainer){ var callbacks = lib.getExitCallbacks(animationOptions), modifiedOptions = animationOptions? $.extend({ } , animationOptions): { } ; $.each(callbacks, function (callbackName, callback){ var messageContainer = outerMessageContainer[callbackName]; modifiedOptions[callbackName] = function (){ var args = $.makeArray(arguments); if (_AN_Read_length("length", args) === 1) args.push(undefined); args.splice(2, 0, messageContainer); callback.apply(this, args); } ; } ); return modifiedOptions; } function hasBrowserFailedToUpdate(property, lastReal, history){ var i, steps, hasFailed = false , lenReal = _AN_Read_length("length", history.real), lenExpected = _AN_Read_length("length", history.expected), real = lastReal[property]; i = lenReal - 1; steps = 6; while (i >= 0 && lenReal - i <= steps && !hasFailed){ hasFailed = real === history.real[i][property]; i-- ; } i = lenExpected - 1; steps = 6; while (i >= 0 && lenExpected - i <= steps && !hasFailed){ hasFailed = real === history.expected[i][property]; i-- ; } return hasFailed; } function getCurrentStepHistory($scrollable, options){ return getCurrentStepHistory_QW(new queue.QueueWrapper($scrollable, options.queue)); } function getCurrentStepHistory_QW(queueWrapper){ var info = queueWrapper.getFirstInfo(); return info? info.history: undefined; } function createOuterMessageContainer(){ var outer = { } ; $.each(animationExitCallbacks, function (index, callbackName){ outer[callbackName] = { } ; } ); return outer; } function getMessageContainers($scrollable, options, callbackNames){ return getMessageContainers_QW(new queue.QueueWrapper($scrollable, options.queue), callbackNames); } function getMessageContainers_QW(queueWrapper, callbackNames){ var messageContainers = [] , infoEntries = queueWrapper.getInfo(), outerMessageContainers = $.map(infoEntries, function (info){ return info.callbackMessages; } ); callbackNames || (callbackNames = animationExitCallbacks); $.each(callbackNames, function (index, callbackName){ if (!lib.isInArray(callbackName, animationExitCallbacks)) throw new Error('Invalid animation callback name. Expected the name of an exit callback ("' + animationExitCallbacks.join('", "') + '"), but got "' + callbackName + '"') $.each(outerMessageContainers, function (index, outerMessageContainer){ messageContainers.push(outerMessageContainer[callbackName]); } ); } ); return messageContainers; } function getCurrentTravelDistance($container, targetPosition){ var currentPosition = lib.getCurrentScrollPosition($container), deltaX = targetPosition[norm.HORIZONTAL] === norm.IGNORE_AXIS? 0: targetPosition[norm.HORIZONTAL] - currentPosition[norm.HORIZONTAL], deltaY = targetPosition[norm.VERTICAL] === norm.IGNORE_AXIS? 0: targetPosition[norm.VERTICAL] - currentPosition[norm.VERTICAL]; return Math.sqrt(Math.pow(deltaX, 2) + Math.pow(deltaY, 2)); } function pick(hash, keyNames){ var picked = { } ; if (hash) { $.each(keyNames, function (index, name){ var value = hash[name]; if (value !== undefined) picked[name] = value; } ); } return picked; } $.Animation.prefilter(function (elem, properties, options){ var $container, distance, thresholdDistance, minSpeed, maxDuration, targetPosition = { } , hasX = properties && "scrollLeft" in properties, hasY = properties && "scrollTop" in properties, isScrollAnimation = properties && (hasX || hasY) && options && options._jqScrollable; if (isScrollAnimation && options.lockSpeedBelow) { $container = norm.normalizeContainer($(elem)); targetPosition[norm.HORIZONTAL] = hasX? properties.scrollLeft: norm.IGNORE_AXIS; targetPosition[norm.VERTICAL] = hasY? properties.scrollTop: norm.IGNORE_AXIS; distance = getCurrentTravelDistance($container, targetPosition); thresholdDistance = options.lockSpeedBelow; if (distance < thresholdDistance) { minSpeed = thresholdDistance / options.duration; maxDuration = distance / minSpeed; this.duration = options.duration = Math.min(maxDuration, options.duration); } } } ); } )(lib, norm, queue, core); (function (core, lib){ "use strict"; var _windowScrollable; core.getScrollable = function ($container){ return $.isWindow($container[0])? getWindowScrollable($container[0]): $container; } ; core.animateScroll = function ($container, position, options){ var $scrollable = core.getScrollable($container); lib.addScrollAnimation($scrollable, position, options); } ; function getWindowScrollable(currentWindow){ var iframe, element, tagName, currentDocument = (currentWindow || window).document; if (currentDocument.compatMode === "BackCompat") return $(currentDocument.body); if (!_windowScrollable) { element = document.scrollingElement; tagName = element && element.tagName; if (tagName && tagName.toLowerCase) { tagName = tagName.toLowerCase(); if (tagName === "html" || tagName === "body") _windowScrollable = tagName; } } if (!_windowScrollable) { iframe = createScrollableTestIframe(); iframe.contentWindow.scrollTo(1, 0); _windowScrollable = iframe.contentDocument.documentElement.scrollLeft === 1? "html": "body"; document.body.removeChild(iframe); } return _windowScrollable === "html"? $(currentDocument.documentElement): $(currentDocument.body); } function createScrollableTestIframe(){ var iframe = _AN_Call_createelement("createElement", document, "iframe"), body = document.body; _AN_Write_csstext("cssText", iframe.style, false , "position: absolute; top: -600px; left: -600px; width: 500px; height: 500px; margin: 0px; padding: 0px; border: none; display: block;"); iframe.frameborder = "0"; _AN_Call_appendchild("appendChild", body, iframe); _AN_Write_src("src", iframe, false , 'about:blank'); _AN_Call_write('write', iframe.contentDocument, ''); return iframe; } $(function (){ if (_windowScrollable === undefined) getWindowScrollable(); } ); } )(core, lib); } (typeof jQuery !== "undefined"? jQuery: $)); } ));