(function(jQuery) {

    jQuery.fn.dragScroller = function(options) {

        return jQuery(this).each(function(i, element) {
            var state = {};
            state.oDefaults = jQuery.extend({}, options);

            state.oDefaults.dragElement = jQuery(element).children().eq(0);
            
            state.oDefaults.x = {};
            state.oDefaults.x.marginType = "marginLeft";
            state.oDefaults.x.iStartMargin = parseInt(state.oDefaults.dragElement.css(state.oDefaults.x.marginType), 10);
            state.oDefaults.x.iOriginalParentLength = state.oDefaults.dragElement.parent().width();
            state.oDefaults.x.iOriginalLength = state.oDefaults.dragElement.parent().get(0).scrollWidth - state.oDefaults.x.iOriginalParentLength; 
            
            state.oDefaults.y = {};
            state.oDefaults.y.marginType = "marginTop";
            state.oDefaults.y.iStartMargin = parseInt(state.oDefaults.dragElement.css(state.oDefaults.x.marginType), 10);
            state.oDefaults.y.iOriginalParentLength = state.oDefaults.dragElement.parent().height();
            state.oDefaults.y.iOriginalLength = state.oDefaults.dragElement.parent().get(0).scrollHeight - state.oDefaults.y.iOriginalParentLength; 

            jQuery(element)
                .bind("mousedown", {state: state}, startDrag)
                .bind("click", function(event) {
                event.preventDefault();
            });

        });
    };

    var startDrag = function(event) {
        event.preventDefault();
        var data = {state: event.data.state};
        var stateDefaults = data.state.oDefaults;

        jQuery(document.body)
            .bind("mousemove", data, dragElement)
            .bind("mouseup", data, stopDrag)
            .bind("mouseleave", data, stopDrag);
            
        stateDefaults.dragElement.parent().addClass("dragging");
        
        stateDefaults.y.iInitalOffset = parseInt(stateDefaults.dragElement.css("marginTop"), 10);
        stateDefaults.y.iStart = event.pageY;
        stateDefaults.y.iStartPos = event.originalEvent.pageY;
        stateDefaults.x.iInitalOffset = parseInt(stateDefaults.dragElement.css("marginLeft"), 10);
        stateDefaults.x.iStart = event.pageX;
        stateDefaults.x.iStartPos = event.originalEvent.pageX;
        stateDefaults.iStartTime = new Date().getTime();
        return false;
    };


    var dragElement = function(event) {

        event.preventDefault();

        var stateDefaults = event.data.state.oDefaults;
        
        if (stateDefaults.axis.indexOf("y") !== -1) {
            var elementMarginTop = ((event.pageY - stateDefaults.y.iStart)+stateDefaults.y.iInitalOffset);
            stateDefaults.dragElement.css("marginTop", elementMarginTop);
        }
        
        if (stateDefaults.axis.indexOf("x") !== -1) {
            var elementMarginLeft = ((event.pageX - stateDefaults.x.iStart)+stateDefaults.x.iInitalOffset);
            stateDefaults.dragElement.css("marginLeft", elementMarginLeft);
        }
 
        return false;
    };
    
    var stopDrag = function(event) {
        
        var stateDefaults = event.data.state.oDefaults;
        
        jQuery(document.body)
            .unbind("mousemove", dragElement)
            .unbind("mouseup", stopDrag)
            .unbind("mouseleave", stopDrag);
            
        stateDefaults.dragElement.parent().removeClass("dragging");
        
        if(stateDefaults.inertia === true) {
            
            var inertiaAnimation = {};
            var options = {};
            if (stateDefaults.axis.indexOf("y") !== -1) {
                evaluatePosition(event.originalEvent.pageY, stateDefaults.dragElement, stateDefaults.y, stateDefaults.iStartTime, inertiaAnimation, options);
            }
            if (stateDefaults.axis.indexOf("x") !== -1) {
                evaluatePosition(event.originalEvent.pageX, stateDefaults.dragElement, stateDefaults.x, stateDefaults.iStartTime, inertiaAnimation, options);
            }
            stateDefaults.dragElement.stop().animate(inertiaAnimation, options.duration, options.transition);
            
        }
        return false;
    };
    
    var evaluatePosition = function(startMousePos, dragElement, stateDefaultsAxisData, iStartTime, animation, options) {
        
        var elementExistingMargin = parseInt($(dragElement).css(stateDefaultsAxisData.marginType), 10);
        if (elementExistingMargin > stateDefaultsAxisData.iStartMargin) {
            animation[stateDefaultsAxisData.marginType] = stateDefaultsAxisData.iStartMargin;
            options.transition = 'easeOutSine';
            options.duration = 300;
        }
        else if (elementExistingMargin < -stateDefaultsAxisData.iOriginalLength) {
            animation[stateDefaultsAxisData.marginType] = -stateDefaultsAxisData.iOriginalLength;
            options.transition = 'easeOutSine';
            options.duration = 300;
        }
        else {
            var iFinalPosition = calculateInertia(stateDefaultsAxisData.iStartPos, startMousePos, iStartTime) + elementExistingMargin;
            if(iFinalPosition > stateDefaultsAxisData.iStartMargin) {
                iFinalPosition = stateDefaultsAxisData.iStartMargin;
            } else if (iFinalPosition < -stateDefaultsAxisData.iOriginalLength) {
                iFinalPosition = -stateDefaultsAxisData.iOriginalLength;
            }
            animation[stateDefaultsAxisData.marginType] = iFinalPosition;
            options.transition = 'easeOutQuad';
            options.duration = 500;
        }
        
    };
    
   var calculateInertia = function(iMouseStartPos, iMouseEndPos, iStartDragTime) {
        
        var iInertiaLength = 200;
        var duration = 300;
        var iEndDragTime = new Date().getTime();
        var iScrollLength = iMouseStartPos-iMouseEndPos;
        var iScrollTime = iStartDragTime - iEndDragTime;
        var iVelocity = iScrollLength/iScrollTime;
        var iInertia = iVelocity*iInertiaLength;
       return iInertia;
        
    };

})(jQuery);

