// Use object detection to determine browser type. IE or Not.
var isIE = window.ActiveXObject; 

// This is a hack. This function will be reassigned to a private
// function within OGrid later so that the function bcjump() can
// be accessed globally by Html codes.
var global_bc = function(target){
	alert('nothing is defined');
}

function OGrid(num_rows, row_height, num_cols, container) {
		
	//Initial Configuration
	container.style.overflow ="auto";
	var self = this;
	var col_width = container.style.width;
	col_width = col_width.substring(0,col_width.length-2);
	col_width -= 35;		

	// Component Vars
	var contentsList = new Array();
	var rows = new Array();
	var req; // xmlhttprequest
	var processor = new XSLTProcessor();	
	
	// State vars	
	var keywords = '';
	var matchList = new Array();
	var fullList = new Array();
	var curRow = 0;	
	var curRowStack = new Array();
	var scrollTopStack = new Array();
	var contentStack = new Array();
	var bcStack = new Array(); //breadcromb
	var level = 0;
	var lastCache = 0; //the last listing we have in localy
	var isAppend = false;
	var curAbbr = "";
	var getContent = false;
	var lock = false;
	var requestStack = new Array();
	var watchlist = new Array();	
	var inWatchlist = false;
	var wl_level = 0;
	
	this.refreshBC = function(){
		var bc = document.getElementById("breadcrumb");
		var str = "";
		for(var i=0; i<bcStack.length; i++){
			str += bcStack[i];			
			//bc.appendChild(bcStack[i]);
		}
		bc.innerHTML = str;
	}
	
	this.bcjump = function(target) {		
		while( level != target ){
			self.exit();
		}		
	}
	
	this.initBreadcromb = function(){		
		//var cromb = document.createTextNode('Home ');
		//cromb.onclick = self.bcjump;
		global_bc = this.bcjump;
		bcStack.push('<a href="#" onclick="global_bc(0)">Home </a>');		
		//bcStack.push(cromb);
		this.refreshBC();		
	}		
	
	this.initProcessor = function(){
		var myRequest = new XMLHttpRequest();
		myRequest.open("GET", "http://josephlai.com/projects/SuperCraig/lib/ogrid_xsl.php", false);
		myRequest.send(null);
		
		var xslRef = myRequest.responseXML;				
		processor.importStylesheet(xslRef);
	}
	
	this.setFullList = function(){
		var elm = container.getElementsByTagName("div");		
		fullList = new Array(elm.length);
		for(var i=0; i<elm.length; i++){						
			fullList.push(elm[i]);
		}									
	}
	
	this.buildMenu = function(){
		for(var i=0; i<mainmenu.length; i++){
			var disabb0 = mainmenu[i][0].split(":");
			var dis = disabb0[0];
			var abbr = disabb0[1];
			contentsList[i] = new menuitem(dis, abbr, 0);
			var next = new Array();
			for(var j=0; j<mainmenu[i][1].length; j++){				
				var disabb1 = mainmenu[i][1][j].split(":");
				var dis1 = disabb1[0];
				var abbr1 = disabb1[1];
				next[j] = new menuitem(dis1, abbr1, 1);
				next[j].setNext("query");
				next[j].setPrev(contentsList);
			}						
			contentsList[i].setNext(next);
			contentsList[i].setPrev("root");
		}
	}
	
	this.setContents = function(newContents){
		contentsList = newContents;
		rows = new Array(newContents.length);		
		for(var i=0; i<newContents.length; i++){
			rows[i] = new Row(num_cols, row_height, col_width, i);
			rows[i].setContents(contentsList[i]);
		}		
	}
	
	this.invalidate = function(){
		container.innerHTML = this.toString();
		var loadingBlock = document.getElementById("ogrid_loading");		
		loadingBlock.style.visibility = "hidden";
	}
	
	// Highlights the selected row
	this.setHighlight = function(index){
		
		var elm = document.getElementById("ogrid_row_"+index);				
		//elm.style.backgroundColor = '#E9F2F8';
		elm.style.backgroundColor = '#E4E9F3';
		
		if( index > 0 ) document.getElementById("ogrid_row_"+ (index-1) ).style.backgroundColor = '';
		if( index < rows.length-1 ) document.getElementById("ogrid_row_"+ (index+1) ).style.backgroundColor = '';		
	}
	
	this.loadingBlock = function(){
		var ret = '';
		ret = '<span id="ogrid_loading" style="display:block;visibility:visible;width:32px;height:32px;position:absolute;top:200px;left:475px;';
		ret += 'background-image: url(img/loading.gif); z-index:100;"></span>';
		return ret;
	}
	
	this.hideLoadingIcon = function() {
		var loadingBlock = document.getElementById("ogrid_loading");
		loadingBlock.style.visibility = "hidden";
	}
	
	// Post-process the Ajax request
	this.processReqChange = function() {
		// only if req shows "loaded"
		if (req.readyState == 4) {
			// only if "OK"
			if (req.status == 200) {
				
				if( !isAppend && !getContent){				
					scrollTopStack.push(container.scrollTop);
					self.updateMap(-122.316192, 47.659820);
					
					// OK, we are good. Process it!
					var target = req.responseXML;				
					var newFragment = processor.transformToFragment(target, document);																
					
					container.innerHTML = "";
					
					container.innerHTML += self.loadingBlock();
					self.hideLoadingIcon();
					
					container.appendChild(newFragment);						
					
					var elm = container.getElementsByTagName("div");
					rows = new Array(elm.length);
					fullList = new Array(elm.length);
					for(var i=0; i<elm.length; i++){
						elm[i].id = "ogrid_row_"+i;							
						rows[i] = elm[i];
						fullList.push(elm[i]);
					}											
					
					curRowStack.push(curRow);								
					level++;
					curRow = 0;
					self.setHighlight(0);
					lastCache = 10;
					isAppend = true; // start appending from here on
					
				} 
				// We are appending!
				else if (!getContent) { 
					// set the map focus to curRow
					// self.updateMap(-122.316192, 47.659820);
					
					// OK, we are good. Process it!
					var target = req.responseXML;				
					var newFragment = processor.transformToFragment(target, document);																				
					container.appendChild(newFragment);										
					
					// Reassign id's for everybody
					var elm = container.getElementsByTagName("div");
					rows = new Array(elm.length);
					fullList = new Array(elm.length);
					for(var i=0; i<elm.length; i++){
						elm[i].id = "ogrid_row_"+i;	
						rows[i] = elm[i];
						fullList.push(elm[i]);
					}																
					self.setHighlight(curRow);
					lastCache += 10;
					
				} else if ( getContent ){
					scrollTopStack.push(container.scrollTop);
					self.updateMap(-122.316192, 47.659820);
					
					// Save the old stuff
					self.hideLoadingIcon();
					contentStack.push(container.innerHTML);
					
					// OK, we are good. Process it!
					var target = req.responseText;									
					container.innerHTML = target;
					container.scrollTop = 0;
										
					curRowStack.push(curRow);								
					if(!inWatchlist) level++;
					curRow = 0;						
				}
				self.setIcon();
								
			} else {
				alert("There was a problem retrieving the XML data:\n" +
					req.statusText);
			}
		}
	}
	
	// Send the HTTP GET Request!
	this.loadXMLDoc = function(url) {
		req = false;
		// branch for native XMLHttpRequest object
		if(window.XMLHttpRequest) {
			try {
				req = new XMLHttpRequest();
			} catch(e) {
				req = false;
			}
		// branch for IE/Windows ActiveX version
		} else if(window.ActiveXObject) {
			
			alert("Sorry, Internet Explorer version is under development ... :( ");
			/*
			try {
				req = new ActiveXObject("Msxml2.XMLHTTP");
			} catch(e) {
				try {
					req = new ActiveXObject("Microsoft.XMLHTTP");
				} catch(e) {
					req = false;
				}
			}
			*/
		}
		if(req) {
			requestStack.push(req);
			req.onreadystatechange = this.processReqChange;
			req.open("GET", url, true);
			req.send("");			
		}
	}		
	
	this.updateMap = function(lon, lat){
		map.clearOverlays();
		var point = new GPoint(lon, lat);
		map.centerAndZoom(point, 4); 	
		var marker = new GMarker(point);
		map.addOverlay(marker);	
	}
	
	this.refreshWatchlist = function(){
		container.innerHTML = "";
		container.innerHTML += self.loadingBlock();
		self.hideLoadingIcon();
		rows = new Array(watchlist.length);
		
		if( watchlist.length > 0){
			for(var i=0; i<watchlist.length; i++){
				var elm = watchlist[i];
				elm.id = "ogrid_row_"+i;						
				rows[i] = elm;
				container.appendChild(elm);	
			}														
		} else {
			var elm = document.createElement('div');
			elm.id = 'ogrid_row_0';
			elm.innerHTML = 'Your Watchlist is empty. <br> Press <b>+</b> while browsing to add items.';
			elm.style.fontSize = '16pt';
			elm.style.fontFamily = 'Verdana, Geneva, Arial, helvetica';	
			rows = elm;
			container.appendChild(elm);
		}
	}
	
	this.setIcon = function(){
		var elm = container.getElementsByTagName("div");		
		for(var i=0; i<elm.length; i++){
			var myLink = (elm[i].getElementsByTagName('a'))[0];
			var exist = false;
			for(var j=0; j<watchlist.length; j++){
				existingLink = (watchlist[j].getElementsByTagName('a'))[0];
				if( myLink.toString() == existingLink.toString() ){
					exist = true;		
					break;
				}
			}
			
			var myImg = (elm[i].getElementsByTagName('img'))[0];
			if( exist )
				myImg.src = 'img/in_watchlist.gif';
			else
				myImg.src = 'img/add_watchlist.gif';			
		}															
	}
	
	// Empty pending requests
	this.abortRequests = function() {		
		while(requestStack.length > 0){
			var myReq = requestStack.pop();
			myReq.abort();
		}
	}
	
	// Show the loading icon
	this.showLoadingIcon = function() {
		var loadingBlock = document.getElementById("ogrid_loading");
		loadingBlock.style.visibility = "visible";
	}
	
	this.filter = function(){
		//keywords
		
		
	}
									  	
	this.enter = function(){
		
		// Empty pending requests
		self.abortRequests();
		
		if( level == 0 && !inWatchlist){ //contentsList[curRow].getNext() != "query"
			bcStack.push(' > <a href="#" onclick="global_bc(1)"> '+ contentsList[curRow] +'</a>');
			curRowStack.push(curRow);
			scrollTopStack.push(container.scrollTop);												
			self.setContents( contentsList[curRow].getNext() );						
			self.invalidate();
			self.setFullList();
			self.setHighlight(0);
			curRow = 0;
			level++;
		} else if ( level == 1 && !inWatchlist) {
			
			bcStack.push(' > <a href="#" onclick="global_bc(2)"> '+ contentsList[curRow] +'</a>');			
			self.showLoadingIcon();
			curAbbr = contentsList[curRow].getAbbr();
			//var url = 'http://josephlai.com/projects/SuperCraig/bin/cl.cgi?list='+curAbbr+'&start=1';
			var url = 'http://josephlai.com/projects/SuperCraig/bin/getListings.php';
			self.loadXMLDoc(url);
			
			// level++ & center and zoom and mark the first point 
			// are in processReqChange() to accomodate async behavior
		} else if (level == 2 && !inWatchlist) {			
			self.showLoadingIcon();
			var myRow = document.getElementById("ogrid_row_"+ curRow);
			//var myLink = (myRow.getElementsByTagName('a'))[0];			
			var myLink = 'http://www.google.com/';
			//var url = 'http://abstract.cs.washington.edu/~ccerng/get.py?url=' + myLink;
			var url = 'http://josephlai.com/projects/SuperCraig/bin/get.cgi?url=' + myLink;
			getContent = true;
			self.loadXMLDoc(url);
			// load the page						
			// push current contents
			// push curRow
			// push scrollTop
			// increment level
			// center and zoom and mark the first point
		} else if (wl_level == 0 && inWatchlist) {						
			// Save old state						
			curRowStack.push(curRow);
			scrollTopStack.push(container.scrollTop);
			contentStack.push(container.innerHTML);			
			
			var w_icon = document.getElementById('watchlist_icon');
			w_icon.style.backgroundColor = '#EEFF66';
			container.style.backgroundColor = '#FFFFE5';
			
			self.refreshWatchlist();
			curRow = 0;
			self.setHighlight(curRow);
			wl_level++;
		} else if (wl_level == 1 && inWatchlist && watchlist.length > 0) {			
			self.showLoadingIcon();
			
			var myRow = document.getElementById("ogrid_row_"+ curRow);
			//var myLink = (myRow.getElementsByTagName('a'))[0];			
			var myLink = 'http://www.google.com/';
			//var url = 'http://abstract.cs.washington.edu/~ccerng/get.py?url=' + myLink;
			var url = 'http://josephlai.com/projects/SuperCraig/bin/get.cgi?url=' + myLink;						
			getContent = true;
			self.loadXMLDoc(url);
			
			wl_level++;
		}
		
		self.refreshBC();
	}
	
	this.exit = function() {
		
		// Empty pending requests
		self.abortRequests();
		
		if( level < 2 && contentsList[curRow].getPrev() != "root" && !inWatchlist){
			bcStack.pop();
			self.setContents( contentsList[curRow].getPrev() );											
			self.invalidate();
			self.setFullList();
			curRow = curRowStack.pop();
			container.scrollTop = scrollTopStack.pop();
			level--;
			self.setHighlight(curRow);
		} else if(level == 2 && !inWatchlist) {			
			map.clearOverlays();
			lastCache = 0;
			isAppend = false;
			
			bcStack.pop();
			self.setContents( contentsList );
			self.invalidate();
			self.setFullList();
			curRow = curRowStack.pop();
			container.scrollTop = scrollTopStack.pop();						
			level--;
			self.setHighlight(curRow);
		} else if(level == 3 && !inWatchlist){
			getContent = false;
			container.innerHTML = contentStack.pop();
			
			// set the "rows" back
			var elm = container.getElementsByTagName("div");
			rows = new Array(elm.length);
			for(var i=0; i<elm.length; i++){
				elm[i].id = "ogrid_row_"+i;	
				rows[i] = elm[i];
			}
			
			self.setIcon();			
			curRow = curRowStack.pop();
			container.scrollTop = scrollTopStack.pop();						
			level--;
			self.setHighlight(curRow);
		} else if (wl_level == 1 && inWatchlist) {						
			container.innerHTML = contentStack.pop();			
			curRow = curRowStack.pop();									
			container.scrollTop = scrollTopStack.pop();
			wl_level--;
			inWatchlist = false;
			
			if(level == 2) self.setIcon();
			
			// Stupid stuff we have to do to accommodate old stupid codes...will fix some day		
			if( level < 2 ){
				self.setContents( contentsList );
				self.invalidate();
			} else if (level == 2 ){
				var elm = container.getElementsByTagName("div");
				rows = new Array(elm.length);
				for(var i=0; i<elm.length; i++){
					elm[i].id = "ogrid_row_"+i;	
					rows[i] = elm[i];
				}
			}			
			container.style.backgroundColor = '#FFFFFF';
			var w_icon = document.getElementById('watchlist_icon');
			w_icon.style.backgroundColor = '#FFFFFF';
			if(level < 3) self.setHighlight(curRow);
			
		} else if (wl_level == 2 && inWatchlist) {	
			container.innerHTML = contentStack.pop();			
			curRow = curRowStack.pop();									
			container.scrollTop = scrollTopStack.pop();
			wl_level--;
			self.setHighlight(curRow);
		}
		
		self.refreshBC();		
	}
	
	// Handles the key down event
	var kbMap = {
		8  : "backspace",
		13 : "enter",
		37 : "left",
		38 : "up",
		39 : "right",
		40 : "down",
		33 : "pageUp",
		34 : "pageDown",
		36 : "home",
		35 : "end",
		106: "star",
		107: "plus",
		109: "minus"
		};
	
	// Because this function will be assigned to the window object,
	// where the keyword "this" will then be pointing to window,
	// we use a varible "self" to keep a reference to OGrid.
	this.kbPressHandler = function(event){							
		var kcode = kbMap[event.keyCode];
		if (kcode)	{
			switch(kcode){
				case "up" :
					if( level < 3 || inWatchlist){
						if(curRow > 0){
							curRow--;
							self.setHighlight(curRow);
						
							if( level == 2){
								self.updateMap(-122.316192, 47.659820);
							}
						
							myRow = document.getElementById("ogrid_row_"+ curRow);
							
							// Scroll the window if out of visible bound
							if( (container.scrollTop ) > 
								(myRow.offsetTop - container.offsetTop) ) {
								
								container.scrollTop -= myRow.offsetHeight;
							}		
						}						
					} else {
						container.scrollTop -= 10;
					}							
					break;
				case "down" :					
					if( level < 3 || inWatchlist ){
						if(curRow < rows.length-1){
							curRow++;
							self.setHighlight(curRow);
											
							if( level == 2){
								self.updateMap(-122.316192, 47.659820);
								if( (lastCache - curRow) < 8 ){
									
									//fetch some more
									//var url = 'http://abstract.cs.washington.edu/~ccerng/cl.py?list='+curAbbr+'&start='+(lastCache+1);									
									//self.loadXMLDoc(url);
								}
							}
											
							myRow = document.getElementById("ogrid_row_"+ curRow);
							
							// Scroll the window if out of visible bound
							if( (container.scrollTop + container.offsetHeight) < 
								(myRow.offsetHeight + myRow.offsetTop - container.offsetTop) ) {
								
								container.scrollTop += myRow.offsetHeight;
							}					
						}						
					} else {
						container.scrollTop += 14;
					}								
					break;
				case "enter" :
				case "right" :
					self.enter();					
					break;	
				case "backspace" :
					var filter_textbox = document.getElementById('filter_text');
					if( filter_textbox.value != ''){
						// fake the effect of backspace
						var str = filter_textbox.value;
						str = str.substring(0, str.length-1);
						filter_textbox.value = str;				
					} else {
						self.exit();
					}
					break;
				case "left" :															
					self.exit();					
					break;				
			}		
			return false;
			event.stopPropagation();			 
		}
	};			

	this.kbDownHandler = function(event){		
		var kcode = kbMap[event.keyCode];			
		if (kcode)	{
			switch(kcode){		
				case 'star' :
					if(!inWatchlist){												
						inWatchlist = true;
						self.enter();	
					} else {						
						self.exit();						
					}										
					break;
				case 'plus' :
					if( level == 2 && !inWatchlist){
						var myRow = document.getElementById("ogrid_row_"+ curRow);
						
						// use the link as id to pick out duplicates.
						var myLink = (myRow.getElementsByTagName('a'))[0];
						var exist = false;
						for(var i=0; i<watchlist.length; i++){
							existingLink = (watchlist[i].getElementsByTagName('a'))[0];
							if( myLink.toString() == existingLink.toString() ){
								exist = true;		
								break;
							}
						}
						
						if( !exist ){
							var copy = myRow.cloneNode(true); // get a deep clone
							copy.style.backgroundColor = '#FFFFFF';
							((copy.getElementsByTagName('img'))[0]).src = 'img/remove_watchlist.gif';
							watchlist.push(copy); 
							var myImg = (myRow.getElementsByTagName('img'))[0];
							myImg.src = 'img/in_watchlist.gif';
						} 
						// else do nothing
					}
					break;
				case 'minus' :
					if( wl_level == 1 && inWatchlist && watchlist.length > 0){
						for(var i=0; i<watchlist.length; i++){
							if( watchlist[i].id == 'ogrid_row_'+ curRow ){
								for(var j = i; j<watchlist.length-1; j++){
									watchlist[j] = watchlist[j+1];
								}
								watchlist.pop();
								break;
							}
						}
						self.refreshWatchlist();
						if(curRow > watchlist.length-1 ) curRow--;
						self.setHighlight(curRow);
					}				
					break;				
			}									
		} else {					
			var filter_textbox = document.getElementById('filter_text');			
			filter_textbox.focus();
			return true;
		}
		return false; 
	};	
	
	this.kbUpHandler = function(event){		
		var filter_textbox = document.getElementById('filter_text');
		filter_textbox.blur();
		var temp_text = filter_textbox.value;
		if( keywords != temp_text ){
			keywords = temp_text;
			self.filter();
		}
		
		/*
		var kcode = kbMap[event.keyCode];
		if (kcode)	{
			switch(kcode){
				case "backspace" :										
					return true;
					break;
			}
		}
		*/										
		return false;
	}

	// Prints out the HTML tags
	this.toString = function(){
		var ret = "";
		
		ret += this.loadingBlock();
		
		ret += '<div>';
			for(var i=0; i<rows.length; i++){
				if(rows[i].isExternal){
					
				} else {
					ret += rows[i].toString();
				}
			}
		ret += '</div>';								
		
		return ret;
	}		
	
	/*get the event model*/
	//emod = (e) ? (e.eventPhase) ? "W3C" : "NN4" : (window.event) ? "IE4+" : "unknown";

	//if (emod == "NN4") {
    //doc.captureEvents(Event.KEYDOWN);	
	//window.onkeydown = this.kbHandler;			
	
	// Initialize the OGrid	
	this.initProcessor();
	this.initBreadcromb();
	this.buildMenu();
	this.setContents(contentsList);	
	this.invalidate();				
	this.setFullList();
						
	window.onkeypress = this.kbPressHandler;		
	//document.body.onkeypress = this.kbPressHandler;	
	//addEvent(container, 'onkeypress', this.kbPressHandler, false);
	window.onkeydown = this.kbDownHandler;
	//addEvent(window, 'onkeydown', this.kbDownHandler, false);
	//document.body.onkeydown = this.kbDownHandler;
	window.onkeyup = this.kbUpHandler;
	//document.body.onkeyup = this.kbUpHandler;
	this.setHighlight(0);
}

function Row(num_cols, row_height, col_width, i) {
	var contents = "Contents coming soon...";
	var index = i;
	
	this.setContents = function(newContents){
		contents = newContents;
	}
	
	this.getContents = function(){
		return contents;
	}	
	
	// Prints out the HTML tags
	this.toString = function(){
		var str = '';
		str += '<div ';
		str += 'style="';
		//str += 'border: 1px solid #ADADAD;';
		str += 'height:'+row_height+'px;';
		str += 'width:'+col_width*num_cols+'px;';
		str += 'padding-left:10px;'
		str += 'padding-top:6px;'		
		str += '" ';
		str += 'id="ogrid_row_' + index + '" >';
		
		str += '<table><tr>';
		
			str += "<td ";															
			str += 'style="';
			//str += 'border: 1px solid #ADADAD;';
			str += 'width:35px;';
			str += 'font-family: Verdana, Geneva, Arial, helvetica;';
			str += 'font-size:16pt;';
			str += '" ';							
			str += '>';	
			//var test = contents; //.substring(0,1);
			str += '['+ contents.toString().substring(0,1) +']';
			str += '</td>'		
			
			str += "<td ";															
			str += 'style="';
			//str += 'border: 1px solid #ADADAD;';
			//str += 'width:' + (col_width -80) + 'px;';			
			str += 'font-family: Verdana, Geneva, Arial, helvetica;';
			str += 'font-size:16pt;';
			str += '" ';							
			str += '>';			
			str += contents;
			str += '</td>'
			
			str += "<td ";															
			str += 'style="';
			//str += 'border: 1px solid #ADADAD;';
			str += 'text-align:right;';
			str += 'width:25px;';
			str += 'font-family: Verdana, Geneva, Arial, helvetica;';
			str += 'font-size:16pt;';
			str += '" ';							
			str += '>';			
			str += '>';
			str += '</td>'
						
		str += '</tr></table>';
		str += '</div>';
		return str;
	}
}

function menuitem(_display, _abbr, _level) {
	var display = _display;
	var abbr = _abbr;
	var level = _level;
	var next;
	var prev;
	
	this.getAbbr = function(){
		return abbr;
	}
	
	this.setNext = function(_next){
		next = _next;
	}
	
	this.getNext = function(){
		return next;
	}
	
	this.setPrev = function(_prev){
		prev = _prev;
	}
	
	this.getPrev = function(){
		return prev;
	}
	
	this.toString = function(){
		return display;
	}
}

var mainmenu = [
	[ "community:ccc" , 
		[ "activities:act", "artists:ats",  "childcare:kid",  "general:com",  "groups:grp",  "pets:pet",  
		  "events:cal",  "lost+found:laf", "musicians:muc",  "news+views:vnn",  "politics:pol",  
		  "rideshare:rid",  "volunteers:vol",  "classes:cal/#classes"
		]
	]
	, 
	[ "personals:", 
		[ "strictly platonic:stp",  "women seek women:w4w",  "women seeking men:w4m",  
		  "men seeking women:m4w",  "misc romance:msr",  "missed connections:mis"
		]
	]
	,
	[ "housing:hhh",
		[ "apts / housing:apa", "rooms / shared:roo", "sublets / temporary:sub",  "housing wanted:hsw",
		  "housing swap:swp",  "vacation rentals:vac",  "parking / storage:prk",  "office / commercial:off",
		  "real estate for sale:rfs"
		]
	]
	,
	[ "for sale:sss",
		[ "barter:bar",  "bikes:bik",  "boats:boa",  "books:bks", "free:zip",  "furniture:fur",  "general:for",  
		  "jewelry:jwl",  "sporting:spo", "tickets:tix",  "tools:tls", "wanted:wan", "auto parts:pts",  
		  "baby+kids:bab",  "cars+trucks:car",  "cds/dvd/vhs:emd", "clothes+acc:clo",  "collectibles:clt",  
		  "computer:sys",  "electronics:ele",  "garage sale:gms",  "household:hsh", "motorcycles:mcy",  
		  "music instr:msg"
		]
	]
	, 
	[ "services",
		[ "computer:cps",  "creative:crs", "event:evs",  "financial:fns",  "legal:lgs",  "lessons:lss", 
		  "automotive:aos", "household:hss", "labor/move:lbs", "skill'd trade:sks", "real estate:rts",
		  "sm biz ads:biz",  "therapeutic:ths"
		]
	]
	,	 
	[ "gigs",
		[ "computer:cpg", "creative:crg", "crew:cwg", "domestic:dmg", "event:evg", "labor:lbg", 
		  "writing:wrg", "talent:tlg"
		]
	]
]
