// 
//  base.js
//  velo city bags
//  
//  Created by Stephen Chai on 2009-07-26.
//  Copyright 2009 stephen.chai.name. All rights reserved.
// 

// jquery compatibility mode
var $j = jQuery.noConflict();

// global vars
var vcb_product = {};

// foxycart stuffs
fc_tb_pathToImage = 'http://' + document.location.host + '/foxycart/img/loading.gif';
fc_tb_WIDTH = 780;
fc_tb_HEIGHT = 530;

// on ready
$j(document).ready(function() {
	vcb_bind();
});

// apply bindings
function vcb_bind() {
	// hide no javascript content
	$j('.js_no').addClass('display_none');
	// show javascript enabled content
	$j('.js_req').removeClass('js_req');
	// bind cart controls (add to cart, print, email)
	vcb_cartControlsBind('ul.product_summary_controls');
	// image galleries
	vcb_galleryAnimate('ul.gallery');
	// image zoom bindings
	vcb_imageZoomBind('a.imagezoom');
	// slide list animate (call before nav animate)
	vcb_slideListAnimate('ul.slide_list');
	// animate navigation (call after creating image galleries else their navigation won't animate)
	vcb_navAnimate('ul.nav');
	// radio icons
	vcb_radioIconsBind('ul.radio_icons');
	// give some extra padding if large enough window resolution
	vcb_viewPad();
	// store only
	if ($j('body#template_store').length) {
		// init
		vcb_productSummaryInitialize();
		// product enable / disable dependencies
		vcb_productDependencies();
		// bind / initalize product summary
		vcb_productSummaryBind();
		// section scroller bindings / intialize
		vcb_sectionScrollerInitialize();
		// auto hide sections until needed
		vcb_sectionAutoDisplayInitialize();
	}
	// de-confuse emails
	$j('.email_link').each(function() {
		var email_address = $j(this).text().replace(/\(at\)/, '@');
		$j(this).html('<a href="mailto:'+email_address+'">'+email_address+'</a>');
	});
}

// cart controls, bind
function vcb_cartControlsBind(cart_controls) {
	// add to cart
	$j(cart_controls + ' li.cart a').click(function() {
		// if no errors (errors are alerted if present)
		if (vcb_productValidate()) {
			// write the foxycart href from vcb_product
			var href = 'https://velocitybags.foxycart.com/cart?' + encodeURI('name=' + vcb_product.name + '&style=' + vcb_product.style + '&size=' + vcb_product.size.name + '&customize=' + vcb_product.upgrades.join(', ') + '&colors=' + vcb_product.colors.join(', ') + '&price=' + vcb_product.price);
			$j(this).attr('href', href);
			// and let foxycart go w/ it
			return true;
		}
		// else bummer
		else return false;
	});
	// share
	$j(cart_controls + ' li.share a').click(function() {
		// if no errors (errors are alerted if present)
		if (vcb_productValidate()) {
			// generate link
			var href = 'http://' + document.location.host + '/share?product_name=' + encodeURI(vcb_product.name + '&product_style=' + vcb_product.style + '&product_size=' + vcb_product.size.name + '&product_colors=' + vcb_product.colors.join(',') + '&product_customize=' + vcb_product.upgrades.join(',') + '&product_price=' + vcb_product.price);
			// write modal HTML content
			var html = '<div class="product_share type_sans leading_open"><p class="link">(Generating&hellip;)</p><p class="instructions">Your share link saves your current product setup.<br />Use it whenever, wherever and however you like.</p></div>';
			// show the modal window
			vcb_modalShow(html);
			// and get the product link via ajax
			vcb_shortenUrl(href, '#vcb_modal_wrapper .link');
		}
		// else bummer
		else return false;
	});
	// print
	$j(cart_controls + ' li.print a').click(function() {
		// print
		window.print();
		// and scroll back to last section (print seems to want to scroll it left all the way)
		vcb_sectionScrollerGoTo($j('.section').length - 1);
	});
}

// debug msg
function vcb_debug(msg) {
	if (typeof(console) !== 'undefined' && console != null) console.log('[vcb] ' + msg);
}

// gallery, setup controls / animation
function vcb_galleryAnimate(gallery) {
	// for each
	$j(gallery).each(function() {
		// dont re-apply if already setup
		if ($j(this).hasClass('gallery_animate')) return;
		// dont apply if has only one <li>
		if ($j(this).children('li').length < 2) return;
		// generate id
		var id = 'vcb_gallery_'+$j('ul.gallery').index(this);
		// add id
		$j(this).attr('id', id).addClass('gallery_animate');
		// add controls
		$j(this).after('<ul class="block nav block_controls_small block_controls_small_right gallery_controls"><li class="previous"><a href="javascript: void(0)" onclick="javascript: vcb_galleryShowImage(\''+id+'\', -1)" title="View Previous Image" class="tir">Previous</a></li><li class="next"><a href="javascript: void(0)" onclick="javascript: vcb_galleryShowImage(\''+id+'\', 1)" title="View Next Image" class="tir">Next</a></li></ul>');
		// pre-load gallery images
		$j(this).children('li').each(function(index) {
			// ie sucks at this, dont have time to figure out right now
			if ($j.browser.msie) return;
			// generate <li> id
			var li_id = id + '_li_' + index;
			// add loading class and id to <li>
			$j(this).addClass('loading').attr('id', li_id);
			// preload image
			$j('<img />').attr('for', li_id).attr('src', $j(this).find('img').eq(0).attr('src')).load(function() { $j('#'+$j(this).attr('for')).removeClass('loading'); });
		});
	});
}

// gallery, show an image
function vcb_galleryShowImage(gallery_id, move_by) {
	// index to show
	var index_to_show = $j('#'+gallery_id+' li').index($j('#'+gallery_id+' li.selected')) + move_by;
	if (index_to_show < 0) index_to_show = $j('#'+gallery_id+' li').length - 1;
	else if (index_to_show > $j('#'+gallery_id+' li').length - 1) index_to_show = 0;
	// animate direction
	var animate_direction = move_by > 0 ? -1 : 1;
	// hide current image
	$j('#'+gallery_id+' li.selected').animate({ left: $j(this).width() * animate_direction }, 350, 'swing', function() { $j(this).removeClass('selected').css('left', 0); });
	// change current caption (if exists)
	$j('#'+gallery_id).siblings('.caption').fadeOut(350, function() { $j(this).html($j('#'+gallery_id+' li').eq(index_to_show).children('img').attr('alt')).fadeIn(150); });
	// show new
	$j('#'+gallery_id+' li').eq(index_to_show).css('left', $j(this).width() * animate_direction * -1).addClass('selected').animate({ left: 0 }, 500, 'swing');
}

// image zoom, add bindings
function vcb_imageZoomBind(a) {
	// preload imagezoom background image (must match CSS)
	$j('<img />').attr('src', '/img/layout/modal_content.png');
	// for each link
	$j(a).each(function() {
		// on click
		$j(this).click(function() { vcb_imageZoomShow($j(this)); return false; });
	});
}

// image zoom, show
function vcb_imageZoomShow($a) {
	// write imagezoom markup
	var html = '<ul class="gallery gallery_large">';
	$j('a[rel="'+$a.attr('rel')+'"]').each(function() { html += '<li'+($j(this).attr('href') == $a.attr('href') ? ' class="selected"' : '')+'><img src="'+$j(this).attr('href')+'" alt="'+$j(this).attr('title')+'" /></li>'; });
	html += '</ul>';
	html += '<div class="caption type_sans">'+$a.attr('title')+'</div>';
	// show the modal window
	vcb_modalShow(html, 1);
	// animate gallery
	vcb_galleryAnimate('#vcb_modal_wrapper ul.gallery');
	// animate nav
	vcb_navAnimate('#vcb_modal_wrapper ul.imagezoom_controls');
	vcb_navAnimate('#vcb_modal_wrapper ul.gallery_controls');
}

// modal window, hide
function vcb_modalHide() {
	// fade out
	$j('#vcb_modal_wrapper').fadeOut(250, function() {
		// remove all markup
		$j('#vcb_modal_wrapper').remove();
		$j('#vcb_imagezoom_tnfx').remove();
	});
}

// modal window, show
function vcb_modalShow(html, delay) {
	// cya, remove any previous image zooms
	$j('#vcb_modal_wrapper').remove();
	// modal opening HTML
	var modal_html = '<div id="vcb_modal_wrapper" style="display: none;"><div class="bg"></div><div class="content">';
	// modal content HTML
	modal_html += html;
	// modal controls HTML
	modal_html += '<ul class="block nav block_controls_small block_controls_small_left modal_controls"><li class="close"><a href="javascript: void(0)" onclick="vcb_modalHide()" title="Close" class="tir">Close</a></li></ul>';
	// modal closing HTML
	modal_html += '</div></div>';
	// write the HTML
	$j('body').append(modal_html);
	// center vertically
	$j('#vcb_modal_wrapper .content').css({ top: ( ($j(window).height() / 2 - $j('#vcb_modal_wrapper .content').outerHeight() / 2) + $j(window).scrollTop() )+'px' });
	// animate nav
	vcb_navAnimate('#vcb_modal_wrapper ul.modal_controls');
	// and fade in
	setTimeout("$j('#vcb_modal_wrapper').fadeIn(250).find('.bg').css('cursor', 'pointer').click(function() { vcb_modalHide(); });", delay ? delay : 1);
}

// nav, replace css sprites w/ js animate
function vcb_navAnimate(nav) {
	$j(nav).children('li').each(function() {
		// apply only if not currently selected
		if (!$j(this).children('a').eq(0).hasClass('selected')) {
			// copy background style from <a> to parent <li>, use style instead of CSS to avoid ie8 bug (LAME O, once again, mind blown)
			var styles_to_copy = ['background-image', 'background-position-x', 'background-position-y', 'background-position'];
			var style = '';
			for (var i=0; i < styles_to_copy.length; i++) {
				style += styles_to_copy[i] + ': ' + $j(this).children('a').eq(0).css(styles_to_copy[i]) + ';';
			};
			$j(this).attr('style', style);
			// hide <a>
			$j(this).children('a').addClass('js_nav').css('opacity', '0');
			// apply bindings
			$j(this).mouseover(function() {
				// mouseover, animate if not disabled
				if (!$j(this).hasClass('disabled')) $j(this).children('a').animate({ opacity: 1 }, 300, 'swing');
			}).mouseout(function() {
				// mouseout, animate if not disabled
				if (!$j(this).hasClass('disabled')) $j(this).children('a').stop().animate({ opacity: 0 }, 350, 'swing');
			}).children('a').click(function() {
				// click, stop click if disabled
				if ($j(this).closest('li').hasClass('disabled')) return false;
				return true;
			});
		}
	});
}

// product, dependencies
function vcb_productDependencies() {
	// product radio dependency, enable depender if a radio option is selected, disable if not
	$j('.product_radio_dependency').each(function() {
		// the upgrade to enable / disable
		var $dependee = $j('#' + $j(this).attr('id').replace(/_requires/, ''));
		// size / style options req'd (string format input_name:value_1|value_2|value_3,input_name:value_1,value_2)
		var depender_groups = $j(this).attr('value').split(',');
		// for each depender group
		for (var i=0; i < depender_groups.length; i++) {
			// split the group (input_name:value_1|value_2)
			var pieces = depender_groups[i].split(':');
			// the depender input name
			var depender_input_name = pieces[0];
			// for each depender input (size or style radio selector, listen to all)
			$j('input[name="' + depender_input_name + '"]').each(function() {
				// apply binding to parent <li>
				$j(this).closest('li').click(function() { vcb_productRadioDependencyUpdate(depender_groups, $dependee); });
			});
			// trigger initial update
			vcb_productRadioDependencyUpdate(depender_groups, $dependee);
		};
		// the IDs of the sizes that enable the upgrade (comma separated)
		var dependers = $j(this).attr('value').split(',');
	});
	// checkbox dependency (enable depender if checkbox is clicked, disable if not)
	$j('.product_checkbox_dependency').each(function() {
		// the input to enable / disable
		var $dependee = $j('#' + $j(this).attr('id').replace(/_requires/, ''));
		// the input to watch
		var $depender = $j('#' + $j(this).attr('value'));
		// on depender change
		$depender.click(function() { vcb_productCheckboxDependencyUpdate($j(this), $dependee); });
		// and do initial update
		vcb_productCheckboxDependencyUpdate($depender, $dependee);
	});
}

// checkbox dependency, trigger update (enable depender if checkbox is clicked, disable if not)
function vcb_productCheckboxDependencyUpdate($depender, $dependee) {
	// debug
	vcb_debug('Updating checkbox dependency ' + '#' + $dependee.attr('id') + ' requires #' + $depender.attr('id') + ' which is' + ($depender.attr('checked') ? ' ' : ' not') + ' clicked.');
	// image (for changing upgrade images on dependency click)
	var $img = $j('#' + $dependee.attr('id') + '_image');
	// is checked
	if ($depender.attr('checked')) {
		// un-disable and hide
		$dependee.removeAttr('disabled').removeClass('display_none');
		$j('label[for="'+$dependee.attr('id')+'"]').removeClass('display_none');
		// if has image
		if ($img.length) {
			// show the right image and hide the others
			$img.siblings('img:not(".display_none")').fadeOut(300, function() { $j(this).addClass('display_none'); });
			$img.removeClass('display_none').fadeOut(0).fadeIn(300);
		}
	}
	// isn't checked
	else {
		// disable and hide
		$dependee.attr('disabled', 'disabled').addClass('display_none');
		$j('label[for="'+$dependee.attr('id')+'"]').addClass('display_none');
		// show the right image and hide the others
		$dependee.closest('li').find('img:not(:first)').fadeOut(300, function() { $j(this).addClass('display_none'); });
		$dependee.closest('li').find('img:eq(0)').removeClass('display_none').fadeOut(0).fadeIn(300);
	}
	// update product summary
	vcb_productSummaryUpdate();
}

// product radio dependency, trigger update, enable depender if a radio option is selected, disable if not
function vcb_productRadioDependencyUpdate(depender_groups, $dependee) {
	// debug
	vcb_debug('Updating radio dependency #' + $dependee.attr('id') + ' which requires ' + depender_groups.join(', ') + ' dependency');
	// the number of dependencies met
	var dependencies_met = 0;
	// for each dependency group
	for (var i=0; i < depender_groups.length; i++) {
		// split the group (input_name:value_1|value_2)
		var pieces = depender_groups[i].split(':');
		// the depender input name
		var depender_input_name = pieces[0];
		// the ids that if met should enable the dependee
		var depender_enable_ids = pieces[1].split('|');
		// loop through each depender enable ids
		for (var c=0; c < depender_enable_ids.length; c++) {
			// if dependency is met
			if ($j('#' + depender_enable_ids[c]).attr('checked')) {
				// increment # of dependencies met
				dependencies_met++;
				// don't continue cycling
				break;
			}
		};
	};
	// if the number of met dependencies matches the number of dependency groups
	if (dependencies_met == depender_groups.length) {
		// debug
		vcb_debug('Dependency met, showing');
		// show li
		$dependee.closest('li').removeClass('display_none');
		// if the dependee was previously checked, re-check it it
		if ($dependee.hasClass('vcb_was_checked')) $dependee.removeClass('vcb_was_checked').attr('checked', true);
	}
	// else nope, hide and disable
	else {
		// debug
		vcb_debug('Dependency not met, hiding');
		// if the dependency is currently shown, scroll to next li
		if ($dependee.closest('li').hasClass('selected')) vcb_slideListShow($dependee.closest('ul').attr('id'), 1);
		// hide li
		$dependee.closest('li').addClass('display_none');
		// if the dependee is currently clicked, save the state of the click
		if ($dependee.attr('checked')) $dependee.addClass('vcb_was_checked');
		// and un-click it
		$dependee.removeAttr('checked');
	}
	// and slide list style dependee
	if ($dependee.closest('ul').hasClass('slide_list')) {
		// update slide list position text
		vcb_slideListPositionTextUpdate($dependee.closest('ul').attr('id'));
	}
	// update product summary
	vcb_productSummaryUpdate();
}

// product, check for errors
function vcb_productValidate() {
	// error check
	var errors = [];
	if (!vcb_product.name) errors.push('Somehow you dont have a product name. How did you manage that?');
	if (vcb_product.name == 'messenger' && !vcb_product.style) errors.push('Please select a product style.');
	if (!vcb_product.size.name) errors.push('Please select a product size.');
	// alert if errors
	if (errors.length) {
		alert(errors.join("\r\n"));
		return false;
	}
	// else good to go
	return true;
}

// apply product summary bindings
function vcb_productSummaryBind() {
	// product size click
	$j('input[name="product_size"]').click(function() { vcb_productSummaryUpdate(); });
	// product colors change
	$j('#product_colors select').change(function() { vcb_productSummaryUpdate(); });
	// product upgrades change
	$j('#product_upgrades input').change(function() { vcb_productSummaryUpdate(); });
}

// initialize product summary
function vcb_productSummaryInitialize() {
	// create product json
	vcb_product = {
		name: '',
		colors: [],
		size: { name: '', price: 0 },
		style: '',
		price: 0,
		upgrades: []
	};
	// set product name
	vcb_product.name = $j('#product_name').attr('value');
	// update product summary
	vcb_productSummaryUpdate();
}

// update product summary (updates JSON, calculates total cost, and updates #product_summary html)
function vcb_productSummaryUpdate() {
	// vars
	var $product_size = $j('input[name="product_size"]:checked');
	var $product_style = $j('input[name="product_style"]:checked');
	var size_class = '';
	var size_title = '';
	var style_class = '';
	var style_title = '';
	// start JSON price
	vcb_product.price = 0;
	// if selected size
	if ($product_size.length) {
		// update JSON size
		vcb_product.size.name = $product_size.attr('value');
		vcb_product.size.price = parseInt($product_size.siblings('label').children('.price').text().substr(1));
		// update size class, size title and total cost
		size_class = $j('label[for="'+$product_size.attr('id')+'"]').children('.icon').get(0).className;
		size_title = vcb_product.size.name;
		vcb_product.price += vcb_product.size.price;
	}
	// else no selected size
	else {
		// update size class, size title and disable total price
		size_class = 'tir icon icon_none';
		size_title = '?';
		vcb_product.price = false;
	}
	// if selected style
	if ($product_style.length) {
		// update JSON style
		vcb_product.style = $product_style.attr('value');
		// update style class and style title
		style_class = $j('label[for="'+$product_style.attr('id')+'"]').children('.icon').get(0).className;
		style_title = vcb_product.style;
	}
	// else no selected style
	else {
		// update style class and style title
		style_class = 'tir icon icon_none';
		style_title = '?';
	}
	// update JSON colors
	vcb_product.colors = [];
	$j('#product_colors select').each(function(index) {
		// only add if enabled
		if (!($j(this).attr('disabled'))) vcb_product.colors.push($j(this).children('option:selected').text() + ' ' + $j(this).siblings('label[for='+$j(this).attr('id')+']').text());
	});
	// update JSON upgrades
	vcb_product.upgrades = [];
	$j('#product_upgrades input:checked').each(function(index) {
		// only add if enabled
		if (!($j(this).attr('disabled'))) {
			vcb_product.upgrades.push($j('label[for="'+$j(this).attr('id')+'"]').children('.name').text());
			if (vcb_product.price) vcb_product.price += parseInt($j(this).siblings('label').children('.price').text().substr(1))
		}
	});
	// update summary HTML
	if ($j('#product_summary .summary .icon_size').length) $j('#product_summary .summary .icon_size').removeClass().addClass(size_class + ' icon_size').attr('title', size_title);
	if ($j('#product_summary .summary .icon_style').length) $j('#product_summary .summary .icon_style').removeClass().addClass(style_class + ' icon_style').attr('title', style_title);
	$j('#product_summary .summary .colors p').html(vcb_product.colors.join(', '));
	$j('#product_summary .summary .upgrades p').html(vcb_product.upgrades.length ? vcb_product.upgrades.join(', ') : '(None)');
	$j('#product_summary .summary .price').html(vcb_product.price ? '$'+vcb_product.price : '?');
}

// radio icon bindings
function vcb_radioIconsBind(ul) {
	// apply bindings (w/ bg positioning animate bug fix)
	$j(ul).children('li').addClass('js').each(function() {
		// set background position (bg position animate bug fix), on <li> click
		$j(this).css('background-position', $j(this).hasClass('selected') ? '30px 0' : '30px -60px').click(function() {
			// auto display next section on click?
			var auto_display = $j(this).parent().eq(0).hasClass('auto_display') ? $j(this).parent().eq(0).attr('rel') : false;
			// if already selected do nothing
			if ($j(this).hasClass('selected')) return;
			// make sure the radio for the click is selected (if <li> instead of <label> inside it clicked)
			$j(this).find('input[type="radio"]').attr('checked', 'checked');
			vcb_productSummaryUpdate();
			// indexes of currently selected size and size to change to
			var index_selected  = $j(this).parent().children('li').index($j(this).parent().children('li.selected'));
			var index_to_select = $j(this).parent().children('li').index(this);
			// animate direction
			var animate_direction = index_selected > -1 ? (index_selected > index_to_select ? -1 : 1) : -1;
			// animate previously selected
			$j(this).siblings('li.selected').stop().removeClass('selected').animate({ backgroundPosition: '(30px '+(animate_direction * 60)+'px)' }, 300, 'swing');
			// animate newly selected, then auto show next section if set
			$j(this).addClass('selected').css('background-position', '30px '+(animate_direction * (index_selected > -1 ? -1 : 1) * 60)+'px').stop().animate({ backgroundPosition: '(30px 0px)' }, 300, 'swing', function() { if (auto_display) vcb_sectionAutoDisplayShow(auto_display); });
		});
	});
}

// auto display, initialize
function vcb_sectionAutoDisplayInitialize() {
	// cover all the sections (effectively hide them while positions and whatnot are calculated)
	$j('#content_cover').removeClass('display_none');
	// wait a little bit and then set current position in case the user clicked 'back' or 'refresh' and the browser is trying to respect their previous window settings
	setTimeout('vcb_sectionAutoDisplaySetCurrent()', 200);
}

// auto display, set current position
function vcb_sectionAutoDisplaySetCurrent() {
	// content offset
	var offset = $j('#content').offset();
	// current scroll pos
	var scroll_pos = 0;
	// if the window is scrolled, figure out current scroll pos
	if ($j(window).scrollLeft()) {
		// get the window's current scroll left to window center
		var window_center = $j(window).scrollLeft() + ($j(window).width() / 2);
		// get the section center positions
		var section_centers = [];
		$j('div.section').each(function(index) {
			var offset = $j(this).offset();
			section_centers.push(Math.floor(offset.left + ($j(this).width() / 2)));
		});
		// get the current scroll_pos by comparing the window's scroll pos to the section positions
		for (var i=0; i < section_centers.length; i++) {
			if (section_centers[i] + 10 >= window_center) {
				scroll_pos = i;
				break;
			}
		}
	}
	// else use scroll_pos
	else scroll_pos = $j('div.section').index($j('div.section.scroll_pos'));
	// set section.scroll_pos
	$j('div.section').removeClass('scroll_pos').eq(scroll_pos).addClass('scroll_pos');
	// set section visibility
	$j('div.section').each(function(index) {
		// if this section is or comes after the currently scroll_pos, hide it
		if (index >= scroll_pos) $j(this).css('display', 'none');
	});
	// hide the content cover
	$j('#content_cover').addClass('display_none');
	// show the current section
	vcb_sectionAutoDisplayShow(scroll_pos);
}

// auto display, show a section
function vcb_sectionAutoDisplayShow(target_index) {
	// force target_index to in
	target_index = parseInt(target_index);
	// sections
	$sections = $j('div.section');
	// if the section is already shown or trying to show non-existent section, do nothing
	if (!$sections.eq(target_index) || $sections.eq(target_index).css('display') == 'block') return;
	// if not the last section bind the show next stuff
	if (target_index != $sections.length - 1) {
		// if the section has radio icons, show next on radio select
		if ($sections.eq(target_index).find('ul.radio_icons').length) $sections.eq(target_index).find('ul.radio_icons').addClass('auto_display').attr('rel', target_index + 1);
		// else add next button, show next section on next button click
		else $sections.eq(target_index).append('<ul class="block nav section_controls"><li class="tir next"><a href="javascript: void(0)" onclick="vcb_sectionAutoDisplayShow('+(target_index + 1)+');">Next</a></li></ul>');
		// animate section controls
		vcb_navAnimate('ul.section_controls');
	}
	// if previous section has next button, fade it out
	$sections.eq(target_index - 1).find('ul.section_controls').fadeOut(350, function() { $j(this).remove(); });
	// fade in target section, scroll to if !1st section
	$sections.eq(target_index).fadeIn(350, function() { if (target_index > 0) vcb_sectionScrollerGoTo(target_index); });
}

// section scroll, go to div.sections[target_index]
function vcb_sectionScrollerGoTo(target_index) {
	// no uncertainty, non numbers or negativity
	if (target_index == undefined || typeof(target_index) != 'number' || target_index < 0) target_index = 0;
	// if not a valid target, do nothing
	if (target_index > $j('div.section').length - 1) return;
	// if we're scrolling already stop it
	$j.scrollTo.window().queue([]).stop(true);
	// scroll target
	var $target = $j('div.section').eq(target_index);
	// if target is not visible, decrement target_index until it is
	while ($target.css('display') != 'block') {
		// decrement target index
		target_index--;
		// avoid the infinite
		if (target_index < 0) break;
		// set new target
		$target = $j('div.section').eq(target_index);
	}
	// update scrolled_to target
	$j('div.section').removeClass('scroll_pos').eq(target_index).addClass('scroll_pos');
	// absolute position of scroll to section
	var offset = $target.offset();
	// set scroll to y_pos, if first section set to zero, else center section in window
	var y_pos = target_index == 0 ? 0 : offset.left - (($j(window).width() - $target.width()) / 2);
	// and scroll x pos to center section
	$j.scrollTo(y_pos, 350, { axis: 'x', easing: 'swing' });
	// debug
	vcb_debug('Scrolling to ' + y_pos);
	// update nav
	vcb_sectionScrollerUpdateNav(target_index);
}

// section scroller, move by x number of sections
function vcb_sectionScrollerMoveBy(move_by) {
	// get current index, add move_by to get target_index, and go to it
	vcb_sectionScrollerGoTo($j('div.section').index($j('div.section.scroll_pos')) + move_by);
}

// section scroller, initialize
function vcb_sectionScrollerInitialize() {
	// bind previous nav
	$j('#scroll_controls li.previous a').click(function() { vcb_sectionScrollerMoveBy(-1); });
	$j('#scroll_controls li.previous a').dblclick(function() { vcb_sectionScrollerGoTo(0); });
	// bind next nav
	$j('#scroll_controls li.next a').click(function() { vcb_sectionScrollerMoveBy(1); });
	$j('#scroll_controls li.next a').dblclick(function() { vcb_sectionScrollerGoTo($j('div.section').length - 1); });
	// if no section.scroll_pos set default
	if (!$j('div.section').hasClass('scroll_pos')) $j('div.section').eq(0).addClass('scroll_pos');
	// don't show any scroll bars
	$j('body').css('overflow', 'hidden');
	// inital nav update
	vcb_sectionScrollerUpdateNav(0, true, true);
}

// section scroll, update prev / next nav
function vcb_sectionScrollerUpdateNav(current_index, force_disable_prev, force_disable_next) {
	// force defaults
	if (!force_disable_prev) force_disable_prev = false;
	if (!force_disable_next) force_disable_next = false;
	// get number of visible sections
	var visible_sections = 0;
	$j('div.section').each(function(index) { if ($j(this).css('display') == 'block') visible_sections++; });
	// update prev (changes when current_index is 1st section)
	if (force_disable_prev || current_index == 0) $j('#scroll_controls .previous').addClass('disabled').css('opacity', .5).children('a').css('opacity', 0);
	else $j('#scroll_controls .previous').removeClass('disabled').css('opacity', 1);
	// update next (changes when current_index is last visible section)
	if (force_disable_next || current_index + 1 == visible_sections) $j('#scroll_controls .next').addClass('disabled').css('opacity', .5).children('a').css('opacity', 0);
	else $j('#scroll_controls .next').removeClass('disabled').css('opacity', 1);
}

// bit.ly a link, replace selected content
function vcb_shortenUrl(href, replace) {
	// submit ajax request
	$j.post('/shorten-url', { long_url: href }, function(text) { $j(replace).html(text) }, 'text');
}

// slide list animate
function vcb_slideListAnimate(slide_list) {
	// foreach
	$j(slide_list).each(function(index) {
		// don't re-apply if already setup
		if ($j(this).hasClass('slide_list_applied')) return;
		// don't apply if only has one <li>
		if ($j(this).children('li').length < 2) return;
		// generate ID if neccessary
		if (!$j(this).attr('id')) $j(this).attr('id', 'vcb_slide_list_' + new Date().getTime());
		// id
		var id = $j(this).attr('id');
		// add controls
		$j(this).after('<ul class="block nav block_controls_small block_controls_small_right gallery_controls"><li class="previous"><a href="javascript: void(0)" onclick="javascript: vcb_slideListShow(\''+id+'\', -1)" title="View Previous" class="tir">Previous</a></li><li class="next"><a href="javascript: void(0)" onclick="javascript: vcb_slideListShow(\''+id+'\', 1)" title="View Next" class="tir">Next</a></li></ul>');
		// add current position (1 of X)
		$j(this).after('<p id="' + $j(this).attr('id') + '_position" class="slide_list_position type_sans h3"><span class="slide_list_position_current">1</span> of <span class="slide_list_position_total">' + $j(this).children('li').length + '</span></p>');
	});
}

// gallery, show an image
function vcb_slideListShow(slide_list_id, move_by) {
	// the slide list
	var $slide_list = $j('#' + slide_list_id);
	// index to show
	var index_to_show = $slide_list.find('li').index($slide_list.find('li.selected')) + move_by;
	if (index_to_show < 0) index_to_show = $j('#'+slide_list_id+' li').length - 1;
	else if (index_to_show > $slide_list.find('li').length - 1) index_to_show = 0;
	// if the index to show is hidden, get the next index
	while($slide_list.find('li').eq(index_to_show).hasClass('display_none')) {
		// get the next index
		index_to_show += move_by > 0 ? 1 : -1;
		// loop to zero if neccessary
		if (index_to_show < 0) index_to_show = $slide_list.find('li').length - 1;
		// loop to last if neccessary
		else if (index_to_show > $slide_list.find('li').length - 1) index_to_show = 0;
	}
	// animate direction
	var animate_direction = move_by > 0 ? -1 : 1;
	// if current <li> is shown, animate it out
	if (!$slide_list.find('li.selected').hasClass('display_none')) $slide_list.find('li.selected').animate({ left: $j(this).width() * animate_direction }, 350, 'swing', function() { $j(this).removeClass('selected').css('left', 0); });
	// else just remove the class (have to do it this way for timing reasons)
	else $slide_list.find('li.selected').removeClass('selected');
	// show new <li>
	$slide_list.find('li').eq(index_to_show).css('left', $j(this).width() * animate_direction * -1).addClass('selected').animate({ left: 0 }, 500, 'swing');
	// update the position text
	vcb_slideListPositionTextUpdate(slide_list_id, index_to_show + 1);
}

// slide list, update position (1 of X) text
function vcb_slideListPositionTextUpdate(slide_list_id, item_shown) {
	// the slide list
	var $slide_list = $j('#' + slide_list_id);
	// the total number of <li>'s
	var total_items = $slide_list.find('li').length;
	// the total number of shown (non-disabled) <li>'s
	var total_items_shown = $slide_list.find('li:not(.display_none)').length;
	// if item shown not passed, figure it out
	if (item_shown == undefined) item_shown = $slide_list.find('li').index($slide_list.find('li.selected')) + 1;
	// the number of items hidden before the item shown (need to know to accurately show "X of Total")
	var items_hidden_before_item_shown = 0;
	// cycle through and minus if <li> before item_shown is hide
	$slide_list.find('li').each(function(index) {
		// the item number (index + 1)
		var item_to_check = index + 1;
		// if the item is hidden and comes before the item shown, increment the number of items hidden before the item shown
		if ($j(this).hasClass('display_none') && item_to_check < item_shown) items_hidden_before_item_shown++;
	});
	// adjust for number of hidden items before the item shown
	item_shown -= items_hidden_before_item_shown;
	// if item is less than 1, show as 1
	if (item_shown < 1) item_shown = 1;
	// update position current text
	$j('#' + slide_list_id + '_position .slide_list_position_current').html(item_shown);
	// update position total text
	$j('#' + slide_list_id + '_position .slide_list_position_total').html(total_items_shown);
}

// give some extra padding if window size is big enough
function vcb_viewPad() {
	// if window size large enough
	if ($j(window).width() > 1200 && $j(window).height() > 800) {
		// give some cushion
		$j('#content, #header, #cart_controls, #ownership, #scroll_controls, #vcenter').addClass('view_pad');
		// use larger sized gallery_iframe
		$j('#gallery_iframe').attr('width', '675').attr('height', '450');
	}
}

// jquery extension, reverse a result
$j.fn.reverse = Array.prototype.reverse;

// // jquery.backgroundposition.js, @author Alexander Farkas, v. 1.03
(function($){function toArray(strg){strg=strg.replace(/left|top/g,'0px');strg=strg.replace(/right|bottom/g,'100%');strg=strg.replace(/([0-9\.]+)(\s|\)|$)/g,"$1px$2");var res=strg.match(/(-?[0-9\.]+)(px|\%|em|pt)\s(-?[0-9\.]+)(px|\%|em|pt)/);return[parseFloat(res[1],10),res[2],parseFloat(res[3],10),res[4]];}
$.extend($.fx.step,{backgroundPosition:function(fx){if(!fx.bgPosReady){var start=$.curCSS(fx.elem,'backgroundPosition');if(!start){return false;}
start=toArray(start);fx.start=[start[0],start[2]];var end=toArray(fx.end);fx.end=[end[0],end[2]];fx.unit=[end[1],end[3]];fx.bgPosReady=true;}
var nowPosX=[];nowPosX[0]=((fx.end[0]-fx.start[0])*fx.pos)+fx.start[0]+fx.unit[0];nowPosX[1]=((fx.end[1]-fx.start[1])*fx.pos)+fx.start[1]+fx.unit[1];fx.elem.style.backgroundPosition=nowPosX[0]+' '+nowPosX[1];}});})(jQuery);

// // jquery.dimensions.js, Copyright (c) 2007 Paul Bakaus (paul.bakaus@googlemail.com) and Brandon Aaron (brandon.aaron@gmail.com || http://brandonaaron.net), Version: 1.2
(function($){$.dimensions={version:'1.2'};$.each(['Height','Width'],function(i,name){$.fn['inner'+name]=function(){if(!this[0])return;var torl=name=='Height'?'Top':'Left',borr=name=='Height'?'Bottom':'Right';return this.is(':visible')?this[0]['client'+name]:num(this,name.toLowerCase())+num(this,'padding'+torl)+num(this,'padding'+borr);};$.fn['outer'+name]=function(options){if(!this[0])return;var torl=name=='Height'?'Top':'Left',borr=name=='Height'?'Bottom':'Right';options=$.extend({margin:false},options||{});var val=this.is(':visible')?this[0]['offset'+name]:num(this,name.toLowerCase())+num(this,'border'+torl+'Width')+num(this,'border'+borr+'Width')+num(this,'padding'+torl)+num(this,'padding'+borr);return val+(options.margin?(num(this,'margin'+torl)+num(this,'margin'+borr)):0);};});$.each(['Left','Top'],function(i,name){$.fn['scroll'+name]=function(val){if(!this[0])return;return val!=undefined?this.each(function(){this==window||this==document?window.scrollTo(name=='Left'?val:$(window)['scrollLeft'](),name=='Top'?val:$(window)['scrollTop']()):this['scroll'+name]=val;}):this[0]==window||this[0]==document?self[(name=='Left'?'pageXOffset':'pageYOffset')]||$.boxModel&&document.documentElement['scroll'+name]||document.body['scroll'+name]:this[0]['scroll'+name];};});$.fn.extend({position:function(){var left=0,top=0,elem=this[0],offset,parentOffset,offsetParent,results;if(elem){offsetParent=this.offsetParent();offset=this.offset();parentOffset=offsetParent.offset();offset.top-=num(elem,'marginTop');offset.left-=num(elem,'marginLeft');parentOffset.top+=num(offsetParent,'borderTopWidth');parentOffset.left+=num(offsetParent,'borderLeftWidth');results={top:offset.top-parentOffset.top,left:offset.left-parentOffset.left};}return results;},offsetParent:function(){var offsetParent=this[0].offsetParent;while(offsetParent&&(!/^body|html$/i.test(offsetParent.tagName)&&$.css(offsetParent,'position')=='static'))offsetParent=offsetParent.offsetParent;return $(offsetParent);}});function num(el,prop){return parseInt($.curCSS(el.jquery?el[0]:el,prop,true))||0;};})(jQuery);

// jquery.scrollTo.js, @author Ariel Flesler, @version 1.4.1
(function($){var m=$.scrollTo=function(b,h,f){$(window).scrollTo(b,h,f)};m.defaults={axis:'xy',duration:parseFloat($.fn.jquery)>=1.3?0:1};m.window=function(b){return $(window).scrollable()};$.fn.scrollable=function(){return this.map(function(){var b=this,h=!b.nodeName||$.inArray(b.nodeName.toLowerCase(),['iframe','#document','html','body'])!=-1;if(!h)return b;var f=(b.contentWindow||b).document||b.ownerDocument||b;return $.browser.safari||f.compatMode=='BackCompat'?f.body:f.documentElement})};$.fn.scrollTo=function(l,j,a){if(typeof j=='object'){a=j;j=0}if(typeof a=='function')a={onAfter:a};if(l=='max')l=9e9;a=$.extend({},m.defaults,a);j=j||a.speed||a.duration;a.queue=a.queue&&a.axis.length>1;if(a.queue)j/=2;a.offset=n(a.offset);a.over=n(a.over);return this.scrollable().each(function(){var k=this,o=$(k),d=l,p,g={},q=o.is('html,body');switch(typeof d){case'number':case'string':if(/^([+-]=)?\d+(\.\d+)?(px)?$/.test(d)){d=n(d);break}d=$(d,this);case'object':if(d.is||d.style)p=(d=$(d)).offset()}$.each(a.axis.split(''),function(b,h){var f=h=='x'?'Left':'Top',i=f.toLowerCase(),c='scroll'+f,r=k[c],s=h=='x'?'Width':'Height';if(p){g[c]=p[i]+(q?0:r-o.offset()[i]);if(a.margin){g[c]-=parseInt(d.css('margin'+f))||0;g[c]-=parseInt(d.css('border'+f+'Width'))||0}g[c]+=a.offset[i]||0;if(a.over[i])g[c]+=d[s.toLowerCase()]()*a.over[i]}else g[c]=d[i];if(/^\d+$/.test(g[c]))g[c]=g[c]<=0?0:Math.min(g[c],u(s));if(!b&&a.queue){if(r!=g[c])t(a.onAfterFirst);delete g[c]}});t(a.onAfter);function t(b){o.animate(g,j,a.easing,b&&function(){b.call(this,l,a)})};function u(b){var h='scroll'+b;if(!q)return k[h];var f='client'+b,i=k.ownerDocument.documentElement,c=k.ownerDocument.body;return Math.max(i[h],c[h])-Math.min(i[f],c[f])}}).end()};function n(b){return typeof b=='object'?b:{top:b,left:b}}})(jQuery);