/**	
 * Pagination jQuery Plug-in - Compatible with jQuery-1.2.6.js
 *
 * @author Karl Stanton (Fi)
 * @version 1.0 
 * @version 1.1 (Now with added AJAX & Search Results Toggling options!)
 * @version 1.2 jsLint Verified!
 * @version 2.0 goAjax method refactored and jsLint Verified
 * 
 * 
 * Usage: 
 *   - Create a containing div (parent) filled with child elements that will be paginated (visible / invisible).
 *   - If you have chosed an AJAX options, ensure you have double the amount of children than your perpage setting present on page load
 *   - If you have other child elements within the container that don't require pagination, then please use the options (documented below)
 *   - In a 'document ready' function, execute the plug in as follows:
 *   
	$(document).ready(function () {
		// Without defining options (will paginate all children)
		$("[element containing children]").pagination();

		// Defining options
		$("[element containing children]").pagination({
			perpage:4, [number of children to paginate per page]
			pgmenu: [pre-made pagination menu as #object],							ie: "#menu"
			children: [exclusive children to paginate as #object]					ie: ".paginate-these",
			showcounter: [do we want to show the numbers between the prev/next buttons?],
			counterstyle: [0, 1, 2, 3]													ie: [1,2,3,4,5 .... or .... 1 of 5] .... or dots (o o o)   or include a toggler
			num_counters: [number of total page counters excluding start & end]		ie: 1...5,6,7,8,9...15,
			animation: [show fade or not],
			ajax: [URL of web service to hit, if this is set, it kicks into ajax mode],
			results: [starting result amount set on page load],
			limitcap: [max limit of pages to show at once in the toggler]
			restrictwidth: [force the width of pagination],	
			restrictheight: [force the height of pagination],
			textdetails: [span of view 1 of 50 etc],
			childmultiplier: [number of "true" elements being shown per paginated object]
		});

	});
 * 
 *
 * Order of programming execution:
 *   - Loop through all matched elements and bind variables & functions
 *	 - Read options and create pagination menu if not specified
 *	 - Bind event listeners to buttons & links (if specified in options)
 *     - When events are triggered, parse in itself via the {events} object to ensure multiple instances can exist on one page
**/
/* To help when using JSLint
var $ = {};
var jQuery = {};
var EA = {};
var config = {};
var Framework = {};*/

(function ($) {
	$.fn.pagination = function ($options) {

		// set default options
		var $defaults = {
			css: 'pg',					// The CSS Class that relates to all pagination items
			perpage: 2,					// The default amount of items to be paginated
			pgmenu: null,				// The pagination menu, if not specified, it is created below the parent (el) div
			children: null,				// Used for specific pagination items only, incase the user doesn't want to paginate all items in the parent (el) div
			showcounter: true,			// Do we want to show the numbers links
			counterstyle: 0,			// 		- if so, do we show as (0)  1,2,3,4,5"     or   (1)   "1 of 5"     or (2)  "o (o) o"     or (3)  include toggle
			num_counters: 5,			// Default number of pagination numbers to show. With the current page in between 2 at start, and 2 at end.
			animation: true,			// Default to show animation
			ajax: null,					// Are we relying on AJAX?
			results: null,				// Defined by global page property object
			limitcap: 35,				// Max limit of pages to show at once
			restrictwidth: false,		// do we force the width of the pagination?
			restrictheight: false,		// do we force the height of the pagination?
			textdetails: null,			// element of the textual representation, can be a group of elements
			equalizecols: null,			// equalize heights of the following elements in the page content
			childmultiplier: 1,			// will multiply the values of "1 of 5" if we are corcerned about the children of what's being paginated
			viewall: false,				// are we viewing all of the results at once via toggle dropdown?
			addDividers: false,			// do we want dividing lines between rows of results? 
			dividingClass: 'hdivider',	// default styleclass to apply for dividing lines
			dividingItemsPerRow: null,	// if the number of items per row is other than the default perpage value, use this variable to set the correct amount 
			defperpage: null			// default perpage, used when resizing toggle view
		};
		
		// extend the options
		var $opts = jQuery.extend($defaults, $options);
		
		// bring the options to the pagination object
		for (var i in $opts) {
			if (i) {
				jQuery.pagination[i]  = $opts[i];
			}
		}

		return this.each(function () {
			var el = jQuery(this);

			// Turn our options passed in into an object
			var opts = jQuery.meta ? jQuery.extend({}, $opts, el.data()) : $opts;
			opts.el = el;
	
			// Did the user pass in an exclusive selection of elements to paginate?
			var items = el.children();
			if (opts.children !== null) {
				items = el.find(opts.children);
			}

			// Default our child properties, and perform our multiplyer calculation
			// We do this here as some search results may return less children than
			// what is required to kick start pagination
			opts.num_children = items.length;
			opts.childrencount = opts.results / opts.childmultiplier;
			opts.results = Math.ceil(opts.results / opts.childmultiplier);
			
			// If we're running AJAX, update our default properties
			if (opts.ajax) {
				// Remove previously used query string variables &i= &s= &c=
				opts.ajax = opts.ajax.split("&i=");
				opts.ajax = opts.ajax[0];
				opts.num_children = opts.results;
				opts.pagesLoaded = [1, 2];
				opts.pageOffset = 0;
			}
			
			// If the user didn't specify a container div, create one, else use their own
			var pgmenu, moreLink;
			if (opts.pgmenu === null) {
				pgmenu = jQuery('<div class="' + opts.css + '_menu"></div>');
				el.after(pgmenu);
				moreLink = null;
			} else {
				pgmenu = jQuery(opts.pgmenu);
				if (pgmenu.children("a")) {
					moreLink = pgmenu.children("a");
				}
			}
			
			// Decide if we need pagination 
			if (opts.num_children > opts.perpage || opts.counterstyle === 3) {
				
				el.addClass("has-pagination");
				pgmenu.addClass("pagination").removeClass("no-pagination");
				pgmenu.empty();
					
				// Start creating the menu
				var div = jQuery("<div class=\"" + opts.css + "_contents\"></div>");
				jQuery(pgmenu).append(div);
				//var div = jQuery(pgmenu).children("." + opts.css + "_contents");

				// Create the buttons
				var next_but = jQuery("<span class=\"" + opts.css + "_next\"></span>");
				var prev_but = jQuery("<span class=\"" + opts.css + "_prev\"></span>");
				
				// Add the previous button first
				div.append(prev_but);

				// Create our pagination container object
				var pages = jQuery('<div class="' + opts.css + '_pagenums"></div>');
				
				// Get the number of pages
				if (opts.viewall) {
					opts.num_pages = 1;
				}else{
					opts.num_pages = Math.ceil(opts.num_children / opts.perpage);					
				}
								
				// Create an object full of our options that we will pass to the element for retrieval later
				var options = {
					num_pages: opts.num_pages,
					pages: pages[0],
					pgmenu: pgmenu[0],
					index: 1,
					last_index: 1,
					next_page: null,
					next_but: next_but,
					prev_but: prev_but,
					current_page: opts.perpage,
					inprogress: false,
					contents: div,
					childmultiplier: opts.childmultiplier			
				};
				
				// We want our index counter to be the center of the previous / next pages ... 10, 11, 12, [13], 14, 15, 16 ... 
				// So, start our index at (num_counters / 2) before the requested
				opts.padding = Math.floor(opts.num_counters / 2);

				// Default our toggle ID and what our next page is
				opts.toggleid = el.attr("id") + "-toggle";
				opts.to = opts.perpage;
				
				if (opts.defperpage === null) {
					opts.defperpage = opts.perpage;
				}
				opts.items = items;
				
				// Compile our default options into a single array and bind to the element
				for (var i in opts) {
					if (i !== null) {
						options[i] = opts[i];
					}
				}
				jQuery.data(el.get(0), "options", {	options: options });
				
				// Now add the next button
				div.append(next_but);
				
				// --- EA.com specific ---
				// Prev But / Next But = 25px wide = 50px
				var widthFormula = 50;
				
				// Show the counter (by default, it's true, user can turn off via options;
				if (opts.showcounter) {
					switch (opts.counterstyle) {
					// The default switch is 0 for "1,2,3,4,5"
					case 0:
						prev_but.after(pages);
						// Build the page numbers
						jQuery.pagination.buildPageNumbers(el, 1);
						// Set the width
						if (!opts.restrictwidth) {
							// --- EA.com specific ---
							// Page = 24px l/r padding = 20px
							widthFormula += 20;
							widthFormula += 22 * jQuery(pages).children().length;
							if ($.browser.msie && $.browser.version < 7) {
								widthFormula += 22;
							}
							div.width(widthFormula);
						}
						jQuery.pagination.textDetails(opts);
						break;
					case 1:
						// If the user passed in 1, we show the numbers like "1 of 5"
						next_but.after(pages);
						pages.append("<span>1</span> of <span>{0}</span>".localize([opts.num_pages]));
						break;
					case 2:
						// This builds the dot system as outlined by EA.
						prev_but.after(pages);
						pgmenu.addClass("dots");
						jQuery.pagination.buildDots(el);
						if (!opts.restrictwidth) {
							// --- EA.com specific ---
							// Page = 5px l / 10px r padding = 15px
							widthFormula += 15;
							widthFormula += 12 * jQuery(pages).children().length;
							if ($.browser.msie && $.browser.version < 7) {
								widthFormula += 5;
							}
							div.width(widthFormula);
						}
						
						// Add the more link back
						pgmenu.prepend(moreLink);
						break;
					case 3:
						// List shows a textual representation of pages "Viewing 1-4 of 20" and a toggle menu
						prev_but.after(pages);
						
						// Decide if we need pagination now that we've adjusted the per page value
						if (opts.num_children <= opts.perpage) {
							div.hide();
						} else {
							div.show();
							// Build the page numbers
							jQuery.pagination.buildPageNumbers(el, 1);
						}

						pgmenu.prepend("<div class=\"" + opts.css + "-toggle drop-default\" id=\"" + opts.toggleid + "\"><span></span></div>");
						pgmenu.prepend("<div class=\"" + opts.css + "-search-counter\"></div>");

						// Set default drop text
						jQuery.pagination.toggleTextSet(opts);
						
						if (opts.num_children < opts.perpage || opts.num_children === opts.defperpage) {
							jQuery("#" + opts.toggleid).hide();
						}

						// Create the search options array, and push our first value into it
						i = opts.defperpage;
						opts.searchoptions = [];
						
						// Append other search options to the array
						for (var j = 1; j <= Math.floor(opts.results / opts.defperpage); j++) {
							if ((j * opts.defperpage) < opts.limitcap) {
								opts.searchoptions.push(j * opts.defperpage);
							}
						}
						if (opts.searchoptions.length === 1) {
							opts.searchoptions.push("View All".localize());
						}
						
						// Set initial textual representation
						jQuery.pagination.textDetails(opts);
						pgmenu.append("<div class=\"drop-default_wrapper\">");

						// Openstate is the pagination menu
						var openstate = jQuery(opts.pgmenu).find(".drop-default_wrapper");
						openstate.empty();
						
						// Now create the list of search options
						var list = jQuery("<ul class=\"drop-default\">");					

						var viewall = false;
						var n;
						for (i = 0; i < opts.searchoptions.length; i++) {
							var li;
							// Add the text
							if (opts.searchoptions[i] === "View All".localize()) {
								li = jQuery("<li><span>" + "View All".localize() + "</span></li>");
								n = opts.results;
								viewall = true;
							} else {
								viewall = false;
								n = opts.searchoptions[i];
								if (i === 0) {
									li = jQuery("<li><span style=\"border-top: 0px;\">" + "Show {0} at a time".localize([n]) + "</span></li>");
								} else if (i > 0) {
									li = jQuery("<li><span>" + "Show {0} at a time".localize([n]) + "</span></li>");
								}
							}
							// Bind the rollover, rollout and click events. We add the rollover, rollout events to settle a beef with IE6 not liking :hover on non anchor objects
							var curr_li;
							(function () {
								li.mouseover(function () {
									li = list.children("li");
									curr_li = jQuery(li).index(this);
									if (curr_li === 0) {
										jQuery(this).addClass("first_hover");
									} else {
										jQuery(this).addClass("hover");
									}
								}).mouseout(function () {
									jQuery(this).removeClass("first_hover").removeClass("hover");
								}).bind("click", {
										to: n,
										v: viewall,
										el: el
									}, jQuery.pagination.toggleSearch);
								list.append(li);
							}());
						}
						openstate.append(list);

						// Event for the pagination menu
						jQuery("#" + opts.toggleid).bind("click", function (e) {

							// Build search open state containing number of search results passed in by options
							jQuery.pagination.toggleClose(el);
							jQuery("#" + opts.toggleid).addClass("drop-default_on");

							// Move the list to the appropriate position
							var left;
							if ($.browser.msie && $.browser.version < 7) {
								left = -2;
							} else {
								left = jQuery("#" + opts.toggleid).position().left - 2;
							}
							openstate.css({
								left: left,
								top: jQuery("#" + opts.toggleid).position().top + jQuery("#" + opts.toggleid).outerHeight() - 2,
								position: 'absolute',
								visibility: 'visible'
							});
							
						}).mouseover(function () {
							$(this).addClass("drop-default_hover");
						}).mouseout(function () {
							$(this).removeClass("drop-default_hover");
						});
						
						// Event for closing pagination menu, attached to the window object
						$(document).click(function (e) {
							if (e.target.nodeName !== "SPAN") {
								jQuery.pagination.toggleClose(el);
							}
						});							


						jQuery("#ajax-loader").remove();
						
						if (!opts.restrictwidth) {
							// Page = 10px l/r padding = 20px
							widthFormula += 20;
							widthFormula += 22 * jQuery(pages).children().length;
							if ($.browser.msie && $.browser.version < 7) {
								widthFormula += 24;
							}
							div.width(widthFormula);
						}
						
						//show dividers between rows
						if (opts.addDividers) {
							jQuery.pagination.showDividers(opts);
						}
						
						// Show the element again
						jQuery(el).show();
						
						break;
					}
				}
				
				// Make objects out of our new buttons and bind our functions
				next_but.click(function () {
					jQuery.pagination.next(el);
				}).mouseover(function () {
					jQuery(this).addClass(opts.css + "_next_hover");
				}).mouseout(function () {
					jQuery(this).removeClass(opts.css + "_next_hover");
				});
				prev_but.addClass(opts.css + "_prev_disabled").click(function () {
					jQuery.pagination.prev(el);
				}).mouseover(function () {
					jQuery(this).addClass(opts.css + "_prev_hover");
				}).mouseout(function () {
					jQuery(this).removeClass(opts.css + "_prev_hover");
				});
				
				jQuery(pgmenu).append("<div class=\"clear\"></div>");
		
				// Now paginate the defaults
				items.hide().filter(":lt(" + opts.perpage + ")").show();
		
				if (options.restrictheight) {
					var h = el.outerHeight(true);
					if (h > 0) {
						el.height(h);
					} else {
						el.height("auto");
					}
				}
				
				//if IE6 pop paging menu out from DOM and insert it again to make it position correctly
				if (jQuery.browser.msie) {
					if (parseInt(jQuery.browser.version) == 6) {
						if ($(opts.pgmenu).find(".pg_contents").children() !== null) {
							$(opts.pgmenu).find(".pg_contents").children().css("position", "absolute");
							$(opts.pgmenu).find(".pg_contents").children().css("position", "static");
						}
					}
				}
				
			} else {
				// Remove the pagination class
				jQuery(opts.pgmenu).empty().removeClass("pagination");
				// Add the more link back
				pgmenu.prepend(moreLink);
				jQuery.pagination.textDetails(opts);
				items.show();
			}
		});
	};
	
	// Public Functions
	$.extend({pagination : {
	
		// Builds the page numbers including truncation
		buildPageNumbers : function (el, index) {
			var opts = el.data("options").options;

			// Example. [1, 2 ... 10, 11, 12 ... 34, 35]
			
			var n = opts.num_pages - opts.num_counters;
			
			// First, clear page numbers (animation?)
			jQuery(opts.pages).empty();
			
			// If our current index is less than our desired amount of counters on each side, just show from the 1st page until our num_counter
			if (n <= opts.padding) {
				for (var i = 1; i <= opts.num_pages; i++) {
					jQuery(opts.pages).append(jQuery.pagination.pageLink(el, i, index));
				}
			} else {
				// If our page number is less than the number of counters, load em up
				if (index < opts.num_counters) {
					for (i = 1; i <= opts.num_counters; i++) {
						jQuery(opts.pages).append(jQuery.pagination.pageLink(el, i, index));
					}
				}
				// Else, we must show the 1st page, some truncation "..." and then the num_counter amount of counters
				else if (index > n) {
					// Show the 1st page
					jQuery(opts.pages).append(jQuery.pagination.pageLink(el, 1)).append("<span>...</span>");
					for (i = n; i <= opts.num_pages; i++) {
						jQuery(opts.pages).append(jQuery.pagination.pageLink(el, i, index));
					}
				} else {
					// Show the 1st page
					jQuery(opts.pages).append(jQuery.pagination.pageLink(el, 1)).append("<span>...</span>");
					i = index - opts.padding;
					// And loop until 3 pages after (making sure we're less than the total amount of pages, of course)
					while (i < (index + opts.num_counters - opts.padding) && (i < opts.num_pages)) {
						jQuery(opts.pages).append(jQuery.pagination.pageLink(el, i, index));
						i++;
					}
				}
				// Add the last page
				if (i < opts.num_pages) {
					jQuery(opts.pages).append("<span>...</span>").append(jQuery.pagination.pageLink(el, opts.num_pages));
				}
			}
		},
	
		// Creates a link object and returns it
		pageLink : function (el, i, index) {
			var link = jQuery("<span>" + i + "</span>").bind("click", {to: i, el: el}, jQuery.pagination.num);
			if (i === index) {
				link.addClass("bold");
			} else {
				link.addClass("underline");
			}
			return link;
		},
	
		// Builds the dot version of the pagination
		buildDots : function (el) {
			var opts = el.data("options").options;
			for (var i = 1; i <= opts.num_pages; i++) {
				var span = jQuery("<span class=\"pg" + i + "\"></span>");
				if (i === 1) {
					span.addClass("on");
				}
				span.bind("click", {
					to: i,
					el: el
				}, jQuery.pagination.num);
				jQuery(opts.pages).append(span);
			}
		},
	
		// Shuffles dot classes
		setDots : function (el, index) {
			var opts = el.data("options").options;
			jQuery(opts.pages).children().removeClass("on").end().children(":eq(" + (index - 1) + ")").addClass("on");
		},
		
		// Updates the text notification elements
		textDetails : function (opts) {

			// Results
			var results = opts.childrencount * opts.childmultiplier;
			// Textual love.
			var resultsText = "";//results".localize();
			if (results === 1) {
				//resultsText = "result".localize();
			}
			
			// From 
			var from;
			// Set to 1 if undefined
			if (opts.from === null || opts.from === undefined) {
				from = 1;
			// Or set it to what our option is set too
			} else {
				from = opts.from;
			}
			// If we're not on the first page, times by our multiplyer and add 1 (so its the first item of the set, not the last)
			if (from > 1) {
				from = (from * opts.childmultiplier) + 1;
			} else if (from === 0) {
				from = 1;
			}
				
			// To
			var to;
			// Set to the amount of results if undefined
			if (opts.to === null || opts.to === undefined) {
				to = results;
			// Or to what our option is set too
			} else {
				to = opts.to;
			}
			if ((to * opts.childmultiplier) > results) {
				to = results;
			} else {
				to = to * opts.childmultiplier;
			}
			var textDetails;
			if (results > 0) {
				textDetails = "Viewing <strong>{0}-{1}</strong> <span>of</span> <strong>{2}</strong> <span>{3}</span>".localize([from,to,results,resultsText]);
			} else {
				textDetails = "No results found.".localize();
			}

			// Apply Text Details
			if (opts.textdetails !== null) {
				jQuery(opts.textdetails).each(function () {
					jQuery(this).html(textDetails);
				});
			} else {
				// If we have made our own, apply text there also
				if (jQuery("." + opts.css + "-search-counter")) {
					jQuery("." + opts.css + "-search-counter, .paginationResults").html(textDetails);
				}
			}
		},

		// Set the textaul representation on the toggle drop
		toggleTextSet : function (opts) {

			if (opts.viewall) {
				jQuery("#" + opts.toggleid + " span").text("View All".localize());
			} else {
				jQuery("#" + opts.toggleid + " span").html("Show {0} at a time".localize([opts.to]));
			}

		},

		// Kicks off the pagination search based on toggle selection
		toggleSearch : function (e) {
			var el = e.data.el;
			
			jQuery.pagination.toggleClose(el);
			var opts = el.data("options").options;
			var to;
			opts.to = e.data.to;
			opts.viewall = e.data.v;
			
			jQuery.pagination.toggleTextSet(opts);
			
			if (opts.perpage !== opts.to) {
				// Reload pages via ajax
				if (opts.ajax) {
					if (opts.to < opts.results) {
						to = opts.to * 2;
					} else {
						to = opts.to;
					}
					jQuery.pagination.callAjax(el, 1, to, "toggle");
				} else {
					// Load new pagination
					jQuery.pagination.reload(el, opts);		
				}
			} else {
				jQuery.pagination.toggleClose(el);
			}
		},

		// Closes the toggle
		toggleClose : function (el) {
			var opts = el.data("options").options;
			// Reset Styles
			jQuery("#" + opts.toggleid).removeClass("drop-default_on");
			jQuery(opts.pgmenu).find(".drop-default_wrapper").css({visibility: 'hidden'});
			opts.open = false;
		},

		// Load new pagination
		reload : function (el, opts) {
			jQuery(opts.pgmenu).empty();
			el.pagination({
				css: opts.css,
				perpage: opts.to,
				pgmenu: opts.pgmenu,
				children: opts.children,
				showcounter: opts.showcounter,
				counterstyle: opts.counterstyle,
				num_counters: opts.num_counters,
				animation: opts.animation,
				ajax: opts.ajax,
				results: opts.results,
				limitcap: opts.limitcap,
				defperpage: opts.defperpage,
				restrictwidth: opts.restrictwidth,
				restrictheight: opts.restrictheight,
				textdetails: opts.textdetails,
				childmultiplier: opts.childmultiplier,
				viewall: opts.viewall,
				addDividers: opts.addDividers,
				dividingClass: opts.dividingClass,
				dividingItemsPerRow: opts.dividingItemsPerRow
			});
		},

		// Clicked a page number
		num : function (e) {
			var opts = e.data.el.data("options").options;
			jQuery.pagination.gotopage(e.data.el, e.data.to, opts.animation);
		},
		
		// Move to a direct index
		gotopage : function (el, i, ani) {
			var opts = el.data("options").options;
			var next_page = parseInt(i, 10) * opts.perpage;
			if (next_page !== opts.current_page && !opts.inprogress) {
				next_page = parseInt(i - 1, 10) * opts.perpage;
				opts.from = next_page;
				opts.to = next_page + opts.perpage;
				opts.current_page = parseInt(i, 10) * opts.perpage;
				opts.index = opts.current_page / opts.perpage;
				jQuery.pagination.go(el, ani);
			}
		},
		
		// Next Button
		next : function (el) {
			var opts = el.data("options").options;
			var next_page = opts.current_page + opts.perpage;
			if (opts.last_index < opts.num_pages && !opts.inprogress) {
				opts.from = opts.current_page;
				opts.to = next_page;
				opts.current_page = next_page;
				opts.index++;
				jQuery.pagination.go(el, opts.animation);
			}
		},
		
		// Previous Button
		prev : function (el) {
			var opts = el.data("options").options;
			var next_page = opts.current_page - opts.perpage;
			if (next_page > 0 && !opts.inprogress) {
				opts.from = next_page - opts.perpage;
				opts.to = next_page;
				opts.current_page = next_page;
				opts.index--;
				jQuery.pagination.go(el, opts.animation);
			}
		},
		
		// Slices the pagination in a bracket - from a start position, to end position
		go : function (el, ani) {
			var opts = el.data("options").options;
			// If we're running AJAX
			if (opts.ajax) {
				// Make the ajax call
				jQuery.pagination.goAjax(el);
			}
			// Else just paginate
			else {
				if (ani) {
					opts.items.hide().slice(opts.from, opts.to).fadeIn("fast");
				} else {
					opts.items.hide().slice(opts.from, opts.to).show();
				}
				jQuery.pagination.update(el);
			}
			jQuery.pagination.setWidth(opts);
		},
		
		// Sorts an array into proper numerical order 1,2,10,20
		sortNumber : function (a, b) {
			return a - b;
		},
		
		/**
		 * The AJAX function. The primary location for all AJAX based communication
		 * used heavily by:
		 * 		jQuery.pagination.goAjax
		 * 		jQuery.pagination.toggleSearch
		 * @param {Object} el 				- The Pagination element
		 * @param {Object} ajaxFrom			- The starting index of the page query
		 * @param {Object} ajaxTo			- The amount of pages to return
		 * @param {Object} successFunction	- What to do on success
		 * @param {Object} domFrom			- Starting point of the DOM elements (opts.items)
		 * @param {Object} domTo			- The end point of the DOM elements (opts.items)
		 */
		callAjax : function (el, ajaxFrom, ajaxTo, successFunction, domFrom, domTo) {
			var opts = el.data("options").options;

			// For finding out if we're the beginning or end of a pre-existing query
			var start = "?";
			if (opts.ajax.indexOf("?") > 0) {
				start = "&";
			}
			
			// hecking: don't know what else to do. BT
			var sort = '';
			if (opts.ajax.indexOf("sort") < 1) {
				sort = 'sort=' + $("#browserResultsDrop").val() + '&';
			}
			
			// Building the URL to include the parsed in HREF, to and from values, and a cache buster
			var url = opts.ajax + start + sort + "i=" + ajaxFrom + "&s=" + ajaxTo + "&c=" + parseInt(Math.random() * 99999999, 10);

			// The almighty AJAX function
			jQuery.ajax({
				type: "GET",
				url: url,
				beforeSend: function () {
					opts.inprogress = true;
					switch (successFunction) {
					case "c":
					case "d":
					case "toggle":
						Framework.prototype.paginationBeforeAjax(el);
						break;
					}
				},
				success: function (data) {
				
					switch (successFunction) {
				
					// Adds data to back of pack
					case "a":
						el.append(data);
						
						// Container div of results passed back by EA. Remove this.
						$("#paginationResultCount").remove();
			
						opts.pagesLoaded.push(opts.index + 1);
						jQuery.pagination.endAjax(el, 1);					
						break;
				
					// Adds to the front of the pack
					case "b":
						el.prepend(data);
						$("#paginationResultCount").remove();
						opts.pagesLoaded.unshift(opts.index - 1);
						opts.pageOffset = 1;
						jQuery.pagination.endAjax(el, 1);
						break;
					
					// Adds in Previous, Current, Next Pages moving Forward
					case "c":
						jQuery.pagination.endAjax(el, 1);

						el.html(data);
						
						// Container div of results passed back by EA. Remove this.
						$("#paginationResultCount").remove();
						el.children().hide().slice(opts.perpage, domTo).show();

						if (opts.index < opts.num_pages) {
							// Previous - Current - Next
							opts.pagesLoaded = [opts.index - 1, opts.index, opts.index + 1];
						} else if (opts.index === opts.num_pages) {
							// Previous - Current
							opts.pagesLoaded = [opts.index - 1, opts.index];
						}
						opts.pageOffset = 1;
						break;
						
					// Adds in Previous, Current, Next Pages moving Backward
					case "d":
						jQuery.pagination.endAjax(el, 1);

						// Clear the div, load in 3 surrounding elements
						el.html(data);
						$("#paginationResultCount").remove();
						el.children().hide().slice(domFrom, domTo).show();

						if (opts.index === 1) {
							opts.pagesLoaded = [opts.index, opts.index + 1];
							opts.pageOffset = 0;									
						} else if (opts.index > 1 && opts.index < opts.num_pages) {
							opts.pagesLoaded = [opts.index - 1, opts.index, opts.index + 1];
							opts.pageOffset = 1;
						}
						break;
					case "toggle":
						el.html(data);
						$("#paginationResultCount").remove();
						jQuery.pagination.reload(el, opts);
						break;
					}
					if (successFunction !== "toggle") {
						//show dividers between rows
						if (opts.addDividers) {
							jQuery.pagination.showDividers(opts);
						}
					}
				},
				error: function () {
					opts.inprogress = false;
					Framework.prototype.paginationAjaxError(el);
				}
			});
		},
		
		/**
		 * Used by jQuery.pagination.goAjax
		 * 
		 * Returns DOM elements to slice based on current offset and what pages have
		 * been loaded. This means real DOM indexes, NOT pagination indexes
		 * @param {Object} opts - Element Options
		 */
		returnDOMSlice : function (opts) {
			opts.pagesLoaded.sort(jQuery.pagination.sortNumber);
			opts.pageOffset = jQuery.inArray(opts.index, opts.pagesLoaded);
			var domFrom = opts.pageOffset * opts.perpage;
			var domTo = domFrom + opts.perpage;
			// Return our offset as groups of elements [domFrom, domTo]
			return [domFrom, domTo];
		},
				
		/**
		 *  The core of the pagination method.
		 *  
		 *  Here I am using 4 types of user interactivity, with the last 2 examples having a subset function each
		 *  
		 *  a = Paginating page-by-page in a forward direction:  1,2,3
		 *  b = Paginating page-by-page in a backward direction: 3,2,1
		 *  c = Paginating by page jumping in a forward direction:   Was page 1, jumped to page 10
		 *  	- Subset function: If user jumps forward, and the page has already loaded, it will determine whether to load another page
		 *  		- Uses function "a"
		 *  d = Paginating by page jumping in a backward direcetion: Was page 10, jumped to page 1
		 *  	- Subset function: If user jumps backward, and the page has already loaded, it will determine whether to load another page
		 *  		- Uses function "b"
		 *	@param {Object} el - Pagination Element
		 */
		goAjax : function (el) {

			var opts = el.data("options").options;
			// If we're in the middle of an ajax call, cancel this
			if (opts.inprogress) {
				return;
			}
						
			// Finds out which direction we're heading and how many steps we've taken
			var buffer = opts.index - opts.last_index;
			var from, to, url, domFrom, domTo, fetch_amount;
			
			$.log("Going from: " + opts.from + " To: " + opts.to);
			$.log("Previous:" + opts.last_index);
			$.log("Current:" + opts.index);
			$.log("Requested:" + opts.to);
			$.log("Buffer:" + buffer);
			$.log("Pages Loaded:" + opts.pagesLoaded);

			/**
			 * 	Method A & B
			 *	Going to the next or previous page
			 */
			if (buffer === 1 || buffer === -1) {
				$.log("--- A & B---");
				// Create DOM relevant variables for slicing
				from = (opts.pageOffset * opts.perpage) + (opts.perpage * buffer);
				to = from + opts.perpage;

				// Slice the DOM elements
				el.children().hide().slice(from, to).fadeIn("fast");
				
				// Now update the pagination
				jQuery.pagination.update(el);

				// Do we have the next/previous page loaded in the DOM? If not, load via AJAX...
				if (jQuery.inArray(opts.index + buffer, opts.pagesLoaded) === -1) {
				
					// Load in the NEXT PAGE
					if (buffer === 1) {
						// If we're not asking for the last page, then call the AJAX function
						// as the last page has already been loaded from a previous AJAX call
						if (opts.index !== opts.num_pages) {
							jQuery.pagination.callAjax(el, (opts.to + 1), opts.perpage, "a");
						}
						
					}
					// Load in the PREVIOUS PAGE
					else {
						// If we're not asking for a page before the first, then call the AJAX function
						// as the last page has already been loaded from a previous AJAX call
						if (opts.index !== 1) {
							// This loads in one page before the requested page
							from = opts.from - opts.perpage + 1;
							jQuery.pagination.callAjax(el, from, opts.perpage, "b");
						}
					}
					
				}
				// Update our offset and reorganise our array
				opts.pageOffset = jQuery.inArray(opts.index, opts.pagesLoaded);
				opts.pagesLoaded.sort(jQuery.pagination.sortNumber);
			/**
			 *	Method C
			 *	Jumped to a new index that has no pre-loaded content on either side of the pagination
			 *	heading in a forwards direction
			 */
			} else if (buffer > 1) {
				$.log("--- C ---");

				// If the requested page does NOT exist, request it via AJAX
				if (jQuery.inArray(opts.index, opts.pagesLoaded) === -1) {
					$.log("--- C 1 ---");
					
					// Load 3 pages worth [previous, present, next]
					fetch_amount = opts.perpage * 3;
					// If we tried to load the last page, then only load [previous, present]
					if (opts.index === opts.num_pages) {
						fetch_amount = opts.perpage * 2;
					}
					
					from = opts.from - opts.perpage + 1;
					to = opts.perpage * 2;
					
					jQuery.pagination.callAjax(el, from, fetch_amount, "c", from, to);
				}
				// Else, paginate and attempt to pre-load another page
				else {
					$.log("--- C 2 ---");

					// Get the DOM elements to slice
					var domElements = jQuery.pagination.returnDOMSlice(opts);

					// Update the elements
					el.children().hide().slice(domElements[0], domElements[1]).fadeIn("fast");
					
					/**
					 *	Method C subset
					 *	As the requested C page was preloaded already, this checks to see if we need to load in 
					 *	another page, uses function "a"
					 */
					if (jQuery.inArray(opts.index + 1, opts.pagesLoaded) === -1 && (opts.index + 1 < opts.num_pages)) {
						$.log("--- C 3 ---");
						from = opts.to - opts.perpage;
						to = from + opts.perpage + 1;
						jQuery.pagination.callAjax(el, to, opts.perpage, "a");
					}
				}
				jQuery.pagination.update(el);
			/**
			 *	Method D
			 *	Jumped to a new index that has no pre-loaded content on either side of the pagination
			 *	heading in a forwards direction
			 */
			} else if (buffer < -1) {
				$.log("--- D ---");
				if (jQuery.inArray(opts.index, opts.pagesLoaded) === -1) {
					$.log("--- D 1 ---");

					// Load in past, current, next pages
					fetch_amount = (opts.perpage * 2);
					domFrom = 0;
					domTo = opts.perpage;

					// But, if the first page is coming up, don't load anything before it
					if (opts.index > 1) {
						fetch_amount = (opts.perpage * 3);
						domFrom = opts.perpage;
						domTo = opts.perpage * 2;
					}

					from = (opts.from - opts.perpage);
					if (from < 0) {
						from = 0;
					}
					jQuery.pagination.callAjax(el, from + 1, fetch_amount, "d", domFrom, domTo);

				} else {
					$.log("--- D 2 ---");
					
					// Get the DOM elements to slice
					domElements = jQuery.pagination.returnDOMSlice(opts);

					// Paginate the pre-existing elements
					el.children().hide().slice(domElements[0], domElements[1]).fadeIn("fast");
					
					/**
					 *	Method D subset
					 *	As the requested D page was preloaded already, this checks to see if we need to load in 
					 *	another page, uses function "b"
					 */
					if (jQuery.inArray(opts.index - 1, opts.pagesLoaded) === -1 && (opts.index > 1)) {
						$.log("--- D 3 ---");
						from = opts.from - opts.perpage;
						to = opts.perpage;
						if (from < 0) {
							from = 0;
						}
						from++; // Add on the extra +1 integer to align with EA.com's backend index starting at 1
						jQuery.pagination.callAjax(el, from, to, "b");
					}
				}
				jQuery.pagination.update(el);
			}
		},
		
		/**
		 * Kick starts any pre-required actions before the AJAX is fired
		 * @param {Object} el - Pagination Element
		 */
		beginAjax : function (el) {
			Framework.prototype.paginationBeforeAjax(el);
		},
		
		/**
		 * This is triggered when the AJAX function has finished (success or fail)
		 * @param {Object} el
		 */
		endAjax : function (el) {
			var opts = el.data("options").options;
			
			// Let the use interact again
			opts.inprogress = false;
			
			// Show the element again
			jQuery(el).fadeIn("fast");	
			jQuery("#ajax-loader").fadeOut("slow").remove();

		},
				
		/**
		 * The final phase in pagination. This updates our variables from the old page
		 * to the new page, sets visual styles on buttons and on text/labels
		 */
		update : function (el) {
			
			$.log("--- Updated ---");
		
			// Update the buttons
			var opts = el.data("options").options;
			
			// Next button
			if (opts.index === opts.num_pages) {
				jQuery(opts.next_but).addClass(opts.css + "_next_disabled");
			} else {
				jQuery(opts.next_but).removeClass(opts.css + "_next_disabled");
			}
			
			// Previous Button
			if (opts.index > 1) {
				jQuery(opts.prev_but).removeClass(opts.css + "_prev_disabled");
			} else {
				jQuery(opts.prev_but).addClass(opts.css + "_prev_disabled");
			}
			
			// Update the textual representation of page numbers
			if (opts.counterstyle === 0 || opts.counterstyle === 3) {
				jQuery.pagination.buildPageNumbers(el, opts.index);
				
				var page = opts.current_page;
				if (opts.current_page > opts.results) {
					page = opts.results;
				}
				jQuery.pagination.textDetails(opts);
								
			} else if (opts.counterstyle === 1) {
				jQuery(opts.pages).find("span:first").text(opts.index);
			} else if (opts.counterstyle === 2) {
				jQuery.pagination.setDots(el, opts.index);
			}
			
			if (!opts.restrictwidth) {
				jQuery.pagination.setWidth(opts);
			}
			

			//show dividers between rows
			if (opts.addDividers) {
				jQuery.pagination.showDividers(opts);
			}

			//check if we want to equalize the height of something inside the page content
			if (opts.equalizecols !== null && jQuery.fn.equalizeCols !== undefined) {
				jQuery.equalizeCols.afterPagination(opts);
			} else {
				//el.parents(".sections").height("auto");
			}
			
			var elScroll = $(opts.pgmenu).offset().top - $(window).scrollTop();
			if (elScroll < 0) {
				$(window).scrollTop(0);
			}
			/*var winScroll = $(window).scrollTop(); 
			$.log("el scroll: " + elScroll);
			$.log("win scroll: " + winScroll);
			// Adjust the windowScroll
			if(elScroll < winScroll){
				$(window).scrollTop(winScroll);
			}*/

			// When we itterate again, our last page will be our current page.
			opts.last_index = opts.index;
		},
		
		/**
		 * Sets the width of the pagination menu. This is primarily used
		 * when we're using truncated numbers and the menu grows as we
		 * paginate
		 */
		setWidth: function (opts) {
			var w = opts.prev_but.width();
			w += jQuery(opts.pages).outerWidth();
			w += opts.next_but.width();
			if ($.browser.msie && $.browser.version < 7) {
				w += 24;
			}
			opts.contents.width(w);
		},
		
		/**
		 * Resets the pagination back to page 1
		 */
		reset: function (el) {
			var opts = el.data("options").options;
			var next_page = opts.perpage;
			opts.current_page = next_page;
			opts.index = 1;
			jQuery.pagination.update(el);
		},
		
		showDividers: function (opts) {
			
			// add dividers to all loaded items
			var items = opts.el.children().addClass(opts.dividingClass);
			
			var to = opts.to;
			var itemsPerRow = opts.defperpage;
	
			if (opts.dividingItemsPerRow !== null) {
				itemsPerRow = opts.dividingItemsPerRow;
			}

			var numpages = opts.num_pages;
			
			//oop through each page and remove dividers for last row on each page
			for (var i = 1; i <= numpages; i++) {
				
				var to = opts.perpage * i;
				var itemsOnLastRow = itemsPerRow;
				
				//if to is larger than the amounts of items, set it to the last item
				if (to > items.length) {
					to = items.length;
				}

				//calculate how many items we have on the last row 
				var mod = (to) % itemsPerRow;
				
				//if last row not full - adjust number of items on last line
				if (mod > 0) {
					itemsOnLastRow = mod;
				}	

				//remove dividers for last row on this page
				var from = to - itemsOnLastRow;
				items.slice(from, to).removeClass(opts.dividingClass);	
			}
		}	
		
	}});
	
})(jQuery);