
/*===============================================================================
	PageScroller.js
	John Larson
	1/22/08
	
	Component for making any div which contains a collection of homogeneous items
	to be scroll-pagable acrosss pages of those items.
	
	Example:
	<span class="pageScrollLinks"></span>
	<div id="myPagedWindow">
	  <item1 />
	  <item2 />
	  <item3 />
	  <item4 />
	  <item5 />
	</div>
	<span class="pageScrollLinks"></span>
	
	new PageScroller('myPagedWindow', {pageSize: 2, currentPage: 2});
	
	would imply a pagination of 3 pages (page 1: items 1 and 2, page 2: items 3
	and 4, page 3: item 5).  The pageScrollLinks spans would be infused with
	clickable paging links ("1 2 3"), "2" would be highlighted as the current
	page, the div would be sized to be the combined height of items 3 and 4,
	and would scroll to show only those items.

===============================================================================*/


var PageScroller = new Class({
	
	options: {
		pageSize:				2,
		itemClass:				'scrollItem',
		pageLinksSpanClass:		'pageScrollLinks',
		pageLinkCurrentClass:	'pageLink-current',
		pageLinkClass:			'pageLink',
		minHeight:				0,
		manageHistory:			false,
		currentPage:			1
	},

	initialize: function(scrollWindowElement, options){
		
		this.scrollWindowElement = $(scrollWindowElement);
		if (!this.scrollWindowElement) {
			return;
		}
		
		this.setOptions(options)
		
		PageScroller.instanceCount++;
		
		if (this.options.manageHistory) {
			this.historyKey = 'PageScroller' + PageScroller.instanceCount;
			this.history = HistoryManager.register(
				this.historyKey,
				[this.options.currentPage],
				function(values) {
					this.scrollToPage(parseInt(values[0]));
				}.bind(this),
				function(values) {
					return this.historyKey + '(' + values[0] + ')';
				}.bind(this),
				this.historyKey + '\\((\\d+)\\)');
		}
		this.winScroller = new Fx.Scroll(scrollWindowElement);
		
		this.pageSize = options.pageSize || 2;
		this.currentPage = options.currentPage || 1;
		this.minHeight = options.minHeight || 0;
		
		
		this.items = $ES('.' + this.options.itemClass, this.scrollWindowElement);
		this.itemCount = this.items.length;
		this.pageCount = Math.ceil(this.itemCount / this.options.pageSize);
		
		this.writePaging();
		this.setScrollWindowHeightToCurrentPage();
	},
	
	refreshPaging: function() {
		
		this.items = $ES('.' + this.options.itemClass, this.scrollWindowElement);
		this.itemCount = this.items.length;
		this.pageCount = Math.ceil(this.itemCount / this.options.pageSize);
		
		this.writePaging();
		
		if (this.currentPage > this.pageCount) {  // we've lost 1 or more pages since last build
			this.currentPage = Math.min(this.currentPage, this.pageCount);
			this.scrollToPage(this.currentPage);
		}
		
	},
	
	writePaging: function() {
		
		myBoundPageScroll = this.scrollToPage.bind(this);
		
		$ES('.' + this.options.pageLinksSpanClass).each(function(theSpan) {
			
			theSpan.empty(); // clear out whatever is already there
			
			for(i=1; i <= this.pageCount; i++) {
				
				thisPageNumber = new Element('a', {myPageNumber: i});
				thisPageNumber.addClass(this.currentPage == i ? 
					this.options.pageLinkCurrentClass : this.options.pageLinkClass);
				
				thisPageNumber.setText(i);
				thisPageNumber.addEvents({
					'click': function() {
						myBoundPageScroll(this.getText());
						return false;
					}
				});
				theSpan.adopt(thisPageNumber);
				theSpan.appendText(' ');
				
			}
		}, this);
		
	},
	
	setScrollWindowHeightToCurrentPage: function() {
		
		if(this.currentPage == 0) {
			dbug.log('empty scroll window.');
			this.scrollWindowElement.effect('height').start(this.minHeight);
			return;
		}
		
		var itemIndex = (this.currentPage-1) * this.pageSize;  // array index of our item to scroll to
		
		// how tall should our window be to show this page?
		totalHeight = 0;
		for (var i=0; i < this.pageSize  &&  i < this.items.length - itemIndex; i++) {
			totalHeight += this.items[itemIndex + i].getSize()['size']['y'];
		}
		
		// resize:
		this.scrollWindowElement.effect('height').start(Math.max(totalHeight, this.minHeight));
		
		dbug.log('sizing page ' + (this.currentPage-1) + ' to height ' + totalHeight);
		
	},
	
	scrollToPage: function(pageNumber) {
		
		if(this.pageCount == 0) return;
		
		var oldCurrentPage = this.currentPage;
		
		// Update the state of things:
		this.currentPage = pageNumber;
		
		this.setScrollWindowHeightToCurrentPage()
		
		var itemIndex = (this.currentPage-1) * this.pageSize;  // array index of our item to scroll to
		
		if (itemIndex < 0) {  // no items, nothing here
			return;
		}
		
		function myLateScroll() {
			this.winScroller.toElement(this.items[itemIndex]);
		}
		
		myLateScroll.delay(500, this);
		
		// Update our paging link classes (current vs. the rest):
		$ES('.' + this.options.pageLinksSpanClass).each(function(theSpan) {
			
			$ES('a', theSpan).each(function(theLink) {
				
				if(theLink.getText() == (pageNumber + '')) {
					theLink.removeClass(this.options.pageLinkClass);
					theLink.addClass(this.options.pageLinkCurrentClass);
				}
				else if(theLink.getText() == (oldCurrentPage + '')) {
					theLink.removeClass(this.options.pageLinkCurrentClass);
					theLink.addClass(this.options.pageLinkClass);
				}
				
			}, this);
		}, this);
		
		if (this.options.manageHistory)
			this.history.setValue(0, pageNumber);
		
	}
	
	
});

PageScroller.implement(new Options, new Events);
PageScroller.instanceCount = 0;

