﻿/**
* Initializes the Pager object
* 
* Should pass an argument array with viewport, transition, pager_caption, pager_prev, pager_next and optionally step specified
* 
* @constructor
*/
function Pager(args) {
    // Set the viewport
    this._viewport = args.viewport;

    // Set the previous and next links
    this._pager_prev = args.pager_prev;
    this._pager_next = args.pager_next;

    // Set the element to hide if there is only one page
    this._hide_if_single = args.hide_if_single;

    // If there is a step argument, set it
    this._step = args.step == null ? 1 : args.step;

    // If fade is true, set the flag
    this._fade = args.fade == null ? false : args.fade;
    this._fade_duration = args.fade_duration == null ? 300 : args.fade_duration;

    // Set the viewport fix height flag
    this._viewport_fix_height = args.viewport_fix_height;
    this._viewport_fix_height_duration = args.viewport_fix_height_duration == null ?
		this._fade_duration : args.viewport_fix_height_duration;

    // Get the caption and set the template text
    this._caption = args.pager_caption;
    
    if(this._caption)
        this._caption_tmpl = $(this._caption).text();

    // Find the array of items
    this._items = $('#' + this._viewport.id + ' .scroll-item');

    // Set the initial index
    this._currentIndex = 0;

    // Set the initial caption
    if(this._caption)
        this._setCaption();

    // Make sure the viewport is pointing at the current index
    this._moveTo(this._currentIndex);

    // Set visibility
    this._setVisibility();
}

Pager.prototype.constructor = Pager;

/**
* Focuses on the element
* 
* @param {Element}	element			The element that will be focused on
* @param {bool}	transitions		Defaults to true
*/
Pager.prototype.focusOn = function(element, transitions) {
    transitions = transitions == null ? true : transitions;

    // Find the element in the items index
    var elementIndex = this._items.index(element);
    // Find the index given the number of steps
    var moveIndex = elementIndex - (elementIndex % this._step);
    // Roll the correct number of steps to focus on the element
    this._roll(this._currentIndex + moveIndex, transitions);
}

/**
* Injects a link that calls the given JavaScript function when clicked, 
* using the item's text as the link text
* 
* @param {Element} el		Element to inject the link into
* @param {Function} fun	Function the link should call when clicked
*/
Pager.prototype.injectLink = function(el, fun) {
    // Get the inner html and empty the element
    var html = $(el).html();
    $(el).empty();

    // Construct the link
    var link = $('<a href="javascript:void(0)"></a>')

    // Set the click event
    link.click(fun);

    // Set the link's HTML to the old element HTML
    link.html(html);

    // Inject the link into the element
    $(el).append(link);
}

/**
* Step forwards
*/
Pager.prototype.stepForwards = function() {
    this._roll(this._step);
}

/**
* Step backwards
*/
Pager.prototype.stepBackwards = function() {
    this._roll(this._step * -1);
}

/**
* Rolls the item the specified number of steps, forwards or backwards
* 
* @param {int}		steps			The number of steps to roll. Can be a positive or negative number
* @param {bool}	transitions		Run any transitions. Defaults to true
*/
Pager.prototype._roll = function(steps, transitions) {
    transitions = transitions == null ? true : transitions;

    var newIndex = this._currentIndex + steps;

    // Bounds check
    if (newIndex < 0 || newIndex >= this._items.length) {
        return;
    }

    // Set the new index
    this._currentIndex = newIndex;

    // Move the viewport to point to the new item by index
    this._moveTo(this._currentIndex, transitions);

    // Set the caption
    if(this._caption)
        this._setCaption();

    // Set visibility
    this._setVisibility();
}

/**
* Sets the caption of the scroller
*/
Pager.prototype._setCaption = function() {
    current = this._currentIndex + 1;

    if (this._step > 1) {
        current_end = this._currentIndex + this._step > this._items.length ?
			this._items.length :
			this._currentIndex + this._step;

        current = current + '&ndash;' + current_end;
    }

    $(this._caption).html(
		this._caption_tmpl.
			replace(/{current}/, current).
			replace(/{total}/, this._items.length)
	);
}

/**
* Sets the visibility of the arrows
*/
Pager.prototype._setVisibility = function() {

    // Hide the specified element if there is only one page
    if (this._items.length <= 1 && this._hide_if_single != null) {
        $(this._hide_if_single).css('display', 'none');
    }

    if (this._currentIndex == 0) {
        this._pager_prev.className = 'pager-link-inactive';
    } else {
        this._pager_prev.className = 'pager-link-active';
    }

    if (this._currentIndex + this._step >= this._items.length) {
        this._pager_next.className = 'pager-link-inactive';
    } else {
        this._pager_next.className = 'pager-link-active';
    }
}


/**
* Moves the viewport to point at the given index
* 
* @param {int}		index
* @param {bool}	transitions
*/
Pager.prototype._moveTo = function(index, transitions) {
    // If we are supposed to fade, fade out
    if (this._fade && transitions) {
        // Bind 'this' to a local variable so the callback functions can hold a reference to it
        me = this;

        $(this._viewport).animate({ opacity: 0 }, this._fade_duration, null, function() {
            me._moveToWorker(index, transitions);

            $(me._viewport).animate({ opacity: 100 }, me._fade_duration);
        });

    } else {
        this._moveToWorker(index, transitions);
    }
}

/**
* Moves the viewport to the point at the given index with no fade in/out transitions. Called from _moveTo
* 
* @param {int} 	index
* @param {bool}	transitions
*/
Pager.prototype._moveToWorker = function(index, transitions) {
    if (this._items.length == 0) return;

    // Point the viewport to the new item
    $(this._viewport).scrollTo(this._items[index], 0, { axis: 'x' });

    // If the viewport_fix_height flag is set, fix the viewport 
    // height based on the height of the current scroll item
    if (this._viewport_fix_height) {
        if (transitions) {
            $(this._viewport).animate({ height: $(this._items[index]).height() }, this._viewport_fix_height_duration);
        } else {
            $(this._viewport).height($(this._items[index]).height());
        }
    }
}

