function getPaginationLinkData(pageNum, totalItems, itemsPerPage, maxLinkBarWidth,
		preferredCurrOffset) {
	var totalPages = Math.ceil(totalItems / itemsPerPage);
	var nextPageNum = totalPages > pageNum ? pageNum + 1 : undefined;
	var prevPageNum = pageNum > 1 ? pageNum - 1 : undefined;

	// current page offset defaults to half of link bar width
	var currOffset = preferredCurrOffset != undefined ? preferredCurrOffset
		: Math.floor((maxLinkBarWidth-1) / 2);

	var minPage = pageNum - currOffset;
	var maxPage = minPage + maxLinkBarWidth - 1;
	if (maxPage > totalPages) {
		maxPage = totalPages;
		minPage = maxPage - maxLinkBarWidth + 1;
	}
	if (minPage < 1) {
		minPage = 1;
		maxPage = minPage + maxLinkBarWidth - 1;
	}
	if (maxPage > totalPages)
		maxPage = totalPages;
	
	return new PaginationLinkData(pageNum, prevPageNum, nextPageNum,
		minPage, maxPage);
}

function PaginationLinkData(currentPageNum, prevPageNum, nextPageNum, numberedPagesStart,
		numberedPagesEnd) {
	this.currentPageNum = currentPageNum;
	this.prevPageNum = prevPageNum;
	this.nextPageNum = nextPageNum;
	this.numberedPagesStart = numberedPagesStart;
	this.numberedPagesEnd = numberedPagesEnd;

	function toTemplate(s) {
		return typeof s == 'string' ? new Template(s) : s;
	}

	this.getLinks = function(currTemplate, prevTemplate, nextTemplate, otherTemplate) {
		var links = new Array();
		if (prevPageNum != undefined && prevTemplate != undefined)
			links.push(toTemplate(prevTemplate).evaluate({num: prevPageNum}));
		if (numberedPagesStart != undefined && numberedPagesEnd != undefined
				&& nextTemplate != undefined && otherTemplate != undefined) {
			otherTemplate = toTemplate(otherTemplate);
			currTemplate = toTemplate(currTemplate);
			for (var i=numberedPagesStart; i<=numberedPagesEnd; i++) {
				var tpl = i == currentPageNum ? currTemplate : otherTemplate;
				links.push(tpl.evaluate({num: i}));
			}
		}
		if (nextPageNum != undefined && nextTemplate != undefined) {
			links.push(toTemplate(nextTemplate).evaluate({num: nextPageNum}));
		}
		return links;
	};
}

function getStandardPaginationLinks(callbackName, pageNum, totalItems, itemsPerPage,
		maxLinkBarWidth) {
	if (maxLinkBarWidth == undefined)
		maxLinkBarWidth = 7;
	var linkData = getPaginationLinkData(pageNum, totalItems, itemsPerPage, maxLinkBarWidth);
	var link = "javascript:" + callbackName + "(#{num})";
	var links = linkData.getLinks('<a href="' + link + '" class="active">#{num}</a>',
			'<a href="' + link + '">&laquo;</a>',
			'<a href="' + link + '">&raquo;</a>',
			'<a href="' + link + '">#{num}</a>');
	return links;
}
