function cnCarousel(thisCarIn,jsonArray,optionsIn) {
	//instance args
        var data= jsonArray ? jsonArray : {};
	var thisCar=thisCarIn;
	//Overwrite options in third argument with an object hash like so: var aNewCarousel = new cnCarousel($('#id_of_carousel_window'),recs.placements[0],{ horizontal:false, itemsMoved:2 })
        var options = {
		horizontal:true,
		itemsMoved:1, //enter 'page' for full page of items.
                loadAhead:1, //enter 'page' for one page ahead.
		marginSpace:5, //minimum space on either side of item divs inside carousel
		sleeper:false, //horizontal list of items that becomes a carousel if enough items are present.
		sleeperMax:8, //# of items to exceed for carousel interaction to kick in - default set for softline prod. page swatches
		newMax:0, //assigned to data.maxItems if it's lower and not 0.
		minWidth:168, // width of whatever item is in the LI - it's assumed these are already set in the CSS
		minHeight:240, // height of item in the LI - it's assumed these are already set in the CSS
                buttonWidth:23, // inside the carousel_window container - it's assumed width is set in the CSS.
                buttonsBegone:false,
                killContainer:$(thisCar), //specify the container that gets toggled on/off whether items are present.
		slideButtons:true //sets whether button image changes when no more items can be scrolled from a direction - must have a sprite-style horizontal slider with off btn to the right.
	}
        
        for (x in optionsIn) { options[x] = optionsIn[x]; }
	
	var updates = {
		itemFit:function()
			{
				var itemSpace = options.horizontal ? $(thisCar).width() - (options.buttonWidth * 2): $(thisCar).outerHeight() - 49;
				var minSpace = options.horizontal ? options.minWidth + (2 * options.marginSpace):options.minHeight;
				return (Math.floor(itemSpace / minSpace));
			},
		itemsLoaded:0,
                remainingItems:0,
                totalMoves:0, //This is actually somewhat counterintuitively negative when you'd expect positive,
                overMax:false,
		setLiWidth:function() {
			if (options.horizontal && !options.sleeper)
			{
				//var fitItems = (data.maxItems >= updates.itemFit()) ? updates.itemFit():data.maxItems;
                                if(data.maxItems >= updates.itemFit()) { var fitItems = updates.itemFit(); updates.overMax = false; }
                                else { var fitItems = data.maxItems; updates.overMax = true; }
                                if(updates.overMax && updates.buttonsBegone) {
                                  var newMargin = ($(thisCar).width() - (fitItems * options.minWidth)) / 2 + 'px';
                                  $(thisCar).find('ul').css('left-margin',newMargin);
                                  $(thisCar).find('ul li').css('width',options.minWidth + (options.marginSpace * 2) + 'px');
                                }
                                var liWidth = (100/fitItems) - ((((options.buttonWidth * 2)/$(thisCar).width()) * 100) / fitItems);
                                liWidth = liWidth * ( $(thisCar).width() / $(thisCar).find('ul').width() );
                                $(thisCar).find('ul li').css('width',liWidth + '%');
			}
		},
                moveInc:0,
		buttonCheck:function() {
			if (options.slideButtons) {
				if(updates.totalMoves == 0) 
					{ $(thisCar).find('.back_btn_slider').css('background-position',(-1 * options.buttonWidth) + 'px 0px'); }
				else
					{ $(thisCar).find('.back_btn_slider').css('background-position','0px 0px'); }
				if(updates.itemFit() - updates.totalMoves >= data.maxItems)
					{ $(thisCar).find('.forward_btn_slider').css('background-position',(-1 * options.buttonWidth) + 'px 0px'); }
				else
					{ $(thisCar).find('.forward_btn_slider').css('background-position','0px 0px'); }
			}
		}
	}
        
	this.initialize = function() {
                if (jsonArray && jsonArray.items) { //looks redundant but in some cases RR passes an empty object - other times it's undefined
                  if (!data.maxItems || (data.maxItems > data.items.length)) { data.maxItems = data.items.length }
                  if ((options.newMax > 0) && (options.newMax < data.maxItems)) {
                          $(data.items).each( function(i) { if (i > options.newMax) { delete data.items[i] } });
                          data.maxItems = options.newMax;
                  }
                  if (options.loadAhead == 'page') { var loadIn = updates.itemFit() }
                  else { var loadIn = options.loadAhead; }
                  $(thisCar).find('h2').html(data.strategy);
                }
                else {
			data.maxItems = $(thisCar).find('ul li').size();
			data.items = $(thisCar).find('ul li .item').get();
		}
		if(options.sleeper) {
			$(thisCar).css('width',(options.minWidth * options.sleeperMax)  + (2 * options.buttonWidth) + 1 + 'px');
			$(thisCar).find('.carousel_btn').css('display',(options.sleeperMax > data.maxItems) ? 'none' : 'block');
		}
		updates.setLiWidth();
                updates.remainingItems = data.maxItems > updates.itemFit() ? data.maxItems - updates.itemFit():0;
		$(thisCar).find('.carousel_btn').unbind().click( function(e) { moveItems(e); } );
                updates.buttonCheck();
                if(options.horizontal && !options.sleeper) {
			$(window).resize( function() {
				if (options.loadAhead == 'page') { loadIn = updates.itemFit(); }
				if(data.maxItems > updates.itemsLoaded) {
					itemBuild(data.items,updates.itemsLoaded,(updates.itemFit() + loadIn));
				}
                                updates.setLiWidth();
                                if(updates.itemFit() >= data.maxItems) {
                                  if(options.buttonsBegone) {
                                    $(thisCar).find('.carousel_btn').css('display','none'); 
                                    $(thisCar).find('ul').css('margin-left','0px').css('margin-right',options.buttonWidth + 'px');
                                  }
                                  $(thisCar).find('ul').css('left','0px'); updates.totalMoves = 0;
                                }
                                else {
                                  if(options.buttonsBegone) {
                                    $(thisCar).find('.carousel_btn').css('display','block'); 
                                    $(thisCar).find('ul').css('margin-right','0px').css('margin-left',options.buttonWidth + 'px');
                                  }
                                  var leftShift = (updates.itemFit() - updates.totalMoves) - data.maxItems;
                                  if(leftShift > 0) { updates.totalMoves += leftShift; }
                                  $(thisCar).find('ul').css('left',updates.totalMoves * $(thisCar).find('ul li').width() + 'px');
                                }
				updates.buttonCheck();
			});
		}
                if(jsonArray) { itemBuild(data.items,0,(updates.itemFit() + loadIn)); }
		$(document).ready( function() {
			if($(thisCar).hasClass('carouselNew') && $(thisCar).hasClass('carousel_window')) {
				$(options.killContainer).css('display','block')
			} else {
			$(thisCar).find('ul li').size() > 0 ? $(options.killContainer).css('display','block') : $(options.killContainer).css('display','none');
			}
		});
	}
	
	function moveItems(e) {
                if (options.itemsMoved == 'page') { var moves = updates.itemFit(); }
                else { var moves = options.itemsMoved; }
                if ((e.target.className.match(/forward/)) || (e.target.className.match(/down/))) {
			var forwardBtn = true;
                        moves = (moves * -1);
                        var availMove = ((-1 * updates.totalMoves) + updates.itemFit()) - data.maxItems;
                        if(updates.itemFit() >= data.maxItems) {
                          availMove = 0;
			}
                }
                else {
                        var availMove = Math.abs(updates.totalMoves);
                }
		updates.moveInc = options.horizontal ? $(thisCar).find('ul li').width():$(thisCar).find('ul li').outerHeight();
                (Math.abs(moves) > Math.abs(availMove)) ? updates.totalMoves+= availMove: updates.totalMoves += moves;
		var carUl = $(thisCar).find('ul');
		var Direction = (updates.moveInc * updates.totalMoves) + 'px';
                if (options.loadAhead == 'page') { var loadIn = updates.itemFit() }
                else { var loadIn = options.loadAhead; }
		var buildAndUpdate = function() {
			itemBuild(data.items,updates.itemsLoaded,updates.itemsLoaded+loadIn);
			if(options.horizontal) { updates.buttonCheck(); }
		}
		if (options.horizontal) { (forwardBtn) ? carUl.animate({left:Direction},{complete:buildAndUpdate}) : carUl.animate({left:Direction},{complete:updates.buttonCheck}); }
		else { (forwardBtn) ? carUl.animate({top:Direction},{complete:buildAndUpdate}) : carUl.animate({top:Direction}); }
	}
	
	var itemBuild = function(itemData,itemStart,itemEnd) {
		options.killContainer.css('display',itemData.length == 0 ? 'none':'block');
		var html_element = '';
		var itemsToAdd = $(itemData).slice(itemStart,itemEnd);
		$(itemsToAdd).each(function(i)
		{
			if(i >= itemsToAdd.length || (updates.itemsLoaded >= data.maxItems)) { return false; }
			var arrayIndex = itemStart + i;
			html_element += "<li>";
			html_element += "<div class=\"" + (itemData[arrayIndex].type?itemData[arrayIndex].type:"item normal") + "\">";
			html_element += "<h4><a href=\""+(itemData[arrayIndex].url?itemData[arrayIndex].url:"javascript:;")+"\">" + (itemData[arrayIndex].name.length > 40 ? itemData[arrayIndex].name.substr(0,40) + "..." : itemData[arrayIndex].name) + "</a></h4> ";
			html_element += "<p class=\"placeimage\"><a href=\""+((itemData[arrayIndex].url)?itemData[arrayIndex].url:"javascript:;")+"\"><img src=\"" + itemData[arrayIndex].image + "&qlt=75" + "\" alt=\""+itemData[arrayIndex].name+
			"\" style=\"margin-left:20px;\" /></a></p>";
//i18n changes
		if(isI18NConvReq()){
			html_element +=	"<div class=\"saveStory\">"+fnConSSfly(itemData[arrayIndex].attr_savestory)+"</div>"
		}else{
			html_element +=	"<div class=\"saveStory\">"+itemData[arrayIndex].attr_savestory+"</div>"
		}
			html_element += "<a class=\"view_more_btn\" href=\"" + ((itemData[arrayIndex].url)?itemData[arrayIndex].url:"javascript:;") + "\"></a></div>";
			html_element += "</li>";
			updates.itemsLoaded++;
		});
		$(thisCar).find('ul').append(html_element);
		if(options.horizontal){updates.setLiWidth();}
	}
        this.initialize();
}

cnCarousel.setCarousels = function(car_windows,jsonObj,optionsIn) {
  var carousels = {};
  $(car_windows).each( function(i) {
    carousels['carousel_' + i] = new cnCarousel($(car_windows[i]),jsonObj[i],optionsIn);
    carousels['carousel_' + i].onLoad = function() {
            $("a.mapLink").click(function() {mapClick(this)});
    }
  });
}