/* jslint verified 2008.11.15 - 120: missing new, 239: missing break */
/*jslint browser: true, onevar: false, undef: true, white: false, eqeqeq: true */
/*globals document, $, $$, Constants, Ajax, statusBar, Element, MMMID, ProfileUserStatic, SORT_INDEX, makeSortable, UserSearch, isset, Palette, MbitInfo, MbitLinks, JSON, Flow, User, mmmComment, Song */
// Handfuls Manager

if (!isset(typeof(MbitInfo))) { var MbitInfo = {}; }
var Handfuls = function () {
	var _ajax, _url = Constants.ajax() + 'candyBowl', _range = 3, _xpars = null,
		_data = {}, _counts = {}, _curPage = {}, _totalPages = {}, _totals = {},
		_type = false, _list = false, _id = false, _num = false,
		_howmany,
		_params = {userprofile_u: 't=m&l=u&w=c&s=u', userprofile_c: 't=m&l=u&w=c&s=c',
					palette_pinn: 't=m&l=p&w=t', index_users: 't=u&l=s',
					palette_port: 't=m&l=p&w=p', palette_down: 't=m&l=p&w=d',
					admin_users: 't=u&l=s'
				},
		_error = function () {
			var tmsg = (!self.typeSet() ? "Handful type is not set." : ""),
				lmsg = (!self.listSet() ? "Handful listnode not set." : "");
			statusBar.error(tmsg + lmsg);
		},
		_loading = function () {
			if (self.listSet()) {
				if (_howmany) { _howmany.update('...'); }
				_list.empty();
				var td = _list.create("tbody").
						create("tr").classify("h_loading").
						create("td").classify("h_loading");
				td.create("img").attrib("src", Constants.ajaxImages()+"clock.gif");
				td.create("div").createText("Loading...");
			} else { _error(); }
		},
		_addPageSelect = function(parent, page, more) {
			parent = parent ? parent : document;
			var span = parent.create('span').classify('pageSelect').textify(page);
			if (page === _curPage[_type]) { span.addClassName('thisPage'); }
			else { span.setClick(Handfuls.go.curry(page)); }
			if (more) { span.createText(', '); }
			return span;
		},
		_setupPager = function(count, top, loading) {
			if (typeof(_xpars)!=="string") { _xpars = null; }
			var pshID = 'pageSelectHolder_'+(top ? 'top' : 'btm' ),
				psH = $(pshID),
				pageSelectHolderOut = document.create('div').identify(pshID).classify('pageSelectHolder').classify('arrow'),
				pageSelectHolder = pageSelectHolderOut.create('span').classify('border');
			if (count > 0 && !loading) {
/** prev **/	var prevPage = pageSelectHolder.create('span', 'prevPage', null, "&lt;&lt;previous "),
					numPages = _totalPages[_type];
/** prev **/	if (numPages !== 1 && _curPage[_type] > 1) { prevPage.setClick(Handfuls.prev); }
/** prev **/	else { prevPage.addClassName('disabled'); }
				var startI = Math.max(1,Math.min(numPages-3,_curPage[_type]-1)),
					endI = Math.min(startI+3,numPages-1),
					i;
				if (startI > 1) { _addPageSelect(pageSelectHolder, 1); }
				if (startI !== 1) { pageSelectHolder.createText(' ...'); } // if first num shown isn't 1
/** pages **/	for (i = startI; i <= endI; ++i)
/** pages **/		{ _addPageSelect(pageSelectHolder, i, (i !== endI && startI !== _totalPages[_type])); }
				if (endI < (numPages-1)) { pageSelectHolder.createText('... '); } // if last num shown isn't last
/** pages **/	else if (endI >= startI) { pageSelectHolder.createText(', '); }
/** pages **/	_addPageSelect(pageSelectHolder, numPages, false);
				pageSelectHolder.createText(' ');
/** next **/	var nextPage = pageSelectHolder.create('span', 'nextPage', null, " next&gt;&gt;");
/** next **/	if (_curPage[_type] !== numPages) { nextPage.setClick(function () { Handfuls.next(); }); }
/** next **/	else { nextPage.addClassName('disabled'); }
			} else if (loading) {
/** load **/	pageSelectHolder.empty().create('div').classify('loading').show('inline');
			}
			if (psH) { psH.remove(); }
			if (count === 0) { pageSelectHolderOut.cloak(); }
			var display = _list.getStyle('display');
			if (display === 'none') { pageSelectHolderOut.hide(); }
			else { pageSelectHolderOut.show('inline'); }
			if (top) { _list.parentNode.insertBefore(pageSelectHolderOut, _list); }
			else { _list.insertAfter(pageSelectHolderOut); }
		},
		self = {
			setup: function () {
				_howmany = $('howMany');
			},
			setNum: function(num) {
				if (self.numSet(num)) { _num = num; }
				return self.numSet();
			},
			setID: function(id) {
				if (self.idSet(id)) { _id = id; }
				return self.idSet();
			},
			setType: function(type) {
				if (self.typeSet(type) && isset(typeof(_params[type]))) {
					if (type !== _type) { _xpars = null; }
					_type = type;
					if (!isset(typeof(_data[type]))) { _data[type] = {}; }
					if (!isset(typeof(_counts[type]))) { _counts[type] = {}; }
					if (!isset(typeof(_curPage[type]))) { _curPage[type] = 1; }
					if (!isset(typeof(_totalPages[type]))) { _totalPages[type] = 0; }
					if (!isset(typeof(_totals[type]))) { _totals[type] = 0; }
				}
				return self.typeSet();
			},
			setList: function(node) {
				if (self.listSet(node)) {
					_list = node.id ? $(node.id) : false;
					_setupPager(-1, true, true);
					_setupPager(-1, false, true);
				}
				return self.listSet();
			},
			setXpars: function(xpars) { _xpars = xpars; },
			getXpars: function () { return _xpars; },
			typeSet: function(type) { return (!isset(typeof(type)) ? _type : type) !== false; },
			listSet: function(list) { return (!isset(typeof(list)) ? _list : list) !== false; },
			idSet: function(id) { return MMMID.validUser(!isset(typeof(id)) ? _id : id); },
			numSet: function(num) { return (!isset(typeof(num)) ? _num : num) !== false; },
			first: function () { self.go(0); },
			last: function () { self.go(-1); },
			next: function () { self.go(_curPage[_type] + 1); },
			prev: function () { self.go(_curPage[_type] - 1); },
			go: function(page) {
				if (isset(typeof(MbitLinks))) { MbitLinks().hideLinks(true); }
				if (self.typeSet() && self.listSet()) {
					if (!isset(typeof(page))) { page = _curPage[_type]; }
					else if (page === 0) { _curPage[_type] = 1; }
					else if (page < 0 || page > _totalPages[_type]) { _curPage[_type] = _totalPages[_type]; }
					else { _curPage[_type] = page; }
					self.getPage(page);
					self.slough();
				} else { _error(); }
			},
			purge: function () {
				if (self.typeSet() && self.listSet()) {
					_data[_type] = {};
					_counts[_type] = {};
					_curPage[_type] = 1;
					_totalPages[_type] = 0;
					_totals[_type] = 0;
				}
				return self.typeSet();
			},
			siphon: function () { return [_data[_type], _counts[_type], _curPage[_type], _totalPages[_type], _totals[_type]]; },
			fill: function(dump) {
				if (isset(typeof(dump))) {
					_data[_type] = dump[0];
					_counts[_type] = dump[1];
					_curPage[_type] = dump[2];
					_totalPages[_type] = dump[3];
					_totals[_type] = dump[4];
				}
			},
			slough: function () {
				if (self.typeSet() && self.listSet()) {
					// deletes _data[_type] entries for _curPage[_type] - 3
					var cur = _curPage[_type], i;
					if (isset(typeof(_data[_type][cur + _range]))) {
						i = cur + _range;
						do {
							delete(_counts[_type][i]);
							delete(_data[_type][i++]); // delete up
						} while ( isset(typeof(_data[_type][i])));
					}
					if (cur - _range > 0 && isset(typeof(_data[_type][cur - _range]))) {
						i = cur - 3;
						do {
							delete(_counts[_type][i]);
							delete(_data[_type][i--]); // delete down
						} while ( i > 0 && isset(typeof(_data[_type][i])));
					}
				} else { _error(); }
			},
			getPage: function(page) {
				if (typeof(_xpars)!=="string") { _xpars = null; }
				if (self.typeSet() && self.listSet()) {
					if (!page) { page = _curPage[_type]; }
					_loading();
					if (!isset(typeof(_data[_type][page]))) {
						// retrieve with ajax
						_ajax = new Ajax( _url,
							{ method: 'get', asynchronous: true,
							parameters: _params[_type]+'&p='+page+
								(self.idSet() ? '&id='+_id : '')+
								(self.numSet() ? '&n='+_num : '')+
								(typeof(_xpars)==="string" && _xpars!=='' ? '&'+_xpars : ''),
							onSuccess: function(request) { Handfuls.handlePage(request, _list, _type, page); },
							onFailure: function(request) { statusBar.error("Unknown handfuls error. Please report this."); } });
						_ajax.send();
					} else { self.handlePage(false, _list, _type, page); }
				} else { _error(); }
			},
			handlePage: function(request, list, type, page) {
				if (!page) { page = _curPage[type]; }
				if (self.typeSet(type) && self.listSet(list)) {
					var resp = request === false ? false : JSON.parse(request.responseText), data, count,
						tmp = _type;
					self.setType(type);
					_type = tmp;
					if (resp) {
						if (!resp.y && resp.errors) {
							var msg = "ERROR: This handful requires you to be logged in, and you aren't logged in.";
							list.empty().create("div").textify(msg);
							statusBar.error(msg);
						} else if (resp.y && (resp.p === page || resp.p === 0)) {
							_data[type][page] = resp.d;
							_counts[type][page] = resp.c;
							data = resp.d;
							count = data === null ? 0 : resp.c;
							_totalPages[type] = resp.tp;
							_totals[type] = resp.t;
							if (isset(typeof(resp.i))) { Object.extend(MbitInfo, resp.i); }
						} else { /* error */ statusBar.error("Handfuls error!!"); console.log("ERROR"); }
					} else {
						data = _data[type][page];
						count = _counts[type][page];
					}
					if (!isset(typeof(count))) { count = _counts[type][page] = 0; }
					self.parse(data, count, list, type);
				} else { _error(); }
			},
			parse: function(data, count, list, type) {
				if (self.typeSet(type) && self.listSet(list)) {
					$$(list).empty();
					if (!self.numSet() || _num < count) { self.setNum(count); }
					var paltab = "",
						ltbd = list.create("tbody");
					switch (type) {
					case 'userprofile_u':
					case 'userprofile_c':
						var un = $('thoughtPoolMessage').childNodes[0].nodeValue.split(' ')[0],
							mine = un.substr(0,3) === "you";
						ProfileUserStatic.rowHeader(ltbd);
						if (count > 0) { ProfileUserStatic.rowsHandful(data, ltbd); }
						else {
							var UtdNo = ltbd.create("tr").create("td").classify("noMbits");
							UtdNo.create("div", null, "arrow", (mine ? "You have" : un+" has")+" nothing "+list.id+".");
							UtdNo.setAttribute("colspan", 6);
						}
						ProfileUserStatic.setupThoughtPoolTable(list.id, count === 0);
						makeSortable(list, isset(typeof(SORT_INDEX)) ? SORT_INDEX.length : 0);
						break;
					case 'palette_port':	paltab = "in your ";
					case 'palette_pinn':
					case 'palette_down':
						Palette.rowHeader(ltbd, type==='palette_port');
						if (count > 0) { Palette.rowsHandful(data, ltbd, type==='palette_port'); }
						else {
							var xpars = typeof(_xpars)==="string" ? _xpars.split('&') : [],
								all = true;
							for (var i = 0; i < xpars.length; ++i) { all = all && +xpars[i].split('=')[1] === 1; }
							var PtdNo = ltbd.create("tr", list.id + "_none").create("td").classify("noMbits"),
								noStr = isset(typeof(xpars)) && !all && type !== 'palette_pinn' ? "Nothing "+paltab+list.id+" is currently being displayed.<br /> Please select an additional filter requirement above." : "You have nothing "+paltab+list.id+".";
							PtdNo.create("div", null, "paC", noStr);
							PtdNo.setAttribute("colspan", 7);
						}
						Palette.setupTable(list.id, _totalPages[type] === 1);
						// table setup?
						var per = _counts[type][_curPage[type] - 1],
							start = 1 + (_num ? _num : (per ? per : count)) * (_curPage[type] - 1),
							end = start + count - 1;
						$('howManyShown').update(count > 0 ? start + " - " + end : 0);
						break;
					case 'index_users':
						if (count > 0) { UserSearch.loadHandful(data, count); }
						else { list.create("div", null, "userRow last", "There are no users on the site."); }
						break;
					case 'admin_users':
						if (count > 0) { UserSearch.loadHandful(data, count); }
						else { list.create("div", null, "userRow last", "There are no users that meet the criteria."); }
						break; 
					default:
					}
					if (_howmany) { _howmany.update(_totals[type]); }
					if (self.listSet()) {
						_setupPager(count, true, false);
						_setupPager(count, false, false);
					}
				} else { _error(); }
			},
			loading: _loading
		};
	return self;
}();
window.addLoad(Handfuls.setup);

var Handful = function (container, rowsHandler, ajaxObject, page, perPage) {
	var _results, _loading, _pagerTop, _pagerBtm, _ajaxParams = '', _responses = {},
		classes = {disabled: 'disabled', loadingBar: 'loadingBar'},
		msgs = {
			// 171 = <<, 187 = >>
			previous: String.fromCharCode(171) + ' previous ', next: ' next '+ String.fromCharCode(187),
			loading: 'Loading...'
		},
		_total = -1, _lastPage = -1,
		_error = function (origFunc) {
			console.log(origFunc);
		},
		_showLoading = function () {
			_results.addClassName(classes.disabled);
			var w = _results.clientWidth || _results.offsetWidth, h = _results.clientHeight || _results.offsetHeight,
				loadingOld = _results.getElementsByClassName(classes.loadingBar, 'div')[0],
				loading = document.create('div').classify(classes.loadingBar).
					update('<table width="100%"><tr>' +
							'<td valign="middle" width="'+w+'" height="'+h+'"><div>' +
								msgs.loading + '<br><img src="'+Constants.images()+'blueLoadingBar.gif" />' +
							'</div></td></tr></table>');
			if (loadingOld) { loadingOld.remove(); }
			_results.insertBefore(loading, _results.firstChild);
			return loading;
		},
		_isStored = function (pageNum) { return isset(typeof(_responses[pageNum])) && _responses[pageNum] !== false; },
		_siphon = function (pageNum) { return _responses[pageNum]; },
		_store = function (pageNum, resp) { _responses[pageNum] = resp; },
		_purge = function () { _responses = {}; },
		_slough = function () {
			var i, range = 3;
			if (_isStored(page + range)) {
				i = page + range;
				do {
					delete(_responses[i++]); // delete up
				} while (_isStored(i));
			}
			if (page - range > 0 && _isStored(page - range)) {
				i = page - range;
				do {
					delete(_responses[i--]); // delete down
				} while ( i > 0 && _isStored(i));
			}
		},
		_getParams = function () {
			return (_ajaxParams.length > 1 ? _ajaxParams + '&' : '') + 'page=' + page + '&num=' + perPage;
		},
		_getPage = function (pageNum) {
			_showLoading();
			if (_isStored(pageNum)) {
				ajaxObject.options.onSuccess();
			} else {
				ajaxObject.updateOptions({ parameters: _getParams() });
				ajaxObject.send();
			}
		},
		_go = function (pageNum, e) {
			if (!isset(typeof(pageNum))) { pageNum = page; }
			else if (pageNum === 0) { page = 1; }
			else if (pageNum < 0 || pageNum > _lastPage) { page = _lastPage; }
			else { page = parseInt(pageNum, 10); }
			if (e === true || window.altKey(e)) { _store(page, false); }
			_getPage(page);
			_slough();
		},
		_refresh = function (pageNum) {
			_store(pageNum, false);
			_go(pageNum);
		},
		_first = _go.curry(0),
		_last = _go.curry(-1),
		_next = function () { return _go(page + 1); },
		_prev = function () { return _go(page - 1); },
		_makePageSelect = function (pageNum, more) {
			var span = document.create('span').classify('pageSelect').textify(pageNum);
			if (pageNum === page) { span.classify('thisPage', 'hand').setClick(_refresh.curry(pageNum)); }
			else { span.setClick(_go.curry(pageNum)); }
			if (!!more) { span.createText(', '); }
			return span;
		},
		_setupPager = function (hidden) {
			var i, pager = $$(document.createDocumentFragment()),
				startI = Math.max(1, Math.min(_lastPage - 2, page - 1)),
				endI = Math.min(startI + 2, _lastPage - 1),
/* refresh */	refresh = pager.create('div').classify('grey', 'hand', 'orangeHover').
/* refresh */		stylify('cssFloat', 'right').
/* refresh */		update('refresh'+(window.ie ? '' : ' \u267A')).setClick(_refresh.curry(page)),
/* prev */		prevPage = pager.create('span').update(msgs.previous);
			if (hidden) { pager.cloak(); }

/* prev */	if (!hidden && _lastPage > 1 && page > 1) { prevPage.setClick(_prev); }
/* prev */	else { prevPage.addClassName(classes.disabled); }
			if (!hidden) {
				if (startI > 1) { pager.appendChild(_makePageSelect(1)); }
				if (startI > 2) { pager.createText(' ... '); } // if first num shown isn't 2
				else if (startI === 2) { pager.createText(', '); }
/* pages */		for (i = startI; i <= endI; ++i)
/* pages */			{ pager.appendChild(_makePageSelect(i, (i !== endI && startI !== _lastPage))); }
				if (endI < (_lastPage - 1)) { pager.createText(' ... '); } // if last num shown isn't last
/* pages */		else if (endI >= startI) { pager.createText(', '); }
/* pages */		pager.appendChild(_makePageSelect(_lastPage));
				pager.createText(' ');
			}

/* next */	var nextPage = pager.create('span').update(msgs.next);
/* next */	if (page < _lastPage) { nextPage.setClick(_next); }
/* next */	else { nextPage.addClassName(classes.disabled); }

			return pager;
		},
		_successFunc = function (origFunc, request) {
			var resp, count = -1;
			try { resp = JSON.parse((request || {}).responseText); }
			catch (e) { resp = false; }
			if (_lastPage < 0) { _lastPage = parseInt(resp.totalPages, 10); }
			if (_total < 0) { _total = parseInt(resp.total, 10); }

			if (_isStored(page)) { resp = _siphon(page); }
			else { _store(page, resp ); }

			count = rowsHandler(_results, resp);
			_results.removeClassName(classes.disabled);
			_pagerTop.empty().appendChild(_setupPager(count <= 0));
			_pagerBtm.empty().appendChild(_setupPager(count <= 0));

			if (typeof(origFunc)==="function") { return origFunc(request); }
		},
		_setupDom = function () {
			var key, succ, fail;
			$$(container, /* forceID */ true).empty();
			_pagerTop = container.create('div').classify('pageSelectHolder');
			_results = container.create('div').classify('results');
			_pagerBtm = container.create('div').classify('pageSelectHolder');
			_pagerTop.appendChild(_setupPager(true));
			_pagerBtm.appendChild(_setupPager(true));
			page = parseInt(page || 1, 10);
			perPage = parseInt(perPage || 10, 10);
			if (ajaxObject instanceof Ajax && typeof(rowsHandler) === "function") {
				_ajaxParams = ajaxObject.options.parameters;
				succ = _successFunc.curry(ajaxObject.options.onSuccess);
				fail = _error.curry(ajaxObject.options.onFailure);
				ajaxObject.updateOptions({
					parameters: _getParams(),
					onSuccess: succ,
					onFailure: fail
				});
			} else {
				// error
			}
			container.handful = {
				nextPage: _next,
				prevPage: _prev,
				firstPage: _first,
				goToPage: _go,
				loading: _showLoading,
				refresh: _refresh
			};
			_showLoading();
			return container;
		};

	return _setupDom();
};
Handfuls.prepFriendsList = function (listID, friendID, friendUN, gender, isFriendsPage) {
	listID = $(listID);
	if (!listID || !MMMID.validUser(friendID)) {
		statusBar.error("Invalid handfuls list and/or friend ID.");
	} else {
		listID.subjectID = friendID;
		var rowsHandler = function (list, resp) {
				var id, nounverb = '', action, yours = Flow.loggedIn() && Flow.getMMMID() === friendID,
					genders = {M: 'him', F: 'her', U: 'him or her'}, genderPronoun = genders[gender] || genders.U;
				list.empty();
				if (resp.count > 0) {
					for (id in resp.f) {
						if (resp.f.hasOwnProperty(id)) {
							list.appendChild( new User(id, resp.f[id], 'friend') );
						}
					}
				} else {
					nounverb = yours ? 'You have' : friendUN + ' has';
					action = list.create('div').classify('fr_emptyList').
						textify(nounverb + ' no friends - not even Tom!').
						create('br').parentNode.create('br').parentNode;
					if (yours) {
						action.createText('Go get yourself some friends!');
					} else {
						action.createText('Why not ');
						if (!Flow.loggedIn()) {
							action.create('a').classify('hand').update('log in').attrib('href', Constants.relRoot()+'login');
							action.createText(', add');
						} else {
							action.create('a').classify('hand').update('add');// .setClick()
						}
						action.textify(' ' + genderPronoun + ', and make ' + genderPronoun + ' feel special?');
					}
				}
				return resp.count;
			},
			ajax = new Ajax(Constants.ajax() + 'processFriends', {method: 'get', parameters:'m=f&id='+encodeURIComponent(friendID)}),
			handful = new Handful(listID.classify('friendStyle'), rowsHandler, ajax);
		if (isFriendsPage || !handful.parentNode.hidden()) { handful.handful.firstPage(); }
	}
};

Handfuls.prepWallList = Handfuls.prepCommentList = function (listID, subjectIDs) {
	listID = $(listID);
	var single = !Array.is_a(subjectIDs);
	if (!listID || (single && !(MMMID.validUser(subjectIDs) || MMMID.validBit(subjectIDs)))) {
		statusBar.error("Invalid handfuls list and/or invalid subject ID ("+(!single ? 'wall' : 'comments')+").");
	} else {
		listID.subjectID = subjectIDs;
		var rowsHandler = function (list, resp) {
				var id, cmt;
				list.empty();
				if (resp.count > 0) {
					for (id in resp.comments) {
						if (resp.comments.hasOwnProperty(id)) {
							cmt = new mmmComment(id, single ? resp.subj : subjectIDs[0], resp.comments[id], single);
							list.appendChild( cmt );
							cmt.squash();
							cmt.squash = noop;
							cmt = null;
						}
					}
				} else {
					list.create('p').classify('grey').update('No one has said anything yet.' + (Flow.loggedIn() ? ' Be the first!' : ''));
				}
				return resp.count;
			},
			params = !single ? 'ids='+subjectIDs : 'id='+encodeURIComponent(subjectIDs),
			ajax = new Ajax(Constants.ajax() + 'getComments', {method: 'get', parameters: params}),
			handful = new Handful(listID.classify('commentStyle'), rowsHandler, ajax);
		handful.handful.firstPage();
	}
};

Handfuls.prepBitBy = function (type, listID, subjectID) {
	listID = $(listID);
	var types = {d: 'downloaded by', p: 'pinned by'},
		param = {d: 'd', p: 'f'};
	if (!listID || !MMMID.validBit(subjectID)) {
		statusBar.error("Invalid handfuls list and/or invalid subject ID (" + types[type] + ").");
	} else {
		listID.subjectID = subjectID;
		var rowsHandler = function (list, resp) {
				var id, subs = {d: 'downloaders', p: 'pinners'},
					subarray = resp[subs[type]];
				list.empty();
				if (resp.count > 0) {
					for (id in subarray) {
						if (subarray.hasOwnProperty(id)) {
							list.appendChild( new User(id, subarray[id]) );
						}
					}
				} else { list.create('p').classify('grey').update('This audio has not been ' + types[type] + ' anyone yet.'); }
				return resp.count;
			},
			params = 't='+param[type]+'&id='+encodeURIComponent(subjectID),
			ajax = new Ajax(Constants.ajax() + 'profileTabs', {method: 'get', parameters: params}),
			handful = new Handful(listID, rowsHandler, ajax);
		if (!handful.parentNode.hidden()) { handful.handful.firstPage(); }
	}
};
Handfuls.prepDownloadedBy = Handfuls.prepBitBy.curry('d');
Handfuls.prepPinnedBy = Handfuls.prepBitBy.curry('p');

Handfuls.prepEvolutions = function (type, listID, subjectID) {
	listID = $(listID);
	var types = {c: 'used in', p: 'attributed to'},
		param = {c: 'ec', p: 'ep'},
		datas = {c: 'children', p: 'parents'};
	if (!listID || !(MMMID.validUser(subjectID) || MMMID.validBit(subjectID))) {
		statusBar.error("Invalid handfuls list and/or invalid subject ID (" + types[type] + ").");
	} else {
		listID.subjectID = subjectID;
		var rowsHandler = function (list, resp) {
				var id;
				list.empty();
				if (resp.count > 0) {
					for (id in resp[datas[type]]) {
						if (resp[datas[type]].hasOwnProperty(id)) {
							list.appendChild( new Song(id, resp[datas[type]][id]) );
						}
					}
				} else { list.create('p').classify('grey').update('This audio has not been ' + types[type] + ' anything yet.'); }
				return resp.count;
			},
			params = 't='+param[type]+'&id='+encodeURIComponent(subjectID),
			ajax = new Ajax(Constants.ajax() + 'profileTabs', {method: 'get', parameters: params}),
			handful = new Handful(listID.classify(/*'commentStyle'*/), rowsHandler, ajax);
		if (!handful.parentNode.hidden()) { handful.handful.firstPage(); }
	}
};
Handfuls.prepSequencedIn = Handfuls.prepEvolutions.curry('c');
Handfuls.prepAttributedTo = Handfuls.prepEvolutions.curry('p');

Handfuls.prepMusicList = function (listID, userID, sequenced) {
	listID = $(listID);
	var which = !!sequenced ? 'creations' : 'uploads';
	if (listID && MMMID.validUser(userID)) {
		listID.subjectID = userID;
		var rowsHandler = function (list, resp) {
				var id;
				list.empty();
				if (resp.count > 0) {
					for (id in resp[which]) {
						if (resp[which].hasOwnProperty(id)) {
							list.appendChild( new Song(id, resp[which][id]) );
						}
					}			
				} else {
					list.create('p').
						classify('grey').
						update('This user has not '+(!!sequenced ? 'created' : 'uploaded')+' any songs yet.');
				}
				return resp.count;
			},
			ajax = new Ajax(Constants.ajax() + 'profileTabs',
				{method: 'get', parameters:'t='+which.substr(0,1)+'&id='+encodeURIComponent(userID)}),
			handful = new Handful(listID, rowsHandler, ajax);
		if (!handful.parentNode.hidden()) { handful.handful.firstPage(); }
	} else {
		statusBar.error("Invalid handfuls list and/or invalid subject ID (" + which + ").");
	}
};