	// for browsers without native indexOf *cough* IE *cough*
	if(!Array.indexOf){
	    Array.prototype.indexOf = function(obj){
	        for(var i=0; i<this.length; i++)
	            if(this[i]==obj)
	                return i;
	        return -1;
	    }
	}
	Array.prototype.findAttribute = function(sAttributeName){
		for(var i=0; i<this.length; i++)
			for(var attribute in this[i])
				if(attribute==sAttributeName)
					return i;
		return -1;
	}

	var D=YAHOO.util.Dom;
	var E=YAHOO.util.Event;
	var disableAttributesEvent = new YAHOO.util.CustomEvent("disableAttributesEvent");
	var skuSelectionComplete = new YAHOO.util.CustomEvent("skuSetupComplete");
	var clickHandlerComplete = new YAHOO.util.CustomEvent("clickHandlerComplete");

	var aAttributes = [];
	var aAttributeSelections = [];
	var aContainers = [];
	var aSelectionData = [];
	var aAvailabilityMatrices = [];

	var aDisableEvents = [];
	var aDisableEventHandlers = [];

	var aEnableEvents = [];
	var aEnableEventHandlers = [];

	var aEnableNextEvents = [];
	var aEnableNextEventHandlers = [];
	aEnableNextEvents[-1] = new YAHOO.util.CustomEvent("EnableNext_-1"); // set this up so we can enable the first attribute

	var aRestoreNextEvents = [];
	var aRestoreNextEventHandlers = [];
	aRestoreNextEvents[-1] = new YAHOO.util.CustomEvent("RestoreNext_-1"); // set this up so we can restore the first attribute

	var aSetupEvents = [];
	var aSetupEventHandlers = [];

	var aClearSelectionEvents = [];
	var aClearSelectionEventHandlers = [];
  function fireEvent(element,event)
	{  
		if (document.createEventObject)
		{  
			// dispatch for IE  
			var evt = document.createEventObject();  
			return element.fireEvent('on'+event,evt)  
		}  
		else
		{  
			// dispatch for firefox + others  
			var evt = document.createEvent("HTMLEvents");  
			evt.initEvent(event, true, true ); // event type,bubbling,cancelable  
			return !element.dispatchEvent(evt);  
		}  
  }  

	function selectSingleAvailableOption(e)
	{
		var attributes = D.getElementsByClassName('selector_container','div','fancy_selectors');
		for(var i=0; i<attributes.length; i++)
		{
			if(! D.hasClass(attributes[i], 'disabled_attribute'))
			{
				var options = D.getElementsByClassName('available','a',attributes[i]);
				if(options.length ==1)
					fireEvent(options[0], 'click');
			}
		}
	}

	var ChangeImage = { 
			sport_state : null,
			color_state : null,
			model_state : null,
			handleSuccess:function(o){ 
				if(o.responseText)
				{
					var image = D.get( o.argument );
					image.src="http://static"+Math.floor(Math.random()*5+1)+".matrixsports.com/images/products/"+o.responseText;
					D.removeClass(image, 'hide');
				}
			}, 
			handleFailure:function(o){}, 
			startRequest:function( aAttributes )
			{ 
				if(!aAttributes)
					return;

				var sport = '';
				var color = '';
				var model = '';

				var sportIndex = aAttributes.findAttribute('SPORT');
				if(sportIndex != -1)
					sport = aAttributes[sportIndex]['SPORT'];

				var colorIndex = aAttributes.findAttribute('COLOR');
				if(colorIndex != -1)
					color = aAttributes[colorIndex]['COLOR'];
					
				var modelIndex = aAttributes.findAttribute('MODEL');
				if(modelIndex != -1)
					model = aAttributes[modelIndex]['MODEL'];

				var sGet = "base="+idProductIDBase+'&action=get_image&sSport='+sport+'&sColor='+color+'&sModel='+model;
                                YAHOO.util.Connect.asyncRequest('GET', '/ajax_image_functions.php?'+sGet+'&imageSize=l', ChangeSmallProductImageCallback);
				YAHOO.util.Connect.asyncRequest('GET', '/ajax_image_functions.php?'+sGet+'&imageSize=d', ChangeDetailProductImageCallback);
			} 
	}; 
	var ChangeSmallProductImageCallback = 
	{ 
			success:ChangeImage.handleSuccess, 
			failure:ChangeImage.handleFailure, 
			scope: ChangeImage,
			timeout: 3000,
			argument: 'productImage'
	}; 
	var ChangeDetailProductImageCallback =
	{ 
			success:ChangeImage.handleSuccess, 
			failure:ChangeImage.handleFailure, 
			scope: ChangeImage,
			timeout: 3000,
			argument: 'detail-img-productImage'
	}; 
	var ChangePrice = { 
			idProductIDFull : "",
			iQty : 1,
			handleSuccess:function(o){ 
				if(o.responseText)
				{
					var el = D.getElementsByClassName('pre-selection', 'div', 'cart_options')[0];
					if(!el) alert('Missing pre-selection price container');
					else D.addClass(el, 'hide');

					var el = D.getElementsByClassName('post-selection', 'div', 'cart_options')[0];
					if(!el) alert('Missing post-selection price container');
					else{
						try {
							var response = YAHOO.lang.JSON.parse(o.responseText);
							el.innerHTML = response['html'];
							eval(response['script']);
						} 
						catch (e) { } 
          	D.removeClass(el, 'hide');
					}
				}
			}, 
			handleFailure:function(o){}, 
			startRequest:function( idProductIDFull, iQty )
			{ 
				var sGet = "full="+idProductIDFull+'&action=get_price&iQty='+iQty;
				YAHOO.util.Connect.asyncRequest('GET', '/ajax_product_functions_2.php?'+sGet, ChangePriceCallback);
			} 
	}; 
	var ChangePriceCallback = 
	{ 
			success:ChangePrice.handleSuccess, 
			failure:ChangePrice.handleFailure, 
			scope: ChangePrice,
			timeout: 3000
	}; 
	function startChangePriceRequest()
	{
		/* start ajax request to get the price based on the full sku selected and the quantity. The ChangePriceCallback
       will switch the visibilities on pre-selection and post-selection price containers if it finds a price successfully */

		// idProductIDFull is a global variable set and cleared in the finalSetupHandler() and finalClearSelectionHandler() respectively
		iQuantity = D.get('iQuantity').value;

		if( idProductIDFull && iQuantity )
			ChangePrice.startRequest( idProductIDFull, iQuantity );
	}


	function setupAvailabilities(iAttributeNumber, preceding_attributes)
	{
		if(preceding_attributes)
			partial_sku = idProductIDBase + '-' + preceding_attributes;
		else
			partial_sku = idProductIDBase;

		var attribute_options = D.getElementsByClassName('selector','a',aContainers[iAttributeNumber]);
		var aAvailable = [];
		var aUnavailable = [];
		for(var i=0; i<attribute_options.length; i++)
		{
			if( aAvailabilityMatrices[iAttributeNumber][partial_sku + '-' + attribute_options[i].getAttribute('value')] == 1)
				aAvailable.push( attribute_options[i] );
			else
				aUnavailable.push( attribute_options[i] );
		}
		D.removeClass(aAvailable, 'unavailable');
		D.addClass(aAvailable, 'available');

		D.removeClass(aUnavailable, 'available');
		D.addClass(aUnavailable, 'unavailable');
	}
	function clickHandler(e) { 
		//get the resolved (non-text node) target: 
		var elTarget = E.getTarget(e);    
		if(! elTarget)
			return;

		while ( ! D.hasClass(elTarget,'selector_container')) // set an upper limit in the DOM traversal
		{
			//are you an A? 
			if(elTarget.nodeName.toUpperCase() == "A") { 
				//yes, an A: so do stuff and then stop looking: 

				var container = D.getAncestorByClassName( elTarget, 'selector_container' );
				var iAttributeNumber = aContainers.indexOf( container.id );

				// dont allow a disabled, unavailable, or selected selector do anything
				if ( D.hasClass( container, "disabled_attribute") || D.hasClass( elTarget, 'unavailable') || D.hasClass(elTarget, 'selected'))
					return;

				// store the selection and the label
				aSelectionData[iAttributeNumber].selected_label = elTarget.getAttribute('label');
				aSelectionData[iAttributeNumber].selected_value = elTarget.getAttribute('value');
				aAttributeSelections[iAttributeNumber] = {};
        aAttributeSelections[iAttributeNumber][aAttributes[iAttributeNumber]] = elTarget.getAttribute('value');

				// display the label
				D.get("selection_label_"+iAttributeNumber).innerHTML = aSelectionData[iAttributeNumber].selected_label;

				// remove any other selected and set this one as selected
				D.removeClass( D.getElementsByClassName( 'selector', 'a', elTarget.parentNode ), 'selected' );
				D.addClass( elTarget, 'selected' );

				// tell any "lower" attributes to clear their selections since a new one was made at a higher level
				aClearSelectionEvents[iAttributeNumber].fire();

				// disable any "lower" attributes
				aDisableEvents[iAttributeNumber].fire();

				if(aSelectionData[iAttributeNumber].preceding_partial_sku != '')
					valueToSend = aSelectionData[iAttributeNumber].preceding_partial_sku + '-'+aSelectionData[iAttributeNumber].selected_value;
				else
					valueToSend = aSelectionData[iAttributeNumber].selected_value;

				// tell the next attribute to setup its preceding_partial_sku
				aSetupEvents[iAttributeNumber].fire( {"preceding_partial_sku" : valueToSend} );

				aEnableNextEvents[iAttributeNumber].fire({"preceding_partial_sku" : valueToSend,
																			            "fired_from" : iAttributeNumber });

				// fire this event so it's handler can auto-select the next attribute if it has only a single available option
				clickHandlerComplete.fire();
				break; 
			} else { 
				//wasnt the container and wasnt an A; so lets step up the DOM and keep looking: 
				elTarget = elTarget.parentNode; 
			} 
		}
	} 
	function mouseoverHandler(e) {
		//get the resolved (non-text node) target: 
		var elTarget = E.getTarget(e);    
		if(! elTarget)
			return;

		while ( ! D.hasClass(elTarget,'selector_container' )) // set an upper limit in the DOM traversal
		{
			//are you an A? 
			if(elTarget.nodeName.toUpperCase() == "A") { 
				//yes, an A: so do stuff and then stop looking: 

				var container = D.getAncestorByClassName( elTarget, 'selector_container' );
				var iAttributeNumber = aContainers.indexOf( container.id );

				// dont allow a disabled or unavailable selector do anything
				if ( D.hasClass( container, "disabled_attribute") || D.hasClass( elTarget, 'unavailable') || D.hasClass( elTarget, 'selected') )
					return;

				aSelectionData[iAttributeNumber].temp_preceding_partial_sku = elTarget.getAttribute('value');
				
				// add a mouseover class for styling
				D.addClass(elTarget, 'mouseover');
				D.get('selection_label_'+iAttributeNumber).innerHTML = elTarget.getAttribute('label');

				/*if(iAttributeNumber > 0)
					var tmp = aAttributeSelections.slice(iAttributeNumber - 1);
				else
					var tmp = [];
				
				tmp[iAttributeNumber] = {};
        tmp[iAttributeNumber][aAttributes[iAttributeNumber]] = elTarget.getAttribute('value');
				ChangeImage.startRequest( tmp );*/

				// disable any "lower" attributes (they will be listening for this event)
				aDisableEvents[iAttributeNumber].fire();

				if( aSelectionData[iAttributeNumber].preceding_partial_sku != '')
					valueToSend = aSelectionData[iAttributeNumber].preceding_partial_sku + '-'+ aSelectionData[iAttributeNumber].temp_preceding_partial_sku;
				else
					valueToSend = aSelectionData[iAttributeNumber].temp_preceding_partial_sku;

				aEnableNextEvents[iAttributeNumber].fire({"preceding_partial_sku" : valueToSend,
																			            "fired_from" : iAttributeNumber});
				break; 
			} else { 
				//wasnt the container, but wasnt an A; so lets step up the DOM and keep looking: 
				elTarget = elTarget.parentNode; 
			} 
		}
	}
	function mouseoutHandler(e) {
		//get the resolved (non-text node) target: 
		var elTarget = E.getTarget(e);    
		while ( ! D.hasClass(elTarget, 'selector_container') ) // set an upper limit in the DOM traversal
		{
			//are you an A? 
			if(elTarget.nodeName.toUpperCase() == "A") { 
				//yes, an A: so do stuff and then stop looking: 

				var container = D.getAncestorByClassName( elTarget, 'selector_container' );
				var iAttributeNumber = aContainers.indexOf( container.id );

				// dont allow a disabled or unavailable selector to do anything
				if ( D.hasClass( elTarget, 'unavailable') || D.hasClass( container, "disabled_attribute"))
					return;

				aSelectionData[iAttributeNumber].temp_preceding_partial_sku = aSelectionData[iAttributeNumber].preceding_partial_sku;
				if(aSelectionData[iAttributeNumber].selected_value)
					aSelectionData[iAttributeNumber].temp_preceding_partial_sku += '-'+aSelectionData[iAttributeNumber].selected_value;
				
				D.removeClass(elTarget, 'mouseover');
				D.get('selection_label_'+iAttributeNumber).innerHTML = '';

				aRestoreNextEvents[iAttributeNumber - 1].fire();

				// disable any "lower" attributes (they will be listening for this event)
				aDisableEvents[iAttributeNumber].fire()

				// tell the next attribute to restore itself (and chain the event)
				aRestoreNextEvents[iAttributeNumber].fire();
				//ChangeImage.startRequest( aAttributeSelections );

				aValuesToSend = []
				if(aSelectionData[iAttributeNumber].preceding_partial_sku != '')
					aValuesToSend.push(aSelectionData[iAttributeNumber].preceding_partial_sku);
				if(aSelectionData[iAttributeNumber].selected_value != '')
					aValuesToSend.push(aSelectionData[iAttributeNumber].selected_value);
				valueToSend = aValuesToSend.join('-');

				// only enable the next attribute if the current one has a selected value
				if(aSelectionData[iAttributeNumber].selected_value)
					aEnableNextEvents[iAttributeNumber].fire({"preceding_partial_sku" : valueToSend, "fired_from" : iAttributeNumber});

				break; 
			} else { 
				//wasnt the container, but wasnt an A; so lets step up the DOM and keep looking: 
				elTarget = elTarget.parentNode; 
			} 
		}
	}

	function finalSetupHandler(e, args)
	{
		/* This method switches the visibility of various elements, begins */

		ChangeImage.startRequest(aAttributeSelections);
		if(args[0].preceding_partial_sku)
			idProductIDFull = idProductIDBase + '-' + args[0].preceding_partial_sku;
		else
			idProductIDFull = idProductIDBase;

		startChangePriceRequest();

		/* if base==full, then dont show full */
		if(idProductIDBase != idProductIDFull)
		{
			var el = D.get('idProductIDFull');
			if(!el) alert('Missing element with id=idProductIDFull');
			else {
				el.innerHTML = idProductIDFull;
				D.removeClass(el, 'hide');
			}
		}

		var el = D.get('form_idProductIDFull');
		if(!el || el.nodeName.toUpperCase() != 'INPUT') alert('Missing <input> with id=form_idProductIDFull');
		else {
			el.setAttribute('value', idProductIDFull);
			D.removeClass(el, 'hide');
		}

		var el = D.get('add_to_cart');
		if(!el) alert('Missing element with id=add_to_cart');
		else D.removeClass(el, 'hide');

		var el = D.get('select_product_message');
		if(!el) alert('Missing element with id=select_product_message');
		else D.addClass(el, 'hide');

		//skuSelectionComplete.fire( { "idProductIDFull" : idProductIDBase + '-' + args[0].preceding_partial_sku } );
	}
	function finalClearSelectionHandler(e)
	{
		/* This method switches the visibility of various elements. */
		idProductIDFull = '';

		var el = D.get('idProductIDFull');
		if(!el) alert('Missing element with id=idProductIDFull');
		else {
			el.innerHTML = idProductIDFull;
			D.addClass(el, 'hide');
		}

		var el = D.get('form_idProductIDFull');
		if(!el || el.nodeName.toUpperCase() != 'INPUT') alert('Missing <input> with id=form_idProductIDFull');
		else{
			el.setAttribute('value', idProductIDFull);
			D.addClass(el, 'hide');
		}

		/* post-selection and pre-selection price containers get visibility switched in the PriceChangeCallback */
		var el = D.getElementsByClassName('post-selection', 'div', 'cart_options')[0];
		if(!el) alert('Missing post-selection price container');
		else D.addClass(el, 'hide');

		var el = D.getElementsByClassName('pre-selection', 'div', 'cart_options')[0];
		if(!el) alert('Missing pre-selection price container');
		else D.removeClass(el, 'hide');

		var el = D.get('add_to_cart');
		if(!el) alert('Missing element with id=add_to_cart');
		else D.addClass(el, 'hide');

		var el = D.get('select_product_message');
		if(!el) alert('Missing element with id=select_product_message');
		else D.removeClass(el, 'hide');
	}
