/**
 * jQuery jqGridCustomPager - v1.03
 * @author Jonathan Franks
 * 
 * Binds a custom pagination to a jqGrid. Built for Hach pagination design.
 * Code may be modified if other design needs require changes. Depends on jqGrid and hence
 * jQuery and jQuery UI libraries.
 * 
 * Usage: The element that is used for the jqGrid must be wrapped inside another div. 
 * This div acts as a container for the grid and all the pagination elements you wish to have. 
 * Certain elements with classes are needed inside your pagination containers as well. 
 * Here is an example of the recommended HTML structure:
 * 
 * <div class="gridContainer">
 *     <div class="resultsToolbar">
 *         <div class="right">
 *             <div class="pagination">
 *                 <input class="buttons previous" type="button" value="&lt;" /> 
 *                 <span>page</span>
 *                 <input type="text" class="pageNumber" maxlength="3" />
 *                 <span>of <span class="pageCount">1</span></span>
 *                 <input class="buttons next" type="button" value="&gt;" />
 *             </div>
 *         </div>
 *     </div>
 *     <table id="gridTable"><tr><td></td></tr></table>
 *     <div class="resultsToolbar">
 *         <div class="right">
 *             <div class="pagination">
 *                 <input class="buttons previous" type="button" value="&lt;" /> 
 *                 <span>page</span>
 *                 <input type="text" class="pageNumber" maxlength="3" />
 *                 <span>of <span class="pageCount">1</span></span>
 *                 <input class="buttons next" type="button" value="&gt;" />
 *             </div>
 *         </div>
 *     </div>
 * </div>
 * 
 * The classes that are important are:
 * 
 * - next, previous classes for the next/previous function events
 * - pageNumber class on an input element for an editable field that displays the current page
 * - pageCount class on a text element to display the maximum number of pages
 * 
 * To call the plugin, use a selector on the grid you want to have the pagination binded to and call 
 * the function inside the loadComplete event of the grid. Normally, you can just use jQuery(this):
 * 
 * jQuery('#gridTable').jqGrid({
 *     ...
 *     loadComplete: function() {
 * 
 *         jQuery(this).jqGridCustomPager();
 * 
 *     },
 *     ...
 * });
 * 
 * This plugin expects to be called on the loadComplete of the jqGrid it is binded to. 
 * For now, there are no configuration options available to pass to this plugin as parameters.
 * 
 * If you need to recalculate the pagination variables because of a results per page option or other reason, 
 * you may invoke the recalculatePagination method like this:
 * 
 * jQuery('#gridTable').jqGridCustomPager('recalculatePagination');
 * 
 * This will recalc all the pagination values.
 * 
 */
(function(jQuery) {
    
    var namespace = 'jqGridCustomPager';
    var gridIdPrefix = 'gbox_';
    
    var loadPage = function(gridId, page) {
        if (isNaN(page)) {
            page = 1;
        }
        
        var currentPage = parseInt(page);
        var highestPage = jQuery('#' + gridId).data(namespace)[gridId].highestPage;
        var lowestPage = jQuery('#' + gridId).data(namespace)[gridId].lowestPage;
        var currentElement = jQuery('#' + gridId).data(namespace)[gridId].currentElement;
        var grid = jQuery('#' + jQuery('#' + gridId).data(namespace)[gridId].gridId);

        if (currentPage < lowestPage) {
            currentPage = lowestPage;
        }
        
        if (currentPage > highestPage) {
            currentPage = highestPage;
        }
        
        if (grid.getGridParam('page') != currentPage) {
            grid.setGridParam({'page': currentPage});
            grid.trigger('reloadGrid');
        }
        
        currentElement.find('input.pageNumber').val(currentPage);
        
        jQuery('#' + gridId).data(namespace)[gridId].currentPage = currentPage;
    };
    
    var evalPaginationButtonsState = function(gridId) {
    	var currentPage = jQuery('#' + gridId).data(namespace)[gridId].currentPage;
        var highestPage = jQuery('#' + gridId).data(namespace)[gridId].highestPage;
        var lowestPage = jQuery('#' + gridId).data(namespace)[gridId].lowestPage;
        var currentElement = jQuery('#' + gridId).data(namespace)[gridId].currentElement;

        if (currentPage <= lowestPage || currentPage > highestPage) {
            currentElement.find('input.previous').attr('disabled', 'disabled').removeClass('button').addClass('disabledButton');
        } else {
            currentElement.find('input.previous').removeAttr('disabled').removeClass('disabledButton').addClass('button');
        }
        
        if (currentPage >= highestPage) {
            currentElement.find('input.next').attr('disabled', 'disabled').removeClass('button').addClass('disabledButton');
        } else {
            currentElement.find('input.next').removeAttr('disabled').removeClass('disabledButton').addClass('button');
        }
    };
    
    var bindPaginationEvents = function(gridId) {
        var currentElement = jQuery('#' + gridId).data(namespace)[gridId].currentElement;
        
        currentElement.find('input.previous').bind('click.' + namespace, function() {
            loadPage(gridId, jQuery('#' + gridId).data(namespace)[gridId].currentPage - 1);
        });
        
        currentElement.find('input.next').bind('click.' + namespace, function() {
            loadPage(gridId, jQuery('#' + gridId).data(namespace)[gridId].currentPage + 1);
        });
        
        currentElement.find('input.pageNumber')
            .bind('blur.' + namespace, function() {
            	if (jQuery(this).val() != "") {
            		loadPage(gridId, jQuery(this).val());
            	}
            })
            .bind('keydown.' + namespace, function(e) {
            	if (e.keyCode == 13) {
            		if (jQuery(this).val() != "") {
            			loadPage(gridId, jQuery(this).val());
            		}
                } else {
                    if (e.keyCode < 8 || e.keyCode > 9) {
                        if ((e.keyCode < 33 || e.keyCode > 57)) {
                            if (e.keyCode < 96 || e.keyCode > 105) {
                                return false;
                            }
                        }
                    }
                }
            });
    };
    
    var getHighestPage = function(grid) {
        
        var highestPage = 0;
        
        for (var i = 0; i < grid.getGridParam('records'); i+=grid.getGridParam('rowNum')) {
            highestPage++;
        }
        
        return highestPage;
        
    };
    
    var methods = {
        init: function(options) {
            
            var settings = {
            };
            
            if (options) {
                jQuery.extend(settings, options);
            }
            
            return this.each(function() {

            	var grid = jQuery(this);
                var gridId = grid.attr('id');
                
                // Only execute init method on first run - otherwise, simply recalculate the pagination in case the dataset was changed.
                if (typeof jQuery('#' + gridId).data(namespace) != "undefined" && typeof jQuery('#' + gridId).data(namespace)[gridId] != 'undefined' && jQuery('#' + gridId).data(namespace)[gridId].initCompleted) {
                	grid.jqGridCustomPager('recalculatePagination');
                	return;
                }
                
                jQuery('#' + gridId).data(namespace, {});
                grid.jqGridCustomPager('recalculatePagination');
                
                bindPaginationEvents(gridId);
                loadPage(gridId, 1);
            });
            
        },
        goToFirstPage: function() {
        	return this.each(function() {
        		loadPage(jQuery(this).attr('id'), 1);
        	});
        },
        recalculatePagination: function() {
            
            var grid = jQuery(this);
            var gridId = grid.attr('id');
            var currentElement = jQuery('#' + gridIdPrefix + gridId).parent();
            
            jQuery('#' + gridId).data(namespace)[gridId] = {
                currentPage: grid.getGridParam('page'),
                lowestPage: 1,
                highestPage: 0,
                gridId: gridId,
                currentElement: currentElement,
                initCompleted: true
            };
            
            var highestPage = getHighestPage(grid);
            
            // If only 1 page, hide the results toolbar. If the data in the grid changes, this method should be called again manually.
            if (highestPage <= 1) {
                currentElement.find('div.resultsToolbar').hide();
            } else {
                currentElement.find('div.resultsToolbar').show();
            }
            
            jQuery('#' + gridId).data(namespace)[gridId].currentElement.find('span.pageCount').text(highestPage);
            jQuery('#' + gridId).data(namespace)[gridId].highestPage = highestPage;
            
            evalPaginationButtonsState(gridId);
            
        },
        unload: function() { // Unloads the pager (Keeps HTML structure but unbinds all events)
            return this.each(function() {
                
                var grid = jQuery(this);
                var gridId = grid.attr('id');
                
                // Unbind pagination control events
                jQuery('#' + gridIdPrefix + gridId).parent().find('input.previous').unbind('click.' + namespace);
                jQuery('#' + gridIdPrefix + gridId).parent().find('input.next').unbind('click.' + namespace);
                jQuery('#' + gridIdPrefix + gridId).parent().find('input.pageNumber').unbind('blur.' + namespace).unbind('keydown.' + namespace);
                
            });
        }
    };
    
    jQuery.fn.jqGridCustomPager = function(method) {

        if (methods[method]) {
            return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
        } else if (typeof method === 'object' || !method) {
            return methods.init.apply(this, arguments);
        } else {
            jQuery.error('Method ' + method + ' does not exist on jQuery.jqGridCustomPager');
        }

    };
    
})(jQuery);
