/**
 * Class for the Accessory Overview page.
 */
var AccessoryOverview = Class.create();
AccessoryOverview.prototype = {
	// Constants.
	MAX_LEVEL_NR: 3, // Maximum product level.
	VISIBLE_ITEMS_PER_LIST: 5, // Maximum nr. of visible items in list.
	ITEM_HEIGHT: 20, // Height of a single A element in a list; must match CSS height for A.
	RESULTS_PER_PAGE: 15, // Number of accessory results per page.
	MAX_PAGE_LINKS: 8, // Maximum number of page links in page navigation.
	STATE_PART_SEPARATOR: "/", // Character to separate state string parts.
	
	// Constructor.	
	initialize: function() {
		this.initialTitle = document.title;
		historyManager.frameTitle = document.title;
		this.accessoryDetails = new AccessoryDetails(Messages["details.title"], false);
		this.stripTrailingRE = new RegExp("\\" + this.STATE_PART_SEPARATOR + "+$", "g");
		
		this.currentGroupCode = null;
		this.currentProductCode = null;
		this.currentLevel1ProductCode = null;
		this.currentLevel2ProductCode = null;
		this.currentLevel3ProductCode = null;
		this.currentAccessoryCode = null;
		this.currentYear = 0;
		this.currentPageNr = 0;
		
		this.pageCount = 1;
		this.lastPageNr = 0;
		this.totalCount = 0;
		
		this.varPartCodeToMainPartCode = {};
	},
	
	 // On-load setup.
	onLoad: function() {
		common.log("OverView.onLoad: start");
		common.log(" - history size: " + window.history.length);
		
		// Remember initial URL.
		this.initialUrl = document.location.href;

		// Remember initial state.
		this.initialGroupCode = this.currentGroupCode;
		this.initialProductCode = this.currentProductCode;
		this.initialLevel1ProductCode = this.currentLevel1ProductCode;
		this.initialLevel2ProductCode = this.currentLevel2ProductCode;
		this.initialLevel3ProductCode = this.currentLevel3ProductCode;
		this.initialAccessoryCode = this.currentAccessoryCode;
		this.initialYear = this.currentYear;
		this.initialPageNr = this.currentPageNr;
		
		Util.blockWindow();
		
		// Load-time initializaton of the details object.
		this.accessoryDetails.onLoad();

		// Replace links in selection lists with onclick-calls to this class.
		for( var i = 0; i <= this.MAX_LEVEL_NR; ++i ) {
			this.wireUpItemList(i);
		}
		
		// Register overview events.
		$("year").onchange = this.yearSelected.bind(this);

		$("firstPageLinkTop").onclick = this.gotoPage.bind(this, 0);
		$("lastPageLinkTop").onclick = this.gotoPage.bind(this, -1);
		$("previousPageLinkTop").onclick = this.previousPage.bind(this);
		$("nextPageLinkTop").onclick = this.nextPage.bind(this);

		$("firstPageLinkBottom").onclick = this.gotoPage.bind(this, 0);
		$("lastPageLinkBottom").onclick = this.gotoPage.bind(this, -1);
		$("previousPageLinkBottom").onclick = this.previousPage.bind(this);
		$("nextPageLinkBottom").onclick = this.nextPage.bind(this);

		$("overview_email_button").onclick = this.mailToFriend.bind(this);
		$("overview_view_wishlist").onclick = this.viewWishList.bind(this);
		var aboutLink = $("overview_help_wishlist");
		if (aboutLink) {
			if (Config.ypec) {
				Element.hide(aboutLink);
				Element.hide("overview_help_wishlist_img");
			} else {
				aboutLink.onclick = this.aboutWishList.bind(this);
			}
		}

		// Initialize DHTML history; calls the listener already.
		// Prevent that by setting the flag this.inOnLoad.
		// This also prevents calls to saveState() while parseState() is running.
		this.inOnLoad = true;
		try {
			Util.initializeHistory();
			Util.setHistoryListener(this.onHistoryChange.bind(this));
		
			// Parse current state from URL.
			common.log("Before parse state '" + document.location.hash.substring(1) + "'...");
			this.parseState(document.location.hash.substring(1));
		} finally {
			Util.unblockWindow();
			this.inOnLoad = false;
		}
		
		common.log("OverView.onLoad: end");
	},

	// Called when user presses the Back or Forward button.
	onHistoryChange: function(newLocation) {
		common.log("History changed: '" + newLocation + "'");
		common.log(" - history size: " + window.history.length);

		// For IE, check if new location matches with the new current URL,
		// if not fix it.
		var currentNewLocation = document.location.href.split('#')[1] || '';
		
		// Check if back at first page.
		if (Prototype.Browser.IE) {
			common.log("Frame state: " + historyManager._getFrameStateIE());
			var li = this.initialUrl.indexOf("#");
			if (li != -1) {
				common.log("Initial state: " + this.initialUrl.substring(li + 1));
			}
			if (historyManager._getFrameStateIE() == this.initialUrl.substring(li + 1)) {
				common.log("IE On initial page");
			}
		}
		
		if (document.location.href == this.initialUrl) {
			common.log("On initial page: " + this.initialUrl);
		} else {
			var li = document.location.href.indexOf("#");
			if (li != -1) {
				common.log("On other page: " + document.location.href.substring(li + 1));
			}
		}

		// Reset title, since it may have been changed by a details popup.
		document.title = this.initialTitle;
		
		if (!this.inOnLoad) {
			this.inHistoryChange = true;
			try {
				this.parseState(newLocation);
			} finally {
				this.inHistoryChange = false;
			}
		}
	},
	
	// Check if the overview is on the first state in the history.
	isOnInitialPage: function() {
		if (Prototype.Browser.IE) {
			// Must look at frame state, since browser state is not correct at this point.
			var li = this.initialUrl.indexOf("#");
			if (li != -1) {
				common.log("On initial page: " + this.initialUrl.substring(li + 1));
				return historyManager._getFrameStateIE() == this.initialUrl.substring(li + 1);
			}
			return false;
		} else {
			return document.location.href == this.initialUrl;
		}
	},
	
	// Fix up links in item lists so that their onclick event calls the
	// itemSelected() method.
	wireUpItemList: function(listLevel) {
		var listId = this.getListId(listLevel);
		var list = $(listId);
		if (list != null) {
			var items = list.childNodes;
			for( var i = 0; i < items.length; ++i ) {
				var link = items[i];
				if (link.tagName == "A") {
					link.onclick = this.itemSelected.bind(this, listId, listLevel, this.getCodeFromId(link.id));
					link.removeAttribute("href");
				}
			}
		}
	},
	
	// Retrieve the ID of the list for the given list level.
	getListId: function(listLevel) {
		if (listLevel == 0) {
			return "groupSelect";
		} else {
			return "level" + listLevel + "Select";
		}
	},
	
	// Create a string suitable as an ID of a DOM element from a group, product or
	// accessory code.
	makeIdFromCode: function(level, code) {
		switch( level ) {
			case 0: return "G" + this.escapeCode(code); // Group.
			case 1:
			case 2:
			case 3: return "P" + this.escapeCode(code); // Product.
			case 4: return "A" + code; // Accessory.
			default:
				return "";
		}
	},
	
	// Strip off the DOM ID prefix from an item ID.
	getCodeFromId: function(prefixedId) {
		return this.unescapeCode(prefixedId.substring(1));
	},
	
	// Retrieve the results accessory DIV for the given accessory part code.
	getAccessoryDiv: function(accessoryPartCode) {
		return $("div-" + this.makeIdFromCode(4, accessoryPartCode));
	},
	
	// Find selected product ID of the right-most list.
	getMostSpecificProductCode: function() {
		return this.currentLevel3ProductCode ||
			   this.currentLevel2ProductCode ||
			   this.currentLevel1ProductCode ||
			   this.currentGroupCode;
	},
	
	// Find selected product level of the right-most list.
	getMostSpecificProductLevel: function() {
		if (this.currentLevel3ProductCode != null) {
			return 3;
			
		} else if (this.currentLevel2ProductCode != null) {
			return 2;
		
		} else if (this.currentLevel1ProductCode != null) {
			return 1;
		
		} else if (this.currentGroupCode != null) {
			return 0;
		}
		
		return -1;
	},
	
	// Create the link to the details page for when JavaScript is disabled.
	// This links to the detail page with the groupCode, productCode and accessoryCode
	// parameters, so that the JSP knows what to render.
	getAccessoryNoScriptUrl: function(accessory) {
		var url = [];
		url.push(Config["overview.jsp"]);
		url.push("?groupCode=" + this.escapeCode(this.currentGroupCode));
		
		var productCode = this.getMostSpecificProductCode();
		var level = this.getMostSpecificProductLevel();
		if (level > 0) {
			url.push("&amp;productCode=" + level + "$" + this.escapeCode(productCode));
		}
		
		url.push("&amp;year=" + this.currentYear);
		url.push("&amp;pageNr=" + this.currentPageNr);
		url.push("&amp;accessoryCode=" + accessory.partCodeEncoded);
		
		return url.join("");
	},
	
	// Escape a single code in the state string.
	escapeCode: function(code) {
		return code == null ? "" : encodeURIComponent(code).replace(/%20/g, "+");
	},
	
	// Unescape a single code from the state string.
	unescapeCode: function(code) {
		return code == null ? null : decodeURIComponent(code.replace(/\+/g, " "));
	},
	
	// Default state to use when no state specified.
	getDefaultState: function() {
		var defaultState = [
			this.initialGroupCode,
			this.initialLevel1ProductCode,
			this.initialLevel2ProductCode,
			this.initialLevel3ProductCode,
			this.initialAccessoryCode,
			this.initialYear,
			this.initialPageNr
		];
		
		common.log("Default state: " + defaultState.join(this.STATE_PART_SEPARATOR));
		return defaultState;
	},
	
	// Parse the state from a location string.
	getStateFromLocation: function(location) {
		var state = location.split(this.STATE_PART_SEPARATOR);
		
		// Convert empty codes to null.
		try {
			state[0] = Util.nullIfEmpty(this.unescapeCode(state[0]));
			state[1] = Util.nullIfEmpty(this.unescapeCode(state[1]));
			state[2] = Util.nullIfEmpty(this.unescapeCode(state[2]));
			state[3] = Util.nullIfEmpty(this.unescapeCode(state[3]));
			state[4] = Util.nullIfEmpty(this.unescapeCode(state[4]));
			
			var year = state[5];
			year = Util.nullIfEmpty(year) == null ? 0 : parseInt(year);
			if (isNaN(year) || year < 0 || year > 2100) {
				year = 0;
			}
			state[5] = year;
			
			var pageNr = state[6];
			pageNr = Util.nullIfEmpty(pageNr) == null ? 0 : (parseInt(pageNr) - 1);
			if (isNaN(pageNr) || pageNr < 0 || pageNr > 2100) {
				pageNr = 0;
			}
			state[6] = pageNr;
		} 
		
		catch( e ) {
			// Ignore errors in state string.
			common.log("Error in state string: " + e.description);
			return state;
		}			
		return state;
	},
	
	// Create the state string, for storing in history or passing to mailToFiend page.
	getStateAsString: function() {
		// State is an array (because this gives the cleanest URL) with the following elements.
		// - index 0: group ID; null if no item selected;
		// - index 1: product level 1 ID; null if no item selected;
		// - index 2: product level 2 ID; null if no item selected;
		// - index 3: product level 3 ID; null if no item selected;
		// - index 4: accessory ID; null if no accessory selected.
		// - index 5: year value; 0 if no year selected;
		// - index 6: page number; 0 if on first page;
		var state = [];
		
		state[0] = this.escapeCode(this.currentGroupCode);
		state[1] = this.escapeCode(this.currentLevel1ProductCode);
		state[2] = this.escapeCode(this.currentLevel2ProductCode);
		state[3] = this.escapeCode(this.currentLevel3ProductCode);
		state[4] = this.escapeCode(this.currentAccessoryCode);
		state[5] = this.currentYear == 0 ? "" : this.currentYear;
		state[6] = this.currentPageNr == 0 ? "" : (this.currentPageNr + 1);
		
		return state.join(this.STATE_PART_SEPARATOR).replace(this.stripTrailingRE, '');
	},
	
	// Call this to save current selection state in URL after '#'.
	saveState: function() {
		common.log("Saving state: inOnLoad=" + this.inOnLoad + ", inParseState=" + this.inParseState);
		if (this.inParseState) {
			// Don't save state if we're busy parsing a new state.
			common.log(" - state not saved since in parseState()");
			return;
		}

		var state = this.getStateAsString();
		var currentState = document.location.hash.substring(1);
		common.log("new state: " + state + "\ncurrent state: " + currentState + "\nsame: " + (state == currentState));
		if (state == currentState) {
			common.log(" - state not saved since not changed");
			return;
		}
		
		common.log(" - saving state: " + state);
		Util.addHistoryState(state);
		common.log(" - saved state: " + state);
	},
	
	// Parse a state as present in the hash part of the URL.
	parseState: function(location) {
		common.log("parseState: begin: " + location);
		this.inParseState = true;
		common.beginBatch();
		
		// Retrieve current wish list if we're in onLoad.
		if (this.inOnLoad) {
			common.retrieveWishList();
			common.addWishListListener(this);
		}
		
		var state;
		var listsPrefilled;
		if (location.length == 0) {
			// Lists have been filled by JSP already, don't retrieve them
			// (unless this call is the result of a back-button action).
			state = this.getDefaultState();
			listsPrefilled = true && !this.inHistoryChange;
		} else {
			// Use state from hash location.
		 	state = this.getStateFromLocation(location);
			listsPrefilled = false;
		}
		
		// Check changes and store ID's.
		var levelChanged = [];
		levelChanged[0] = listsPrefilled;
		levelChanged[1] = listsPrefilled;
		levelChanged[2] = listsPrefilled;
		levelChanged[3] = listsPrefilled;
		var modelYearsChanged = false;
		var yearChanged = listsPrefilled;
		var pageNrChanged = listsPrefilled;
		
		if (this.currentGroupCode != state[0]) {
			levelChanged[0] = true;
			levelChanged[1] = true;
			levelChanged[2] = true;
			levelChanged[3] = true;
			modelYearsChanged = true;
			this.currentGroupCode = state[0];
		}
		
		if (this.currentLevel1ProductCode != state[1]) {
			levelChanged[1] = true;
			levelChanged[2] = true;
			levelChanged[3] = true;
			modelYearsChanged = true;
			this.currentLevel1ProductCode = state[1];
		}
		
		if (this.currentLevel2ProductCode != state[2]) {
			levelChanged[2] = true;
			levelChanged[3] = true;
			modelYearsChanged = true;
			this.currentLevel2ProductCode = state[2];
		}
		
		if (this.currentLevel3ProductCode != state[3]) {
			levelChanged[3] = true;
			modelYearsChanged = true;
			this.currentLevel3ProductCode = state[3];
		}

		this.currentAccessoryCode = state[4];
		common.log(" - parse accessory: " + this.currentAccessoryCode);
		
		if (this.currentYear != state[5]) {
			yearChanged = true;
			this.currentYear = parseInt(state[5]);
		}
		
		if (this.currentPageNr != state[6]) {
			pageNrChanged = true;
			this.currentPageNr = parseInt(state[6]);
		}
		
		if (listsPrefilled) {
			// No need to retrieve data, just make the selected items visible.
			// Do this with a delay, since doing it directly does not work in IE.
			window.setTimeout(
				function() {
					for( var i = 0; i <= this.MAX_LEVEL_NR; ++i ) {
						var listId = this.getListId(i);
						var selectedIndex = this.findSelectedIndex(listId);
						if (selectedIndex != -1) {
							this.makeSelectionVisible(listId, selectedIndex);
						}
					}
				}.bind(this), 10);
			 	
		} else {
			// Select the proper group item now, since items always present.
			if (this.currentGroupCode != null) {
				var groupListId = this.getListId(0);
				this.setSelectedValue(groupListId, this.currentGroupCode);
			}
			
			// Retrieve data for all product lists that have changed.
			for( var i = 1; i <= this.MAX_LEVEL_NR; ++i ) {
				var listId = this.getListId(i);
				if (state[i - 1] != null) {
					if (levelChanged[i - 1]) {
						common.log("- retrieving list contents at level " + i);
						// Previous list has selection, retrieve items for this list.
						this.clearList(listId, i);
						AccessoryService.getValidProductsForLevel(state[i - 1], i,
												Config.countryCode, Config.languageCode,
												this.handleParseStateItems.bind(this, i, state[i]));
					} else {
						// Parent selection was not changed, no need to retrieve items.
						this.setSelectedValue(listId, state[i]);
					}
				} else {
					// Parent selection was cleared, clear list to the right.
					this.clearList(listId, i);
				}
			}
		}
		
		// Update the year dropdown.
		if (modelYearsChanged) {
			// Retrieve model years for the selected segment.
			common.log("Model years changed, retrieving...");
			AccessoryService.getProductModelYears(
				this.getMostSpecificProductCode(), this.getMostSpecificProductLevel(), Config.countryCode, Config.languageCode,
				this.handleNewModelYears.bind(this, this.currentYear));

		} else if (yearChanged) {
			// Just change the selection.
			$("year").value = this.currentYear;
			Effects.highlight("year");
		}
		
		// ID's are reset by filling lists above, so set them to the current state.
		this.currentGroupCode = state[0];
		this.currentLevel1ProductCode = state[1];
		this.currentLevel2ProductCode = state[2];
		this.currentLevel3ProductCode = state[3];
		
		common.log("State: group=" + this.currentGroupCode + 
			" product1=" + this.currentLevel1ProductCode +
			" product2=" + this.currentLevel2ProductCode +
			" product3=" + this.currentLevel3ProductCode +
			" accessory=" + this.currentAccessoryCode +
			" year=" + this.currentYear +
			" pageNr=" + this.currentPageNr);
			
		// Update the search results if any relevant selection changed.
		if (levelChanged[0] || levelChanged[1] || levelChanged[2] || levelChanged[3] ||
			yearChanged || pageNrChanged || listsPrefilled) {
			this.searchAccessories(true, true);
		}
		
		if (this.inHistoryChange && this.isOnInitialPage()) {
			// Note that for IE we always must go back two steps because the initial page
			// is recorded twice in the history.
			if (Prototype.Browser.IE || this.currentAccessoryCode != null) {
				// Back-button was pressed and we arrived on the first page with an accessory popup.
				// Do an extra back to make the popup appear to be outside the history.
				common.cancelBatch();
				common.log("parseState: back needed");
				this.inParseState = false;
	
				if (Prototype.Browser.IE) {
					window.history.go(-1);
				} else {			
					window.history.back();
				}
				
				// Close details anyway, since if we're already on the first page,
				// the back above does nothing.
				window.setTimeout(function() {
					this.closeAccessory(!this.inOnLoad);
					//try { common.endBatch(); } catch( e ) { }
					common.log("parseState: end after failed back");
					this.inParseState = false;
				}.bind(this), 100);
				
				return;
			}
		}
		
		if (this.currentAccessoryCode != null) {
			this.showAccessory(!this.inOnLoad);
		} else {
			this.closeAccessory(!this.inOnLoad);
		}

		common.endBatch();
		common.log("parseState: end");
		this.inParseState = false;
	},
	
	// Callback to handle products received from DWR call above.
	handleParseStateItems: function(listLevel, selectedValue, products) {
		// Check if all following lists are empty, since parseState(
		// only clears the list right next to the changed list. This could leave
		// contents in the third or fourth lists if the first or second list is
		// changed.
		if (products.length == 0) {
			for( var i = listLevel + 1; i <= this.MAX_LEVEL_NR; ++i ) {
				var nextList = $(this.getListId(i));
				common.log(" - clearing list at level " + i + ", nodes: " + nextList.childNodes.length);
				if (nextList.childNodes.length > 0) {
					this.clearList(nextList, i);
				}
			}
			return;
		}
		
		// Don't fill this list if previous list is empty.
		var previousList = $(this.getListId(listLevel - 1));
		if (previousList.childNodes.length == 0) {
			common.log(" - skipping fill of list at level " + listLevel);
			return;
		}

		// Highlight and fill the updated list.
		common.log(" - filling list at level " + listLevel);	
		var listId = this.getListId(listLevel);
		if (products.length > 0) {
			Effects.highlight(listId);
		}
		this.fillList(listId, listLevel, products, selectedValue);
	},
	
	// Callback to handle new model years received.
	handleNewModelYears: function(selectedYear, years) {
		common.log("Retrieved " + years.length + " new model years.");
		var yearDropdown = $("year");
		yearDropdown.options.length = 1;
		
		var foundSelectedYear = false;
		for( var i = 0; i < years.length; ++i ) {
			var year = years[i];
			var option = common.createOption(year, year);
			if (year == selectedYear) {
				option.selected = true;
				foundSelectedYear = true;
			}
			yearDropdown.options[yearDropdown.length] = option;
		}
		
		// If the selected year is not found, it is a left-over from a previous
		// year-selection, which turned out not to be valid for the newly selected
		// segment.
		if (selectedYear != 0 && !foundSelectedYear) {
			this.currentYear = 0;
			this.saveState();
			this.searchAccessories(true);
		}
	},
	
	// Remove all items from a selection list and clear the selection.
	clearList: function(listId, listLevel) {
		var list = $(listId);
		if (list != null) {
			while( list.childNodes.length > 0 ) {
				var item = list.firstChild;
				list.onclick = null;
				list.removeChild(item);
			}
		}
		
		switch( listLevel ) {
			case 0:
				this.currentGroupCode = null;
				this.currentLevel1ProductCode = null;
				this.currentLevel2ProductCode = null;
				this.currentLevel3ProductCode = null;
				break;
			case 1:
				this.currentLevel1ProductCode = null;
				this.currentLevel2ProductCode = null;
				this.currentLevel3ProductCode = null;
				break;
			case 2:
				this.currentLevel2ProductCode = null;
				this.currentLevel3ProductCode = null;
				break;
			case 3:
				this.currentLevel3ProductCode = null;
				break;
		}
	},
	
	// Fill a list with items from a list of products.
	fillList: function(listId, listLevel, productList, selectedValue) {
		var list = $(listId);
		if (list != null) {
			this.clearList(listId, listLevel);

			var selectedIndex = -1;		
			for( var i = 0; i < productList.length; ++i ) {
				var product = productList[i];
				
				with( DomBuilder ) {
					var item = a({
							id: this.makeIdFromCode(listLevel, product.code),
							onclick: this.itemSelected.bind(this, listId, listLevel, product.code),
							className: (selectedValue && selectedValue == product.code) ? "on" : ""
						},
						text(product.displayName)
					);
					list.appendChild(item);
				}
				
				if (selectedValue && selectedValue == product.code) {
					selectedIndex = i;
				}
			}
			
			if (selectedIndex != -1) {
				this.makeSelectionVisible(listId, selectedIndex);
				
				// Update ID's.
				switch( listLevel ) {
					case 0: this.currentGroupCode = selectedValue; break;
					case 1: this.currentLevel1ProductCode = selectedValue; break;
					case 2: this.currentLevel2ProductCode = selectedValue; break;
					case 3: this.currentLevel3ProductCode = selectedValue; break;
				}
			}
		}
	},
	
	// Make the item with the given index visible in the given list by scrolling down.
	makeSelectionVisible: function(listId, selectedIndex) {
		// Do this with a delay, since doing it directly does not work always in IE.
		window.setTimeout(
			function() {
				var list = $(listId);
				if (selectedIndex >= this.VISIBLE_ITEMS_PER_LIST) {
					list.scrollTop = (selectedIndex - 1) * this.ITEM_HEIGHT;
				} else {
					list.scrollTop = 0;
				}
		 	}.bind(this), 10);
	},
	
	// Find the ID of the A element in the given DIV that has class "on".
	findSelectedIndex: function(listId) {
		var list = $(listId);
		if (list != null) {
			var selectedIndex = 0;
			var items = list.childNodes;
			for( var i = 0; i < items.length; ++i ) {
				var link = items[i];
				if (link.tagName == "A") {
					if (Element.hasClassName(link, "on")) {
						return selectedIndex;
					}
					++selectedIndex;
				}
			}
		}
		return -1;
	},
	
	// Find the ID of the A element in the given DIV that has class "on".
	findSelectedValue: function(listId) {
		var list = $(listId);
		if (list != null) {
			var items = list.childNodes;
			for( var i = 0; i < items.length; ++i ) {
				var link = items[i];
				if (link.tagName == "A") {
					if (Element.hasClassName(link, "on")) {
						return this.getCodeFromId(link.id);
					}
				}
			}
		}
		return null;
	},
	
	// Set the value of the selection DIV to the given value, by
	// looking for the A element in the DIV with the given value for it's ID,
	// and given that A element class "on", while removing this class from
	// all other A elements. This method does NOT retrieve the items for the
	// next list.
	setSelectedValue: function(listId, idValue) {
		var list = $(listId);
		if (list != null) {
			var selectedIndex = -1;
			var items = list.childNodes;
			for( var i = 0, linkIndex = 0; i < items.length; ++i ) {
				var link = items[i];
				if (link.tagName == "A") {
					// Strip off ID prefix from link ID before comparing.
					if (this.getCodeFromId(link.id) == idValue) {
						Element.addClassName(link, "on");
						selectedIndex = linkIndex;
					} else {
						Element.removeClassName(link, "on");
					}
					++linkIndex;
				}
			}
			
			// Make sure the selected item is visible: if it is not in the first
			// 5 rows, scroll to the selected item so that it appears in the second
			// visible row.
			this.makeSelectionVisible(list, selectedIndex);
		}
	},
	
	// Called when selection is made in one of the selection lists.
	itemSelected: function(listId, listLevel, itemCode) {
		// Remember old value so we know if a change of selection happened.
		var currentValue = this.findSelectedValue(listId);
		var newValue = null;
		common.log("itemSelected: " + listId + ", " + listLevel + ": " + listLevel + ", itemCode: " + itemCode);
		
		// Highlight new item, unhighlight old item.
		var list = $(listId);
		var items = list.childNodes;
		for( var i = 0; i < items.length; ++i ) {
			var link = items[i];
			if (link.tagName == "A") {
				if (this.getCodeFromId(link.id) == itemCode) {
					Element.addClassName(link, "on");
					newValue = itemCode;
				} else {
					Element.removeClassName(link, "on");
				}
			}
		}
		
		// If the same selected item was clicked again, ignore it.		
		if (currentValue == newValue) {
			// Do nothing.
			return false;
		}
		
		// Update ID's.
		common.log("itemSelected: new=" + newValue + ", current=" + currentValue);
		common.log("Setting code at level " + listLevel + " to '" + newValue + "'");
		switch( listLevel ) {
			case 0: this.currentGroupCode = newValue; break;
			case 1: this.currentLevel1ProductCode = newValue; break;
			case 2: this.currentLevel2ProductCode = newValue; break;
			case 3: this.currentLevel3ProductCode = newValue; break;
		}
		
		// Clear lists and selections to the right.
		for( var level = listLevel + 1; level <= this.MAX_LEVEL_NR; ++level ) {
			this.clearList(this.getListId(level), level);
		}
		
		// Reset page number.
		this.currentPageNr = 0;
		
		// Retrieve data for the list next to this one, if not in the last list.
		common.beginBatch();
		
		if (listLevel < this.MAX_LEVEL_NR) {
			AccessoryService.getValidProductsForLevel(newValue, listLevel + 1,
									Config.countryCode, Config.languageCode,
									this.handleItemSelectedItems.bind(this, listLevel + 1));
		}
		
		// Retrieve model years for the newly selected segment.
		common.log("Model years changed, retrieving...");
		AccessoryService.getProductModelYears(
			this.getMostSpecificProductCode(), this.getMostSpecificProductLevel(), Config.countryCode, Config.languageCode,
			this.handleNewModelYears.bind(this, this.currentYear));
			
		// Save current selection and update the search results.
		this.saveState();
		this.searchAccessories(true);
		
		common.endBatch();
		
		//Send selected groups to webtrends
		var urlhash = window.location.hash;
		var hashParts = urlhash.substr(1).split("/");
		var groupId = hashParts[0];
		for (var i=1;i < hashParts.length;i++){
			if(hashParts[i].length > 0){
				groupId += "%20" + hashParts[i];
			}
		}
		dcsMultiTrack('DCS.dcsuri',window.location.pathname + '?grpId=' + groupId,'WT.ti',document.title);
		return false;
	},
	
	// DWR callback for AccessoryService.getValidProductsForLevel above.
	handleItemSelectedItems: function(listLevel, products) {
		var list = this.getListId(listLevel);
		if (products.length > 0) {
			Effects.highlight(list);
		}
		this.fillList(list, listLevel, products);
	},
	
	// Called when a different year is selected.
	yearSelected: function() {
		// Save current selection and update the search results.
		this.currentYear = parseInt($("year").value);
		this.saveState();
		this.searchAccessories(true);
	},
	
	// Called when an accessory is selected from the results.
	selectAccessory: function(accessoryCode) {
		// Save current selection and update the search results.
		this.currentAccessoryCode = accessoryCode;
		this.saveState();
		this.showAccessory(true);
		return false;
	},
	
	// Update the page navigation links, and set search result labels.
	// If count is null, displays "searching...", and clears the page
	// list and previous/next links.
	updatePageNavigation: function(count, hasLocalAccessories, newSearch) {
		common.log("updatePageNavigation: count=" + count + ", newSearch=" + newSearch);
	
		// Update page navigation links.
		if (count != null) {
			if (this.currentPageNr > 0) {
				Element.show("previousPageTop");
				Element.show("previousPageBottom");
			} else {
				Element.hide("previousPageTop");
				Element.hide("previousPageBottom");
			}
			
			if (this.currentPageNr < this.lastPageNr && this.pageCount > 1) {
				Element.show("nextPageTop");
				Element.show("nextPageBottom");
			} else {
				Element.hide("nextPageTop");
				Element.hide("nextPageBottom");
			}
			
			// Create page links.
			this.updatePageLinks("pageListTop");
			this.updatePageLinks("pageListBottom");
		}
	
		// Update results count.
		if (count == null) {
			$("resultCount").innerHTML = "";
			Element.hide("resultOneLabel");
			Element.hide("resultMoreLabel");
			Element.show("resultSearching");
		} else if (count == 1) {
			$("resultCount").innerHTML = count;
			Element.hide("resultMoreLabel");
			Element.hide("resultSearching");
			Element.show("resultOneLabel");
		} else {
			$("resultCount").innerHTML = count;
			Element.hide("resultOneLabel");
			Element.hide("resultSearching");
			Element.show("resultMoreLabel");
		}
		
		if (count == null) {
			Element.hide("localAccessoryLabel");
		} else if (hasLocalAccessories) {
			Element.show("localAccessoryLabel");
		} else {
			Element.hide("localAccessoryLabel");
		}
	},
	
	// Recreate the page link list.	
	updatePageLinks: function(listId) {
		var list = $(listId);

		var addFirstPage = false;
		var addLastPage = false;
		
		// Only show the MAX_PAGE_LINKS links around the current page.
		var half = this.MAX_PAGE_LINKS / 2;
		var firstPage = this.currentPageNr - half;
		var lastPage = this.currentPageNr + half;
		if (firstPage < 0) {
			firstPage = 0;
			lastPage = Math.min(this.lastPageNr, this.MAX_PAGE_LINKS - 1);
		} else if (firstPage > 0) {
			addFirstPage = true;
		}
		
		if (lastPage > this.lastPageNr) {
			firstPage = Math.max(0, this.lastPageNr - this.MAX_PAGE_LINKS + 1);
			lastPage = this.lastPageNr;
		} else if (lastPage < this.lastPageNr) {
			addLastPage = true;
		}
		
		if (addFirstPage) {
			Element.show("firstPageTop");
			Element.show("firstPageBottom");
		} else {
			Element.hide("firstPageTop");
			Element.hide("firstPageBottom");
		}
		
		if (addLastPage) {
			Element.show("lastPageTop");
			Element.show("lastPageBottom");
		} else {
			Element.hide("lastPageTop");
			Element.hide("lastPageBottom");
		}
		
		var fragment = document.createDocumentFragment();
		for( var pageNr = firstPage; pageNr <= lastPage; ++pageNr ) {
			var link = document.createElement("A");
			link.id = listId + "-link-" + pageNr;
			link.onclick = this.gotoPage.bind(this, pageNr);
			link.className = pageNr == this.currentPageNr ? "selectedPage" : "otherPage";
			link.href = "#";
			link.appendChild(document.createTextNode(String(pageNr + 1)));
			fragment.appendChild(link);
			fragment.appendChild(document.createTextNode(" "));
		}
		
		while( list.childNodes.length > 0 ) {
			var link = list.firstChild;
			if (link.tagName == "A") {
				link.onclick = null;
			}
			list.removeChild(link);
		}
		list.appendChild(fragment);
	},

	// Update the search results using the current selections.
	searchAccessories: function(newSearch, keepCurrentPageNr) {
		var productCode = this.getMostSpecificProductCode();
		common.log("Search accessories: newSearch=" + newSearch + ", keepPage=" + keepCurrentPageNr + " productCode=" + productCode);
		if (productCode == null) {
			this.updatePageNavigation(0, false, true);
			this.clearAccessories();
			return false;
		}
		
		if (newSearch) {
			this.updatePageNavigation(null, false, true);
			if (!keepCurrentPageNr) {
				this.currentPageNr = 0;
			}
		}
		
		common.log("Searching:");
		common.log(" - productCode: " + productCode);
		common.log(" - level: " + this.getMostSpecificProductLevel());
		common.log(" - year: " + this.currentYear);
		common.log(" - currentPageNr: " + this.currentPageNr);
		common.log(" - firstIndex: " + this.currentPageNr * this.RESULTS_PER_PAGE);
		common.log(" - maxCount: " + this.RESULTS_PER_PAGE);
		AccessoryService.searchAccessories(
			productCode,
			this.getMostSpecificProductLevel(),
			this.currentYear == 0 ? -1 : this.currentYear,
			this.currentPageNr * this.RESULTS_PER_PAGE, this.RESULTS_PER_PAGE,
			Config.countryCode, Config.languageCode,
			{ callback: this.handleSearchAccessories.bind(this, newSearch, keepCurrentPageNr),
			  errorHandler: this.handleSearchAccessoriesError.bind(this),
			  exceptionHandler: this.handleSearchAccessoriesError.bind(this) });
	},
	
	// Error handler for when the searchAccessories call fails.
	handleSearchAccessoriesError: function(error, exception) {
		common.log("handleSearchAccessoriesError: " + error);
		this.updatePageNavigation(0);
		this.clearAccessories();
		common.handleRemoteError(error, exception);
	},
	
	// Callback to handle results from searchAccessories call above.
	handleSearchAccessories: function(newSearch, keepCurrentPageNr, result) {
		common.log("handleSearch: newSearch = " + newSearch);
		var hasLocalAccessories = false;
		
		// Update page stats.
		if (newSearch) {
			if (!keepCurrentPageNr) {
				this.currentPageNr = 0;
			}
			this.pageCount = Math.ceil(result.totalCount / this.RESULTS_PER_PAGE);
			this.lastPageNr = this.pageCount - 1;
			this.totalCount = result.totalCount;
			
			common.log("New search results: " + result.totalCount);
			common.log(" - currentPageNr: " + this.currentPageNr);
			common.log(" - pageCount: " + this.pageCount);
			common.log(" - lastPageNr: " + this.lastPageNr);
		}
		
		// Create map from variation part code to accessory ID, to be able
		// to find the accessory DIV when a wish list change is received.
		this.varPartCodeToMainPartCode = {};

		// Create fragment with all new accessories.
		var b = DomBuilder;
		var fragment = document.createDocumentFragment();
        var accessoryRowDiv = document.createDocumentFragment();
		for( var i = 0; i < result.accessories.length; ++i ) {
			var accessory = result.accessories[i];
			if (accessory.local) {
				hasLocalAccessories = true;
			}

			// Update variation to main part code.
			for( var v = 0; v < accessory.variationPartCodes.length; ++v ) {
				this.varPartCodeToMainPartCode[accessory.variationPartCodes[v]] = accessory.partCode;
			}
            if ((i % 5)==0){
                accessoryRowDiv = b.div({className: "accessoryrow"});
                fragment.appendChild(accessoryRowDiv);
            }
		
			var accessoryDiv = 
				b.div({id: "div-" + this.makeIdFromCode(4, accessory.partCode), className: "accessory", accessory: accessory},
					b.a({className: "img",
							href: this.getAccessoryNoScriptUrl(accessory), 
							onclick: this.selectAccessory.bind(this, accessory.partCode)
						},
						b.img({src: common.getAccessoryThumbnail(accessory.imageName), alt: ""})
					),
					b.br(),
					accessory.variationCount != 0
						? (this.isInWishList(accessory)
							? (common.enableRemoveFromWishList
								? b.a({className: "addWishList", href: "#", onclick: this.removeFromWishList.bind(this, accessory.partCode),
										title: Messages["common.removeWishList"]},
									b.img({className: "button", src: Config["inWishList.gif"], alt: ""}),
									b.span({className: "wishlist"}, b.text(Messages["common.removeWishListShort"]))
								)
								: b.a({className: "addWishList", onclick: this.ignoreClickHandler},
									b.img({className: "button", src: Config["inWishList.gif"], alt: ""}),
									b.span({className: "wishlist"}, b.text(Messages["common.inWishList"]))
								)
							)
							: b.a({className: "addWishList", href: "#", onclick: this.addToWishList.bind(this, accessory.partCode)},
								b.img({className: "button", src: Config["addWishList.gif"], alt: ""}),
								b.span({className: "wishlist"}, b.text(Messages["common.addWishList"]))
							)
						)
						: b.a({className: "addWishList"},
							b.img({className: "button", src: Config["spacer.gif"], alt: "", width: 1, height: 13})
						),
					b.br(),
					b.a({className: "label",
							href: this.getAccessoryNoScriptUrl(accessory), 
							onclick: this.selectAccessory.bind(this, accessory.partCode)
						},
						b.span({className: "label"}, b.text(accessory.displayName))
					),
					!accessory.local ? null : b.fragment(
						b.text(" "),
						b.img({src: Config["localStar.gif"], className: "local", alt: ""})
					),
					accessory.minimumPrice == 0 ? null : (
						b.fragment(
							b.br(),
							(accessory.specialOffer ? (
								b.fragment(
									b.span({className: "special"},
										b.text(Messages["overview.specialOffer"], " ")
									),
									b.span(b.text(Messages["common.currencySymbol"], accessory.minimumPrice))
								)
							) : (
								b.text(
									Messages["overview.priceFrom"], 
									" ", 
									Messages["common.currencySymbol"], 
									" ", 
									formatFloat(accessory.minimumPrice,2)
								)
							)
						))
					)
				);
			
			accessoryRowDiv.appendChild(accessoryDiv);
		}

		// Replace current accessories with new ones.		
		this.clearAccessories();
		var results = $("acc_search_accessories");
		results.appendChild(fragment);
		
		// Update number of results and label.
		this.updatePageNavigation(result.totalCount, hasLocalAccessories, newSearch);
		
		if (newSearch) {
			Effects.highlight("acc_result_labels_top");
			Effects.highlight("acc_result_labels_bottom");
		}
	},
	
	// Clear the search results area.
	clearAccessories: function() {
		var results = $("acc_search_accessories");
		while( results.childNodes.length > 0 ) {
			var div = results.firstChild;
			
			if (div.tagName == "DIV") {
				div.accessory = null;
				// Clear references from DOM to JS to avoid IE memleaks.
				var links = div.getElementsByTagName("A");
				links[0].onclick = null; // Accessory link.
				links[1].onclick = null; // Add to wish list link.
				links[2].onclick = null; // Label link.
			}
			
			results.removeChild(div);
		}
	},
	
	// The previous page link was clicked.
	previousPage: function() {
		if (this.currentPageNr > 0) {
			--this.currentPageNr;
			this.switchPage(this.currentPageNr + 1, this.currentPageNr);
			
			// Save state and update results.
			this.saveState();
			this.searchAccessories(true, true);
		}
		return false;
	},
	
	// The next page link was clicked.
	nextPage: function() {
		if (this.currentPageNr < this.lastPageNr) {
			++this.currentPageNr;
			this.switchPage(this.currentPageNr - 1, this.currentPageNr);
			
			// Save state and update results.
			this.saveState();
			this.searchAccessories(true, true);
		}
		return false;
	},
	
	// A page number link was clicked.
	gotoPage: function(pageNr) {
		if (this.currentPageNr != pageNr) {
			var previousPageNr = this.currentPageNr;
			this.currentPageNr = pageNr == -1 ? this.lastPageNr : pageNr;
			this.switchPage(previousPageNr, this.currentPageNr);
			
			// Save state and update results.
			this.saveState();
			this.searchAccessories(true, true);
		}
		return false;
	},
	
	// Switch the page selection from an old to a new page number.
	switchPage: function(oldPageNr, newPageNr) {
		common.log("Switching from " + oldPageNr + " to " + newPageNr);
		var lists = ["pageListTop", "pageListBottom"];
		for( var i = 0; i < lists.length; ++i ) {
			var which = lists[i];
			var oldPage = which + "-link-" + oldPageNr;
			var newPage = which + "-link-" + newPageNr;

			Element.removeClassName(oldPage, "selectedPage");
			Element.addClassName(oldPage, "otherPage");
			Element.removeClassName(newPage, "otherPage");
			Element.addClassName(newPage, "selectedPage");
		}
	},
	
	// Show current accessory details in a popup DIV.
	showAccessory: function(backOnCloseDetails) {
		Util.setBlockerOpacity(0.4);
		Util.blockWindow();
		
		var accessoryDiv = this.getAccessoryDiv(this.currentAccessoryCode);
		if (accessoryDiv == null || accessoryDiv.accessory == null) {
			// Results not loaded yet, probably being called from onLoad.
			// Just pass the accessory ID to let the details object retrieve the
			// accessory details.
			common.log("Showing details by part code: " + this.currentAccessoryCode);
			this.accessoryDetails.showByPartCode(this.currentAccessoryCode, "acc_details", this.closeAccessory.bind(this, null, backOnCloseDetails));
		} else {
			common.log("Showing details by DIV: " + accessoryDiv.accessory.partCode);
			this.accessoryDetails.show(accessoryDiv.accessory, "acc_details", this.closeAccessory.bind(this, null, backOnCloseDetails));
		}
		//Send selected groups to webtrends
		var urlhash = window.location.hash;
		var hashParts = urlhash.substr(1).split("/");
		var groupId = hashParts[0];
		for (var i=1;i < 4;i++){
			if(hashParts[i].length > 0){
				groupId += "%20" + hashParts[i];
			}
		}
		var productId = hashParts[4];
		dcsMultiTrack('DCS.dcsuri',window.location.pathname + '?grpId=' + groupId + '&productId=' + productId ,'WT.ti',document.title);
	},
	
	// Close the popup DIV again, invoked by the AccessoryDetails object.
	closeAccessory: function(unblock, backOnCloseDetails) {
		common.log("closeAccessory: back=" + backOnCloseDetails);
		if (backOnCloseDetails) {		
			window.history.back();
			return true;
		}
		
		this.accessoryDetails.hide();
		this.currentAccessoryCode = null;
		this.saveState();
		
		if (unblock == null || unblock) {
			Util.unblockWindow();
		}
		
		Util.setBlockerOpacity(0);
		return false;
	},
	
	// Invoked by the "Mail to friend" button.
	mailToFriend: function() {
		var link = $("overview_email_button").href;
		link += "?linkurl=" + encodeURIComponent(document.location.href) + "&linktext=" + encodeURIComponent(document.title);
		Util.showPopup(link, "mailToFriend", {center: true, width: 426, height: 550, closeOnEscape: false});
		return false;
	},
	
	// Invoked by the "View wish list" button to show the current wish list.
	viewWishList: function() {
		var link = $("overview_view_wishlist").href;
		Util.showPopup(link, "viewWishList", {center: true, width: 431, height: 585, scrollbars: true, closeOnEscape: true});
		return false;
	},

	// Invoked by the "What's wish list" button to show an explanation of the wish list.
	aboutWishList: function() {
		var aboutLink = $("overview_help_wishlist");
		if (aboutLink) {
			var link = aboutLink.href;
			Util.showPopup(link, "helpWishList", {center: true, width: 431, height: 400, scrollbars: true, closeOnEscape: true});
		}
		return false;
	},
	
	// Check if the accessory with given part code is in the wish list.
	isInWishList: function(accessory) {
		// Check variations of accessory; if any of them in wish list, return true.
		var partCodes = accessory.variationPartCodes;
		for( var i = 0; i < partCodes.length; ++i ) {
			if (common.isInClientWishList(partCodes[i])) {
				return true;
			}
		}
		return false;
	},
	
	// Invoked by the "Add to wish list" button to add the selected accessory
	// to the wish list.
	addToWishList: function(partCode) {
		if (!common.cookieCheck()) {
			return false;
		}
	
		common.log("Overview: adding to wish list, selected '" + partCode + "'...");
		var div = this.getAccessoryDiv(partCode);
		if (div != null && div.accessory != null) {
			// Accessories were loaded by JavaScript.
			var accessory = div.accessory;
			if (accessory.variationCount == 1) {
				// No variations, we can added the part code of the variation directly.
				var varPartCode = accessory.variationPartCodes[0];
				common.log("Overview: one variation: '" + varPartCode + "'...");

				AccessoryService.addToWishList(varPartCode, Config.countryCode, Config.languageCode,
											   this.handleAddToWishList.bind(this, accessory, varPartCode));
				
			} else if (accessory.variationCount > 1) {
				// Open the details view to let the user choose which variation to add to the wish list.
				this.selectAccessory(partCode);
			}
			
		} else {
			// Accessories were loaded by JSP. Retrieve details first.
			AccessoryService.getAccessoryDetails(partCode, 
				Config.countryCode, Config.languageCode, this.handleGetDetailsForAdd.bind(this));
		}
		return false;
	},
	
	// Callback for DWR request AccessoryService.addToWishList() above.
	handleAddToWishList: function(accessory, varPartCode, added) {
		if (added) {
			// Add partcode to local wish list.
			common.addToClientWishList(varPartCode);

			// Replace "Add to wish list" link.
			this.showAddWishList(varPartCode, false);
		}
	},
	
	// Callback for DWR request AccessoryService.getAccessoryDetails() above.
	handleGetDetailsForAdd: function(accessory) {
		alert("Not implemented yet (page generated by JSP, JS enabled).");
	},
	
	// Invoked by the "Remove from wish list" button.
	removeFromWishList: function(partCode) {
		if (!common.cookieCheck()) {
			return false;
		}
		
		var div = this.getAccessoryDiv(partCode);
		if (div != null && div.accessory != null) {
			// Accessories were loaded by JavaScript.
			var accessory = div.accessory;
			AccessoryService.removeFromWishList(accessory.variationPartCodes, Config.countryCode, Config.languageCode,
												this.handleRemoveFromWishList.bind(this, accessory));
		} else {
			// Accessories were loaded by JSP. Retrieve details first.
			AccessoryService.getAccessoryDetails(partCode, 
				Config.countryCode, Config.languageCode, this.handleGetDetailsForRemove.bind(this));
		}
		return false;
	},
	
	// Callback for DWR request AccessoryService.getAccessoryDetails() above.
	handleGetDetailsForRemove: function(accessory) {
		alert("Not implemented yet (page generated by JSP, JS enabled).");
	},
	
	// Callback for DWR request AccessoryService.removeFromWishList() above.
	handleRemoveFromWishList: function(accessory, removed) {
		if (removed) {
			// Remove all accessories from local wish list.
			for( var i = 0; i < accessory.variationPartCodes.length; ++i ) {
				var partCode = accessory.variationPartCodes[i];
				common.removeFromClientWishList(partCode);
			}
			
			// Replace "Add to wish list" link.
			this.showAddWishList(accessory.partCode, true);
		}
	},
	
	// To be called by external windows (e.g. the wish list popup) to refresh
	// the accessories overview according to the current wish list. The
	// readyCallback is an optional function that is called after the new
	// wish list has been retrieved and processed. Used in wishlist.jsp to
	// finally close the popup window. This cannot be done directly since
	// then the remote DWR call fails if the calling window is already closed.
	wishListChangedExtern: function(readyCallback) {
		common.retrieveWishListSynch(this.handleWishListAfterRetrieve.bind(this, readyCallback));
	},
	
	// Wish list listener callback.
	wishListChanged: function(partCode, added, readyCallback) {
		common.log("Overview.wishListChanged: partCode: ' " + partCode + "', added: " + added);
		common.log(" - showing add wish list: " + !added);
		common.log(" - changed part code '" + partCode + "'");
		
		// This is a variation part code, we must use the main accessory part code to
		// find the DIV. Look it up in the variation-to-main-partcode map.
		var mainPartCode = this.varPartCodeToMainPartCode[partCode];
		if (mainPartCode != null) {
			if (added) {
				this.showAddWishList(mainPartCode, false);
			} else {
				// Part code removed, check if any variations of this accessory left in wish list.
				var accessoryDiv = this.getAccessoryDiv(mainPartCode);
				if (accessoryDiv != null && accessoryDiv.accessory != null) {
					var variationsLeft = false;
					var partCodes = accessoryDiv.accessory.variationPartCodes;
					for( var i = 0; i < partCodes.length; ++i ) {
						if (common.isInClientWishList(partCodes[i])) {
							variationsLeft = true;
							break;
						}
					}
					this.showAddWishList(accessoryDiv.accessory.partCode, !variationsLeft);
				}
			}
		}
	},
	
	// Process the retrieved wish list.
	handleWishListAfterRetrieve: function(readyCallback) {
		common.log("Retrieved wish list from server...");
		var results = $("acc_search_accessories");
		var accessoryDivs = results.getElementsByTagName("DIV");
		for( var i = 0; i < accessoryDivs.length; ++i ) {
			var div = accessoryDivs[i];
			if (div.accessory != null) {
				common.log(" - updating wish list link for accessory: " + div.accessory.displayName);
				this.showAddWishList(div.accessory.partCode, !this.isInWishList(div.accessory), true);
			}
		}
		
		if (readyCallback != null) {
			readyCallback();
		}
	},
	
	// Show "Add to wish list" or "In wish list" for the selected accessory.
	showAddWishList: function(partCode, showAdd, highlight) {
		// Replace "Add to wish list" link.
		var div = this.getAccessoryDiv(partCode);
		if (div != null) {
			// Find second link, that is the wish list button.
			var links = div.getElementsByTagName("A");
			
			// Retrieve elements to change.
			var wishListLink = links[1];
			var img = wishListLink.getElementsByTagName("IMG")[0];
			var span = wishListLink.getElementsByTagName("SPAN")[0];
			var changed = (showAdd && String(img.src).indexOf(Config["addWishList.gif"]) == -1) ||
						  (!showAdd && String(img.src).indexOf(Config["addWishList.gif"]) != -1);
			if (changed) {
				if (highlight && span != null) {
					Effects.highlight(span);
				}
			
				if (showAdd) {
					wishListLink.onclick = this.addToWishList.bind(this, partCode);
					wishListLink.title = "";
					wishListLink.href = "#";
					
					img.src = Config["addWishList.gif"];
					if (span != null) {
						span.innerHTML = Messages["common.addWishList"];
					}
				} else {
					if (common.enableRemoveFromWishList) {
						wishListLink.onclick = this.removeFromWishList.bind(this, partCode);
						wishListLink.href = "#";
					} else {
						wishListLink.onclick = common.ignoreClickHandler;
						wishListLink.removeAttribute("href");
					}
					img.src = Config["inWishList.gif"];
					
					if (common.enableRemoveFromWishList) {
						wishListLink.title = Messages["common.removeWishList"];
						if (span != null) {
							span.innerHTML = Messages["common.removeWishListShort"];
						}
					} else {
						wishListLink.title = "";
						if (span != null) {
							span.innerHTML = Messages["common.inWishList"];
						}
					}
				}
			}
		}
	}
};

// Create page class and initialize page when done loading.
var page = new AccessoryOverview();
Event.observe(window, "load", page.onLoad.bind(page));
