// +Lift+ UI widget creates a gradually exposing element
// The +container_selector+ constructor attribute is used to provide the Lift dataset — a collection of same-height elements.
// Note: The container will be resized based on equation: visible_elements * element_height
//
// All magic is done via +animate(options)+ method, which takes some basic parameters:
// * visible(integer) - a number of visible elements, and also the desired visible*element_height size of the container
// * speed(integer) - time in miliseconds between state change
// * duration(integer) - duration of the state change
// * direction(string: bottom|top) - direction of the state change, i.e. "bottom" reads as "move to bottom" (add on top)
//
// Usecase:
// var lift = new Lift("#feed").animate({ duration: 30000, speed: 500 });
function Lift(container_selector) {
    // Default configuration — please change only when it is really changing the *default* behaviour
    this.default_options = {
        visible: 4,
        speed: 5000,
        duration: 1500,
        direction: "bottom"
    }

    // Preconfiguration
    this.container_selector = container_selector;
    this.container = function() {
        return $(this.container_selector);
    }


    // +animate(options)+ method starts up the animation process.
    //
    // Method takes options object with fields:
    // * visible(integer) - a number of visible elements, and also the desired visible*element_height size of the container
    // * speed(integer) - time in miliseconds between state change
    // * duration(integer) - duration of the state change
    // * direction(string: bottom|top) - direction of the state change, i.e. "bottom" reads as "move to bottom" (add on top)
    //
    // The workflow is as follows:
    // 1. Resize the container element to be exactly visible_elements * element_height big
    // 2. Move all the inner elements to div.container element to provide smoother animation
    // 3. Actually animate the container (uses overflow:hidden behaviour and absolute inner positioning)
    this.animate = function(assigned_options) {
        // Handle the default options
        var options = this.default_options;
        jQuery.extend(options, assigned_options); // Note to future me: This method is destructive ;)

        // Calculate some useful things and resize the container to fit the elements
        // Use outerHeight() instead of height() to get the height of the element with padding and border
        $this = this;
        var lift_height = $(this.container().find(">div")[0]).outerHeight();
        this.container().height(lift_height * options.visible);

        // Move the elements to a special wrapper to handle the movement smoothly
        this.container().prepend("<div class='container' style='position: relative'></div>");
        $.each(this.container().find(">div[class!=container]"), function(index, element) {
            $this.container().find("div.container").append($(element));
        });
        

        // Position the wrapper element in a good place
        if (options.direction == "bottom") {
            $this.container().find("div.container").animate({ top: this.container().height() - this.container().find("div.container").height() }, 0);
        } else if (options.direction == "top") {
            $this.container().find("div.container").animate({ top: 0 }, 0);
        }

        // Animate the wrapper element
        var counter_start = this.container().find(".container>div").length - 1;
        var counter_end = (this.container().height() / lift_height);
        for (var i = counter_start; i >= counter_end; i--) {
            if (options.direction == "bottom") {
                setTimeout((function() {
                    $this.container().find("div.container").animate({ top: "+=" + lift_height }, options.duration);
                }), ((i - counter_end + 1) * options.speed));
            } else if (options.direction == "top") {
                setTimeout((function() {
                    $this.container().find("div.container").animate({ top: "-=" + lift_height }, options.duration);
                }), ((i - counter_end + 1) * options.speed));
            }
        }
    }
}

