var maxResults = 20, //MUST BE REVERTED TO 20!
	addrLen	   = 26, //how many numbers to add to the address filter
	requestsLocked = false,//mutex of ajax requests
	scrollLocked = false,//mutex for on demand requests based on scroll
	befEnd = 43*5, //how many pixels (before the end) to ask for more.
	storeNameTruncation = 21,//at how many chars to truncate store name in stores list
	headerHeight = 43,//height in pixels
	macworldID = 300000,
	alphaStoreGroup = false,//group stores by letter in the alphabetic list ?
	hoursExpiration = 2,//how many hours to keep the login cookie alive for.
	maxNumberOrdering = 999999999,
	level,
	extra;

if( document.domain == "i.zami.com" )
	maxResults = 20;

function persist(){
	var a = persist.last = arguments;
	List.each(['country','state','city','street','zip'],function( v, i ){
		if( a[i] != -1 )
			Cookie.set( v, a[i]||'');
	});
};
function getPersistedState( state ){
	return state.name && state.name != 'NO_STATE' ? US(state.country.name) ? state.abbreviation : state.name : '';
};
function populate( form, name, cname, value ){
	if( form[name] )
		form[name].value = unescape( value || Cookie.get( cname || name ) || '');
};

function makeCityStateCountry( city, zip ){//formats the city into the form of city, state, country
	var state = city.state,
		statename = state.name;

	var data = [city.name + ','],
		middle = getPersistedState(state);
	
	if( middle )
		data.push( middle );
	if( zip )
		data.push( zip );
	data.push( state.country.name );
	return data.join(' ');
};

var getStoreName = alphaStoreGroup && function( store ){
	return store.name;	
};

function makeAddress(store){
	var street = store.street,
		format = street.city.state.country.orderType.code;
	
	switch( format ){
		case 'NF'://number first
			return store.rawNumber + ' ' + street.name;
		case 'NAF'://name first
		default:
			return street.name + ' ' + store.rawNumber;
	}
};
var Auth = {
	add:function(list){
		var code = Cookie.get('AnonymousLoginCode'),
			confirmed = parseInt(Cookie.get('LoginCodeConfirmed'),10),
			state =  !code ? 'login' : confirmed ? 'logout' : 'check';
		//To know if we are inside MacWorld city, use Cache('macworld') == false/true
		list = DOM.prepend( list , 'div' );
		list.className = 'auth';
		var a = DOM.append( list, '<a id="lnk_login" href="#">&nbsp;</a>');
		Auth.setState( state, a );
	},
	login:function(){
		History.load('login.html', function(){
			var form = DOM.$('doLogin');
			turnSave( form );
			DOM.text( 'forwardButton', Lex.get('submit'), true );
			Validator.prepare( form, function(){
				Form.saveLogin(
					function(){
						Cookie.remove('LoginCodeConfirmed');
						setTimeout(function(){
							Auth.setState('check');
							Tip.flash( Lex.get('thx_login'), 4000 ); }
						, 800);
					},
					function( res ){
						if( (/\w{64}/).test( res ) ){
							var expire = new Date;
							expire.setHours( expire.getHours() + hoursExpiration );
							Cookie.set('AnonymousLoginCode', res, expire);
							return 'OK';
						}
						return res;
					}
				);
			});
			populate( form, 'userEmail' );
		});
	},
	logout:function(){
		Cookie.remove('AnonymousLoginCode');
		Auth.setState( 'login' );
	},
	check:function(){
		if( Cookie.get('LoginCodeConfirmed') ){
			confirmed();
		}else{
			Remote.get( 'login-validated', function( response ){
				if( response.trim() == 'OK' )
					confirmed();
			});
		}
		function confirmed(){
			Cookie.set('LoginCodeConfirmed','1');
			Auth.setState( 'logout' );
			var span = DOM.$('message_await');
			if( span )
				DOM.remove( span );
		};
	},
	setState:function( state, link ){
		link = link || DOM.$('lnk_login');
		if( !link ) return;
		if( state == 'check' && !link.previousSibling )
			DOM.prepend( link.parentNode, '<span id="message_await" class="await">'+Lex.get('wait_conf')+'</span>' );
		link.onclick = Auth[state];
		DOM.text( link, Lex.get(state), true );
	}
};

function submitMessage(){//shows a message after posting a form
	setTimeout(function(){
		var logged = Cookie.get('LoginCodeConfirmed') && Cookie.get('AnonymousLoginCode');
		Tip.flash(
			Lex.get( logged ? 'thx_submit' : 'thx_confirm' )
		, logged ? 2000 : 5000 ); //thx_submit's message is shorter.
		if( logged && Cache('general.section') == 'store' ){
			History.skip();
			loadStorePage( Cache('stores.lastid') );//reload stores page.
		}
	},1000);
};

function loadFromURL(){
	var vars = location.search.slice(1).split('&'),
		data = {};
	for( var i = vars.length, d; i; ){
		d = vars[--i].split('=');
		data[ d[0] ] = d[1];
	}
	
	if( loadFromURL.map[data.cat] ){
		History.oneCallback = function(){
			var back = DOM.$('backButton');
			DOM.text( back, Lex.get('home'), true );
			back.onclick = Form.oneCallback = function(){
				History.skip();
				loadCitiesPage();
				back.onclick = back._click_;
				Form.oneCallback = null;
			};
		};
		loadFromURL.map[data.cat]( data.id, data.replyOfId );		
	}else
		loadCitiesPage();
};

// Mapping GET categories ('cat') to real functions
loadFromURL.map = {
	streets: loadStreetsPage,
	stores: loadStoresPage,
	store: loadStorePage,
	others: loadStoresFromUndefinedCites,
	'add-store': loadAddStorePage,
	'add-message': loadAddMessagesPage,
	'add-description':loadAddDescriptionPage,
	'add-review': loadAddCommentsPage,
	messagesByCity: loadMessagesPageByCity,
	messagesByStreet: loadMessagesPageByStreet,
	reviewsByCity: loadReviewsPageByCity,
	reviewsByStreet: loadReviewsPageByStreet,
	descriptionsByCity: loadDescriptionsPageByCity,
	descriptionsByStreet: loadDescriptionsPageByStreet,
	login: Auth.login

};

function loadCitiesPage(){
	CityRegistry.getAllCitiesWithStores(maxResults, function(cities){
		var page = new Page( false, '{0}', 'cities' ),
			list = page.getList(),
			state = cities[0].state;
		Cache( 'cities.lastPage', page );
		
		persist();
		
		Grouper.page( cities, list, 'loadStreetsPage',function(city){ 
			return makeCityStateCountry(city);
		}, null, null, "loadReviewsPageByCity",  "loadMessagesPageByCity", "loadDescriptionsPageByCity");

		DOM.text( 'backButton', Lex.get('about'), true );
		DOM.append( list, '<h2>-</h2>' );
		
		var other_cities = DOM.nest( list,'ul', 'li', 'a' );
		other_cities.href = '#';
		DOM.text( other_cities, Lex.get('other_cities') );
		other_cities.onclick = loadStoresFromUndefinedCites;

		Auth.add( page.wrapper );

		if( location.search.indexOf('macworld=1') != -1 )
			Cookie.set( 'defaultCity', macworldID );
		else if( Cookie.get('defaultCity') == macworldID )
			Cookie.remove( 'defaultCity' );

		var dc = Cookie.get('defaultCity');
		if( dc ){
			page.wrapper.style.display = 'none';
			requestsLocked = false;
			loadStreetsPage( dc );
		}else{
			page.set();
		}
	});
};

function cacheAlphaData( sv ){ // Creates the top and bottom of streets and alpha stores
	var key = sv.stores !== undefined ? 'stores' : 'streets',
		arr = sv[key] = sv[key] || [ ];

	Cache( key+'.top', {
		actual:sv.letterPosition,
		modifier:-1,
		action:'prepend',
		done: !sv.letterPosition
	});
	Cache( key+'.bottom', {
		actual:sv.letterPosition + (arr.length||1) - 1,
		modifier:0,
		done:arr.length < maxResults
	});
};

/****************** STREETS *******************/
function loadStoresFromUndefinedCites(){
	Cache('stores.moreMethod', loadMoreStoresFromUndefinedCities);
	StoreRegistry.getStoreViewAlphaFromUndefinedCities( maxResults, 0, function(sv){
		cacheAlphaData( sv );
		
		var usefilter = sv.stores.length >= maxResults,
			page = new Page( usefilter, '{0} {1}', 'stores', Lex.get('from_other_cities') );		

		Cache( 'stores.lastPage', page );
		var list = DOM.ensure( page.getList(), 'ul' );

		Grouper.undefinedStore( sv.stores, list );

		if( usefilter )
			Grouper.letters( page.getFilter(), 'loadStoresFromUndefinedCitesStartingWith' );

		page.set();
	});
};
function loadStoresFromUndefinedCitesStartingWith( letter ){
	var page = Cache('stores.lastPage'),
		list = DOM.ensure( page.getList(), 'ul' );

	StoreRegistry.getStoreViewAlphaFromUndefinedCitiesStartingWith( letter, maxResults, function( sv ){
		cacheAlphaData( sv );

		Grouper.undefinedStore( sv.stores, list );

		var lis = DOM.$$('li', list), i=0;
		while( lis[i] && lis[i].firstChild.childNodes[1].data < letter )
			i++;

		resetScroll( lis[i] || lis[lis.length-1] );
	});
};

function loadMoreStoresFromUndefinedCities( up ){
	scrollLocked = true;
	var 
		data = up ? Cache('stores.top') : Cache('stores.bottom'),
		diff = up ? -Math.min(maxResults,data.actual) : 1,
		next = data.actual + diff,

		page = Cache( 'stores.lastPage' ),
		list = DOM.first( page.getList(), 'ul' ),

		loading = DOM.create('<div id="loading">');
	DOM.text( loading, Lex.get('loading') );
	DOM[data.action||'append']( list, loading );

	StoreRegistry.getStoreViewAlphaFromUndefinedCities( maxResults, next, function( sv ){
		var 
			lh = loading.clientHeight,
			stores = sv.stores || [ ],
			first;

		list.removeChild( loading );

		if( up ){
			first = list.firstChild;
			stores.length = Math.min( data.actual, stores.length );
		}
		data.actual = up ? next : data.actual + stores.length;
		data.done = !data.actual || stores.length < maxResults;
		
		Grouper.undefinedStore( sv.stores, list, data.action, true );
		
		if( first )
			resetScroll( first.previousSibling );
		scrollLocked = false;
	});
};

function loadStreetsByLetter( letter ){
	var page = Cache('streets.lastPage');
	//Opacity.hide( page.getList() );
	StreetRegistry.getStreetViewByCityStartingWith( Cache('cities.last'), letter, maxResults,function( sv ){
		cacheAlphaData( sv );

		Grouper.street( sv.streets, page.getList() );

		var h2s = DOM.$$('h2', page.getList()), i=0;
		while( h2s[i] && h2s[i].firstChild.nodeValue < letter )
			i++;

		resetScroll( h2s[i] || h2s[h2s.length-1] );
	});
};

function loadMoreStreets( up ){
	scrollLocked = true;
	var 
		data = up ? Cache('streets.top') : Cache('streets.bottom'),
		diff = up ? -Math.min(maxResults,data.actual) : 1,
		next = data.actual + diff,

		page = Cache( 'streets.lastPage' ),
		list = page.getList(),
		loading = DOM.create('<div id="loading">');

	DOM.text( loading, Lex.get('loading') );
	DOM[data.action||'append']( list, loading );

	StreetRegistry.getValidatedStreetsByCity( Cache('cities.last'), maxResults, next, function( streets ){
		var lh = loading.clientHeight, first;
		list.removeChild( loading );
		streets = streets || [ ];
		if( up ){
			first = list.firstChild;
			streets.length = Math.min( data.actual, streets.length );
		}
		data.actual = up ? next : data.actual + streets.length;
		data.done = !data.actual || streets.length < maxResults;
		
		Grouper.street( streets, list, data.action, true );

		if( first )
			resetScroll( first.previousSibling );
		scrollLocked = false;
	});
};

function loadStreetsPage(cityId){
	var city = Cache( 'cities.last', {id:cityId} );
	Cache('macworld', cityId == macworldID );
	StreetRegistry.getValidatedStreetsByCity( city, maxResults, null, function(streets){
		var 
			usefilter = streets.length >= maxResults,
			city = streets[0].city,
			state = city.state,
			country = state.country.name,
			page = new Page( usefilter, '{1} {0}', 'streets', city.name );
			
		Cache( 'streets.top', {done: true} );
		Cache( 'streets.bottom', {
			actual: streets.length - 1,
			modifier:0,
			done: !usefilter
		});

		Cache( 'streets.lastPage', page );
		Grouper.street( streets, page.getList() );

		if( Cache('macworld') ){
			DOM.$$('h2', page.getList() )[0].style.display = 'none';
			var ul = DOM.first(page.getList(), 'ul'), i = 2;
			//Adding empty LI and a link to thealphabetical list
			DOM.append( ul , 'li', '<li><a href="#" onclick="loadAlphaStoresPage()">'+Lex.get('alpha_list')+'</a></li>');
			var click = function(){
				Cookie.set('mapFloor', (this.parentNode.index+1).toString() );
			};
			var links_li = DOM.$$('li', ul);
			while( i-- ){
				var l = links_li[i];
				l.index = i;
				l.firstChild.addEventListener('click',click,true);
			}
		}

		Auth.add( page.wrapper );
		persist( country, getPersistedState(state), city.name );

		if( usefilter )
			Grouper.letters( page.getFilter(), 'loadStreetsByLetter' );
		var lpage = Cache('cities.lastPage');
		if( Cookie.get('defaultCity') && lpage ){
			lpage.set();
			page.set();
			lpage.wrapper.style.display = 'block';
		}else{
			page.set();
		}
	});
};
/***************** STORES **********************/
function cacheStoresData( sv ){
	var stores = sv.stores = sv.stores|| [ ];
	Cache( 'stores.top', {
		actual:sv.storePosition,
		modifier:-1,
		action:'prepend',
		done: !sv.storePosition || (stores[0] && stores[0].number == sv.minNumber)
	});
	Cache( 'stores.bottom', {
		actual:sv.storePosition + (stores.length||1) - 1,
		done: stores.length < maxResults || stores[maxResults-1].number == sv.maxNumber,
		modifier:0
	});
};

function loadStoresFrom( start ){
	var page = Cache('stores.lastPage'),
		list = DOM.ensure( page.getList(), 'ul' );

	StoreRegistry.getStoreViewStartingFrom( Cache('streets.last'), start, maxResults,function( sv ){
		cacheStoresData( sv );
		Grouper.store( sv.stores, list );

		var lis = DOM.$$('li', list), i=0;
		while( sv.stores[i] && sv.stores[i].number < start )
			i++;

		resetScroll( lis[i] || lis[lis.length-1] );
	});
};

function loadMoreStores( up ){
	if( Cache('stores.moreMethod') )
		return Cache('stores.moreMethod')( up );

	scrollLocked = true;

	var 
		data = up ? Cache('stores.top') : Cache('stores.bottom'),
		diff = up ? -Math.min(maxResults,data.actual) : 1,
		next = data.actual + diff,
		page = Cache( 'stores.lastPage' ),
		list = DOM.first( page.getList(), 'ul' ),
		loading = DOM.create('<div id="loading">');

	DOM.text( loading, Lex.get('loading') );
	DOM[data.action||'append']( list, loading );

	StoreRegistry.getStoreViewByStreet( Cache('streets.last'), null, maxResults, next, function( sv ){
		var lh = loading.clientHeight, first;
		list.removeChild( loading );
		var stores = sv.stores || [ ];
		if( up ){
			first = list.firstChild;
			stores.length = Math.min( data.actual, stores.length );
		}
		data.actual = up ? next : data.actual + stores.length;
		data.done = !data.actual || stores.length < maxResults;
		Grouper.store( stores, list, data.action, true );
		if( first )
			resetScroll( first.previousSibling );
		scrollLocked = false;
	});
};

function loadStoresPage(streetId){
	Cache('stores.moreMethod', false);
	var street = Cache( 'streets.last', {id:streetId} );
	StoreRegistry.getStoreViewByStreet( street, null, maxResults, 0, function( sv ){
		sv.storePosition = 0;
		cacheStoresData( sv );

		var stores = sv.stores,
			step = Math.floor(( sv.maxNumber - sv.minNumber ) / addrLen ),
			usefilter = step >= 1 && stores.length == maxResults,
			min = sv.minNumber,
			limit = false;

		if( Cache('macworld') ){
			step = 100;
			min = Math.floor((stores[6]&&stores[6].number||min)/100)*100;//hack for the index, to skip the first numbers
			limit = sv.maxNumber;
		}

		var street = stores[0].street, country = street.city.state.country.name,
			page = new Page( usefilter, '{1} {0}', 'stores', street.name );
		Cache( 'stores.lastPage', page );

		var list = DOM.ensure( page.getList(), 'ul' );
		Grouper.store( stores, list );

		persist( country, getPersistedState( street.city.state ), street.city.name, street.name );

		if( usefilter )
			Grouper.range( page.getFilter(), min, step, 'loadStoresFrom', limit );
		page.set();
	});
};
/***************** STORES-Macworld **********************/
function loadAlphaStoresFrom( letter ){
	var page = Cache('stores.lastPage'),
		list = DOM.ensure( page.getList(), 'ul' );

	StoreRegistry.getStoreViewAlphaByCityStartingWith( Cache('cities.last'), letter, maxResults, function( sv ){
		cacheAlphaData( sv );
		Grouper.store( sv.stores, list, null, null, Grouper.genericGroup, getStoreName );

		var lis = DOM.$$('li', list), i=0;
		while( lis[i] && lis[i].firstChild.childNodes[1].data < letter )
			i++;

		resetScroll( lis[i] || lis[lis.length-1] );
	});
};

function loadAlphaMoreStores( up ){
	scrollLocked = true;

	var 
		data = up ? Cache('stores.top') : Cache('stores.bottom'),
		diff = up ? -Math.min(maxResults,data.actual) : 1,
		next = data.actual + diff,

		page = Cache( 'stores.lastPage' ),
		list = DOM.first( page.getList(), 'ul' ),
		loading = DOM.create('<div id="loading">');
		
	DOM.text( loading, Lex.get('loading') );
	DOM[data.action||'append']( list, loading );

	StoreRegistry.getStoreViewAlphaByCity( Cache('cities.last'), maxResults, next, function( sv ){
		var lh = loading.clientHeight, first;
		list.removeChild( loading );
		var stores = sv.stores || [ ];
		if( up ){
			first = list.firstChild;
			stores.length = Math.min( data.actual, stores.length );
		}
		data.actual = up ? next : data.actual + stores.length;
		data.done = !data.actual || stores.length < maxResults;
		Grouper.store( stores, list, data.action, true, Grouper.genericGroup, getStoreName );
		if( first )
			resetScroll( first.previousSibling );
		scrollLocked = false;
	});
};

function loadAlphaStoresPage(){
	Cache('stores.moreMethod', loadAlphaMoreStores);
	
	var city = Cache( 'cities.last', { id:macworldID } );

	StoreRegistry.getStoreViewAlphaByCity( city, maxResults, null, function( sv ){
		cacheAlphaData( sv );

		var 
			stores = sv.stores,
			usefilter = stores.length >= maxResults,
			store = stores[0],
			street = store.street,
			city = street.city,
			country = city.state.country.name,
			page = new Page( usefilter, '{1} {0}', 'stores', city.name );
 
		Cache( 'stores.lastPage', page );

		var list = DOM.ensure( page.getList(), 'ul' );
		Grouper.store( stores, list, null, null, Grouper.genericGroup, getStoreName );
		
		persist( country, getPersistedState( city.state ), city.name, street.name );

		if( usefilter )
			Grouper.letters( page.getFilter(), 'loadAlphaStoresFrom' );
		page.set();
	});
};

/****** Store Page ************/
function loadStorePage(storeId, callback){
	Cache( 'stores.lastid', storeId );
	StoreRegistry.getOneStoreViewById( storeId, function( sv ){
		var s = sv.store, ul, li, st = s.street, c = st.city, template,
			addr = makeAddress(s), city = makeCityStateCountry(c, s.zip);

		var page = DOM.create('div');
		page.setAttribute('title',s.name);//Lex.get('store_inf'));
		page.type = page.id = 'store';

		/* pre-parsing */
		s.zip = s.zip ? s.zip.replace(/\D/g,'').replace(/(\d{5})(\d*)/,'$1-$2').replace(/-$/,'') : '' ;
		if( s.telephone )//remove non-numerical chars, and leading '1'. reformat, clean spaces and possible empty city code.
			s.telephone = s.telephone.replace(/\D/g,'').replace(/1?(\d{3})?(\d{3})(\d{4})(\d*)/g,'($1) $2-$3 $4').replace('()','').trim();
		if( s.website )
			s.website = s.website.replace('http://','');
		/* */
		
		var country = c.state.country.name;
		persist( country, getPersistedState( c.state ), c.name, st.name, s.zip  );

		/**
		 * Store Info
		 */
		addUL();
			addLI();//name of the store
				field('name','p');
		//address (street # + street name)
				if( Cache('macworld') ){
					var fl = s.rawNumber.charAt(0).toLowerCase();
					if( /[ws]/.test(fl) ){
						addUL();
							addLI();
						Cookie.set('mapFloor', fl == 's' ? '1' : '2' );
						theFloorGlobal = fl == 's' ? '1' : '2';
						field( 'booth','a', join(s.number,st.name), {href:'#'+s.number, onclick:loadMacWorld});
					}
				}else{
					addUL();
						addLI();
					field( 'address','a',addr + '<br />' + city,{href:'http://maps.google.com/maps?q='+join(addr,city).replace(/[\s,.+]+/g,'+').replace(/\+$/,''),target:'_blank'});
				}
		addUL();
			if( s.telephone ){//phone field of the store
				addLI(); field( 'phone', 'a', s.telephone,{href:'tel:'+s.telephone});
			}
			if( s.website ){//web field of the store
				addLI(); field( 'web', 'a', s.website, {href:'http://' + s.website,target:'_blank'} );
			}
			if( s.email ){//email field of the store
				addLI(); field( 'email', 'a', s.email, {href:'mailto:'+s.email});
			}
			addLI();//merge the array of keywords into a string.
				field( 'tags', 'p', List.map(s.keywords,'name').join(', ')||'<em>'+Lex.get('no_keys')+'</em>');
		/**
		 * Descriptions
		 */
		addH2(Lex.get('description'));
			addUL();
				addLI();

		li.id = 'description';
		template = '<p class="long{2}"><span class="date">{0}</span><span class="author"> {3}</span>{1}</p>';
		li.innerHTML =html(List.map( sv.descriptions.reverse(), function( d, i ){//join the descriptions, hide all but the last
			return template.format( d.tstamp.format(Lex.get('date_format')), d.description, i ? ' hidden' : '', /*user(d)*/'' );
		}).join('')	|| '<p class="long"><em>'+Lex.get('no_desc')+'</em></p>');
		links('description', sv.descriptions.length > 1, Cache('macworld') );//generate the New and All Descriptions links

		/**
		 * Messages
		 */
		addH2(Lex.get('messages'));
		addUL();
			addLI();

		sv.messageBoxes = sv.messageBoxes || [ ];
		var mess = DOM.append(li,'ul');
		mess.id = 'messages';
		li.className = 'part';//join the message, hide all but the last 2.
		template = '<li class="{3}"><h4><span class="date">{0}</span><span class="author"> {1}</span></h4> <p>{2}</p><a href="#" class="reply" onclick="loadAddMessagesPage(null,{5})">{4}</a></li>';
		mess.innerHTML = html(List.map( sv.messageBoxes.reverse(), function( m, i ){
			return template.format( m.tstamp.format(Lex.get('date_format')), FormatUtil.parseUserName(m), m.message, i<2?'':'hidden', 'Reply', m.id );
		}).join('') || '<li><h4><em>'+ Lex.get('no_mess') + '</em></h4></li>');
		links( 'messages', sv.messageBoxes.length > 2  );//generate the New and All Messages links

		/**
		 * Rating
		 */
		addH2('{0} ({1} {2})'.format(Lex.get('rating'), sv.comments.length, Lex.get('votes')));//rating title
		addUL();
			addLI();

		sv.ratings = sv.ratings || [0,0,0,0,0];//just in case

		var rating = DOM.append(li,'ul');
		rating.id = 'rating';
		template = '<li><p>{1}</p><div class="rating"><span style="width:{0}%">&nbsp;</span></div></li>';
		rating.innerHTML = List.map( sv.ratings, function(perc,i){//format the ratings
			return template.format( perc, Lex.get('rate_'+i) );
		}).join('');

		/**
		 * Comments
		 */
		addH2(Lex.get('comments'));
		addUL();
			addLI();

		var comm = DOM.append(li,'ul');
		comm.id = 'comments';
		li.className = 'part';//join the comments, hide all but the last 2.
		template = '<li class="{4}"><h4><span class="date">{0}</span>{1}<span class="author"> {2}</span></h4><p>{3}</p></li>';
		comm.innerHTML = html(List.map( sv.comments.reverse(), function( c, i ){
			return template.format( c.tstamp.format(Lex.get('date_format')), c.title, FormatUtil.parseUserName(c), c.comment, i<2?'':'hidden' );
		}).join('') || '<li><h4><em>'+ Lex.get('no_comm') + '</em></h4></li>');
		links( 'comments', sv.comments.length > 2  );//generate the New and All Comments links

		/**
		 * Finale
		 */
		if( typeof callback == 'string' ){//floor number coming from the map
			Cookie.set( 'mapFloor', callback );
			callback = null;
			if( Cookie.get('idForStore') )//remove the previous
				History.skip();
		}
		if( callback )
			callback();
		History.addPage( page );//add it to DOM and history

		/* generic link generator for New and All_X */
		function links( name, more, ms ){
			var l = li;
			l.className = 'part';
			var p = DOM.append(li,'p');
			p.className = 'long';
			p.innerHTML = '';
			if( !ms )
				p.innerHTML = '<a href="#" class="inbutton new" onclick="loadAdd{0}Page();return false">{1}</a>&nbsp;'
							.format(name.capitalize(), Lex.get('new'));
			if( more ){//some elements are hidden, generate 'All x' link
				var a = DOM.append( p, '<a href="#" class="inbutton showall">' + Lex.get('all_'+name) + '</a>');
				a.onclick = function(){
					DOM.removeClass( l,'part');
				};
			}
		};
		/* generic field generator for the store data */
		function field( name, tag, txt, attrs ){
			DOM.text( DOM.append(li,'label'), Lex.get(name) );
			var a = DOM.append(li, tag);
			a.innerHTML = txt||s[name];
			if( attrs )
				DOM.extend( a, attrs );
		};
		function addUL(){ ul = DOM.append( page, 'ul'); };
		function addLI(){ li = DOM.append( ul,'li' );   };
		function addH2(txt){ ul = DOM.text(DOM.append( page,'h2' ), txt ); };
		function join(){ return [].join.call(arguments,' '); };
		function html(s){  return s.replace(/\n/g, '<br />');};
	});
};

function loadAddStorePage(){
	antiCacheRand = Math.random();
	History.load('add-store.html?acr=' + antiCacheRand,function(){
		var form = DOM.$('addStore');
		turnSave( form );
		Validator.prepare( form, function(){
			Form.saveStore(submitMessage);
		});
		
		populate( form, 'otherCityCountry', 'country' );
		populate( form, 'otherCityState', 'state' );
		populate( form, 'otherCityCity', 'city' );
		
		populate( form, 'zip');
		populate( form, 'streetName', 'street' );
		populate( form, 'userEmail' );
		populate( form, 'userName' );
		DOM.$('cityStateCountry').onchange = function(){
			var visible = this.value == 'otherCity';
			
			DOM.toggle( 'container_country', visible );
			DOM.toggle( 'container_state', visible );
			DOM.toggle( 'container_city', visible );
		};
	});
};

function loadAddCommentsPage( id ){
	History.load('add-comment.html', function(){
		DOM.$("storeId").value = id || Cache('stores.lastid');
		var form = DOM.$('addComment');
		turnSave( form );
		Validator.prepare( form, function(){
			Form.saveComment(submitMessage);
		});
		var rating = DOM.$('rating');
		List.each( DOM.$$('div',rating), function( r ){
			if( !DOM.hasClass(r, 'rating') ) return;
			var hidden = DOM.next( r, 'input' ),
				span = DOM.first( r, 'span' );
			List.each( DOM.$$('a',r), function( b, i ){
				b.onclick = function(){
					hidden.value = i;
					span.style.width = (i+1) * 20 + '%';
				};
			});
		});
		populate( form, 'userEmail' );
		populate( form, 'userName' );
	});
};

function loadAddMessagesPage( id, replyOfId ){
	History.load('add-message.html', function(){
		DOM.$("storeId").value = id || Cache('stores.lastid');
		DOM.$('replyOfId').value = replyOfId || '';
		var form = DOM.$('addMessage');
		turnSave( form );
		Validator.prepare( form, function(){
			Form.saveMessage(submitMessage);
		});
		populate( form, 'userEmail' );
		populate( form, 'userName' );
	});
};

function loadAddDescriptionPage( id ){
	History.load('add-description.html', function(){
		DOM.$("storeId").value = id || Cache('stores.lastid');
		var form = DOM.$('addDescription');
		turnSave( form );
		Validator.prepare( form, function(){
			Form.saveDescription(submitMessage);
		});
		populate( form, 'userEmail' );
		populate( form, 'userName' );
	});
};

function revertSave(){
	var f = DOM.$('forwardButton');
	DOM.text( f, Lex.get('add_listing'), true );
	f.onclick  = loadAddStorePage;
};

function turnSave( form ){
	var f = DOM.$('forwardButton');
	DOM.text( f, Lex.get('save'), true );
	f.onclick = function(e){
		form.onsubmit();
	};
};
function loadAboutPage(){
	History.load('about.html');
};

function loadSettingsPage(){
	History.load('settings.html');
};

function loadStatsPage(){
	History.load('stats.html');
};
function loadMacWorld(){
	theFloor = theFloorGlobal;

	var id = this.hash && this.hash.slice(1) || '',
		floor = Cookie.get('mapFloor'),
		name = 'macworld-'+theFloor;

	Cookie.set( 'idForStore', id );
	window.open( name + (Cache('map.html.'+theFloor)? '-cached' :'' )+ '.html', name );
};


/************** Messages and Reviews ****************/

function loadReviewsPageByCity(cityId){
	CommentRegistry.getEnabledCommentsByCity( {id:cityId}, maxResults, null, function(cmts){
		commonItemsHandler(cmts, 'city', 'reviews');
	});
};

function loadReviewsPageByStreet(streetId){
	CommentRegistry.getEnabledCommentsByStreet( {id:streetId}, maxResults, null, function(cmts){
		commonItemsHandler(cmts, 'street', 'reviews');
	});
};

function loadMessagesPageByCity(cityId){
	MessageBoxRegistry.getNonExpiredMessageBoxesByCity( {id:cityId}, maxResults, null, function(msgs){
		commonItemsHandler(msgs, 'city', 'messages');
	});
};

function loadMessagesPageByStreet(streetId){
	MessageBoxRegistry.getNonExpiredMessageBoxesByStreet( {id:streetId}, maxResults, null, function(msgs){
		commonItemsHandler(msgs, 'street', 'messages');
	});
};

function loadDescriptionsPageByCity(cityId){
	DescriptionRegistry.getEnabledDescriptionsByCity( {id:cityId}, maxResults, null, function(descs){
		commonItemsHandler(descs, 'city', 'descriptions');
	});
};

function loadDescriptionsPageByStreet(streetId){
	DescriptionRegistry.getEnabledDescriptionsByStreet( {id:streetId}, maxResults, null, function(descs){
		commonItemsHandler(descs, 'street', 'descriptions');
	});
};

/**
 * @param items
 * 		The items collection
 * @param from
 * 		Defines if it comes from a city or a street
 * @param type
 * 		The type of item to be parsed (review or message)
 */
function commonItemsHandler(items, from, type){
	/* for the Analytics to know what to skip */
	level = from;
	extra = type;
	/* */
	
	var street = items[0].store.street,
		itm = from == "city" ? street.city: street,
		page = new Page( false, '{1} {0}', type, itm.name ),
		list = DOM.ensure( page.getList(), 'ul' );	

	Grouper[type]( items, list );
	page.set();

	// The following set of hacks are needed to make the pages look good
	// This should be done with CSS rules in some way

	DOM.$("lists").id = "buttonPage";
	DOM.$$("html")[0].setAttribute("style", "height:100%");
	DOM.$("contentHolder").setAttribute("style", "height:100%");
	DOM.$("wrapper").setAttribute("style", "height:100%");
	DOM.$("buttonPage").setAttribute("style", "height:100%");
	DOM.$("store").setAttribute("style", "height: 100%");
};