/*!

* jQuery.scrollTo
* Copyright (c) 2007-2015 Ariel Flesler - aflesler<a>gmail<d>com | http://flesler.blogspot.com
* Licensed under MIT
* http://flesler.blogspot.com/2007/10/jqueryscrollto.html
* @projectDescription Lightweight, cross-browser and highly customizable animated scrolling with jQuery
* @author Ariel Flesler
* @version 2.1.1
*/

;(function(factory) {

'use strict';
if (typeof define === 'function' && define.amd) {
        // AMD
        define(['jquery'], factory);
} else if (typeof module !== 'undefined' && module.exports) {
        // CommonJS
        module.exports = factory(require('jquery'));
} else {
        // Global
        factory(jQuery);
}

})(function($) {

'use strict';

var $scrollTo = $.scrollTo = function(target, duration, settings) {
        return $(window).scrollTo(target, duration, settings);
};

$scrollTo.defaults = {
        axis:'xy',
        duration: 0,
        limit:true
};

function isWin(elem) {
        return !elem.nodeName ||
                $.inArray(elem.nodeName.toLowerCase(), ['iframe','#document','html','body']) !== -1;
}               

$.fn.scrollTo = function(target, duration, settings) {
        if (typeof duration === 'object') {
                settings = duration;
                duration = 0;
        }
        if (typeof settings === 'function') {
                settings = { onAfter:settings };
        }
        if (target === 'max') {
                target = 9e9;
        }

        settings = $.extend({}, $scrollTo.defaults, settings);
        // Speed is still recognized for backwards compatibility
        duration = duration || settings.duration;
        // Make sure the settings are given right
        var queue = settings.queue && settings.axis.length > 1;
        if (queue) {
                // Let's keep the overall duration
                duration /= 2;
        }
        settings.offset = both(settings.offset);
        settings.over = both(settings.over);

        return this.each(function() {
                // Null target yields nothing, just like jQuery does
                if (target === null) return;

                var win = isWin(this),
                        elem = win ? this.contentWindow || window : this,
                        $elem = $(elem),
                        targ = target, 
                        attr = {},
                        toff;

                switch (typeof targ) {
                        // A number will pass the regex
                        case 'number':
                        case 'string':
                                if (/^([+-]=?)?\d+(\.\d+)?(px|%)?$/.test(targ)) {
                                        targ = both(targ);
                                        // We are done
                                        break;
                                }
                                // Relative/Absolute selector
                                targ = win ? $(targ) : $(targ, elem);
                                if (!targ.length) return;
                                /* falls through */
                        case 'object':
                                // DOMElement / jQuery
                                if (targ.is || targ.style) {
                                        // Get the real position of the target
                                        toff = (targ = $(targ)).offset();
                                }
                }

                var offset = $.isFunction(settings.offset) && settings.offset(elem, targ) || settings.offset;

                $.each(settings.axis.split(''), function(i, axis) {
                        var Pos = axis === 'x' ? 'Left' : 'Top',
                                pos = Pos.toLowerCase(),
                                key = 'scroll' + Pos,
                                prev = $elem[key](),
                                max = $scrollTo.max(elem, axis);

                        if (toff) {// jQuery / DOMElement
                                attr[key] = toff[pos] + (win ? 0 : prev - $elem.offset()[pos]);

                                // If it's a dom element, reduce the margin
                                if (settings.margin) {
                                        attr[key] -= parseInt(targ.css('margin'+Pos), 10) || 0;
                                        attr[key] -= parseInt(targ.css('border'+Pos+'Width'), 10) || 0;
                                }

                                attr[key] += offset[pos] || 0;

                                if (settings.over[pos]) {
                                        // Scroll to a fraction of its width/height
                                        attr[key] += targ[axis === 'x'?'width':'height']() * settings.over[pos];
                                }
                        } else {
                                var val = targ[pos];
                                // Handle percentage values
                                attr[key] = val.slice && val.slice(-1) === '%' ?
                                        parseFloat(val) / 100 * max
                                        : val;
                        }

                        // Number or 'number'
                        if (settings.limit && /^\d+$/.test(attr[key])) {
                                // Check the limits
                                attr[key] = attr[key] <= 0 ? 0 : Math.min(attr[key], max);
                        }

                        // Don't waste time animating, if there's no need.
                        if (!i && settings.axis.length > 1) {
                                if (prev === attr[key]) {
                                        // No animation needed
                                        attr = {};
                                } else if (queue) {
                                        // Intermediate animation
                                        animate(settings.onAfterFirst);
                                        // Don't animate this axis again in the next iteration.
                                        attr = {};
                                }
                        }
                });

                animate(settings.onAfter);

                function animate(callback) {
                        var opts = $.extend({}, settings, {
                                // The queue setting conflicts with animate()
                                // Force it to always be true
                                queue: true,
                                duration: duration,
                                complete: callback && function() {
                                        callback.call(elem, targ, settings);
                                }
                        });
                        $elem.animate(attr, opts);
                }
        });
};

// Max scrolling position, works on quirks mode
// It only fails (not too badly) on IE, quirks mode.
$scrollTo.max = function(elem, axis) {
        var Dim = axis === 'x' ? 'Width' : 'Height',
                scroll = 'scroll'+Dim;

        if (!isWin(elem))
                return elem[scroll] - $(elem)[Dim.toLowerCase()]();

        var size = 'client' + Dim,
                doc = elem.ownerDocument || elem.document,
                html = doc.documentElement,
                body = doc.body;

        return Math.max(html[scroll], body[scroll]) - Math.min(html[size], body[size]);
};

function both(val) {
        return $.isFunction(val) || $.isPlainObject(val) ? val : { top:val, left:val };
}

// Add special hooks so that window scroll properties can be animated
$.Tween.propHooks.scrollLeft = 
$.Tween.propHooks.scrollTop = {
        get: function(t) {
                return $(t.elem)[t.prop]();
        },
        set: function(t) {
                var curr = this.get(t);
                // If interrupt is true and user scrolled, stop animating
                if (t.options.interrupt && t._last && t._last !== curr) {
                        return $(t.elem).stop();
                }
                var next = Math.round(t.now);
                // Don't waste CPU
                // Browsers don't render floating point scroll
                if (curr !== next) {
                        $(t.elem)[t.prop](next);
                        t._last = this.get(t);
                }
        }
};

// AMD requirement
return $scrollTo;

});