/* [file src="Portal.js"]
*/
/*	usemedia.com . joes koppers . 11.2007
	thnx for reading this code */


/*	7s Main app
*/

function Portal7(dev,lan,link)
{
	/*	bootstrap
	*/
	var obj = this;
	
	//wait anim preload
	var img = new Image(16,16); img.src='media/logging_in.gif';
	
	//defaults
	this.name = '7scenes';
	this.version = '1.0';
	this.language = lan;
	this.mode = (dev)? 'test':'production';
	this.infosite = 'info.'; //vhost for drupal
	
	this.disable_live_polling = true;		//do not poll for live scenes (development feature)
	this.scene_activity_check = dev? 0:30;	//seconds, polling interval for scene activity
	this.scene_traces_check = dev? 60:20;	//seconds, polling interval for live traces

	this.state = 'portal'; //current state of interface, possible values: portal, scene (playback, play), direct, directplaces
	this.login = { 
		autologin:false,
		set_autologin:true,
		roles: { 
			admin:new Object(),
			director:new Object()
		}
	};

	//check autologin cookie	
	var autologin = this.autoLogin();
	
	// deeplinking to project, user, scene?
	var callback;
	if (link)
	{
		p = link.split('/');
		var type = p.length>1 ? p[0]:'project';
		var id = p.length>1 ? p[1]:p[0];
		this.link = { type:type, id:id, path:link };
		
		dbg.msg('link=',dbg.obj(this.link));
		
//		var callback = function(type) { obj.deepLink(type) }
	}
	
 	//template defaults //->REFACTOR, get from db
	this.template = new Object();
	this.template.placetypes = ['task','text','image','audio','video','reward','trade'];
	this.template.placeNames = { task:'task', text:'note', image:'photo', audio:'sound', video:'video', reward:'reward', trade:'trade' };
	
	this.template.placeNames['task-multichoice'] = 'quiz task';
	this.template.placeNames['task-photosubmit'] = 'photo task';
		
	this.template.gameplay = ['roles','skills','assignments','objects'];
		this.template.roles = ['role1','role2','role3','role4'];
		this.template.skills = ['skill1','skill2','skill3','skill4'];
		this.template.assignments = ['assignment1','assignment2','assignment3','assignment4'];
		this.template.objects = ['object1','object2','object3','object4'];
	this.template.maxtime = [15,30,45,60,75,90,120,150,180];
	this.template.skillvalues = [10,15,20,25,50,75,100,250];
	this.template.task_scores = [0,10,20,30,40,50,100,150,200,250];
	this.template.teamcolors = ['8b0000','4169E1','006400','DAA520','DA70D6','008B8B'];
	
	//states
	this.scenestates = { 1:'draft', 2:'public', 3:'closed' };
	this.eventstates = { 1:'scheduled', 2:'running', 3:'done' };
	
	//initialize GWorx
	GW.init(function(id,error,details,tagname) { obj.gwNegrsp(id,error,details,tagname) },30,'/gstudio');
	
	this.path = '/gstudio/';
	
	/*	collections
	*/
	this.lists = new Object(); //collection of lists
	//projects
	this.projects = new Projects7(this);		//base collection (no list)
	this.portalprojects = new Projects7(this);	//listed projects in portal
	//scenes 
	this.scenes = new Scenes7(this);		//base collection (no list)
	this.portalscenes = new Scenes7(this); 	//listed scenes in portal
	this.projectscenes = new Scenes7(this); //listed scenes in project

	//GUI
	this.initGui(callback);
		
	//login? (based on cookie)
	if (this.login.autologin) this.doLogin('auto');
	
	//show info pane?
	if (this.layout.dashboard.autoShow()) window.setTimeout(function() { obj.layout.dashboard.show() },1400);
}

Portal7.prototype.initGui = function(callback)
{
	/*	init layout and gui, follow deeplink if any
	*/
	var obj = this;
	
	this.addLayout();
	this.addGui();

	//build main layout
	this.layout.createBaseLayers();
	this.layout.createMenuLayers();
	this.layout.addDashboard();	
	
	this.gui.createCommonPanes();
	
	/*	get scenetypes, deeplink, scenes, projects
	*/
	this.getSceneTypes(undefined,function() {
	
		//deeplink
		if (obj.link) obj.deepLink(obj.link.type);
		
		//portal listings //-> REFACTOR
		obj.getInitialScenes();
		obj.getInitialProjects();
	});
}


/*	scenes and projects
*/

Portal7.prototype.getSceneTypes = function(records,callback)
{
	if (!records)
	{
		var obj = this;
		GW.QUERY.queryStore('q-scene-types-info',function(rsp) { obj.getSceneTypes(rsp,callback) } );
		return;
	}
	
	//parse records into scenetypes object
	this.scenetypes = new Object();
	for (var i=0; i<records.length; i++)
	{
		this.scenetypes[ records[i].getField('type') ] = { name:records[i].getField('name') }
	}
	
	if (callback) callback();
}

Portal7.prototype.getInitialScenes = function(callback)
{
	/*	get selected scenes,
		all for now, later: top 50?, most popular?, recently added?, ... */
		
	var query = { max:50, minstate:2 };
	//if (this.group) query.group = this.group;
	var list = this.lists['portal'];
	this.getScenes(query,list,callback);
}

Portal7.prototype.getInitialProjects = function(callback)
{
	//->for now, change later!
	this.getProjects(undefined,undefined,callback);
}

Portal7.prototype.deepLink = function(type,sceneid)
{
	/*	open project, scene, trace or event, 
		based on friendly url */
		
		
	dbg.msg('<span style="color:#006699">Portal: deepLink, type=',type);	
	
	var obj = this;
	switch (type)
	{
		case 'project':
			//find project in collection
			var project = this.projects.getProjectByName(this.link.id);

 			if (!project)
 			{
 				/*	wait for initial projects to be loaded //-> REFACTOR when portalprojects list is used (like portalscenes), future release
 				*/
 				window.setTimeout(function() { obj.deepLink(type,sceneid) },100);
 				return;
 			}
// 			{
// 				//get and add project first;
// 				var callback = function(type) { obj.deepLink(type) };
// 				GW.QUERY.queryStore('q-project-info', function(rsp) { obj.addProjects(undefined,callback,rsp) },{ name:this.link.id });
// 				return;
// 			}
			
			//open project
			this.layout.showLayers(['project']);
			
			if (this.login.autologin && !this.login.id) this.projects.select(project.id); //just select, autologin will re-open any selected project
			else this.openProject(project.id);
			
			break;
			
		case 'scene':
		
			var id = sceneid || this.link.id;
 			var action = this.portalscenes[id]? 'open':'get';
 			this[action+'Scene'](id);

//			this.getScene(sceneid || this.link.id);

// 				if (this.login.autologin && !this.login.id) //-> CHECK if required
// 				{
// 					//autologin will re-open a selected scene
// 					this.portalscenes.select(id); 
// 				}

			break;
		
		case 'event':
		case 'trace':
			//get scene id for this trace/event
			var callback = function(type) { obj.deepLink(type) };
			var q = type=='trace'? 'q-trace':'q-round';
			
			GW.QUERY.queryStore(q,function(records) {
				if (records.length>0)
				{
					obj.deepLink('scene',records[0].getField('sceneid'))
				}
			},{ id:this.link.id });
			break;
	}
}




Portal7.prototype.getScene = function(id,records)
{
	/*	get scene (by id)
	*/
	if (!records)
	{
		var obj = this;
		var query = { sceneid:id };
		dbg.tmr();
		GW.QUERY.queryStore('q-scenes', function(rsp) { obj.getScene(id,rsp) },query);
		return;
	}

	if (records.length==0) return;

	dbg.msg('Portal: getScene, id=',id);
	
	this.foundScene(records[0]);	
}

Portal7.prototype.getScenes = function(query,list,callback,records)
{
	/*	get scenes (by query object)
	*/
	if (!records)
	{
		var obj = this;
		dbg.tmr();
		query = query || null;
		GW.QUERY.queryStore('q-scenes', function(rsp) { obj.getScenes(query,list,callback,rsp) },query);
		return;
	}
	
	dbg.msg('Portal: getScenes, records=',records.length,'tmr');
	
	this.addScenes(list,callback,records);
}

Portal7.prototype.addScenes = function(list,callback,records)
{
	dbg.msg('Portal: add scenes to list id=',list.id,', records=',records.length);
	//dbg.msg('list = ',dbg.obj(list));
	
	//clear collection first...
	//for (var id in list.scenes) list.scenes.dispose(id);
	list.scenes.clear();
	
	
	if (list.div.lastChild.className!='list-navigation-button') list.div.removeChild(list.div.lastChild);
	
	
	if (records.length==0)
	{
		var div = document.createElement('div');
			div.innerHTML = Locale7.get('gui','noscenes');
			div.style.width = '100%';
			div.style.textAlign = 'center';
		list.div.appendChild(div);
	}
	
	
	
	//list.scenes.count = 0;

	//var fields = ['name','type','image','description','state','user','admin','eventcount','creationdate','modificationdate'];

	for (var n=0; n<records.length; n++)
	{
		var rec = records[n];
		var id = rec.getField('id');
		
		list.scenes.create( {
			id:id,
			index:n,
			
			name:rec.getField('name'),
			type:rec.getField('type'),
			icon:rec.getField('sceneiconid')? {
				id:rec.getField('sceneiconid'),
				scale:rec.getField('scale'),
				offsetx:rec.getField('offsetx'),
				offsety:rec.getField('offsety')
			}:undefined,
			description:rec.getField('description'),
			state:rec.getField('state'),
			
			user:rec.getField('user'),
			admin:rec.getField('admin'),
			
			eventcount:rec.getField('eventcount'),
			creationdate:rec.getField('creationdate'),
			modificationdate:rec.getField('modificationdate')
		}, list );
	}

	//update display
	list.scenes.list();
	
	if (callback) callback('scene');
}

Portal7.prototype.getProjects = function(query,list,callback,records)
{
	/*	get projects (by query object)
	*/
	if (!records)
	{
		var obj = this;
		dbg.tmr();
		query = query || null;
		
		//query = { max:0 };//test
		
		GW.QUERY.queryStore('q-projects', function(rsp) { obj.getProjects(query,list,callback,rsp) },query);
		return;
	}

	dbg.msg('Portal: getProjects, records=',records.length,'tmr');
	
	this.addProjects(list,callback,records);
}

Portal7.prototype.addProjects = function(list,callback,records)
{
	dbg.msg('Portal: add projects, n=',records.length);
	
	if (this.link && this.link.type=='project' && records.length==0) return; //invalid deeplink!
	
	//clear collection first...
	//for (var id in this.projects) this.projects.dispose(id);
	this.projects.clear();

	var str = ''; //tmp listing
	
	for (var n=0; n<records.length; n++)
	{
		var rec = records[n];
		var id = rec.getField('id');
	
		this.projects.create( {
			id:id,
			index:n,
			name:rec.getField('name'),
			description:rec.getField('description')
		} );
		
		
		str+= '<a href="javascript://project" onclick="portal.openProject('+id+')">'+rec.getField('name')+'</a><br>';
	}
	
	//just write test list for now, use this.projects.list() later
	this.layout.layers['portal'].panels['projects'].setContent(str);
	
	if (callback) callback('project');
}


Portal7.prototype.openScene = function(id)
{
	/*	open a scene (for playback or play)
	*/

	dbg.msg('<span style="color:#999966">portal.openScene(',id,')</span>');
	
// 	if (this.scenes[id]) this.scenes[id].open();
// 	else 
	if (this.portalscenes[id]) this.portalscenes[id].open();
}

Portal7.prototype.directScene = function(id)
{
	/*	open a scene for editing
	*/
	
	dbg.msg('<span style="color:#999966">portal.directScene(',id,')</span>');

	//add later
	//..
}

Portal7.prototype.openProject = function(id)
{
	/*	open a project page
	*/
	
	dbg.msg('<span style="color:#999966">Portal: openProject(',id,') current=',this.project? this.project.id:'none','</span>');

	//close current scene
	if (this.scene) this.scene.close();
	
	if (this.project && this.project.id==id)
	{
		//project is open, just show layer
		this.layout.showLayers(['project']);
		return;
	}
	
	if (!id) 
	{
		//re-open current project
		var id = this.project.id;
		this.project.close(true);
	}

	this.projects[id].open();
}

/*	scenes search
*/

Portal7.prototype.find = function(query,close)
{
	/*	open search layer, enable search gui
	*/
		
	$j(this.layout.layers['portal'].menubar['find'].div).find('a')
		.css('background-image',close? 'url(media/menu/find.png)':'url(media/menu/find_x.png)');
	
	this.layout[close? 'hideLayers':'showLayers'](['find']);
	if (close) return;

	//hide profile, myfind
	this.layout.hideLayers(['profile']);
	if (this.myFind) this.myFind(undefined,true);
	
	//put search layer inline with menu
	var menu = this.layout.layers['portal'].menubar['find'];
	var w = this.layout.w - $j(menu.div).offset().left;
	var layer = this.layout.layers['find'];
		layer.setSize(w);
	
	//context search?
	if (this.project)
	{
		var name = this.project.name;
		if (name.length>15) name = name.substring(0,15)+'..';
		document.getElementById('search-context').innerHTML = name;
	}
	
	document.getElementById('search-context-toggle').style.display = this.project? 'block':'none';
		
	//layer.show();
	
	var form = document.forms['searchform'];
		form.query.focus();

	//event handling
	var obj = this;
	var ignoreKeys = { 9:'tab',16:'shift',17:'ctrl',18:'alt',224:'apple',37:'arrow',38:'arrow',39:'arrow',40:'arrow' };
	form.query.onkeydown = function(e)
	{
		/*	execute search at small delay if input is more than 1 chars
		*/
		if (!e) e = event;
 		if (obj.searching) window.clearTimeout(obj.searching);
		if (ignoreKeys[e.keyCode]) return e.returnValue;
		else
		{
	 		obj.searching = window.setTimeout(function() { obj.query(undefined,form.query.value) },500);
			return e.returnValue;
		}
	}
	form.context.onclick = function()
	{
		obj.query(undefined,form.query.value);
	}

	//auto query
	if (query)
	{
		form.query.value = query;
		this.query(undefined,query);
	}
}

Portal7.prototype.query = function(records,q,my)
{
	/*	perform search query
	*/

	var obj = this;

	if (!records)
	{
		/*	reset
		*/
		if (q!=undefined && q.length<2)
		{
			if (my && q.length==0)
			{
				//my: default all scenes (max 100)
				this.gui.updateSearchResult('searching',undefined,my);
				var query = { max:101, user:this.login.loginname };
				dbg.tmr();
				GW.QUERY.queryStore('q-scenes', function(rsp) { obj.query(rsp,undefined,my) },query);
			}
			else
			{
				//default: clear list
				this.gui.updateSearchResult('clear',undefined,my);
			}
			return;
		}
		
		/*	query
		*/
		dbg.msg('Portal: execute query, q=',q,my? ', [my]':'');
		this.gui.updateSearchResult('searching',undefined,my);
		
		
		var query = { max:101, minstate:2, search:escape(q) };
		if (my)
		{
			query.user = this.login.loginname;
			delete query.minstate;
		}
		else if (this.project && document.forms['searchform'].context.checked)
		{
			query.contextid = this.project.id;
			if (this.project.admin) query.minstate = 1; //project admin can find all scenes within context
		}
	
		dbg.tmr();
		GW.QUERY.queryStore('q-scenes', function(rsp) { obj.query(rsp,undefined,my) },query);
		return;
	}
	
	dbg.msg('Portal: query, records=',records.length,'tmr');

	/*	store and display result
	*/
	this[my? 'myfoundscenes':'foundscenes'] = records;
	this.gui.updateSearchResult(records,undefined,my);
}

Portal7.prototype.foundScene = function(record,my)
{
	/*	open a scene from record or query result list
	*/
	
	dbg.msg('Portal: foundScene',typeof(record)=='number'? ', list index='+record:'',my? ', my=true':'');
	
	if (this.scene) this.scene.close();
	this.scenes.clear();

	var rec = typeof(record)=='number'? this[my? 'myfoundscenes':'foundscenes'][record]:record;
	var id = rec.getField('id');
	
	//check access rights, draft scenes can only be viewed by owner or admin
	var state = rec.getField('state');
	var admin = rec.getField('admin');
	var user = rec.getField('user');
	var enabled = (user==this.login.loginname || admin==this.login.loginname || this.login.role==0);
	if (state<2 && !enabled)
	{
		dbg.msg('Portal: foundScene, draft scene view not allowed');
		return;
	}
		
	this.scenes.create( {
		id:id,
	
		name:rec.getField('name'),
		type:rec.getField('type'),
		state:state,

		image:rec.getField('image'), //backwards compatibilty scene icons
		icon:rec.getField('sceneiconid')? {
			id:rec.getField('sceneiconid'),
			scale:rec.getField('scale'),
			offsetx:rec.getField('offsetx'),
			offsety:rec.getField('offsety')
		}:undefined,
		
		description:rec.getField('description'),
		
		user:user,
		admin:admin,
		
		eventcount:rec.getField('eventcount'),
		creationdate:rec.getField('creationdate'),
		modificationdate:rec.getField('modificationdate')
	} );
	
	this.scenes[id].enable();
	this.scenes[id].open();
}

Portal7.prototype.foundProject = function(record)
{
	/*	open a project from records
	*/



}

/*	general features
*/

Portal7.prototype.setLanguage = function()
{
	/*	language switch gui, direct toggle for now
	*/
	dbg.msg('Portal: setLanguage, current=',this.language);
	
	var path = this.link? this.link.path:'';
	var lan = this.language=='en'? 'nl/':'';
	
	//store language pref in cookie
	var days = 14; //cookie expires 2 weeks from now
	var value = this.language=='en'? 'nl':'en';
	var d = new Date();
	d.setTime(d.getTime()+(days*24*60*60*1000));
	var expires = '; expires='+d.toGMTString();
	document.cookie = 'language='+value+expires+'; path=/';
		
	
	//reload entire interface (!)
	window.location = '/'+lan+path;
}



Portal7.prototype.help = function(record,r)
{
	

	if (typeof(record)!='object')
	{
		dbg.msg('Portal: help(',record,')')

	
		//get help text
		var obj = this;
		GW.QUERY.queryStore('q-text-by-name',function(rsp) { obj.help(rsp,record) },'name',record+'_help','lang',this.language.toUpperCase());
		return;
	}
	
	//set help pane contents and show
	if (record[0]) var help = record[0].getField('content');
	else var help = 'help file missing ('+r+'_help, language:'+this.language+')';
	this.layout.panes['help'].setContent(help);
	this.layout.panes.show('help');
}

Portal7.prototype.signup = function()
{
	/*	register a new user
	*/
	//this.layout.layers['signup'].show();

	alert('sorry!\n\nsignup is not available yet');
	
	//todo
	//..
}

Portal7.prototype.about = function()
{
	/*	display welcome/info layer (slide-in)
	*/
	
	dbg.msg('Portal: about..');
}

Portal7.prototype.go = function(path)
{
	/*	jump to info (drupal) site
	*/
	dbg.msg('Portal: go(',path,')');
	var locale = this.language!='en'? '/'+this.language:'';
	var host = window.location.host.replace('live.','');
	
	//window.location = 'http://'+this.infosite+window.location.host+locale+'/gwsession/'+GW.agentKey+'/'+path;
	window.location = 'http://'+host+locale+'/gwsession/'+GW.agentKey+'/'+path;
}

Portal7.prototype.embed = function(id)
{
	var type = id? 'trace':'scene';
	var lan = this.language=='nl'? '/nl':'';
	var str = '<iframe src="http://'+window.location.host+lan+'/widget/'+type+'/'+(id || this.scene.id)+'" width="240" height="295" frameborder="0"></iframe>';
	
	//->disabled
	//var str = Locale7.get('gui','n_a'); 

	document.forms['shareform'].embed.value = str;
}

/*	authentication
*/

Portal7.prototype.autoLogin = function()
{
	var autologin = utils.readCookie('autologin');

	dbg.msg('autologin = ',autologin);

	if (autologin && autologin!='false')
	{
		var data = utils.encrypt(autologin,true).split(',');
		this.login.loginname = data[0];
		this.login.password = data[1];
		this.login.language = data[2] || 'en';
		this.login.autologin = true;
		//this.doLogin('auto');
		//return true;
	}
}

Portal7.prototype.setAutoLogin = function(set)
{
	if (set=='toggle')
	{
		this.login.set_autologin = !this.login.set_autologin;
		dbg.msg('Portal: autologin toggle, new state=',this.login.set_autologin);
		//focus login field
		document.forms['loginform'].login.focus();
	}
	else
	{
		dbg.msg('set autologin=',set);
	
		//(re)set cookie
		if (!set) this.login.autologin = false;
		var days = (!set)? -1:14; //cookie expires 2 weeks from now
		var value = (!set)? false:utils.encrypt(this.login.loginname+','+this.login.password);	
		var d = new Date();
		d.setTime(d.getTime()+(days*24*60*60*1000));
		var expires = '; expires='+d.toGMTString();
		document.cookie = 'autologin='+value+expires+'; path=/';
	}
}

Portal7.prototype.doLogin = function(action)
{
	dbg.msg('doLogin:',action);

	if (action=='logout')
	{
		this.logout();
		return false;
	}

	//get form values
	if (action!='auto')
	{
		this.login.loginname = document.forms['loginform'].login.value;
		this.login.password = document.forms['loginform'].password.value;
	}
	
	//validate
	if (this.login.loginname=='' || this.login.password=='') 
	{
		alert('please enter both username and password');
		return false;
	}

	//display wait anim
	this.layout.layers['portal'].menubar['login'].setContent(this.gui.create('logging_in'));
	
	//send login request
	var obj = this;
	GW.login(this,this.login.loginname,this.login.password);
	
	return false; //prevent form from submitting
}

Portal7.prototype.loginRsp = function(rsp)
{
	this.login.agentkey = rsp.getAttribute('agentkey');
	this.login.userid  = rsp.getAttribute('userid');
	this.login.role = rsp.getAttribute('role');
	
	dbg.msg('login succes: id=',this.login.userid,', name=',this.login.loginname,', key=',this.login.agentkey);
	
	if (!this.login.autologin && this.login.set_autologin) this.setAutoLogin(true);
	
	/*	we are now logged in
	*/

	dbg.msg('* we are now logged in *');
	
	//show login info for test version
	//if (this.mode=='test') document.getElementById('dev_login_info').innerHTML = 'you are: <b>'+this.login.loginname+' ('+this.login.id+')<b>';

	this.loadUserScripts(1);
}

Portal7.prototype.loginNegRsp = function(errorId,error,details,tagname)
{
	dbg.msg('login error: id=',errorId,', err=',error,', details=',details);

	switch (errorId)
	{
		case '4100':
		case '4101':
		default:
			alert('Error logging in:\n - '+details+'\n\nplease try again..');
			delete this.login.id;
			delete this.login.loginname;
			delete this.login.password;
			break;
	}
	
	//reset login form
	var obj = this.layout.layers['portal'].menubar['login'];
	var delayed = function() { obj.expand(1) };
	
	window.setTimeout(delayed,200);
}

Portal7.prototype.logout = function()
{
	dbg.msg('logging out..');
	
	if (this.scene) 
	{
		var id = this.scene.id;
		if (!this.scene.close(false,true)) return;
	}
	
	GW.logout();
	
	delete this.login;
	this.login = { autologin:false, set_autologin:true, roles: { admin:new Object(), director:new Object() } };

	this.setAutoLogin(false);
	
	if (this.mode!='test')
	{
		this.layout.dashboard.toggleShow(false); //auto show dashboard at next visit
		utils.over(this.layout.dashboard.toggleshow.nextSibling,false);
	}
	
	/*	we are now logged out
	*/

	this.setUserEnabled(false);
}

Portal7.prototype.lostPassword = function()
{
	/*	retrieve lost password dialogue
	*/
	portal.go('gworx/user/reset');
}

Portal7.prototype.loadUserScripts = function(init)
{
	/*	load registered user scripts
	*/
	
// 	var scripts = [
// 		{ id:'Portal_extend', src:'Portal.extend.js' },
// 		{ id:'Portal_Gui_extend', src:'Portal.Gui.extend.js' },
// 		{ id:'Portal_Direct', src:'Portal.Direct.js' },
// 		{ id:'Scenes_edit', src:'Scenes.edit.js' },
// 		{ id:'Scenes_play', src:'Scenes.play.js' }
// 	];
	var minify = this.mode=='production'? 'minify=true&':'';
	var scripts = [
		{ id:'userscripts', src:'script.php?'+minify+'scripts=Portal.extend.js,Portal.Gui.extend.js,Portal.Direct.js,Scenes.edit.js,Scenes.play.js,tiny_mce/jquery.tinymce.js&' }
	];
		
//	var scripts = ['script.php?scripts=Portal.extend.js,Portal.Gui.extend.js,Portal.Direct.js,Scenes.edit.js,Scenes.play.js&'];
	if (browser.cssfilter) scripts[0].src += 'callback=userscripts&';

	if (init)
	{
		//dbg.tmr();
		this.scripts_loaded = 0;
		var obj = this;
		
		for (var i=0; i<scripts.length; i++)
		{
			utils.loadScript(scripts[i].src,scripts[i].id,function(id) { dbg.msg('[ script loaded, id=',id,' ]'); obj.scripts_loaded++; obj.loadUserScripts() } )
		}
	}
	else
	{
		//continue if all scripts are loaded
		if (this.scripts_loaded==scripts.length)
		{
			delete this.scripts_loaded;
			dbg.msg('* all private scripts loaded','tmr');
			this.setUserEnabled(true);
		}
	}
}

Portal7.prototype.setUserEnabled = function(enable)
{
	dbg.msg('setUserEnabled:',enable);
	
	this.find(undefined,true);
	if (this.myFind) this.myFind(undefined,true);
	
	//disable/enable registered user functions
	var layer = this.layout.layers['portal'];
		layer.menubar['direct'].setEnabled(enable);
		layer.menubar['my'].setEnabled(enable);
		
	this.layout.layers['scene'].menubar['play'].setEnabled(enable);
	$j('#layer-widget-add-comment')[enable? 'show':'hide']();
	
	if (enable)
	{
		this.login.id = this.login.userid; //user login completed..
	
		/*	enable registered user functionality
		*/
		this.extendGui();

		//collapse login menu (displays logout and profile img)
		layer.menubar['login'].expand(false);

		//profile and roles
		this.addProfile();
		this.getRoles();
	
		//highlight scenes for by this user
		this.portalscenes.enable();
// 		var obj = this;
// 		window.setTimout(function() {
// 			obj.portalscenes.enable();
// 		},5000);

		//re-open scene
		//if (this.scene) this.scene.close(true);
// 		if (this.portalscenes.scene && !this.project) 
// 		{
// 			var id = this.scene.id;
// 			this.scene.close(true);
// 			this.openScene(id);
// 		}
	
		this.loginAction();
		//this.layout.layers['portal'].showMenus(['myscenes']);
	}
	else
	{
		/*	logged out, disable all user functionality
		*/
		this.portalscenes.enable(false);
		
		//remove user layers
		this.layout.layers.dispose('myfind');
		this.layout.layers.dispose('direct');
		//this.layout.layers.dispose('play');
		this.layout.layers.dispose('profile');
		this.layout.layers.dispose('adminsignup');
		
		
		//remove manage (admin) button
		var layer = this.layout.layers['portal']
		if (layer.menubar['manage']) layer.removeMenu('manage');
		
		//reset search
		this.layout.hideLayers(['search']);
		this.gui.updateSearchResult('reset');
		
		//remove myscenes.. (?)
		delete this.lists['myscenes'];
		
		delete this.direct;
		
		//unload scripts -> ??
		//utils.unloadScript('Scenes.private.js');
		
		//unload extended GUI
		for (var id in this.gui) if (typeof(this.gui[id])=='function') delete this.gui[id];
		
		//unload registered user functionality
		delete Portal7.prototype.extendGui;
		delete Portal7.prototype.addDirect;
		
		delete Scenes7.prototype.enableEditing;
		delete Scenes7.prototype.enablePublishing;
		delete Scenes7.prototype.enablePlay;

		if (this.medialib)
		{
			this.medialib.dispose();
			delete this.medialib;
			delete Portal7.prototype.addMedialib;
		}

		//show (and reset) signup button
// 		var menu = this.layout.layers['portal'].menubar['signup'];
// 		this.gui.shear(menu.bg,menu.w,menu.h,'#73734d','#666644'); //color2:'#666644', color:'#73734d'
// 		menu.show();
// 		var obj = this;
// 		menu.onclick = function() { obj.signup() }

		//collapse login menu (display login button)
		this.layout.layers['portal'].menubar['login'].expand(false);
		this.layout.layers['portal'].hideMenus(['myscenes']);

		//close and reopen project, if any
		if (this.project) this.openProject();
	}
}


/*	GWorx 
*/

Portal7.prototype.gwNegrsp = function(errorId,error,details)
{
	dbg.msg('gwNegrsp: errorid=',errorId,', error=',error,', details=',details);
	
	if (errorId=='6007')
	{
		//insufficient rights, generic alert
		alert('Error!\n\nthis action is not allowed for your login');
	}
}


/*	utilities
*/

Portal7.prototype.debug = function(something)
{
	switch (something)
	{
		case 'scenelist':
			var str = 'scenelist:';
			for (var id in this.scenes)
			{
				if (typeof(this.scenes[id])!='object') continue;
				str+= '<br>id='+id+', index='+this.scenes[id].index+', enabled='+this.scenes[id].enabled;
			}
			dbg.msg(str);
			break;
			
		case 'list':
			var str = 'list elements:'
			var elms = this.layout.list.pages.array[0].content.childNodes;
			for (var i=0; i<elms.length; i++)
			{
				str+= '<br>'+i+'- offsetTop='+elms[i].offsetTop;
			}
			dbg.msg(str);
			break;
			
		case 'scene':
			dbg.msg(dbg.obj(this.scene));
			break;
	}
}

Portal7.prototype.stripTags = function(str)
{
	/*	(basic) html tag removal for strings
	*/
	return str.replace(/<\/?[^>]+>/gi,'');
}

Portal7.prototype.escapeHTML = function(str)
{
	if (browser.safari)
	{
		//innerHTML method below does not seem to work properly in Safari, use string conversion instead.. 
		return str.replace(/(>)/g,'&gt;').replace(/(<)/g,'&lt;').replace(/(&)/g,'&amp;');
	}

	var div = document.createElement('div');
		div.appendChild(document.createTextNode(str));

	return div.innerHTML;
}

Portal7.prototype.unescapeHTML = function(str)
{
	var div = document.createElement('div');
		div.innerHTML = str;

	return div.firstChild? div.firstChild.nodeValue:str; //innerHTML;
}

Portal7.prototype.parseXML = function(elm)
{
	/*	parse XML element to JSON
		conventions:	arrays are defined as <elmlist><elm></elm></elmlist>  */
		
	if (!elm || !elm.attributes) return;

	if (elm.tagName && elm.tagName.indexOf('list')!=-1)
	{
		/*	list
		*/
		var elms = elm.getElementsByTagName(elm.tagName.substring(0,elm.tagName.indexOf('list')));

		var list = new Array();
		for (var i=0; i<elms.length; i++)
		{
			list.push(this.parseXML(elms[i]));
		}
		return list;
	}
	else if (elm.childNodes.length==1 && elm.firstChild.nodeType==3)
	{
		/*	element with text value
		*/
		if (elm.firstChild.nodeValue=='true') return true;
		if (elm.firstChild.nodeValue=='false') return false;
		return elm.firstChild.nodeValue;
	}
	else if (elm.childNodes.length==0 && elm.attributes.length==0)
	{
		/*	empty element
		*/
		return null;
	}
	else
	{
		/*	attributes and/or nested elements, recursive parsing
		*/
		var object = new Object();

		for (var i=0; i<elm.attributes.length; i++)
		{
			object[elm.attributes.item(i).name] = elm.attributes.item(i).value;
		}

		for (var i=0; i<elm.childNodes.length; i++)
		{
			object[elm.childNodes[i].tagName] = this.parseXML(elm.childNodes[i]);
		}

		return object;
	}
}

Portal7.prototype.getElementValue = function(parent,tagname)
{
	/*	return XML node value by tagname from parent elm, if exists
	*/
	function getElement(tagname)
	{
		return parent.getElementsByTagName(tagname)[0] || false;
	}

	if (getElement(tagname) && getElement(tagname).firstChild)
	{
		return getElement(tagname).firstChild.nodeValue;
	}
	else
	{
		return false;
	}
}

/* [file src="Scenes.js"]
*/
/*	usemedia.com . joes koppers . 01.2008 [rev 09.2008]
	thnx for reading this code */


/*	7s Scenes
*/

function Scenes7(portal,id)
{
	/*	a scenes collection
	*/
	
	this.count = 0;
	
	this.create = function(properties,list)
	{
		if (!properties.id) return;
		
		var scene = new Scene(this,properties,list);
		//add to collection
		this[properties.id] = scene;
		this.count++;
		
		return scene;
	}

	this.select = function(id)
	{
		/*	select current scene
		*/

		dbg.msg('Scenes: ',id? ' select('+id+')':'unselect',this.getList? ', list='+this.getList().id:'');

		if (!id)
		{
			if (this.scene) this.scene.expanded = false;
			delete portal.scene;
			delete this.scene;
		}
		else
		{
			portal.scene = this[id];
			this.scene = this[id];
		}
	}
	
	this.dispose = function(id)
	{
		/*	remove a scene obj
		*/
		if (typeof(this[id])!='object') return;
		
		//dbg.msg('dispose sceneobj, id=',id);
		
		var deleted_index = this[id].index;
		delete this[id];
		this.count--;
		
		//update indexes..
		for (var id in this)
		{
			if (typeof(this[id])!='object') continue;
			if (this[id].index>deleted_index) this[id].index--;
		}
	}

	this.clear = function()
	{
		/*	remove all objects from collection
		*/
		for (var id in this)
		{
			if (typeof(this[id])=='object') delete this[id];
		}
		this.count = 0;
	}
	
	this.clone = function(scene,index)
	{
		/*	copy all properties from scene obj to a new object in this collection, insert at index (optional)
		*/
		var id = scene.id;
		var index = index!=undefined ? index:0;
		
		//new scene object
		this.create( { id:id, index:index } );
	
		//copy properties
		for (var p in scene)
		{
			if (typeof(scene[p])!='function' && p!='collection' && p!='list' && p!='listitem') this[id][p] = scene[p];
		}
		
		//list and list item
		if (this.getList)
		{
 			var obj = this[id];
			var list = this.getList();

			this[id].list = list;
 			this[id].listaction = list.action || function() { obj.open() }
 			this[id].listitem = this[id].createListItem();
 		}
	}
	
	this.list = function()
	{
		/*	draw scenes in list, requires this.getList() with list reference
		*/
		
		dbg.msg('Scenes, getlist=',this.getList);
		
		if (!this.getList) return;
		
		var list = this.getList();
		if (!list.layer.visible) return; //list must be visible for update

		dbg.msg('Scenes: draw list (id=',list.id,')');

		//create ordered array (by scene.index)
		var scenelist = this.order();

		//add to list
		list.addElements(scenelist);

		this.enable();
		this.checkLive();
 	}
	
	this.order = function()
	{
		/*	return ordered scene array
		*/
		var scenelist = new Array();
		for (var id in this)
		{
			if (id=='scene' || id=='new' || typeof(this[id])!='object') continue;
			if (this[id].listitem) scenelist[this[id].index] = id;
		}
		return scenelist;
	}
	
	this.enable = function(force)
	{
		dbg.msg('Scenes[',this.getList().id,']: enable list');

		for (var id in this)
		{
			if (id=='scene' || id=='new' || typeof(this[id])!='object') continue;
			this[id].enable(force);
		}
	}
	
	this.checkLive = function(records,stop)
	{
		return; //->REFACTOR
		
		if (this.livepolling) window.clearTimeout(this.livepolling);
		if (stop) return;
	
		/*	find live scenes in current collection
		*/
		if (!records)
		{	
			if (this.order().length==0) return; //no scenes to check
			var obj = this;
			GW.QUERY.queryStore('q-live-scenes',function(rsp) { obj.checkLive(rsp) },'ids',this.order().join());
			return;
		}
		
		//reset all first
		this.resetLive();
		
		dbg.msg('Scenes[',this.getList().id,']: ',records.length,' scenes are live');
		
		for (var i=0; i<records.length; i++)
		{
			var id = records[i].id;
			dbg.msg('Scenes.setlive, id=',id);
			this[id].setLive(true);
		}
		
		//update every half minute
		var obj = this;
		if (!portal.disable_live_polling) this.livepolling = window.setTimeout(function() { obj.checkLive() },30000);
	}
	
	this.resetLive = function()
	{
		for (var id in this)
		{
			if (id=='scene' || id=='new' || typeof(this[id])!='object') continue;
			this[id].setLive(false);
		}
	}
	
// 	this.addList = function()
// 	{
// 	
// 	
// 	}


	/*	portal scene object
	*/

	function Scene(collection,p,list)
	{
		/*	p 		scene properties object: id, index (in collection), type, name, image, desc, user, [admin], [creationdate], [modificationdate]
			[list]	the list it will display in */
		
		//references
		this.portal = portal;
		this.gui = portal.gui;
		this.layout = portal.layout;
		
		this.collection = collection;
		this.layer = portal.layout.layers['scene'];
 		this.placeslayer = portal.layout.layers['places'];

		this.id = p.id;
		this.index = (p.index!=undefined)? p.index:portal.scenes.count;
		this.list = list;
		
		this.name = p.name;
		this.type = p.type;
		this.icon = p.icon;

		this.user = p.user;
		this.admin = p.admin;
		
		this.creationdate = Number(p.creationdate);
		this.modificationdate = Number(p.modificationdate);
		this.eventcount = p.eventcount;
		this.state = p.state; //states: 1=draft, 2=final, 3=public (playback for all)
		if (this.state==3) this.allowplayback = true;
		
		this.meta = new Object();
		this.meta.initial = true;
		this.content = new Object();
		this.activity = new Object();
		
		this.meta.description = p.description || '';
		// = description.substring(0,80); //max 80 chars for desc
		
		if (p.image) this.meta.image = { id:p.image }; //icon
		
		//dislay in portal or project
		if (this.list)
		{
			var obj = this;
			this.listaction = list.action || function() { obj.open() }
			this.listitem = this.createListItem();
		}
	}

	Scene.prototype.createListItem = function()
	{
		/*	list item //->will be replaced in next release (portal redesign)
		*/
//		dbg.msg('Scene: createListItem, id=',this.id,', list=',this.collection.getList().id);
		
		//-> todo: include list prefs..
		
		//->random scaling, use more sofisticated popularity model here later
		var scale = 0.5 + (Math.random()*.40);

		var options = { scale:scale }
		if (this.collection.getList)
		{
			if (this.collection.getList().id=='myscenes' || this.collection.getList().id=='project')	var options = { width:185 }; //->CHANGE THIS
			if (this.collection.getList().id=='project' && this.portal.lists['project'].type=='cloud') var options = { scale:0.45 + (Math.random()*.25) }
		}
		
		if (this.state=='1')
		{
			options.draft = true;
		}

		//create listItem		
		var div = this.portal.gui.createListItem(this.list,'scene',options);
				
		//div.object.setContent('['+this.index+']'+this.name,this.user,this.creationdate,this.meta.description,this.meta.image);
		
		//set contents
		div.object.setContent(this.name,this.user,this.creationdate,this.meta.description,this.icon,this.state=='1');
		
		//attach open event
		var obj = this;
		div.onclick = function() { obj.listaction(obj.id) };
		
		return div;
	}
	
// 	Scene.prototype.updateListItem = function(p)
// 	{
// 		/*	update content of listitem, 
// 			p = { name, user, creationdate, description, image } */
// 		
// 		if (!this.listitem) return;
// 
// 		dbg.msg('Scene: update listitem for id=',this.id);
// 		
// 		this.listitem.object.setContent(p.name,p.user,p.creationdate,p.description,p.image,this.state=='1');
// 		
// 		//update scene object
// 		this.name = p.name;
// 		this.meta.description = p.description;
// 		this.meta.image = p.image;
// 	}
	
	Scene.prototype.enable = function(force)
	{
		/*	enable or disable scene
		*/
		var enable = (this.user==this.portal.login.loginname || this.admin==this.portal.login.loginname || this.portal.login.role==0);
		if (force!=undefined) enable = force;
		
		this.enabled = enable;
		if (enable) this.allowplayback = true;

		//update in portal.list
		if (this.listitem) this.listitem.object.enable(enable);
	}
	
	Scene.prototype.setLive = function(live)
	{
		/*	live indicator, scene page and listitem
		*/
		
		$j(this.layer.menubar['activity'].div)
			.add(this.layer.menubar['activity_x'].div)
			.find('.scene-live')
			.remove();
		
		if (live)
		{
			$j('<img>')
				.attr('src','media/icon_live.gif')
				.addClass('scene-live')
				.appendTo(this.layer.menubar['activity'].div);
			$j('<img>')
				.attr('src','media/icon_live_selected.gif')
				.addClass('scene-live')
				.appendTo(this.layer.menubar['activity_x'].div);
		}


		//->TODO include listitem when redesigned
	
		return;
	
	
// 		/*	add live indication/button to listitem and/or scenepage
// 		*/
// 		
// 		//dbg.msg('Scene[',this.id,'] setLive(',live,')');
// 		
// 		if (this.portal.state.indexOf('direct')!=-1) return;
// 		
// 		//listitem
// 		if (this.listitem)
// 		{
// 			var elm = this.listitem.object.titlediv.firstChild;
// 			if (live && !elm.firstChild)
// 			{
// 				var img = document.createElement('img');
// 					img.className = 'list-item-live';
// 					img.src = 'media/icon_live_small.png';
// 				elm.appendChild(img);
// 			}
// 			else
// 			{
// 				if (elm.firstChild) elm.removeChild(elm.firstChild)
// 			}
// 		}
// 		//scenepage
// 		if (this.expanded)
// 		{
// 			var elm = document.getElementById('scene-live-button');
// 			if (live && !elm.firstChild)
// 			{
// 				var live = utils.pngButton( {
// 					src:'button_live',
// 					title:'',
// 					style:{ verticalAlign:'middle', marginLeft:'20px', width:'43px', height:'18px' }
// 				} );
// 				
// 				var obj = this;
// 				live.onclick = function() { if (obj.portal.state!='play') obj.playback(undefined,'live') }
// 				elm.appendChild(live);
// 			}
// 			else
// 			{
// 				if (elm.firstChild) elm.removeChild(elm.firstChild);
// 			}
// 
// 		}
	}
	
	Scene.prototype.setTitle = function(title)
	{
		/*	update title in scene layer
		*/
		this.layer.setTitle(
			this.portal.gui.create({ 
				id:'scene_title', 
				name:title || this.name,
				state:this.state
			})
		);	
	}
	
	Scene.prototype.open = function()
	{
		/*	open scenelayer with scene details, plays etc
		*/
		dbg.msg('<span style="color:#999966">Scene.open(',this.id,')</span>');

		this.portal.state = 'scene';
		this.expanded = true;

		//show (empty) layer for interface 'snappiness'
		this.layer.clear();
		this.placeslayer.hide();
		this.setTitle();
		this.layer.show();
		
		//other layers		
		this.portal.layout.hideLayers(['project']);

		//this.collection.resetLive();
		this.collection.select(this.id); //-> select model needs refactoring!
		
		//edit
		this.layer.menubar['edit'][this.enabled? 'show':'hide']();

		//get info + auto actions afterwards
		var obj = this;
		this.getInfo(undefined,function() {
		
			//deeplink to event or trace
			if (obj.portal.link)
			{
				if (obj.portal.link.type=='trace')
				{
					obj.selectedTrace = this.portal.link.id;
					obj.openPlacesMenu('traces');
				}
				
				if (obj.portal.link.type=='event')
				{
					obj.selectedEvent = this.portal.link.id;
					obj.openPlacesMenu('events');
				}
				
				delete obj.portal.link; //only once..
				return; //do not refresh widgets
			}
			
			//refresh menu layers (if visible)
			var l = obj.layout.layers;
			if (l['play'].visible) obj.openMenu('play');
			if (l['comment'].visible) obj.openMenu('comment');
			if (l['events'].visible) obj.openPlacesMenu('events');
			if (l['traces'].visible) obj.openPlacesMenu('traces');

			if (l['activity'].visible) 
			{
				//show or hide places-menu placeholder
				obj.layer.menubar['places_x'][obj.enabled? 'hide':'show']();
			}
		});

		//places
		this.getPlaceInfo();
		
		//setup playback		
		this.collection.enablePlayback();
		
		//activity
		this.getActivity();
	}
	
	Scene.prototype.getInfo = function(records,callback)
	{
		/*	get (publically available) info about scene
			includes counts of places (per type), rounds, roles, skills, assignments, objects */
			
		if (!records)
		{
			dbg.msg('Scene: getInfo()');
			var obj = this;
			GW.QUERY.queryStore('q-scene-info',function(records) { obj.getInfo(records,callback) },'id',this.id);
			return;
		}

		var rec = records[0];
		
		//name and description
		this.name = rec.getField('name');
		this.meta.description = rec.getField('description');
		this.setTitle(this.portal.state=='direct'? 'preview "'+this.name+'"':this.name);
		
		//icon (can be updated in direct)
		if (rec.getField('sceneiconid'))
		{
			this.icon = {
				id:rec.getField('sceneiconid'),
				scale:rec.getField('scale'),
				offsetx:rec.getField('offsetx'),
				offsety:rec.getField('offsety')
			}
		}

		//state and playback
		this.state = rec.getField('state');
		this.allowpublishing = rec.getField('allowpublishing')=='true';
		this.allowplayback = rec.getField('hascompleted')=='true'? true:false;
		if (rec.getField('isgame') && rec.getField('isgame')=='true') this.isgame = { webplayer:rec.getField('webplayer')=='true'? true:false };
		else this.isgame = false;

		//overrule playback rights
		if (this.enabled || this.state=="3") this.allowplayback = true;
		if (this.isgame==false) this.allowplayback = true; //allow playback for all, except in games genres
		
		this.info = {
			creationdate:Number(rec.getField('creationdate')),
			context:{
				id:rec.getField('contextid'),
				name:rec.getField('contextname')
			},
			product:{
				name:rec.getField('productname')
			},
			director:{
				name:rec.getField('owner'),
				image:rec.getField('ownerimage')
			},
			placecount:{
				total:Number(rec.getField('places'))
			},
			events:rec.getField('rounds'),
			maxtime:rec.getField('maxtime')? rec.getField('maxtime')+' min':Locale7.get('scene','no_maxtime'),
			cost:rec.getField('cost')? '&euro; '+rec.getField('cost'):Locale7.get('scene','free'),
			
			slogan:this.meta.description,
			tags:rec.getField('tags') || undefined
		}

		//place type counts
		for (var i=0; i<this.portal.template.placetypes.length; i++)
		{
			var type = this.portal.template.placetypes[i];
			this.info.placecount[type] = Number(rec.getField(type+'places'));
		}
		
		/*	update page
		*/
		var preview = this.portal.direct? this.portal.direct.previewing:false;
		if (this.portal.state.indexOf('direct')!=-1 && !preview)
		{
			this.updateDirectPage();
		}
		else
		{
			this.gui.updateScenePage(this);
			this.getContent();
			
			//traces for scene widget?
			if (this.portal.login.id) this.getUserTraces();
		}

		if (callback) callback();
	}
	
	Scene.prototype.getContent = function(rsp,slogan)
	{
		/*	get scene story- and mediumlist, update scene page
		*/
		if (!rsp)
		{
			var obj = this;
			GW.SCENE.get(function(rsp) { obj.getContent(rsp,slogan) }, this.id, 'content');
			return;
		}

		this.content = this.portal.parseXML(rsp.getElementsByTagName('content')[0]);
		this.gui.updateSceneStory(this.content.backdrop || 0,slogan || this.meta.description,this.content.storylist,this.content.mediumlist);
	}
	
	Scene.prototype.getPlaceInfo = function(records)
	{
		/*	get and display places on (non-interactive) map in scenelayer about page
		*/
		if (!records)
		{
			var obj = this;
			GW.QUERY.queryStore('q-scene-place-info',function(records) { obj.getPlaceInfo(records) },'id',this.id);
			return;
		}
		
		dbg.msg('Scene: getPlaceInfo, count=',records.length);

		//remove previous places, if any
		if (this.infoplaces) this.infoplaces.disposeAll();

		this.infoplaces = new Places7(this);

		if (records.length>0)
		{
			var max = (this.type=='secrettrail')? 1:records.length; //->REFACTOR to exclude use of template name..
			for (var i=0; i<max; i++)
			{
				var rec = records[i];
			
				this.infoplaces.create( {
					id: rec.getField('id'),
					type: 'none',
					noninteractive:true,
					name: rec.getField('name'),
					lat: rec.getField('lat'),
					lon: rec.getField('lon'),
					gmap:this.gui.sceneGMap
				} );
			}
			
			this.gui.sceneGMap.checkResize();
			this.infoplaces.setMapToBounds(-4);
		}
	}

	Scene.prototype.getActivity = function()
	{
		/*	get latest traces, events, includes live check
		*/
		
		dbg.msg('Scene: getActivity..');
			
		if (this.activity.check) window.clearTimeout(this.activity.check);
		
		//reset current activity
		this.activity = {
			check:false,
			live:false
		}
		
		if (this.portal.state.indexOf('direct')==-1) //do not update in direct mode
		{
			//10 latest events
			//this.getEvents(undefined,{ sceneid:this.id, max:10 },true); //-> do not include events in activity pane for now, add later?
	
			//10 latest traces
			this.getTraces('traces',undefined,{ sceneid:this.id, max:10 },true);
			
			//ugc
			if (this.layout.layers['activity'].visible) this.getUGC();
		}
		
		
		//keep polling	
		if (this.portal.scene_activity_check)
		{
			var obj = this;
			this.activity.check = window.setTimeout(function() { obj.getActivity() },this.portal.scene_activity_check*1000);
		}
	}
	
	Scene.prototype.getUGC = function(records)
	{
		/*	get and display uploaded media (slideshow) 
		*/
		if (!records)
		{
			var obj = this;
			GW.QUERY.queryStore('q-recent-user-media',function(records) { obj.getUGC(records) },{ sceneid:this.id, max:10 });
			return;
		}
		
		dbg.msg('Scene: got UGC, records=',records.length);
		
		if (records.length==0)
		{
			this.gui.UGCslides.rset();
			this.gui.UGCslides.display.html('<div style="text-align:center; padding-top:100px">'+Locale7.get('scene','no_uploads')+'</div>');
		}
		else
		{
			this.gui.UGCslides.add(records);
		}
	}
	
	Scene.prototype.getComments = function(records,options)
	{
		/*	get and display comments
		*/
		var obj = this;

		if (!records)
		{
			if (!options) options = new Object();
			
			//clear
			$j('#comments-list').empty();
			$j('#add-comment-form').val('');
			
			var query = { target:this.id, offset:options.offset || 0, max:options.limit || 1000, ownerinfo:true, last:true };

			dbg.tmr();
			GW.QUERY.queryStore('q-comments-for-target',function(records) { obj.getComments(records,options) },query);
			return;
		}	
		
		dbg.msg('Scene: got comments, records='+records.length,'tmr');

		if (this.portal.login.id && !this.commenting)
		{
			this.portal.addSceneCommenting();
		}
		
		var p = $j('#comments-list');
		var s = true;
		$j.each(records, function(i,rec) {
		
			s = !s; //alternate left and right style
			
			p.append(obj.portal.gui.create({ id:'comment', comment:{
				id:rec.getField('id'),
				user:rec.getField('ownername'),
				userid:rec.getField('owner'),
				date:Number(rec.getField('modificationdate')),
				type:s? 'right':'left',
				text:rec.getField('content'),
				remove:rec.getField('ownername')==obj.portal.login.loginname || obj.enabled
			}}))
		});
		
		//remove buttons
		$j('#comments-list')
			.find('.comment-remove')
			.click(function() {
				obj.commenting.del(this.id.substr(15));
				$j(this).find('a').blur();
			})
	}
	
	Scene.prototype.getUserTraces = function(records)
	{
		/*	get user traces for widget
		*/
		if (!records)
		{
			dbg.tmr();
			var obj = this;
			GW.QUERY.queryStore('q-traces',function(records) { obj.getUserTraces(records) },{ sceneid:this.id, userid:this.portal.login.id });
			return;
		}
		
		dbg.msg('Scene: getUserTraces, result=',records.length);
		
		if (records.length>0)
		{
			//add to traces select in widget
			var form = document.forms['shareform'];
			$j(form).find('option').not(':first').remove();

			$j.each(records, function(i,rec) {
				var date = rec.getField('modificationdate') || rec.getField('enddate');
				var score = rec.getField('score')? ', score '+rec.getField('score'):'';

				form.trace.options[i+1] = new Option(new Date(Number(date)).format('shorterhumandate')+score,rec.getField('id'));
			});
		}

		$j('#scene-share-trace')[records.length>0? 'show':'hide']();
	}
	
	Scene.prototype.getTraces = function(type,records,options,activity)
	{
		/*	get scene traces (plays), also used for getting events (rounds)
		*/
		var obj = this;
		var layer = this.layout.layers[type];
		var live = this.portal.state=='live' || this.portal.state=='play';
		
		if (!records)
		{
			//define options
			if (!options) options = { sceneid:this.id };
			if (type!='event-traces')
			{
				if (!activity && type.indexOf('events-')==-1) this.gui.updateSearchHeader(layer,'searching');
				if (!live) $j('#layer-widget-'+(activity? 'activity-':'')+type+' .widget-body-content').empty();
			}
			if (type=='traces' && !activity && (this.selectedLiveTrace || live)) options['liveonly'] = true;
			
			var q = type.indexOf('traces')!=-1? 'q-traces':'q-rounds';
			if (type=='events-invited') q+= '-playable';

			dbg.tmr();
			GW.QUERY.queryStore(q,function(records) { obj.getTraces(type,records,options,activity) },options);
			return;
		}	

		dbg.msg('Scene: get-',type,', result=',records.length,activity? ' [activity]':'');

		if (activity)
		{
			this.gui.updateWidgetList('activity-'+type,records);
		
			//live check (traces only, live events will have live traces..)
			if (type=='traces')
			{
				this.activity.live = $j.grep(records, function(rec) { return rec.getField('islive')=='true' }).length;
				dbg.msg('Scene: getTraces, live=',this.activity.live);
				this.setLive(this.activity.live);
			}
		}
		else
		{
			//store list, check deeplinks
			switch(type)
			{
				case 'events-publish':
				case 'events':
					$j.each(records,function(i,rec) {
						var id = rec.getField('id');
						obj.eventslist[id] = {
							id:id,
							state:rec.getField('state'),
							name:records[i].getField('name'),
							start:Number(rec.getField('startdate')),
							end:Number(rec.getField('enddate')),
							lastplaydate:Number(rec.getField('enddate')),
							hiscore:Number(rec.getField('hiscore'))
						};
					});
					
					if (this.selectedEvent)
					{
						dbg.msg('Scene, getTraces, selected event=',this.selectedEvent);
						//collapse search
						$j('#layer-widget-events .widget-header').trigger('collapse',[true]);
						//load event
						this.playback('playback-event',this.selectedEvent);
						delete this.selectedEvent;
					}
					break;

				case 'event-traces':
				case 'traces':
					$j.each(records,function(i,rec) {
						var id = rec.getField('id');
						obj['traceslist'][id] = {
							id:id,
							state:rec.getField('state'),
							is_team:rec.getField('team')? true:false,
							is_live:rec.getField('islive')? true:false,
							name:rec.getField('team') || records[i].getField('user'),
							date:Number(rec.getField('modificationdate')),
							players:rec.getField('players') || rec.getField('user'),
							score:Number(rec.getField('score')),
							rewards:rec.getField('rewards'),
							color:rec.getField('color'),
							roundid:rec.getField('roundid'),
							roundname:rec.getField('roundname')
						};
					});
					
					if (this.selectedTrace)
					{
						dbg.msg('Scene, getTraces, selected trace=',this.selectedTrace,this.selectedLiveTrace? ' [=LIVE]':'');
						
						//collapse search
						if (!this.selectedLiveTrace) $j('#layer-widget-traces .widget-header').trigger('collapse',[true]);
						//load trace
						this.playback(this.selectedLiveTrace? 'live':'playback',this.selectedTrace);
						delete this.selectedTrace;
						delete this.selectedLiveTrace;
					}
					break;
			}

			//list traces
			this.gui.updateWidgetList(type,records);
			
			//init event playback if event-traces	
			if (type=='event-traces') this.selectEvent(this.selectedEvent.id,records);
		}
	}

	Scene.prototype.getPlaces = function(rsp,callback,preview)
	{
		if (!rsp)
		{
			var obj = this;
			GW.SCENE.get(function(rsp) { obj.getPlaces(rsp,callback,preview) }, this.id, 'places');
			return;
		}

		if (this.type=='adventure') this.has_place_conditions = true; //REFACTOR, remove use of scenetype here later

		//reset current places collection, create new
		if (this.places) this.places.disposeAll();
		this.places = new Places7(this);
		
		var places = rsp.getElementsByTagName('place');
		dbg.msg('Scene: got places, count=',places.length);

		for (var i=0; i<places.length; i++)
		{
			var p = this.portal.parseXML(places[i]);
			
			//dbg.msg('Scene: getPlaces, place=',dbg.obj(p,1));
			
			if (!p.meta) p.meta = new Object(); //compatibility with (old) incomplete places //->REMOVE later
			
			if (i>0 && this.type=='secrettrail' && preview && !p.meta.ispublic) continue; //do not show all places for secrettrail type
			
			this.places.create( {
				id: p.id,
				type: (!preview || p.meta.ispublic)? p.type:'none',
				tasktype: p.meta.tasktype || undefined,
				name: p.name,
				lat: p.geometry.point.lat,
				lon: p.geometry.point.lon,
				radius: p.geometry.point.radius,
				meta: p.meta,
 				content: p.content
			} );
		}
		
		//create a dragable list
		if (this.portal.state=='direct_places')
		{
			this.addDragableList();
		}

		//center map to places bounds
		this.places.setMapToBounds();
		
		if (callback) callback();
	}

	Scene.prototype.publicPublishing = function()
	{
		/*	add public publishing
		*/
		
		if (!this.portal.userlib)
		{
			//load userlib first
			var obj = this;
			this.portal.loadLib('Userlib',function() { obj.publicPublishing() } );
			return;
		}
		
		var publish = {
		
			portal:this.portal,
			scene:this,
			widget:$j('#layer-widget-events-publish'),

			getEvents:function()
			{
				//get events for login
				this.scene.getTraces('events-publish',undefined,{ sceneid:this.scene.id, ownerid:this.portal.login.id });
				
				//reset gui
				this.rset();
			},
		
			add:function(edit,id)
			{
				//hide list, add form
				$j('#layer-widget-events-publish')
					.find('.widget-body-content,.layer-widget-header-scheduled')
					.hide();

				$j('#public-publish')
					.html(this.portal.gui.create('scene_public_publish_form'))
				
				//date picker
				var form = document.forms['eventform'];
				$j(form.startdate).add(form.enddate)
					.datepicker({
						dateFormat:'dd-mm-yy',
						showAnim:'fadeIn'
					});
				
				//default dates
				if (!edit)
				{
					$j(form.startdate).datepicker('setDate','0');
					$j(form.enddate).datepicker('setDate','+1');
					var d = new Date();
					form.starthour.value = form.endhour.value = d.getHours();
					form.startminute.value = form.endminute.value = Math.floor(d.getMinutes()/15)*15;
				}
				
				//close and save buttons
				var obj = this;
				this.portal.gui.button('close','close_pane',18)
					.css({
						position:'absolute', right:38, width:18, top:3
					})
					.appendTo('#public-publish-form')
					.find('a')
					.click(function() {
						obj.rset();
					});

				this.portal.gui.button('save','save_pane',37)
					.css({
						position:'absolute', right:2, width:37, top:3
					})
					.appendTo('#public-publish-form')
					.find('a')
					.click(function() {
						obj.update(id);
					});
					
				//add-team button
				this.portal.gui.button('add','add_small',150,Locale7.get('publish','add_team'))
					.css({ position:'relative', width:150, height:12, lineHeight:'normal' })
					.appendTo('#public-publish')
					.find('a')
					.css({ 'padding-left':22, 'margin-left':4 })
					.click(function() {
						obj.addTeam();
						this.blur();
					});
					
				//users button
				this.portal.gui.button('users','users',20)
					.css({ width:20, height:20, right:5, bottom:4 })
					.appendTo('#public-publish-form')
					.find('a')
					.css('height',20)
					.click(function() {
						obj.portal.userlib.selectTab('search');
						this.blur();
					});
			},
			
			rset:function()
			{
				//show list
				$j('#layer-widget-events-publish')
					.find('.widget-body-content,.layer-widget-header-scheduled')
					.show();
				
				var obj = this;
				$j('#public-publish').html(
					this.portal.gui.button('add','add_small',150,Locale7.get('publish','add_event'))
						.find('a')
						.css({ 'padding-left':22, 'margin-left':4, 'line-height':'normal' })
						.click(function() {
							obj.add();
						})
						.end()
				);
				
				//userlib reset
				this.portal.userlib.removeDropTargets();
				this.portal.userlib.hide();
			},

			edit:function(id,records)
			{
				//gui
				this.add(true,id);
				
				//get round details
				GW.ROUND.get(this,id);
			},
			
			getRsp:function(rsp)
			{
 				var round = this.portal.parseXML(rsp.firstChild);
 				
 				dbg.msg('Scene.publicPublish: got round:',dbg.obj(round,3));
 				
				//update form values
				var form = document.forms['eventform'];
					form.name.value = round.name;
					
				$j.each(['start','end'], function(i,v) {
					var d = new Date(Number(round[v+'date']));
					form[v+'hour'].value = d.getHours();
					form[v+'minute'].value = d.getMinutes();
					$j(form[v+'date']).datepicker('setDate',d);
				});
				
				//teams
				var obj = this;
				$j.each(round.teamlist, function(i,team) {
					obj.addTeam(team);
				});
			},
			
			getNegRsp:function(errorId,error,details,tagname)
			{
				dbg.msg('Scene.publicPublish: getNegRsp, error=',error);
				alert('error!\n\nCannot get event:\n'+details);
			},
			
			update:function(id)
			{
				dbg.msg('update..',id? 'id='+id:'');
				
				/*	gather and validate data
				*/
				
				//event data
				var fail, startdate, enddate;
				var form = document.forms['eventform'];
				var name = form.name.value;
				if (name=='') fail = 'no_name';
				
				var d = form.startdate.value.split('-');
				if (d.length<2) fail = 'no_startdate';
				else
				{
					var startdate = new Date(d[2],Number(d[1])-1,Number(d[0]), form.starthour.value, form.startminute.value).getTime();
				}
				var d = form.enddate.value.split('-');
				if (d.length<2) fail = 'no_enddate';
				else
				{
					var enddate = new Date(d[2],Number(d[1])-1,Number(d[0]), form.endhour.value, form.endminute.value).getTime();
				}
				if (startdate>enddate) fail = 'enddate_before';
				
				if (fail)
				{
					return alert(Locale7.get('error','publish_'+fail));
				}
			
				dbg.msg('fail=',fail,'/ name=',name,', start=',startdate,', end=',enddate);
			
				//team data
				var fail;
				var obj = this;
				var teamlist = new Array();
				this.widget.find('div.public-publish-team').each(function() {

					var team = new Object();
						team.id = $j(this).data('id');
						if (team.id.indexOf('new')!=-1) delete team.id;
						team.name = $j(this).find('input[name="name"]').val();
						team.color = $j(this).find('.team-color').data('color');
						
					var feature = obj.scene.meta.rolelist || obj.scene.meta.assignmentlist;
					if (feature)
					{
						team[obj.scene.meta.rolelist? 'role':'assignment'] = $j(this).find('select[name="feature"]').val();
					}

					if (team.name=='')
					{
						fail = 'no_teamname';
						return false; //break loop
					}
					
					//users
					var userlist = new Array();
					$j(this).find('div.public-publish-user').each(function() {
						userlist.push({
							id:$j(this).data('id'),
							name:$j(this).text()
						});
					});
					
					if (userlist.length==0)
					{
						fail = 'no_teamusers';
						return false; //break loop
					}
					
					team.userlist = userlist;
					teamlist.push(team);
				});
				
				if (!fail && teamlist.length==0) fail = 'no_teams';
				
				if (fail)
				{
					return alert(Locale7.get('error','publish_'+fail));
				}
				
				var event = {
					name:name,
					startdate:startdate,
					enddate:enddate,
					teamlist:teamlist
				}

				dbg.msg('Scene.publicPublish: create/update event=',dbg.obj(event,3));

				//update or create
 				if (id)
				{
					GW.ROUND.update(this,id,event);
				}
				else
				{
					GW.ROUND.create(this,this.scene.id,event);
				}
			},
			
			addTeam:function(team)
			{

				//new team?
				if (!team) team = { 
					id:'new-team-'+new Date().getTime(),//id must be unique
					name:'',
					color:this.portal.template.teamcolors[Math.floor(Math.random()*this.portal.template.teamcolors.length)],
					userlist:[]
				}

				//team feature? (role or assignment)
				var feature = this.scene.meta.rolelist || this.scene.meta.assignmentlist;
				if (feature)
				{
					var type = this.scene.meta.rolelist? 'role':'assignment';
					feature = {
						type:type,
						selected:team[type],
						list:feature
					};
				}
			
				var t = $j('#public-publish-list')
					.append(this.portal.gui.create({ id:'scene_public_publish_team', feature:feature }))
					.find('.public-publish-team:last')
					.data('id',team.id)
					.hover(function() {
						$j(this).css('background-color','#222222');
					}, function() {
						$j(this).css('background-color','transparent');
					});
					
				//remove button
				var obj = this;
				this.portal.gui.button('remove','minimize_pane',18)
					.css({
						position:'absolute', right:3, width:14, top:5
					})
					.appendTo(t)
					.find('a')
					.click(function() {
						if (confirm(Locale7.get('publish','remove_team')))
						{
							obj.portal.userlib.removeDropTarget(team.id);
							$j(t).fadeOut('normal',function() { $j(this).remove() });
						}
					});
					
				//update form values
				t.find('input[name="name"]').val(team.name);
				t.find('.team-color').css({
					'background-color':'#'+team.color,
					'color':'#'+team.color
					})
					.click(function() {
						obj.colorPicker(this);				
					})
					.data('color',team.color)

				//users
				var elm = t.find('.public-publish-team-users');
				
				$j.each(team.userlist, function(i,user) {
					obj.addUser(elm,user);
				});
				
				//droptarget
				this.portal.userlib.addDropTarget({
					id:team.id,
					getX:function() { return elm.offset().left },
					getY:function() { return elm.offset().top },
					getH:function() { return elm.height() },
					w:elm.width(),
					h:elm.height(),
					elm:elm[0],
					borderstyle:'1px solid transparent',
					callback:function(name,id,drop,uid) {
						$(elm).css('border','1px solid transparent');
						obj.addUser(elm,{ id:uid, name:name });
					}
				});
			},
			
			addUser:function(elm,user)
			{
				var name = user.name.length>10? user.name.substr(0,10)+'..':user.name;
			
				$j('<div></div>')
					.addClass('public-publish-user')
					.insertBefore(elm.find('em').remove().end().find('.clear'))
					.data('id',user.id)
					.attr('title',user.name)
					.html(name+'<img src="media/icon_remove_small.png">')
					.hover(function() {
						$j(this.lastChild).show();
					}, function() {
						$j(this.lastChild).hide();
					})
					.find(':last')
					.click(function() {
						$j(this.parentNode).fadeOut('normal',function() { $j(this).remove() });
					});
			},
			
			createRsp:function()
			{
				this.getEvents();
			},
			
			createNegRsp:function(errorId,error,details,tagname)
			{
				dbg.msg('Scene.publicPublish: createNegRsp, error=',error);
				alert('error!\n\nCannot create event:\n'+details);
			},
			
			updateRsp:function()
			{
				this.rset();
				this.getEvents();
			},
			
			updateNegRsp:function(errorId,error,details,tagname)
			{
				dbg.msg('Scene.publicPublish: updateNegRsp, error=',error);
				alert('error!\n\nCannot update event:\n'+details);
			},

			remove:function(id)
			{
				if (confirm(Locale7.get('publish','remove_event')))
				{
					GW.ROUND.del(this,id);
				}
			},
			
			delRsp:function()
			{
				this.getEvents();
			},
			
			delNegRsp:function(errorId,error,details,tagname)
			{
				dbg.msg('Event: delNegRsp, error=',error);
				alert('error!\n\nCannot delete event:\n'+details);
			},

			colorPicker:function(elm)
			{
				/*	color select overlay
				*/
				
				var div = $j('<div></div>')
					.attr('id','public-colorpicker')
					.css({
						left:$j(elm).offset().left,
						top:$j(elm).offset().top + 20
					})
					.appendTo('body')
					
				$j.each(this.portal.template.teamcolors, function(i,c) {
					
					$j('<div></div>')
						.addClass('team-color')
						.css('background-color','#'+c)
						.data('color',c)
						.appendTo(div)
						.hover(function() {
							$j(this).css('border-color','#ffffff');
						},function() {
							$j(this).css('border-color','#000000');
						})
						.mousedown(function() {
							var c = $j(this).data('color');
							$j(elm)
								.css({
									'background-color':'#'+c,
									'color':'#'+c
								})
								.data('color',c);
						});
				});
				
				div.append('<div class="clear"></div>');
			
				$j(elm).blur(function() {
					$j(div).fadeOut('fast',function() { $j(this).remove() });
				});
			}
		};

		//update my-events list		
		publish.getEvents();
		
		this.publicPublish = publish;
	}


	/*	layer menus, close
	*/

	Scene.prototype.openMenu = function(menu,close,places)
	{
		/*	open a menu (overlay) layer
		*/
		
		if (!close) //close others
		{
			var menus = places? ['events','traces']:['activity','comment','play'];
			for (var i=0; i<menus.length; i++)
			{
				if (menus[i]!=menu) this.openMenu(menus[i],true,places);
			}
		}
		
		if (!places)
		{
			//hide scene layer scrollbar
			this.layer.blur(['scrollbar'],!close);
			
			//show places menu placeholder
			if (!this.enabled)
			{
				this.layer.menubar['places_x'][close? 'hide':'show']();
			}
			else
			{
				this.layer.menubar['places_x'].hide();
			}
		}
		
		this[places? 'placeslayer':'layer'].menubar[menu][close? 'show':'hide']();
		this[places? 'placeslayer':'layer'].menubar[menu+'_x'][close? 'hide':'show']();

		var layer = this.layout.layers[menu];

		if (!close && menu=='play') $j(layer.div).find('.layer-widget').hide(); //hide all widgets in play menu

		layer[close? 'hide':'show']();

		//reset plays 
		if (this.traces) this.traces.resetAll();
			
		if (close)
		{
			//stop slideshow
			if (menu=='activity')
			{
				this.gui.UGCslides.rset();
			}
			
		
			//unload traces, event
			if (menu=='events' && this.selectedEvent)
			{
				this.selectedEvent.dispose();
				delete this.selectedEvent;
			}
			return;
		}
	
		switch (menu)
		{
			case 'activity':
				this.getActivity();
				break;
			case 'comment':
				this.getComments();
				break;
			case 'play':
				this.getTraces('events-scheduled',undefined,{ sceneid:this.id, maxstate:1, limit:10 });
				this.getTraces('events-invited',undefined,{ sceneid:this.id, userid:this.portal.login.id });

				/*	public publishing
				*/
				if (this.isgame && this.state==2 && this.allowpublishing)
				{
					$j('#layer-widget-events-publish').show();

					if (this.meta.initial)
					{
						//get full scene meta first
						var obj = this;
						GW.SCENE.get(function(rsp) { 
							dbg.msg('Scene/publicPublish: updating scene meta..');
							obj.meta = obj.portal.parseXML(rsp.getElementsByTagName('meta')[0]);
							obj.publicPublishing();
						},this.id,'meta');
					}
					else
					{
						this.publicPublishing();
					}
				}
				else
				{
					$j('#layer-widget-events-publish').hide();
				}
				break;
				
			case 'events':
			case 'traces':
				if (this.allowplayback)
				{
					/*	get events or traces
					*/
					$j('#layer-widget-'+menu).show().prev().hide();

					//expand search
					if (!this.selectedEvent && !this.selectedTrace)
					{
						$j('#layer-widget-'+menu+' .widget-header').trigger('collapse',[false]);
					}
				 	this.getTraces(menu);
				}
				else
				{
					/*	no playback allowed (game genres)
					*/
					var w = $j('#layer-widget-no'+menu);
					if (w.length) w.show().next().hide();
					else
					{
						$j(this.layout.layers[menu].content)
							.prepend(this.gui.createMenuLayerWidget('no'+menu));
						$j('#layer-widget-no'+menu)
							.find('.widget-body-content')
							.append('<div class="msg">'+Locale7.get('scene','allow_traces')+'</div>')
							.end().next().hide();
					}
				}
				break;
		}
	}
	
	Scene.prototype.openPlacesMenu = function(menu,close)
	{
		this.openMenu(menu,close,true);
	}
	
	Scene.prototype.openPlaces = function(select)
	{
		/*	open places layer in view mode (show public places)
		*/
		
		//close menus
		var obj = this;
		var menus = ['activity','play','comment'];
		$j.each(menus, function(i,m) { if (obj.layout.layers[m].visible) obj.openMenu(m,true) });
		
		this.playback('places');
	}
	
	Scene.prototype.closePlaces = function()
	{
		/*	close places (map) layer, 
			and exit current state (edit, live, play or playback) */
			
		dbg.msg('Scene: close places, state=',this.portal.state);

		this.layer.blur(['content','scene_extras'],false);
		this.layer.showMenus(['places']);
		this.layer.hideMenus(['places_x']);
		
		this.placeslayer.hideMenus(['livetraces_x']);
		$j('#layer-widget-header-live').remove();

		if (this.expanded_place)
		{
			if (!this.expanded_place.collapse())
			{
				//reset marker, and cancel add
				this.portal.layout.panes['addplaces'].dragmarker.setPosition(-50,0);
				return;
			}
		}
		
		//exit preview/play/live/playback
		if (this.portal.state.indexOf('direct')==-1)
		{
			//unload places and plays
			if (this.places)
			{
				this.places.disposeAll();
				delete this.places;
			}
			if (this.selectedEvent)
			{
				this.selectedEvent.dispose();
				delete this.selectedEvent;
			}
			if (this.traces)
			{
				this.traces.disposeAll();
				//delete this.traces;
			}

			if (this.webplay) delete this.webplay;
			
			this.portal.layout.panes.hide('playback','puppetmaster');
			this.portal.layout.panes.dispose('IM');

			//stop events			
			if (this.portal.state=='live' || this.portal.state=='play')
			{
				this.exitLivePlayback();
			}
		}

		this.placeslayer.hide();
//		this.playlayer.menubar['puppetmaster'].hide();
		if (this.portal.layout.fullscreen) this.portal.layout.toggleFullscreen();
		
		if (this.portal.state=='direct_places')
		{
			this.getInfo(); //updates placecount

			//this.updateDirectPage();
			
			if (this.portal.direct.stage)
			{
				//continue stepwise direct
				this.portal.direct.step(this.portal.direct.stage);
				//update gui to 'edit' mode
// 				this.layer.menubar['viewscene'].show();
// 				this.layer.menubar['deletescene'].show();
				//this.setTitle('edit');
			}
		}
		
		//hide medialib
		this.portal.layout.panes.hide('mymedia');
		
		//reset edit mode
		if (this.portal.state=='direct_places')
		{
			this.portal.state = 'direct';
		}
		else
		{
			this.portal.state='scene';
		}
	}

	Scene.prototype.close = function(force,logout)
	{
		/*	unload scene
		*/

		//exit direct mode first
		if (this.portal.state.indexOf('direct')>-1)
		{
			//check unsaved changes
			if (!this.portal.direct.exit(force)) return false;	
		}
		if (this.portal.state=='live' || this.portal.state=='play') this.closeScenePlaces();
		
		dbg.msg('<span style="color:#669999">Scene: unloading.. ('+this.id+')</span>');
		
		//exit places
		if (this.placeslayer.visible)
		{
			this.closePlaces();
		}

		if (this.activity && this.activity.check) window.clearTimeout(this.activity.check);

		//reset gui
		this.layer.clear();
		this.layout.hideLayers(['scene','direct','directplaces']);
		this.layout.panes.hide('help','players','mymedia');
		if (logout && this.layout.layers['play'].visible) this.openMenu('play',true);
		
		delete this.gmap;
		
		//unload data
		if (this.infoplaces)
		{
			this.infoplaces.disposeAll();
			delete this.infoplaces;
		}
		if (this.places)
		{
			this.places.disposeAll();
			delete this.places;
		}
		if (this.traces)
		{
			this.traces.disposeAll();
			delete this.traces;
		}
		if (this.selectedEvent)
		{
			this.selectedEvent.dispose();
			delete this.selectedEvent;
		}
					
		delete this.info;
		delete this.stats;
		
		/*	class unload: remove all (added) functions [from direct, playback, play]
			prototype functions will remain.. */
			
		for (var id in this.portal.scene)
		{
			if (id!='listaction' && typeof(this.portal.scene[id])=='function') delete this.portal.scene[id];
		}

		this.collection.select();


		//clear collection
//		this.portal.scenes.clear();


		//un-select (in all collections) //->NEEDS refactoring, use single global collection
// 		this.portal.portalscenes.select();
// 		this.portal.projectscenes.select();
// 		if (this.portal.myscenes) this.portal.myscenes.select();

		this.portal.state = 'portal';
		this.expanded = false;
		
		this.collection.checkLive();
		
		
		
		
		
		
		//if (this.portal.my) this.portal.openMyScenes();
		
		return true;
	}
}

/* [file src="Scenes.playback.js"]
*/
/*	usemedia.com . joes koppers . 03.2008-04.2008
	thnx for reading this code */


/*	7s Scene (play and) playback engine
*/

Scenes7.prototype.enablePlayback = function()
{
	/*	enable playback functions for current scene
	*/

	if (!this.scene) return;
	
	dbg.msg('Scenes: enabling playback for current scene..');
	
	//caching, lists
	this.scene.traceslist = new Object();
	this.scene.eventslist = new Object();
	this.scene.eventtraceslist = new Object();
	
	this.scene.traces = new Plays(this.scene);	
	this.scene.players = new Players7(this.scene);

	this.scene.playback = function(mode,id)
	{
		/*	open places layer in play/playback/event/live mode
		*/
		dbg.msg('<span style="color:#999966">scene.playback, mode=',mode.toUpperCase(),id? ', id='+id:'','</span>');
		
		var obj = this;

		//update scene meta, used for reward places a.o.
		if (this.meta.initial)
		{
			GW.SCENE.get(function(rsp) { 
				dbg.msg('Playback: updating scene meta..');
				obj.meta = obj.portal.parseXML(rsp.getElementsByTagName('meta')[0]) 
			},this.id,'meta');
		}
		
		this.live = (mode=='live' || mode=='play');

		//prepare and show places layer
		this.layer.hideMenus(['places']);
		this.portal.layout.panes.hide('addplaces','sceneplaces');
		if (this.portal.state=='live' && !this.live)
		{
			//->NEEDS TESTING
			
			dbg.msg('===reset live view===');
		
			//reset live view
			this.placeslayer.hideMenus(['livetraces_x']);
			$j('#layer-widget-header-live').remove();
			
			//remove players, ucg
			if (this.traces) this.traces.disposeAll();
			//if (this.players) this.players.disposeAll();
			
			
			//reset menus
			this.placeslayer.showMenus(['events']);
		}
		
// 		if (this.live)
// 		{
// 		
// 		
// 		}
		
		this.placeslayer.show();
		
		switch (mode)
		{
			case 'places':
			
				/*	show map with all places in preview mode, 
					public places are expandable */
				
				//expand widgets
				$j('#layer-widget-traces .widget-header, #layer-widget-events .widget-header').trigger('collapse',[false]);	

				this.getPlaces(undefined,undefined,this.state!=3); //in closed state all places are expandable
				this.portal.state = 'places';
				break;
		
			case 'playback':
			case 'playback-event':

				/*	show map with all places, 
					load selected trace or event */
					
				//collections
				if (!this.traces) this.traces = new Plays(this)
				if (!this.players) this.players = new Players7(this);
				
				var select = mode.indexOf('-event')!=-1? 'selectEvent':'selectTrace';				

				//get playback places, if not done so already
				if (this.portal.state!='playback')
				{
					this.portal.state = 'playback';
					this.getPlaces(undefined,function() { obj[select](id) } );
					return;
				}
				else
				{
					this[select](id);
				}
				
				this.portal.state = 'playback';
				break;

			case 'live':
			case 'play':
			
				/*	live (or webplay), show live traces list, load selected live trace
					add live event handling */
				
				this.placeslayer.hideMenus(['events','traces','traces_x']);
				
				dbg.msg('///////////////// liveHandling = ',this.liveHandling)
				
				if (!this.liveHandling)
				{
					/*	init live view and handling if not done already
					*/
					GW.EVENT.start(this,this);

					if (mode=='live') this.getPlaces(undefined,undefined,this.state!=3);
					
					//replace events/traces menus with live traces menu
					this.placeslayer.showMenus(['livetraces_x']);
					
					//add live header to traces widget
					$j('<img src="media/widget/menu_layer_header_live.png">')
						.attr('id','layer-widget-header-live')
						.appendTo('#layer-widget-traces .widget-header');
					
					//expand list
					$j('#layer-widget-traces .widget-header').trigger('collapse',[false]);
						
					//dispose current (playback) traces
					if (this.traces) this.traces.disposeAll();	
					
					this.portal.state = mode;
					
					this.addLiveHandling();
					
					//start polling
					if (this.portal.scene_traces_check) this.addLiveTracesCheck();
				}
				
				//if (this.portal.state=='live') GW.EVENT.subscribe(this,'/live/'+this.id+'/'+roundid);
				if (this.portal.state=='live')
				{
					//add play object
					this.selectTrace(id);
								
					/*	subscribe to trace live events
					*/
					window.setTimeout(function() {
						GW.EVENT.subscribe(obj,'/live/'+obj.id+'/'+obj.traceslist[id].roundid+'/'+id);
					},1000);
					
					
					
					
					
					
				}



					//enable messaging if owner or project-admin ("puppetmaster")
// 					if (this.enabled && this.isgame)
// 					{
// 						this.collection.enablePlay();
// 						this.puppetMaster();
// 					}

					
			
				break;
		}
	}
	
	this.scene.addLiveTracesCheck = function()
	{
		/*	keep live traces list updated
		*/
		dbg.msg('Playback: live traces check..');
			
		if (this.liveTracesCheck) window.clearTimeout(this.liveTracesCheck);
		
		//10 latest traces
		delete this.selectedTrace;
		this.getTraces('traces');
		
		//keep polling	
		if (this.portal.scene_traces_check)
		{
			var obj = this;
			this.liveTracesCheck = window.setTimeout(function() { obj.addLiveTracesCheck() },this.portal.scene_traces_check*1000);
		}
	}
	
	this.scene.addLiveHandling = function()
	{
		/*	add GW live event handlers
		*/
		this.liveHandling = true;
		
		//GW events
		this.startRsp = function(rsp)
		{	
			dbg.msg('Live: started listening..');
		}
		this.stopRsp = function(rsp)
		{
			dbg.msg('Live: stopped..');
		}
		this.subscribeRsp = function(rsp)
		{	
			dbg.msg('Live: subscribed..');
		}
		this.unsubscribeRsp = function(rsp)
		{	
			dbg.msg('Live: unsubscribed..');
		}
		this.onIndication = function(rsp)
		{
			/*	parse event
			*/
			if (this.portal.state=='live')
			{
				dbg.msg('Live: <span style="color:#990000">received event=',rsp.firstChild.tagName+'</span>');
				
				//get play id from topic
				var topic = rsp.getAttribute('topic'); //-> 0/live/sceneid/roundid/playid/userid
				var ids = topic.split('/');
				var playid = ids[4];
				//var roundid = ids[3];
				
				var elm = rsp.firstChild;
			}
			else
			{
				dbg.msg('Live[play]: <span style="color:#990000">received event=',rsp.tagName+'</span>');
				
				var roundid = this.webplay.roundid;
				
				if (!rsp.getAttribute('playid')) 
				{	
					dbg.msg('Live(play): no playid, skipping event..');
					//return;
				}
				
				var playid = rsp.getAttribute('playid') || this.webplay.playid; //->REMOVE THIS
				//var playid = this.webplay.playid;
			
				var elm = rsp;
			}

			/*	add event to play
			*/
			if (this.traces && this.traces[playid])  
			{
				if (this.traces[playid].addLiveEvent)
				{
					this.traces[playid].addLiveEvent(elm);
					//this.traces[playid].pane.show();
				}
			}
// 					else
// 					{
// 						/*	create new trace(play) first
// 						*/
// 						dbg.msg(this.portal.state,': adding play ',playid);
// 						
// 						//create empty trace immediately (prevent creation of multiple plays)
// 						this.traces[playid] = new Object();
// 
// 						var obj = this;
// 						var addTrace = function(records)
// 						{
// 							if (!records || records.length==0) return;
// 					
// 							var rec = records[0];
// 							var id = rec.getField('id');
// 							var players = rec.getField('players') || rec.getField('user');
// 							
// 							dbg.msg('rec.players=',rec.getField('players'));
// 
// 							obj.traces.create( {
// 								id:id,
// 								name:rec.getField('team') || rec.getField('user'),
// 								is_team:rec.getField('team')? true:false,
// 								color:rec.getField('color'),
// 								
// 								users:players.split(','),
// 								
// 								roundid:rec.getField('roundid'),
// 								roundname:rec.getField('roundname'),
// 								state:rec.getField('state'),
// 								startdate:Number(rec.getField('startdate')),
// 								
// 	 							mode:(this.webplay && id==this.webplay.playid)? 'play':'live'
// 							} );
// 							
// 							//load trace and add the event
// 							obj.traces[id].load();
// 							obj.traces[id].addLiveEvent(elm);
// 						}
// 
// 						//get trace obj from server //->UPDATE with better query when available
// 						GW.QUERY.queryStore('q-trace',function(records) { addTrace(records) },'id',playid);
// 					}
		}
	
	
	
	
	}
	
	this.scene.selectEvent = function(id,traces)
	{
		/*	create event and related traces
		*/
		dbg.msg('Playback: selectEvent, id=',id);
		
		if (!traces)
		{
			//clear current event, only one event view allowed 
			if (this.selectedEvent) this.selectedEvent.dispose();
		
			//create a new event object
			this.selectedEvent = new Round(this,id);
			
			//get traces for this event
			this.getTraces('event-traces',undefined,{ roundid:id });
			return;
		}

		/*	add trace and load event
		*/
		dbg.msg('Playback: selectedEvent, traces=',traces.length);
		
		this.selectedEvent.addTraces(traces);
		this.selectedEvent.load();
	}

	this.scene.addTrace = function(plays,play)
	{
		/*	add trace from cached play object
		*/
		
		plays.create( {
			id:play.id,
			name:play.name,
			is_team:play.is_team,
			color:play.color,
			users:play.players.split(','),
			roundid:play.roundid,
			roundname:play.roundname,
			state:play.state,
			startdate:play.startdate,
			enddate:play.enddate,
			mode:this.portal.state
		} );
	}

	this.scene.selectTrace = function(id)
	{
		/*	(create and) load a selected trace
		*/
		dbg.msg('Playback: selectTrace, id=',id);
		
		//cache	
		var play = this.traceslist[id];
		if (!this.traces[play.id]) this.addTrace(this.traces,play);
		
		this.traces[id].load();
	}
	
	this.scene.exitLivePlayback = function(places)
	{
		/*	exit live mode, optionally return to 'places' mode
		*/
		dbg.msg('Playback: exiting..');
		
		this.liveHandling = false;
		
		if (this.liveTracesCheck) window.clearTimeout(this.liveTracesCheck);

		if (this.portal.state=='play') GW.PLAY.exit(function(){ dbg.msg('Live: exited play..') });
		if (this.portal.state=='live') GW.EVENT.unsubscribe(function(){ dbg.msg('Live: unsubcribed..') });
		GW.EVENT.stop(function(){ dbg.msg('Live: stopped..') });

		//reset traces widget/menu
		this.placeslayer.hideMenus(['livetraces_x']);
		$j('#layer-widget-header-live').remove();
		
		if (!places)
		{
			this.openPlacesMenu('traces',true);
			this.placeslayer.showMenus(['events','traces']);
		}
		else
		{
			this.openPlacesMenu('traces',true);
			this.playback('places');
		}
	}

	this.scene.playbackPlaceData = function(place)
	{
		/*	update place pane with (playback) data
		*/
		
		dbg.msg('scene.playbackPlaceData, place=',place.id);
		
		switch (place.type)
		{
			case 'task':
				var task = place.content.task;

				var panel = place.pane.panels['task'];
				if (task.maxtime)
				{
					panel.addContent(this.portal.gui.create( { id:'playback_place_maxtime', time:task.maxtime } ) );
				}

				panel.addContent(task.intro);
				if (task.medium && task.medium.image)
				{
					panel.addContent('<img src="'+this.portal.path+'media.srv?id='+task.medium.image.id+'&resize=240x" style="margin-top:4px;">');
				}
				if (task.mediasubmit)
				{
					for (type in task.mediasubmit) break; //pick first type for now //->CHANGE LATER to multi-type
					var mediasubmit_type = type;
					var mediasubmit_amount = task.mediasubmit[type];

					var str = '<br><b>requires '+mediasubmit_amount+' ';
						str+= (mediasubmit_amount>1 && mediasubmit_type!='audio')? mediasubmit_type+'s':mediasubmit_type;
						str+= '</b>';
						
					if (place.state=='play') str+= '<div class="line"></div>'; //media dropzones
					
					panel.addContent(str);
				}
				
				//quiz
				var panel = place.pane.panels['quiz'];
				if (task.quiz)
				{
					var answerlist = (task.quiz.multichoice)? task.quiz.answerlist:undefined;
					panel.setContent(this.portal.gui.create( { id:'playback_place_quiz', question:task.quiz.question, answerlist:answerlist } ) );
				}
				else
				{
					place.pane.removePanel(panel.id);
				}
				
				//rewards
				var panel = place.pane.panels['rewards'];
				panel.setContent('maximum score: '+task.score);
				break;

			case 'text':
				var panel = place.pane.panels['text'];
				panel.setContent(place.content.text)
				break;
				
			case 'audio':
			case 'video':
			case 'image':
				var panel = place.pane.panels[place.type];
				
				if (place.type=='image')
				{
					panel.setContent('<img src="'+this.portal.path+'media.srv?id='+place.content.image.id+'&resize=200x" style="margin-top:4px;">');
				}
				else
				{
					var id = place.content[place.type].id;
					var w = 200;
					var h = (place.type=='video')? 150:20;
									
					//clear and add new swfobject
					var display = panel.content;
					if (display.lastChild) display.removeChild(display.lastChild);
					
					this.portal.gui.embedMedium(display,{type:place.type, id:id},w,h);
				}

				var panel = place.pane.panels['description'];
				var desc = place.meta.description || '';
				panel.setContent(desc);
				//panel.toggleExpand();
				break;
				
			case 'trade':
				var panel = place.pane.panels['description'];
				var desc = place.meta.description || '';
				panel.setContent(desc);

				var panel = place.pane.panels['trade'];
				if (place.meta.peerid) panel.setContent( this.portal.gui.create( { id:'playback_place_trade', peername:place.scene.places[place.meta.peerid].name, peerid:place.meta.peerid } ) );
				
				break;

				
			case 'reward':
				if (place.meta.rewards)
				{
					dbg.msg('playbackPlaceData: rewards=',dbg.obj(place.meta.rewards,1));

					var list = this.meta.objectlist || this.meta.skilllist;
					if (!list) break;
					
					var l = place.meta.rewards.objectlist || place.meta.rewards.skilllist;
					var reward = l[0];
					
					//find rewarded object/skill in scene meta
					for (var i=0; i<list.length; i++)
					{
						dbg.msg('list, found key=',list[i].key);
						if (list[i].key==reward.key) break;
					}
					
					var str = '';
						str+= reward.amount+' "'+list[i].name+'"<br>';
						if (list[i].image) str+= '<img src="'+this.portal.path+'media.srv?id='+list[i].image.id+'&resize=200x" style="margin-top:4px;">';
					
					place.pane.panels['reward'].setContent(str);
				}
				break;
				
			case 'ugc':	
				var panel = place.pane.panels[place.ugctype];
				switch (place.ugctype)
				{
					case 'text':
						panel.setContent(place.content.text);
						break;
					
					case 'image':
						panel.setContent('<img src="'+this.portal.path+'media.srv?id='+place.content.image.id+'&resize=200x" style="margin-top:4px;">');
						break;
						
					default:
						//->only user generate notes and images are supported at this moment
				}
				break;
		}
	}


	/*	event playback object
	*/
	function Round(scene,id)
	{
		this.scene = scene;
		this.id = id;
		this.event = scene.eventslist[id];
		this.traces = new Plays(scene);

		//main playback object for this event (combined log playback)
		this.play = this.traces.create({
			id:'event',
			roundid:this.id,
			mode:'playback'
		})
		this.play.traces = this.traces; //for players reset
		
		//create widget
		this.widget = scene.gui.createEventWidget(this);
		this.play.widget = this.widget;
		this.play.eventsdiv = this.widget.find('.trace-events-list');
		
		//update traces header
		this.widget.find('.event-traces-header span:first').html(this.scene.isgame? 'team':'player	')
			.next().html('rewards')
			.next().html('score');
		
		//add slider
		this.play.createSlider();
		//this.widget.find('.trace-control').css('margin-top',18);
		
		
		//overrule getEvents function for this play
		this.play.getEvents = function() {
			dbg.msg('Play: getEvents, type=Round, round=',this.round);
			GW.PLAY.getRoundEventList(this,this.round,true);
 		}
	}
	
	Round.prototype.addTraces = function(records)
	{
		//update count display
		this.widget.find('.event-traces-count').html(records.length+' trace'+(records.length>1? 's':''));	

		//add play object for each trace in event
		for (var i=0; i<records.length; i++)
		{
			var id = records[i].getField('id');
			this.scene.addTrace(this.traces,this.scene.traceslist[id]);
			//trace find toggle
			this.traces[id].player.toggle = $j('#trace-target-'+id);
		}
	}
	
	Round.prototype.load = function()
	{
		this.play.getEvents();
	}
	
	Round.prototype.dispose = function()
	{
		$j('#layer-widget-events .widget-header').trigger('collapse',[false]);
		this.traces.disposeAll();
		this.widget.remove();
	}

	
	/*	plays collection
	*/
	function Plays(scene)
	{
		this.create = function(properties)
		{
			if (!properties.id) return;
			
			var play = new Play(properties);
			//add to collection
			this[properties.id] = play;
			
			return play;
		}
		
		this.disposeAll = function()
		{
			for (var id in this)
			{
				if (typeof(this[id])!='object') continue;
				this.dispose(id);
			}
		}
		
		this.resetAll = function()
		{
			for (var id in this)
			{
				if (typeof(this[id])=='object') this[id].rset();
			}
		}
		
		this.dispose = function(id)
		{
			if (!this[id]) return;
			this[id].dispose();
			delete this[id];
		}

		/*	play(back) object
		*/
		function Play(p)
		{
			this.scene = scene;
			
			//dbg.msg('Play, scene=',dbg.obj(scene));
			
			this.id = p.id;
			this.name = p.name;
			this.is_team = p.is_team;
			this.users = p.users;
			this.color = p.color;
			
			this.round = p.roundid;
			this.roundname = p.roundname;

			this.mode = p.mode;			
 			this.live = (p.mode!='playback');
			
			this.state = p.state;
			this.date = p.date;

			this.enabled = (this.state!='scheduled');
			this.showstats = false;
			this.is_playing = false;
			this.minimized = false;
			this.playing = false;
			this.speed = 240; //200; //smaller = faster;
			
			this.trace = false;
			this.trace_tolerance = 0.00006; //margin (degrees lat/lon) to skip adding points to trace
			this.trace_min_distance = 10; //meters
			this.trace_max_distance = 100; //meters
			
			this.step = 0;
			
			//setup player
			if (p.id!='event')
			{
				dbg.msg('==create player==')
			
				this.player = scene.players.create( {
					id:this.id,
					team:this.name,
					color:this.color,
					user:this.user
				} );
			}

			this.skipped = 0;
		}
		
		Play.prototype.createSlider = function()
		{
			var obj = this;
			
			var container = document.createElement('div');
				container.style.position = 'absolute';
				container.style.left = (this.mode=='playback')? '25px':'25px';
				container.style.top = '1px';
				container.style.width ='241px';
				container.style.height = '14px';
				if (!browser.cssfilter) container.style.backgroundImage = 'url(media/slider_bg-.png)';
				//else container.style.filter = utils.pngBgImage('slider_bg-').substr(7);
				else
				{
					//extra elm for IE, bug with layering pngs
					var img = document.createElement('img');
						img.style.position = 'absolute';
						img.style.left = '0px';
						img.style.top = '0px';
						img.style.width = '241px';
						img.style.height = '14px';
						img.onload = function() { utils.pngFix(this) }; //ie png fix
						img.src = 'media/slider_bg-.png';
					container.appendChild(img);
				}
				
			var play = document.createElement('img');
				play.style.position = 'absolute';
				play.style.left = '0px';
				play.style.top = '0px';
				play.style.width = '14px';
				play.style.height = '14px';
				if (browser.cssfilter) play.onload = function() { utils.pngFix(this) }; //ie png fix
				var src = (this.mode=='playback')? 'play':'pause';
				play.src = 'media/slider_'+src+'.png';
			//event handling
			if (this.mode=='playback')
			{
				play.style.cursor = 'pointer';
				var obj = this;
				play.onmouseover = function() { utils.over(this,1) }
				play.onmouseout = function() { utils.over(this,0) }
				play.onclick = function() { obj.startstop() }
			}
			
			container.appendChild(play);

			var handle = document.createElement('img');
				handle.style.position = 'absolute';
				handle.style.left = '14px';
				handle.style.top = '1px';
				handle.style.width = '25px';
				handle.style.height = '12px';
				//handle.style.cursor = 'pointer';
				if (browser.cssfilter) handle.onload = function() { utils.pngFix(this) }; //ie png fix
				handle.src = 'media/slider_handle.png';
			//event handling

			handle.onmouseover = function() { if (obj.slider.enabled) utils.over(this,1) }
			handle.onmouseout = function() { utils.over(this,0) }
			
			container.appendChild(handle);
			
			var drag = makeDragableItem(handle);
				drag.setRange('x',true,0,200);
				drag.appearance = function(d) { } //?
				drag.ondragstart = function()
				{
					if (obj.playing) //stop playing
					{
						this.resume = true;
						obj.startstop()
					}
				}
				drag.dragging = function()
				{
					//update events
					if (!obj.events) return;
					var index = Math.floor((this.x-14)*(obj.events.length/200));
					obj.playback(index);
				}
				drag.drop = function()
				{
					if (this.resume) //restart playing
					{
						this.resume = false;
						obj.startstop();
					}
				}
			if (this.mode!='play')
			{
				drag.setEnabled(true,true); //x-only dragging
				handle.style.cursor = 'pointer';
			}
			else drag.setEnabled(false); //dragging will be enabled after first (live)event

			//append to widget
			var control = $j(this.widget).find('.trace-control').append(container);
			
			if (this.mode=='playback')
			{
				//reset button
				$j('<div class="button"><a href="javascript://reset" style="width:14px; background-image:url(media/button/trace_reset.png)"></a>')
					.css({ left:7, width:14, top:1 })
					.appendTo(control)
					.find('a').click(function() {
						obj.rset();
						this.blur();
					});
			}
			
			this.playbutton = play;
			this.slider = drag;
		}
		
		/*	event load/handling
		*/
		Play.prototype.load = function()
		{
			/*	load playback: 
				(create and) show playback widget */
				
			dbg.msg('Play[',this.id,']:load..');
				
			var obj = this;
			
			if (!this.widget)
			{
				this.widget = this.scene.gui.createTraceWidget(this);
				this.widget.css('margin-bottom',15);
				this.eventsdiv = this.widget.find('.trace-events-list');
				
				this.player.toggle = this.widget.find('.trace-follow .button');

				switch (this.mode)
				{
					/*	play/playback: add slider, and playback controls (not in play mode)
					*/
					case 'playback':
					case 'play':
						this.createSlider();
						this.getEvents();

						break;
					
					/*	play: slider without playback ctls
					*/
// 					case 'play':
// 						return; //->NEEDS UPDATE
// 						
// // 						this.createSlider();
// // 						this.getEvents(); //playback history till now
//  
//  						
//  						break;

					/*	live: no playback controls, no slider
					*/
					case 'live':
					
						//add live indication to eventslist
						var msg = Locale7.get('scene','playback_live');
						$j('<div></div>')
							.addClass('trace-event')
							.html('<span class="event">'+msg+'</span>')
							.appendTo(this.eventsdiv)
							.parent().css('visibility','visible');
						this.eventsdiv.css('top',-(this.eventsdiv.children(':last').position().top)-1);

						break;
				}
			}
			
			this.widget.show();
			if (!this.live) this.rset();
		}

		Play.prototype.close = function()
		{
			/*	disable player, hide trace widget
				but do not unload events */
			
			this.widget.hide();
 			this.rset();
 			
 			//in live mode: do dispose entirely
 			if (this.mode=='live')
 			{
 				this.scene.traces.dispose(this.id);
 			}
		}
		
		Play.prototype.minimize = function()
		{
			/*	collapse/expand trace widget
			*/
			this.minimized = !this.minimized;
			if (this.mode!='live') this.widget.find('.trace-control')[this.minimized? 'hide':'show']();
			this.widget.find('.widget-body-content').css('height',this.minimized? 30:100);
			this.widget.find('a[href*="minimize"]').css('background-image',this.minimized? 'url(media/button/maximize_pane.png)':'url(media/button/minimize_pane.png)');
		}
		
		Play.prototype.toggleStats = function(elm,expand)
		{
			/*	show/hide stats display, used in event widget traces
			*/
			
			this.showstats = expand==undefined? !this.showstats:expand;
			
			elm.css('height',this.showstats? 'auto':20);
			elm.find('.event-trace-stats')[this.showstats? 'show':'hide']();

			//get stats at first show	
			if (this.showstats && !this.stats) this.getStats(elm);
		}
		
		Play.prototype.getStats = function(elm,records)
		{
			if (!records)
			{
				dbg.msg('Play['+this.id+']: getStats()');
				var obj = this;
				GW.QUERY.queryStore('q-play-stats',function(records) { obj.getStats(elm,records) },'id',this.id);
				return;
			}

			if (records.length==0) return;
			
			//parse play stats
			this.stats = this.scene.portal.parseXML(records[0].xml);
			
			dbg.msg('Play(',this.id,'), stats=',dbg.obj(this.stats));
			
			$j('<div></div>')
				.addClass('event-trace-stats')
				.html(this.scene.gui.create({ id:'event_trace_stats', stats:this.stats }))
				.appendTo(elm)
				.after('<div class="clear"></div>');
				
		}
	
		Play.prototype.getEvents = function(rsp)
		{
			/*	get and parse events
			*/
			GW.PLAY.getEventList(this,this.id);
		}
		
		Play.prototype.getEventListRsp = function(rsp)
		{
			/*	parse event list
			*/
			
			//reset
			this.events = new Array();
			this.display_events = new Array();
			this.first_display = -1;
			this.last_display = -1;

			var events = rsp.getElementsByTagName('eventlist')[0].childNodes;
			
 			dbg.msg('Play['+this.id+']: got eventlist, eventcount=',events.length,', start parsing..');
 			dbg.tmr();
			
			for (var i=0; i<events.length; i++)
			{
				var event = this.parseEvent(events[i],i);
				if (event) this.events.push(event);
			}

			dbg.msg('Play['+this.id+']: eventlist done (count='+this.events.length+')','tmr');
			
			this.eventsdiv.css('visibility','hidden');
			
			//append to eventlist div
			for (var i=this.events.length-1; i>=0; i--)
			{
				if (this.events[i].display)
				{
					this.eventsdiv.append(this.events[i].display);
				}
			}
			
			//add ready or play indication, and reveal list
			var msg = Locale7.get('scene',this.mode=='playback'? 'playback_ready':'playback_live');
			
			$j('<div></div>')
				.addClass('trace-event')
				.html('<span class="event">'+msg+'</span>')
				.appendTo(this.eventsdiv)
				.parent().css('visibility','visible');
			
				
			//play/live specific handling				
			if (this.mode=='play')
			{
				//enable scroll
				this.slider.setEnabled(true,true);
				this.slider.elm.style.cursor = 'pointer';
				
				//player icon to last location
				this.player.updateToLastLocation();
				
				//jump to last event
				this.playback(this.events.length-1);
				
				//scrollbar to end
				var step = this.events.length;
				var x = 14 + (step/(this.events.length/200));
				this.slider.setPosition(x,1,true);
			}
			else
			{
				this.rset();
			}
			
			dbg.msg('Play['+this.id+']: skipped points=',this.skipped);
		}

		Play.prototype.getEventListNegRsp = function(errorId,error,details,tagname)
		{
			dbg.msg('Play: getEvents NegRsp, error=',error,', details=',details);
			alert('Error!\n\nCould not get playback:\n'+details);
			
			this.widget.remove();
			delete this.widget;
		}
		
		Play.prototype.addLiveEvent = function(eventelm)
		{
			/*	parse and add live event
			*/
			
			var myteam = (this.scene.webplay && this.scene.webplay.playid==this.id)? ' =myteam':'';
			dbg.msg('Play: addLiveEvent(',eventelm.tagName,')',myteam);

			if (!this.events)
			{
				//dbg.msg('Play: live, init events array');
			
				this.events = new Array();
				this.display_events = new Array();
				this.first_display = -1;
				this.last_display = -1;
				
				//enable scroll for play
				if (this.mode=='play')
				{
					this.slider.setEnabled(true,true);
					this.slider.elm.style.cursor = 'pointer';
				}
			}

			var event = this.parseEvent(eventelm,this.events.length);
			if (event) this.events.push(event);
			if (event.display)
			{
				this.eventsdiv.prepend(event.display);
				//scroll into view
				this.displayEvent(this.events.length);
			}
			
			dbg.msg('added event, count=',this.events.length);
			
			if (event.action) event.action();
			if (event.liveAction) event.liveAction(); //actions that are not executed in playback
			
			//update scrollbar in play mode
			if (this.mode=='play')
			{
				var step = this.events.length;
				var x = 14 + (step/(this.events.length/200));
				this.slider.setPosition(x,1,true);
				//show last event
				this.displayEvent(this.events.length);
				
			}
		}
		
		Play.prototype.parseEvent = function(event,index)
		{
			/*	parse event xml element to event object,
				poperties:
					eventobj.timestamp	- timestamp of event (required)
					eventobj.msg		- message to display in playback pane (optional)
					eventobj.display	- DOM elm for display in events list (created when msg)
					eventobj.action		- function to execute (optional)
					eventobj.liveAction - function to execute in live mode (play or live, optional)
					eventobj.link		- function to execute when clicked on event (optional) */
	
			var eventobj = new Object();
				eventobj.timestamp = Number(event.getAttribute('time'));
				eventobj.tag = event.tagName;

			//dbg.msg('Play['+this.id+']: parse event, index=',index,', tag=',event.tagName);

			var obj = this;
			//var team = 'team '+this.team;
			var team = '';
				
			switch (eventobj.tag)
			{
				//<play-enter-ind user="just" team="crimson" time="1204234913772"/>
				case 'play-enter-ind':
					eventobj.msg = '<span>'+event.getAttribute('user')+' started playing</span>';
					break;

				case 'play-exit-ind':
					eventobj.msg = '<span>'+event.getAttribute('user')+' stopped playing</span>';
					break;
				
				//<play-location-ind user="just" team="crimson" lon="4.85347167" lat="52.31189167" time="1204234915195"/>
				//<play-location-ind user="use1" playid="39265" team="user-use1" lon="4.92339850" lat="52.37686650" time="1250541872450" speed="10.95" course="107"/>
				case 'play-location-ind':

					//dbg.msg('Play: parseEvent, event=',eventobj.tag);
					//dbg.msg('parse: trace=',this.player.trace.points);
					if (this.id=='event')
					{
						//get player from event trace
						var playid = event.getAttribute('playid');
						var player = this.scene.selectedEvent.traces[playid].player;
					}
					else
					{
						var player = this.player;
					}
					
					var lat = event.getAttribute('lat');
					var lon = event.getAttribute('lon');
					var prev_point = player.trace.points.length>1? player.trace.points[player.trace.points.length-1]:undefined;
					var new_point = new google.maps.LatLng(lat,lon);
					
					//skipp or add point
					if (prev_point!=undefined)
					{
						//dbg.msg('distance to previous = ',new_point.distanceFrom(prev_point));
					
						if (new_point.distanceFrom(prev_point)<this.trace_min_distance)
						{
							this.skipped++; //do not add to events collection
							return false;
						}
						
						if (new_point.distanceFrom(prev_point)>this.trace_max_distance) //-> NEEDS implemenation of Trace class
						{
							dbg.msg('--add new trace segment (future version)');
						}
					}

					//add to player trace, attach player update action
					player.trace.points.push(new_point);
					eventobj.index = player.trace.points.length;
					eventobj.action = function(step) 
					{
						//dbg.msg('Play: update loc, index=',this.index);
						
						player.playback = this.index;
						player.updateTrace();
						player.updateLocation(lat,lon,eventobj.timestamp,step) 
					}
					break;
			
				//<play-place-enter-ind id="6228" user="just" team="crimson" result="none" type="text" state="hit" time="1204234923801"/>
				case 'play-place-enter-ind':
					var id = event.getAttribute('id');
					var state = event.getAttribute('state');
					eventobj.msg = '<img src="media/icon_marker.png" style="height:16px; vertical-align:middle; margin-right:5px;">';
					var user = this.id=='event'? 'team '+event.getAttribute('team')+' ':'';
					eventobj.msg+= '<span>'+user+'hit place "'+this.scene.places[id].name+'"</span>';
					
					if (this.mode!='play')
					{
						eventobj.action = function() { obj.scene.places[id].hit() };
					}
					eventobj.link = function() { obj.scene.places[id].expand() };
					break;
					
				//<play-place-done-ind id="6228" user="just" team="crimson" type="text" result="ok" state="done" time="1204234925063"/>
				case 'play-place-done-ind':
					//eventobj.msg = '<span>place done, result:'+event.getAttribute('result')+'</span>';
					break;

				//<play-place-open-ind placeid="6233" time="1204234925073" user="just"/>
				case 'play-place-open-ind': //secrettrail
					//eventobj.msg = '<span>place '+event.getAttribute('placeid')+' is now available</span>';
					break;

				//<play-place-inside-ind id="6228" user="just" team="crimson" result="ok" type="text" state="done" time="1204234928672"/>
				case 'play-place-inside-ind':
					break;

				//<play-place-exit-ind id="6228" user="just" team="crimson" type="text" state="done" time="1204234940998"/>
				case 'play-place-exit-ind':
					break;

				//<play-answer-task-ind placeid="6233" answer="daan en isabel" result="ok" time="1204234953660" user="just"/>	
				case 'play-answer-task-ind':
					var user = this.id=='event'? 'team '+event.getAttribute('team')+' ':'';
					eventobj.msg = '<span>'+user+'answered "'+event.getAttribute('answer')+'",</span><br>';
					var result = event.getAttribute('result');
					
					if (result=='ok' || result=='hinted') eventobj.msg+= '<span style="color:#00dd00">correct answer!<span>';
					else
					{
						eventobj.msg+= '<span style="color:#dd0000">wrong answer!<span>';
					}
					if (result=='hinted') eventobj.msg+= ' but a hint was given';
					break;

				//<play-skip-task-ind placeid="37165" user="use1" team="us" playid="37307" time="1243442279997"/>
				case 'play-skip-task-ind':
					var id = event.getAttribute('placeid');
					var user = this.id=='event'? 'team '+event.getAttribute('team')+' ':'';
					eventobj.msg = '<span>'+user+'skipped task "'+this.scene.places[id].name+'"</span>';
					eventobj.link = function() { obj.scene.places[id].expand() };
					break;

				//<play-add-medium-ind id="7258" type="image" user="just" team="crimson" lon="4.85344667" lat="52.31189167" placeid="6233" time="1204234989863"/>
				case 'play-add-medium-ind':
					var type = event.getAttribute('type');
					if (type=='plain') type = 'text';
					var medium = (type=='image')? 'an image':(type=='text')? 'a note':'a '+type;
					var id = event.getAttribute('id');
					var user = this.id=='event'? 'team '+event.getAttribute('team'):event.getAttribute('user');
					eventobj.msg = '<img src="media/icon_type_'+type+'.png" style="vertical-align:middle; margin-right:2px; margin-left:-3px"><span>'+user+' uploaded '+medium+'..</span><br>';
					if (type!='text')
					{
						eventobj.msg+= '<img src="'+this.scene.portal.path+'media.srv?id='+id+'&resize=170x&format=jpg" style="width:170px; margin-top:2px">';
						var content = id;
					}
					else
					{
						//get text from media servlet via synchronous request. //->NOTE: replace when better service is available
						var req = new XMLHttpRequest();
							req.open('GET',this.scene.portal.path+'media.srv?id='+id,false);
							req.send(null);
						eventobj.msg+= req.responseText;
						var content = req.responseText;
					}
					
					//submitted to task? add link to place
					var placeid = event.getAttribute('placeid');
					if (placeid && this.scene.places)
					{
						var p = this.scene.places[placeid];
						if (p.type=='task' && p.content.task.mediasubmit && event.getAttribute('type')=='image')
						{
							eventobj.link = function() { obj.scene.places[placeid].expand() };
						}
						else
						{
							//add to map as UGC-place
							this.parseUGC(eventobj,event,content);
						}
					}
					else
					{
						//add to map as UGC-place
						this.parseUGC(eventobj,event,content);
					}
					break;

				case 'play-message-ind':
					if (event.getAttribute('islocal'))
					{
						var msg = event.msg;
					}
					else
					{
						var type = event.firstChild.tagName;
						if (type=='text') var msg = event.firstChild.firstChild ? event.firstChild.firstChild.nodeValue:'';
						else var msg = '-media msg-'; //add later
					}
					eventobj.msg = '<img src="media/icon_messaging.png" style="vertical-align:middle; margin-right:3px; margin-left:-3px"><span>'+event.getAttribute('user')+': </span>'+msg;
					if (this.mode=='play') eventobj.link = function() { obj.scene.messaging() }
					break;
			}
			
			//additional (web)play events are parsed in scenes.play.js
			if (this.mode=='play')
			{
				eventobj = this.scene.parsePlayEvent(event,index,eventobj);
			}
			
			//create and add event list elm
			if (eventobj.msg)
			{
				eventobj.display = this.scene.portal.gui.createTraceEvent(eventobj);

				//store first and last display index
				if (this.first_display==-1) this.first_display = this.events.length;
				this.last_display = this.events.length;
				
				//hilight function
				eventobj.setSelected = function(select)
				{
					this.selected = select;
					this.display.find('span:first')
						.css('font-weight',select? 'bold':'normal')
						.end().css('color',select? '#ffffff':'#b4b4b4');
				}

				//onload handler
 				if (eventobj.tag=='play-add-medium-ind' && type=='image' && !this.live) //loading images changes height of eventsdiv, reset
 				{
 					eventobj.display.find('img:last').load(function() {
 						obj.eventsdiv.css('top',-(obj.eventsdiv.children(':last').position().top)-1);
 					});
 				}
			}
			
			return eventobj;
		}
		
		Play.prototype.parseUGC = function(eventobj,event,content)
		{
			/*	add medium as UGC-place to map (via Player)
			*/
			var placeid = 'ugc_'+event.getAttribute('time');
			var player = this.player || this.scene.players[event.getAttribute('playid')];

			player.addUGC({
				id:placeid,
				type:event.getAttribute('type'),
				lat:event.getAttribute('lat'),
				lon:event.getAttribute('lon'),
				content:content,
				user:this.scene.isgame? event.getAttribute('team'):event.getAttribute('user')
			});
			
			eventobj.link = function() { player.ugcplaces[placeid].expand() };
// 			eventobj.ugc = function(hide) {
// 				var place = player.ugcplaces[placeid];
// 				place.showMarker(hide);
// 			}
			
//			return function() { player.ugcplaces[placeid].expand() };
		}
		
		Play.prototype.showUGC = function(time,place)
		{
			/*	show or hide user generated place, related to this play
			*/
			
			//hide marker if current time is before ugc timestamp
			var hide = time<Number(place.id.substr(4));
						
			if (hide && place.expanded) place.collapse();
			place.showMarker(hide);
		}
	
		Play.prototype.startstop = function(play)
		{
			this.is_playing = (play!=undefined)? play:!this.is_playing;
			
			dbg.msg('Play['+this.id+']: this.is_playing=',this.is_playing);
			
			if (this.is_playing)
			{
				//start playback
				this.playback();
				this.playbutton.src = 'media/slider_pause.png';
			}
			else 
			{
				//stop playback
				if (this.playing) window.clearTimeout(this.playing);
				if (this.player) this.player.update();
				this.playing = false;
				this.playbutton.src = 'media/slider_play.png';
			}
		}

		Play.prototype.playback = function(step)
		{
			/*	playback events from list
			*/

			if (step && this.step==step) return; //no update needed
			if (!this.events) return;
			if ((this.step>=this.events.length && !step) || step>=this.events.length)
			{
				//end of playback, update ctls
				if (this.playing) this.startstop();
				return;
			}
			
			if (step)
			{
				var mode = step>this.step? 'forward':'backward';
				this.step = step;
			}
			else
			{
				var mode = 'forward';
				//update slider
				//Math.floor((this.x-14)*(obj.events.length/200));
				var x = 14 + (this.step/(this.events.length/200));
				this.slider.setPosition(x,1,true);
			}

			this.displayEvent(this.step,mode);

			var event = this.events[this.step];

			if (event && event.action) event.action(step);
			
			//continue playback
			if (step==undefined && !this.live)
			{
				this.step++;
				
				var obj = this;
				this.playing = window.setTimeout(function() { obj.playback()},this.speed)
			}
		}
		
		Play.prototype.displayEvent = function(index,mode)
		{
			/*	jump to display event nearest to index
			*/
			if (this.first_display==-1)	return; //no events to display
			
			if (index>=this.last_display) index = this.last_display;
			else if (index<=this.first_display) index = this.first_display;
			else
			{
				if (!this.events[index].display)
				{
					//get nearest event with display backwards
					for (var i=index; i>=this.first_display; i--) if (this.events[i].display) break;
					index = i;
				}
			}
			this.eventsdiv.css('top',-(this.events[index].display.position().top)-1)
			this.selectEvent(index,mode);
			
			var timestamp = this.events[index].timestamp;
			
			/*	user generated content on map
			*/
			var obj = this;
			if (this.player)
			{
				$j.each(this.player.ugcplaces, function(n, place) {
					if (typeof(place)!='object') return true;

					obj.showUGC(timestamp,place);

				});
			}
			else if (this.id=='event')
			{
				$j.each(this.scene.players, function(i,player) {
					if (!player.active) return true;
					
	//				dbg.msg('check ugc for player=',player.id);
					
					$j.each(player.ugcplaces, function(n, place) {
						if (typeof(place)!='object') return true;
						
						obj.showUGC(timestamp,place);
						
						
	// 					dbg.msg('ugcplace, id=',place.id,', time=',timestamp);
						
					
					
					});
				});
			}
// 			for (var id in this.scene.players)
// 			{
// 				var player = this.scene.player[id];
// 				if (player.active && this.scene
// 			
// 			}
			
		}
		
		Play.prototype.selectEvent = function(index,mode)
		{
			/*	show event header in bold, reset others
			*/
			
			//dbg.msg('select event, mode=',mode);
			
			this.events[index].setSelected(true);
			
			for (var i=index-1; i>=this.first_display; i--)
			{
				if (this.events[i].selected) this.events[i].setSelected(false);
			}
			
			this.eventlink = this.events[index].link;
			this.widget.find('.trace-events img:last')
				.css('cursor',this.eventlink? 'pointer':'auto')
				.attr('src','media/trace_event_select.png');


			//ugc display
// 			if (this.events[index].ugc)
// 			{
// 				this.events[index].ugc(mode=='backward');
// 			}
				
				

			//this.eventdiv.style.cursor = (this.eventlink)? 'pointer':'auto';
			//if (!this.eventlink) utils.over(this.eventdiv,0);
		}

		Play.prototype.rset = function()
		{
			dbg.msg('Play['+this.id+']: rset()');
		
			if (this.is_playing && !this.live) this.startstop(false);
			
			this.step = 0;
			
			//reset live playback
			if (this.live)
			{
				delete this.events;
				this.player.rset();
			}
			
			if (this.player)
			{
				this.player.enable(false,true);
				this.player.playback = 0;
				this.player.lat = this.player.lon = undefined;
			}
			
			if (this.id=='event')
			{
				//reset players for all traces in event
				for (var id in this.traces)
				{
					if (typeof(this.traces[id])!='object' || id=='event') continue;
					var player = this.traces[id].player;
						player.enable(false,true);
						player.playback = 0;
						player.lat = player.lon = undefined; //force update on first point
				}
			}
			
			if (this.slider) this.slider.setPosition(14,1,true);
			if (this.eventsdiv.children().length)
			{
				this.eventsdiv.css('top',-(this.eventsdiv.children(':last').position().top)-1);
			}
		}

		Play.prototype.dispose = function()
		{
			//stop playing
			if (this.playing) window.clearTimeout(this.playing);
		
//			if (this.pane) scene.portal.layout.panes.dispose('playback_'+this.id);
			if (this.widget) this.widget.remove();
			
			//remove player
			if (this.player) this.scene.players.dispose(this.player.id);
			
			if (this.mode=='live')
			{
				//unsubscribe from event
				GW.EVENT.unsubscribe(function(){ dbg.msg('Live: unsubcribed..') },'/live/'+this.scene.id+'/'+this.round+'/'+this.id);
			}
			
		}
	}

}

//callback for IE (doesn't support script onload event)
//if (utils.loadScriptCallback['Scenes_playback']) utils.loadScriptCallback['Scenes_playback']();

/* [file src="Projects.js"]
*/
/*	usemedia.com . joes koppers . 08.2008
	thnx for reading this code */


/*	7s Projects, grouped scenes + content page(s)
*/

function Projects7(portal)
{
	/*	project collection
	*/
	
	this.count = 0;

	this.create = function(properties)
	{
		if (!properties.id) return;
		
		var project = new Project(portal,this,properties);
		//add to collection
		this[properties.id] = project;
		this.count++;
		
		return project;
	}

	this.select = function(id)
	{
		/*	(un)select current project
		*/
		dbg.msg('Projects: select(',id,')');

		if (!id)
		{
			delete portal.project;
			delete this.project;
		}
		else
		{
			portal.project = this[id];
			this.project = this[id];
		}
	}
	
	this.getProjectByName = function(name)
	{
		/*	return project obj by name
		*/
		for (var id in this)
		{
			if (typeof(this[id])=='object' && this[id].name==name) return this[id];
		}
		return false;
	}

	this.dispose = function(id)
	{
		/*	remove a scene obj
		*/
		dbg.msg('dispose sceneobj, id=',id);
		
		var deleted_index = this[id].index;
		delete this[id];
		this.count--;
		
		//update indexes..
		for (var id in this)
		{
			if (typeof(this[id])!='object') continue;
			if (this[id].index>deleted_index) this[id].index--;
		}
	}
	
	this.clear = function()
	{
		/*	remove all objects from collection
		*/
		for (var id in this)
		{
			if (typeof(this[id])=='object') delete this[id];
		}
		this.count = 0;
	}


	/*	portal project object
	*/

	function Project(portal,collection,p)
	{
		/*	p = properties object, 
				values: name, [desription]	*/
		
		this.portal = portal;
		this.collection = collection;
		this.layer = portal.layout.layers['project'];
		this.id = p.id;
		
		this.name = p.name;
		this.description = p.description || '';
	}
	
	Project.prototype.getDefaults = function(type,id)
	{
		/*	return defaults: 
			default content structure, panel style, panel contents */
	
		switch(type)
		{
			case 'content':
				/*	XML sample
				<content>
					<tablist>
						<tab>
							<layout>home</layout>
							<name>home</name>
							<panel>
								<type>about</type>
								<style></style>
								<options></options>
								<name></name>
								<content></content>
							</panel>
							<panel>
								<type>list</type>
								<style></style>
								<options></options>
								<name></name>
								<content></content>
							</panel>
							<panel>
								<type>media</type>
								<style></style>
								<options></options>
								<name></name>
								<content></content>
							</panel>
							<panel>
								<type>feed</type>
								<style></style>
								<options></options>
								<name></name>
								<content></content>
							</panel>
						</tab>
					</tablist>
					<mediumlist>
						<medium id="" name="">
					</medialist>
				</content> 	*/
		
				return {
					tablist: [ {
						index:0,
						layout:'home',
						name:'home',
						panellist: [ 
							{ type:'about', edit:true, style:null, options:null, name:null, content:{ title:null, text1:null, text2:null } },
							//{ type:'scenes', style:null, options:null, name:null, content:null },
							{ type:'feed', style:null, options:null, name:null, content:null },
							{ type:'media', edit:true, style:null, options:null, name:null, content:null }
						] 
					} ],
					mediumlist: [ ]
// 						{ id:'26284', name:'icon' }
// 						{ id:'22268', name:'something' }
// 					]
				};
				break;
				
			case 'panel':
				switch(id)
				{
					case 'about':
						return {
							style:{ width:'60%', marginTop:'15px', fontSize:'13px' },
							options:{ template:'paper' },
							//options: { },
							content:''
						};
// 					case 'scenes':
// 						return {
// 							//style:{ marginLeft:'20px', marginTop:'15px', width:'42%', height:'340px' },
// 							style:{ position:'absolute', left:'0px', top:'0px', right:'0px', height:'170px' },
// 							options:{},
// 							//options:{ can_be_resized:true },
// 							content:'-scenes-'
// 						};
					case 'feed':
						return {
							style:{ display:'none', marginTop:'20px', marginLeft:'20px', height:'122px', width:'42%' },
							options:{ can_be_resized:true },
							content:'-feed-'
						};
					case 'media':
						return {
							style:{ marginLeft:'15px', marginTop:'15px', width:'35%', minWidth:'300px' },
							options:{ },
							content:'-no media found-'
						};
				}
				break;
		}
	}
	
	Project.prototype.getMediumByName = function(name)
	{
		/*	return medium from content.mediumlist by name
		*/
		if (!this.content.mediumlist) return false;
		for (var i=0; i<this.content.mediumlist.length; i++) if (this.content.mediumlist[i].name==name) return { index:i, elm:this.content.mediumlist[i] };
	}

	Project.prototype.createContentTabs = function()
	{
		/*	create tabs
		*/
		if (this.content.tablist.length>1)
		{
			//add menu options

			//->FUTURE version
			//..
		}
	}
	
	Project.prototype.createContentPage = function(page)
	{
		/*	draw tab panels
		*/
		var tab = this.content.tablist[page];
		this.tab = tab;
		
		dbg.msg('Project: create content, tab=',tab.name);
		
		this.layer.updateBg();

		if (this.layer.content.childNodes)
		{
			//page not empty, clear first
			this.layer.clear();
			this.layer.panels.clear();
		}
		
		//shift content layer
		this.layer.content.style.top = '162px';
		
		//(re)create scenes listing
		if (page==0)
		{
			this.portal.gui.addProjectScenes(this);
			this.getScenes();			
		}

		//add panels		
		for (var i=0; i<tab.panellist.length; i++)
		{
			var p = tab.panellist[i];
			
			if (p.type=='scenes') continue; //->backwards compatibility, remove later
			
			var panel = this.layer.panels.create( {
				id:p.type,
				index:i,
				parent:this.layer.content,
				title:p.name,
				border:p.type=='media' ? { light:'transparent', dark:'transparent' }:undefined,
				style:p.style || this.getDefaults('panel',p.type).style,
				content:this.portal.gui.create( { id:'project_panel_'+p.type, project:this, content:p.content } )
			} );
			
			//options
			panel.addOptions( p.options || this.getDefaults('panel',p.type).options );
			
			//editable?
			panel.edit = p.edit;
			
			//dynamic content (media, feed, ...)
			switch (p.type)
			{
				case 'media':
					panel.content.style.marginTop = '-10px';
					//embed videos
					this.addEmbeddedMedia(panel);
					break;
					
				case 'feed':
					this.addFeed();
					break;
			}
		}
		
		//show tab edit buttons
		if (this.admin && this.enableEditing)
		{
			this.enableEditing();
		}
			
// 			if (this.enableEditing) this.enableEditing();
// 			else
// 			{
// 				//wait for script to be loaded
// 				var obj = this;
// 				var delayed = function()
// 				{
// 					dbg.msg('Project, enableEditing not avail, retry in 100ms..');
// 					if (obj.enableEditing) obj.enableEditing();
// 					else window.setTimeout(delayed,100);
// 				}
// 				delayed();
// 			}
//		}
	}
	
	Project.prototype.setHeaderBg = function(id)
	{
		/*	update header background of project home page
		*/
		dbg.msg('Project: setHeaderBg, id=',id);
		
		if (id==undefined)
		{
			var m = this.getMediumByName('header');
			if (m) id = m.elm.id;
		}

		var src = !id ? 'media/bg_project_header.jpg':this.portal.path+'media.srv?id='+id+'&cmd=header7';
		this.layer.sceneslist.lastChild.style.backgroundImage = 'url('+src+')';
	}
	
	Project.prototype.addEmbeddedMedia = function(panel)
	{
		/*	add videos to panel
		*/
		var elms = panel.content.getElementsByTagName('div');
		for (var i=0; i<elms.length; i++)
		{
			if (elms[i].className && elms[i].className=='project-media') 
			{
				var id = elms[i].innerHTML;
				elms[i].innerHTML = '';
				this.portal.gui.embedMedium(elms[i],{ type:'video', id:id },300,200);
			}
		}
	}
	
	Project.prototype.open = function()
	{
		/*	open project page, get contents
		*/
		dbg.msg('<span style="color:#999966">Project: open, id=',this.id,'</span>');
		
		//if (!this.portal.layout.layers['project']) this.portal.layout.createLayer('project');
		this.layer = this.portal.layout.layers['project'];
		this.layer.show();
		
		this.collection.select(this.id);
		
		
		//check user role for this project
		if (this.portal.login.id)
		{

			if (this.portal.login.roles['director'][this.id])
			{
				/*	we're project director, show direct button
				*/
				this.director = true;
				//this.layer.menubar['direct'].show();
			}


			if (this.portal.login.roles['admin'][this.id]) 
			{
				/*	we're project owner, enable admin features
				*/
				this.admin = true;

				if (this.collection.enableEditing)
				{
					this.collection.enableEditing();
				}
				else
				{
					//load the script first
					var obj = this;
					utils.loadScript('Projects.edit.js','Projects_edit',function(id) { 
						dbg.msg('[ script loaded, id=',id,' ]');
						obj.collection.enableEditing();
						obj.getInfo();
					});
					return;
				}
			}
		}
		
		//get contents
		this.getInfo();
	}
	
	Project.prototype.getInfo = function(records)
	{
		/*	project info, parse content xml
		*/
		if (!records)
		{
			var obj = this;
			GW.QUERY.queryStore('q-project-info', function(rsp) { obj.getInfo(rsp) },{ id:this.id });
			return;
		}

		//layer title
		this.layer.setTitle( this.portal.gui.create( { id:'projecttitle', title:this.name } ) + this.portal.gui.create( { id:'layertitle', title:'scenes', margin:'6px 0px 0px 0px'  } ) );
	
		var content = records[0].getXMLField('content');

		//parse to object
		if (content) this.content = this.portal.parseXML(content);
		else this.content = this.getDefaults('content');

		//dbg.msg('Project: content=',dbg.obj(this.content,3));
		
		//tabs and default page (home)
		this.createContentTabs();
		this.createContentPage(0);
	}

	Project.prototype.getScenes = function()
	{
		/*	get and list scenes for this project
		*/
		if (!this.portal.lists['project'])
		{
			//create list
			//var panel = this.layer.panels['scenes'];
		}
	
		var minstate = (this.admin)? 1:2; //->admin can see all scenes in project, remove when directors admin page is ready
	
		if (this.portal.mode=='test') minstate = 1; //overrule to show all in dev mode
	
		var query = { contextid:this.id, max:50, minstate:minstate };
		var list = this.portal.lists['project'];
		
		var callback;
		if (this.admin)
		{
			var obj = this.portal;
			callback = function() { dbg.msg('Project: enable all scenes for admin'); obj.projectscenes.enable(true) };
		}

		this.portal.getScenes(query,list,callback);
	}

	Project.prototype.addFeed = function()
	{
		/*	stats and activity feed
		*/
	
		//->todo
		//..
	}

	Project.prototype.close = function(reopen)
	{
		/*	unload project, close projectlayer
		*/
		
		dbg.msg('Project[',this.id,']: close');
		
		//this.layer.menubar['stats'].hide();
// 		this.layer.menubar['directors'].hide();
// 		this.layer.menubar['direct'].hide();
		
		
		if (this.portal.userlib) this.portal.userlib.hide();
		
		this.layer.clear();
		this.layer.panels.clear();
		if (!reopen) this.layer.hide();
		
		if (this.layer.sceneslist)
		{
			this.layer.div.removeChild(this.layer.sceneslist);
			delete this.layer.sceneslist;
			delete this.layer.extendUpdate['scenes_list'];
		}
		
		delete this.admin;
		delete this.director;
		delete this.panel_to_edit;
		
		//list
		delete this.portal.lists['projectscenes'];
		
		//unload edit features
		if (this.enableEditing)
		{
// 		this.layer.menubar['manage'].show();
			delete this.adminpage;

			if (this.portal.signupByAdmin) this.portal.signupByAdmin.hide();
			if (this.portal.userlib) this.portal.userlib.hide();
			
//			this.layer.showMenus(['manage']);
			this.layer.hideMenus(['manage','manage_done','add_director','add_director_x']);

		}
		
		//un-select project
		this.collection.select(); 
		//stop polling for live scenes
		this.portal.projectscenes.checkLive(undefined,true);
	}
}

/* [file src="Places.js"]
*/
/* 	usemedia.com . joes koppers . 01.2008
	thnx for reading this code */


/*	7s Places
	requires common/use.js */


function Places7(scene)
{
	/*	places collection
	*/

	this.create = function(properties)
	{
		if (!properties.id) return;
		var place = new Place(properties);
		//add to collection
		this[properties.id] = place;
		
		return place;
	}
	
	this.update = function()
	{
		/*	update all places
		*/
		for (var id in this) if (typeof(this[id])=='object') this[id].update();
	}

	this.showAll = function(hide)
	{
		/*	show or hide all places
		*/
		for (var id in this)
		{
			if (typeof(this[id])!='object') continue;
			if (hide && this[id].expanded) this[id].collapse();
			this[id].showMarker(hide);
		}
	}
		
	this.disposeAll = function()
	{
		/*	delete all places
		*/
		for (var id in this)
		{
			if (typeof(this[id])!='object') continue;
			this.dispose(id);
		}
	}
	
	this.getByType = function(type,exclude_id)
	{
		/*	return array of place with type
		*/
		var places = new Array();
		for (var id in this)
		{
			if (typeof(this[id])!='object') continue;
			if (this[id].type==type && id!=exclude_id) places.push(this[id]);
		}
		return places;
	}
			
	
	this.dispose = function(id)
	{
		/*	delete a place by id
		*/
		if (!this[id]) return;
		this[id].dispose();
		delete this[id];
	}
	
	this.setMapToBounds = function(offset)
	{
		/*	set map zoom/area to bounds of all places
		*/
		var locations = [];
		for (var id in this) if (typeof(this[id])=='object') locations.push(this[id].geo);
		
		if (locations.length==0) return;
		
		//get gmap reference from first place
		for (var id in this) if (typeof(this[id])=='object') { var gmap = this[id].gmap; break; }
		
		var bounds = new google.maps.Polyline(locations).getBounds();
		var zoom = gmap.getBoundsZoomLevel(bounds);
		
		//zoom--; //zoomout one level
		if (offset) zoom += offset;
		
		if (zoom!=gmap.getZoom()) gmap.setCenter(bounds.getCenter(),zoom);
		else
		{
			if (offset==undefined) gmap.panTo(bounds.getCenter());
			else gmap.setCenter(bounds.getCenter());
			this.update();
		}
	}
	

	/*	scene place object
	*/

	function Place(p)
	{
		/*	Place constructor

			p = properties object, 
			values: id,name,type,lat,lon,meta,content,enabled,radius(opt) */

		var obj = this;

		this.scene = scene; //scene object the place belong to (closure var)
		if (scene.portal.state.indexOf('direct')!=-1) this.state = 'direct';
		else this.state = scene.portal.state;
	
		this.id = p.id;
		this.name = p.name;
		this.type = p.type;
		this.ugctype = p.ugctype;
		this.tasktype = p.tasktype;
		this.lat = p.lat;
		this.lon = p.lon;
		this.radius = p.radius;

		this.geo = new google.maps.LatLng(p.lat,p.lon);
		this.gmap = p.gmap || scene.portal.layout.gmap;

		this.meta = p.meta || new Object();
		this.content = p.content || new Object();

		
		/*	marker 
		*/
		
		//marker default size
		this.s = 1;
		this.w = this.default_w = 40;
		this.h = this.default_h = 84;
		this.shw = 1.84; //scale factor of shadow in relation to marker
		this.shh = 0.65;

		var marker = document.createElement('img');
		var state = this.type=='trade' && this.meta.peerid? '_paired':'';
			marker.className = 'marker';
			marker.style.width = this.w +'px';
			marker.style.height = this.h +'px';
			//marker.style.border = '1px solid red';
			marker.style.zIndex = this.z = parseInt(this.lat * -100000);
			if (browser.cssfilter) marker.onload = function() { utils.pngFix(this) }; //ie png fix
			marker.src = 'media/marker_'+this.type+state+'.png';
		
		if (this.type=='none') marker.title = this.name;

		this.marker = marker;
		
		if (!p.noninteractive)
		{
			var markerevents = document.createElement('img'); //separate elm, so markers in shadow of place-pane can be clicked..
				markerevents.className = 'marker';
				markerevents.style.width = this.w +'px';
				markerevents.style.height = this.h +'px';
				markerevents.style.zIndex = this.z = parseInt(this.lat * -100000);
				markerevents.src = 'media/blank.gif';
				//event handling
	// 			markerevents.onmouseover = function() { obj.showInfo() }
	// 			markerevents.onmouseout = function() { obj.scene.portal.layout.panes['place_info'].hide() }
				markerevents.onclick = function() { obj.expand() }
				markerevents.onmousedown = function(e) { utils.cancelEvents(e,true) } //disable gmap dragging
				
				markerevents.title = this.name;
				
			this.markerevents = markerevents;
			this.gmap.getPane(google.maps.MAP_MARKER_MOUSE_TARGET_PANE).appendChild(markerevents);
		}

		var shadow = document.createElement('img');
			shadow.style.position = 'absolute';
			shadow.style.width = Math.round(this.shw * this.w)  +'px';
			shadow.style.height = Math.round(this.shh * this.h) +'px';
			shadow.style.zIndex = this.z;
			if (browser.cssfilter) shadow.onload = function() { utils.pngFix(this) }; //ie png fix
			shadow.src = 'media/marker_shadow.png';

		this.shadow = shadow;

		//scale and postion based on mapstate
		this.update();
			
		//append to map
		this.gmap.getPane(google.maps.MAP_MARKER_PANE).appendChild(marker);
		this.gmap.getPane(google.maps.MAP_MARKER_SHADOW_PANE).appendChild(shadow);
	}
	
	Place.prototype.getPlace = function(rsp)
	{
		/*	updates place obj to server data, used in 'direct'
		*/
		
		if (!rsp)
		{
			var obj = this;
			GW.SCENE.getPlace(function(rsp) { obj.getPlace(rsp) }, this.id);
			return;
		}
		
		dbg.msg('Place: updating to server data (revert)');
	
		var places = rsp.getElementsByTagName('place');
		var p = this.scene.portal.parseXML(places[0]);

		dbg.msg(dbg.obj(p,1));
		
		this.id = p.id;
		this.name = p.name;
		this.type = p.type;
		this.lat = p.geometry.point.lat;
		this.lon = p.geometry.point.lon;
		this.radius = p.geometry.point.radius;
		this.geo = new google.maps.LatLng(this.lat,this.lon);

		this.meta = p.meta;
		this.content = p.content;
		
		//update (or add to) places list display
		var listname = (this.name.length>=23)? this.name.substring(0,21)+'..':this.name;
		if (this.meta.ispublic) listname = '* '+listname;
		var state = (this.type=='trade' && this.meta.peerid)? '_paired':'';
		this.marker.src = 'media/marker_'+this.type+state+'.png';
		var scene = this.scene;
		var listobject = { 
			id:this.id,
			html:'<img src="media/icon_type_'+this.type+state+'.png" style="float:left; margin:0px 2px; width:18px; height:18px"; onload="utils.pngFix(this)">'+listname,
			callback:function() { scene.places[this.id].expand() }
		}

		if (!this.scene.list.pane.visible) this.scene.list.pane.show(); //pane has to be visible during list update //->FIND FIX LATER
		this.scene.list.updateElement(listobject);
		
		//update display
		this.update();
	}
	
	Place.prototype.updatePeers = function()
	{
		/*	update peers, used for trade places in scenetype=internationaltrading
		*/
		if (this.type!='trade') return;
		
		//get new peer from current placeform
		var old_peer = this.meta.peerid;
		var new_peer = document.forms['placeform'].peerid.value;
		
		dbg.msg('Place: updatePeers, old=',old_peer,', new=',new_peer);
		
		if ((new_peer=='' && old_peer) || (new_peer!='' && this.scene.place_to_delete))
		{
			//disconnected peer
			dbg.msg('==>diconnected peer, id=',old_peer);
			this.scene.places[old_peer].getPlace();
		}
		
		if (new_peer!='' && !old_peer)
		{
			//new peer
			dbg.msg('==>new peer, id=',new_peer);
			this.scene.places[new_peer].getPlace();
		}
		else if (new_peer!='' && old_peer!=new_peer)
		{
			//changed peer
			dbg.msg('==>changed peer, prev=',old_peer,' new=',new_peer);
			this.scene.places[old_peer].getPlace();
			this.scene.places[new_peer].getPlace();
		}		
	}

	Place.prototype.update = function()
	{
		//mapzoom based scale
		var z = this.gmap.getZoom();
		var s = Math.pow(3,(z/9)) / 10;
		this.s = Math.max(.3,s);
		this.w = this.s*this.default_w;
		this.h = this.s*this.default_h;
		
		//position on map
		var px = this.gmap.fromLatLngToDivPixel(this.geo);
		this.x = px.x - (this.w/2);
		this.y = px.y -  this.h;
		
		//apply to elements
		this.marker.style.left = this.x +'px'; 
		this.marker.style.top = this.y +'px';
		this.marker.style.width = this.w +'px';
		this.marker.style.height = this.h +'px';

		if (this.markerevents)
		{
			this.markerevents.style.left = this.x +'px'; 
			this.markerevents.style.top = this.y +'px';
			this.markerevents.style.width = this.w +'px';
			this.markerevents.style.height = this.h +'px';
			this.markerevents.title = this.name;
		}

		this.shadow.style.width = Math.round(this.shw * this.w)  +'px';
		this.shadow.style.height = Math.round(this.shh * this.h) +'px';
		this.shadow.style.left = this.x +'px'; 
		this.shadow.style.top = this.y + this.h - Math.round(this.shh * this.h) +'px';
		
		//pane
		if (this.pane)
		{
			var x = this.x+(this.w/2) - ((this.pane.w/2)+19);
			var y = this.y+this.h - (this.pane.h+68);
			this.pane.setPosition(x,y);
			
			this.pane.shadow.style.left = x +'px'; 
			this.pane.shadow.style.top = y + this.pane.h +70 - Math.round(0.65 * this.pane.h) +'px';
		}
	}
	
	Place.prototype.updateGeo = function(p)
	{
		/*	update geo with GLatLng obj
		*/
		this.geo = p;
		this.lat = p.lat();
		this.lon = p.lng();
		
		this.update();
	}
	
	Place.prototype.updateState = function(visit)
	{
		/*	update place-visit (state of place in play)
		*/
		dbg.msg('updating place-visit for place=',this.id);
		this.visit = visit;

		//states: hidden, open, hit, done
		//results: none, ok, skipped, timeout, hinted, notok
		//todolist: hit, fetch, [submitmedium], [quiz]
		
		var state = (this.visit.state!='hidden' && this.visit.state!='open')? 'hit':'';
		if (this.type=='task' && this.visit.state=='done') state = 'done';
		this.setState(state);
		
		//update if visible
		if (this.state=='play' && this.expanded)
		{
			if (this.visit.result=='skipped') this.expand()
			else this.scene.playPlaceData(this);
		}
	}
	
	Place.prototype.setState = function(state)
	{
		/*	update icon src to state
		*/
		var state = (state=='hit')? '_hit':(state=='done')? '_done':'';
		if (state=='') var state = (this.type=='trade' && this.meta.peerid)? '_paired':'';
		this.marker.src = 'media/marker_'+this.type+state+'.png';
	}
	
	Place.prototype.hit = function()
	{
		/*	hit a place, set icon state, reset after timeout
		*/
		this.setState('hit');
		var obj = this;
		this.hit_timeout = window.setTimeout(function() { obj.setState() },5000);
	}
	
	Place.prototype.expand = function()
	{
		if (this.type=='none')
		{
			alert(Locale7.get('scene','non_public_place'));
			return;
		}
	
		/*	create pane based on portal state
		*/
		
		dbg.msg('expand place id=',this.id);
		
		if (this.scene.expanded_place)
		{
			if (!this.scene.expanded_place.collapse()) return;
		}
		
		this.expanded = true;
		this.scene.expanded_place = this;
		
		if (!this.pane)
		{
			//create pane
			var w = (this.type=='task')? (this.state=='direct')? 320:277:250;
			var h = (this.type=='task')? (this.state=='direct')? 380:330:250;
			if (this.type=='ugc') {	w = 230; h = 210; }
			
			this.scene.portal.gui.createPlacePane(this,w,h);
			
			//set form data
			if (this.state=='direct') this.scene.editPlaceData(this);
			if (this.state=='places' || this.state=='live' || this.state.indexOf('play')!=-1) this.scene.playbackPlaceData(this);
			if (this.state=='play') this.scene.playPlaceData(this);
			
			//set droptargets (for direct mode, and webplay)
			if (this.state=='direct' || (this.state=='play' && this.type=='task'))
			{
				var obj = this;
				var type = this.state=='direct' ? obj.type:obj.type+'_mediasubmit';
				var delayed = function() { obj.scene.portal.gui.addDropTargets('place_'+type) }
				window.setTimeout(delayed,100); //firefox needs tiny delay to read DOM elm values after creation
			}
		}

		this.hideMarker();
		this.pane.show();
		this.pane.shadow.style.display = 'block';
		this.center();
	}
	
	Place.prototype.collapse = function()
	{
		if (this.state=='direct' && this.modified) 
		{
			//confirm if edited or new
			
			dbg.msg('collapse modified place, confirm..');
			this.pane.show();
			
			if (confirm(Locale7.get('direct_places','unsaved')))
			{
				dbg.msg('discard changes confirmed');
				
				this.modified = false;
				
				if (this.id!='new')
				{
					//revert
					this.getPlace();
				}
				else
				{
					//cancel new place
					this.scene.cancelAddPlace();
					return true;
				}
				
				//return true;
			}
			else
			{
				dbg.msg('not confirmed, return false');
				
				return false;
			}
		}
	
		this.expanded = false;
		delete this.scene.expanded_place;
		
		this.showMarker();

		//remove pane and shadow
		this.gmap.getPane(google.maps.MAP_FLOAT_SHADOW_PANE).removeChild(this.pane.shadow);
		this.scene.portal.layout.panes.dispose('place');
		delete this.pane;
		
		//reset droptargets
		if (this.scene.portal.medialib) this.scene.portal.medialib.removeDropTargets();
		
		return true;
	}
	
	Place.prototype.showMarker = function(hide)
	{
		this.marker.style.display = (hide)? 'none':'block';
		this.shadow.style.display = (hide)? 'none':'block';
		if (this.markerevents) this.markerevents.style.display = (hide)? 'none':'block';
	}
	
	Place.prototype.hideMarker = function()
	{
		this.showMarker(true);
	}
	
	Place.prototype.showInfo = function()
	{
		var pane = this.scene.portal.layout.panes['place_info'];
			pane.setPosition(this.x,this.y-this.h);
			pane.div.style.border = '1px solid red';
			pane.show();
	
	}
	
	Place.prototype.center = function()
	{
		if (this.expanded)
		{
			//shift based on pane height
			var px = this.gmap.fromLatLngToDivPixel(this.geo);
			var x = px.x; //->optionally add horizontal offset here later based on open panes
			var y = px.y - (this.pane.h/1.9);
			var p = this.gmap.fromDivPixelToLatLng(new GPoint(x,y))
		}
		else var p = this.geo;
		
		this.gmap.panTo(p);
		
		this.scene.places.update();
	}

	Place.prototype.zoomTo = function()
	{
		//if (this.collection.info) this.collection.info.hide(1);
		this.gmap.setCenter(this.geo,17);
	}


	Place.prototype.dispose = function()
	{
		/*	remove DOM elements
		*/
		
		if (this.hit_timeout) window.clearTimeout(this.hit_timeout);
		
		if (this.expanded) this.collapse(); //removes pane
		
		this.gmap.getPane(google.maps.MAP_MARKER_PANE).removeChild(this.marker);
		this.gmap.getPane(google.maps.MAP_MARKER_SHADOW_PANE).removeChild(this.shadow);
		if (this.markerevents) this.gmap.getPane(google.maps.MAP_MARKER_MOUSE_TARGET_PANE).removeChild(this.markerevents);
	}
}

/* [file src="Players.js"]
*/
/*	usemedia.com . joes koppers . 02.2008
	thnx for reading this code */


/*	7s Scene players
*/

function Players7(scene)
{
	/*	players collection
	*/

	this.create = function(properties)
	{
		if (!properties.id) return;
		var player = new Player(properties);
		//add to collection
		this[properties.id] = player;
		
		return player;
	}

	this.update = function(by_map)
	{
		/*	update all players
		*/
		for (var id in this)
		{
			if (typeof(this[id])!='object') continue;
			if (!by_map || (by_map && this[id].active))
			{
				this[id].update();
			}
		}
	}
		
	this.disposeAll = function()
	{
		/*	delete all players
		*/
		for (var id in this)
		{
			if (typeof(this[id])!='object') continue;
			this.dispose(id);
		}
	}
	
	this.dispose = function(id)
	{
		/*	delete a player by id
		*/
		if (!this[id]) return;
		this[id].dispose();
		delete this[id];
	}

	/*	scene player object
	*/

	function Player(p)
	{
		/*	Player constructor

			p = properties object, 
			values: id,team,color,user,lat,lon */

		dbg.msg('create player, id=',p.id);

		this.scene = scene;
		this.gmap = scene.portal.layout.gmap;
		
		this.id = p.id;
		this.team = p.team;
		this.color = p.color;
		this.user = p.user;
		this.lat = p.lat;
		this.lon = p.lon;
		this.active = false;
		this.live = false;
		this.playback = -1; //step in trace.points array;
		
		//user generated content (places)
		this.ugcplaces = new Places7(this.scene);

		//trace //->not using Trace object at this time
		this.trace = new Object();
		this.trace.points = new Array();
		this.trace.overlay = false;
		this.trace.enabled = false;

		if (this.lat)
		{
			this.prevgeo = this.geo = new google.maps.LatLng(this.lat,this.lon);
			this.trace.points.push(this.geo);
			this.active = true;
		}

		/*	icon
		*/
		
		//marker default size
		this.scale = 1; //scale is defined by map zoom
		this.maxw = 23;
		this.maxh = 23;
		this.w = this.scale * this.maxw;
		this.h = this.scale * this.maxh;		

		//create
		var player = document.createElement('canvas');
			player.style.display = (this.active)? 'block':'none'; //default hidden, usually doesn't have location at init
			player.className = 'player';
			player.style.width = this.w +'px';
			player.style.height = this.h +'px';
			player.width = this.w;
			player.height = this.h;
			
		//event handling
		var obj = this;
// 		player.onmouseover = function() { } //->add later, user/team details rollover
// 		player.onmouseouf = function() { }
		player.onclick = function() { dbg.msg('player click, id=',obj.id) }

		//append to map
		scene.portal.layout.gmap.getPane(google.maps.MAP_MARKER_PANE).appendChild(player);
		//init canvas element for IE
		if (typeof(G_vmlCanvasManager)=='object') player = G_vmlCanvasManager.initElement(player);
			
		this.canvas = player;

		//animation setup
		this.anim = {
			x_smoothing:.2,
			y_smoothing:.2,
			move:140,
			blink:1500,
			moving:false,
			blinking:false
		}
		
		//scale and position based on mapstate
		if (this.geo) this.update();
	}
	
	Player.prototype.addUGC = function(p)
	{
		/*	add a user generated content place
		*/
		
		dbg.msg('Player[',this.id,'].addUCG: place=',dbg.obj(p));
		
		var content = new Object();
		switch (p.type)
		{
			case 'plain':
				content.text = p.content;
				break;
				
			case 'image':
				content.image = { id:p.content };
				break;
				
			default:
				//-> only user generated notes and images are supported at this time
		}

		this.ugcplaces.create({
			id: p.id,
			type: 'ugc',
			ugctype: p.type=='plain'? 'text':p.type,
			content:content,
			name: p.user,
			lat: p.lat,
			lon: p.lon,
			gmap:this.gmap
		});
		
		
	}
	
	Player.prototype.enable = function(enable,removetrace)
	{
		this.active = (enable==undefined) || enable;
		//dbg.msg('Player[',this.id,'] active=',this.active);
		this.canvas.style.display = (this.active)? 'block':'none';

		//show/hide find button in trace widget
		if (this.toggle) this.toggle[this.active? 'show':'hide']();
		
		if (removetrace && this.trace.overlay) this.gmap.removeOverlay(this.trace.overlay);
		
		//dbg.msg('Player[',this.id,'] ugc show=',this.active);
		if (!this.active)
		{
			//hide all ugc places
			this.ugcplaces.showAll(true);
		}
	}
	
	Player.prototype.update = function(position_only,force)
	{
		/* 	scale based on map state
		*/
		if (!position_only)
		{
			var z =this.gmap.getZoom();
			var s = Math.pow(3,(z/9)) / 10;
			
			this.scale = Math.max(.3,s);
			this.w = this.scale * this.maxw;
			this.h = this.scale * this.maxh;
	
			this.canvas.style.width = Math.ceil(this.w) +'px';
			this.canvas.style.height = Math.ceil(this.h) +'px';
			this.canvas.width = Math.ceil(this.w);
			this.canvas.height =  Math.ceil(this.h);
			
			this.updateTrace();
		}

		if (this.geo)
		{
			if (!this.active) this.enable();
		
			//position
			var px = this.gmap.fromLatLngToDivPixel(this.geo);
			this.anim.x = this.x = px.x - this.w/2;
			this.anim.y = this.y = px.y - this.h/2;
			//apply to elm			
			this.canvas.style.left = this.x +'px'; 
			this.canvas.style.top = this.y +'px';
		}
		
		//redraw
		if (force) delete this.heading;
		
		this.drawIcon();
		
		//ucg
		this.ugcplaces.update();
	}
	
	Player.prototype.updateToLastLocation = function()
	{
		if (this.trace.points.length<1) return; //no last loc
		this.geo = this.trace.points[this.trace.points.length-1];
		this.enable(true);
		this.update(false,true);
	}
	
	Player.prototype.rset = function() //not used
	{
		this.trace.points = new Array();
// 		this.x = -100;
// 		this.y = -100;
// 		this.canvas.style.left = this.x +'px';
// 		this.canvas.style.
	}

	Player.prototype.drawIcon = function()
	{
		var ctx = this.canvas.getContext('2d');
		var s = this.w;

		ctx.clearRect(0,0,s,s);
	
		// 	var gradient = ctx.createLinearGradient(0,0,0,h);
		// 		gradient.addColorStop(0, '#ffffff');
		// 		gradient.addColorStop(1, '#dddddd');
	
		//color	fill
		var x = (s/2);
		var y = (s/2);
		var l = (s/12); //stroke width
		var r = (s/2)-l;

		ctx.beginPath();
		ctx.globalAlpha = .8;
		ctx.arc(x,y,r,0,2*Math.PI,true);
		ctx.fillStyle = '#'+this.color;
		ctx.fill();
	
		//stroke (white)
		var r = (s/2)-(l/2);
		ctx.beginPath();
		ctx.globalAlpha = 1;
		ctx.arc(x,y,r,0,2*Math.PI,true);
		ctx.strokeStyle = '#ffffff';
		ctx.lineWidth = l;
		ctx.stroke();
		
		//heading dot (white)
		if (this.heading!=undefined)
		{
			var a = (Math.PI/180) * this.heading;
			var r = (s/2)-(3*l); //distance from edge;
			var dx = r * Math.sin(a);
			var dy = r * Math.cos(a);
			var hr = s/10; //size

			ctx.beginPath();
			ctx.arc(x+dx,y-dy,hr,0,2*Math.PI,true);
			ctx.globalAlpha = 1;
			ctx.fillStyle = '#ffffff';
			ctx.fill();
		}
		
		//force canvas dimensions for IE
		$j(this.canvas).find('div').css({
			width:s,
			height:s
		});
	}

	Player.prototype.updateHeading = function()
	{
		//get heading based on previous screen position
		with (Math)
		{
			var dx = abs(this.x-this.anim.x);
			var dy = abs(this.y-this.anim.y);
// 			var dx = abs(this.x-this.prevx);
// 			var dy = abs(this.y-this.prevy);
			if (dy==0)
			{
				var c = (this.x>this.anim.x)? 90:270; //going straight east or west
			}
			else
			{
				var c = atan(dx/dy) * (360/(2*PI)); //in degrees
			}
		}
		//transform to 0-360 degrees, with N=0, E=90, S=180, W=270
		if (this.x>this.anim.x && this.y>this.anim.y) c = 180 - c;
		if (this.x<this.anim.x && this.y>this.anim.y) c = 180 + c;
		if (this.x<this.anim.x && this.y<this.anim.y) c = 360 - c;
	
		//dbg.msg('new heading=',c);
		this.heading = c;
		this.drawIcon();
	}
	
	Player.prototype.updateLocation = function(lat,lon,timestamp,force)
	{
		/*	update player location on map,
			if animated player.move() will update to new target position */
	
		//dbg.msg('player (',this.id,') updateLocation, force=',force,', this.pb=',this.playback)
		
		if (this.lat==lat && this.lon==lon) return; //no update needed
		
		//update history
		this.prevx = this.x;
		this.prevy = this.y;
		this.prevgeo = this.geo;
		this.prevtime = this.timestamp;

		//update current
		this.timestamp = timestamp;
		this.lat = lat;
		this.lon = lon;
		this.geo = new google.maps.LatLng(lat,lon);
		
		if (this.live)
		{
			this.trace.points.push(this.geo);
			this.updateTrace();
		}
		
		var px = scene.portal.layout.gmap.fromLatLngToDivPixel(this.geo);
		this.x = Math.round(px.x - this.w/2);
		this.y = Math.round(px.y - this.h/2);

		//initial or forced location
		if (this.live)
		{
			if ((this.trace.length>=1 && !this.active) || force) this.update();
		}
		else
		{
			if ((this.playback==1 && !this.active) || force) this.update(undefined,true);
		}

		if (force)
		{
			//stop anim
			if (this.anim.moving)
			{
				window.clearTimeout(this.anim.moving);
				this.anim.moving = false;
			}
		}

// 		if (scene.portal.state=='playback')
// 		{
// 			//no move anim in playback, update location immediately
// 			this.update();	
// 		}
// 		else
// 		{
		else
		{
			//restart smooth if needed
			var obj = this;
			if (!this.anim.moving) this.anim.moving = window.setTimeout(function() { obj.move() },this.anim.move);
		}
		
// 		}
	}
	
	Player.prototype.enableTrace = function(enable)
	{
		this.trace.enabled = enable;
		if (enable) this.updateTrace(true);
		else if (this.trace.overlay) this.gmap.removeOverlay(this.trace.overlay);
	}

	Player.prototype.updateTrace = function(playback)
	{
		/*	temporay alt version to Trace object
		*/
		
		if (!this.trace.enabled) return;
		if (this.trace.points.length<2) return; //we need two points minimum
		if (playback && this.playback==-1) return;
		
		//dbg.msg('player id=',this.id,': updateTrace');

		//remove current trace
		if (this.trace.overlay) this.gmap.removeOverlay(this.trace.overlay);

		//width adjusted to mapview
		var w = this.gmap.getZoom()/4;
		w = (w>4)? Math.round(w):Math.floor(w);
		//only show last 100 points (for performance)
//		var trace = (this.trace.length>ii_max_livetrace+100)? this.trace.slice(this.trace.length-ii_max_livetrace):this.trace;
		var trace = (this.playback!=-1)? this.trace.points.slice(0,Math.max(0,this.playback-2)):this.trace.points;

		//add new trace
		this.trace.overlay = new google.maps.Polyline(trace,'#'+this.color,Math.max(1,w),0.8);
		this.gmap.addOverlay(this.trace.overlay);
 	}
	
	//->add later
	Player.prototype.addTrace = function()
	{
		/*	attach existing trace or add new (for live tracing)
		*/
		
// 		var trace = false;
// 		var traces = wp_games.game[wp_selected_game].traces;
// 		
// 		
// 		for (var id in traces.trace)
// 		{
// 			if (traces.trace[id].user==this.name) 
// 			{
// 				trace = id;
// 				break;
// 			}
// 		}
// 	
// 		if (trace)
// 		{
// 			tmp_debug(3,'user:',this.name,', found trace');
// 			this.trace_obj = traces.trace[id];
// 		}
// 		else
// 		{
// 			tmp_debug(3,'user:',this.name,', create new trace obj');
// 	
// 			this.trace_obj = new wpTrace(this.name,'livetrace',this.color)
// 			wp_games.game[wp_selected_game].traces.push( this.trace_obj );
// 	
// 		}
	}

	Player.prototype.showTrace = function()
	{
		//add point to trace obj
// 		var geo = (wp_mode=='play')? this.prevgeo:this.geo;
// 		if (geo) this.trace_obj.addLivePoint(geo);
	}
	//<--
	
	Player.prototype.move = function()
	{
		if (this.trace.length>1) // && Math.floor(this.anim.x)==this.x && Math.round(this.anim.y)==this.y )
		{
			var dx = Math.abs(this.anim.x - this.x);
			var dy = Math.abs(this.anim.y - this.y);
			
			//dbg.msg('player.move, dx=',dx,', dy=',dy);
			
			if (dx<=1 && dy<=1)
			{
				//reached destination, stop moving
				this.anim.moving = false;
				
				//if (this.playback==-1)
				return;
			}
		}

		//smooth by moving slowly to current value
		this.anim.x += this.anim.x_smoothing * (this.x - this.anim.x);
		this.anim.y += this.anim.y_smoothing * (this.y - this.anim.y);
		//apply to icon div
		this.canvas.style.left = Math.round(this.anim.x) +'px'; 
		this.canvas.style.top = Math.round(this.anim.y) +'px';
		
		this.updateHeading();
//		this.drawIcon();
	
		//icon info + shadow
	// 	this.infodiv.style.left = Math.round(this.smoothX) + (this.w/2) -65 +"px"; 
	// 	this.infodiv.style.top = Math.round(this.smoothY) + (this.h/2) -126 +"px";
	// 	this.infoshadowdiv.style.left = Math.round(this.smoothX) + (this.w/2) -37 +"px"; 
	// 	this.infoshadowdiv.style.top = Math.round(this.smoothY) + (this.h/2) -61 +"px";
	
		//interval
		var obj = this;
		this.anim.moving = window.setTimeout(function() { obj.move() },this.anim.move);
	}
	
	Player.prototype.center = function()
	{
		/*	center map to current player location
		*/
	
		if (this.geo)
		{
			var b = this.gmap.getBounds()
			if (b.contains(this.geo)) this.gmap.panTo(this.geo);
			else
			{
				this.gmap.setCenter(this.geo);
				this.scene.portal.layout.zoomend();
			}
		}
	}
	
	Player.prototype.animate = function(show)
	{
		var obj = this;
		if (show)
		{
			//this.animating = window.setInterval(function() { obj.smooth() } ,150);
			if (wp_mode!='view') this.animating = window.setTimeout(function() { obj.smooth() },this.smooth_timeout);
			this.blink(0);
		}
		else
		{
			//if (this.animating) window.clearInterval(this.animating);
			if (this.animating) window.clearTimeout(this.animating);
			this.blink(1);
		}
	}
	
	Player.prototype.blink = function(stop)
	{
		this.div.style.visibility = (this.blinkdelay==500 || stop)? 'visible':'hidden';
	
		//show is longer than hide
		this.blinkdelay = (this.blinkdelay==1500)? 500:1500;
	
		var obj = this;
		if (!stop) this.blinking = window.setTimeout(function() { obj.blink() },this.blinkdelay);
		else if (this.blinking) window.clearTimeout(this.blinking);
	}
	
	Player.prototype.getSpeed = function()
	{
		return (this.geo.distanceFrom(this.prevgeo) / 1000) / ((this.time - this.prevtime) / 3600000);
	}




	Player.prototype.dispose = function(playeronly)
	{
		if (this.anim.moving) window.clearTimeout(this.anim.moving);
	
		//remove DOM elm
		scene.portal.layout.gmap.getPane(google.maps.MAP_MARKER_PANE).removeChild(this.canvas);
		
		//remove trace
		if (this.trace.overlay) this.gmap.removeOverlay(this.trace.overlay);
		
		//remove user generated content
		if (this.ugcplaces) this.ugcplaces.disposeAll();
	}


	//-> use trace class below in future version	
	
	function Trace(id,trackid,color)
	{
		/*	obj constructor
		*/
		
		this.id = this.user = id; //==user name
		this.trackid = trackid;
		this.color = color;
		
		this.segments = [];
	
		this.media = new wpLocations('trace');
	
		var c;
		switch (color.substring(0,1))
		{
			case 'r': c = '#c80014'; break;
			case 'g': c = '#2daa4b'; break;
			case 'b': c = '#3264c8'; break;
			case 'y': c = '#ffd02b'; break;
			case 'o': c = '#df8021'; break;
			case 'p': c = '#8d2bcb'; break;
		}
		this.tracecolor = c;
		/// this.tracecolor = '#00ff00'; //debug
		
		var obj = this;
		if (trackid!='livetrace') GW.QUERY.queryStore('get-track',function(resp) { obj.parse(resp) },'id',this.trackid,'mindist',25);
	}
	
	Trace.prototype.parse = function(resp)
	{
		/*	parse the gpx resp, get points and media
		*/
		
		this.gpx = resp;
	
		//get (segments and) points
		var segments = this.gpx.getElementsByTagName('seg');
		for (var i=segments.length-1; i>=Math.max(0,segments.length-10); i--) this.addSegment(segments[i]); //only use last 10 segments (?)
	//	for (var i=0; i<segments.length; i++) this.addSegment(segments[i]);
	
		//get media (only for current user)
		if (wp_login.loginname==this.user)
		{
			var media = this.gpx.getElementsByTagName('medium');
			for (var i=0; i<media.length; i++) this.addMedium(media[i]);
		}
		
		this.update();
	}
	
	Trace.prototype.addSegment = function(segment)
	{
		/*	add new segment to trace, and fill it with points
		*/
		
		this.segments.push( { trace:false, points:new Array() } );
	
		var points = segment.childNodes;
		for (var i=0; i<points.length; i++) this.addPoint(points[i]);
	}
	
	Trace.prototype.needSegment = function(point)
	{
		/*	determine if new segment is created:
			if distance from last point > 500m */
		
		//get last point
		var lastsegment = this.segments[this.segments.length-1];
		var lastpoint = (lastsegment.points.length>0)? lastsegment.points[lastsegment.points.length-1]:false;
	
		if (lastpoint) tmp_debug(3,'[',this.user,'] ','needSegment: d=',point.distanceFrom(lastpoint),'->',point.distanceFrom(lastpoint)>500);
		
		if (lastpoint && point.distanceFrom(lastpoint)>500) return true;
		else return false;
	}
	
	Trace.prototype.addPoint = function(pt)
	{
		/* add point to trace(segment)
		*/
	
		var last = this.segments.length-1;
		
	//	var t = pt.getAttribute('t');
		var lat = pt.getAttribute('lat');
		var lon = pt.getAttribute('lon');
		var geo = new GLatLng(lat,lon);
			
	//	this.segments[last].points.push( { geo:geo, timestamp:t } );
		this.segments[last].points.push(geo);
	}
	
	Trace.prototype.addLivePoint = function(geo)
	{
		/* 	add live point to trace(segment)
			check first if new segment is needed */
	
		if (this.segments.length==0 || this.needSegment(geo))
		{
			this.segments.push( { trace:false, points:new Array() } );
		}
		
		this.segments[this.segments.length-1].points.push(geo);
		this.update();
	}
	
	Trace.prototype.addMedium = function(medium)
	{
		/* add location (medium)
		*/
	
		var id = medium.getAttribute('id');
		var name = medium.getAttribute('name');
		var kind = medium.getAttribute('kind');
		var lat = medium.getAttribute('lat');
		var lon = medium.getAttribute('lon');
		var geo = new GLatLng(lat,lon);
		
		this.media.push( new wpLocation({trace:true},id,geo,'medium','enabled',name) );
	}
	
	Trace.prototype.addLiveMedium = function(medium)
	{
		/* add live uploaded medium
		*/
		
		var id = medium.id;
		var name = medium.name;
		var lat = medium.lat;
		var lon = medium.lon;
		var geo = new GLatLng(lat,lon);
		
		this.media.push( new wpLocation({trace:true},id,geo,'medium','enabled',name) );
		this.update();
	}
	
	Trace.prototype.update = function()
	{
		/*	redraw trace (segments) (for current zoomlevel)
			and update (media)locations
		*/
		
		for (var s in this.segments)
		{
			var segment = this.segments[s];
	
			if (segment.points.length<2) continue; //we need two points minimum	
	
			tmp_debug(1,'update trace id=',this.id,', segment=',s);
		
			//line-width adjusted to mapview
			var w = gmap.getZoom()/4;
			w = (w>4)? Math.round(w):Math.floor(w);
	
			//remove current trace
			if (segment.trace) gmap.removeOverlay(segment.trace);
			//generate and draw new trace
			segment.trace = new GPolyline(segment.points,this.tracecolor,Math.max(1,w),0.8);
			gmap.addOverlay(segment.trace);
		}
		
		if (this.media.length>0) this.media.update();
	}
	
	Trace.prototype.dispose = function()
	{
		/*	remove trace overlay and media
		*/
	
		for (var s in this.segments)
		{
			var segment = this.segments[s];
			if (segment.trace) gmap.removeOverlay(segment.trace);
		}
		
		if (this.media) 
		{
			tmp_debug(3,'removing media');
			for (var id in this.media.location) this.media.del(id);
		}
	}






}

/* [file src="Portal.Layout.js"]
*/
/*	usemedia.com . joes koppers . 11.2007 [rev 08.2009]
	thnx for reading this code */


/*	7s Layout Object (Layers, GMap, Lists)
*/

Portal7.prototype.addLayout = function()
{
	function Layout(portal)
	{
		/*	main layout
		*/
		this.portal = portal; //ref to parent
		
		this.div = $j('#layout')[0];
		this.fullscreen = false;
		this.resize(1); //set initial dimensions

		this.panes = new Panes7(portal); //->REFACTOR: move panes to Gui object (?)
		this.layers = new Layers7(portal);
		
		//window resize event
		var obj = this;
		$j(window).resize(function(){ obj.resize() });
	}
	
	Layout.prototype.dispose = function()
	{
		this.layers.disposeAll();
		this.dashboard.dispose();
	}
	
	Layout.prototype.resize = function(skiplist)
	{
		/*	cross browser document body dimensions
		*/
		this.w = (window.innerWidth!=null)? window.innerWidth:(document.documentElement && document.documentElement.clientWidth)? document.documentElement.clientWidth:(document.body!=null)? document.body.clientWidth:0;
		this.h = (window.innerHeight!=null)? window.innerHeight:(document.documentElement && document.documentElement.clientHeight)? document.documentElement.clientHeight:(document.body!=null)? document.body.clientHeight:0;

		//development
		if (typeof(dbg)=='object') dbg.moveTo(20,this.h-dbg.h-10);
		
		if (this.layers) this.update(skiplist);
		if (this.dashboard) this.dashboard.update();
	}
	
	Layout.prototype.update = function(skiplist)
	{	
		/* resize all layers in layout
		*/
		for (var id in this.layers)
		{
			if (typeof(this.layers[id])=='object') this.layers[id].update();
		}
		
		//update the list in portal layer //->REFACTOR, move to parent layer
		if (this.list && !skiplist)
		{
			//->TMP: update panel dimensions
			//this.layers['portal'].wpanels['scenes'].layoutUpdate();
		
			//delayed update of list
			dbg.msg('resize: will update list in 250ms..');
			if (this.listupdate) window.clearTimeout(this.listupdate);
			var obj = this;
			this.listupdate = window.setTimeout(function() { obj.list.update() },250);
		}
	}
	
	Layout.prototype.createBaseLayers = function()
	{
		/*	create base layers + menus
			portal, scene, places  */
		
			/*	sample color scheme for menus
	
				//private (reds)
				color2:'#9b0000', color:'#a80000', //1-lighter 
				color2:'#850000', color:'#940000', //1-light 
				color2:'#6e0000', color:'#7d0000', //2
				color2:'#570000', color:'#650000', //3
				color2:'#400000', color:'#4f0000', //4-dark
	
				//public (yellows)
				color2:'#666644', color:'#73734d', //1-dark
				color2:'#808055', color:'#8c8c5e', //2
				color2:'#999966', color:'#a6a67c', //3
				color2:'#b3b386', color:'#bfbf8f', //4-light */
				
		var obj = this;
		dbg.msg('Layout: creating base layers..');

	
		/*	portal (scenes)
		*/
		var layer = this.layers.create({
			id:'portal',
			x:0, y:0, r:0, b:0,
			z:0,
			bcolor:'#003c5e',
			gradient:'bg_portal_satellite.jpg',
			hcolor2:'#efefef',
			title:this.portal.gui.create({ id:'layertitle',title:'7scenes',w:69,h:17 }),
			parent:this
		});
		
		$j(layer.content).css({
			color:'#f2f2f2',
			overflow:'hidden'
		});
		
		//add 'home' click
		$j('#portal')
			.find('.header, .titlebar')
			.css('cursor','pointer')
			.attr('title',Locale7.get('gui','back_to_scenes'))
			.click(function(e) {
				if (e.shiftKey && typeof(dbg)=='object') 
				{	
					//development feature, shift-click to enable debugger
					dbg.setEnabled(true);
					return;
				}
				if (obj.portal.scene) obj.portal.scene.close();
				if (obj.portal.project) obj.portal.project.close();
			});

		//portal layer contents
		this.portal.gui.createPortalPage();
		
			/*	portal layer menus
			*/
			var login = layer.addMenu({
				id:'login',
				w:90,
				color2:'#9b0000', color:'#a80000', //1-lighter 
				parent:layer,
				content:'<a href="javascript://login" style="left:6px; width:65px; height:22px; background-image:url(media/menu/login.png)"></a>'
			});
			//add login input interaction
			this.portal.gui.enableLoginMenu();
			
			var direct = layer.addMenu({
				id:'direct',
				w:80,
				color2:'#850000', color:'#940000', //1-light 
				parent:layer,
				enabled:false,
				content:'<a href="javascript://direct" style="left:-2px; width:65px; height:22px; background-image:url(media/menu/direct.png)"></a>'
			});
			$j(direct.div).find('a').click(function() {
				if (!direct.enabled) return alert(Locale7.get('error','no_login'));
				obj.portal.direct.direct('new');
			});

			var my = layer.addMenu({ 
				id:'my',
				w:75,
				color2:'#6e0000', color:'#7d0000', //2
				//enabled:false,
				parent:layer,
				enabled:false,
				content:'<a href="javascript://my" style="left:-2px; width:65px; height:22px; background-image:url(media/menu/my.png)"></a>'
			});
			$j(my.div).find('a').click(function() {
				if (!my.enabled) return alert(Locale7.get('error','no_login'));
				obj.portal.myFind(undefined,obj.layers['myfind'].visible);
			});
			
// 			var signup = layer.addMenu({ 
// 				id:'signup',
// 				w:93,
// 				color2:'#666644', color:'#73734d', //1-dark
// 				parent:layer,
// 				content:this.portal.gui.create( { id:'layermenu', title:'signup', w:77, margin:'0px 0px 0px -2px' } )
// 			});
// 			signup.onclick = function() { obj.portal.signup() }
	
			var find = layer.addMenu({ 
				id:'find',
				w:80,
				color2:'#808055', color:'#8c8c5e', //2
				parent:layer,
				content:'<a href="javascript://find" style="left:-2px; width:65px; height:22px; background-image:url(media/menu/find.png)"></a>'
			});
			$j(find.div).find('a').click(function() { 
				obj.portal.find(undefined,obj.layers['find'].visible);
			});
			
			var language = layer.addMenu({ 
				id:'language',
				w:42,
				color:'#000000',
				alpha:.5,
				parent:layer,
				content:'<a href="javascript://language" style="left:2px; width:19px; height:22px; background-image:url(media/menu/'+this.portal.language+'.png)"></a>'
			});
			$j(language.div).find('a').click(function() {
				obj.portal.setLanguage();
			});
			
			//about
			var about = layer.addMenu({ 
				id:'about',
				w:80,
				color:'#000000',
				alpha:.3,
				parent:layer,
				content:'<a href="javascript://info" style="left:2px; width:65px; height:22px; background-image:url(media/menu/about.png)"></a>'
			});
			$j(about.div).find('a').click(function() {
				obj.dashboard.show();
			});


		/*	scene
		*/
		var layer = this.layers.create({
			id:'scene',
			x:38, y:29, r:17, b:0,
			z:100,
			bcolor:'#b3b386',
			gradient:'media/bg_scene_page.png',
			hcolor:'#e3e3aa', hcolor2:'#cccc99',
			closebutton:true,
			shadow:true,
			parent:this.layers['portal'],
			hidden:true
		});
		
		$j(layer.closebutton)
			.attr('title',Locale7.get('scene','close'))
			.click(function() { 
				obj.portal.scene.close();
			});

		//layer modifications
		$j('#scene .bg:first').css('borderLeft','1px solid #9e9e69');
 		$j('#scene .bg:last').css('borderTop','1px solid #a3a376');
 		$j(layer.content).css({
 			left:36,
 			top:46
// 			minWidth:930
 		});
 		$j(layer.titlebar).css('overflow','visible');
 		
 		this.portal.gui.createScenePage();

		//add 'home' click
		$j('#scene')
			.find('.header, .titlebar, #scene-icon')
			.css('cursor','pointer')
			.attr('title',Locale7.get('gui','back_to_scene'))
			.click(function(e) {
				obj.portal.scene.closePlaces();
			});

		
		//extend layer clear function to include extras, widgets
		layer.clear = function()
		{
			obj.portal.gui.updateScenePage(undefined,true);
		}

			/*	scene layer menus
			*/
			var edit = layer.addMenu( {
				id:'edit',
				w:80,
				color2:'#850000', color:'#940000', //1-light 
				parent:layer,
				content:'<a href="javascript://edit" style="left:0px; width:50px; height:22px; background-image:url(media/menu/edit.png)"></a>'
			} );
			edit.hide();
			$j(edit.div).find('a').click(function() {
				if (!obj.portal.direct) return;
				if (obj.portal.direct.previewing)
				{
					obj.portal.direct.preview(true);
				}
				else
				{
					obj.portal.direct.direct(obj.portal.scene.id);
				}
			});

			var play = layer.addMenu( {
				id:'play',
				w:85,
				color2:'#6e0000', color:'#7d0000', //2
				parent:layer,
				enabled:false,
				content:'<a href="javascript://play" style="left:0px; width:65px; height:22px; background-image:url(media/menu/play.png)"></a>'
			} );
			$j(play.div).find('a').click(function() {
				if (!play.enabled) return alert(Locale7.get('error','no_login'));
				//expand play
				obj.portal.scene.openMenu('play');
			});
			var play_x = layer.addMenu( {
				id:'play_x',
				w:85,
				color:'#050000', alpha:.82,
				parent:layer,
				content:'<a href="javascript://play" style="left:0px; width:65px; height:22px; background-image:url(media/menu/play_x.png)"></a>'
			} );
			play_x.hide();
			$j(play_x.div).find('a').click(function() {
				obj.portal.scene.openMenu('play',true);
			});

			var comment = layer.addMenu( {
				id:'comment',
				w:105,
				color2:'#666644', color:'#73734d', //1-dark
				parent:layer,
				content:'<a href="javascript://comment" style="left:2px; width:85px; height:22px; background-image:url(media/menu/comment.png)"></a>'
			} );
			$j(comment.div).find('a').click(function() {
				obj.portal.scene.openMenu('comment');
			});
			var comment_x = layer.addMenu( {
				id:'comment_x',
				w:105,
				color:'#050000', alpha:.82,
				parent:layer,
				content:'<a href="javascript://comment" style="left:2px; width:85px; height:22px; background-image:url(media/menu/comment_x.png)"></a>'
			} );
			comment_x.hide();
			$j(comment_x.div).find('a').click(function() {
				obj.portal.scene.openMenu('comment',true);
			});

			var activity = layer.addMenu( {
				id:'activity',
				w:100,
				color2:'#808055', color:'#8c8c5e', //2
				parent:layer,
				content:'<a href="javascript://activity" style="left:0px; width:85px; height:22px; background-image:url(media/menu/activity.png)"></a>'
			} );
			$j(activity.div).find('a').click(function() {
				obj.portal.scene.openMenu('activity');
			});
			var activity_x = layer.addMenu( {
				id:'activity_x',
				w:100,
				color:'#050000', alpha:.82,
				parent:layer,
				content:'<a href="javascript://activity" style="left:0px; width:85px; height:22px; background-image:url(media/menu/activity_x.png)"></a>'
			} );
			activity_x.hide();
			$j(activity_x.div).find('a').click(function() {
				obj.portal.scene.openMenu('activity',true);
			});

// 			var places = layer.addMenu( {
// 				id:'places',
// 				w:80,
// 				color2:'#a8a87e', color:'#b8b88a', //4-light, modified */
// 				parent:layer,
// 				content:'<a href="javascript://places" style="left:2px; width:60px; height:22px; background-image:url(media/menu/places.png)"></a>'
// 			} );
// 			$j(places.div).find('a').click(function() {
// 				if (!obj.layers['places'].visible) obj.portal.scene.openPlaces();
// 			});

			var places_x = layer.addMenu( { //place-holder for places menu
				id:'places_x',
				w:80,
				color2:'#a8a87e', color:'#b8b88a', //4-light, modified */
				parent:layer,
				content:''
			} );
			places_x.hide();
		
		
		/*	places
		*/
		var layer = this.layers.create({
			id:'places',
			x:38, y:29,	r:17, b:0,
			z:10,
			bcolor:'rgb(153,179,204)',
			hcolor:'#ddddaa',
			hcolor2:'#bbbb88',
			title:this.portal.gui.create({ id:'layertitle',title:'places',w:49,h:20 }),
			parent:this.layers['scene'],
			hidden:true,
			nocontent:true
		});
		layer.show = function(hide) //extend layer class
		{
			this.visible = !hide;
			$j(this.div)[hide? 'hide':'show']();

			//hide scene layer contents
			obj.layers['scene'].blur(['content','backdrop'],!hide);

			if (!hide)
			{
				//update map to current layer size
				if (obj.gmap) obj.gmap.checkResize();
				//add controls when first shown (needed to align type controls at bottom of map)
				if (!obj.gmap.controlsAdded) obj.addGMapControls();
			}
		}
		
			/*	places layer menus
			*/

			//fullscreen toggle
			$j(layer.menudiv).css('right',32); //move menu to left
			var fullscreen = utils.pngButton({ src:'icon_enterfull', style:{ position:'absolute', right:'6px', top:'5px', width:'20px', height:'16px', zIndex:5200 }, title:'toggle fullscreen display' });
				fullscreen.onclick = function () { obj.toggleFullscreen() };
	
			layer.div.appendChild(fullscreen);
			layer.fullscreenbutton = fullscreen;

			var done = layer.addMenu( {
				id:'done',
				w:80,
				color2:'#850000', color:'#940000', //1-light 
				parent:layer,
				content:'<a href="javascript://edit" style="left:-2px; width:55px; height:22px; background-image:url(media/menu/edit-done.png)"></a>'
			} );
			done.hide();
			$j(done.div).find('a').click(function() {
				obj.portal.scene.closePlaces();
			});
			
			var livetraces_x = layer.addMenu( {
				id:'livetraces_x',
				w:105+95,
				color:'#050000', alpha:.82,
				parent:layer,
				content:'<a href="javascript://livetraces" style="left:30px; width:105px; height:22px; background-image:url(media/menu/live_traces_x.png)"></a>'
			} );
			$j(livetraces_x.div).find('a').click(function() {
				obj.portal.scene.exitLivePlayback('places');
			});
			livetraces_x.hide();

			var traces = layer.addMenu( {
				id:'traces',
				w:105,
				color2:'#666644', color:'#73734d', //1-dark
				parent:layer,
				content:'<a href="javascript://traces" style="left:0px; width:75px; height:22px; background-image:url(media/menu/traces.png)"></a>'
			} );
			$j(traces.div).find('a').click(function() {
				obj.portal.scene.openPlacesMenu('traces');
			});
			var traces_x = layer.addMenu( {
				id:'traces_x',
				w:105,
				color:'#050000', alpha:.82,
				parent:layer,
				content:'<a href="javascript://traces" style="left:0px; width:75px; height:22px; background-image:url(media/menu/traces_x.png)"></a>'
			} );
			traces_x.hide();
			$j(traces_x.div).find('a').click(function() {
				obj.portal.scene.openPlacesMenu('traces',true);
			});

			var events = layer.addMenu( {
				id:'events',
				w:95,
				color2:'#808055', color:'#8c8c5e', //2
				parent:layer,
				content:'<a href="javascript://events" style="left:0px; width:75px; height:22px; background-image:url(media/menu/events.png)"></a>'
			} );
			$j(events.div).find('a').click(function() {
				obj.portal.scene.openPlacesMenu('events');
			});
			var events_x = layer.addMenu( {
				id:'events_x',
				w:95,
				color:'#050000', alpha:.82,
				parent:layer,
				content:'<a href="javascript://events" style="left:0px; width:75px; height:22px; background-image:url(media/menu/events_x.png)"></a>'
			} );
			events_x.hide();
			$j(events_x.div).find('a').click(function() {
				obj.portal.scene.openPlacesMenu('events',true);
			});
			
			var scene = layer.addMenu( {
				id:'scene',
				w:80,
				color2:'#a8a87e', color:'#b8b88a', //4-light, modified */
				parent:layer,
				content:'<a href="javascript://scene" style="left:-2px; width:60px; height:22px; background-image:url(media/menu/scene.png)"></a>'
			} );
			$j(scene.div).find('a').click(function() {
				//close places layer
				obj.portal.scene.closePlaces();
			});

		//force resize and add map to places layer
		this.update(1);
		this.addGMap();
		

		/*	project layer
		*/
		var layer = this.layers.create({
			id:'project',
			x:38, y:29, r:17, b:0,
			z:50,
			bcolor:'#d1d1d1',
			hcolor:'#b3b386',
			gradient:'bg_project_gradient+.gif',
			closebutton:true,
			shadow:true,
			parent:this.layers['portal']
		});
		layer.hide();
		$j(layer.closebutton)
			.attr('title',Locale7.get('project','close'))
			.click(function() {
				obj.portal.project.close();
			});
		
		//extend layer clear function to include extras
		layer.clear = function()
		{
			$j(this.content)
				.empty()
				.css('top',34);
			
			if (this.sceneslist)
			{
				this.div.removeChild(this.sceneslist);
				delete this.sceneslist;
			}
		}
				
		layer.panels = new Panels7(layer);
		this.portal.gui.createGroupHeader('project');
				
			/*	project layer menus
			*/
			
			var manage = layer.addMenu( {
				id:'manage',
				w:105,
				color2:'#850000', color:'#940000', //1-light 
				parent:layer,
				content:'<a href="javascript://manage" style="left:0px; width:85px; height:22px; background-image:url(media/menu/manage.png)"></a>'
			} );
			$j(manage.div).find('a').click(function() {
				obj.portal.project.selectAdminPage('directors');
			});
			manage.hide();

			var manage_done = layer.addMenu( {
				id:'manage_done',
				w:105,
				color2:'#850000', color:'#940000', //1-light 
				parent:layer,
				content:'<a href="javascript://manage" style="left:10px; width:65px; height:22px; background-image:url(media/menu/manage_done.png)"></a>'
			} );
			$j(manage_done.div).find('a').click(function() {
				obj.portal.project.selectAdminPage('directors');
			});
			manage_done.hide();
			
			var add_director = layer.addMenu( {
				id:'add_director',
				w:105,
				color2:'#6e0000', color:'#7d0000', //2
				parent:layer,
				content:'<a href="javascript://director" style="left:0px; width:90px; height:22px; background-image:url(media/menu/add_director.png)"></a>'
			} );
			$j(add_director.div).find('a').click(function() {
				if (obj.portal.signupByAdmin) obj.portal.signupByAdmin.show();
				//obj.portal.scene.openPlacesMenu('traces');
			});
			add_director.hide();
			
			var add_director_x = layer.addMenu( {
				id:'add_director_x',
				w:105,
				color2:'#6e0000', color:'#7d0000', //2
				parent:layer,
				content:'<a href="javascript://director" style="left:0px; width:90px; height:22px; background-image:url(media/menu/add_director_x.png)"></a>'
			} );
			$j(add_director_x.div).find('a').click(function() {
				if (obj.portal.signupByAdmin) obj.portal.signupByAdmin.hide();
				//obj.portal.scene.openPlacesMenu('traces');
			});
			add_director_x.hide();

			
// 			var directors = layer.addMenu( { 
// 				id:'directors',
// 				w:97,
// 				color2:'#6e0000', color:'#7d0000', //2
// 				parent:layer,
// 				content:this.portal.gui.create( { id:'layermenu', title:'directors', w:83, margin:'0px 0px 0px -4px' } )
// 			} );
// 			//directors.onclick = function() { obj.portal.project.selectAdminPage(this,'directors') }
// 			directors.hide();
	}
	
	Layout.prototype.createMenuLayers = function()
	{
		/*	create overlay layers,
			find, activity, comment, events, traces */
	
		var obj = this;
		dbg.msg('Layout: creating menu layers..');

		/*	portal > search
		*/
		var find = this.layers.create({
			id:'find',
			w:251, y:23, r:0, h:100,
			z:250,
			bcolor:'#050000',
			gradient:'bg_black_75.png',
			bgrepeat:'repeat', bgtransparent:true,
			header:false,
			rounded:{ alpha:.75 },			
			hidden:true,
			parent:this.layers['portal']
		});
		$j(find.titlebar).hide();
		$j(find.content)
			.addClass('search-result')
			.before($j('<div></div>')
				.attr('id','search-header')
				.addClass('search-header')
				.html(this.portal.gui.create('search'))
			);
		$j('#search-header .button:first').find('a')
			.click(function() {
				obj.portal.query(undefined,'');
				$j(document.forms['searchform'].query).val('').focus();
			});
		
		find.context = $j('#search-context-toggle')[0];

		/*	portal > signup
		*/
// 		var signup = this.layers.create({
// 			id:'signup',
// 			w:251, y:23, r:0, h:200,
// 			z:51,
// 			bcolor:'#666644',
// 			header:false,
// 			rounded:true,			
// 			hidden:true,
// 			closebutton:true,
// 			parent:this.layers['portal']
// 		});
// 		$j(signup.closebutton).click(function() {
// 			signup.hide();
// 		});

		/*	scene > activity
		*/
		var activity = this.layers.create({
			id:'activity',
			w:394, y:23, r:0, b:0,
			z:250,
			bcolor:'#050000',
			gradient:'bg_black_80.png',
			bgrepeat:'repeat', bgtransparent:true,
			header:false,
			hidden:true,
			parent:this.layers['scene']
		});

		/*	scene > comment
		*/
		var comment = this.layers.create({
			id:'comment',
			w:394, y:23, r:0, b:0,
			z:250,
			bcolor:'#050000',
			gradient:'bg_black_80.png',
			bgrepeat:'repeat', bgtransparent:true,
			header:false,
			hidden:true,
			parent:this.layers['scene']
		});

		/*	scene > play
		*/
		var play = this.layers.create({
			id:'play',
			w:394, y:23, r:0, b:0,
			z:250,
			bcolor:'#050000',
			gradient:'bg_black_80.png',
			bgrepeat:'repeat', bgtransparent:true,
			header:false,
			hidden:true,
			parent:this.layers['scene']
		});
		$j(play.content)
			.css({
				left:25,
				top:22
			});

		/*	places > events
		*/
		var events = this.layers.create({
			id:'events',
			w:312, y:23, r:0, b:0,
			z:250,
			bcolor:'#050000',
			gradient:'bg_black_80.png',
			bgrepeat:'repeat', bgtransparent:true,
			header:false,
			hidden:true,
			parent:this.layers['places']
		});
		
		/*	places > traces
		*/
		var traces = this.layers.create({
			id:'traces',
			w:312, y:23, r:0, b:0,
			z:250,
			bcolor:'#050000',
			gradient:'bg_black_80.png',
			bgrepeat:'repeat', bgtransparent:true,
			header:false,
			hidden:true,
			parent:this.layers['places']
		});

		$j(activity.content)
			.add(comment.content)
			//.add(play.content)
			.css({
				left:25,
				top:22
			});

		$j(events.content)
			.add(traces.content)
			.css({
				left:20,
				top:22
			});


		this.portal.gui.createActivityPage();
		this.portal.gui.createCommentPage();

		this.portal.gui.createPlacesPages();
	}
	
	Layout.prototype.showLayers = function(layers,hide)
	{
		/*	show/hide array of layers
		*/
		for (var i=0; i<layers.length; i++)
		{
			if (this.layers[layers[i]]) this.layers[layers[i]].show(hide);
		}
	}

	Layout.prototype.hideLayers = function(layers)
	{
		/*	hide one or multipe layers
		*/
		this.showLayers(layers,true);
	}
	
	Layout.prototype.addDashboard = function()
	{
		/*	info dashboard at bottom of window, with slide-in and out
		*/
		
		function Dashboard(layout)
		{
			var obj = this;

			this.layout = layout;
			this.portal = layout.portal;
			
			this.y = -240;
			
			//create DOM element(s)			
			var div = document.createElement('div');
				div.id = 'dashboard';
				div.innerHTML = this.portal.gui.create('dashboard');

			layout.div.appendChild(div);
			this.div = div;
			
// 			this.toggleshow = $j('#dashboard-toggle')
// 				.find('.checkbox')
// 				.click(function() {
// 					obj.toggleShow(this.checked);
// 				})[0];
			
			//toggle event
			this.toggleshow = document.getElementById('dashboard-toggle').firstChild;
			this.toggleshow.onclick = function() { obj.toggleShow(this.checked) };

			//document.getElementById('dashboard-toggle').onclick = function() { obj.toggleShow(this.firstChild.checked) };
			//this.toggleshow.onchange = function() { obj.toggleShow(this.checked) };
			

			//minimize button
			var m = utils.pngButton( {
					src: 'icon_remove_pane',
					title:'hide this',
					style: { position:'absolute', right:'4px', top:'4px', width:'14px', height:'14px' } 
				} );
				m.onclick = function() { obj.show(true) }
			div.appendChild(m);
			
			//info site links
			this.addLink('what',38,'content/what-7scenes',Locale7.get('dashboard','what'));
			this.addLink('tour',223,'content/take-tour',Locale7.get('dashboard','tour'));
			this.addLink('blog',408,'blog',Locale7.get('dashboard','blog'));
			this.addLink('get',593,'content/get-7scenes',Locale7.get('dashboard','get'));
			
			this.autoshow = true;
			this.visible = false;
			this.slidespeed = .25;
			
			this.update();
		}
		
		Dashboard.prototype.update = function()
		{
			//center Dashboard
			this.div.style.left = (this.layout.w - 800)/2 +'px';
			this.div.style.bottom = this.y +'px';
			
			//move debugger out of the way, develop feature
			if (this.portal.mode=='test' && this.visible) dbg.moveTo(20,this.layout.h-dbg.h-190-10);
		}
		
		Dashboard.prototype.addLink = function(type,x,link,extra)
		{
			/*	create and add link panel
			*/
			var div = document.createElement('div');
				div.className = 'dashboard-link';
				div.style.backgroundImage = 'url(media/bg_dashboard_link_'+type+'.png)';
				div.style.left = x +'px';
			var img = document.createElement('img');
				img.className = 'dashboard-link-title';
				img.src = 'media/label_dashboard_link_'+type+'.png';
			div.appendChild(img);
			
			if (extra)
			{
				var txt = document.createElement('div');
					txt.className = 'dashboard-link-caption';
					txt.innerHTML = extra;
				div.appendChild(txt);
			}
			
			//event handling
			div.onmouseover = function() { utils.over(this.firstChild,1) };
			div.onmouseout = function() { utils.over(this.firstChild,0) };
			var obj = this.portal;
			div.onclick = function() { utils.over(this.firstChild,0); obj.go(link) };
			
			this.div.appendChild(div);
		}
		
		Dashboard.prototype.show = function(hide)
		{
			/*	slide-in or out
			*/
			this.layout.layers['portal'].showMenus(['about'],!hide);
			this.ty = hide ? -240:-45;
			
			this.visible = !hide;
			this.slide();
		}
		
		Dashboard.prototype.slide = function()
		{
			/*	slide the dashboard
			*/
			var obj = this;

			//move to target	
			var dy = this.ty-this.y;
			this.y = this.y + this.slidespeed*(this.ty-this.y);
			
			var update_x = false;
			var update_y = (dy>0 && this.y<this.ty-1) || (dy<0 && this.y>this.ty+1);
			
			if (update_x || update_y)
			{
				this.update();
				//loop
				this.sliding = window.setTimeout(function() { obj.slide() },40);
			}
			else
			{
				//done, set to exact end target position
				this.y = this.ty;
				this.update();
			}
		}
		
		Dashboard.prototype.autoShow = function(set)
		{
			var auto = utils.readCookie('showinfo');
			
			if (auto && set==undefined)
			{
				this.autoshow = (auto=='true')? true:false;
				//update toggle display
				this.toggleshow.checked = !this.autoshow;
				utils.over(this.toggleshow.nextSibling,!this.autoshow);
			}
			else
			{
				//(re)set cookie
				var show = (set==undefined)? 'true':set;
				
				var days = 90; //3 months
				var d = new Date();
				d.setTime(d.getTime()+(days*24*60*60*1000));
				var expires = '; expires='+d.toGMTString();
				document.cookie = 'showinfo='+show+expires+'; path=/'; //default is to show info

				this.autoshow = (set==undefined || set=='true')? true:false;
			}
	
			return this.autoshow;
		}
		
		Dashboard.prototype.toggleShow = function(value)
		{
			/*	toggle auto-show
			*/
			dbg.msg('Dashboard: toggle, value=',value);
			
			this.autoshow = value;
			
 			//(re)set cookie
			var set = (!this.autoshow)? 'true':'false';
			this.autoShow(set);
		}
		
		Dashboard.prototype.dispose = function()
		{
			this.layout.div.removeChild(this.div);
		}
		
		
		/*	init
		*/
		this.dashboard = new Dashboard(this);
	}

	Layout.prototype.addGMap = function()
	{
		if (!google.maps) return; //for development
		dbg.msg('init googlemap');
		
 		var layer = this.layers['places'];

		//innershadow
		var shadows = ['left','top','right'];
		for (var i=0; i<shadows.length; i++)
		{
			var div = document.createElement('div');
				div.className = 'innershadow';
			switch (shadows[i])
			{
				case 'left':
					div.style.left = '0px';
					div.style.top = '30px';
					div.style.width = '39px';
					break;
				case 'right':
					div.style.right = '-12px';
					div.style.width = '39px';
					break;
				case 'top':
					div.style.left = '26px';
					div.style.height = '38px';
					break;
			}
			if (!browser.cssfilter) div.style.backgroundImage = 'url(media/innershadow_'+shadows[i]+'.png)';
			else div.style.filter = utils.pngBgImage('innershadow_'+shadows[i]).substr(7);

			layer.div.insertBefore(div,layer.div.childNodes[2]);
		}
		//shear edge fix
		$j('<div></div>')
			.addClass('innershadow_fix')
			.css({
				'background-color':layer.parent.bc,
				'background-image':layer.parent.gradient? 'url(media/'+layer.parent.gradient+')':'',
				'background-position':'0px -29px'
			})
			.insertBefore('#places > canvas.header');
		
		//add map div
 		var map = document.createElement('div');
 			map.id = 'gmap';

		layer.div.insertBefore(map,layer.div.childNodes[2]);
		layer.mapdiv = map;

		//add map
 		this.gmap = new google.maps.Map2(map,{ backgroundColor:'rgb(153,179,204)' });

		//change gmap bgcolor (after init, otherwise overruled)
		//map.style.backgroundColor = 'rgb(153,179,204)';

		//custom map settings
		this.gmap.defaultlocation = new google.maps.LatLng(52.37278268707501, 4.900324791669846); //waag
		this.gmap.defaultzoom = 14;
		
		//init
		this.gmap.setCenter(this.gmap.defaultlocation, this.gmap.defaultzoom);
		//if (this.portal.mode=='test') this.gmap.enableGoogleBar();
		this.gmap.enableGoogleBar();

		//this.gmap.setMapType(google.maps.SATELLITE_MAP);
		this.gmap.enableContinuousZoom();
		//this.gmap.setMapType(google.maps.NORMAL_MAP);
		
		//event handling
		var obj = this;
		this.zoomend = function()
		{
			//dbg.msg('zoomend, obj=',dbg.obj(obj));
			//update map Places
			if (obj.portal.scene)
			{
				if (obj.portal.scene.places) obj.portal.scene.places.update();
				if (obj.portal.scene.players) obj.portal.scene.players.update(true);
			}
		}
		
		this.map_zoomend = google.maps.Event.addListener(this.gmap,'zoomend',this.zoomend);

		//->tmp debug function
		this.map_loaded = true;
		this.map_click = google.maps.Event.addListener(this.gmap,'click',function(o,p){dbg.msg('map click:<br>&nbsp; lat=',p.lat(),', lon=',p.lng())});
		
		dbg.msg('Layout: google map loaded');
	}

	Layout.prototype.addGMapControls = function()
	{
		dbg.msg('gmap: addControls');
	
		/*	add controls to map //->CUSTOMIZE these later (7scenes design)
		*/
		this.gmap.addControl(new google.maps.LargeMapControl(),new google.maps.ControlPosition(google.maps.ANCHOR_TOP_LEFT,new google.maps.Size(20,40)));
		this.gmap.addControl(new google.maps.MapTypeControl(),new google.maps.ControlPosition(google.maps.ANCHOR_BOTTOM_RIGHT,new google.maps.Size(10,18)));
		this.gmap.controlsAdded = true;

		//re-center map
		//this.gmap.setCenter(this.gmap.defaultlocation, this.gmap.defaultzoom);
	}

	Layout.prototype.toggleFullscreen = function()
	{
		/*	expand/collapse map (places layer) to fullscreen
		*/
		var layer = this.layers['portal'];
	
		this.fullscreen = !this.fullscreen;
		var obj = this;
		var mode = this.fullscreen? 'exit':'enter';

		if (this.layers['places'].collapsed) this.layers['places'].collapse();

		layer.slide_speed = 0.3;
		layer.slideCallback = function()
		{
			dbg.msg('toggleFullscreen: change button src, mode=',mode);
			obj.layers['places'].fullscreenbutton.src = 'media/icon_'+mode+'full.png';
			
			obj.gmap.checkResize();
			
			//default location of add places pane
// 			var pane = obj.panes['addplaces'];
// 			var h = mode=='exit' ? 200:140;
// 			if (!pane.hasmoved) pane.setPosition(pane.x,obj.layers['places'].h-140);
		}

		if (this.fullscreen)
		{
			layer.slideTo(-78,-58);
			//layer.resizeTo( { tr:-51, tb:0 } );
			layer.resizeTo( { tr:-34, tb:0 } );
		}
		else
		{
			layer.slideTo(0,0);
			layer.resizeTo( { tr:0, tb:0 } );
		}
	}
	
	
	/*	Lists
	*/
	
	Layout.prototype.addList = function(scenes,layer,parent,id,options)
	{
		/*	create a new List,
				scenes		scenes collection
				layer		layer list is in
				parent		DOM element to attach list to
				id			list id
				[options]	 object: { [type], [style] } */

		return new List(scenes,layer,parent,id,options);
	}
	
	function List(scenes,layer,parent,id,options)
	{
		//this.layout = layout;
		this.scenes = scenes;
		this.layer = layer;
		this.parent = parent;
		this.id = id;
		
		//settings and defaults		
		options = options || new Object();
		this.style = options.style || new Object();
		this.type = options.type || 'list'; //default type
		
		this.elements = new Array();
		this.element = 0;
		
		this.pages = new Object();
		this.pages.array = new Array();
		this.pages.rset = function()
		{
			this.x = 0;
			this.slidespeed = .25;
			this.current = 0;
			if (this.div) this.div.style.left = '0px';
		}
		this.pages.rset();
		
		//create DOM elements
		var div = document.createElement('div');
			div.className = 'list-container';
		var container = document.createElement('div');
			container.className = 'list-pages-container';
		div.appendChild(container);

		var nav = document.createElement('div'); //for pages index
			nav.className = 'list-navigation';
		div.appendChild(nav);
		
		var buttons = ['next','prev'];
		var obj = this;
		for (var i=0; i<buttons.length; i++)
		{
			var direction = (buttons[i]=='next')? 'right':'left';
			var button = document.createElement('img');
				button.className = 'list-navigation-button';
				button.style[direction] = '0px';
				if (browser.cssfilter) button.onload = function() { utils.pngFix(this) }; //ie png fix
				button.src = 'media/icon_arrow_'+direction+'.png';
				button.title = (buttons[i]=='next')? 'next scenes':'previous scenes';
			//events
			button.onmouseover = function() { utils.over(this,1) }
			button.onmouseout = function() { utils.over(this,0) }
			button.onclick = (buttons[i]=='next')? function() { obj.gotoPage('next') }:function() { obj.gotoPage('prev') };
			
			div.appendChild(button);
			this[buttons[i]+'button'] = button;
		}
		
		parent.appendChild(div);
		
		this.div = div;
		this.pages.div = container;
		this.navdiv = nav;
		
		this.setSize();
		//this.updateNavigation();
		
		dbg.msg('List: id=',this.id,', create done');
	}

	List.prototype.setSize = function()
	{
		this.w = Math.round(this.parent.offsetWidth);
		this.h = Math.round(this.parent.offsetHeight);

		//center
		var x = Math.round((this.parent.offsetWidth - this.w)/2);
		var y = Math.round((this.parent.offsetHeight - this.h)/3);
		
		//dbg.msg('List: this.w=',this.w,', this.h=',this.h);

// 		this.div.style.left = Math.max(x,0) +'px';
// 		this.div.style.top = y +'px';

		this.div.style.width = this.w +'px';
		this.div.style.height = this.h +'px';
		
		this.prevbutton.style.top = (this.h/2) -(.1*this.h) +'px';
		this.nextbutton.style.top = (this.h/2) -(.1*this.h) +'px';
		
		this.pages.div.style.height = this.h +'px';
	}
	
	List.prototype.addElements = function(items)
	{
		/*	add items array as elements to list
		*/
		this.elements = new Array();
		for (var i=0; i<items.length; i++) this.addElement(items[i]);
		
		this.update();
	}
	
	List.prototype.addElement = function(id,index,update)
	{
		/*	add element to list [at index]
		*/
		if (index!=undefined) this.elements.splice(index,0,id);
		else this.elements.push(id);
		
		if (update) this.update();
	}

	List.prototype.clear = function(dispose)
	{
		/*	remove all pages
		*/
		while (this.pages.array.length>0) this.removePage(dispose);
		this.element = 0;
		this.pages.rset();
		
// 		if (dispose)
// 		{
// 			for (var i=total; i>this.pages.array.length; i--)
// 			{
// 				//dbg.msg('removing childnode');
// 				this.pages.div.removeChild(this.pages.div.firstChild);
// 			}
// 		
// 		
// 		}
	}
		
	List.prototype.update = function()
	{
		/*	resize page(s), 
			divide listitems over pages accordingly */
		
		dbg.tmr();
		dbg.msg('List['+this.id+']: update');
		
		//dbg.msg(dbg.obj(this));
		
		this.setSize();			
	
		//rebuild pages, dispose if no elements
		this.clear( this.elements.length==0 );
		
		if (this.elements.length>0) this.process();
	}
	
	List.prototype.process = function()
	{
		/*	add listitems to list in (centered) rows,
			create pages as needed */
		
		if (this.elements.length==0) return; 
		if (this.pages.array.length==0) this.addPage();
		
		var page = this.pages.array[this.pages.array.length-1];
		var fit = true;
		
		if (this.type=='cloud')
		{
			/*	CLOUD type, use rows to center elements per row
			*/
			var rows = new Array();
			var elm = this.scenes[this.elements[0]].listitem;
 			var h = elm.object.h + elm.object.margin;
			var row = addRow(h);
			
			while (fit = page.div.scrollHeight<=page.h)
			{
				if (this.element==this.elements.length) break; //we're done
				
				//append element to current row
				var elm = this.scenes[this.elements[this.element]].listitem;
				row.appendChild(elm);
				
				//check if on same row
				if (elm.previousSibling && elm.offsetTop==elm.previousSibling.offsetTop)
				{
					//yes, update row width
					rows[rows.length-1].w += elm.object.w;
				}
				else
				{
					//no, add new row and append element
					if (rows.length>0)
					{
						//set final with of current row (so it apears centered)
						row.style.width = rows[rows.length-1].w +'px';
						//new row
						var row = addRow(h);
						row.appendChild(elm);
					}
					//set current row width
					rows.push( { w:elm.object.w } );
					
					//first row? remove last element for nicer 'cloud' appearance..
					if (rows.length==2 && this.element>1)
					{
						//dbg.msg('List[',this.id,']: first row, removing last element ('+this.element+') for cloud');
					
						var prev_elm = this.scenes[this.elements[this.element-1]].listitem;
						row.insertBefore(prev_elm,elm);
						//update current row dimensions
						rows[rows.length-1].w += prev_elm.object.w;
						//update first row
						rows[0].w -= prev_elm.object.w;
						row.previousSibling.style.width = rows[0].w +'px';
					}
				}
				this.element++;
			}
		}
		else
		{
			/*	LIST type, speedier list creation (without rows)
			*/
			while (fit = page.div.scrollHeight<=page.h)
			{
				if (this.element==this.elements.length) break; //we're done
				page.div.appendChild(this.scenes[this.elements[this.element]].listitem);
				this.element++;
			}
		}
		
		//page done, new page?
		if (!fit)
		{
			//undo last move 
			this.element--;
			
			//remove last element for nicer 'cloud' appearance..
			if (this.type=='cloud' && row.previousSibling.childNodes.length>1 && rows.length>2)
			{
				dbg.msg('List[',this.id,']: removing last element for cloud');
			
				this.element--;
				//update dimensions
				var prev_elm = this.scenes[this.elements[this.element]].listitem;
				rows[rows.length-2].w -= prev_elm.object.w;//prev_elm.object.div_w + parseInt(prev_elm.style.marginRight);
				row.previousSibling.style.width = rows[rows.length-2].w +'px';
			}
			
			//remove element and last row
			if (this.type=='cloud')
			{
				row.removeChild(row.lastChild);
				page.div.removeChild(row);
				
				//for single row clouds, undo move of last elm in first row (cloud appearance)
				if (rows.length==2) this.element--;
			}
			
			//continue on new page
			this.addPage();
			this.process();
		}
		else
		{
			//set final row width
			if (this.type=='cloud') row.style.width = rows[rows.length-1].w +'px';

			//list done!
			dbg.msg('List['+this.id+']: done, '+this.element+' elements on '+this.pages.array.length+' pages','tmr');

			this.updateNavigation();
			
			//remove obsolete pages (done AFTER moving list elements due to IE display bug)
			var total = this.pages.div.childNodes.length;
			for (var i=total; i>this.pages.array.length; i--)
			{
				//dbg.msg('removing childnode');
				this.pages.div.removeChild(this.pages.div.firstChild);
			}
		}
		
		//local create row element function
		function addRow(h)
		{
			var div = document.createElement('div');
				div.className = 'list-row';
 				div.style.width = page.w +'px';
 				div.style.height = h+'px';
				//div.style.border = '1px dotted darkred'; //tmp
				//div.style.backgroundColor = 'darkred'; //tmp
			return page.div.appendChild(div);
		}
	}
	
	List.prototype.addPage = function()
	{
		/*	adds page object to the list
		*/
		this.pages.array.push(new Page(this));
	}
	
	List.prototype.removePage = function(dispose)
	{
		/*	removes last page from list
		*/
		var page = this.pages.array.pop();
		if (dispose) page.dispose();
	}
	
	List.prototype.gotoPage = function(page)
	{
		if (page=='next') page = this.pages.current+1;
		if (page=='prev') page = this.pages.current-1;
		
		if (page<0) page = 0;
		if (page>=this.pages.array.length) page = this.pages.array.length-1;
		
		this.pages.current = page;
		this.updateNavigation();
		this.pages.array[page].slideIntoView();
	}

	List.prototype.updateNavigation = function()
	{
		//previous and next button
		this.prevbutton.style.display = (this.pages.current>0)? 'block':'none';
		this.nextbutton.style.display = (this.pages.current<this.pages.array.length-1)? 'block':'none';
		
		//page index
		while (this.navdiv.firstChild) this.navdiv.removeChild(this.navdiv.lastChild);
//			this.navdiv.innerHTML = '';
		for (var i=0; i<this.pages.array.length; i++) this.addPageDot(i);
		var w = (this.pages.array.length * 15);
		this.navdiv.style.width = w +'px';
		this.navdiv.style.height = '7px';
		this.navdiv.style.left = Math.round((this.w/2) - (w/2)) +'px';
	}
	
	List.prototype.addPageDot = function(index)
	{
		var r = (index==this.pages.current)? 7:3;
	
		var canvas = document.createElement('canvas');
			canvas.width = 13;
			canvas.height = 7;
			canvas.style.width = 13 +'px'; //needed for safari
			canvas.style.height = 7 +'px';
			canvas.style.cursor = 'pointer';
			canvas.style.cssFloat = 'left';
			canvas.style.position = 'relative';
			canvas.style.margin = '0px 1px';
		
		this.navdiv.appendChild(canvas);
		//init canvas element for IE
		if (typeof(G_vmlCanvasManager)=='object') canvas = G_vmlCanvasManager.initElement(canvas);
		
		var ctx = canvas.getContext('2d');
			ctx.beginPath();
			ctx.arc(13/2,7/2,r/2,0,2*Math.PI,true);
			ctx.fillStyle = '#ffffff';
			ctx.fill();
			
		var obj = this;
		if (index!=this.pages.current)
		{
			canvas.onclick = function() { obj.gotoPage(index) };
			canvas.title = 'jump to page '+(index+1);
		}
	}
	
	List.prototype.slidePages = function()
	{
		/*	slide the pages div
		*/
		var obj = this;
		
		//move to target	
		var dx = this.pages.tx-this.pages.x;
		//var dy = this.pages.ty-this.pages.y;
		
		this.pages.x = this.pages.x + this.pages.slidespeed*(this.pages.tx-this.pages.x);
		//this.pages.y = this.pages.y + this.pages.slidespeed*(this.pages.ty-this.pages.y);
		
		var update_x = (dx>0 && this.pages.x<this.pages.tx-1) || (dx<0 && this.pages.x>this.pages.tx+1);
		var update_y = false; //(dy>0 && this.pages.y<this.pages.ty-1) || (dy<0 && this.pages.y>this.pages.ty+1);
		
		if (update_x || update_y)
		{
			this.updatePages();
			//loop
			this.sliding = window.setTimeout(function() { obj.slidePages() },40);
		}
		else
		{
			//done, set to exact end target position
			this.pages.x = this.pages.tx;
			//this.pages.y = this.pages.ty;
			this.updatePages();
			if (this.slideCallback) this.slideCallback();
		}
	}
	
	List.prototype.updatePages = function()
	{
		/*	draw pagesdiv at current position
		*/
		this.pages.div.style.left = this.pages.x +'px';
		//this.pages.div.style.top = this.pages.y +'px';
	}
	
// 	List.prototype.createItem = function(scale)
// 	{
// 		/*	create and return list item div
// 		*/
// 		var item = new Item(this.layout,scale);
// 		return item;
// 	}
	
	function Page(list)
	{
		/*	list Page object
		*/
		
		this.list = list;
		this.index = list.pages.array.length;
		this.margin_x = list.style.page_h_margin!=undefined ? list.style.page_h_margin:50;
		this.margin_y = list.style.page_v_margin!=undefined ? list.style.page_v_margin:20;
		
		//create page elm
		var div = document.createElement('div');
			div.className = 'list-page';
			div.style.top = this.margin_y/2 +'px';
			
			if (this.list.type=='cloud') div.style.textAlign = 'center';
			
			//div.style.border = '1px dotted red';
			
		this.div = div;
		this.setSize();
		
		list.pages.div.appendChild(div);

		this.x = this.margin_x + (this.index*(this.w+(2*this.margin_x)));
		this.div.style.left = this.x +'px';
	}
	
	Page.prototype.setSize = function()
	{
		this.w = this.list.w - (2*this.margin_x);
		this.h = this.list.h - (2*this.margin_y);
		this.div.style.width = this.w +'px';
		this.div.style.height = this.h +'px';
	}
	
	Page.prototype.slideIntoView = function()
	{
		var offset = this.margin_x;
		this.list.pages.tx = -this.x +offset;
		this.list.slidePages();
	}
		
	Page.prototype.dispose = function()
	{
		this.list.pages.div.removeChild(this.div);
	}
	

	/*	init
	*/
	this.layout = new Layout(this);
}

/* [file src="Portal.Gui.js"]
*/
/*	usemedia.com . joes koppers . 07.2008
	thnx for reading this code */


/*	7s (public) GUI (Panes, Panels, HTML)
*/

Portal7.prototype.addGui = function()
{
	function Gui(portal)
	{
		this.portal = portal; //ref to parent
		this.layout = portal.layout; //ref to layout
	}
	
	/*	layer contents
	*/
	
	Gui.prototype.createPortalPage = function()
	{
		/*	portal layout: selected scenes, top 10, ... ?
		*/
		var layer = this.layout.layers['portal'];
		layer.panels = new Panels7(layer);

		//selected scenes (first 50 of q-scenes, change later)
		var selectedpanel = layer.panels.create( {	
			id:'selected',
			parent:layer.content,
			title:'<img src="media/label_recently_active_scenes.png" style="width:166px; height:23px">',
			style:{ marginLeft:'20px', marginTop:'18px', width:(0.66*layer.content.offsetWidth)+'px', height:(0.90*layer.content.offsetHeight)+'px' },
			//border:{ light:'transparent', dark:'transparent' },
			content:'<div style="width:100%; height:100%;"></div>'
		} );
			//add list
			selectedpanel.content.style.overflow = 'hidden';
			selectedpanel.content.style.marginLeft = '5px';
			selectedpanel.content.style.paddingRight = '5px';
			
			var listcontainer = selectedpanel.content.firstChild;
			var list = this.layout.addList(this.portal.portalscenes,layer,listcontainer,'portal',{ type:'cloud', style:{ page_h_margin:25, page_v_margin:5 } });
			
			this.portal.lists['portal'] = list;
			this.portal.portalscenes.getList = function() { return list }; //add list ref to scenes collection
			
			//add refresh button
			var refresh = utils.pngButton( {
				src:'icon_refresh',
				title:'refresh list',
				style:{ position:'absolute', right:'5px', top:'3px', width:'17px', height:'19px' }
			} );
			var obj = this.portal;
			refresh.onclick = function() { obj.getInitialScenes() }
			selectedpanel.header.appendChild(refresh);

		
		//projects
		var projectspanel = layer.panels.create( {
			id:'projects',
			parent:layer.content,
			title:'<img src="media/label_projects.png" style="width:64px; height:23px">',
			style:{ marginLeft:'20px', marginTop:'18px', width:'250px', maxHeight:(0.90*layer.content.offsetHeight)+'px' },//, height:(0.9*layer.content.offsetHeight)+'px' },
			content:'-no projects found-'
		} );
		projectspanel.content.style.overflow = 'auto';

		//extend layer resize with panel resize and listupdate
		layer.extendUpdate['selected_panel'] = function()
		{
			var w = Math.round(0.66*layer.content.offsetWidth);
			var h = Math.round(0.9*layer.content.offsetHeight);
			selectedpanel.updateSize(w,h);
			
			projectspanel.content.style.maxHeight = h-24 +'px';
			projectspanel.div.style.maxHeight = h +'px';
			
			//delayed list update
			dbg.msg('Panel (id=selected) resize, update list in 250ms..');
			if (selectedpanel.listupdate) window.clearTimeout(selectedpanel.listupdate);
			selectedpanel.listupdate = window.setTimeout(function() { list.update() },250);
		}
	}
	
	Gui.prototype.createScenePage = function()
	{
		var obj = this;
		var layer = this.layout.layers['scene'];
			
		layer.backdrop = $j('<div></div>')
			.attr('id','scene-page-backdrop')
			.html(
				'<div id="scene-backdrop-image"></div>'+
				'<div class="scene-backdrop-shadow left"></div>'+
				'<div class="scene-backdrop-shadow right"></div>'
			)
			.insertBefore(layer.content);

		//left column: detail and story widget
		$j('<div></div>')
			.attr('id','scene-page-leftcolumn')
			.append('<div id="scene-detail"></div>')
			.append(this.createWidget('large','story'))
			.append('<div id="clear"></div>')
			.appendTo(layer.content);
			
		//right column: director, map and share widget
		$j('<div></div>')
			.attr('id','scene-page-rightcolumn')
			.append(this.createWidget('small','director'))
			.append(this.createWidget('small','map'))
			.append(this.createWidget('small','share'))
			.append('<div id="clear"></div>')
			.appendTo(layer.content);
			
		//icon
		$j('<img>')
			.attr({
				id:'scene-icon',
				src:'media/blank.gif'
			})
			.appendTo(layer.div);	


		/*	widgets
		*/

		//story
		$j('#scene-widget-story .widget-body').append('<div id="scene-story"></div>');

		//director
		$j('#scene-widget-director .widget-body')
			.css('padding-top',5)
			.append($j('<div></div>')
				.css({
					width:286,
					height:64,
					marginLeft:2,
					backgroundColor:'#999966'
				})
				.html(this.create('scene_director'))
			);
		
		//map
		var b = $j('#scene-widget-map .widget-body').css('padding-top',5);
		var map = $j('<div></div>')
			.attr({
				id:'scene-gmap',
				title:Locale7.get('scene','places')
			})
			.appendTo(b);
			
		layer.display();

 		var gmap = new google.maps.Map2(map[0],{ backgroundColor:'#999966' });
			gmap.setCenter(new google.maps.LatLng(52.37278268707501, 4.900324791669846),10);
			gmap.disableDragging();
			
			this.sceneGMap = gmap;
		
			//tinted overlay
			$j('<img>')
				.attr('src','media/scene_map_fade.png')
				.addClass('scene-gmap-overlay')
				.css({
					width:gmap.getSize().width,
					height:gmap.getSize().height
				})
				.appendTo(gmap.getPane(google.maps.MAP_MAP_PANE));
	
			//add places update event
			google.maps.Event.addListener(gmap,'zoomend',function() { 
				if (obj.portal.scene && obj.portal.scene.infoplaces)
				{
					obj.portal.scene.infoplaces.update();
				}
			});
			
			//shadow + go button overlay
			$j('<div></div>')
				.attr({
					'id':'scene-gmap-overlay',
					title:Locale7.get('scene','places')
				})
				.css('cursor','pointer')
				.html('<div id="scene-gmap-button" class="button"><a href="javascript://map"></a></div>')
				.hover(function() {
					$j(this).find('a').addClass('button-over');
				},function() {
					$j(this).find('a').removeClass('button-over');
				})
				.click(function() {
					if (obj.portal.state!='direct') obj.portal.scene.openPlaces();
				})
				.appendTo(b);

			
		layer.display(true);
		
		//share
		$j('#scene-widget-share .widget-body')
			.css('position','static')
			.append(this.create('scene_share'));
		
		$j('#scene-widget-share input')
			.mousedown(function() { return false })
			.click(function() { this.focus(); this.select() });

		$j('#scene-widget-share select[name="trace"]')
			.change(function() {
				obj.portal.embed(this.value);
			});
	}
	
	Gui.prototype.updateScenePage = function(scene,clr)
	{
		dbg.msg('Gui: updateScenePage, ',clr? 'clear=true':'scene='+scene.id);
		
		if (clr)
		{
			/*	clear widget contents
			*/
			$j('#scene .titlebar:first,#scene-detail,#scene-story,#scene-director').empty();
			$j('#scene-director-image').css('background-image','url()');
			$j('#scene-backdrop-image').css('background-image',undefined);
			$j(document.forms['shareform']).find('input').val('');
			$j('#scene-share-trace').hide();
			$j('#scene-icon').attr('src','media/blank.gif');
			return;
		}
	
		var obj = this;
		var info = scene.info;
		
		//icon
		if (scene.icon)
		{
			dbg.msg('sceneicon=',dbg.obj(scene.icon));
		
			var x = Number(scene.icon.offsetx); 
			var y = Number(scene.icon.offsety);
			var crop = (x>0? '-'+x:'+'+Math.abs(x))+(y>0? '-'+y:'+'+Math.abs(y));
			var url = 'id='+scene.icon.id+'&cmd=sceneicon&crop=64x28'+encodeURIComponent(crop)+'&geometry='+encodeURI(Number(scene.icon.scale)*100+'%')+'&format=png';
			$j('#scene-icon').attr('src',this.portal.path+'media.srv?'+url);
		}

		//context (link to project)
		$j('<div></div>')
			.addClass('scene-context')
			.attr('title',Locale7.get('project','show'))
			.data('context',info.context.id)
			.html('<img src="media/icon_project.png"><span>'+info.context.name+'</span>')
			.click(function(e) {
				
				dbg.msg('data=',$j(this).data('context'));
			
				if (obj.portal.state!='direct')
				{
					obj.portal.openProject($j(this).data('context'));
					
				}
				e.stopPropagation();
			})
			.appendTo('#scene .titlebar:first');
		
		//detail
		var tags = info.tags? info.tags.split(' '):[];
		$j.each(tags,function(i,val) {
			tags[i] = '<a href="javascript://tag">'+val+'</a>';
		});
		
		var style = tags.length>0? 'style="margin-top:1px;"':'';
		tags = tags.length>0? tags.splice(0,3).join(''):'<em>'+Locale7.get('gui','no')+' tags</em>';
		var s = info.placecount.total>1? 's':'';
		var places = info.placecount.total==0? Locale7.get('scene','no_places'):info.placecount.total+' place'+s;
				
		$j('#scene-detail').html(
			'<img src="media/icon_scene_'+(scene.isgame? 'multi':'single')+'play.png"><span>'+this.portal.scenetypes[scene.type].name+'</span>'+
			'<img src="media/icon_scene_places.png"><span>'+places+'</span>'+
 			'<img src="media/icon_scene_maxtime.png"><span>'+info.maxtime+'</span>'+
			'<img src="media/icon_scene_cost.png"><span>'+info.cost+'</span>'+
			'<img src="media/icon_scene_info.png"><span id="scene-tags" '+style+'>'+tags+'</span>'
		);
		
		//enable tag search
		$j('#scene-tags a')
			.attr('title',Locale7.get('scene','related'))
			.click(function() {
				if (obj.portal.mode=='direct') return;
				obj.portal.find($j(this).text());
			});

		//director
		$j('#scene-director').html(
			Locale7.get('gui','by') +':&nbsp;<strong>'+ info.director.name +'</strong><br>'+
			Locale7.get('gui','on') +':&nbsp;'+ new Date(info.creationdate).format('humandate')
		);
		$j('#scene-director-image').css('background-image','url('+(info.director.image? this.portal.path+'media.srv?id='+info.director.image+'&resize=80x60':'media/icon_director.png')+')');
		
		//map: remove logo form mini map, probably not legal..
		$j('#scene-gmap ').find('#logocontrol, #copyright, div:last').hide();

		//share
		var form = document.forms['shareform'];
		var lan = this.portal.language!='en'? '/'+this.portal.language:'';

		form.url.value = window.location.host+lan+'/scene/'+this.portal.scene.id;
		this.portal.embed();
	}
	
	Gui.prototype.updateSceneStory = function(backdrop,slogan,story,media)
	{
		/*	scene story layout (including scene page backdrop)
		*/
		
		if (!story)
		{
			//backwards compatibility for scenes without storylist
			story = [{ text:portal.scene.content.background, layout:0 }];
		}
		if (!media) media = [];
		var obj = this;

		//backdrop
		if (backdrop==2)
		{
			var medium = $j.grep(media, function(m) { return m.name=='backdrop' })[0];
			var src = this.portal.path+'media.srv?id='+medium.id+'&resize=x600&format=jpg';
		}
		else
		{
			var src = backdrop==0? 'media/bg_scene_urban.jpg':'media/bg_scene_nature.jpg';
		}
		$j('#scene-backdrop-image').css('background-image','url('+src+')');

		//slogan	
		$j('<div></div>')
			.addClass('slogan')
			.html('<span>'+slogan+'<span>')
			.appendTo('#scene-story');

		//story elements
		$j.each(story, function(index) {
		
			var text = story[index].text || '';

			var elm = $j(obj.create({ id:'scene_story', text:text, layout:story[index].layout }))
				.appendTo('#scene-story')
				.find('.story-medium');
		
			//medium
			var medium = $j.grep(media, function(m) { return m.name==story[index].key })[0];
			if (medium)
			{
				var resize = story[index].layout==2? 'x'+elm.height():elm.width()+'x';
				if (medium.type=='video')
				{
					var w = story[index].layout==2? 320:200;
					var h = story[index].layout==2? 240:150;
					obj.embedMedium(elm[0],{ type:'video', id:medium.id },w,h,undefined,'#ffffff');
				}
				else
				{
					$j('<img>')
						.attr('src',obj.portal.path+'media.srv?id='+medium.id+'&resize='+resize+'>')
						.appendTo(elm);
				}
			}
			else
			{
				elm.hide();
			}

			//remove margin of first paragraph
			elm.next().find('p:first-child').css('margin-top',0);
		});

		//append clear div to force parent height		
		$j('#scene-story').append('<div class="clear"></div>');
	}
	
	Gui.prototype.createActivityPage = function()
	{
		/*	add widgets to activity menu-layer
		*/
		$j(this.layout.layers['activity'].content)
			.append(this.createMenuLayerWidget('activity-events',342))
			.append(this.createMenuLayerWidget('activity-traces',342))
			.append(this.createMenuLayerWidget('activity-uploads',342));
	
		$j.each(['events','traces','uploads'], function(i,v) {
			$j('#layer-widget-activity-'+v)
				.find('.widget-header')
				.append('<div style="padding:4px 0px 6px 7px"><img src="media/icon_'+v+'.png" style="float:left; margin-right:4px">'+Locale7.get('scene','latest_'+v)+'</div>');
		});
		
		$j('#layer-widget-activity-events').hide(); //->do not display events in activity for now (add later?)

	
		//slideshow recent uploads
		this.UGCslides = {
			
			gui:this,
			widget:$j('#layer-widget-activity-uploads'),
			slides:new Array(),
			running:false,
			
			create:function()
			{
				var obj = this;
				var header = this.widget.find('.widget-header');
				//controls
				this.gui.button('previous','browse_left',14)
					.css({
						position:'absolute', right:46, width:14, top:6
					})
					.appendTo(header)
					.find('a')
					.click(function() {
						obj.show(-1);
					});
				this.gui.button('next','browse_right',14)
					.css({
						position:'absolute', right:10, width:14, top:6
					})
					.appendTo(header)
					.find('a')
					.click(function() {
						obj.show(1);
					});
				this.playbutton = this.gui.button('next','play',18)
					.css({
						position:'absolute', right:26, width:18, top:4
					})
					.appendTo(header)
					.find('a')
					.click(function() {
						obj[obj.running? 'pause':'play']();
					});
					
				//display
				this.display = this.widget
					.find('.widget-body-content')
					.css({
						backgroundColor:'black',
						height:255
					});
				
				//slide index indicator
				this.index = $j('<div></div>')
					.attr('id','ugc-slideshow-index')
					.appendTo(this.widget.find('.widget-footer'));
			},
			
			add:function(records)
			{
				//clear current
				this.rset();
				
				var obj = this;
				$j.each(records, function(i,rec) {

					if (rec.getField('type')!='image') return true;

					obj.slides.push({
						id:rec.getField('id'),
						user:rec.getField('username') || '...',
						date:new Date(Number(rec.getField('creationdate'))).format('shortrelative')
					})
				});
				
				dbg.msg('UGCslides: add, slides=',this.slides.length);
			
				this.widget.find('.button')[this.slides.length>1? 'show':'hide']();

				//start with first slide
				if (this.slides.length>0)
				{
					this.slide = 0;
					this.play();
				}
			},
			
			show:function(s)
			{
				/*	slide fade-in
				*/
				if (this.playing) window.clearTimeout(this.playing);
				
				this.slide+= s? s:1;
				//cycle
				if (this.slide==this.slides.length+1) this.slide = 1;
				if (this.slide==0) this.slide = this.slides.length;

				var obj = this;
				var slide = this.slides[this.slide-1];
				$j('<div></div>')
					.addClass('ugc-slideshow-display')
					.html('<img src="'+this.gui.portal.path+'/media.srv?id='+slide.id+'&resize=340x255">'+
						'<div class="ugc-slideshow-caption">'+Locale7.get('gui','by')+' <strong>'+slide.user+'</strong> '+slide.date+'</div>'
					)
					.appendTo(this.display)
					.css('opacity',0)
					.click(function() {
						obj.playbutton.click();
					})
					.find('img')
					.load(function() {
						//update index
						obj.index.html(obj.slide+'/'+obj.slides.length);
						//fade-in					
						$j(this).parent()
							.css('background-Image','url('+this.src+')')
 							.animate({ opacity:1 },s? 'fast':1200,undefined,function() {
								//remove previous slide(s)
								$j(this).prevAll().remove();
							});
					});
				
				var obj = this;
				if (this.slides.length>1) this.playing = window.setTimeout(function() { obj.show() },2500);
			},
			
			rset:function()
			{
				this.pause();
				this.display.empty();
				this.index.empty();
				this.slides = new Array();
				this.widget.find('.button').hide();
			},
			
			play:function()
			{
				this.running = true;
				this.playbutton.css('background-image','url(media/button/pause.png)').blur();
				this.show();
			},
			
			pause:function()
			{
				if (this.playing) window.clearTimeout(this.playing);
				this.running = false;
				this.playbutton.css('background-image','url(media/button/play.png)').blur();
			}
		};
		
		this.UGCslides.create();
	}
	
	Gui.prototype.createCommentPage = function()
	{
		/*	add widgets to comment menu-layer
		*/
		
		$j(this.layout.layers['comment'].content)
			.append(this.createMenuLayerWidget('add-comment',342))
			.append('<div id="comments-list"></div>');

		var obj = this.portal;
		$j('#layer-widget-add-comment')
			.css({
				display:'none',
				marginBottom:20
			})
			.find('.widget-header')
			.append('<div style="padding:4px 0px 6px 5px"><img src="media/icon_messaging.png" style="float:left; margin-right:4px">'+Locale7.get('scene','add_comment')+'</div>')
			.parent().find('.widget-body-content')
			.append('<textarea id="add-comment-form"></textarea>')
			.append('<div id="add-comment" class="button"><a href="javascript://send" style="width:43px; background-image:url(media/button/send.png)"></a></div>')
			.find(':last').click(function() {
				obj.scene.commenting.add();
				$j(this).find('a').blur();
			});
	}
	

	Gui.prototype.createPlacesPages = function()
	{
		/*	add list widgets to events and traces menu-layers
		*/
		
		$j(this.layout.layers['events'].content)
			.append(this.createMenuLayerWidget('events'));
	
		$j(this.layout.layers['traces'].content)
			.append(this.createMenuLayerWidget('traces'));
				
		//search header
		var obj = this;
		var widgets = $j('#layer-widget-traces, #layer-widget-events')
		
		widgets.find('.widget-header')
			.data('collapsed',false)
			.bind('collapse', function(e,c) {
				var h = $j(this);

				if (c==undefined) c = !h.data('collapsed');//toggle
				h.data('collapsed',c);
			
				h.find('.result-header')[c? 'hide':'show']();
				h.next()[c? 'hide':'show']();
				h.find('.button:last a').css('background-image',c? 'url(media/button/maximize_pane.png)':'url(media/button/minimize_pane.png)');
			})
			.each(function() {
				var type = $j(this).parent().attr('id').substr(13);
				$j(this).append(obj.create({ id:'widget_search', type:type }));
			})
			.end().find('.widget-body').css('padding-top',5);
			
		//collapse
		$j(this.create({ id:'button', title:'minimize', src:'minimize_pane', w:14 }))
			.css({
				right:16, top:2
			})
			.appendTo('#layer-widget-traces .widget-header, #layer-widget-events .widget-header')
			.click(function() {
				$j(this).parent().trigger('collapse');
				$j(this).find('a').blur();
			});
			
		//input
		widgets.find('input')
			.focus(function() {
				$j(this).parent().trigger('collapse',[false]);
			});
			
			//->TODO add local filtering
			
	}

	Gui.prototype.updateWidgetList = function(type,records,offset)
	{
		/*	update widgets in menu-layers: activity, events, traces
		*/
		
		var obj = this;
		var elm = $j('#layer-widget-'+type+' .widget-body-content').empty();
		
		if (!offset) offset = 0;
		
		switch(type)
		{
			case 'activity-events': //->Not used at the moment
				if (records.length==0) return elm.html(Locale7.get('scene','noevents'));
				elm.html('records='+records.length);
				break;
				
			case 'activity-traces':
				if (records.length==0) return elm.html('<span style="padding-left:10px">'+Locale7.get('scene','notraces')+'</span>');

				$j.each(records, function(i,rec) {
					
					$j('<div></div>')
						.addClass('layer-widget-list button'+(rec.getField('islive')=='true'? ' live':''))
						.html(obj.create({
							id:'list_trace',
							type:'activity',
							name:rec.getField('team') || rec.getField('user'),
							date:rec.getField('modificationdate') || rec.getField('enddate')
						}))
						.data('play',{ id:rec.getField('id'), live:rec.getField('islive')=='true' })
						.hover(function(){
							$j(this).css('background-color','#4089a9');
						}, function() {
							$j(this).css('background-color','transparent');
						})
						.click(function() {
						
							if (!obj.portal.scene.allowplayback) return alert(Locale7.get('scene','allow_traces'));
							
							var p = $j(this).data('play');

							//select trace after getTraces
							obj.portal.scene.selectedTrace = p.id;
							obj.portal.scene.selectedLiveTrace = p.live;
							obj.portal.scene.openPlacesMenu('traces');

							//close activity layer
							obj.portal.scene.openMenu('activity',true);
						
							$j(this).find('a').blur();
						})
						.appendTo(elm);
				});
				break;

			case 'events-scheduled':
				if (records.length==0) return elm.html('<span style="padding-left:22px">'+Locale7.get('scene','noevents')+'</span>');
					
				$j.each(records, function(i,rec) {
					
					$j('<div></div>')
						.addClass('layer-widget-list button default')
 						.html(obj.create({
 							id:'list_event_scheduled',
 							name:rec.getField('name'),
 							start:rec.getField('startdate'),
 							end:rec.getField('enddate')
 						}))
						.appendTo(elm);
				});
				break;
				
			case 'events-invited':
			case 'events-publish':
				if (records.length==0)
				{
					if (type=='events-invited')
					{
						$j('#layer-widget-events-scheduled').show();
						$j('#layer-widget-'+type).hide();
					}
					return elm.html('<span style="padding-left:22px">'+Locale7.get('scene','noevents')+'</span>');
				}
				
				//$j('#layer-widget-events-scheduled').hide(); //do not show upcoming events when invitations/public events are present
				$j('#layer-widget-'+type).show();
					
				$j.each(records, function(i,rec) {
					
					var event = $j('<div></div>')
						.addClass('layer-widget-list button default')
 						.html(obj.create({
 							id:'list_event_invited',
 							name:rec.getField('name'),
 							date:rec.getField('startdate'),
 							play:rec.getField('playid'),
 							state:rec.getField('state')
 						}))
 						.appendTo(elm);
 						
 					if (type=='events-invited' && obj.portal.scene.isgame && obj.portal.scene.isgame.webplayer && new Date().getTime()>Number(rec.getField('startdate')))
 					{
						/*	add webplay option
						*/
						event.removeClass('default').addClass('play')
 							.data('roundid',rec.getField('id'))
 							.data('playid',rec.getField('playid'))
							.hover(function(){
								$j(this).css('background-color','#4089a9');
							}, function() {
								$j(this).css('background-color','transparent');
							})
							.click(function() {
							
								alert('sorry, not available in this release');
							
// 								dbg.msg('webplay: round=',$j(this).data('roundid'),' ,play=',$j(this).data('playid'));
// 								
// 								var s = obj.portal.scene;
// 
// 								//close play layer
// 								obj.portal.scene.openMenu('play',true);
// 
// 
// 								//(enable and) enter (web) play
// 								if (!s.play) s.collection.enablePlay();
// 								s.play($j(this).data('roundid'),$j(this).data('playid'));

	 							$j(this).find('a').blur();
							})
					}
					
					if (type=='events-publish')
					{
						/*	add remove button and edit style
						*/
						if (rec.getField('state')<3)
						{
							event.removeClass('default').addClass('edit')
								.data('playid',rec.getField('id'))
								.hover(function(){
									$j(this).css('background-color','#4089a9');
								}, function() {
									$j(this).css('background-color','transparent');
								})
								.click(function() {
									obj.portal.scene.publicPublish.edit($j(this).data('playid'));
									$j(this).find('a').blur();
								});
						}
							
						obj.button('remove','minimize_pane',18)
							.css({
								position:'absolute', right:46, width:14, top:3
							})
							.appendTo(event)
							.find('a')
 							.data('playid',rec.getField('id'))
							.click(function(e) {
								obj.portal.scene.publicPublish.remove($j(this).data('playid'));
								e.stopPropagation();
							});
					}
				});
				break;
				
			case 'traces':
			
				this.updateSearchHeader(this.layout.layers['traces'],records,offset);
			
				for (var i=offset; i<Math.min(records.length,offset+10); i++)
				{
					var rec = records[i];
				
					$j('<div></div>')
						.addClass('layer-widget-list button default')
						.html(this.create({
							id:'list_trace',
							name:rec.getField('team') || rec.getField('user'),
							date:rec.getField('modificationdate')// || rec.getField('enddate')
						}))
						.data('playid',rec.getField('id'))
						.hover(function(){
							$j(this).css('background-color','#4089a9');
						}, function() {
							$j(this).css('background-color','transparent');
						})
						.click(function() {
							obj.portal.scene.playback(obj.portal.state=='live' || obj.portal.state=='play'? 'live':'playback',$j(this).data('playid'));
							$j(this).find('a').blur();
						})
						.appendTo(elm);
				};
				break;
				
			case 'events':
			
				var list = [];
				var n = 10;
				for (var i=offset; i<Math.min(records.length,offset+n); i++)
				{
					var rec = records[i];
					
					//remove scheduled events (no playback data)
					if (!rec.getField('lastplaydate'))
					{ 
						n++;
						continue
					};
					
					list.push(rec);
			
					$j('<div></div>')
						.addClass('layer-widget-list button default')
 						.html(this.create({
 							id:'list_event',
 							name:rec.getField('name'),
 							date:rec.getField('startdate'),
 							hiscore:rec.getField('hiscore')
 						}))
						.data('eventid',rec.getField('id'))
						.hover(function(){
							$j(this).css('background-color','#4089a9');
						}, function() {
							$j(this).css('background-color','transparent');
						})
						.click(function() {
							obj.portal.scene.playback('playback-event',$j(this).data('eventid'));
							$j(this).find('a').blur();
						})
						.appendTo(elm);
				};

				this.updateSearchHeader(this.layout.layers['events'],list,offset);
				break;
				
			case 'event-traces':
			case 'play-traces':
			
				var elm = $j('#layer-widget-event-'+this.portal.scene.selectedEvent.id+' .widget-body-content .event-traces').empty();
				
				$j.each(records, function(i,rec) {
				
					var div = $j('<div></div>')
						.addClass('layer-widget-list button default')
						.html(obj.create({
							id:'event_trace',
							name:rec.getField('team') || rec.getField('user'),
							color:rec.getField('color'),
							rewards:rec.getField('rewards'),
							score:rec.getField('score')
						}))
						.data('playid',rec.getField('id'))
						.hover(function() {
							if (!$j(this).data('expanded')) $j(this).css('background-color','#222222');
						}, function() {
							if (!$j(this).data('expanded')) $j(this).css('background-color','transparent');
						})
						.click(function() {
							obj.portal.scene.selectedEvent.traces[$j(this).data('playid')].toggleStats($j(this));
 							$j(this).find('a').blur();
						})
						.appendTo(elm);
		
					//add target icon
					$j('<img src="media/icon_target.png">')
						.attr('id','trace-target-'+rec.getField('id'))
						.addClass('event-trace-target')
						.appendTo(div.find('a'))
						.hover(function() {
							$j(this).attr('src','media/icon_targetX.png');
						}, function() {
							$j(this).attr('src','media/icon_target.png');
						})
						.click(function(e) {
							e.stopPropagation();
							obj.portal.scene.selectedEvent.traces[$j(this).parent().parent().data('playid')].player.center();
						});
					
					//add colored circle to trace icon
					$j(obj.draw(div.find('.event-trace-icon'),{
						type:'circle',
						w:12, h:12,
						r:6,
						c:'#'+rec.getField('color'),
						alpha:.8
					})).css({
						position:'absolute',
						left:6, top:4,
						zIndex:-1
					});
				});

				break;
		}
	}
	
	Gui.prototype.addProjectScenes = function(project)
	{
		/*	scenes listing at top of project layer
		*/
		var layer = project.layer;
		var div = document.createElement('div');
			div.id = 'project-scenes-list';
			div.innerHTML = '<div style="position:absolute; left:40px; right:40px; top:-20px; height:132px; z-index:1;"></div>';
	 		div.innerHTML+= '<div id="project-header-bg"></div>';
			
		layer.div.appendChild(div);
		layer.sceneslist = div;
		
		//set header background
		project.setHeaderBg();
		
		var listcontainer = div.firstChild;
		var list = this.portal.layout.addList(this.portal.projectscenes,project.layer,listcontainer,'project',{ type:'cloud', style:{ page_h_margin:30, page_v_margin:0 } });
		
		this.portal.lists['project'] = list;
		this.portal.projectscenes.getList = function() { return list }; //add list ref to scenes collection

		//extend layer resize with listupdate
		project.layer.extendUpdate['scenes_list'] = function()
		{
			if (!layer.visible) return;
			if (layer.listupdate) window.clearTimeout(layer.listupdate);
			layer.listupdate = window.setTimeout(function() { list.update() },250);
		}
	}
	

	/*	layer features
	*/

	Gui.prototype.enableLoginMenu = function()
	{
		/*	create login menu gui:
			input form, logged in/out status */
	
		var login = this.layout.layers['portal'].menubar['login'];
		var obj = this.portal;
		
		$j(login.div).click(function(){
 			if (login.expanded) return;
 			if (!obj.login.id) login.expand(1);

			//reset search layer
			obj.find(0,true);
//  			obj.layout.hideLayers(['find']);
//  			obj.gui.updateSearchResult('reset');
		});

		login.expand = function(expand)
		{
			this.content.style.visibility = 'hidden'; //hide while update
			this.expanded = expand;
			this.prevw = this.w;
			this.w = (this.expanded)? 480:90;
			this.div.style.width = this.w +'px';
			//redraw bg
			obj.gui.shear(this.bg,this.w,23,this.c,this.c2);
			
			if (!this.expanded)
			{
				/*	login or logout menu
				*/
				if (!obj.login.id) 
				{
					this.setContent('<a href="javascript://login" style="left:6px; width:65px; height:22px; background-image:url(media/menu/login.png)"></a>');
				}
				else
				{
					this.setContent( obj.gui.create('logout') );
					
					$j(this.content).find('div, a:first')
						.attr('title',Locale7.get('gui','profile').replace('$1',obj.login.loginname))
						.click(function(e) {
							obj.login.profile.edit();
							e.stopPropagation();
						});
					
					$j(this.content).find('a:last')
						.attr('title',Locale7.get('gui','logout'))
						.click(function(e) {
							obj.logout();
							e.stopPropagation();
						});
				}
			}
			else
			{
				/*	login form
				*/
				this.setContent( obj.gui.create('loginform') );
				
				//cancel and login buttons
				var loginobj = this;
				
				$j('<div class="button"><a href="javascript://cancel" style="width:18px; background-image:url(media/button/close.png)"></a>')
					.css({ width:18, right:69, top:3 })
					.appendTo($j(this.content))
					.click(function(e) {
						loginobj.expand(0);
						e.stopPropagation();
					});
					
				$j('<div class="button"><a href="javascript://login" style="width:43px; background-image:url(media/button/login.png)"></a>')
					.css({ width:43, right:25, top:3 })
					.appendTo($j(this.content))
					.click(function() {
						obj.doLogin();
					});
					
				
				//lost password
				$j('#lostpassword').click(function() {
					obj.lostPassword();
				});
				
				//form focus and enter-key handling
				var form = document.forms['loginform'];
				window.setTimeout(function() { form.login.focus() },100);

				var submit = function(e)
				{
					if (!e) e = event;
					if (e.keyCode==13) return obj.doLogin('login');
				}
				form.password.onkeydown = submit;
				form.login.onkeydown = submit;
				
				//auto-login toggle
				form.autologin.checked = obj.login.set_autologin;
				utils.over(form.autologin.nextSibling,obj.login.set_autologin);
				form.autologin.onchange = function() { obj.setAutoLogin('toggle') }
			}
			
			//reveal content
			this.content.style.visibility = 'visible';
		}
	}
	
	Gui.prototype.updateSearchResult = function(list,offset,my)
	{
		/*	create and display search result list, 
			with offset navigation if length>20	*/
			
		offset = offset || 0;
		var m = my? 'my':'';
		var layer = this.layout.layers[my? 'myfind':'find'];
		//var header = $j(my? '#my-search-header':'#search-header').find('.search-result-header')[0];
		
		//reset layer
		$j(layer.content).empty();
		layer.setSize(undefined,100);

		//update header (and layer if return true)
		if (this.updateSearchHeader(layer,list,offset))
		{
			//height of layer
			var h = (list.length<20 || list.length-offset<20)? Math.min(list.length,list.length-offset)*20:400;
			layer.setSize(undefined,100+h);
			
			//create and add list elements
			var obj = this;
			var by = Locale7.get('gui','by');
			var off = my? 50:150;
			var maxl = Math.round((layer.w-off)/7); //max chars for scene name
			
			for (var i=offset; i<Math.min(offset+20,list.length); i++)
			{
				var name = list[i].getField('name');
				if (name.length>maxl) name = name.substring(0,maxl)+'..';
				
				var user = list[i].getField('user');
				if (user.length>10) user = name.substring(0,10)+'..';
			
				var str = '<img src="media/icon_scene_list_'+list[i].getField('state')+'.png">';
					str+= name;
					if (!my) str+= ', '+by+' <b>'+user+'</b>';
	
				$j('<div></div>')
					.addClass('search-result-listelement')
					.attr('title','"'+list[i].getField('name')+'" '+by+' '+list[i].getField('user'))
					.data('index',i)
					.css('margin-top',i==offset? 8:0)
					.html(str)
					.hover(function(){
						$j(this).css('background-color','#4089a9');
					}, function() {
						$j(this).css('background-color','transparent');
					})
					.click(function() {
						obj.portal[my? 'myFind':'find'](undefined,true);
						//layer.hide();
						obj.portal.foundScene($j(this).data('index'),my);
					})
					.appendTo(layer.content);
			}
		}
	}

	Gui.prototype.updateSearchHeader = function(layer,list,offset)
	{
		/*	update search result header, 
			generic, used for (my)find and menu-layer widgets */
			
		var header = $j(layer.div).find('.result-header').children().empty().end();
		var type = layer.id.indexOf('find')!=-1? 'find':layer.id;
		var max = type=='find'? 20:10;
		var my = layer.id.indexOf('my')!=-1;

		if (typeof(list)=='string')
		{
			switch(list)
			{
				case 'searching':
					var s = '';
					if (my) s = 'my';
					if (layer.id=='traces' || layer.id=='events') s = 'widget';
					
					header.find('.result-nav')
						.html('<img src="media/loading_'+s+'search.gif" style="position:absolute; right:0px; top:-1px">');
					break;
					
				case 'reset':
					header.find('input').val('');
					break;
			}		
			
			//reset layer height
			return false;
		}

		if (list.length==0)
		{
			var str = Locale7.get('gui','find_zero').replace('$1',type=='find'? 'scenes':type);
		}
		else
		{
			var str = (list.length>100)? '100+ ':list.length+' ';
				str+= type=='find'? 'scene':type.substr(0,type.length-1);
				if (list.length>1) str+= 's';
				str+= ' '+Locale7.get('gui','find_result');
		}
		header.find('.result-count').html(str);
		
		//paging
		var nav = header.find('.result-nav');
		if (list.length>max)
		{
			nav.html((offset+1)+'...'+Math.min(offset+max,list.length));
			
			//prev
			if (offset>0)
			{
				addButton(this,'left',offset-max,my);
			}
			//next
			if (offset<list.length-max && offset<100-max)
			{
				addButton(this,'right',offset+max,my);
			}
		}
		
		var obj = this;
		function addButton(gui,style,offset)
		{
			$j(gui.create({ id:'button', title:style=='left'? 'prev':'next', src:'browse_'+style }))
				.css({ width:14, top:0 })
				.css(style,0)
				.appendTo(nav)
				.find('a').click(function() {
					if (type=='find')
					{
						obj.updateSearchResult(list,offset,my);
					}
					else
					{
						obj.updateWidgetList(type,list,offset);
					}
					this.blur();
				});
		};
		
		return true;
	
	
	}

	Gui.prototype.createGroupHeader = function(type)
	{
		/*	modify project and myscenes layer appearance (add multiple shears)
		*/
		var layer = this.layout.layers[type];
		//var color = (type=='project') ? '#b3b386':'#6e0000';
		//var color = (type=='project') ? '#a4a5a6':'#6e0000';
		var color = (type=='project') ? '#dadaa5':'#6e0000';
		var stroke = (type=='project') ? '#02405f':'#035e8b';//196b94';//035e8b
		
		
		//stroke default shear
		this.shear(layer.header,42,30,color,{color:stroke,fill:color}); //146288 /00628c
		
		//add two more shears to create 'group' icon
		var shear = this.shear(layer.div,38,27,color,{color:stroke,fill:color});
			shear.style.zIndex = 5001;
			shear.style.left = '11px';
			shear.style.top = '22px';
		var shear = this.shear(layer.div,38,27,color);
			shear.style.zIndex = 4999;
			shear.style.left = '29px';
			shear.style.top = '14px';
	}
	

	/*	scene widget
	*/
	
	Gui.prototype.createWidget = function(type,title)
	{
		/*	scene page widgets
				type	string: large (white, 600px) or small (green, 290px)
				title	string: widget title (=image) */
				
		return $j('<div class="scene-widget"></div>')
			.addClass(type)
			.attr('id','scene-widget-'+title)
			.html(
				'<div class="widget-header"><img class="widget-title" src="media/widget/label_'+title+'.png"></div>'+
				'<div class="widget-body"></div>'+
				'<div class="widget-footer"></div>'
			);
	}
	
	Gui.prototype.createMenuLayerWidget = function(title,w)
	{
		/*	menu layer widgets
				title	string: widget title
				[w]		number: width in pixels, defaults to 270+border */

		w = w? w+2:272;

		return $j('<div class="layer-widget"></div>')
			.attr('id','layer-widget-'+title)
			.css('width',w)
			.html(
				'<div class="widget-header">'+
					'<div class="widget-bg header-left"></div><div class="widget-bg header-right"></div>'+
				'</div>'+
				'<div class="widget-body">'+
					'<div class="widget-bg body-left"></div><div class="widget-bg body-right"></div>'+
					'<div class="widget-body-content"></div>'+
				'</div>'+
				'<div class="widget-footer">'+
					'<div class="widget-bg footer-left"></div><div class="widget-bg footer-right"></div>'+
				'</div>'
			);
	}


	/*	public panes
	*/
	
	Gui.prototype.createCommonPanes = function()
	{
		/*	common panes:
			help, mediumview, ... */
			
		//help
		var help = this.layout.panes.create( { 
			id:'help',
			x:90, y:68, w:300, h:220, z:101,
			//hcolor:'#f2f2f2',
			hcolor:'#c9c996',
			color:'bl',
			dragable:true, marginleft:17, margintop:15,
			resize:{ min_x:150, min_y:150 },
			parent:this.layout.layers['portal'].div
		} );
		help.addCloseButton();
		help.setTitle(this.create( { id:'panetitle', title:'help' } ) );
		help.setContent('<div style="width:90%"></div>');
		help.setContent = function(str) { this.content.firstChild.innerHTML = str };
	

		/*	//-> removed for now
		
			//media viewer
			var view = this.layout.panes.create( { 
				id:'viewmedium',
				x:65, y:65, w:340, h:260, z:100,
				hcolor:'#f2f2f2',
				color:'bl',
				dragable:true, marginleft:17, margintop:15,
				resize:{ min_x:320, min_y:240 },
				parent:scenelayer.div
			} );
			view.addCloseButton();
			view.setTitle('<span style="margin-left:20px; font-weight:bold">medium (view)</span>')
			//dragable on entire pane
			view.header.onmouseover = null;
			view.header.onmouseout = null;
			view.drag.setEnabled(true);
		
			//rollover for places
			var tooltip = this.layout.panes.create( {
				id:'place_info',
				x:100, y:100, w:100, h:0,
				color:'bl',
				style:'tooltip',
				hcolor:'#f2f2f2',
				dragable:false,
				delay:250,
				//marginleft:17,margintop:15,
				parent:sceneplay.div
			} );
		*/		
	}

	
	/*	playback
	*/

	Gui.prototype.createPlacePane = function(place,w,h)
	{
		/*	content pane for expanded places
		*/
	
		//'place' style pane	
		var pane = this.layout.panes.create( {
			id:'place',
			x:place.x+(place.w/2) - ((w/2)+19), 
			y:place.y+place.h - (h+68), 
			w:w, h:h,
			style:place.type=='ugc'? 'ugcplace':'place',
			hcolor:'#f2f2f2',
			close:function() { place.collapse() },
			parent:this.layout.gmap.getPane(google.maps.MAP_FLOAT_PANE)
		} );
		//modify content appearance
		pane.content.style.left = '15px';
		pane.content.style.width = w +22 +'px';
	
		//add pane shadow
		var shadow = document.createElement('img');
			shadow.style.display = 'none';
			shadow.style.position = 'absolute';
			shadow.style.left = pane.x +'px';
			shadow.style.top = pane.y + pane.h +70 - Math.round(0.65 * pane.h) +'px';
			shadow.style.width = Math.round(1.675 * pane.w)  +'px';
			shadow.style.height = Math.round(0.65 * pane.h) +'px';
			shadow.style.zIndex = pane.z;
			//ie png fix
			if (browser.cssfilter) shadow.onload = function() { utils.pngFix(this) }; 
			shadow.src = 'media/pane_shadow2.png';
		//show shadow
		this.layout.gmap.getPane(google.maps.MAP_FLOAT_SHADOW_PANE).appendChild(shadow);
	
		place.pane = pane;
		place.pane.shadow = shadow;
	
		//add editing features to pane (move, delete, etc)
		if (place.state=='direct') place.scene.editPlace(place); 
		
		//title
		var displayname = (place.name.length>=30)? place.name.substring(0,28)+'..':place.name;
		if (place.ugctype) displayname = '<span style="font-weight:normal">'+Locale7.get('scene','uploaded_by')+'</span> '+displayname;
		var edit = (place.state=='direct')? '_edit':'';
		var state = (place.type=='trade' && place.meta.peerid)? '_paired':'';
		var type = place.ugctype || place.type
		pane.setTitle(this.portal.gui.create( { id:'place_title'+edit, type:type+state, name:displayname } ) );
		
		//block googlemap events
		pane.div.onmousedown = function(e) { utils.cancelEvents(e) } 
		pane.div.ondblclick = function(e) { utils.cancelEvents(e) }
		
	
		/*	add content panels
		*/
		
		//-> determine panels based on type and templace, CHANGE TO FACTORY LATER ?
		var panels = new Array();
		
		//preview ('public places') on/off panel for places in direct
		if (place.state=='direct')
		{
			panels.push(
				{ t:'public', e:false }
			);
		}
		
		switch (place.type)
		{
			/*	panels per place type,
				t:id/title (required), e:expanded, c:collapsable */
			
			case 'task':
			
				if (place.state=='direct')// && place.tasktype)
				{
					panels.push(
						{ t:'task_info', title:'intro' },
						{ t:'task_all', title:'task' },
						{ t:'rewards' , e:true }
					);
				}
				else
				{
					panels.push(
						{ t:'task', c:false },
						{ t:'quiz', e:true, c:false },
						{ t:'rewards' , e:true, c:false }
					);
				}
					
				if (place.state=='direct')
				{
					panels.push( { t:'outro', e:false } );
					if (place.scene.has_place_conditions) panels.push( {t:'conditions',e:false} );
				}
				break;
	
			case 'text':
				panels.push( {t:place.type, title:this.portal.template.placeNames[place.type], c:false} );
				break;
				
			case 'image':
			case 'audio':
			case 'video':
				dbg.msg('media placetype, scenetype=',place.scene.type);
				
				//->TEMPLATE, change later
				var template_panel1, template_panel2;
				if (place.scene.type=='storylinetm') {
					// Fijne hack hoor.
					// Always allow optional radius (meters) aorund geopoint
					// for storylinetm (extrapassenger) places
					template_panel1 = { t:'radius'};

					// Additional: sound fx for storylinetm audio places
					if (place.type=='audio') {
						template_panel2 = { t:'soundfx'};
					}
				}
	
				panels.push( 
					{ t:place.type, title:this.portal.template.placeNames[place.type], c:false },
					{ t:'description', e:(place.state!='direct'), c:(place.state=='direct') }, 
					template_panel1,
					template_panel2
				);
				//panels.push( {t:place.type,c:false}, {t:'description',c:(place.state=='direct')}, template_panel );
				break;
				
			case 'multimedia':
				dbg.msg('multimedia placetype, scenetype=',place.scene.type);
				break;
				
			case 'trade':
				panels.push( { t:'description' } );
				if (place.state=='direct') panels.push ( { t:'radius'} );
				panels.push( { t:'trade', title:'peer place' } );
				break;
				
			case 'reward':
				panels.push( {t:'reward'} );
				break;
			
			case 'ugc':
				panels.push( {t:place.ugctype, title:this.portal.template.placeNames[place.ugctype], c:false} );
				break;
		}

		//add panels to pane		
		for (var i=0; i<panels.length; i++)
		{
			if (panels[i]==undefined) continue;
			pane.addPanel( {
				id:panels[i].t,
				title:panels[i].title || panels[i].t,
				content:(place.state=='direct')? this.create( { id:'place_'+panels[i].t, place:place } ):'',
				expanded:panels[i].e,
				collapse:true//panels[i].c
			} );
		}
	}
	
	Gui.prototype.createTraceWidget = function(trace)
	{
		var widget = $j(this.createMenuLayerWidget('trace-'+trace.id))
			.insertAfter('#layer-widget-traces')
			.find('.widget-header')
			.css('background','transparent url(media/widget/bg_trace_header.png) 1px 1px no-repeat')
			.append(this.create({ id:'trace_header', trace:trace }))
			.end();
			
			//resizing
			widget.find('.widget-body-content')
				.html(this.create('trace_body'))
				.css('height',46)
				.resizable({
					handles:'s',
					minHeight:30
				});
			$j('<img src="media/icon_resize.png">')
				.css({
					position:'absolute', right:3, bottom:3
				})
				.appendTo(widget.find('.widget-footer'));
				
			//events
			widget.find('.trace-events img:last').hover(function() {
					if (trace.eventlink) $j(this).attr('src','media/trace_event_selectX.png');
				}, function() {
					if (trace.eventlink) $j(this).attr('src','media/trace_event_select.png');
				})
				.click(function() {
					if (trace.eventlink) trace.eventlink();
				});
					
			//close, collapse
			$j(this.create({ id:'button', title:'close', src:'close_pane', w:14 }))
				.css({
					right:16, top:2
				})
				.appendTo(widget.find('.widget-header'))
				.click(function() {
					trace.close();
				});
			$j(this.create({ id:'button', title:'minimize', src:'minimize_pane', w:14 }))
				.css({
					right:32, top:2
				})
				.appendTo(widget.find('.widget-header'))
				.click(function() {
					trace.minimize();
					$j(this).find('a').blur();
				});

			//locate
			$j(this.create({ id:'button', title:'find', src:'target', w:24 }))
				.css({
					left:0,	top:0, display:'none'
				})
				.appendTo(widget.find('.trace-follow'))
				.find('a')
				.css('padding-left',18)
				.append(Locale7.get('gui','find'))
				.click(function() {
					trace.player.center();
					this.blur();
				});
				
			//trace toggle
			$j('<a href="javascript://trace"><img src="media/icon_checkbox.png" style="float:left; border:0px; margin-right:4px">trace</a>')
				.addClass('trace-toggle')
				.appendTo(widget.find('.trace-follow'))
				.click(function() {
					trace.trace = !trace.trace;
					trace.player.enableTrace(trace.trace);
					
					$j(this).children(':first').attr('src',trace.trace? 'media/icon_checkboxX.png':'media/icon_checkbox.png')
					this.blur();
				});
			
			//show or hide controls
			widget.find('.trace-control')[trace.mode!='live'? 'show':'hide']();
	
		return widget;	
	}
	
	Gui.prototype.createEventWidget = function(event)
	{
		var widget = $j(this.createMenuLayerWidget('event-'+event.id))
			.insertAfter('#layer-widget-events')
			.find('.widget-header')
			.css('background','transparent url(media/widget/bg_trace_header.png) 1px 1px no-repeat')
			.append(this.create({ id:'event_header', event:event.event }))
			.end();

		//resizing
		widget.find('.widget-body-content')
			.html(this.create('event_body'))
//				.css('height',46)
			.find('.event-events')
			.css('height',46)
			.resizable({
				handles:'s',
				minHeight:30
			});
		$j('<img src="media/icon_resize.png">')
			.css({
				position:'absolute', right:3, bottom:3
			})
			.appendTo(widget.find('.widget-footer'));

		//events
		widget.find('.trace-events img:last').hover(function() {
				if (event.play.eventlink) $j(this).attr('src','media/trace_event_selectX.png');
			}, function() {
				if (event.play.eventlink) $j(this).attr('src','media/trace_event_select.png');
			})
			.click(function() {
				if (event.play.eventlink) event.play.eventlink();
			});
	
		//close
		var obj = this;
		$j(this.create({ id:'button', title:'close', src:'close_pane', w:14 }))
			.css({
				right:16, top:2
			})
			.appendTo(widget.find('.widget-header'))
			.click(function() {
				obj.portal.scene.selectedEvent.dispose();
			});


		//traces toggle
		$j('<a href="javascript://traces"><img src="media/icon_checkbox.png" style="float:left; border:0px; margin-right:4px">traces</a>')
			.addClass('event-traces-toggle')
			.css('border',0)
//			.addClass('trace-toggle')
			.appendTo(widget.find('.event-control'))
			.click(function() {
 				event.play.trace = !event.play.trace;
 				
 				for (id in event.traces)
 				{
 					if (typeof(event.traces[id])!='object' || id=='event') continue;
 					
					event.traces[id].trace = !event.traces[id].trace;
					event.traces[id].player.enableTrace(event.traces[id].trace);
				}

// 				trace.player.enableTrace(trace.trace);
				
				$j(this).children(':first').attr('src',event.play.trace? 'media/icon_checkboxX.png':'media/icon_checkbox.png')
				this.blur();
			});

	
		return widget;
	}
	
	Gui.prototype.createTraceEvent = function(eventobj)
	{
		/*	create trace event listitem
		*/
		return $j('<div></div>')
			.addClass('trace-event')
			.append('<div class="time">'+new Date(eventobj.timestamp).format('longtime')+'</div>')
			.append('<div class="event">'+eventobj.msg+'</div>')
	}
	
	
	/*	List items //->REDESIGN, will be replaced
	*/
	
	Gui.prototype.createListItem = function(list,type,options)
	{
		/*	create a list Item
		*/
		return new ListItem(this,list,type,options);
	}
	
	function ListItem(gui,parent,type,options)
	{
		this.parent = parent; //the list object
		this.gui = gui;
		this.type = type; //'scene' or 'project' //->add 'user' later?
		
		var elm = this.create(options);
		return elm;
	}
	
	ListItem.prototype.create = function(options)
	{
		/*	create DOM elements, based on type. 
			[options]
				scale	scale of sheared canvas (scale/100) */
				

		//dbg.msg('Gui: Listitem.create, options.draft=',options.draft);
				
				
		//default canvas dimensions
		this.a = (Math.PI/180)*40; //shear angle
		this.cw = 35;//42;
		this.ch = 25;//30;
		this.s = this.ch * Math.tan(this.a);
		
		//list-elm dimensions and margin, type dependend
		switch(this.parent.type)
		{
			case 'cloud':
				var scale = (options && options.scale)? options.scale:1;
				
				//override canvas defaults
				var ratio = this.ch/this.cw;
				this.cw = Math.round(scale * 100);
				this.ch = Math.round(ratio * this.cw);
				this.s = this.ch * Math.tan(this.a);
				
				this.flip_h = Math.round(this.ch/Math.cos(this.a));

				//dropshadow distance
				this.dx = 4; 
				this.dy = 10;
				
				//div
				this.w = this.cw + 115;
				this.h = 100;
				this.margin = 5;

				var title_margin_x = this.cw +5;
				
				//var title_margin_x = this.cw -10;
				
				var title_margin_y = Math.max(0,this.h - this.ch - this.dy -29);

				break;
				
			case 'list':
				this.flip_h = Math.round(this.ch/Math.cos(this.a));

				//dropshadow distance
				this.dx = 3; 
				this.dy = 8;

				//div
				
				//dbg.msg('ListItem: parent=',dbg.obj(this.parent));
				var w = (options && options.width) ? options.width:this.parent.w-20;
				
				this.w = w;
				this.h = this.flip_h + this.dy;
				this.margin = 0;

				var title_margin_x = this.cw + this.s //-10;// +5;
				var title_margin_y = 0;
			
				break;
		}
		
		/*	common elements
		*/

		//container
		
		//if (this.parent.type=='list') dbg.msg('Gui: createListItem, w=',this.w,',h=',this.h);
		
		var div = document.createElement('div');
			div.className = 'list-item';
			div.style.width = this.w +'px';
			div.style.height = this.h +'px';
			div.style.marginBottom = this.margin +'px';
			
			//div.style.backgroundColor = 'rgb(125,209,250)';//tmp
			
		//dropshadow	
		var dropw = Math.round((this.cw+this.s) *1.03);
		var drop = document.createElement('img');
			drop.className = 'list-item-shadow';
			drop.style.left = this.dx +'px';
			drop.style.width =  dropw +'px';
			drop.style.height =  Math.round((83/175)*dropw) +'px';
			//if (browser.cssfilter) drop.onload = function() { utils.pngFix(this) }; //ie png fix
			drop.src = 'media/dropshadow_light.png';
		div.appendChild(drop);

		//flip
		var flip = document.createElement('div');
			flip.className = 'list-item-flip';
			flip.style.width = this.cw +'px';
			flip.style.height = this.flip_h +'px';
			flip.style.bottom = this.dy +'px';
// 			flip.style.backgroundColor = '#d7d7a1';
// 			flip.style.display = 'none';
		div.appendChild(flip);
		
		//shear
		//var alpha = options.draft? .5:1;
		//var style = options.draft? { color:'#d7d7a1', fill:'#666644' }:'#d7d7a1';
		//var c = options.draft? '#73734d':'#dedeab';
		//var style = options.draft? '#666644':'#d7d7a1';

		var c = '#dedeab'; 
		var style = '#d7d7a1';
		
		this.canvas = this.gui.shear(div,this.cw,this.ch,c,style);
		this.canvas.style.bottom = this.dy + 'px';
		
		if (options.draft)
		{
			if (!browser.cssfilter) this.canvas.style.opacity = .5;
		}
		
		

		/*	type specific elements
		*/

		//title
		var title = document.createElement('div');
			title.className = 'list-item-title';

			title.style.marginLeft = title_margin_x +'px';



			//title.style.marginTop = title_margin_y +'px'; //Math.max(0,this.h - this.ch - this.dy -28) +'px';
			
			title.style.paddingTop = title_margin_y +'px';
			
// 			title.style.left = this.cw +5 +'px';
// 			title.style.top = Math.max(0,this.h - this.ch - this.dy -28) +'px';

			//var textoffset = Math.round((this.h - (ratio*this.max_w)) *.45)
			//title.style.top = textoffset + (this.h - this.flip_h - this.dy -4)  +'px';
			
			
		div.appendChild(title);
	
		//details
		var details = document.createElement('div');
			details.className = 'list-item-details';
			
			details.style.marginLeft = title_margin_x +'px';
			
			//details.style.left = this.cw +5 +'px';
//				details.style.top = textoffset +26 +'px';
			details.style.display = 'none';
		div.appendChild(details);
	





		//test
// 		var test = document.createElement('div')
// 			test.style.position = 'absolute';
// 			test.style.left = '0px';
// 			test.style.bottom = this.dy +'px';
// 			test.style.border = '1px solid red';
// 			test.style.width = this.cw +'px';
// 			test.style.height = this.flip_h +'px';
// 
// 		div.appendChild(test)



		//event handling
		var obj = this;
		div.onmouseover = function() { obj.flip(true) }
		div.onmouseout = function() { obj.flip(false) }
		
		div.object = this; //ref to parent object, so we can update contents later
		
		this.div = div;
		this.flipdiv = flip;
		this.titlediv = title;
		this.detailsdiv = details;
		
		return div;
	}
	
	//ListItem.prototype.setContent = function()
	ListItem.prototype.setContent = function(name,user,creationdate,description,image,draft)
	{
		/*	set (cropped) content
		*/

 		this.titlediv.title = name; //fullname
	
		var m = this.parent.type=='list' ? 15:25
		var name = (name.length>m)? name.substring(0,m)+'..':name;
		
// 		var str = '<div style="background:url(media/bg_label_test_bl.png) no-repeat; font-weight:normal; color:#f2f2f2; font-size:11px; padding:2px 8px">'+name+'</div>'
// 		this.titlediv.innerHTML = (this.parent.type=='list')? str:name;
		
		

 		this.titlediv.innerHTML = '<span></span>'+name; //span is for live indication
// 		this.titlediv.innerHTML+= '<img class="list-item-title-bg" src="media/bg_list_item_title.png">';
 		
 		
// 		var offseth = this.titlediv.offsetHeight;
		
// 		var div = this.titlediv;
// 		var delayed = function() { div.innerHTML+= '('+div.offsetHeight+')' };
// 		window.setTimeout(delayed,100);
		
//		this.titlediv.innerHTML+= '('+offseth+')';
		
		
		var user = (user.length>12)? user.substring(0,12)+'..':user;
		var date = new Date(creationdate).format('shortdate');
		var desc = (description.length>35)? description.substring(0,35)+'..':description;

//			this.detailsdiv.top = this.titlediv.offsetTop + this.titlediv.offsetHeight +5 +'px';

//			this.detailsdiv.innerHTML = this.titlediv.style.top +'+'+ this.titlediv.offsetHeight; 
		this.detailsdiv.innerHTML = 'by <b>'+user+'</b>, '+date+'<p>'+desc+'</p>';
		
// 		var obj = this;
// 		window.setTimeout(function() { obj.detailsdiv.style.top = obj.titlediv.offsetTop + obj.titlediv.offsetHeight +5 +'px' },10);

//		return;
		
		//image
		if (image)
		{
			if (!this.image)
			{
				var container = document.createElement('div');
					container.style.position = 'absolute';
					container.style.left = '1px';
					container.style.top = '1px';
					container.style.width = Math.round(.75 * this.cw) +'px';
					container.style.height = Math.round(.75 * this.flip_h) +'px';
					container.style.overflow = 'hidden';
				var img = document.createElement('img');
					img.style.width = Math.round(.75 * this.cw) +'px';
					
				//if (draft) img.style.opacity = .4;

				container.appendChild(img);
				this.image = img;
				this.flipdiv.appendChild(container);
				
				//icon
				var icon = document.createElement('img');
					icon.style.position = 'absolute';
//						icon.style.left = '6px';
//						icon.style.top = this.h-this.h-this.dy +2 +'px';
//						icon.style.width = Math.round(.75 * this.w) +'px';

					icon.style.top = this.h-this.ch-this.dy +1 +'px';

					//var w = .60 * this.w; //->REFACTOR ?
					
// 					var w = .45 * this.cw+this.s;
// 					var h = (53/120) * w;
					
// 					icon.style.width = Math.floor(w) +'px';
// 					icon.style.height = Math.floor(h) +'px';
					
					var w = 64;
					var h = 28; 
					
					var a = (Math.PI/180) * 40; //shear angle
					var x = Math.round(h * Math.tan(a));
					
					//icon.style.left = Math.ceil(this.s - x) +'px';
					icon.style.left = Math.round(this.s - x) +'px';
					
					if (draft)
					{
						if (!browser.cssfilter) icon.style.opacity = .5;
						//else icon.style.filter = 
					}
					
					//icon.style.opacity = .7;

					//icon.style.border = '1px dotted red';
					
					//if (browser.cssfilter) icon.onload = function() { utils.pngFix(this) }; //ie png fix
				this.icon = icon
				this.div.appendChild(icon);
				
			}
			this.image.src = this.gui.portal.path+'media.srv?id='+image.id+'&resize=75x';
//			this.icon.src = this.gui.portal.path+'media.srv?id='+image.id+'&cmd=shear7&format=png';
			
			
			//this.icon.src = this.gui.portal.path+'media.srv?id='+image.id+'&cmd=shear7&format=png';

			var x = Number(image.offsetx); 
			var y = Number(image.offsety);
			var crop = (x>0? '-'+x:'+'+Math.abs(x))+(y>0? '-'+y:'+'+Math.abs(y));
			var url = 'id='+image.id+'&cmd=sceneicon&crop=64x28'+encodeURIComponent(crop)+'&geometry='+encodeURI(Number(image.scale)*100+'%')+'&format=png';
			
			this.icon.src = this.gui.portal.path+'media.srv?'+url;

		}
		else if (this.image)
		{
			this.flipdiv.removeChild(this.image.parentNode);
			delete this.image;
		}
	}

	ListItem.prototype.enable = function(enable)
	{
		/*	add or remove red shear and direct button
		*/
		
		this.enabled = enable;
		
		if (enable)
		{
			//var w = this.w/3;
			var w = 14;//this.parent.type=='cloud' ? 30:20;
			var h = 5;//this.parent.type=='cloud' ? 12:8;
			var offset = this.cw+this.s - w - h*Math.tan(this.a);
			
			this.gui.shear(this.canvas,w,h,'#920000','#750000',1,{x:offset, y:0});
		}
		else
		{
			//redraw entire canvas
			//this.gui.shear(this.canvas,this.cw,this.ch,'#dedeab','#d7d7a1');
			//this.gui.shear(this.canvas,this.cw,this.ch,'#e1e1a9','#adad82');
			//this.gui.shear(this.canvas,this.cw,this.ch,'#eaeaaf','#baba8b');
			this.gui.shear(this.canvas,this.cw,this.ch,'#f2f2b5','#eaeaaf');
		}
	}
	
	ListItem.prototype.setEnabled = function()
	{
		//replaces function above..
	}

	ListItem.prototype.setLive = function()
	{
		return;
	}

	ListItem.prototype.flip = function(flip)
	{
		this.flipped = flip;
		this.canvas.style.display = (flip)? 'none':'block';
		if (this.icon) this.icon.style.display = (flip)? 'none':'block';
		this.flipdiv.style.display = (flip)? 'block':'none';
		if (this.parent.type=='cloud') this.detailsdiv.style.display = (flip)? 'block':'none';
		this.titlediv.style.color = (flip)? '#f2f2f2':'#a0a0a0';//'#a7a0a0';//'#878080';
		//this.titlediv.lastChild.style.display = (flip)? 'none':'block';//'#a7a0a0';//'#878080';
		//this.titlediv.style.color = '#f2f2f2';//:'#a0a0a0';//'#a7a0a0';//'#878080';

		//stop or restart glowing
		if (this.live) this.liveAnim(flip);
	}

	
	/*	general gui elements
	*/
	
	Gui.prototype.button = function(title,img,w,txt)
	{
		/*	create standard href button html with optional txt
		*/
		return $j('<div></div>')
			.addClass('button')
			.html('<a href="javascript://'+title+'" style="width:'+(w||18)+'px; background-image:url(media/button/'+(img||title)+'.png)">'+(txt? txt:'')+'</a>');
	}
	
	Gui.prototype.embedMedium = function(parent,medium,w,h,options,bgcolor)
	{
		/*	use jw_flvplayer/swfobject to embed a medium
				parent		html elment container for player
				medium		medialib object (width medium.id, medium.type)
				[w,h]		dimensions of embeded medium
				[options]	optional override flashvars (e.g. controlbar=none)
				[bgcolor]	optional background color */
		
		var id = medium.id;
		var w = w || 200;
		var h = h || 150;
		var format = (medium.type=='video')? 'flv':'mp3';
		var bg = bgcolor || '#0D3040';
		
		var thumb = (medium.type=='video')? this.portal.path+'media.srv?id='+id+'%26format=jpg%26resize=x'+h+'%26.jpg':'';
		var src = this.portal.path+'media.srv?id='+id+'%26format=flv%26resize=x'+h+'%26.'+format; //we add an extension, otherwise flvplayer won't play
		var display = document.getElementById('place_medium');

		//player container div
		var div = document.createElement('div');
		div.id = 'medium_embed_'+new Date().getTime();
		parent.appendChild(div);

		//init swf player
		var playerid = 'player'+new Date().getTime();
		var flashvars = (medium.type=='video')? 'fullscreen=true':'';
			flashvars += options || '';
		
		var s1 = new SWFObject('media/player.swf',playerid,w,h,'9',bg);
		s1.addParam('allowfullscreen','true');
		s1.addParam('allowscriptaccess','always');
		s1.addParam('wmode','opaque');
		s1.addParam('flashvars','file='+src+'&image='+thumb+'&skin=media/nacht.swf&'+flashvars);
		s1.write(div.id);
		
		return playerid;
	}
	
	Gui.prototype.shear = function(elm,w,h,color,style,alpha,add)
	{
		/*	(create and) draw sheared canvas graphic, params:
				elm			parent to append shear, or canvas element (for color update or draw over)
				w,h			(non sheared) dimensions
				color		base color of shear
				[style]		optional color value for gradient (bottom color) or object for stroked shear (style.color, [style.width], [style.fill])
				[alpha]		optional opacity (0-1)
				[add]		draw onto existing canvas with offsets (add.x, add.y)
				//[extend]	draw over right edge (used for menu.setSelected()) */

		//dimensions
		var a = (Math.PI/180) * 40; //shear angle
		var s = h * Math.tan(a);
		var width = Math.round(w+s);
		
		//get canvas context (create if needed)
		if (elm.nodeName.toLowerCase()!='canvas')
		{
			var canvas = document.createElement('canvas');
				canvas.width = width;
				canvas.height = h;
				canvas.style.width = width +'px'; //needed for safari
				canvas.style.height = h +'px';
			elm.appendChild(canvas);
			//init canvas element for IE
			if (typeof(G_vmlCanvasManager)=='object') canvas = G_vmlCanvasManager.initElement(canvas);
			var ctx = canvas.getContext('2d');
		}
		else
		{
			var canvas = elm;
			var ctx = elm.getContext('2d');
			if (!add)
			{
				//resize if needed
				if (elm.width!=Math.round(w+s)) { elm.width = Math.round(w+s); elm.style.width = Math.round(w+s) +'px' };
				if (elm.width!=h) { elm.height = h; elm.style.height = h +'px' };
			}
		}
		
		//clear current canvas
		if (!add)
		{
			var a = { x:0, y:0 };
			ctx.clearRect(0,0,w+s,h);
		}
		else
		{
			a = add;
		}

		ctx.globalAlpha = alpha || 1;
		
		ctx.beginPath();
		ctx.moveTo(a.x+s,a.y+0);
		ctx.lineTo(a.x+w+s,a.y+0);
		ctx.lineTo(a.x+w,a.y+h);
		ctx.lineTo(a.x+0,a.y+h);

		if (style) 
		{
			if (typeof(style)=='object')
			{
				if (style.fill)
				{
					ctx.fillStyle = style.fill;
					ctx.fill();
				}
				
				//stroked path (with offset for sharp pixellines)
				ctx.lineWidth = style.width || 1;
				ctx.strokeStyle = style.color;

				var offset = ctx.lineWidth/2;
				ctx.beginPath();

// 				ctx.moveTo(s+offset,0+offset);
// 				ctx.lineTo(w+s-offset,0+offset);
// 				ctx.lineTo(w-offset,h-offset);
// 				ctx.lineTo(0+offset,h-offset);
// 				ctx.lineTo(s+offset,0+offset);

				ctx.moveTo(s,0+offset);
				ctx.lineTo(w+s-offset,0+offset);
				ctx.lineTo(w,h-offset);
				ctx.lineTo(0+offset,h-offset);
				ctx.lineTo(s,0+offset);
				
				ctx.stroke();
			}
			else
			{
				//use gradient
				var gradient = ctx.createLinearGradient(0,0,0,h);
					ctx.globalAlpha = alpha || 1;
					gradient.addColorStop(0, color);
					ctx.globalAlpha = alpha || 1;
					gradient.addColorStop(1, style);
				ctx.fillStyle = gradient;
				ctx.fill();
			}
		}
		else 
		{
			ctx.fillStyle = color;
			ctx.fill();
		}
		
		//force canvas dimensions for IE
		if (!add)
		{
			$j(canvas).find('div').css({
				width:width,
				height:h
			});
		}
		
		return canvas;
	}

	Gui.prototype.draw = function(elm,p)
	{
		/*	create canvas with drawn shape of p.type, append to elm
		*/
		var c = document.createElement('canvas');
			c.width = p.w;
			c.height = p.h;
			c.style.width = p.w +'px';
			c.style.height = p.h +'px';

		elm.append(c);
		
		//init canvas element for IE
		if (typeof(G_vmlCanvasManager)=='object') c = G_vmlCanvasManager.initElement(c);

		var ctx = c.getContext('2d');
		
		//draw
		switch (p.type)
		{
			case 'circle':
				var x = (p.w/2);
				var y = (p.h/2);
				var r = p.r;
				ctx.beginPath();
				ctx.globalAlpha = p.alpha || 1;
				ctx.arc(x,y,r,0,2*Math.PI,true);
				ctx.fillStyle = p.c;
				ctx.fill();
				break;
		}


		//force canvas dimensions for IE
		$j(c).find('div').css({
			width:p.w,
			height:p.h
		});
		
		return c;
	}


	/* HTML
	*/
	
	Gui.prototype.create = function(p)
	{
		/*	build and return (HTML) string,
			p = properties object (with p.id) or an id */
	
		var id = (typeof(p)=='object')? p.id:p;
		var str = '';
		var pngfix = (browser.cssfilter)? 'onload="utils.pngFix(this)"':'';
		var clear = '<div class="clear"></div>'; //fix for element height with floating child elements
	
		var toggle_check = 'this.previousSibling.click();utils.over(this,this.previousSibling.checked)';
		var check_on = 'if (!this.form[this.name+\'_on\'].checked) { this.form[this.name+\'_on\'].checked=true; utils.over(this.form[this.name+\'_on\'].nextSibling,1); }';
		
		switch(id)
		{
			/*	general gui
			*/
			
			case 'button':
			if (0) var button = { }; //dummy object for easy access in editor, can be removed in prod version
				
				var w = p.w || 18;
				str+= '<div class="button">';
				str+= '<a href="javascript://'+p.title+'" style="width:'+w+'px; background-image:url(media/button/'+p.src+'.png)"></a>';
				str+= '</div>';
				break;
			
			case 'layertitle':
			if (0) var layertitle = { };
			
				var m = p.margin? ' margin:'+p.margin+';':'';
				var w = p.w? ' width:'+p.w+'px;':'';
				var h = p.h? ' height:'+p.h+'px;':'';
				str+= '<img src="media/label_'+p.title+'_layertitle.png" class="title" style="'+w+h+m+'">';
				break;
				
			case 'projecttitle':
			if (0) var projecttitle = { };

				var margin = this.portal.production? '2px 5px 0px 21px':'2px 0px 0px 16px'; //->REMOVE this when prodserver is identical to testserver
				str+= '<img src="media/generated/labels/label.php?n='+p.title+'" class="title" style="margin:'+margin+'">';
				break;
				
			case 'layermenu':
			if (0) var layermenu = { };

				var w = p.w || 66;
				var h = p.h || 22;
				var margin = p.margin? 'margin:'+p.margin:'';
				str+= '<img src="media/label_'+p.title+'_layermenu.png" '+pngfix+' style="width:'+w+'px; height:'+h+'px; '+margin+'">';
				break;

			case 'label':
			if (0) var label = { };
				
				//p.w, p.h required
				var margin = p.margin? 'margin:'+p.margin:'';
				str+= '<img src="media/label_'+p.src+'.png" '+pngfix+' style="width:'+p.w+'px; height:'+p.h+'px; '+margin+';">';
				break;
				
			case 'language':
			if (0) var language = { };

				//toggle language display, change when more languages become available
				var lan = p.lan=='nl' ? 'en':'nl';
				var title = lan=='nl'? 'toon helpteksten in het Nederlands':'display support texts in English';
				
				var title = Locale7.get('gui','menu_language_title');
				
				str+= '<img class="language-menu" src="media/icon_language_'+lan+'.png" title="'+title+'">';
				break;

			case 'loginform':
			if (0) var loginform = { };
			
				var preset_loginname = this.portal.dev_login || ''; //->for development, REMOVE
				var preset_password = this.portal.dev_login_pass || '';
	
				var login = this.portal.login.loginname || preset_loginname;
				var pass = this.portal.login.password || preset_password;
				
//				var margin = (browser.cssfilter)? 2:1; //visual tweak for IE
				str+= '<div style="margin-top:1px">';
				str+= '<form name="loginform" method="" action="" class="loginform" onsubmit="return false">';
				str+= '<div>name:</div>';
				str+= '<input type="text" name="login" value="'+login+'" class="inputtext" style="width:70px; margin-top:0px; font-size:11px">';
				str+= '<div>password:</div>';
				str+= '<input type="password" name="password" value="'+pass+'" class="inputtext"  style="width:70px; margin-top:0px; font-size:11px">';
				//forgot		
				str+= '<div style="margin-left:5px"><span id="lostpassword" title="'+Locale7.get('gui','retrieve_pw')+'">forgot</span></div>';
				str+= '<div><span> | </span></div>';
				//remember		
				str+= '<div title="'+Locale7.get('gui','save_pw')+'">';
				str+= '<input type="checkbox" name="autologin" style="display:none">';
				str+= '<img src="media/icon_checkbox.png" class="checkbox" style="float:left; margin:0px 5px 1px 0px;" onclick="'+toggle_check+'" '+pngfix+'>';
				str+= '<span onclick="this.previousSibling.previousSibling.click();utils.over(this.previousSibling,this.previousSibling.previousSibling.checked)">remember</span>';
				str+= '</div>';
				str+= '</form>';
				str+= '</div>';
				break;

			case 'logging_in':
			if (0) var logging_in = { };
			
				str+= '<div class="loginform"><img src="media/logging_in.gif" style="float:right; margin-right:26px; width:16px; height:16px; margin-top:2px"></div>';
				break;
				
			case 'panetitle':
			if (0) var panetitle = { };
			
				str+= '<img src="media/label_'+p.title+'.png" '+pngfix+' class="pane7-title-image">';
				break;
				
			case 'scenename':
			if (0) var scenename = { };
			
				str+= '<div style="position:absolute; left:'+p.x+'px; top:5px; font-size:13px; font-weight:bold; color:#f2f2f2;"></div>';
				break;
				
			case 'search':
			if (0) var search = { };
			
				var subclass = p.type? ' '+p.type:''; 
				var type = p.type || '';
				var src = type=='' || type=='my'? 'clear_'+type+'find':'close_pane';
				
				str+= '<div style="padding:10px 10px 5px 15px;">';
				str+= '<form name="'+type+'searchform" onsubmit="return false">';
				str+= '<input name="query" type="text" class="scene-form'+subclass+'" style="width:140px;">';
				str+= '<div class="button" style="left:162px; top:13px"><a href="javascript://clear" style="width:14px; background-image:url(media/button/'+src+'.png)"></a></div>';
				
				str+= '<div class="result-header'+subclass+'">';
				str+= '<div class="result-count"></div><div class="result-nav'+subclass+'"></div>';
				str+= '</div>';
				
				if (type=='')
				{
					str+= '<div id="search-context-toggle">';
					str+= '<input type="checkbox" name="context" style="display:none" checked>';
					str+= '<img src="media/icon_checkbox_darkX.png" class="checkbox" style="float:left; margin:0px 5px 1px 0px;" onclick="'+toggle_check+'" '+pngfix+'>';
					str+= '<span onclick="this.previousSibling.previousSibling.click();utils.over(this.previousSibling,this.previousSibling.previousSibling.checked)">';
					str+= Locale7.get('gui','find_context')+'<span id="search-context" style="font-weight:bold"></span></span>';
					str+= '</div>';
				}
				
				str+= '</form></div>';
				break;
			
				
			case 'dashboard':
			if (0) var dashboard = { };
			
				str+= '<img id="dashboard-header" src="media/label_welcome_header.png">';
				
				str+= '<div id="dashboard-toggle" title="hide dialogue on next visit, requires cookies to be enabled">';
				str+= '<input type="checkbox" name="showinfo" style="display:none">';
				str+= '<img src="media/icon_checkbox.png" class="checkbox" style="float:left; margin:0px 5px 1px 0px;" onclick="'+toggle_check+'" '+pngfix+'>';
				str+= '<span onclick="this.previousSibling.previousSibling.click();utils.over(this.previousSibling,this.previousSibling.previousSibling.checked)">'+Locale7.get('dashboard','hide')+'</span>';
				str+= '</div>';
				
				break;
				
		

			/*	Scene gui
			*/
			
			case 'scene_title':
			if (0) var scene_title = { };
				
				var padding = p.state && p.state!=2? 'style="padding-left:28px"':'';
				
				str+= '<div class="scene-layer-title dropshadow" '+padding+'>'+p.name+'</div>';
				str+= '<div class="scene-layer-title" '+padding+'>'+p.name;
				if (p.state && p.state!=2) str+= '<img class="scene-state" src="media/icon_scene_list_'+p.state+'.png" title="'+Locale7.get('scene','state_'+p.state)+'">';
				
				str+= '</div>';
				break;

			case 'scene_story':
			if (0) var scene_story = { };
			
				var a = ['',' right',' center'];
			
				str+= '<div class="story">';
				str+= '<div class="story-medium'+a[p.layout]+'"></div>';
				str+= '<div class="story-text">'+p.text+'</div>';
				//str+= clear;
				str+= '</div>';
				break;

			case 'scene_director':
			if (0) var scene_director = { };
				
				str+= '<div id="scene-director-image"></div>';
				str+= '<div id="scene-director"></div>';
				break;
				
			case 'scene_share':
			if (0) var scene_share = { };
			
				str+= '<div id="scene-share-labels">';
				str+= 'URL<br>Embed';
				str+= '</div>';
				str+= '<div id="scene-share-form">';
				str+= '<form name="shareform" AUTOCOMPLETE="OFF">';
				str+= '<input type="text" name="url">';
				str+= '<input type="text" name="embed">';
				str+= '<div id="scene-share-trace">';
				str+= Locale7.get('scene','share_trace')+':&nbsp;';
				str+= '<select name="trace"><option value="">'+Locale7.get('gui','select')+'</option></select>';
				str+= '</div>';
				str+= '</form>';
				str+= '</div>';
				str+= clear;
				break;


			/*	menu-layer widgets
			*/
			case 'widget_search':
			if (0) var widget_search = { };
			
				str+= '<div class="widget-search">';
				str+= '<form name="'+p.type+'searchform" onsubmit="return false">';
				
				str+= '<input name="query" type="text">';
				str+= '<div class="button" style="left:155px; top:9px"><a href="javascript://clear" style="width:14px; background-image:url(media/button/close_pane.png)"></a></div>';
				
				str+= '<div class="result-header">';
				str+= '<div class="result-count"></div><div class="result-nav"></div>';
				str+= '</div>';
				
				str+= '</form>';
				str+= '</div>';
				break;

			case 'list_trace':
			if (0) var list_trace = { };
				
				var displayname = p.name.length>15? p.name.substring(0,15)+'..':p.name;
				var date = new Date(Number(p.date))
					date = p.type=='activity'? date.format('shortrelative'):date.format('shorthumandate',', ','time');
				
				str+= '<a href="javascript://trace">';
				str+= '<img src="media/icon_trace.png" class="icon">';
				str+= '<span title="'+p.name+'" class="name">'+displayname+'</span><span class="dimmed" style="font-size:11px">'+date+'</span>';
				str+= '</a>';
				break;
			
			case 'list_event':
			if (0) var list_event = { };

				var displayname = p.name.length>15? p.name.substring(0,15)+'..':p.name;
				var subclass = p.type=='activity'? '':' default';
				var date = new Date(Number(p.date))
					date = p.type=='activity'? date.format('shortrelative'):date.format('shorthumandate');
				
				str+= '<a href="javascript://event">';
				str+= '<img src="media/icon_event.png" class="icon">';
				str+= '<span title="'+p.name+'" class="name narrow">'+displayname+'</span><span class="dimmed" style="font-size:11px">'+date+'</span>';
				str+= '<span class="end">'+(p.hiscore || '')+'</span>';
				str+= '</a>';
				break;

			case 'list_event_scheduled':
			if (0) var list_event_scheduled = { };

				var displayname = p.name.length>16? p.name.substring(0,16)+'..':p.name;
				var startdate = new Date(Number(p.start));
				var enddate = new Date(Number(p.end));
				var start = startdate.format('shortdate');
				var end = enddate.format('shortdate');
					end = start==end? '':end+', ';
					end+= enddate.format('time');
					start+= ', '+startdate.format('time');
			
				str+= '<a href="javascript://event">';
				str+= '<img src="media/icon_event.png" class="icon">';
				str+= '<span title="'+p.name+'" class="name">'+displayname+'</span>';
				str+= '<span class="dimmed" style="font-size:11px">'+start+' - </span>';
				str+= '<span class="dimmed" style="font-size:11px">'+end+'</span>';
				str+= '</a>';
				break;

			case 'list_event_invited':
			if (0) var list_event_invited = { };

				var displayname = p.name.length>14? p.name.substring(0,14)+'..':p.name;
				var date = new Date(Number(p.date)).format('shortday',' ','shorthumandate',', ','time');
			
				str+= '<a href="javascript://event">';
				str+= '<img src="media/icon_event.png" class="icon">';
				str+= '<span title="'+p.name+'" class="name narrow">'+displayname+'</span>';
				str+= '<span style="font-size:11px; padding-left:5px" class="dimmed">'+date+'</span>';
				if (p.state=='3') str+= '<span style="float:right; font-size:11px; margin-right:8px" class="dimmed">done</span>';
				str+= '</a>';
				break;

			case 'events_list_header':
			if (0) var events_list_header = { };
			
				str+= '<div class="layer-widget-header-scheduled">';
				str+= 'event';
				str+= '<span style="padding-left:'+(p.type=='events-scheduled'? 92:87)+'px">'+Locale7.get('gui','starts')+'</span>';
				
				
				if (p.type=='events-scheduled') str+= '<span style="padding-left:62px">'+Locale7.get('gui','ends')+'</span>';
				str+= '</div>';
				break;

			case 'comment':
			if (0) var comment = { };
			
				var t = p.comment.type;
				var remove = p.comment.remove? '<div class="comment-remove button" style="float:'+(t=='left'? 'right':'left')+';" id="comment_remove_'+p.comment.id+'"><a href="javascript://remove" style="width:14px; background-image:url(media/button/remove.png)"></a></div>':'';
				
				str+= '<div id="comment_'+p.comment.id+'" class="comment-container" style="float:'+t+'; margin-'+t+':24px;">';
					str+= '<div class="comment-header"></div>';
					str+= '<div class="comment-body" style="width:300px">'+p.comment.text+'</div>';
					str+= '<div class="comment-footer" style="background-image:url(media/bg_comment_footer_'+t+'.png)"></div>';

					str+= '<div class="comment-details" style="float:'+t+'; margin-'+t+':-24px;"">';
					
					str+= '<div class="comment-icon" style="float:'+t+'"></div>';
					str+= '<div class="comment-detail" style="text-align:'+t+'; padding-'+t+':7px">';
					str+= '<strong>'+p.comment.user+'</strong>,<br>';
					if (t=='right') str+= remove;
					str+= '<span class="scene-dimmed">'+new Date(p.comment.date).format('shortrelative')+'</span>';
					if (t=='left') str+= remove;
					str+= '</div>';

					str+= '</div>';
					str+= clear;
				str+= '</div>';
				break;
		

			/*	Playback gui: events, traces, places
			*/
			
			case 'trace_header':
			if (0) var trace_header = { };

				str+= '<div class="trace-header">';
				str+= '<img src="media/icon_trace_large.png" class="trace-icon" style="background-color:#'+p.trace.color+'"><span class="trace-name">';
				if (p.trace.is_team) str+= '<span style="font-weight:normal">team </span>';
				str+= p.trace.name+'</span>';
				str+= '<div class="trace-follow"></div>';
				str+= '</div>'
				str+= '<div class="trace-control"></div>';
				break;
				
			case 'trace_body':
			if (0) var trace_body = { };
				str+= '<div class="trace-events">';
				str+= '<div class="trace-events-list"></div>';
				str+= '<img src="media/trace_event_select.png" style="position:absolute; left:3px; top:0px; width:264px; height:18px">';
				str+= '</div>';
				break;

			case 'event_header':
			if (0) var trace_header = { };

				str+= '<div class="trace-header" style="height:50px;">';
				str+= '<img src="media/icon_event-inv.png" class="trace-icon"><span class="trace-name" style="font-size:13px; margin-top:5px">';
				str+= p.event.name+'</span>';
				str+= '<div class="event-traces-count"></div>';
				str+= '<div class="event-hiscore"></div>';
				break;

			case 'event_body':
			if (0) var event_body = { };
				str+= '<div class="event-traces-header">';
				str+= '<span style="position:absolute; left:25px"></span>';
				str+= '<span style="position:absolute; left:160px"></span>';
				str+= '<span style="position:absolute; left:220px"></span>';
				str+= '</div>';
				str+= '<div class="event-traces"></div>';
				str+= '<div class="event-control"><div class="trace-control"></div></div>';
				str+= '<div class="event-events"><div class="trace-events">';
				str+= '<div class="trace-events-list"></div>';
				str+= '<img src="media/trace_event_select.png" style="position:absolute; left:3px; top:0px; width:264px; height:18px">';
				str+= '</div></div>';
				break;

			case 'event_trace':
			if (0) var event_trace = { };
				
				var displayname = p.name.length>15? p.name.substring(0,15)+'..':p.name;
				
				str+= '<a href="javascript://trace">';
				str+= '<div class="event-trace-icon"><img src="media/icon_trace.png" class="icon"></div>';
				str+= '<span title="'+p.name+'" class="name">'+displayname+'</span>';
				str+= '<span class="rewards">'+(p.rewards || '')+'</span>';
				str+= '<span class="score">'+(p.score || '')+'</span>';
				str+= '</a>';
				break;

			case 'event_trace_stats':
			if (0) var event_trace_stats = { };
			
				//ugc
				str+= '<div class="stats" style="width:60px; background-image:url(media/icon_type_uploads.png)">';
				str+= p.stats.imagecount+' photo'+(p.stats.imagecount!=1? 's':'');
				str+= '<br>'+p.stats.notecount+' note'+(p.stats.notecount!=1? 's':'');
				str+= '</div>'
			
				//rewards
				if (p.stats.rewards)
				{
					var r = p.stats.rewards.objectlist || p.stats.rewards.skilllist;
					var m = this.portal.scene.meta.objectlist || this.portal.scene.meta.skilllist; //object or skillname is retrieved from scene.meta
					
					str+= '<div class="stats" style="width:80px; background-image:url(media/icon_type_reward.png)">';
					for (var i=0; i<r.length; i++)
					{
						var object = $j.grep(m, function(object) { return object.key==r[i].key });
						if (object.length) var name = object[0].name.substr(0,11);

						str+= r[i].amount+' x '+name+'<br>';
					}
					str+= '</div>';
				}
				
				//tasks
				if (p.stats.taskstotal)
				{
					str+= '<div class="stats" style="width:30px; background-image:url(media/icon_type_task.png)">';
					str+= p.stats.tasksdone+'/'+p.stats.taskstotal;
					str+= '</div>'
				}
				break;

			case 'place_title':
			if (0) var place_title = { };
			
				str+= '<div style="left:18px; top:3px; width:200px; font-weight:bold;">';
				str+= '<img src="media/icon_type_'+p.type+'.png" style="float:left; margin-right:4px; margin-top:-1px; width:18px; height:18px;" '+pngfix+'>'+p.name;
				str+= '</div>';
				break;

			case 'playback_place_trade':
			if (0) var playback_place_trade = { };
				
				str+= '<div class="line">';
				str+= '<span style="color:white; margin-right:3px">"'+p.peername+'"</span> <span onclick="portal.scene.places['+p.peerid+'].expand()" style="cursor:pointer" onmouseover="utils.over(this.lastChild,1)" onmouseout="utils.over(this.lastChild,0)">'+Locale7.get('direct_places','show_on_map');
				str+= '<img src="media/icon_arrow_small_right_pane.png" style="vertical-align:middle; padding:0px 0px 3px 3px"></span>';
				str+= '</div>';
				break;

			case 'playback_place_maxtime':
			if (0) var playback_place_maxtime = { };
			
				if (!p.update) str+= '<div style="font-weight:bold; float:right;">';
				str+= '<img src="media/icon_time.png" style="float:left; width:18px; height:18px; vertical-align:middle">';
				str+= '<span style="float:left; padding:2px 0px 0px 2px">'+p.time+' minute';
				str+= (p.time>1)? 's':'';
				if (!p.update) str+= '</div>';
				break;

			case 'playback_place_quiz':
			if (0) var playback_place_quiz = { };
			
				str+= p.question;
				if (p.answerlist)
				{
					var choice = ['A','B','C'];
					str+= '<div class="line" style="margin-top:5px">';
					for (var i=0; i<p.answerlist.length; i++)
					{
						str+= '<div class="line"><span class="answer-option">'+choice[i]+'</span> - '+p.answerlist[i]+'</div>';
					}
					str+= '</div>';
				}
				break;

			case 'playback_item':
			if (0) var playback_item = { };
			
				var enabled = (p.enabled)? 'onclick="'+toggle_check+'"':'';

				if (p.state=='scheduled') str+= '<div style="position:absolute; right:15px; top:1px; font-style:italic;">scheduled</div>';

				str+= '<input type="checkbox" name="playback_select_'+p.playid+'" style="display:none">';
				str+= '<img src="media/icon_checkbox.png" class="checkbox" style="margin:0px 5px 1px 0px;" '+enabled+' '+pngfix+'>';
				str+= 'team "'+p.team+'"';
				break;
	
			case 'playback_header':
			if (0) var playback_header = { };
			
				str+= '<img src="media/label_'+p.type+'.png" '+pngfix+' style="float:left; margin-left:4px; width:'+p.w+'px; height:20px;">';
				if (p.name) str+= '<div style="position:relative; float:left; font-weight:bold; margin-top:2px; margin-left:2px; font-size:12px">'+p.name+'</div>';
				str+= '<img src="media/icon_target.png" style="margin-left:5px; margin-top:3px; cursor:pointer; visibility:hidden" onmouseover="utils.over(this,1)" onmouseout="utils.over(this,0)" title="show on map">';
				break;
	
			case 'playback':
			if (0) var playback = { };
			
				//controls
				str+= '<div style="position:relative; width:100%; left:0px; top:0px; height:40px; overflow:visible;">';
				str+= '<div style="position:absolute; top:-18px; left:210px; cursor:pointer;">';
				str+= '<img src="media/icon_checkboxX.png" class="checkbox" style="cursor:pointer; margin:0px 6px 4px 0px;" '+pngfix+'><span>trace</span>';
				str+= '</div>';
				str+= '</div>';
				//events 
				str+= '<div style="position:relative; width:313px; left:-10px; height:'+(p.h-45-40)+'px; overflow:hidden;">';
					str+= '<div style="position:absolute; left:10px; top:0px; visibility:hidden"></div>';
					str+= '<img src="media/event_select.png" style="position:absolute; left:0px; top:0px; width:313px; height:18px" '+pngfix+'>';
				str+= '</div>';
				//stats
				str+= '<div style="bottom:0px; height:40px; width:100%; left:0px; overflow:visible">';
					str+= '<div class="divider" style="width:100%; left:-14px;"></div>';
					str+= '<div style="top:4px" class="dimm">';
					str+= '</div>';
				str+= '</div>';
				break;
				
			case 'playback_info':
			if (0) var playback_info = { };
				
				if (p.play.is_team) 
				{
					str+= '<div style="position:relative">';
					str+= 'player';
					str+= (p.play.users.length>1)? 's: ':': ';
					str+= '<span style="color:white">'+p.play.users.join(', ')+'</span>';
					str+= '</div>';
				}
				if (p.play.round)
				{
					str+= '<div style="position:relative">';
					str+= 'event:';
					str+= '<img src="media/icon_type_events.png" style="vertical-align:middle; margin:0px 5px">';
					if (!p.play.live) str+= '<span style="color:white; cursor:pointer" title="show event details and traces">'+p.play.roundname+'</span>';
					else str+= '<span style="color:white;">'+p.play.roundname+'</span>';
					str+= '</div>';
				}
				break;
			
				
				
			/*	project gui
			*/
			
			case 'project_panel_about':
			if (0) var project_panel_about = { };
			
				//icon
				var icon = p.project.getMediumByName('icon');
				if (icon) str+= '<div class="panel7-icon-main"><img src="'+this.portal.path+'media.srv?id='+icon.elm.id+'&resize=160x160"></div>';

				//contents
				var title = p.content.title || p.project.name;
				var text1 = p.content.text1 || '';
				var text2 = p.content.text2 || '';
				
				text1 = this.portal.unescapeHTML(text1);
				text2 = this.portal.unescapeHTML(text2);

				if (text1=='' && text2=='') text2 = Locale7.get('gui','nodesc');
				
				/*	backwards compatibility, can be removed later
				*/
				title = unescape(title);
				text1 = unescape(text1);
				text2 = unescape(text2);
				/*
				*/
				
				str+= '<h2 style="font-size:18px;">'+title+'</h2>';
				str+= '<p style="font-style:italic">'+text1+'</p>';
				//media
				str+= '<div></div>';
				str+= '<p>'+text2+'</p>';
				break;

// 			case 'project_panel_scenes':
// 			if (0) var project_panel_list = { };
// 
// 				str+= '<div style="width:100%; height:100%;"></div>';
// 				break;
				
			case 'project_panel_media':
			if (0) var project_panel_media = { };
			
				//str+= '-media-';
				var mlist = p.project.content.mediumlist
				
				for (var n=0; n<mlist.length; n++)
				{
 					if (mlist[n].name.indexOf('mlist_')==-1) continue;
 					
 					switch (mlist[n].type)
 					{
 						case 'image':
 							str+= '<img class="project-media" src="'+this.portal.path+'media.srv?id='+mlist[n].id+'&resize=300x">';
 							break;
 							
 						case 'video':
 							str+= '<div class="project-media">'+mlist[n].id+'</div>'; //replaced with video afterwards
 							break;
 					}
 				}
				break;
			
			case 'project_panel_feed':
			if (0) var project_panel_feed = { };
				
				str+= '-feed-';
				break;
				
				
			default:
				str+= '-id not found-';
				break;
		}
		
		//gui for logged in users
		if (str=='-id not found-' && this.extendCreate) str = this.extendCreate(p,pngfix,clear,toggle_check,check_on);
		
		return str;
	}


	/*	init
	*/
	this.gui = new Gui(this);
}

/* [file src="Layers.js"]
*/
/* 	usemedia.com . joes koppers . 01.2008 [rev 08.2008]
	thnx for reading this code */


/*	7s layers,
	requires common/use.js,
	requires common/excanvas.js for MSIE */


function Layers7(portal)
{
	/*	layers collection
	*/

	this.create = function(properties)
	{
		if (!properties.id) return;
		var layer = new Layer(portal,properties);
		//add to collection
		this[properties.id] = layer;
		
		return layer;
	}
	
// 	this.drawShear = function(elm,w,h,color,color2,alpha,add)
// 	{
// 		/*	public link to local shear7 function
// 		*/
// 		return shear7(elm,w,h,color,color2,alpha,add);
// 	}
	
	this.createMenu = function(parent,p)
	{
		/*	public link to local Menu class
		*/
		return new Menu(parent,p);
	}
	
	this.dispose = function(id)
	{
		if (!this[id]) return;
		this[id].dispose();
		delete this[id];
	}
	
	this.disposeAll = function()
	{
		for (var id in this) if (typeof(this[id])=='object') this.dispose(id);
	}


	/*	portal layer object
	*/

	function Layer(portal,p)
	{
		/*	Layer constructor

			p = properties object, 
			values: id,x,y,r(ight),b(ottom),w,h,z,bcolor,hcolor,gradient,shadow,parent,hidden,align */

		var obj = this;
		
		this.portal = portal;
		this.parent = p.parent; //parent layer
		
		this.extendUpdate = new Object(); //used for resize handling
	
		this.id = p.id;
		this.x = this.ox = p.x;// || 0; //use x==undefined for right aligned fixed width layers (disables slideTo()!)
		this.y = this.oy = p.y || 0;
		this.z = p.z;
		this.slide_speed = 0.25;
		this.resize_speed = 0.25;
		this.align = p.align;

		if (p.r!=undefined)
		{
			if (this.x==undefined) this.w = p.w;
			this.r = p.r; //use style.right instead of width
		}
		else this.w = p.w; 
		if (p.b!=undefined) this.b = p.b; //use style.bottom instead of height;
		else this.h = p.h;
		
		this.bc = p.bcolor;	//background
		this.hc = p.hcolor || 'white'; //header
		this.hc2 = p.hcolor2; //gradient header
		this.gradient = p.gradient; //gradient bg gif
		this.shadow = p.shadow; //dropshadow
		this.rounded = p.rounded; //rounded corner style (bottom-left corner only, for now)
	
		this.visible = (p.hidden)? false:true;
		this.collapsed = false;
		this.closebutton = p.closebutton;
		
		//container div
		var layer = document.createElement('div');
			layer.style.visibility = 'hidden'; //hide during creation
			if (!this.visible) layer.style.display = 'none';
			layer.id = this.id;
			layer.className = 'layer';
			if (this.x!=undefined) layer.style.left = this.x +'px';
			layer.style.top = this.y +'px';
			if (this.z!=undefined) layer.style.zIndex = this.z;

		this.parent.div.appendChild(layer);
	
		this.div = layer;
		
		//background
		var divs = ['left','main'];
		for (var i=0; i<divs.length; i++)
		{
			var div = document.createElement('div');
				div.className = 'bg';
				div.style.backgroundColor = this.bc;
				if (this.gradient)
				{
					div.style.backgroundImage = 'url(media/'+this.gradient+')';
					div.style.backgroundPosition = (divs[i]=='left')? '0px -30px':'-25px 0px';
					if (p.bgrepeat) div.style.backgroundRepeat = p.bgrepeat;
					if (p.bgtransparent) div.style.backgroundColor = '';
				}
	
			layer.appendChild(div);
		}
		//dropshadow
		if (this.shadow)
		{
			var divs = ['left','right'];
			for (var i=0; i<divs.length; i++)
			{
				var div = document.createElement('div');
					div.className = 'layer-dropshadow layer-dropshadow-'+divs[i];
					div.style.background = 'url(media/bg_layer_shadow_'+divs[i].substring(0,1)+'.png) repeat-y';
				layer.appendChild(div);
			}

			layer.style.overflow = 'visible';
			this.shadow = false;
		}
		//content
		if (!p.nocontent)
		{
			var content = document.createElement('div');
				content.className = 'layercontent';
				layer.appendChild(content);
		}
		this.content = content;
		
		//title
		var div = document.createElement('div');
			div.className = 'titlebar';
		this.div.appendChild(div);
		this.titlebar = div;
		if (p.title) this.setTitle(p.title);

		//header and menu
		if (p.header!=false) this.drawHeader();
		this.addMenuBar();
		if (this.closebutton) this.addCloseButton();
		
		this.setSize();
		
		//rounded?
		if (this.rounded) this.addRoundedCorners();

		//reveal
		layer.style.visibility = 'visible';
	}
	
	Layer.prototype.drawHeader = function(color)
	{
		var w = 42;
		var h = 30;
		var elm = this.header || this.div; //create canvas elm first time
		var c1 = color || this.hc
		var c2 = (!color)? this.hc2:undefined;
		
//		this.header = shear7(elm,w,h,c1,c2);
		this.header = this.portal.gui.shear(elm,w,h,c1,c2);
		
		this.header.className = 'header';
	}
	
	Layer.prototype.addRoundedCorners = function(type)
	{
		/*	draw rounded corners with canvas, 
			only bottom-left corner for now */
	
		//remove backgroundcolor of left bg element
		var elm = this.div.firstChild;
		elm.style.background = '';
		
		if (elm.firstChild) elm.removeChild(elm.firstChild);
		
		//add canvas element
		var h = this.header ? this.h-30:this.h;
		var c = document.createElement('canvas');
			c.width = 25;
			c.height = h;
			c.style.width = '25px'; //needed for safari
			c.style.height = h +'px';
		
		elm.appendChild(c);
		//init canvas element for IE
		if (typeof(G_vmlCanvasManager)=='object') c = G_vmlCanvasManager.initElement(c);
		var ctx = c.getContext('2d');

		ctx.globalAlpha = (typeof(this.rounded)=='object' && this.rounded.alpha)? this.rounded.alpha:1;

		//bottom-left corner..		
		ctx.beginPath();
		ctx.moveTo(0,0);
		ctx.lineTo(25,0);
		ctx.lineTo(25,h);
		ctx.lineTo(10,h);
		ctx.arc(10,h-10,10,.5*Math.PI,Math.PI,false);
		ctx.lineTo(0,0);
		ctx.fillStyle = this.bc;
		ctx.fill();
		
		if (!this.bshadow)
		{
			//add dropshadow at bottom of layer
			this.div.style.overflow = 'visible';
			var img = document.createElement('img');
				img.className = 'layer-shadow-bottom';
				img.src = 'media/layer_shadow_bottom.png';
			this.div.appendChild(img);
			this.bshadow = img;
		}
	
		//force canvas dimensions for IE
		$j(c).find('div').css({
			width:25,
			height:h
		});
	}
	
	Layer.prototype.display = function(hide)
	{
		/*	temporary reveal layer in hidden state, needed for some html creation (like GMap)
		*/
		$j(this.div).css('visibility',hide? 'visible':'hidden');
		this.show(hide);
	}
	
	Layer.prototype.show = function(hide)
	{
		dbg.msg('Layer: ',hide? 'hide':'show','[',this.id,']');
	
		this.visible = (hide)? false:true;
		this.div.style.display = (hide)? 'none':'block';
	}
	
	Layer.prototype.hide = function()
	{
		this.show(1);
	}
	
	Layer.prototype.update = function(p,sliding)
	{
		/*	update layer position and scale
		*/
		if (p)
		{
			this.setPosition(p.x,p.y);
			this.setSize(p.h,p.w);
		}
		else
		{
			var x,y;
			if (this.align && this.align.b) var y = this.parent.h - this.align.b;
			this.setPosition(x,y);
			this.setSize();
		}

		//additional updates (e.g. to layer panels)
		if (!sliding) for (var id in this.extendUpdate) this.extendUpdate[id]();
	}
	
	Layer.prototype.setSize = function(w,h)
	{
		if (w) this.w = w;
		if (h) this.h = h;

		var layer = this.div;

		if (this.r==undefined)
		{
			layer.style.width = this.w +'px';
		}
		else 
		{
			layer.style.right = this.r +'px';
			if (this.x!=undefined) this.w = this.parent.w - (this.x+this.r);
			else layer.style.width = this.w +'px'; 
		}
		if (this.b==undefined)
		{
			layer.style.height = this.h +'px';
		}
		else
		{
			layer.style.bottom = this.b +'px';
			this.h = this.parent.h - (this.y+this.b);
		}
	
		//background divs
		//var s = (this.shadow)? 3:0;
		var s = 0;
		var divs = ['left','main'];
		for (var i=0; i<divs.length; i++)
		{
 			var div = this.div.childNodes[i];
 			div.style.bottom = '0px';
			if (divs[i]=='left')
			{
				div.style.width = '25px';
				div.style.top = this.header ? '30px':'0px';
			}
			else
			{
 				div.style.left = '25px';
				div.style.right = s +'px';
			}
		}
		//content
		if (this.content)
		{
			this.content.style.bottom = '0px';
			this.content.style.right = s+ 'px';
			var pr = (this.content.style.paddingRight)? parseInt(this.content.style.paddingRight):0; //for scene layer
		}
		
		//shadow, rounded
		//if (this.shadow) this.shadow.style.height = (this.h) +'px';
		if (this.bshadow) this.bshadow.style.width = this.w +'px';
		if (this.rounded) this.addRoundedCorners();
	}
	
	Layer.prototype.setPosition = function(x,y)
	{
		if (x!=undefined) this.x = x;
		if (y!=undefined) this.y = y;
	
		if (this.x!=undefined) this.div.style.left = this.x +'px';
		this.div.style.top = this.y +'px';
	}
	
	Layer.prototype.slideTo = function(x,y)
	{
		if (this.x==undefined) return;
		this.tx = x;
		this.ty = y;
		this.slide();
	}
	
	Layer.prototype.slide = function()
	{
		var obj = this;
		
		//move to target	
		var dx = this.tx-this.x;
		var dy = this.ty-this.y;
		
		this.x = this.x + this.slide_speed*(this.tx-this.x);
		this.y = this.y + this.slide_speed*(this.ty-this.y);
		
		var update_x = (dx>0 && this.x<this.tx-1) || (dx<0 && this.x>this.tx+1);
		var update_y = (dy>0 && this.y<this.ty-1) || (dy<0 && this.y>this.ty+1);
		
		if (update_x || update_y)
		{
			this.update(undefined,true);
			//loop
			this.sliding = window.setTimeout(function() { obj.slide() },40);
		}
		else
		{
			//done, set to exact end target position
			this.x = this.tx;
			this.y = this.ty;
			this.update(undefined,true); //sliding=true blocks additional updates (panel lists)
			if (this.slideCallback) this.slideCallback();
		}
	}

	Layer.prototype.resizeTo = function(p)
	{
		this.tw = p.tw;
		this.th = p.th;
		this.tr = p.tr;
		this.tb = p.tb;
 		
 		this.resize();
	}

	Layer.prototype.resize = function()
	{
		var obj = this;
		
		//move to target	
		if (this.tw!=undefined)
		{
			var dw = this.tw-this.w;
			this.w = this.w + this.resize_speed*(this.tw-this.w);
			var update_w = (dw>0 && this.w<this.tw-1) || (dw<0 && this.w>this.tw+1);
		}
		if (this.tr!=undefined) 
		{
			var dr = this.tr-this.r;
			this.r = this.r + this.resize_speed*(this.tr-this.r);
			var update_w = (dr>0 && this.r<this.tr-1) || (dr<0 && this.r>this.tr+1);
		}
		if (this.th!=undefined) 
		{
			var dh = this.th-this.h;
			this.h = this.h + this.resize_speed*(this.th-this.h);
			var update_h = (dh>0 && this.h<this.th-1) || (dh<0 && this.h>this.th+1);
		}
		if (this.tb!=undefined) 
		{
			var db = this.tb-this.b;
			this.b = this.b + this.resize_speed*(this.tb-this.b);
			var update_h = (db>0 && this.b<this.tb-1) || (db<0 && this.b>this.tb+1);
		}
		
// 		this.w = this.w + this.resize_speed*(this.tw-this.w);
// 		this.h = this.h + this.resize_speed*(this.th-this.h);
// 		
// 		var update_w = (dw>0 && this.w<this.tw-1) || (dw<0 && this.w>this.tw+1);
// 		var update_h = (dh>0 && this.h<this.th-1) || (dh<0 && this.h>this.th+1);
		
		if (update_w || update_h)
		{
			this.setSize();
			//loop
			this.resizing = window.setTimeout(function() { obj.resize() },40);
		}
		else
		{
			//done, set to exact end target position
			if (this.tw) this.w = this.tw;
			if (this.tr) this.r = this.tr;
			if (this.th) this.h = this.th;
			if (this.tb) this.b = this.tb;

			this.setSize();
			//this.update(this.tx,this.ty);
//			if (this.resizeCallback) this.resizeCallback();
		}
	}

// 	Layer.prototype.collapse = function()
// 	{
// 		dbg.msg('toggle collapse, layer=',this.id,' collapsed=',this.collapsed);
// 		
// 		this.collapsed = !this.collapsed;
// 		
// 		if (this.collapsed)
// 		{
// 			//exit fullscreen first //-> remove the global ref here!!
// 			if (portal.layout.fullscreen)
// 			{
// 				this.collapsed = false;
// 				portal.layout.toggleFullScreen();
// 				this.collapsed = true;
// 			}
// 			
// 			this.slideCallback = function() { this.align = { b:50 }	} //keep aligned at bottom in collapsed state
// //			delete this.align;
// 			this.slideTo(this.ox,this.parent.h-50);
// 		}
// 		else
// 		{
// 			delete this.align;
// 			this.slideCallback = function()
// 			{
// 				dbg.msg('collapse, slideCallback: update gmap'); //-> remove global ref here!!
// 				if (portal.layout.gmap) portal.layout.gmap.checkResize();
// 			}
// 			this.slideTo(this.ox,this.oy);
// 		}
// 	}
	
	Layer.prototype.blur = function(elms,blur)
	{
		/*	show/hide layer features: content scrollbar, closebutton, ...
		*/
		
		for (var i=0; i<elms.length; i++)
		{
			switch (elms[i])
			{
				case 'content':
					$j(this.content)[blur? 'hide':'show']();
					$j(this.titlebar).css('overflow',blur? 'hidden':'visible');
					break;

				case 'backdrop':
					$j(this.backdrop)[blur? 'hide':'show']();
					break;

				case 'scrollbar':
					$j(this.content).css('overflow',blur? 'hidden':'auto');
					break;
			};
		};
	}

	Layer.prototype.updateBg = function(c,src)
	{
		if (!src) src = this.gradient;
		for (var i=0; i<2; i++)
		{
			var div = this.div.childNodes[i];
				div.style.backgroundColor = c || this.bc;
				div.style.backgroundImage = 'url(media/'+src+')';
		}
	}
	
	Layer.prototype.setTitle = function(html)
	{
		this.titlebar.innerHTML = html;
	}
	
	Layer.prototype.setContent = function(str)
	{
		this.content.innerHTML = str;
	}
	
	Layer.prototype.clear = function()
	{
		$j(this.content).empty();
	}
	
	Layer.prototype.addContent = function(str)
	{
		this.content.innerHTML += str;
	}
	
	Layer.prototype.addCloseButton = function(src)
	{
		/*	add close button
		*/
		$j(this.menudiv).css('right',24); //move menubar to left

		var close = $j('<div class="button"><a href="javascript://close" style="width:18px; background-image:url(media/button/close.png)"></a>')
			.css({ width:18, right:3, top:4, zIndex:5200 })
			.appendTo(this.div)
		
		//reference for event handling
		this.closebutton = close.find('a');
	}
	
	Layer.prototype.addMenuBar = function()
	{
		this.menubar = {
			parent:this
		};
		
		this.menudiv = $j('<div></div>')
			.addClass('menubar')
			.css('height',25+3) 
			.appendTo(this.div)[0];
	}

	Layer.prototype.addMenu = function(p)
	{
		return this.menubar[p.id] = new Menu(this.menubar,p);
	}
	
	Layer.prototype.removeMenu = function(id)
	{
		this.menubar[id].dispose();
		delete this.menubar[id];
	}
	
	Layer.prototype.showMenus = function(menus,hide)
	{
		for (var i=0; i<menus.length; i++)
		{
			if (this.menubar[menus[i]]) this.menubar[menus[i]].show(hide);
		}
	}
	
	Layer.prototype.hideMenus = function(menus)
	{
		this.showMenus(menus,true);	
	}
	
	Layer.prototype.addPanel = function(p)
	{
		/*	create and add panel
		*/
		if (!this.panels) this.panels = new Object(); //collection of panels for this pane
		
		var panel = new Panel(this,p);
		this.panels[p.id] = panel;
		
		return panel;
	}
	
	Layer.prototype.dispose = function()
	{
		//remove DOM element
		this.parent.div.removeChild(this.div)
	}
	

	/* layer menu object
	*/
	
	function Menu(menubar,p)
	{
		/*	p = properties object, 
			values: id,w,color,parent,index */
		
		this.menubar = menubar;
		this.gui = this.menubar.parent.portal.gui;
	
		this.id = p.id;
		this.w = p.w;
		this.h = p.h || 23;
		this.c = p.color;
		this.c2 = p.color2;
		this.parent = menubar.div || p.parent.menudiv; //-> REFACTOR, menubar.div = new style (used in Panels.js)
		this.index = p.index; //insert at specific place in bar, 0=most right
		this.enabled = (p.enabled!=undefined)? p.enabled:true; //default enabled
		
		var div = document.createElement('div');
			div.className = 'menu';
			div.style.width = this.w +'px';
			div.style.height = this.h +'px';
		
		if (this.index==undefined) this.parent.appendChild(div);
		else this.parent.insertBefore(div,this.parent.childNodes[this.index]);
		this.div = div;

		//this.menubar.parent.portal.gui

//		this.bg = shear7(div,this.w,this.h,this.c,this.c2,p.alpha);
		this.bg = this.gui.shear(div,this.w,this.h,this.c,this.c2,p.alpha)
	
		this.setContent(p.content || '');
		
		//dbg.msg('Menu, id=',this.id,', this.enabled=',this.enabled);
		
		this.setEnabled(this.enabled,true);

		//even handling
// 		var obj = this;
// 		div.onmouseover = function() { obj.over(true) }
// 		div.onmouseout = function() { obj.over(false) }
// 		div.onclick = function()
// 		{
// 			if (!obj.enabled)
// 			{
// 				if (obj.menubar.parent && obj.menubar.parent.portal && obj.menubar.parent.portal.login.id) var msg = 'sorry!\n\nnot available for your account..';
// 				else msg = 'sorry!\n\nonly available for registered users,\nplease login or register first..';
// 				
// 				alert(msg);
// 			}
// 			else obj.onclick();
// 		}
		
		//show (will update menubar width)
		this.visible = false;
		this.show();
	}
	
	Menu.prototype.setContent = function(str)
	{
		if (!this.content)
		{
			//create content div
			var div = document.createElement('div');
				div.className = 'menucontent';
				div.style.width = this.w +'px';
				div.style.left = parseInt(this.bg.style.width) - this.w +'px';
			this.div.appendChild(div);
			this.content = div;
		}
		this.content.style.width = this.w +'px'; //might have changed
		this.content.innerHTML = str;
		
		//blur hrefs when clicked
		$j(this.content).find('a').click(function() { this.blur() });
	}
	
	Menu.prototype.show = function(hide)
	{
		var show = hide? false:true;
		if (this.visible==show) return;
		
		this.visible = show;
		this.div.style.display = (this.visible)? 'block':'none';
	}

	Menu.prototype.hide = function(show)
	{
		this.show(true);
	}
	
	Menu.prototype.setEnabled = function(enable,force)
	{
		if (!force && this.enabled==enable) return;
		this.enabled = enable;

		//replace background image	
		var src = $j(this.content).find('a').css('background-image');
			if (!src) return;
			src = enable? src.replace(/-disabled/,''):src.indexOf('-disabled')==-1? src.replace(/.png/,'-disabled.png'):src;
		
		$j(this.content).find('a').css('background-image',src);
	}
	
	Menu.prototype.setSelected = function(select)
	{
		/*	select a menu (draw slightly lowered from menubar), 
			used for menus refering to tab */
		
		if (select==undefined) select = true;
		this.selected = select;
		
		this.div.style.marginTop = (select)? '4px':'0px';
		this.bg.style.left = (select)? '-4px':'0px';
		
		//small bg adjustment for lowered menu
		var add = (select)? { x:1, y:0 }:undefined;
		this.gui.shear(this.bg,this.w,this.h,this.c,this.c2,1,add);
		
	}

	Menu.prototype.over = function(show)
	{
		if (this.enabled) utils.over(this.content.firstChild,show);
	}
	
	Menu.prototype.onclick = function()
	{
		//default empty, extend in object definition
	}

	Menu.prototype.dispose = function()
	{
		this.parent.removeChild(this.div);
	}



	/*	layer panel object //->REFACTOR, remove when all layers use the Panels7 class
	*/
	
	function Panel(layer,p)
	{
		/*	p = properties object,
			values: id, title, title_w, [index, h, header_w, shortcontent, content, expanded, interative] */
			
		var obj = this;
		
		this.layer = layer;

		this.id = p.id;
		this.title = p.title;
		this.index = p.index;
		this.content = p.content || '';
		this.shortcontent = p.shortcontent || '';
		
		this.expanded = false;
		this.interactive = (p.interactive==undefined)? true:p.interactive;
		
//		this.can_be_collapsed = (p.collapse==undefined)? true:p.collapse;
		this.h = p.h || 100; //expanded height
		this.title_w = p.title_w; //for title graphic
		this.header_w = p.header_w || 120;

		//create panel
		var div = document.createElement('div');
			div.className = 'layerpanel';
			div.style.width = '95%';
			div.style.maxWidth = '940px';
			div.onmouseover = function() { obj.setSelected() }
			div.onmouseout = function() { obj.setSelected(false) }
		
			var panel = document.createElement('div');
				panel.className = 'content';
				
				var header = document.createElement('div');
					header.className = 'header';
					header.style.width = this.header_w +'px';
				panel.appendChild(header);
					
					var title = document.createElement('img');
						title.className = 'headertitle';
						title.style.width = this.title_w +'px';
						title.style.cursor = (this.interactive)? 'pointer':'auto';
						
						if (browser.cssfilter) title.onload = function() { utils.pngFix(this) }; //ie png fix
						title.src = 'media/label_panel_'+this.title+'.png';
						
						//event handling
						title.onmouseover = function() { if (obj.interactive) { utils.over(this,1); utils.over(this.parentNode.lastChild,1) } };
						title.onmouseout = function() { if (obj.interactive) { utils.over(this,0); utils.over(this.parentNode.lastChild,0) } };
						title.onclick = function() { if (obj.interactive) obj.toggleView() };
						title.onmousedown = function(e) { utils.cancelEvents(e,true) }; //prevent drag select
						
					header.appendChild(title);
						
					if (this.index!=undefined)
					{
						var headerindex = document.createElement('div');
							headerindex.className = 'headerindex';
							headerindex.innerHTML = this.index;
						header.appendChild(headerindex);
					}
					
					var expand = document.createElement('img');
						expand.style.position = 'absolute';
						expand.style.right = '6px';
						expand.style.top = '11px';
						expand.style.width = '9px';
						expand.style.height = '9px';
						expand.style.cursor = (this.interactive)? 'pointer':'auto';
						if (browser.cssfilter) expand.onload = function() { utils.pngFix(this) }; //ie png fix
						expand.src = 'media/icon_expand.png';
						//event handling
						expand.onmouseover = function() { if (obj.interactive) { utils.over(this,1); utils.over(this.parentNode.firstChild,1) } };
						expand.onmouseout = function() { if (obj.interactive) { utils.over(this,0); utils.over(this.parentNode.firstChild,0) } };
						expand.onclick = function() { if (obj.interactive) obj.toggleView() }
						expand.onmousedown = function(e) { utils.cancelEvents(e,true) }; //prevent drag select
						
					header.appendChild(expand);
				
				var content = document.createElement('div');
					content.style.marginLeft = this.header_w+15 +'px';
					content.style.marginTop = '8px';
				panel.appendChild(content);

					var collapsed = document.createElement('div');
						collapsed.innerHTML = this.shortcontent;
					content.appendChild(collapsed);

					var expanded = document.createElement('div');
						expanded.style.display = 'none';
						expanded.innerHTML = this.content;
					content.appendChild(expanded);

		div.appendChild(panel);
		this.div = div;

		this.panel = panel;
		this.header = header;

		this.shortcontent = collapsed;
		this.content = expanded;
		
		if (layer.content.firstChild && layer.content.firstChild.tagName.toLowerCase()=='form')
		{
			//form present (directmode)
			layer.content.firstChild.appendChild(div);
		}
		else
		{
			layer.content.appendChild(div);
		}

		//create and draw canvas arrow
		if (this.index>1) this.addArrow();

		//expanded by default
 		if (p.expanded || p.expanded==undefined) this.expand(1); 
	}
	
	Panel.prototype.addArrow = function(style)
	{
		var canvas = document.createElement('canvas');
			canvas.className = 'arrow';
			canvas.width = 40;
			canvas.height = 20;
			if (style=='bottom')
			{
				canvas.style.right = '80px';
				canvas.style.bottom = '-20px';
			}
			else //default top
			{
				canvas.style.top = '-12px';
				canvas.style.bottom = '-20px';
			}
		this.div.appendChild(canvas);

		//init canvas element for IE
		if (typeof(G_vmlCanvasManager)=='object') canvas = G_vmlCanvasManager.initElement(canvas);
			
		var ctx = canvas.getContext('2d');
	
		ctx.beginPath();
		ctx.moveTo(0,0);
		ctx.lineTo(40,0);
		ctx.lineTo(20,20);
		ctx.lineTo(0,0);
		ctx.fillStyle = '#999966';
		ctx.fill();
		//shadow
		ctx.beginPath();
		ctx.moveTo(0,0);
		ctx.lineTo(20,20);
		ctx.strokeStyle = '#75744a';
		ctx.stroke();
		//highlight
		ctx.beginPath();
		ctx.moveTo(40,0);
		ctx.lineTo(20,20);
		ctx.strokeStyle = '#baba86';
		ctx.stroke();
	}
	
	Panel.prototype.setSelected = function(select,force)
	{
		if (!this.interactive && !force) return;
	
		var select = (select || select==undefined)? true:false;
		if (this.selected==select) return;
		
		this.selected = select;
		
		this.panel.style.backgroundImage = (select)? 'url(media/gradient_panel.gif)':'';
		this.panel.style.backgroundColor = (select)? '#b2b277':'transparent';
		this.header.style.backgroundImage = (select)? 'url(media/gradient_panel_header.gif)':'';
		this.header.style.backgroundColor = (select)? '#949461':'#878758';
	}
	
	Panel.prototype.toggleInteractive = function()
	{
		this.interactive = !this.interactive;

		//(re)set cursor for header title and arrow
		this.header.firstChild.style.cursor = (this.interactive)? 'pointer':'auto';
		this.header.lastChild.style.cursor = (this.interactive)? 'pointer':'auto';
	}

	Panel.prototype.toggleView = function()
	{
		if (this.expanded) this.collapse();
		else this.expand();
	}
	
	Panel.prototype.expand = function(force,collapse)
	{
		if (!this.interactive && !force) return;

		this.expanded = (collapse)? false:true;

		this.setHeight();
		this.header.lastChild.src = (this.expanded)? 'media/icon_expanded.png':'media/icon_expand.png';

		//toggle expanded and collapsed content
		this.shortcontent.style.display = (this.expanded)? 'none':'block';
		this.content.style.display = (this.expanded)? 'block':'none';

		this.setSelected(this.expanded,force);

		//additional actions (defined in object)		
		this.onexpand();
	}
	
	Panel.prototype.collapse = function(force)
	{
		this.expand(force,true);
	}
	
	Panel.prototype.onexpand = function()
	{
		//default empty, extend in object
	}
	
	Panel.prototype.setHeight = function(h)
	{
		if (h!=undefined) this.h = h; //update
		this.panel.style.height = (this.expanded)? this.h+'px':'35px';
		this.header.style.height = (this.expanded)? this.h+'px':'35px';
		this.content.style.height = (this.expanded)? (this.h-8)+'px':'35px';
	}
	
	Panel.prototype.setShortContent = function(str)
	{
		this.shortcontent.innerHTML = str;	
	}
	
	Panel.prototype.setContent = function(str)
	{
		this.content.innerHTML = str;	
	}
	
	Panel.prototype.addContent = function(str)
	{
		this.content.innerHTML+= str;
	}
	
	Panel.prototype.addMenu = function(p)
	{
		if (!this.menubar) 
		{
			//add menubar first
			this.menubar = new Object();
			this.menubar.parent = this.layer;
			
			this.menudiv = document.createElement('div');
			this.menudiv.className = 'menubar';
			this.menudiv.style.height = '20px';
			this.div.appendChild(this.menudiv);
		}

		//add menu
		var menu = new Menu(this.menubar,p);
		this.menubar[p.id] = menu;
		
		return menu;
	}
	
	Panel.prototype.addMenuTo = function(parent,p)
	{
		/*	add menu to other parent than this.menubar
		*/
		var menu = new Menu(parent,p);
		parent[p.id] = menu;
		
		return menu;
	}

	Panel.prototype.addButton = function(type,position)
	{
		var imgtype = 'icon';
		var button = document.createElement('img');
			button.style.position = 'absolute';
			button.style.cursor = 'pointer';
	
		if (position)
		{
			for (var p in position)
			{
				button.style[p] = position[p] +'px';
			}
		}
		else
		{
			//default positions
			switch (type)
			{
				case 'save':
				case 'next':
				case 'done':
					imgtype = 'button';
					button.style.right = '80px';
					button.style.bottom = '1px';
					button.style.width = '43px';
					button.style.height = '18px';
					break;
					
				case 'help':
					button.style.right = '17px';
					button.style.top = '7px';
					button.style.width = '18px';
					button.style.height = '18px';
					break;
					
				case 'close':
					button.style.right = '125px';
					button.style.bottom = '1px';
					button.style.width = '18px';
					button.style.height = '18px';
					break;
			}
		}

		//ie png fix
		if (browser.cssfilter) button.onload = function() { utils.pngFix(this) };
		button.src = 'media/'+imgtype+'_'+type+'.png';
		//event handling
		button.onmouseover = function() { utils.over(this,1) }
		button.onmouseout = function() { utils.over(this,0) }
		
		this.div.appendChild(button);
		this[type+'button'] = button;
		return button;
	}
	
	Panel.prototype.removeButton = function(type)
	{
		if (!this[type+'button']) return;
		this.div.removeChild(this[type+'button'])
		delete this[type+'button'];
	}
}

/* [file src="Panes.js"]
*/
/* 	usemedia.com . joes koppers . 01.2007 [rev 11.2007]
	thnx for reading this code */


/*	7s popup panes,
	requires common/excanvas.js for MSIE
	requires common/dragdrop.js for dragable panes 
	requires common/use.js for addCloseButton(), addHelpButton() and addPanel() */

function Panes7(portal)
{
	/*	panes collection
	*/
	
	this.create = function(properties)
	{
		if (!properties.id) return;
		var pane = new Pane(properties);
		//add to collection
		this[properties.id] = pane;
		
		return pane;
	}
	
// 	this.createPanel = function(id,properties)
// 	{
// 		/*	create and add panel to pane (id)
// 		*/
// 		if (!this[id].panels) this[id].panels = new Object(); //collection of panels for this pane
// 		var panel = new Panel(properties);
// 		return panel;
// 	}
	
	this.update = function()
	{
		for (var id in this) if (typeof(this[id])=='object') this[id].update();
	}

	/*	show(list), hide(list), dispose(list)
		list = comma separated strings (pane names)
		pane is validated before action */

	this.dispose = function()
	{
		for (var i=0; i<arguments.length; i++)
		{
			var pane = this[arguments[i]];
			if (pane)
			{
				pane.dispose();
				delete this[arguments[i]];
			}
			
		}
	}
	
	this.hide = function(panes)
	{
		for (var i=0; i<arguments.length; i++)
		{
			var pane = this[arguments[i]];
			if (pane) pane.hide(1);
		}
	}

	this.show = function(panes)
	{
		for (var i=0; i<arguments.length; i++)
		{
			var pane = this[arguments[i]];
			if (pane) pane.show();
		}
	}

	/*	Pane object
	*/

	function Pane(p)
	{
		/*	create new pane, 
			p = properties object, 
			values: id, parent,
					x, y, w, h, z, color, hcolor, style
					autohide, autosize, center, resize
					dragable, marginleft, marginright */
	
		var obj = this;
	
		this.id = p.id;
		this.x = p.x || 0;
		this.y = p.y || 0;
		this.w = p.w;
		this.h = p.h;
		this.c = p.color || 'b'; //default black pane
		this.style = p.style;
	//	this.s = p.small; //true for small pane
		if (this.style=='place' || this.style=='ugcplace') this.c = '';
		this.hc = p.hcolor || '#800000'; //header color, default red
		this.z = p.z || 1000;
	
		//static (default) or autohide pane	
		this.autohide = p.delay || false;
		this.delay = p.delay;
		this.hideTimeout = false;
		this.closing = false;
	
		this.parent = p.parent;
		this.autosize = p.autosize;
		this.dragable = p.dragable;
		this.resize = p.resize; //object, properties: min_x,min_y,max_x,max_y,callback (all optional)
		this.centered = p.center;
		this.place = (this.style=='place' || this.style=='ugcplace')? true:false;
		
		//pane div
		var pane = document.createElement('div');
			pane.style.visibility = 'hidden'; //hide during creation
			pane.id = this.id;
			pane.style.display = 'none';
			pane.className = (!this.autosize)? 'pane7':'pane7_autosize';
			pane.style.left = this.x +'px';
			pane.style.top = this.y +'px';
			pane.style.width = (this.w+28+17) +3 +'px'; //->3px extra needed for IE display bug!
			if (this.z) pane.style.zIndex = this.z;
			if (!this.autosize) pane.style.height = (this.h+20+18) +'px';
		
		if (this.parent) this.parent.appendChild(pane);
		else document.getElementById('layout').appendChild(pane);

		this.div = pane;

		//make draggable
		if (this.dragable)
		{
			var ml = p.marginleft || 0;
			var mt = p.margintop || 0;
			//left and top margins
			pane.style.left = ml +'px';
			pane.style.top = mt +'px';
			
			this.drag = makeDragableItem(pane);

			//prevent dragging past left/top margin
			this.drag.setRange('y',true,0,10000);
			this.drag.setRange('x',true,0,10000);

			this.drag.dragging = function(e) { obj.x = this.x; obj.y = this.y; } //update pane obj while dragging
			this.drag.drop = this.drag.dragging; //default, extend in object
			this.drag.appearance = function(d)
			{
				var c = (d)? '#aaaaaa':undefined;
				obj.drawHeader(c);
			}
			if (this.style!='tooltip') this.drag.setEnabled(false); //header mouse-events dis/enables dragging

			//reset to default position
 			this.setPosition(this.x,this.y);
		}
		
		//bg style html
		var c = this.c;
		var s = '';
		if (this.style) s = '_'+this.style;
		if (this.style=='place' || this.style=='ugcplace') s = this.style;
		
//		var s = (this.s)? '_small':''; //small style pane
		var str = '';
		if (!this.autosize)
		{
			//fixed height (content scroll)
			str+= '<div style="left:0px; top:0px; width:28px; height:20px; '+pngBgImage(c+s+'_nw')+'"></div>';
			str+= '<div style="left:28px; top:0px; width:'+(this.w)+'px; height:20px; '+pngBgImage(c+s+'_n')+'"></div>';
			str+= '<div style="left:'+(this.w+28)+'px; top:0px; width:17px; height:20px; '+pngBgImage(c+s+'_ne')+'"></div>';
			str+= '<div style="left:0px; top:20px; width:28px; height:'+(this.h+5)+'px; '+pngBgImage(c+s+'_w')+'"></div>';
			str+= '<div style="left:28px; top:20px; width:'+(this.w)+'px; height:'+(this.h+5)+'px; '+pngBgImage(c+s+'_c')+'"></div>';
			str+= '<div style="left:'+(this.w+28)+'px; top:20px; width:17px; height:'+(this.h+5)+'px; '+pngBgImage(c+s+'_e')+'"></div>';
			str+= '<div style="left:0px; top:'+(this.h+20+5)+'px; width:28px; height:18px; '+pngBgImage(c+s+'_sw')+'"></div>';
			str+= '<div style="left:28px; top:'+(this.h+20+5)+'px; width:'+(this.w)+'px; height:18px; '+pngBgImage(c+s+'_s')+'"></div>';
			str+= '<div style="left:'+(this.w+28)+'px; top:'+(this.h+20+5)+'px; width:17px; height:18px; '+pngBgImage(c+s+'_se')+'"></div>';
			if (this.place) str+= '<div style="left:'+(((this.w+38)/2) -20) +'px; top:'+(this.h+20+5+10)+'px; width:40px; height:29px; '+pngBgImage(s+'_marker')+'"></div>';
		}
		else
		{
			//with vertical auto-sizing
			str+= '<div style="width:28px; height:20px; '+pngBgImage(c+'_nw')+'"></div>';
			str+= '<div style="width:'+(this.w)+'px; height:20px; '+pngBgImage(c+'_n')+'"></div>';
			str+= '<div style="width:17px; height:20px; '+pngBgImage(c+'_ne')+'"></div>';
			//embedded content div (table is necessary to get this working in IE)
			str+= '<div style="width:'+(this.w+28+17)+'px;">';
			str+= '<table border=0 cellpadding=0 cellspacing=0>';
			str+= '<tr>';
			str+= '<td style="width:'+28+'px; '+pngBgImage(c+'_w')+'"></td>';
			str+= '<td style="width:'+this.w+'px; height:'+(this.h)+'px; vertical-align:top; '+pngBgImage(c+'_c')+'"></td>'; //content goes here
			str+= '<td style="width:'+17+'px; '+pngBgImage(c+'_e')+'"></td>';
			str+= '</tr>';
			str+= '</table>';
			str+= '</div>';
			str+= '<div style="width:28px; height:18px; '+pngBgImage(c+'_sw')+'"></div>';
			str+= '<div style="width:'+(this.w)+'px; height:18px; '+pngBgImage(c+'_s')+'"></div>';
			str+= '<div style="width:17px; height:18px; '+pngBgImage(c+'_se')+'"></div>';
		}
		pane.innerHTML = str;
	
		//title
		var div = document.createElement('div');
			div.className = 'pane7-titlebar';
			//div.style.width = (this.w-10) +'px';
		this.div.appendChild(div);
		this.titlebar = div;
	
		//content
		var content = document.createElement('div');
		content.className = 'content';
		if (this.style=='tooltip')
		{
			content.style.left = '15px';
			content.style.top = '10px';
		}
		if (!this.autosize)
		{
			var s = (this.style=='tooltip') ? 15:0;
			content.style.width = this.w +8 +s +'px';
			content.style.height = this.h +s +'px';
			pane.appendChild(content);
		}
		else //append to middle table cell
		{
			pane.getElementsByTagName('td')[1].appendChild(content);
		}
		//block dragging while over content div
 		if (this.dragable) content.onmouseover = function() { obj.drag.setEnabled(false) };
		this.content = content;

		//features
		if (this.style!='tooltip') this.setHeader();
		//make resizable
		if (this.resize) this.addResizing();
		//add closebutton
		if (p.close) this.addCloseButton(p.close);
		
		//visual adjustments for place-type pane
		if (this.place)
		{
			this.header.style.left = '1px';
			this.header.style.top = '1px';
			this.titlebar.style.top = '1px';
			if (this.closebutton) this.closebutton.style.top = '3px';
			
			//this.addPlaceArrow();
		}
		
		//event handling (rollover panes, not for static panes)
		if (this.autohide)
		{
			pane.onmouseover = function() { if (!obj.closing) obj.show() };
			pane.onmouseout = function() { obj.hide() };
		}
	
		if (this.centered) this.center();
	
		//reveal
		pane.style.visibility = 'visible';
	}
	
	Pane.prototype.setContent = function(str)
	{
		this.content.innerHTML = str;
	}
	
	Pane.prototype.addContent = function(str)
	{
		this.content.innerHTML += str;
	}
	
	Pane.prototype.setPosition = function(x,y)
	{
		this.x = x;
		this.div.style.left = this.x +'px';
		if (this.drag) this.drag.x = this.x;
		this.y = y;
		this.div.style.top = this.y +'px';
		if (this.drag) this.drag.y = this.y;
	}
	
	Pane.prototype.update = function()
	{
		//add later (?)
	}
	
	Pane.prototype.center = function() //->CHANGE THIS
	{
		var doc_w = (window.innerWidth!=null)? window.innerWidth:(document.documentElement && document.documentElement.clientWidth)? document.documentElement.clientWidth:(document.body!=null)? document.body.clientWidth:0;
		this.setPosition(Math.floor(doc_w/2 - this.w/2)-20);
		
		if (this.dragable) this.drag.setPosition(this.x,this.y);
	}
	
	Pane.prototype.setSize = function(w,h)
	{
		if (w==this.w && h==this.h) return;
		
		var elms = this.div.childNodes;
	
		this.w = w;
		if (!this.autosize)
		{
			this.h = h;
			elms[1].style.width = this.w +'px';
			elms[2].style.left = this.w+28 +'px';
			elms[3].style.height = this.h +5 +'px';
			elms[4].style.width = this.w +'px'; elms[4].style.height = this.h +5 +'px'; 
			elms[5].style.height = this.h +5 +'px'; elms[5].style.left = this.w+28 +'px';
			elms[6].style.top = this.h+20 +5 +'px';
			elms[7].style.width = this.w +'px'; elms[7].style.top = this.h +5 +20 +'px';
			elms[8].style.left = this.w+28 +'px'; elms[8].style.top = this.h +5 +20 +'px';
			if (this.place) 
			{
				elms[9].style.left = ((this.w+38)/2) -20 +'px'; 
				elms[9].style.top = (this.h+20+5+10) +'px';
			}
		}
		else
		{
			elms[1].style.width = this.w +'px';
			//elms[2].style.left = this.w+28 +'px';
			elms[3].style.width = this.w+28+17 +5 +'px'; 
			elms[5].style.width = this.w +'px';
			elms[3].getElementsByTagName('td')[1].style.width = this.w +'px';
			//elms[6].style.left = this.w+28 +'px';
		}
		
		this.div.style.width = (this.w+28+17) +3 +'px'; //->3px extra needed for IE display bug!
		if (!this.autosize)
		{
			var s = (this.s)? 15:0;
			this.div.style.height = (this.h+20+18) +'px';
			this.content.style.width = this.w +8 +s +'px';
			this.content.style.height = this.h +s +'px';
		}
		
		//titlebar
		//this.titlebar.style.width = (this.w-80) +'px';
		
		//resize drag handler
		if (this.resize)
		{
			this.handle.setPosition(this.w +28 -10,this.h +20);
			//resize margins
			var r = (typeof(this.resize)=='object')? this.resize:new Object();
			var min_x = r.min_x || 100;
			var min_y = r.min_y || 100;
			var max_x = r.max_x || 1000;
			var max_y = r.max_y || 1000;
			
			this.handle.setRange('x',true,min_x-this.w,max_x-this.w);
			this.handle.setRange('y',true,min_y-this.h,max_y-this.h);
		}
	}
	
	Pane.prototype.setHeader = function()
	{
		var canvas = document.createElement('canvas');
			canvas.width = 46;
			canvas.height = 20;
			canvas.style.width = 46 +'px'; //needed for safari
			canvas.style.height = 20 +'px';
			if (this.dragable) canvas.style.cursor = 'move';
			
		this.div.appendChild(canvas);
		//init canvas element for IE
		if (typeof(G_vmlCanvasManager)=='object') canvas = G_vmlCanvasManager.initElement(canvas);
		
		this.header = canvas;
				
		this.drawHeader();

		if (this.dragable)
		{
			var obj = this;
			canvas.onmouseover = function() { obj.drag.setEnabled(true) };
			canvas.onmouseout = function() { obj.drag.setEnabled(false) };
		}
	}
	
	Pane.prototype.drawHeader = function(color)
	{
		if (!this.header) return;
		
		var a = (Math.PI/180) * 40; //shear angle
		var w = 28;
		var h = 20;
		if (this.s)
		{
			w = w/2;
			h = h/2;
		}
		var s = h * Math.tan(a);
		var ctx = this.header.getContext('2d');
		
		ctx.clearRect(0,0,w+s,h);
		
		ctx.beginPath();
		ctx.moveTo(s,0);
		ctx.lineTo(w+s,0);
		ctx.lineTo(w,h);
		ctx.lineTo(0,h);
		
		ctx.globalAlpha = 1;
		ctx.fillStyle = color || this.hc;
		ctx.fill();
	}
	
	Pane.prototype.setTitle = function(html)
	{
		this.titlebar.innerHTML = html;
	}

	Pane.prototype.addCloseButton = function(callback,dispose)
	{
		var img = document.createElement('img');
			img.style.position = 'absolute';
			img.style.right = '12px';
			img.style.top = '2px';
			img.style.width = '14px';
			img.style.height = '14px';
			img.style.cursor = 'pointer';
			//ie png fix
			if (browser.cssfilter) img.onload = function() { utils.pngFix(this) };
			img.src = 'media/icon_close_pane.png';
			
		//event handling
		var obj = this;
		img.onmouseover = function() { utils.over(this,1) }
		img.onmouseout = function() { utils.over(this,0) }
		img.onclick = function() 
		{
			dbg.msg('-close pane- (',obj.id,')');
			//close pane
			if (dispose) obj.dispose();
			else obj.hide(1);
			
			//callback?
			if (typeof(callback)=='function') callback()
		}
			
		this.div.appendChild(img);
		this.closebutton = img;
	}
	
	Pane.prototype.addHelpButton = function(help)
	{
		var img = document.createElement('img');
			img.style.position = 'absolute';
			img.style.right = '28px';
			img.style.top = '2px';
			img.style.width = '14px';
			img.style.height = '14px';
			img.style.cursor = 'pointer';
			//ie png fix
			if (browser.cssfilter) img.onload = function() { utils.pngFix(this) };
			img.src = 'media/icon_help_pane.png';
			
		//event handling
		var obj = this;
		img.onmouseover = function() { utils.over(this,1) }
		img.onmouseout = function() { utils.over(this,0) }
		img.onclick = function() { portal.help(help) }
			
		this.div.appendChild(img);
		this.helpbutton = img;
	}
	
	Pane.prototype.addResizing = function(resize)
	{
		if (resize) this.resize = resize;
		//img
		var img = document.createElement('img');
			img.style.position = 'absolute';
			img.style.right = '13px';
			img.style.bottom = '6px';
			img.style.width = '10px';
			img.style.height = '10px';
			img.src = 'media/icon_resize.png';
			//pngfix for ie	
			if ((typeof(document.body.style.filter)=='string')) 
			{
				img.style.filter = 'progid:DXImageTransform.Microsoft.AlphaImageLoader(sizingMethod=scale,src=\'media/icon_resize.png\')';
				img.src = 'media/blank.gif';
			}
		this.div.appendChild(img);
	
		//handle
		var div = document.createElement('div');
			div.style.position = 'absolute';
			div.style.left = this.w +28 -10 +'px';
			div.style.top = this.h +20 +'px';
			div.style.width = '20px';
			div.style.height = '14px';
			div.style.cursor = 'pointer';//'se-resize';
			div.style.fontSize = '1px';//for IE6
			div.style.zIndex = 1000;
		this.div.appendChild(div);

		var obj = this;
		
		var r = (typeof(this.resize)=='object')? this.resize:new Object();
		var min_x = r.min_x || 100;
		var min_y = r.min_y || 100;
		var max_x = r.max_x || 1000;
		var max_y = r.max_y || 1000;
		var callback = r.callback || false;
		
		var handle = makeDragableItem(div);
			handle.setRange('x',true,min_x-obj.w,max_x-obj.w);
			handle.setRange('y',true,min_y-obj.h,max_y-obj.h);
			handle.appearance = function() { };
			handle.dragging = function()
			{
				//dbg.msg('resizing pane...')
				
				obj.setSize(this.x-28+10,this.y-20);
				if (callback) callback(this.x-28+10,this.y-20);
			}
			handle.drop = handle.dragging;
			
		this.handle = handle;
	}
	
	Pane.prototype.show = function()
	{
		this.doShow(true);
		this.visible = true;
	}
	Pane.prototype.hide = function(force)
	{
		if (force) this.closing = true;
		this.doShow(false,force);
		this.visible = false;
	}
	
	Pane.prototype.doShow = function(show,forcehide)
	{
		/*	show or hide pane, hide occurs after small delay, 
			and can be cancelled by calling pane.show() again */
		
		if (this.hideTimeout) window.clearTimeout(this.hideTimeout);
		
		if (forcehide)
		{
			this.div.style.display = 'none';
			this.hideMore();
			this.closing = false;
			return;
		}
			
		if (show)
		{
			this.div.style.display = 'block';
		}
		else
		{
			//hide afer delay
			var obj = this;
			this.hideTimeout = window.setTimeout( function() { obj.doShow(0,true) }, obj.delay);
		}
	}
	
	Pane.prototype.addPanel = function(p)
	{
		/*	create and add panel
		*/
		if (!this.panels) this.panels = new Object(); //collection of panels for this pane
		
		var panel = new Panel(this,p);
		this.panels[p.id] = panel;
		
		return panel;
	}
	
	Pane.prototype.removePanel = function(id)
	{
		this.content.removeChild(this.panels[id].div);
		delete this.panels[id];
	}
	
	Pane.prototype.dispose = function()
	{
		if (this.parent) this.parent.removeChild(this.div)
		else document.getElementById('layout').removeChild(this.div);
	}
	
	Pane.prototype.hideMore = function()
	{
		/*	called when hiding
			default empty, extend in object */
	}
	
	
	
	/*	panel object
	*/

	function Panel(pane,p)
	{
		/*	p = properties object,
			values: id, style, title, content, [expanded], [collapse], [parent] */
			
		var obj = this;

		this.id = p.id;
		this.expanded = false;
		this.style = p.style || '';
		this.can_be_collapsed = (p.collapse==undefined)? true:p.collapse;

		//create
		var div = document.createElement('div');
			div.className = 'panel';

		var header = document.createElement('div');
			header.style.fontWeight = 'bold';
			header.style.background = 'url(media/dotted_w_1000.gif) no-repeat 21px 7px';
			header.style.marginRight = '15px';
			header.onmousedown = function(e) { utils.cancelEvents(e,true) }; //prevent select

		var img = document.createElement('img');
			img.style.cssFloat = 'left';
			img.style.verticalAlign = 'middle';
			img.style.width = (this.can_be_collapsed)? '9px':'10px';
			img.style.height = '9px';
			var m = (this.can_be_collapsed)? 5:4;
			img.style.margin = '3px 5px 0px '+m+'px';
			//pngfix for ie	
			if (browser.cssfilter) img.onload = function() { utils.pngFix(this) }
			if (this.can_be_collapsed) 
			{
				img.style.cursor = 'pointer';
				img.src = 'media/icon_expand'+this.style+'.png';
			}
			else img.src = 'media/icon_listitem'+this.style+'.png';
			//event handling
			img.onclick = function() { obj.toggleExpand() }
			img.onmousedown = function(e) { utils.cancelEvents(e,true) }; //prevent drag select
		var title = document.createElement('span');
			if (this.can_be_collapsed) title.style.cursor = 'pointer';
			title.style.background = 'url(media/dotted_bl.gif) repeat-x 0px 7px';
			title.style.padding = '0px 8px 0px 0px';
			title.onclick = img.onclick;
			title.onmousedown = function(e) { utils.cancelEvents(e,true) }; //prevent text select
			title.innerHTML = p.title;

		header.appendChild(img);
		header.appendChild(title);
		this.header = header;

		var content = document.createElement('div');
			//content.style.marginLeft = '18px';
			content.style.marginLeft = '28px';
			content.style.marginTop = '3px';
			content.style.paddingRight = '15px';
			content.style.display = 'none';
			content.innerHTML = p.content;
			
		this.content = content;
		
		div.appendChild(header);
		div.appendChild(content);
		
		//add to pane content
		if (p.parent) p.parent.appendChild(div)
		else pane.content.appendChild(div);
		
		this.pane = pane;
		this.div = div;

		if (p.expanded || p.expanded==undefined) this.toggleExpand(); //expanded by default
	}

	Panel.prototype.toggleExpand = function()
	{
		if (this.expanded && !this.can_be_collapsed) return;
		
		this.expanded = !this.expanded;
		
		this.content.style.display = (this.expanded)? 'block':'none';
		if (this.can_be_collapsed) this.header.firstChild.src = (this.expanded)? 'media/icon_expanded'+this.style+'.png':'media/icon_expand'+this.style+'.png';
		
		if (this.expanded)
		{
			//check if in pane content view
			//dbg.msg(this.pane.content.offsetHeight,' - (',this.div.offsetTop,'+',this.content.offsetHeight,')');
			
			var offset = (this.div.offsetTop+this.div.offsetHeight+10) - this.pane.content.offsetHeight;
			if (offset>0) this.pane.content.scrollTop = offset;
			
			if (this.expandCallback) this.expandCallback();
		}
	}
		
	Panel.prototype.setContent = function(str)
	{
		this.content.innerHTML = str;	
	}
	Panel.prototype.addContent = function(str)
	{
		this.content.innerHTML+= str;	
	}



	/*	utils
	*/
	
	function pngBgImage(media)
	{
		/*	png transparency workaround for MSIE
		*/
		if ((typeof(document.body.style.filter)=='string')) return 'FILTER:progid:DXImageTransform.Microsoft.AlphaImageLoader(sizingMethod=scale,src=\'media/pane/'+media+'.png\');';
		else return 'background-image:url(\'media/pane/'+media+'.png\');';
	}
}

/* [file src="Panels.js"]
*/
/* 	usemedia.com . joes koppers . 08.2008
	thnx for reading this code */


/*	7s (layer) panels (Widgets)
	requires common/use.js */

function Panels7(layer)
{
	/*	layers collection
	*/
	
	this.count = 0;

	this.create = function(properties)
	{
		if (!properties.id) return;
		var panel = new Panel(this,layer,properties);
		//add to collection
		this[properties.id] = panel;
		
		this.count++;
		
		return panel;
	}
	
	this.dispose = function(id)
	{
		if (this[id]) this[id].dispose
		delete this[id];
		this.count--;
	}
	
	this.clear = function()
	{
		for (id in this) if (typeof(this[id])=='object') delete this[id];
		this.count = 0;
	}
	
	/*	(layer) panel object
	*/

	function Panel(collection,layer,p)
	{
		/*	p = properties object, options:
			id					unique id, required
			parent				parent DOM elm 
			[index]				index in collection
			[title]				title of pane (string or html)
			[template]			template name (preset panel styles)	
			[can_be_closed]		false, adds close button
			[can_be_minimized]	false, adds minize button
			[can_be_resized]	false, adds resize handle
			[style]				css style object
			[border]			{ light:color, dark:color }, 3d border colors 
			[content_only]		false, do not display header and footer
			[content_padding]	padding for content_only style			*/
	
		this.id = p.id;
		this.title = p.title || '';
		this.portal = layer.portal;
		this.index = p.index || collection.count;
		
		//create DOM elements
		var div = document.createElement('div');
			div.className = 'panel7';
			
		if (p.style) for (style in p.style) div.style[style] = p.style[style];
		
		if (p.border)
		{
			//light and dark shade borders
			div.style.borderTopColor = div.style.borderLeftColor = p.border.dark;
			div.style.borderBottomColor = div.style.borderRightColor = p.border.light;
		}
		else
		{
			//add default 'borders' (transparent overlay images), can be used on gradient background
			div.style.border = '0px';
			div.style.padding = '1px';

			var b = ['top','right','bottom','left'];
			for (var i=0; i<b.length; i++)
			{
				var type = (b[i]=='left' || b[i]=='right') ? 'v':'h';
				var border = document.createElement('div');
					border.className =  'panel7-border panel7-border'+type;
					border.style.top = b[i]=='bottom' ? '':'0px';
					border.style.left = b[i]=='right' ? '':'0px';
					if (b[i]=='bottom') border.style.bottom = '0px';
					if (b[i]=='right') border.style.right = '0px';
					
				var bg = (b[i]=='left' || b[i]=='top') ? 'dark':'light';
				//if (browser.cssfilter) border.style.filter = 'progid:DXImageTransform.Microsoft.AlphaImageLoader(sizingMethod=scale,src=\'media/bg_panelborder_'+bg+'.png\');';
				//else border.style.backgroundImage = 'url(media/bg_panelborder_'+bg+'.png)';
				border.style.backgroundImage = 'url(media/bg_panelborder_'+bg+'.png)';
				
				div.appendChild(border);
			}
		}
		
		if (!p.content_only)
		{
			var header = document.createElement('div');
				header.className = 'panel7-header';
			var menu = document.createElement('div');
				menu.className = 'panel7-menubar';
			header.appendChild(menu);

			var footer = document.createElement('div');
				footer.className = 'panel7-footer';
		}
			
		var content = document.createElement('div');
			content.className = 'panel7-content';
		if (p.style && !p.style.height) content.style.overflow = 'visible'; //auto-(v)size pane, no overflow for content
		if (p.content_only) content.style.marginLeft = '0px';

		content.innerHTML = p.content || '';
		
		//add DOM elements
		if (!p.content_only) div.appendChild(header);
 		div.appendChild(content);
 		if (!p.content_only) div.appendChild(footer);

		p.parent.appendChild(div);

		this.div = div;
		this.header = header;
		this.menu = menu;
		this.content = content;
		this.footer = footer;

		//content only style
		this.content_only = p.content_only;
		this.content_padding = p.content_padding; 
		if (p.content_only && p.content_padding) this.content.style.padding = p.content_padding +'px';

		if (p.style)
		{
			if (p.style.height)
			{
				var h = parseInt(p.style.height);
				if (p.content_only && p.content_padding) h -= 2*p.content_padding;
				this.setContentSize(h);
			}
			if (p.style.minHeight) this.content.style.minHeight = parseInt(p.style.minHeight) - 24 - 15 +'px';
		}

		//menubar
		if (!p.content_only) this.menubar = { parent:this, div:menu, w:4 };
		//title
		if (p.title) this.addTitle(p.title);
	
		//options
		this.addOptions(p);
	}
	
	Panel.prototype.show = function(hide)
	{
		this.div.style.display = hide? 'none':'block';
	}
	
	Panel.prototype.hide = function()
	{
		this.show(true);
	}
	
	Panel.prototype.setTemplate = function(template)
	{
		/*	preset styles
		*/
		switch (template)
		{
			case 'paper':
				this.div.className += ' panel7-paper';
				break;
				
			case 'scene-highlight-panel':
				this.div.className += ' scene-panel';
				break;
		}
	}

	Panel.prototype.addOptions = function(p)
	{
		if (p.template) this.setTemplate(p.template);
		if (p.can_be_closed) this.addCloseButton();
		if (p.can_be_minimized) this.addMinimizeButton();
		if (p.can_be_resized) this.addResizing();
		if (p.can_be_moved) this.enableMoving();
	}
	
	Panel.prototype.addTitle = function(title)
	{
		if (this.content_only) return;
		var div = document.createElement('div');
			div.className = 'panel7-title';
			div.innerHTML = title;
		this.header.appendChild(div);
	}
	
	Panel.prototype.addMenu = function(p)
	{
		/*	add menu to panel, using layer.menu class
			default height = 19 
			add p.menubar to attach menu to other parent than panel.. */
		
		p.h = 19;
		var menu = this.portal.layout.layers.createMenu(p.menubar || this.menubar,p);
		if (p.menubar) p.menubar[p.id] = menu;
		else this.menubar[p.id] = menu;
		
		return menu;
	}
	
	Panel.prototype.addCloseButton = function()
	{
		var close = utils.pngButton( { src:'icon_close', style:{ width:'18px', height:'18px', margin:'2px 3px', cssFloat:'right' } } );
			close.onclick = function() { dbg.msg('close!') };
		
		this.header.insertBefore(close, this.header.lastChild);
		this.closeButton = close;
	}
	
	Panel.prototype.addMinimizeButton = function()
	{
	
	
	}

	
	Panel.prototype.addResizing = function(resize)
	{
		/*	panel resizing, param:
				[resize]	{ min_x, max_y, min_y, may_y, callback } */

		if (!this.div.style.height) return;
				
		//calculated dimensions
		this.w = this.div.offsetWidth;
		this.h = this.div.offsetHeight;

		//gui image				
		var img = document.createElement('img');
			img.className = 'panel7-resize';
			img.src = 'media/icon_resize_panel.png';
		this.footer.appendChild(img)
	
		//drag handle
		var div = document.createElement('div');
			div.className = 'panel7-resizehandle';
			div.style.left = this.w - 15 -2 +'px';
			div.style.top = this.h - 15 - 2 +'px';
		
		this.div.appendChild(div);
		
		var obj = this;
		//default margins
		this.resize = resize || { min_x:200, max_x:1000, min_y:100, max_y:1000 } 

 		var callback = this.resize.callback;
		var handle = makeDragableItem(div);
 			handle.setRange('x',true,this.w-this.resize.min_x,this.w-this.resize.max_x);
 			handle.setRange('y',true,this.h-this.resize.min_y,this.h-this.resize.max_y);
			handle.appearance = function() { };
			handle.dragging = function()
			{
				obj.updateSize(this.x+15, this.y+15);
				if (callback) callback(this.x+15,this.y+15);
			}
			handle.drop = handle.dragging;
			
		this.resize.handle = handle;
	}
	
	Panel.prototype.addArrow = function(style)
	{
		/*	draw shaded arrow at edge of panel
		*/
// 		var canvas = document.createElement('canvas');
// 			canvas.className = 'arrow';
// 			canvas.width = 40;
// 			canvas.height = 20;
// 			if (style=='bottom')
// 			{
// 				canvas.style.right = '80px';
// 				canvas.style.bottom = '-20px';
// 			}
// 			else //default top
// 			{
// 				canvas.style.top = '-12px';
// 				canvas.style.bottom = '-20px';
// 			}
// 		this.div.appendChild(canvas);
// 
// 		//init canvas element for IE
// 		if (typeof(G_vmlCanvasManager)=='object') canvas = G_vmlCanvasManager.initElement(canvas);
// 			
// 		var ctx = canvas.getContext('2d');
// 	
// 		ctx.beginPath();
// 		ctx.moveTo(0,0);
// 		ctx.lineTo(40,0);
// 		ctx.lineTo(20,20);
// 		ctx.lineTo(0,0);
// 		ctx.fillStyle = '#999966';
// 		ctx.fill();
// 		//shadow
// 		ctx.beginPath();
// 		ctx.moveTo(0,0);
// 		ctx.lineTo(20,20);
// 		ctx.strokeStyle = '#75744a';
// 		ctx.stroke();
// 		//highlight
// 		ctx.beginPath();
// 		ctx.moveTo(40,0);
// 		ctx.lineTo(20,20);
// 		ctx.strokeStyle = '#baba86';
// 		ctx.stroke();
	}

	
	
	Panel.prototype.setContent = function(str)
	{
		this.content.innerHTML = str;
	}
	
	Panel.prototype.updateSize = function(w,h)
	{
		/*	used for resizable panels
		*/
		this.div.style.width = w +'px';
		this.div.style.height = h +'px';

		this.setContentSize(h); //content.style.height = h - 24 - 15 +'px';

		this.w = w;
		this.h = h;
	}

	Panel.prototype.setContentSize = function(h)
	{
		/*	update content height (minus header and footer)
		*/
		if (!this.content_only) h = h - 24 - 15;
		this.content.style.height = h +'px';
	}
	

	Panel.prototype.enableMoving = function()
	{
		/*	allow drag-drop placement of widget, add in future version
		*/
	}

	
}

