/* 
	PRODUCT SELECTOR FOR SEARCH
	ProductSelector.js,v 1.12 2008/11/17 04:41:53 mok Exp
*/
/*--- 

	OVERVIEW:
	
		* This script tranforms a normal form select into a DHTML unordered list
		  that has the same functionality as a select menu
		
		* Expected HTML for Widget:
			<select id="search-in">
				<option value="all-categories">Search in all...</option>
				<option value="acrobat" class="icon primary">Acrobat</option>
				<option value="acrobatcapture">Acrobat Capture</option>
				<option value="aftereffects" class="icon">After Effects</option>
				...
			</select>
		* The value of the option (<option value="acrobat">) will be used as the ID and CLASS of the <LI>
		* Options with class of "icon" will create <li class="icon acrobat">
		* "primary" will indicate that the <LI> will appear in a featured product list
		
				OUTPUT --> <li id="acrobat" class="acrobat icon">
				OUTPUT --> <li id="acrobatcapture" class="acrobatcapture off">Acrobat Capture</li>
				OUTPUT --> <li id="aftereffects" class="aftereffects icon off">	
	
	_PRODUCTSSELECTORINSTANCE
		* Extend Behaviors Object
		* Instantiate new ProductSelector with:
		
			document.observe('dom:loaded', function() {
				var selectMenu = $('SELECT_MENU_ID');
				new ProductSelector(selectMenu);
			});
			
		* Configuring Options : new ProductSelector(selectID, labels, cfg, css)
		
			 - selectID : STRING select menu HTML ID
			 - labels	: OBJECT of strings
					allProducts				: "All Products",
					viewAllProducts			: "View all products &#8250;",
					viewFeaturedProducts	: "View featured products &#8250;",
					truncateChars			: 24,
					truncateIconChars		: 22,
					truncateReplace			: " ..."
			
			- cfg : OBJECT
					positionOverride	:	false/true,		// no CSS left and top positioning, set to auto
					customSubmit		:	function		// customize submit function of select menu
			
			- css : OBJECT
					controller			: "category-controller",
					category			: "category",
					categories			: "categories",
					primaryProducts		: "primary-products",
					viewAllProducts		: "view-all-products",
					icon				: "icon",
					focused				: "focused",
					hidden				: "hidden",
					active				: "active",
					inactive			: "off",
					primary				: "primary",	
					selected			: "selected",
					unpositioned		: "unpositioned"
		
		* Generates HTML from <select> and invokes toDom() of the _OPTION class and appends result code to the page
		* Initializes event observers for transformed select menu
		* Initializes handlers for different events (select:change, keypress, clicks)
			
	_OPTION: 	
		* _OPTION is instantiated within _SELECT.MENU and will look for <options> within 
		  SELECT_MENU_ID and transforms them into a list that has the same behaviors as
		  a regular <select> menu
	
---*/

var ProductSelector = (function(replacedSelect,cfg) {
	
	var _Select = {};	
		_Select.States = {
			allOptions			: "all",
			topOptions			: "short",
			closed				: "closed",
			opened				: "opened"
		}		
	/*--- HANDLE EVENTS based on opened or closed state ---*/
	var _state_translation = {
		opened: {
			
			click: function(memo) {			
				this.toggleController();			
				this.setState(_Select.States.closed);
				this.toggleMenu();
				this.undoSelectFix();				
			},			
			focus : function() {
				this.controller.focus();
				
				if(this.optionState == _Select.States.allOptions) {
					this.scrollToOption();	
				}
			},			
			keypress: function(memo) {
										
				switch(this.keyCode) {					
					case "S_:TAB / 9":
						this.toggleController();
						this.handleKeyTab();
						if(this.element.previous()) this.element.previous().focus();
						break;
					case ":ESCAPE / 27":
						this.toggleController();
						this.handleKeyTab();
						this.undoSelectFix();
						break;
					case ":TAB / 9":
						this.toggleController();
						this.handleKeyTab();
						this.undoSelectFix();
						break;
					case ":DOWN / 40":
						this.handleKeyDown();
						break;
					case ":UP / 38":
						this.handleKeyUp();
						break;
					case ":RETURN / 13":
						this.handleKeyReturn();
						break;
					default:
						return;
				}				
			},			
			"select:change" : function(memo) {			
				this.handleSelectChange(memo);
				this.undoSelectFix();
			}
		},
		closed: {			
			click: function(memo) {
				this.toggleController();
				this.setState(_Select.States.opened);
				this.toggleMenu();
				this.doSelectFix();	
				if(this.optionState == _Select.States.allOptions) {
					this.scrollToOption();	
				}
			},		
			focus : function(memo) {
				this.toggleController();
			},		
			keypress: function(memo) {
								
				switch(this.keyCode) {
					case "S_:TAB / 9":
						this.controllerSpan.removeClassName(this.css.focused); 
						return;
						break;
					case ":TAB / 9":
						this.controllerSpan.removeClassName(this.css.focused); 
						break;
					case ":DOWN / 40":
						this.handleKeyDown();
						break;
					case ":UP / 38":
						this.handleKeyUp();
						break;
					case ":RETURN / 13":
						this.handleKeyReturn();
						break;
					default:
						return;
				}
	
			},
			"select:change" : function(memo) {
				this.handleSelectChange(memo);	
				this.undoSelectFix();
			}
		}	
	};
	/*--- HANDLE EVENT NAME THE FIGURE OUT WHAT TO DO! ---*/
	function _get_state_translation(eventname) {
		
		var translation = _state_translation[this.currentstate];
		
		if(!translation) return;
		var method = translation[eventname];
								
		if(!method) return Prototype.emptyfunction;
		
		return method.bind(this);
	}
	/*--- CREATE <LI>'s FROM <OPTIONS>'s ---*/
	var _Option = Class.create({
			
		initialize : function(element,trigger,queried,cfg) {	
			
			this.css=cfg;
			this.id = element.index;
			this.label = element.text;			
			this.value = element.value;
			if(this.value=="disabled") { return }
			this.icon = (Element.hasClassName(element,this.css.icon)) ? " " + this.css.icon : "";
			this.selected = element.selected;
			this.primary = (Element.hasClassName(element,this.css.primary));
			this.queried = queried;
				
			this.state;
			this.trigger=$(trigger);
			this.element = null;
		
			this.trigger.observe("select:change", this.handleStateChange.bindAsEventListener(this)); 
			
		},
		/*--- WATCH CHANGE OF TRIGGER <UL> AND CHANGE CSS CLASS OF SELECTED ---*/
		handleStateChange: function(e) {
			if(e.memo.id==this.id) {
				this.selected = true;
			} else {
				this.selected = false;
			}
					
			this.setView(this.selected);
		},
		/*--- <LI> CSS CLASS UPDATE ---*/
		setView: function() {
			
			if(this.selected) {
				this.element.addClassName(this.css.selected);
			} else {
				this.element.removeClassName(this.css.selected);
			}
			
		},
		/*--- INVOKED FROM _SELECT.MENU, CREATE LI OBJECT WITH LISTENERS ---*/
		toDom: function() {
		
			var li = new Element('li');
			li.className = this.value.gsub(/\s/,"") + this.icon;
			li.id = this.value.gsub(/\s/,"");
			li.innerHTML = this.label;
	
			this.element = li;
			
			if(!this.primary) { li.addClassName(this.css.inactive); }
			
			if(li.id == this.queried) {
				li.addClassName(this.css.selected);
			}
			
			li.observe('click',this.fireSelected.bindAsEventListener(this));
			
			return li;
			
		},
		/*--- TELL THE <UL> TO UPDATE ITS VALUE IF SELECTED ---*/
		fireSelected : function(event) {
			this.trigger.fire('select:change', { id: this.id });
		}
	});
	
	/*--- SELECT MENU BEHAVIORS ---*/
	_Select.Behaviors = function(){};
	_Select.Behaviors.prototype = {
		doSelectFix : function() {			
			if(adobe.hostEnv.ua.indexOf('msie')  && adobe.hostEnv.ieV == 6) {
				/*--- Fix for http://watsonexp.corp.adobe.com/#bug=2308820 and http://watsonexp.corp.adobe.com/#bug=2307741 ---*/
				this.ie6Style = $$('style[title="IE6"]');
				if(this.ie6Style.length > 0) {
					this.ie6StyleCSS = $$('style[title="IE6"]')[0];
					$$('style[title="IE6"]')[0].remove();
				}
				adobe.SelectFix.doFix();
			}
		},
		undoSelectFix : function() {
						
			if(adobe.hostEnv.ua.indexOf('msie')  && adobe.hostEnv.ieV == 6) {
				/*--- Fix for http://watsonexp.corp.adobe.com/#bug=2308820 and http://watsonexp.corp.adobe.com/#bug=2307741 ---*/				
				if($('search-filter')) { Element.insert($('layoutLogic'),this.ie6StyleCSS); }
				adobe.SelectFix.undoFix();
			}
		},
		/*--- THE 'ALL-CATEGORIES' OPTION WAS SELECTED SO SEND 0 AS SELECT VALUE ---*/
		handleAllClick : function(event,memo) {
			memo = { id : 0 }
			this.replacedSelect.selectedIndex = memo.id;
			return _get_state_translation.call(this, "select:change")(memo);			
		},
		/*--- FIND THE VALUE OF A SELECTED ITEM IN 'FEATURED PRODUCTS' LIST WITHIN THE 'ALL PRODUCTS' LIST 
			  BASED ON ITS ID THEN SCROLL TO IT ---*/
		handleAllOptionIndex: function() {
			// if option was selected in primary list, find the index (place) in the whole list so we can set the selectedIndex
			// so if "view all" is selected, it's shown first				
			var topValue = (this.selectedIndex == -1) ? -1 : this.replacedSelect.value;
			var holdValue = this.holdValue;  
			var topToAllValue;
			var foo = this.options.find(function(selected,index){
				if(selected.value == topValue) topToAllValue = index;
				/*--- value was set in in <all products> 
				// switched to <top products> view
				// product picked in <top products>
				// make sure to remove selection of <LI> in <all products> ---*/
				if(holdValue != "" && holdValue != topValue)  {
					if(selected.value == holdValue) {
						selected.element.removeClassName(this.css.selected);	
					}
				}
			});
			this.selectedIndex = (topToAllValue) || 0;
			this.scrollToOption();
		},
		/*--- FIND THE VALUE OF A SELECTED ITEM IN 'ALL PRODUCTS' LIST WITHIN THE 'FEATURED PRODUCTS' LIST ---*/
		handleTopOptionIndex: function() {
							
			var allValue = this.replacedSelect.value;
			
			var allToTopIndex;
			var foo = this.primaryOptions.find(function(selected,index){
				if(selected.id == allValue) {								
					allToTopIndex = index;
				}
			});
			/*--- if <all product> list item isn't within the <top product> list, 
			then keep whatever value the select may have had or set to "" ---*/
			this.holdValue = (allToTopIndex == null) ? this.replacedSelect.value : "";
			this.selectedIndex = (allToTopIndex) || -1;		
									
		},
		/*--- HANDLE TOGGLE OF 'FEATURED PRODUCTS' OR 'ALL PRODUCTS' ---*/
		handleViewAll : function(event,memo) {
			// Featured Products View
			if(this.productcontainer.hasClassName(this.css.primaryProducts)) {
				this.viewAllController.innerHTML = this.labels.viewFeaturedProducts;
				this.productcontainer.removeClassName(this.css.primaryProducts);
				
				this.handleAllOptionIndex.bind(this);
				this.optionState = _Select.States.allOptions;
			// All Products View
			} else {
				this.viewAllController.innerHTML = this.labels.viewAllProducts;
				this.productcontainer.addClassName(this.css.primaryProducts);
				
				this.handleTopOptionIndex.bind(this)
				this.optionState = _Select.States.topOptions;
			}
			this.currentstate = _Select.States.opened;
			return _get_state_translation.call(this, "focus")(0); 
					
		},
		/*--- 'FEATURED PRODUCTS' OR 'ALL PRODUCTS' ---*/
		inPrimaryState : function() {
			return this.optionState == _Select.States.topOptions;
		},
		/*--- SET MENU STATE ---*/
		setState : function(state) {
			this.currentstate = state;
		},
		/*--- TOGGLE DISPLAY OF CONTROLLER ---*/
		toggleController : function() {
			if(this.isOpenedState()) { 
				this.controllerSpan.removeClassName(this.css.focused); 
			} else { 
				this.controllerSpan.addClassName(this.css.focused);
			}
		},
		/*--- TOGGLE DISPLAY STATE OF MENU ---*/
		toggleMenu : function() {		
			this.container.toggleClassName(this.css.hidden);
			this.container.toggleClassName(this.css.active);
		},
		/*--- SCROLL TO OPTION IN 'ALL PRODUCT' STATE ---*/
		scrollToOption : function() {	
			// index is set first in handleTopOptionIndex or handleAllOptionIndex
			// then find its position in the <all products> list and scroll to it
			var index = (this.selectedIndex<0) ? 0 : this.selectedIndex;
			var optY = this.options[index].element.positionedOffset().top;
			var optH = this.options[index].element.getHeight();
			this.productcontainer.scrollTop = optY - (optH*3);				
		},
		/*--- DETERMINE WHICH ARRAY TO USE BASED ON 'FEATURED PRODUCTS' OR 'ALL PRODUCT' STATE ---*/
		getOptionList : function() {
			return (this.inPrimaryState()) ? this.primaryOptions : this.options;	
		},
		/*--- GET STATE OF MENU---*/
		isOpenedState : function() {
			return this.currentstate == _Select.States.opened;
		},
		/*--- UPDATE CONTROLLER AND TRUNATE TEXT ---*/
		updateControllerText : function(text, hasIcon) {
			var truncateAt = (hasIcon) ? this.labels.truncateIconChars : this.labels.truncateChars;	
			var truncatedText = text.truncate(truncateAt, this.labels.truncateReplace);
			this.controller.innerHTML = truncatedText;
		},
		/*--- CHECK IF ELEMENT HAS ICON ---*/
		hasIcon : function(element) {
			return (element.indexOf('icon') > -1);
			
		},
		/*--- HANDLE SELECT CHANGE BEHAVIOR BASED ON ID AND MENU STATE---*/
		handleSelectChange : function(memo) {
			
			this.selectedIndex = memo.id;
			
			if(this.isOpenedState()) {
				// handle if it was click happens outside of opened menu
				if (memo.type == "outclick") {
					// make sure to close the menu
					this.setState(_Select.States.closed);
					this.toggleMenu(); 	
					// get the option value based on 'all products' or 'featured products'
					if(this.inPrimaryState()) {
						this.handleTopOptionIndex();
					} else {
						this.handleAllOptionIndex.bind(this);
					}
					// set selected value of menu based on menu type
					if(!this.inPrimaryState() && this.selectedIndex == -1) { this.controllerSpan.removeClassName(this.css.focused); return; }
					var selected = (this.inPrimaryState()) ? $(this.primaryOptions[this.selectedIndex]) : $(this.options[this.selectedIndex].element);							
					this.replacedSelect.value = (this.selectedIndex > -1) ? selected.id : "";					
					// update the controller
					var productText = (this.selectedIndex > -1) ? selected.innerHTML : this.labels.allProducts;
						productText = (this.selectedIndex == 0) ? this.labels.allProducts : productText;
					if(selected != "undefined")	{ this.updateControllerText(productText); }
					this.controller.className = (this.selectedIndex > -1) ? selected.className : ""; 
					this.controllerSpan.removeClassName(this.css.focused);
					
					return;
				}
				// all products was selected		
				if(memo.id == 0) {
					this.controller.innerHTML = this.labels.allProducts;
					this.controllerSpan.removeClassName("category-controller-focused");
					this.controller.className="";
					this.setState(_Select.States.closed);
					this.toggleMenu(); 
					this.replacedSelect.options[0].value="";
				// set value of selected option and controller 	
				} else {
					var selected = $(this.options[this.selectedIndex].value);
					var selectedIcon = this.options[this.selectedIndex].value + this.options[this.selectedIndex].icon;
					var hasIcon = (this.options[this.selectedIndex].icon != "");
					this.updateControllerText(selected.innerHTML, hasIcon);
					this.controller.className = selectedIcon;
					this.replacedSelect.value = (this.selectedIndex > -1) ? selected.id : "";
				}
			}		
			this.getURL();
		},
		getURL : function() {
			if(typeof getURL == 'function') { 
				getURL();
			} else {
				if(this.cfg.customSubmit) {
					this.cfg.customSubmit();
				}
			}
		},
		/*--- HANDLE TAB KEY ACTION : Close menu and set select value if set---*/
		handleKeyTab : function() {		
			// if not "all products"
			if(this.selectedIndex != -1) {
				var optValue = (this.inPrimaryState()) ? this.primaryOptions[this.selectedIndex] : this.options[this.selectedIndex].element;	
				if(optValue != "undefined") {
					this.controller.innerHTML = optValue.innerHTML;
					this.controller.className = optValue.className;
					this.replacedSelect.value = optValue.id; 
				}
			}
			this.setState(_Select.States.closed);
			this.toggleMenu();
		},
		/*--- HANDLE RETURN KEY ACTION : Close menu and set select value if set---*/
		handleKeyReturn : function() {
			
			var optArray = (!this.inPrimaryState()) ? this.options : this.primaryOptions;
					
			if(this.selectedIndex >= 0) {
				var optValue = (this.inPrimaryState()) ? optArray[this.selectedIndex] : optArray[this.selectedIndex].element;			
				//if option was selected in primary list, find the index (place) in the whole list so we can set the selectedIndex
				// so if "view all" is selected, it's shown first.
				if(this.isOpenedState()) {
					this.selectedIndex = this.options.find(function(selected){
						if (selected.value == optValue.id)  return selected;
					}).id;
					
					var hasIcon = this.hasIcon(optValue.className);
					var productText = (this.selectedIndex > 0) ? optValue.innerHTML : this.labels.allProducts;
					
					this.updateControllerText(productText,hasIcon);
					
					this.controller.className = optValue.className;
					this.controllerSpan.removeClassName(this.css.focused);
						
					this.setState(_Select.States.closed);
					this.toggleMenu();
					this.replacedSelect.value = optValue.id.toString();				
					this.searchform.searchin.value = this.replacedSelect.value;
				}
				optValue.addClassName(this.css.selected);
			} else {
				 var optValue = this.options[0].element;
				 this.replacedSelect.value = "";	
			}
			this.getURL();
			return;
		},	
		/*--- UP KEY NAVIGATION ---*/
		handleKeyUp : function() {
			
			var optArr = (this.inPrimaryState()) ? this.primaryOptions : this.options;
			
			if(this.isSafari2) { this.selectedIndex = this.selectedIndex-1; } else { this.selectedIndex--; }	
			
			if(this.isOpenedState()) {							
							
				if(this.selectedIndex <= 0) {
					this.allController.addClassName(this.css.selected);	
				}
					
				if(this.selectedIndex < 0 && this.inPrimaryState()) {
					this.selectedIndex = -1; // all options
				}
				
				if(this.selectedIndex <= 0 && !this.inPrimaryState()) {
					this.selectedIndex = 0; // all options
				}
						
				var last = this.selectedIndex + 1;
				var optLast = (this.inPrimaryState()) ? this.primaryOptions[last] : this.options[last].element;
				var optNext = (this.inPrimaryState()) ? this.primaryOptions[this.selectedIndex] : this.options[this.selectedIndex].element;
										
				if(this.selectedIndex < optArr.length) {
					if(!this.inPrimaryState() && this.selectedIndex > 9) {
						this.productcontainer.scrollTop-=this.options[this.selectedIndex].element.offsetHeight;
					}
					if(last < optArr.length) 	{ optLast.removeClassName(this.css.selected); }
					if(this.selectedIndex >= 0) { optNext.addClassName(this.css.selected); }	
					if(this.selectedIndex != -1) { this.replacedSelect.value = optNext.id; }
					
				}
			// Closed State	
			} else {
				if(this.selectedIndex < 0) {
					this.selectedIndex = -1;
					this.controller.innerHTML = this.labels.allProducts;
					this.controller.className="";
					
					this.replacedSelect.value = this.options[0].element.id;
				}
											
				if(this.selectedIndex < optArr.length && this.selectedIndex > -1) {
					var option = (!this.inPrimaryState()) ? this.options[this.selectedIndex].element : this.primaryOptions[this.selectedIndex];
					var next_option = (!this.inPrimaryState()) ? this.options[this.selectedIndex+1].element : this.primaryOptions[this.selectedIndex+1];
					
					var hasIcon = this.hasIcon(option.className);
					option.innerHTML = (this.selectedIndex <= 0 && !this.inPrimaryState()) ? this.labels.allProducts :  option.innerHTML;
					this.updateControllerText(option.innerHTML,hasIcon);
					this.controller.className = (this.selectedIndex <= 0 && !this.inPrimaryState()) ? "" : option.className;
					this.controllerSpan.addClassName('');
					
					next_option.removeClassName(this.css.selected)
					option.addClassName(this.css.selected);
					this.replacedSelect.value = option.id;
				}
				
			}
			
		},
		/*--- DOWN KEY NAVIGATION ---*/
		handleKeyDown : function() {
			
			var optArray = (!this.inPrimaryState()) ? this.options : this.primaryOptions;
			var selected;
					
			// if option has been selected, be sure to set before incrementing
			if(this.selectedIndex > optArray.length) {			
				var li = optArray.find(function(s,index) {
					if(s.hasClassName(this.css.selected)) selected = (index);
				});
				this.selectedIndex = selected;	
			}
						
			if(this.selectedIndex < 0) {
				this.selectedIndex = -1; // all options
			}
			
			// is closed and option was selected, be sure to remove selected class of controller
			if(!this.isOpenedState() && this.selectedIndex > 0 || this.selectedIndex < 1) {
				this.allController.removeClassName(this.css.selected);
			}
					
			if(this.isSafari2) { this.selectedIndex = this.selectedIndex+1; } else { this.selectedIndex++; }
					
			var last = (this.selectedIndex <= 0) ? 0 : this.selectedIndex - 1; // don't let index get below 0
			if(this.selectedIndex >= optArray.length) this.selectedIndex = optArray.length-1 // if at the bottom, keep index there					
							
			var optLast = (this.inPrimaryState()) ? optArray[last] : optArray[last].element;
			var optNext = (this.inPrimaryState()) ? optArray[this.selectedIndex] : optArray[this.selectedIndex].element;
			
			optLast.removeClassName(this.css.selected);
			if(this.selectedIndex >= 0) optNext.addClassName(this.css.selected);
			
			// set up controller
			if(!this.isOpenedState()) {
				var hasIcon = this.hasIcon(optNext.className);
				this.updateControllerText(optNext.innerHTML,hasIcon);
				this.controller.className = optNext.className;
			}
			
			this.replacedSelect.value = optNext.id;
			
			// scroll to option in menu if it's greater than the featured length
			if(this.selectedIndex > this.primaryOptions.length) this.productcontainer.scrollTop+=this.options[this.selectedIndex].element.offsetHeight;
					
		}
	}
	
	/*--- SELECT MENU ---*/
	var _ProductSelectorInstance =  Class.create(Object.extend(new _Select.Behaviors(), {
		initialize : function(replacedSelect,labels,cfg,css) {					
			
			if(!$(replacedSelect)) { return; } // don't run this if can't find select in DOM	
		
			this.labels = Object.extend(
			{				
				allProducts				: "All Products",
				viewAllProducts			: "View all products &#8250;",
				viewFeaturedProducts	: "View featured products &#8250;",
				truncateChars			: 24,
				truncateIconChars		: 21,
				truncateReplace			: " ..."
			},labels);
			
			this.cfg = Object.extend({
				positionOverride	:	false,
				customSubmit		:	this.getURL
			},cfg);
			
			this.css = Object.extend(
			{
				controller			: "category-controller",
				category			: "category",
				categories			: "categories",
				primaryProducts		: "categories-primary-products",
				viewAllProducts		: "view-all-products",
				icon				: "icon",
				focused				: "category-controller-focused",
				hidden				: "category-container-hidden",
				active				: "category-container-active",
				inactive			: "off",
				primary				: "primary",	
				selected			: "selected",
				unpositioned		: "category-container-unpositioned"
			},css);
										
			this.currentstate = _Select.States.closed;
			this.replacedSelect = replacedSelect;
			this.selectedIndex = -1;
			this.optionState = _Select.States.topOptions;
			this.holdValue="";
			
			this.controllerSpan = new Element('span').addClassName(this.css.controller);		
			
			this.controller = new Element('a', { "id" : "control", "href" : "javascript:void(0)"  });
			
			this.isSafari2 = false;
			this.isSafari = (adobe.hostEnv.isSafari);
			
			
			this.container = $(replacedSelect).up().up();	// element must exist in the HTML
			this.productcontainer = $(replacedSelect).up();	// element must exist in the HTML
					
			if((adobe.hostEnv.ua.indexOf('multisafari') > -1) || (adobe.hostEnv.isSafari)) {
				this.hiddenInput = new Element('input', { "id" : 'hiddenField', "type" : "text" });
				Element.insert(this.container, {before:this.hiddenInput});
				this.isSafari2 = true;
				this.hiddenInput.observe('focus', this.handleCustomEvent.bindAsEventListener(this, { type: "focus"} ));
			}
			
			this.viewAllController = new Element("span").addClassName(this.css.viewAllProducts);
			
			this.searchform = $(this.replacedSelect.form.id);	
			
			this.element = new Element('ul');
			this.id = this.element.identify();
			
			this.keyCode;				
			
			var selectOptions =  $A(this.replacedSelect.options);
					
			this.queried = getSearchParams('product') || "";			
			
			this.options = selectOptions.collect(createOption,this);
			
			if(this.options=="") return;
			
			function createOption(element) {			
				return new _Option(element,this.element,this.queried,this.css);
			}
			
			Element.insert(this.container, {before:this.controllerSpan})
			
			this.controllerSpan.insert(this.controller);
			Element.insert(this.productcontainer, {after:this.viewAllController})
				
			if(this.cfg.positionOverride) {
				this.container.addClassName(this.css.unpositioned);
			}
				
			this.toDom();			
	
			var rules   = {
				UP:     this.keycheck.bindAsEventListener(this), 
				DOWN:   this.keycheck.bindAsEventListener(this),
				REGEX: [ [null, "RETURN|TAB|ESCAPE", this.keycheck.bindAsEventListener(this)  ] ]};
			
			this.keymap = new GvaScript.KeyMap(rules);
			
			this.keymap.observe("keydown",this.searchform);
			
			this.controller.observe('click', this.handleStateEvent.bindAsEventListener(this));
			if(!this.isSafari2) { this.controller.observe('keypress', this.handleStateEvent.bindAsEventListener(this));	} // safari 2 will try to register keypress twice
			this.controller.observe('focus', this.handleCustomEvent.bindAsEventListener(this, { type: "focus"} ));
			this.viewAllController.observe('click', this.handleViewAll.bindAsEventListener(this));
			this.element.observe('click', this.handleStateEvent.bindAsEventListener(this));
			this.element.observe('select:change', this.handleCustomEvent.bindAsEventListener(this, { type: "select:change"} ))
			document.observe("click", this.handleOutsideClicks.bindAsEventListener(this));
			this.searchform.observe('keydown', this.handleFormSubmit.bindAsEventListener(this));
			
			document.observe("searchbuddy:opened", this.doSelectFix.bindAsEventListener(this));
			document.observe("searchbuddy:closed", this.undoSelectFix.bindAsEventListener(this));
			
		},
		
		handleFormSubmit : function(event) {
			if(event.target.id == "search-text") return;
			if ((adobe.hostEnv.appN.indexOf('netscape')  < 0 || adobe.hostEnv.isSafari == true) && (event.target.id == "search-support")) return;
			if(event.keyCode == "13" && event.target!='javascript:void(0)') { getURL(); return; } // js submit function that's in the page inline only if RETURN hit
		},
		
		toDom : function() {
			
			this.options.invoke("toDom").each((function(element) {
				if(element.id=="disabled") { return }
				this.element.appendChild(element);
			}).bind(this));
					
			this.options[0].element.style.display = "none";
			
			this.allController = new Element('span');
			this.allController.className = this.options[0].value;
			this.allController.innerHTML = this.options[0].label;
			
			Element.insert(this.productcontainer, {before: this.allController});
			this.allController.observe('click', this.handleAllClick.bindAsEventListener(this));	
	
			this.replacedSelect.style.display = 'none';// hide original
			
			this.replacedSelect.parentNode.insertBefore(this.element,this.replacedSelect);						
			
			// Featured Products list
			this.primaryOptions = $$("#"+this.element.id+ " li").findAll(function(li) {
				return !li.hasClassName(this.css.inactive);			   
			}.bind(this));
			
			this.selectedOption = $$("#"+this.element.id+ " li").find(function(li) {
				return li.hasClassName(this.css.selected);			   
			}.bind(this));
			
			var showAll = (this.queried!="") ? true : false;
			
			if(!this.queried.empty() && !Object.isUndefined(this.selectedOption)) {
				// make sure that queried product is truncated
				var hasIcon = this.hasIcon(this.selectedOption.className);
				this.updateControllerText(this.selectedOption.innerHTML, hasIcon);
			} else {
				this.queried="";
				this.controller.innerHTML = this.labels.allProducts;
			}
			
			//this.controller.innerHTML = (this.queried!="") ? this.selectedOption.innerHTML : this.labels.allProducts;
			this.viewAllController.innerHTML = (showAll) ? this.labels.viewFeaturedProducts : this.labels.viewAllProducts;
					
			// if search query exists, set the option value and display
			if(this.queried!="") {
				var opt = this.options.find((function(option) {
					return option.value == this.queried;
				}).bind(this));
											
				this.selectedIndex = opt.id; // set index to whatever queried id is
						
				this.replacedSelect.value = opt.value;
							
				this.controller.className = (this.selectedOption.className);
				this.controller.removeClassName(this.css.selected);
				this.optionState = _Select.States.allOptions;			
			}
							
			this.container.addClassName(this.css.hidden);
			if(!showAll) this.productcontainer.addClassName(this.css.primaryProducts);	
	
			this.element.observe('select:change', (function(event) {
				this.replacedSelect.selectedIndex = event.memo.id;
			}).bind(this)); 
						
		},
		keycheck : function(event) {
					
			var msg = event.keyModifiers + ":" + event.keyName + " / " +  event.keyCode;
			this.keyCode = msg;
			
			if(this.keyCode == ":DOWN / 40" || this.keyCode == ":RETURN / 13" || this.keyCode == ":UP / 38") {			
				if(this.isSafari2) {
					event.stop(); 		// don't alow page scrolling for keying - but pass keycode msg instead (ie's and ff3)	 	
				} else {
					event.preventDefault();				
				}
			}
			if(this.isSafari || adobe.hostEnv.ieV >= 6) {									
				if(event.target.id == "" && !adobe.hostEnv.isSafari) return;		
				memo = {
					keycode : event.keyCode,
					target : event.target,
					id:		event.memo
				}
				return _get_state_translation.call(this, 'keypress')(memo);
			}	
		},
		handleStateEvent : function(event,memo) {
			 if(event.type == 'keypress') { 
				memo = {
					keycode : event.keyCode,
					target : event.target
				}
				if (adobe.hostEnv.ua.indexOf('firefox/2.0.0') > -1) {
					event.stop();	
				}
			}
			return _get_state_translation.call(this, event.type)(memo); 			
		},
		handleOutsideClicks : function(event,memo) {
			
			memo = {	
				nodeName : event.target.nodeName,
				target : event.target.id,
				id : this.selectedIndex,
				type : 'outclick'
			}
			if(event.target.nodeName == "BODY" || !event.target.descendantOf) { 
				if(this.isOpenedState()) {
					this.currentstate = _Select.States.opened;
					return _get_state_translation.call(this, "select:change")(memo); 
				}
				return;
			}
			if(!event.target.descendantOf(this.container) && event.target.id != this.controller.id) { 
				if(this.container.hasClassName(this.css.active)){
					this.currentstate = _Select.States.opened;
					return _get_state_translation.call(this, "select:change")(memo); 
				}
			}
		},
		handleCustomEvent : function(event,customevent) {
			return this.handleStateEvent(customevent,event.memo);
		}
	}));			
	return _ProductSelectorInstance;
})();
