
var cmarket = Class.create();

cmarket.Pageable = Class.create();

cmarket.Pageable.prototype = {

	initialize: function(container, itemSelector, options) {
		this.container = $(container);
		this.currentPage = 1;
		this.itemSelector = itemSelector;
		this.sortable = new cmarket.Sortable(itemSelector);
		
		this.options = Object.extend({
			pageSize: 12,
			style: 'gridview',
			onStateUpdate: Prototype.emptyFunction
		}, options || {});
		
		this.items = this.sortable.items;
		this.pagecount = this.getPageCount();
		this.gotoPage(1);

		this.statemgr = new StateManager(this.updateState.bind(this), { iframesrc:'/cmarketplace/blank.jsp' });
		this.statemgr.startObserving();
	},
	
	sort: function(column) {
		this.setItems(this.sortable.sort(column));
		this.gotoPage(1);
		this.saveState();
	},

	setItems: function(items) {
		this.items = items;

		// reorder items in DOM - appendChild simply moves the element if already exists in DOM
		var parent = this.container;
		$A(this.items).each( function(item) { parent.appendChild(item); } );
		
		this.pagecount = this.getPageCount();
	},
	
	setLayout: function(pageSize, style, page) {
		this.options.pageSize = pageSize;
		this.options.style = style;
		
		this.pagecount = this.getPageCount();
		
		var gotoPage = parseInt(page, 10);
		if (isNaN(gotoPage)) {
			gotoPage = 1;
		}
		
		this.gotoPage(gotoPage);
		this.saveState();
	},
	
	nextPage: function(event) {
		Event.stop(event);
		
		this.gotoPage(this.currentPage + 1);
		this.saveState();
	},

	prevPage: function(event) {
		Event.stop(event);
		
		this.gotoPage(this.currentPage - 1);
		this.saveState();
	},
	
	selectPage: function(event) {
		Event.stop(event);
		var selector = Event.findElement(event, 'select');
		
		var page = parseInt(selector.options[selector.selectedIndex].value, 10);
		if (isNaN(page)) {
			page = 1;
		}
		
		this.gotoPage(page);
		this.saveState();
	},

	gotoPage: function(page) {
		this.currentPage = Math.min(Math.max(1, page), this.pagecount);
		
		this.doPaging();
	},
	
	doPaging: function() {
		
		var pitems = this.buildPagingItems();
		var pmenu = this.buildPagingMenu();
		var plinks = this.buildPagingLinks();
		
		// build top paging bar
		var topBar = $('pagingBarTop');
		if (topBar) {
			topBar.hide();
			topBar.innerHTML = '';
			topBar.appendChild(pitems);
			topBar.appendChild(pmenu); 
			topBar.appendChild(plinks); 
			topBar.className = "paging";
			topBar.show();
		}
		
		pitems = this.buildPagingItems();
		pmenu = this.buildPagingMenu();
		plinks = this.buildPagingLinks();
		
		// build bottom paging bar
		var bottomBar = $('pagingBarBottom');
		if (bottomBar) {
			bottomBar.hide();
			bottomBar.innerHTML = '';
			bottomBar.appendChild(pitems); 
			bottomBar.appendChild(pmenu); 
			bottomBar.appendChild(plinks); 
			bottomBar.className = "paging";
			bottomBar.show();
		}
		
		// switch items
		this.setItemsForPage(); 
	},
	
	buildPagingItems: function() {
		var range = this.getItemRangeForPage();
		var text = 'Items: ' + (range.start + 1) + '-' + range.end + ' of ' + this.items.length;
		
		return Builder.node('span', { className: 'pagingItems' }, [ text ]);
	},
	
	buildPagingMenu: function() {
		var options = [];
		
		options.push(Builder.node('option', { value:'' }, [ "Page " + this.currentPage + " of " + this.pagecount ]));
		for (var i = 0; i < this.pagecount; ++i) {
			options.push(Builder.node('option', { value:(i + 1) }, ["Page " + (i + 1)]));
		}

		var selectElem = Builder.node('select', { name: 'pagingMenu' }, options);
		selectElem.selectedIndex = 0;
		
		var pagingMenu = Builder.node('span', { className: 'pagingMenu' }, [ selectElem ]);

		Event.observe(selectElem, 'change', this.selectPage.bindAsEventListener(this), false);
		
		return pagingMenu;
	},
	
	buildPagingLinks: function() {
		var startpage = this.currentPage - 3;
		
		if (startpage < 1) { 
			startpage = 1; 
		} else if ((this.pagecount - 6) < 1) {
			startpage = 1;
		} else if ((this.currentPage + 3) > this.pagecount) {
			startpage = this.pagecount - 6;
		}

		var endpage = Math.min((startpage + 6), this.pagecount);
		
		var links = [];
		
		if (this.currentPage > 1) {
			links.push(Builder.node('a', { href:'#', className:'pageBackNext' }, [ "< BACK" ]));
			
			Event.observe(links.last(), 'click', this.prevPage.bindAsEventListener(this), false);
		}

		for (var i = startpage; i <= endpage; ++i) {
			if (i == this.currentPage) {
				links.push(Builder.node('span', { className:'pageActive' }, ["" + i] ));
			} else {
				links.push(Builder.node('a', { href:'#', className:'page' }, [ "" + i ]));

				Event.observe(links.last(), 'click', this.gotoPage.bind(this, i), false);
			}
		}

		if (this.currentPage < this.pagecount) {
			links.push(Builder.node('a', { href:'#', className:'pageBackNext' }, [ "NEXT >" ]));
			
			Event.observe(links.last(), 'click', this.nextPage.bindAsEventListener(this), false);
		}
		
		return Builder.node('span', { className: 'pagingLinks' }, links);
	},
	
	setItemsForPage: function() {
		var range = this.getItemRangeForPage();
		var curstyle = this.options.style;
		
		// mark items as hidden or shown
		for (var i = 0; i < this.items.length; ++i) {
			if ((i >= range.start) && (i < range.end)) {
				this.items[i].className = curstyle;
				this.items[i].show();
			} else {
				this.items[i].hide();
			}
		}
	},
	
	getPageCount: function() {
		var pagecount = Math.floor(this.items.length / this.options.pageSize);
		
		if ((this.items.length % this.options.pageSize) > 0) {
			++pagecount;
		}

		return pagecount;		
	},
	
	getItemRangeForPage: function() {
		var startitem = Math.max((this.options.pageSize * (this.currentPage - 1)), 0);
		var enditem = Math.max(Math.min((this.options.pageSize * this.currentPage), this.items.length), 0);
		
		return { start: startitem, end: enditem };
	},
	
	saveState: function() {
		var state = this.currentPage + ':' + this.options.pageSize + ':'  + this.options.style + ':' + (this.sortable.column || 2);
	
		this.statemgr.setState(state);
	},
	
	updateState: function(location) {
		var state = location || this.currentpage + ':' + this.options.pageSize + ':' + this.options.style + ':' + this.sortable.column;
		var parts = $A(location.split(':'));

		var curpage = parseInt(parts[0], 10);
		if (isNaN(curpage)) {
			curpage = 1;
		}

		var pagesize = parseInt(parts[1],  10);
		if (isNaN(pagesize)) {
			pagesize = 12;
		}
		
		var style = parts[2];
		if (!style) {
			style = this.options.style;
		}
		
		var sortby = parseInt(parts[3], 10);
		if (isNaN(sortby)) {	
			sortby = 2;
		}
		
		this.options.pageSize = pagesize;
		this.options.style = style;
		
		if (sortby != this.sortable.column) {
			this.setItems(this.sortable.sort(sortby));
		} else {
			this.pagecount = this.getPageCount();
		}
				
		this.gotoPage(curpage);
		
		this.options.onStateUpdate({ page:curpage, size:pagesize, style:style, sortby:sortby });
	}
};

cmarket.Sortable = Class.create();

cmarket.Sortable.prototype = {

	initialize: function(itemSelector) {
		this.items = $$(itemSelector);
		this.column = null;
		this.sortTypes = {};
		
		this.itemarray = this.getItemArray();
	},

	sort: function(column) {
		this.column = column;
		
		// sort items array
		this.itemarray.sort(this.getSortType(this.itemarray));
		
		var sorted = [];
		
		$A(this.itemarray).each( function(item) { sorted.push(item[0]); } );
		
		return sorted;
	},
	
	getItemArray: function() {
		var itemarray = [];
		
		var func = this.getRowArray.bind(this);
		$A(this.items).each( function(item) { itemarray.push(func(item)); } );
		
		return itemarray;
	},
	
	getRowArray: function(item) {
		var row = [];
		var kids = [];
		
		var children = item.getElementsByTagName("span");
		for (var i = 0; i < children.length; ++i) {
			if ($(children[i]).hasClassName('label')) {
				continue;
			}

			kids.push(children[i]);
		}
		
		row.push(item);
		$A(kids).each( function(kid) { row.push(kid); } );
		
		return row;
	},
	
	setSortType: function(column, type) {
		if ('alpha' == type) {
			this.sortTypes[column] = this.sortAlpha.bind(this);
			return;
		}
		
		if ('numeric' == type) {
			this.sortTypes[column] = this.sortNumeric.bind(this);
			return;
		}
		
		if ('currency' == type) {
			this.sortTypes[column] = this.sortCurrency.bind(this);
			return;
		}
		
		if ('date' == type) {
			this.sortTypes[column] = this.sortDate.bind(this);
			return;
		}
	},
	
	getSortType: function(itemarray) {
		if (this.sortTypes[this.column]) {
			return this.sortTypes[this.column];
		}
		
		var testval = this.getInnerText(itemarray[0][this.column]);
		var sortfn = this.sortAlpha.bind(this);
		
		if (testval.match(/^\d\d[\/-]\d\d[\/-]\d\d\d\d$/)) { 
			sortfn = this.sortDate.bind(this);
		} else if (testval.match(/^\d\d?[\/-]\d\d?[\/-]\d\d$/)) { 
			sortfn = this.sortDate.bind(this);
		} else if (testval.match(/^[\d\.]+$/)) {
			sortfn = this.sortNumeric.bind(this);
		} else if (testval.match(/^[$]/)) {
			sortfn = this.sortCurrency.bind(this);
		}
		
		this.sortTypes[this.column] = sortfn;
		
		return sortfn;
	},
	
	// case insensitive sort
	sortAlpha: function(a, b) {
		var aval = this.getInnerText(a[this.column]).toLowerCase();
		var bval = this.getInnerText(b[this.column]).toLowerCase();
		
		if (aval == bval) {
			return 0;
		}
		if (aval < bval) {
			return -1;
		}
		
		return 1;
	},
	
	sortNumeric: function(a, b) {
		var aval = parseFloat(this.getInnerText(a[this.column]));
		if (isNaN(aval)) {
			aval = 0;
		}
		
		var bval = parseFloat(this.getInnerText(b[this.column]));
		if ((bval)) {
			bval = 0;
		}
	
		return aval - bval;	
	},
	
	sortCurrency: function(a, b) {
		var aval = parseFloat(this.getInnerText(a[this.column]).replace(/[^0-9.]/g,''));
		if (isNaN(aval)) {
			aval = 0;
		}
		
		var bval = parseFloat(this.getInnerText(b[this.column]).replace(/[^0-9.]/g, ''));
		if (isNaN(bval)) {
			bval = 0;
		}
	
		return aval - bval;	
	},
	
	sortDate: function(a, b) {
		var date1 = null;
		var date2 = null;
		var yr = null;
		
		// two digit years less than 50 are treated as 20XX, greater than 50 are treated as 19XX
		var aval = this.getInnerText(a[this.column]);
		var bval = this.getInnerText(b[this.column]);
		
		date1 = this.parseDate(aval);
		date2 = this.parseDate(bval);
		
		if (date1 == date2) { return 0; }
		if (date1 < date2) { return -1; }
		return 1;
	},
	
	getInnerText: function(elem) {
		if (typeof elem == "string") { 
			return elem; 
		}
		if (typeof elem == "undefined") { 
			return elem; 
		}
		if (elem.innerText) { 
			return elem.innerText; 
		}
	
		var str = "";
		var kids = elem.childNodes;
		for (var i = 0; i < kids.length; ++i) {
			if (kids[i].nodeType == Node.ELEMENT_NODE) {
				str += this.getInnerText(kids[i]);
				continue;
			}
			if (kids[i].nodeType == Node.TEXT_NODE) {
				str += kids[i].nodeValue;
				continue;
			}
		}
	
		return str;
	},
	
	parseDate: function(date) {
		var year = null;
		var month = null;
		var day = null;
		var separator = '/';
		
		if (date.indexOf('/') == -1) {
			separator = '-';
		}
		
		var month = date.substr(0, date.indexOf(separator));
		var therest = date.substring(date.indexOf(separator) + 1);
		
		var day = therest.substr(0, therest.indexOf(separator));
		var year = therest.substring(therest.indexOf(separator) + 1);

		if (year.length == 2) {
			if (parseInt(year, 10) < 50) { year = '20'+year; } else { year = '19'+year; }
		}
		if (month.length == 1) {
			month = '0' + month;
		}
		if (day.length == 1) {
			day = '0' + day;
		}
		
		return year + month + day;
	}
};

var cMarketUtils = {};

cMarketUtils.parseRequestParams = function() {
	var paramMap = {};
	var params = window.location.search.substring(1).split("&");

	for (var i = 0; i < params.length; ++i) {
	    var pair = params[i].split("=");
	    paramMap[pair[0]] = pair[1];
	}
	
	return paramMap;
};

cMarketUtils.getPageX = function(target) {
	var el = target;
	if (!el) {
		return 0;
	}
	
	var x = 0;
	
	while (el) {
		if (el.offsetLeft) {
			x += el.offsetLeft;
		}
		
		el = el.offsetParent;
	}
	
	return x;
};

cMarketUtils.getPageY = function(target) {
	var el = target;
	if (!el) {
		return 0;
	}
	
	var y = 0;
	
	while (el) {
		if (el.offsetTop) {
			y += el.offsetTop;
		}
		
		el = el.offsetParent;
	}
	
	return y;
};

cMarketUtils.addEvent = function(obj, event, callback) {
	if (obj.addEventListener) {
		obj.addEventListener(event, callback, false);
		return true;
	} else if (obj.attachEvent) {
		return obj.attachEvent("on" + event, callback);
	} else {
		return false;
	}
};

cMarketUtils.getParent = function(el, tagname) {
	if (!el) {
		return null;
	} else if (el.nodeType == 1 && el.tagName.toLowerCase() == tagname.toLowerCase())	{ 
		return el;
	} else {
		return cMarketUtils.getParent(el.parentNode, tagname);
	}
};

