function HorizTicker(json_feed_url, json_data, parent_selector, items_showing, wait_seconds, doNumbers) {$(function() {


	/* ----------------------
	| PREP
	---------------------- */

	var error = false;
	var thiss = this;
	this.foldCurrShowing = 0;
	var ac = arguments.callee;
	var ie = navigator.appName == 'Microsoft Internet Explorer';
	var fadeSpeed = 500;
	var debug = location.href.indexOf('debug=true') != -1;


	/* ----------------------
	| VALIDATE - complain if problem
	---------------------- */

	if (!json_feed_url && !json_data)
		error = "you must parse a feed URL or manually pass some JSON data";
	else if (json_feed_url && typeof json_feed_url != 'string')
		error = "feed URL passed but is not a string";
	else if (json_data && typeof json_data != 'object')
		error = "data passed but is not JSON object";
	else if (!parent_selector || (typeof parent_selector != 'string' && typeof parent_selector != 'object'))
		error = "no selector passed to indicate where to insert ticker in DOM";
	else if (!items_showing || typeof items_showing != 'number')
		error = "you must specify the number of items to show at any one time";
	else if (!wait_seconds || typeof wait_seconds != 'number')
		error = "you must specify the number of seconds to wait before the ticker transitions to the next items";

	if (error) { alert("HorizTicker error "+error); return; }


	/* ----------------------
	| ESTABLISH DATA - fetch it (if feed URL passed) or use data passed in directly. complain if problem.
	---------------------- */

	if (json_feed_url)
		$.ajax({
			url: json_feed_url,
			dataType: 'json',
			async: false,
			success: function(json) { thiss.data = json; },
			error: function() { error = "Ticker error - could not load the feed URL specified, or it returned non-JSON data"; }
		});
	else
		this.data = json_data;

	if (error) { alert(error); return; }
	if (items_showing > this.data.length) items_showing = this.data.length;


	/* ----------------------
	| BUILD: UL (i.e. ticker), plus buttons to manually change fold
	---------------------- */

	(this.ul = $('<ul>'))
		.addClass('horizTicker')
		.appendTo(parent_selector)
		.append(function() { return $('<span>').addClass('foldIndicator').text(1); });

	var div, jsv = 'javascript:void(0);';
	(div = $('<div>')).attr('id', 'ticker_nav').appendTo(parent_selector);
	$('<span>').appendTo(div).text('1 of '+(Math.ceil(thiss.data.length / items_showing)));
	$('<a>').attr({id: 'ticker_nav-prev', href: jsv}).appendTo(div).text(' ').click(function() { ac.showNextOrPrevItem('prev'); });
	$('<a>').attr({id: 'ticker_nav-next', href: jsv}).appendTo(div).text(' ').click(function() { ac.showNextOrPrevItem('next'); });


	/* ----------------------
	| BUILD: LIs (i.e. items) - only first @items_showing items are showing to start with.
	| Functionality is called later for repopulation.
	---------------------- */

	(ac.buildLIs = function(foldIndex, repopulation) {

		if (!foldIndex) foldIndex = 0;
		var loopStart = foldIndex * items_showing;

		var thisLoopCounter = 0;
		for(var u = loopStart; u < loopStart + items_showing; u++) {

			var content = thiss.data[u] ? (doNumbers ? (u+1)+' - ' : '')+thiss.data[u].name : '!!empty!!';
			var linkAttr = {
				href: thiss.data[u] ? thiss.data[u].url : '#',
				target: !(thiss.data[u] && thiss.data[u].new_window == 1) ? '' : '_blank'
			};

			//on load, build LIs
			if (!repopulation) {
				var li = $('<li>').appendTo(thiss.ul).append($('<a>').attr(linkAttr).html(content));
				if (!ie) li.css('opacity', 0);
			}

			//subsequent repopulation requests - simply change content of LIs
			else
				thiss.ul.find('li:eq('+thisLoopCounter+') a').attr(linkAttr).html(content);

			thisLoopCounter++;

		}

		//give odd LIs applicable class and bring in LIs
		thiss.ul.children('li:odd').addClass('odd');
		thiss.ul.find('li a:not(:contains(!!empty!!))').each(function() {
			!ie ? $(this).parent().animate({opacity: 1, left: 0}, fadeSpeed) : $(this).parent().css('visibility', 'visible');
		});

		// fix heights of LIs to computed height, so when text fades we don't collapse
		thiss.ul.children('li').css('height', thiss.ul.children('li:first').height());

	})();


	/* ----------------------
	| AUTO-ROTATE - transition out current fold and load in next (or, if reached end, first) fold. Also update indicator.
	---------------------- */

	if (this.data.length > items_showing)
		ac.autoInt = setInterval(ac.showNextOrPrevItem = function(nextOrPrev) { //next assumed unless 'prev' passed

			//if here as result of nav button click, kill auto-int
			if (nextOrPrev) clearInterval(ac.autoInt);

			//what direction and what fold to show? Return if same as currently-showing fold
			if (nextOrPrev != 'prev') nextOrPrev = 'next';
			if (nextOrPrev == 'next')
				var goToFoldIndex = (thiss.foldCurrShowing+1) * items_showing >= thiss.data.length ? thiss.foldCurrShowing : thiss.foldCurrShowing+1;
			else
				var goToFoldIndex = thiss.foldCurrShowing >= 1 ? thiss.foldCurrShowing-1 : 0;

			if (goToFoldIndex == thiss.foldCurrShowing) return;

			//do
			thiss.ul.find('li').each(function() {
				!ie ? $(this).animate({left: 15, opacity: 0}, fadeSpeed) : $(this).css('visibility', 'hidden');
			});
			setTimeout(function() { ac.buildLIs(goToFoldIndex, true); }, !ie ? fadeSpeed : 0);
			thiss.foldCurrShowing = goToFoldIndex;
			thiss.ul.find('.foldIndicator').text(goToFoldIndex+1);
			$('#ticker_nav span').text((goToFoldIndex+1)+' of '+$('#ticker_nav span').text().match(/\d+$/));


		}, wait_seconds * 1000)


});}
