var Base = new Class({
	
	initialize: function(){
		this.p = pipio;
		this.registerHandlers();
		if ($defined(this.init))
		{
			var args = [];
			for(var i = 0; i < arguments.length; i++)
			{
				args.push(arguments[i]);
			}
			this.init.run(args, this);
		}
	},
	
	//registers event handlers
	registerHandlers: function(){
		if ($defined(this.EventHandlers))
		{
			for (var i = 0; i < this.EventHandlers.length; i++)
			{
				var event = this.EventHandlers[i];
				this.registerHandler(event, this[event].bind(this));
			}
		}
	},
	
	registerHandler: function(eventName, func){
		this.p.registerHandler(eventName, func);
	},
	
	//calls pipio class to fire events w/ data payload
	fireEvent: function(){
		var args = [];
		for(var i = 0; i < arguments.length; i++)
			args.push(arguments[i]);
		this.p.dispatchEvent.run(args, this.p);
	},
	
	//api calling methods
	call: function(app, name, params, callbackSuccess, callbackFail, form){
		this.p.call(app, name, params, callbackSuccess, callbackFail, form);
	},
	
	//global functions that are implemented in every subclass
	getPrivateUser: function(){
		return this.p.currentUser;
	},
	
	getCurrentLocation: function(){
		return this.p.currentLocation;
	},
	
	getUserLocation: function(username){
		if (this.p.locationsByUsername.has(username))
			return this.p.locationsByUsername.get(username);
		else
			return null;
	},
	
	getSession: function(){
		return this.p.xmpp.clientName;
	},
	
	getGroups: function(){
		return this.p.groups;
	},
	
	getContacts: function(){
		return this.p.connsByUsername;
	},
	
	getContactRequests: function(){
		return this.p.requestsByUsername;
	},
	
	getContactsByGroup: function(group_id){
		return this.p.getContactsByGroup(group_id);
	},
	
	getContact: function(username){
		return this.p.getContact(username);
	},
	
	hasContact: function(username){
		return this.p.connsByUsername.has(username);
	},
	
	getRoom: function(username){
		return this.p.getRoom(username);
	},
	
	getRooms: function(){
		return this.p.roomsByUsername;
	},
	
	getGroup: function(group_id){
		return this.p.connections.getGroup(group_id);	
	},
	
	getUser: function(username){
		
		if (username == this.p.currentUser.username)
			return this.p.currentUser;
		else			
			return this.p.getUser(username);
	},
	
	//other utility funcs
	videoEnabled: function(){
		if (this.p.videoInUse)
			return false;
		else
			return this.p.videoEnabled;
	},
	
	checkTimestamp: function(time){
		return this.p.checkTimestamp(time);
	},
	
	getAppById: function(appId){
		return this.p.getAppById(appId);
	},
	
	isLoggedIn: function(){
		return this.p.isLoggedIn();
	},
	
	getPageTitle: function(){
		return this.p.pageTitle;
	},
	
	getConnectionState: function(username){
		//returns connection state
		// 0- not connected 1- connected 2- request recieved 3- reqeuest sent
		var state = 0;
		
		if (this.p.connsByUsername.has(username))
			state = 1;
		else if (this.p.requestsByUsername.has(username))
			state = 2;
		else if (this.p.requestsOutByUsername.has(username))
			state = 3;
			
		return state;
	}
});var App = new Class({
	Extends: Base,
	
	initialize: function(app){
		this.parent();
		
		this.name = app.name;
		this.iconOptions = app.iconOptions;
		
		this.registerApis();

		this.displayName = app.displayName;
		this.appId = app.id;
		this.isStarted = false;
		this.isDocked = false;
		this.isExpanded = true;
		
		this.defaultMenu = undefined;
		this.defaultContent = undefined;
		
		//some hashes to keep track of menus and contents
		this.menus = $H();
		this.contents = $H();
		this.navs = $H();
	
	},
	
	registerApis: function(){
		if ($defined(this.requests))
		{
			this.requests.each(function(request){
				this.p.registerCall(this.name, request);
			}, this);
		}
	},
	
	//starts the app, this will start all periodical calls & connections
	start: function(options){
		if (!this.isStarted)
		{
			
			if ($defined(this.parseOptions) && $defined(options))
				this.parseOptions(options);
			
			
			this.createApp();
			this.onStart();
			this.isStarted = true;
		}
		else if (this.isStarted && this.isDocked)
		{
			this.undock();
		}
		
		//auto dock if logged out
		if (!this.isLoggedIn())
			this.dock();
		
		this.switchDefault();
	},
	
	//stops app, destroys all elements & data
	stop: function(){
		Logger().log('stopping app' + this.appId);
		if (this.isStarted)
		{
			this.destroyApp();
			this.onStop();
			this.isStarted = false;
			this.isDocked = false;
			this.isExpanded = true;
		}
	},
	
	getDockIcon: function(){
		var icon = new Icon20(this.iconOptions);
		
		var el = new Element('div', {'class': 'app_button has_submenu'}).adopt(
			$(icon),
			$(this.alert)
		);
		
		el.addEvent('click', this.undock.bind(this));
		
		return el;
	},
	
	dock: function(){
		
		DomUtility.hide(this.navSection);
		
		this.dockButton = new Element('div', {'class': 'app_button_wrapper'}).adopt(
			this.getDockIcon(),
			this.navWrapper
		);
		
		this.dockButton.inject('dock');
		
		this.isDocked = true;
		
	},
	
	undock: function(){
		if (!this.isLoggedIn())
		{
			this.switchDefault();
			return;
		}
		
		this.navWrapper.inject(this.navSection);
		
		this.dockButton.destroy();
		
		DomUtility.show(this.navSection);
		
		this.isDocked = false;
		
		this.switchDefault();
	},
	
	switchDefault: function(){
		//switch to default content
		if ($defined(this.defaultContent))
			this.fireEvent('viewSwitch', this.appId, this.defaultContent, this.defaultMenu);	
		
	},
	
	menuAdd: function(menuName, menu){
		this.fireEvent('menuAdd', this.appId, menuName, menu);
		if (menu.isDefault)
			this.defaultMenu = menuName;
		
		this.menus.set(menuName, menu);
	},
	
	menuClose: function(menuName){
		this.fireEvent('menuClose', this.appId, menuName);
		this.menus.erase(menuName);
	},
	
	contentAdd: function(contentName, content){
		this.fireEvent('contentAdd', this.appId, contentName, content);
		if (content.isDefault)
			this.defaultContent = contentName;
		
		this.contents.set(contentName, content);
	},
	
	contentClose: function(contentName){
		this.fireEvent('contentClose', this.appId, contentName);
		this.contents.erase(contentName);
	},
	
	//add/deleting items from the nav
	navAdd: function(nav){
		
		//generate and store navId
		var navId = this.appId + '_' + nav.name;
		nav.navId = navId;
		
		
		if ($defined(nav.parentName))
		{
			nav.parentId = this.appId + '_' + nav.parentName;
		}
		
		if (this.navs.has(navId))
		{
			Logger().log(navId + ' nav exist, did not add');
			return;
		}
		
		//if parent is given, look for parent and inject in parent's subnav
		if ($defined(nav.parentId))
		{
			if (!this.navs.has(nav.parentId))
			{
				Logger().log(nav.parentId + ' parent does not exist');
				return;
			}
			
			//parent exists
			var parent = this.navs.get(nav.parentId);
			if (!parent.hasSubnavs)
			{
				Logger().log(nav.parentId + ' parent does not allow subnavs');
				return;
			}
			
			//all good inject it into parent's subnavs
			else
			{
				//Logger().log('adding nav '+ navId + ' parent ' + nav.parentId);
				parent.addSubnav(nav);
				//$(nav).inject(parent.subnavs);
			}
		}
		else
		{
			//no parent id, set parentId to appId, this will fire alert events up to the app level
			nav.parentId = this.appId;
			if (nav.bottom)
				$(nav).inject(this.bottomNav);
			else
				$(nav).inject(this.nav);
		}

		this.navs.set(navId, nav);
	},
	
	navDelete: function(navName){
		var navId = this.appId + '_' + navName;
		if (!this.navs.has(navId))
			return;
		
		this.navs.get(navId).destroy();
		this.navs.erase(navId);
		
	},
	
	createAppAlert: function(){
		this.alert = new Alert();
		
		this.registerHandler('alertAdd', this.alertAdd.bind(this));
		this.registerHandler('alertClear', this.alertClear.bind(this));
	},
	
	//alert event
	alertAdd: function(navId){
		if (navId != this.appId)	
			return;

		this.alert.increment();
	},
	
	alertClear: function(navId, count){
		if (navId != this.appId)	
			return;
		
		if (!$defined(count))
			var alerts = this.alert.clear();
		else
			this.alert.decrement(count);
	},
	
	createApp: function(){
		
		this.navSection = new Element('div', {'class': 'nav_section'});
		this.navHeader = new Element('div', {'class': 'nav_header clip2'}).inject(this.navSection);
		this.navWrapper = new Element('div', {'class': 'app_sub_menu sub_elements'}).inject(this.navSection);
		this.nav = new Element('div').inject(this.navWrapper);
		this.bottomNav = new Element('div').inject(this.navWrapper);
		this.navSection.inject('nav');
		
		//create header stuff
		this.icon = new Icon20(this.iconOptions);
		$(this.icon).inject(this.navHeader);
		
		this.headerName = new Element('div', {
			'class': 'header_name text12 light', 
			'text': TextUtility.unescapeQuotes(this.displayName)
		}).inject(this.navHeader);
		
		//close button shuts down the app
		this.closeButton = new Element('div', {'class': 'button_nav right'}).adopt(
			new Element('div', {'class': 'action close'})
		).inject(this.navHeader);
		this.closeButton.addEvent('click', this.stop.bind(this));
		
		//dock button puts the app in docked mode
		this.dockButton = new Element('div', {'class': 'button_nav right'}).adopt(
			new Element('div', {'class': 'action dock'})
		).inject(this.navHeader);
		this.dockButton.addEvent('click', this.dock.bind(this));
		
		//create alert
		this.createAppAlert();
		
		if ($defined(this.onCreate))
			this.onCreate();
	},
	
	//destroy all element and free up resources, this app is no longer running
	destroyApp: function(){
		this.contents.each(function(content, contentName){
			this.contentClose(contentName);
		}, this);
		this.contents.empty();
		
		this.menus.each(function(menu, menuName){
			this.menuClose(menuName);
		}, this);
		this.menus.empty();
		
		this.navs.each(function(nav){
			nav.destroy();
		});
		this.navs.empty();
		
		this.navHeader.destroy();
		this.nav.destroy();
		this.navSection.destroy();
		
		if ($defined(this.dockButton))
			this.dockButton.destroy();
		
		this.fireEvent('appClose', this.appId);
		
		if ($defined(this.onDestroy))
			this.onDestroy();
	}
});
var AppInstance = new Class({
	Extends: Base,
	
	Implements: App,
	
	initialize: function(app, options){
		this.parent();
		
		this.parseOptions(options);
		this.name = app.name;
		
		this.registerApis();
		
	
		this.isStarted = false;
		this.isDocked = false;
		this.isExpanded = true;
		
		this.defaultMenu = undefined;
		this.defaultContent = undefined;
		
		//some hashes to keep track of menus and contents
		this.menus = $H();
		this.contents = $H();
		this.navs = $H();
	},

	//starts the app, this will start all periodical calls & connections
	start: function(){
	if (!this.isStarted)
	{
		
		this.createApp();
		this.onStart();
		this.isStarted = true;
	}
	else if (this.isStarted && this.isDocked)
	{
		this.undock();
	}
	
	//auto dock if logged out
	if (!this.isLoggedIn())
		this.dock();
	
	this.switchDefault();
}

});

	/*
	//example, these are the values that must be set
	parseOptions: function(options){
		
		this.displayName = 'App Name',
		this.appId = 'uniqueid';
		this.iconOptions = {iconName: 'pipio'};
	},

	registerApis: function(){
		if ($defined(this.requests))
		{
			this.requests.each(function(request){
				this.p.registerCall(this.name, request);
			}, this);
		}
	},
	
	//starts the app, this will start all periodical calls & connections
	start: function(){
		if (!this.isStarted)
		{
			this.createApp();
			this.onStart();
			this.isStarted = true;
		}
		else if (this.isStarted && this.isDocked)
		{
			this.undock();
		}
		
		this.switchDefault();
	},
	
	//stops app, destroys all elements & data
	stop: function(){
		Logger().log('stopping app' + this.appId);
		if (this.isStarted)
		{
			this.destroyApp();
			this.onStop();
			this.isStarted = false;
			this.isDocked = false;
			this.isExpanded = true;
		}
	},
	
	getDockIcon: function(){
		var icon = new Icon20(this.iconOptions);
		
		var el = new Element('div', {'class': 'app_button has_submenu'}).adopt(
			$(icon),
			$(this.alert)
		);
		
		el.addEvent('click', this.undock.bind(this));
		
		return el;
	},
	
	dock: function(){
		
		DomUtility.hide(this.navSection);
		
		this.dockButton = new Element('div', {'class': 'app_button_wrapper'}).adopt(
			this.getDockIcon(),
			this.navWrapper
		);
		
		this.dockButton.inject('dock');
		
		this.isDocked = true;
		
	},
	
	undock: function(){
		
		this.navWrapper.inject(this.navSection);
		
		this.dockButton.destroy();
		
		DomUtility.show(this.navSection);
		
		this.isDocked = false;
		
		this.switchDefault();
	},
	
	switchDefault: function(){
		//switch to default content
		if ($defined(this.defaultContent))
			this.fireEvent('viewSwitch', this.appId, this.defaultContent, this.defaultMenu);	
		
	},
	
	menuAdd: function(menuName, menu){
		this.fireEvent('menuAdd', this.appId, menuName, menu);
		if (menu.isDefault)
			this.defaultMenu = menuName;
		
		this.menus.set(menuName, menu);
	},
	
	menuClose: function(menuName){
		this.fireEvent('menuClose', this.appId, menuName);
		this.menus.erase(menuName);
	},
	
	contentAdd: function(contentName, content){
		this.fireEvent('contentAdd', this.appId, contentName, content);
		if (content.isDefault)
			this.defaultContent = contentName;
		
		this.contents.set(contentName, content);
	},
	
	contentClose: function(contentName){
		this.fireEvent('contentClose', this.appId, contentName);
		this.contents.erase(contentName);
	},
	
	//add/deleting items from the nav
	navAdd: function(nav){
		
		//generate and store navId
		var navId = this.appId + '_' + nav.name;
		nav.navId = navId;
		
		
		if ($defined(nav.parentName))
		{
			nav.parentId = this.appId + '_' + nav.parentName;
		}
		
		if (this.navs.has(navId))
		{
			Logger().log(navId + ' nav exist, did not add');
			return;
		}
		
		//if parent is given, look for parent and inject in parent's subnav
		if ($defined(nav.parentId))
		{
			if (!this.navs.has(nav.parentId))
			{
				Logger().log(nav.parentId + ' parent does not exist');
				return;
			}
			
			//parent exists
			var parent = this.navs.get(nav.parentId);
			if (!parent.hasSubnavs)
			{
				Logger().log(nav.parentId + ' parent does not allow subnavs');
				return;
			}
			
			//all good inject it into parent's subnavs
			else
				$(nav).inject(parent.subnavs);
			
		}
		else
		{
			//no parent id, set parentId to appId, this will fire alert events up to the app level
			nav.parentId = this.appId;
			if (nav.bottom)
				$(nav).inject(this.bottomNav);
			else
				$(nav).inject(this.nav);
		}

		this.navs.set(navId, nav);
	},
	
	createAppAlert: function(){
		this.alert = new Alert();
		
		this.registerHandler('alertAdd', this.alertAdd.bind(this));
		this.registerHandler('alertClear', this.alertClear.bind(this));
	},
	
	//alert event
	alertAdd: function(navId){
		if (navId != this.appId)	
			return;

		this.alert.increment();
	},
	
	alertClear: function(navId, count){
		if (navId != this.appId)	
			return;
		
		if (!$defined(count))
			var alerts = this.alert.clear();
		else
			this.alert.decrement(count);
	},
	
	createApp: function(){
		
		this.navSection = new Element('div', {'class': 'nav_section'});
		this.navHeader = new Element('div', {'class': 'nav_header clip2'}).inject(this.navSection);
		this.navWrapper = new Element('div', {'class': 'app_sub_menu sub_elements'}).inject(this.navSection);
		this.nav = new Element('div').inject(this.navWrapper);
		this.bottomNav = new Element('div').inject(this.navWrapper);
		this.navSection.inject('nav');
		
		//create header stuff
		this.icon = new Icon20(this.iconOptions);
		$(this.icon).inject(this.navHeader);
		
		this.headerName = new Element('div', {
			'class': 'header_name text12 light', 
			'text': TextUtility.unescapeQuotes(this.displayName)
		}).inject(this.navHeader);
		
		//close button shuts down the app
		this.closeButton = new Element('div', {'class': 'button_nav right'}).adopt(
			new Element('div', {'class': 'action close'})
		).inject(this.navHeader);
		this.closeButton.addEvent('click', this.stop.bind(this));
		
		//dock button puts the app in docked mode
		this.dockButton = new Element('div', {'class': 'button_nav right'}).adopt(
			new Element('div', {'class': 'action dock'})
		).inject(this.navHeader);
		this.dockButton.addEvent('click', this.dock.bind(this));
		
		//create alert
		this.createAppAlert();
		
		if ($defined(this.onCreate))
			this.onCreate();
	},
	
	//destroy all element and free up resources, this app is no longer running
	destroyApp: function(){
		this.contents.each(function(content, contentName){
			this.contentClose(contentName);
		}, this);
		this.contents.empty();
		
		this.menus.each(function(menu, menuName){
			this.menuClose(menuName);
		}, this);
		this.menus.empty();
		
		this.navs.each(function(nav){
			nav.destroy();
		});
		this.navs.empty();
		
		this.navHeader.destroy();
		this.nav.destroy();
		this.navSection.destroy();
		
		this.fireEvent('appClose', this.appId);
		
		if ($defined(this.onDestroy))
			this.onDestroy();
	}
});
*/var Content = new Class({
	Extends: Base,
	
	init: function(options){
		if (!$defined(options))
			options = {};
		
		if ($defined(this.onBeforeInit))
			this.onBeforeInit(options);
	
		this.className = $defined(options.className)? options.className: '';
		this.isDefault = options.isDefault || false;
		this.isOn = true;
		this.firstShow = true;
		this.bottomFuncCalled = false;
		this.atBottom = false;
		
		this.createContent();
		
		if ($defined(this.onInit))
			this.onInit();
	},
	
	checkScroll: function(){
		
		var maxY = $('content_wrapper').getScrollSize().y;
		var bottomY = $('content_wrapper').getScroll().y + $('content_wrapper').getSize().y + 500;
		
		if (bottomY > maxY && !this.bottomFuncCalled && !this.atBottom)
		{
			this.bottomFuncCalled = true;
			this.bottomFunc();
		}
	},
	
	destroy: function(){
		if ($defined(this.onDestroy))
			this.onDestroy();
		
		this.content.destroy();
		
	},
	
	on: function(){
		DomUtility.show(this.content);
		
		if ($defined(this.onShow))
		{
			if (this.firstShow)
			{
				this.onShow(true);
				this.firstShow = false;
			}
			else
				this.onShow(false);
		}
		
		this.isOn = true;
		
		//attach scroll handler if defined
		if ($defined(this.bottomFunc))
		{
			Logger().log('attaching scrollevent');
			$('content_wrapper').addEvent('mousewheel', this.checkScroll.bind(this));
		}
	},
	
	off: function(){
		if ($defined(this.onHide))
			this.onHide();
		
		DomUtility.hide(this.content);
		this.isOn = false;
		
		$('content_wrapper').removeEvents('mousewheel');
	},
	
	createContent: function(){
		this.content = new Element('div', {'class': 'content ' + this.className});
		
	},
	
	toElement: function(){
		return this.content;
	}
});var Alert = new Class({
	initialize: function(){
		this.alerts = 0;
		this.createAlert();
	},
	
	createAlert: function(){
		this.alert = new Element('div', {'class': 'alert', 'text': '0', 'style': 'display: none !important'});
	},
	
	hasAlerts: function(){
		return this.alerts > 0;
	},
	
	update: function(){
		if (this.alerts < 0)
			this.alerts = 0;
		
		this.alert.set('text', this.alerts);
		//set display
		if (this.alerts == 0)
			this.alert.setStyle('display', 'none !important');
		else
			this.alert.setStyle('display', '');
	},
	
	increment: function(x){
		if (!$defined(x))
			this.alerts++;
		else
			this.alerts += x;
		this.update();
	},
	
	decrement: function(x){
		if (!$defined(x))
			this.alerts--;
		else
			this.alerts -= x;
		this.update();
	},
	
	clear: function(){
		var alerts = this.alerts;
		this.alerts = 0;
		this.update();
		return alerts;
	},
	
	toElement: function(){
		return this.alert;
	}
	
});var ButtonMedium = new Class({
	initialize: function(options){
	
		this.displayName = options.displayName;
		this.className = options.className || '';
		this.action = options.action || undefined;
		this.disabled = options.disabled || false;
		
		this.createButton();
	},
	
	createButton: function(){
		this.button = new Element('div', {'class' : 'button_medium'});
		this.button.addClass(this.className);
		
		if ($defined(this.action))
		{
			this.button.addClass('has_action');
			new Element('div', {'class': 'action ' + this.action}).inject(this.button);
		}
		
		this.button.appendText(TextUtility.unescapeQuotes(this.displayName));
	},
	
	showProgress: function(){
		this.button.addClass('progress');
	},
	
	hideProgress: function(){
		this.button.removeClass('progress');
	},
	
	toElement: function(){
		return this.button;
	}
	
});var ButtonSmall = new Class({
	initialize: function(options){
	
		this.displayName = options.displayName;
		this.className = options.className || '';
		this.action = options.action || undefined;
		this.disabled = options.disabled || false;
		
		this.createButton();
	},
	
	createButton: function(){
		this.button = new Element('div', {'class' : 'button_small'});
		this.button.addClass(this.className);
		
		if ($defined(this.action))
		{
			this.button.addClass('has_action');
			new Element('div', {'class': 'action ' + this.action}).inject(this.button);
		}
		
		this.button.appendText(TextUtility.unescapeQuotes(this.displayName));
	},
	
	showProgress: function(){
		this.button.addClass('progress');
	},
	
	hideProgress: function(){
		this.button.removeClass('progress');
	},
	
	toElement: function(){
		return this.button;
	}
	
});var Icon = new Class({
	initialize: function(options){
		this.iconAction = options.iconAction || null;
		this.isUser = $defined(options.user);
		
		if (this.isUser)
			this.user = options.user;
		else
		{
			this.iconName = options.iconName;
			if (this.iconName.contains('http://'))
				this.isClass = false;
			else
				this.isClass = true;
		}
	
		this.createIcon();
	},
	
	createIcon: function(){
		this.icon = new Element('div', {'class': 'icon_wrapper'});
		
			
		if (!this.isUser)
		{	//not a user, either a class or a url to image
			if (this.isClass)
			{
				this.icon.addClass('hasicon on ' + this.iconName);
				this.icon.adopt(
					new Element('div', {'class': 'icon'})
				);
			}
			else
			{
				this.icon.adopt(
					new Element('img', {'src': this.iconName})
				);
			}
		}
		else
		{
			this.icon.addClass('profile_pic_wrapper_16');
			this.icon.adopt(
				new Element('img', {
					'class': 'profile_pic profile_pic_16_' + this.user.username, 
					'src': this.user['profile_pic_16']
					}),
				new Element('div', {'class': 'online_status online_status_' + this.user.username})
			
			);
		}
		
		if ($defined(this.iconAction))
		{
			this.addAction(this.iconAction);
		}
	},
	
	toElement: function(){
		return this.icon;
	},
	
	//adds a action element to icon (add, remove, delete, etc)
	addAction: function(action){
		this.iconAction = action;
		
		//destroy old action if exists
		if ($defined(this.action))	
			this.action.destroy();
		
		//add action
		this.action = new Element('div', {'class': 'icon_action ' + this.iconAction}).inject(this.icon);
	}
});var Icon20 = new Class({
	initialize: function(options){
		this.iconAction = options.iconAction || null;
		this.isUser = $defined(options.user);
		
		if (this.isUser)
			this.user = options.user;
		else
		{
			this.iconName = options.iconName;
			if (this.iconName.contains('http://'))
				this.isClass = false;
			else
				this.isClass = true;
		}
	
		this.createIcon();
	},
	
	createIcon: function(){
		this.icon = new Element('div', {'class': 'icon_wrapper'});
		
			
		if (!this.isUser)
		{	//not a user, either a class or a url to image
			if (this.isClass)
			{
				this.icon.addClass('hasicon on ' + this.iconName);
				this.icon.adopt(
					new Element('div', {'class': 'icon20'})
				);
			}
			else
			{
				this.icon.adopt(
					new Element('img', {'src': this.iconName})
				);
			}
		}
		else
		{
			this.icon.addClass('profile_pic_wrapper_16a');
			this.icon.adopt(
				new Element('img', {
					'class': 'profile_pic profile_pic_16_' + this.user.username, 
					'src': this.user['profile_pic_16']
					}),
				new Element('div', {'class': 'online_status online_status_' + this.user.username})
			
			);
		}
		
		if ($defined(this.iconAction))
		{
			this.addAction(this.iconAction);
		}
	},
	
	toElement: function(){
		return this.icon;
	},
	
	//adds a action element to icon (add, remove, delete, etc)
	addAction: function(action){
		this.iconAction = action;
		
		//destroy old action if exists
		if ($defined(this.action))	
			this.action.destroy();
		
		//add action
		this.action = new Element('div', {'class': 'icon_action ' + this.iconAction}).inject(this.icon);
	}
});var ItemLoader = new Class({
	
	initialize: function(options){
		
		this.items = $H(); //hash containing mapping of item_id => item element
		this.sortValues = $H(); //hash containing mapping of sortValue => item_id
		this.empty = true;
		
		this.highestId = 0;
		this.lowestId = 0;
		this.highestSortValue = 0;
		this.lowestSortValue = 0;
		
		this.idField = options.idField;
		this.sortField = options.sortField;
		this.sortAlpha = options.sortAlpha || false;
		this.sortAscending = $defined(options.sortAscending) ? options.sortAscending : true;
		this.createElementFunc = options.createElementFunc;
		this.emptyEl = options.emptyEl || null;
	
		this.createLoader();
	},
	
	remove: function(id){
		if (!this.items.has(id))
			return;
		
		DomUtility.fadeOutDestroy(this.items.get(id));
		var sortKey = this.sortValues.keyOf(id);
		this.sortValues.erase(sortKey);
		this.items.erase(id);
		
		//check if empty
		if (this.items.getLength() == 0)
		{
			this.empty = true;
			this.highestId = 0;
			this.lowestId = 0;
			this.highestSortValue = 0;
			this.lowestSortValue = 0;
			this.showEmpty();
		}
		
	},
	
	showEmpty: function(){
		if (!$defined(this.emptyEl))
			return;
		
		if (this.empty)	
			DomUtility.show(this.emptyEl);
		else
			DomUtility.hide(this.emptyEl);
		
	},
	
	process: function(data){
		
		//first make sure it's not already in here
		var id = data[this.idField];
		var sortValue = data[this.sortField];
		
		if (this.items.has(id))
		{
			Logger().log('item ' + id + ' exists, do not insert');
			return;
		}
		
		var position = this.checkBoundary(sortValue, id);
		
		if (!position)
		{
			position = this.findPosition(data);
		}
		
		if (typeof position == 'object')
			this.insertItem(data, 'before', position);
		else
			this.insertItem(data, position);
		
	},
	
	findPosition: function(data){
		var id = data[this.idField];
		var sortValue = data[this.sortField];
		
		if (this.empty)
		{
			this.highestId = id;
			this.lowestId = id;
			this.highestSortValue = sortValue;
			this.lowestSortValue = sortValue;
			this.empty = false;
			return 'top';
		}
		else
		{
			//loop thru all sortavlues
			var sortValues = this.getSortValues();
			for (var i = 0; i < sortValues.length; i++)
			{
				if (this.sortAlpha)
				{
					if (this.sortAscending)
					{
						if (sortValue.toString() < sortValues[i].toString())
							return this.items[this.sortValues[sortValues[i]]];
					}
					else
					{
						if (sortValue.toString() > sortValues[i].toString())
							return this.items[this.sortValues[sortValues[i]]];
					}
				}
				else
				{
					if (this.sortAscending)
					{
						if (parseInt(sortValue) < parseInt(sortValues[i]))
							return this.items[this.sortValues[sortValues[i]]];
					}
					else
					{
						if (parseInt(sortValue) > parseInt(sortValues[i]))
							return this.items[this.sortValues[sortValues[i]]];
					}
				}
			}
		}
	},
	
	getSortValues: function(){
		if (this.sortAlpha)
		{
			if (this.sortAscending)
				return this.sortValues.getKeys().sort();
			else
				return this.sortValues.getKeys().sort().reverse();
		}
		else
		{
			if (this.sortAscending)
				return this.sortValues.getKeys().sort(this.sortIntAsc);
			else
				return this.sortValues.getKeys().sort(this.sortIntDesc);
		}
	},
	
	sortIntAsc: function(a, b){
		return a - b;
	},
	
	sortIntDesc: function(a, b){
		return b - a;
	},
	
	//check if item belongs in top or bottom before having to iterate thru
	checkBoundary: function(sortValue, id){
		if (this.sortAlpha)
		{
			if (sortValue.toString() > this.highestSortValue.toString())
			{
				this.highestSortValue = sortValue.toString();
				this.highestId = id;
				return (this.sortAscending)? 'bottom': 'top';
			}
			else if (sortValue.toString() < this.lowestSortValue.toString())
			{
				this.lowestSortValue = sortValue.toString();
				this.lowestId = id;
				
				return (this.sortAscending)? 'top': 'bottom';
			}
			else
				return false;
			
		}
		else
		{
			if (parseInt(sortValue) > parseInt(this.highestSortValue))
			{
				this.highestSortValue = parseInt(sortValue);
				this.highestId = id;
				return (this.sortAscending)? 'bottom': 'top';
			}
			else if (parseInt(sortValue) < parseInt(this.lowestSortValue))
			{
				this.lowestSortValue = parseInt(sortValue);
				this.lowestId = id;
				return (this.sortAscending)? 'top': 'bottom';
			}
			else
				return false;
		}
	},
	
	
	insertItem: function(data, position, beforeEl){
		
		var el = this.createElementFunc(data);
		
		var sortValue = data[this.sortField];
		var id = data[this.idField];
		
		this.items.set(id, el);
		this.sortValues.set(sortValue, id);

		
		if (position == 'top')
		{
			el.inject(this.loader, 'top');
		}
		else if (position == 'before')
		{
			el.inject(beforeEl, 'before');
		}
		else
		{
			el.inject(this.loader, 'bottom');
		}
		
		this.empty = false;
		
		this.showEmpty();
	},
	
	createLoader: function(){
		this.loader = new Element('div', {});
		
		if ($defined(this.emptyEl))
			this.emptyEl.inject(this.loader);
	},
	
	toElement: function(){
		return this.loader;
	}
});var Menu = new Class({
	Extends: Base,
	
	init: function(options){
	
		if ($defined(this.onBeforeInit))
			options = this.onBeforeInit(options);
	
		this.displayName = options.displayName;
		this.className = $defined(options.className)? options.className: '';	
		this.isDefault = options.isDefault || false;
		this.firstShow = true;
		this.isOn = true;
		this.createMenu();
		
		if ($defined(this.onInit))
			this.onInit();
	},
	
	on: function(){
		DomUtility.show(this.menu);
		
		if ($defined(this.onShow))
		{
			if (this.firstShow)
			{
				this.onShow(true);
				this.firstShow = false;
			}
			else
				this.onShow(false);
		}
		
		this.isOn = true;
	},
	
	off: function(){
		if ($defined(this.onHide))
			this.onHide();
		
		DomUtility.hide(this.menu);
		this.isOn = false;
	},
	
	destroy: function(){
		if ($defined(this.onDestroy))
			this.onDestroy();
		
		this.menu.destroy();
	},
	
	addSection: function(section){
		section.inject(this.menu);
	},
	
	createMenu: function(){
		
		this.title = new Element('div', {'class': 'menu_text', 'text': TextUtility.unescapeQuotes(this.displayName)});
		
		this.menu = new Element('div', {'class': 'menu ' + this.className}).adopt(
			new Element('div', {'class': 'menu_title text12 light'}).adopt(
				this.title
			)
		);
		
	},
	
	toElement: function(){
		return this.menu;
	}
});var Nav = new Class({
	Extends: Base,
	
	EventHandlers: [
	                'contentSwitched',
	                'alertAdd',
	                'alertClear'
	],	
	
	init: function(options){
		//this is for subclasses that need to process options before main init
		if ($defined(this.onBeforeInit))
			options = this.onBeforeInit(options);
	
		//required parameters
		this.name = options.name;
		this.iconOptions = options.iconOptions;
		this.displayName = options.displayName;
		
		//the rest are optional
		//sets options for clipping class on right
		this.className = $defined(options.className)? options.className: '';
		
		this.parentName = options.parentName || undefined;
		
		if ($defined(this.parentName))
			this.className += ' sub1';
		
		//if bottom is true, this nav will be inserted in a static section that's always at the bottom of the navs
		//this is implemented in App class, not used anywhere else
		this.bottom = $defined(options.bottom) ? options.bottom: false;

		//whether this nav can contain children navs
		this.hasSubnavs = $defined(options.hasSubnavs)? options.hasSubnavs: false;
		
		//if it's closable
		this.closable = $defined(options.closable)? options.closable: false;
		this.onClose = $defined(options.onClose)? options.onClose: undefined;
		
		
		
		//if by default it's collapsed
		this.defaultClosed = $defined(options.defaultClosed)? options.defaultClosed: false;
		
		//nav actions
		this.onClick = $defined(options.onClick)? options.onClick: undefined;
		
		//default properties
		this.isExpanded = true;
		this.isOn = false;
		
		this.createNav();
		
		//again, for subclasses that need to process stuff after init
		if ($defined(this.onInit))
			this.onInit();
	},
	
	//alert event
	alertAdd: function(navId){
		if (!$defined(this.navId))
			return;
		
		if (navId != this.navId)	
			return;
		
		if (this.isOn)
			return;
		
		this.alert.increment();
		
		//if this nav has a parentId, fire alertAdd for parent 
		if ($defined(this.parentId))
			this.fireEvent('alertAdd', this.parentId);
	},
	
	alertClear: function(navId, count){
		if (!$defined(this.navId))
			return;
		
		if (navId != this.navId)	
			return;
		
		if (!$defined(count))
		{
			var alerts = this.alert.clear();
			//if this nav has a parentId, fire alertClear for parent 
			if ($defined(this.parentId))
				this.fireEvent('alertClear', this.parentId, alerts);
		}
		else
		{
			this.alert.decrement(count);
			//if this nav has a parentId, fire alertClear for parent 
			if ($defined(this.parentId))
				this.fireEvent('alertClear', this.parentId, count);
		}
	},
	
	contentSwitched: function(navId){
		if (!$defined(this.navId))
			return;
		
		if (navId == this.navId)
		{
			this.on();
			this.alertClear(this.navId);
		}
		else if (navId != this.navId && this.isOn)
			this.off();
	},
	
	on: function(){
		this.nav.addClass('on');
		this.isOn = true;
		//clear any alerts
	},
	
	off: function(){
		this.nav.removeClass('on');
		this.isOn = false;
	},
	
	click: function(){
		if ($defined(this.onClick))
			this.onClick();
	},
	
	close: function(e){
		e.stopPropagation();
		
		if ($defined(this.onClose))
			this.onClose();
	},
	
	toggleSubnav: function(e){
		e.stopPropagation();
		
		if (!this.isExpanded)
			this.expand();
		else
			this.collapse();
	},
	
	expand: function(){
		if (!this.isExpanded && this.hasSubnavs)
		{
			DomUtility.expand(this.subnavs);
			this.pivot.removeClass('pivot_right');
			this.pivot.addClass('pivot_down');
			this.nav.removeClass('collapsed');
			this.isExpanded = true;
			
			if ($defined(this.onExpand))
				this.onExpand();
		}	
	},
	
	collapse: function(){
		if (this.isExpanded && this.hasSubnavs)
		{
			DomUtility.collapse(this.subnavs);
			this.pivot.removeClass('pivot_down');
			this.pivot.addClass('pivot_right');
			this.nav.addClass('collapsed');
			this.isExpanded = false;
			
			if ($defined(this.onCollapse))
				this.onCollapse();
		}	
	},
	
	toElement: function(){
		return this.navWrapper;
	},
	
	addSubnav: function(nav){
		if (nav.bottom)
		{
			$(nav).inject(this.subnavBottomItems);
		}
		else
		{
			$(nav).inject(this.subnavItems);
		}
	},
	
	createNav: function(){
		this.navWrapper = new Element('div');
		
		this.nav = new Element('div', {'class': 'nav'}).inject(this.navWrapper);
		
		this.nav.addEvent('click', this.click.bind(this));
			
		if (this.hasSubnavs) //add the subnavs
		{
			this.subnavItems = new Element('div');
			this.subnavBottomItems = new Element('div');
			
			this.subnavs = new Element('div', {'class': 'sub_elements'}).adopt(
				this.subnavItems,
				this.subnavBottomItems
			).inject(this.navWrapper);
			
			this.nav.addClass('has_submenu');
		}
		
		//if it has extra options on right, add class
		this.nav.addClass(this.className);
		
		//basic nav elements
		this.pivot = new Element('div', {'class': 'action pivot_down'}).inject(this.nav);
		this.pivot.addEvent('click', this.toggleSubnav.bindWithEvent(this));
		
		this.icon = new Icon(this.iconOptions);
		$(this.icon).inject(this.nav);
		
		//alert
		this.alert = new Alert();
		$(this.alert).inject(this.nav);
		
		this.navName = new Element('div', {
			'class': 'nav_name text12 light', 
			'text': TextUtility.unescapeQuotes(this.displayName)
		}).inject(this.nav);
		
		if (this.closable)
		{
			this.closeButton = new Element('div', {'class': 'action close'}).inject(this.nav);
			this.closeButton.addEvent('click', this.close.bindWithEvent(this));
		}
		
		//if default closed, close it
		if (this.defaultClosed)
			this.collapse();
	},
	
	destroy: function(){
		if ($defined(this.navWrapper))
			this.navWrapper.destroy();
	}
	
});var ScrollBar = new Class({
	initialize: function(content, wrapper){
		this.content = $(content);
		this.wrapper = $(wrapper);
		
		this.disabled = false;
		this.createScrollbar();
		this.setupHandlers();
	},
	
	disable: function(){
		this.disabled = true;
	},
	
	enable: function(){
		this.disabled = false;		
	},
	
	createScrollbar: function(){
		this.scrollbar = new Element('div', {'class': 'scrollbar'});
		this.scrollbar.inject(this.wrapper);
	},
	
	dragOn: function(){
		this.wrapper.addClass('on');
	},
	
	dragOff: function(){
		this.wrapper.removeClass('on');
	},
	
	setupHandlers: function(){
		this.scrollHandleDrag = new Drag(this.scrollbar, {
			snap: 0,
			limit: {
				'x': [0,0],
				'y': [0, this.getHandleYMax.bind(this)]
			},
			onStart: this.dragOn.bind(this),
			onDrag: this.handleDragHandler.bind(this),
			onComplete: this.dragOff.bind(this),
			onCancel: this.dragOff.bind(this)
			
		});
		
		//scrolly
		this.contentScroller = new Fx.Scroll(this.content, {wheelStops: false});
		this.content.addEvent('mousewheel', this.scrollHandler.bindWithEvent(this));
		//if (this.global)
		//	$('weather').addEvent('mousewheel', this.scrollHandler.bindWithEvent(this));
		
		this.update.periodical(500, this, true);
	},
	
	update: function(){
		if (this.disabled)
			return;
			
		var scrollH = this.content.getScrollSize().y;
		var h = this.content.getSize().y;
		
		if (scrollH > h)
		{
			this.wrapper.addClass('scrolling');
			
			//set handle y position
			var contentScrollRatio = this.content.getScroll().y / this.getContentYMax();
			if (contentScrollRatio > 1) 
			{
				contentScrollRatio = 1;
				this.content.scrollTo(0, this.getContentYMax());
			}
			
			var handleY = this.getHandleYMax() * contentScrollRatio;
			this.scrollbar.setStyle('top', handleY);
			
		}
		else
		{
			this.wrapper.removeClass('scrolling');
		}
		
	},
	
	scrollHandler: function(e){
		if (this.disabled)
			return;
			
		e = new Event(e);
		e.stopPropagation();
		
		/* Mousewheel UP*/
		if (e.wheel > 0) {
	  		this.scrollUp(name);
		} 
		/* Mousewheel DOWN */
		else if (e.wheel < 0) {
			this.scrollDown(name);
		}
		
	},
	
	scrollUp: function(){
		var scrollY = this.content.getScroll().y;
		
		//if scroll is already at top, dont do anything
		if (scrollY == 0)
			return;
		
		scrollY -= 30;
		if (scrollY < 0)
			scrollY = 0;
		
		this.contentScroller.cancel();
		this.contentScroller.set(0, scrollY);
		this.update();
	},
	
	scrollDown: function(){
		var maxY = this.getContentYMax();
		
		if (maxY < 0)	
			maxY = 0;
		
		var scrollY = this.content.getScroll().y;
		
		//if scroll is already at bottom, dont do anything
		if (scrollY >= maxY)
			return;
		
		scrollY += 30;
		if (scrollY > maxY)
			scrollY = maxY;
			
		this.contentScroller.cancel();
		this.contentScroller.set(0, scrollY);
		this.update();
	},
	
	handleDragHandler: function(){
		
		var handleY = this.scrollbar.getPosition(this.wrapper).y;
		var handleYMax = this.getHandleYMax();
		var contentY = this.getContentYMax() * (handleY / handleYMax);
		this.content.scrollTo(0, contentY);
	},
	
	getHandleYMax: function(){
		return this.wrapper.getSize().y - 102; //handle size = 100 + 2px border
	},
	
	getContentYMax: function(){
		return this.content.getScrollSize().y - this.content.getSize().y;
	}
});var StreamLoader = new Class({
	
	initialize: function(options){
		
		this.items = $H();
		this.timestamps = $H();
		
		this.newestTimestamp = 0;
		this.oldestTimestamp = 0;
		this.newestId = 0;
		this.oldestId = 0;
		this.newestTimestampEl = null;
		this.empty = true;
		this.createElementFunc = options.createElementFunc;
		this.alertFunc = options.alertFunc;
		this.emptyEl = options.emptyEl || null;
		this.errorEl = options.errorEl || null;
		
		//this.sourceId = options.sourceId;
		this.idField = options.idField || 'item_id';
		this.dateField = options.dateField || 'date_created';
	
		this.createLoader();
	},
	
	remove: function(id){
		if (!this.items.has(id))
			return;
		
		var el = this.items.get(id);
		//check if item before/afer are dates, if both are, delete the one before
		var prev = el.getPrevious();
		var next = el.getNext();
		
		if (prev.hasClass('seperator') && next.hasClass('seperator'))
			prev.destroy();
			
		DomUtility.fadeOutDestroy(el);
		this.items.erase(id);
		
		//check if empty
		if (this.items.getLength() == 0)
		{
			this.empty = true;
			this.showEmpty();
		}
		
	},
	
	process: function(data){
		
		var position = this.findPosition(data);
		
		if (!position)
			return;
		
		this.insertItem(data, position);
		
	},
	
	showEmpty: function(){
		if (!$defined(this.emptyEl))
			return;
		
		if (this.empty)	
			DomUtility.show(this.emptyEl);
		else
			DomUtility.hide(this.emptyEl);
		
	},
	
	showError: function(){
		if (!$defined(this.errorEl))
			return;
		
		if ($defined(this.emptyEl))
			DomUtility.hide(this.emptyEl);
		
		DomUtility.show(this.errorEl);
	},
	
	insertTimestamp: function(timestamp, position){
		var el = StreamItemUtility.createDateSeperatorItem(timestamp);
		el.inject(this.loader, position);
		
		if (position == 'top')
			this.newestTimestampEl = el;
	},
	
	findPosition: function(data){
		var id = data[this.idField];
		var timestamp = data[this.dateField];
		
		if (this.empty)
		{
			this.insertTimestamp(timestamp, 'top');
			this.oldestTimestamp = timestamp;
			this.newestTimestamp = timestamp;
			this.newestId = id;
			this.oldestId = id;
			this.empty = false;
			return 'top';
		}
		else if (timestamp >= this.newestTimestamp && id > this.newestId)
		{
			if (!this.checkTimestampSameDay(this.newestTimestamp, timestamp))
				this.insertTimestamp(timestamp, 'top');
			
			this.newestTimestamp = timestamp;
			this.newestId = id;
			return 'top';
		}
		else if (timestamp <= this.oldestTimestamp && id < this.oldestId)
		{
			if (!this.checkTimestampSameDay(this.oldestTimestamp, timestamp))
				this.insertTimestamp(timestamp, 'bottom');
			
			this.oldestTimestamp = timestamp;
			this.oldestId = id;
			return 'bottom';
		}
		else
			return false;
	},
	
	checkTimestampSameDay: function(ts1, ts2){
		var d1 = DateUtility.convertFromTimestamp(ts1);
		var d2 = DateUtility.convertFromTimestamp(ts2);
		
		return (d1.getDate() == d2.getDate() && d1.getMonth() == d2.getMonth());
	},
	
	insertItem: function(data, position){
		var el = this.createElementFunc(data);
		
		var id = data[this.idField];
		var date = data[this.dateField];
		
		this.items.set(id, el);
		this.timestamps.set(id, date);

		
		
		if (position == 'top')
		{
			el.inject(this.newestTimestampEl, 'after');
			DomUtility.fadeIn(el, 1000);
		}
		else
		{
			el.inject(this.loader, 'bottom');
		}
		
		this.empty = false;
		this.showEmpty();
		
		//check for alert now
		if ($defined(this.alertFunc) && pipio.checkTimestamp(date))
			this.alertFunc(data);
	},
	
	createLoader: function(){
		this.loader = new Element('div', {});
		
		if ($defined(this.emptyEl))
			this.emptyEl.inject(this.loader);
		
		if ($defined(this.errorEl))
		{
			this.errorEl.inject(this.loader);
			DomUtility.hide(this.errorEl);
		}
	},
	
	toElement: function(){
		return this.loader;
	}
});var Toggle = new Class({
	initialize: function(on){
	
		this.isOn = $defined(on)? on: false;
		
		this.toggle = new Element('div', {'class': 'toggle'}).adopt(
			new Element('div', {'class': 'handle'}),
			new Element('div', {'class': 'toggle_text on', 'text': 'on'}),
			new Element('div', {'class': 'toggle_text off', 'text': 'off'})
		);
		
		this.toggle.addEvent('click', this.switchToggle.bind(this));
		
		this.setOn();
	},
	
	switchToggle: function(){
		this.isOn = !this.isOn;
		this.setOn();
	},
	
	on: function(){
		this.isOn = true;
		this.setOn();
	},
	
	off: function(){
		this.isOn = false;
		this.setOn();
	},
	
	setOn: function(){
		if (this.isOn)
		{
			this.toggle.addClass('on');
			this.toggle.removeClass('off');
		}
		else
		{
			this.toggle.removeClass('on');
			this.toggle.addClass('off');
		}
	},
	
	toInt: function(){
		return (this.isOn)? 1: 0;
	},
	
	toElement: function(){
		return this.toggle;
	}
	
});var Popup = new Class({
	Extends: Base,
	
	initialize: function(options){
		this.parent();
		
		var options = $defined(options)? options : {};
		
		this.navs = $H();
		this.contents = $H();
		//this.p.modules.modules.ui.popupWindows.set(name, this);
		
		//options
		this.contentSize = $defined(options.size) ? options.size : {x: 400, y: 300};
		this.resizable = $defined(options.resizable) ? options.resizable : true;
		this.dockable = $defined(options.dockable) ? options.dockable : true;
		this.closable = $defined(options.closable) ? options.closable : true;
		this.className = $defined(options.className) ? options.className : undefined;
		this.noHide = $defined(options.noHide) ? options.noHide : false;
		//this.top_tabs = $defined(options.top_tabs) ? options.top_tabs : false;
		
		//extra funcs
		this.onClose = $defined(options.onClose) ? options.onClose : undefined;
		
		this.hasTabs = false;
		this.isDocked = false;
		
		this.createPopup();
	},
	
	toElement: function(){
		return this.popup;
	},
	
	addContent: function(contentName, nav, content){
		if (this.contents.has(contentName))
			return;
		
		//inject elements
		$(nav).inject(this.nav);
		$(content).inject(this.content);
		
		this.navs.set(contentName, nav);
		this.contents.set(contentName, content);
		
		//add click event
		nav.onClick = this.switchContent.bind(this, [contentName]);
		
		//add resize func
		content.resizePopup = this.resizePopup.bind(this);
		
		//if nav is closable, attach close event
		if (nav.closable)
		{
			nav.onClose = this.closeContent.bind(this, [contentName]);
			content.onClose = this.closeContent.bind(this, [contentName]);
		}
		this.checkTabs();
		
		this.switchContent(contentName);
	},
	
	closeContent: function(contentName){
		if (!this.contents.has(contentName))
			return;
		
		this.navs.get(contentName).destroy();
		this.contents.get(contentName).off();
		this.contents.get(contentName).close();
		this.navs.erase(contentName);
		this.contents.erase(contentName);
		
		//if this was the last content, close popup
		if (this.contents.getLength() == 0)
		{
			this.close();
			return;
		}
		//if the current one is closed, switch to closest
		if (contentName == this.currentContent)
		{
			this.currentContent = null;
			var last = this.contents.getKeys().pop();
			this.switchContent(last);
		}
		
		this.checkTabs();
		this.setTitle();
	},
	
	switchContent: function(contentName){
		if (!this.contents.has(contentName))
			return;
		
		//if docked, undock first
		if (this.isDocked)
			this.undock();
		
		if (contentName == this.currentContent)
			return;
		
		if ($defined(this.currentContent))
		{
			this.navs.get(this.currentContent).off();
			this.contents.get(this.currentContent).off();
		}
		
		this.navs.get(contentName).on();
		this.contents.get(contentName).on();
		this.currentContent = contentName;
		//set the title
		this.setTitle();
	},
	
	setTitle: function(){
		var nav = this.navs.get(this.currentContent);
		var icon = new Icon20(nav.iconOptions);
		var titleText = new Element('div', {
			'class': 'title_text',
			'text': TextUtility.unescapeQuotes(nav.displayName)
		});
		
		this.titleIcon = $(icon).replaces(this.titleIcon);
		this.titleText = titleText.replaces(this.titleText);
		
	},
	
	getDockIcon: function(){
		var nav = this.navs.get(this.currentContent);
		var icon = new Icon20(nav.iconOptions);
		
		var el = new Element('div', {'class': 'app_button has_submenu'}).adopt(
			$(icon)
		);
		
		el.addEvent('click', this.undock.bind(this));
		
		return el;
	},
	
	//run this everytime a content is added or closed to make sure tabs should be on or off
	checkTabs: function(){
		if (this.contents.getLength() > 1)
		{
			this.popup.addClass('tabbed');
			this._checkBounds();
			this.hasTabs = true;
		}
		else
		{
			this.popup.removeClass('tabbed');
			this.hasTabs = false;
		}
	},
	
	resizePopup: function(x, y){
		var size = {x: x, y: y + 24};
		this.popup.setStyle('height', size.y + 'px');
		this.popup.setStyle('width', size.x + 'px');
		
		this._checkBounds();
	},
	
	reCenter: function(){
		var size = {x: this.popup.getSize().x, y: this.popup.getSize().y};
		
		var pos = {
			x: window.getSize().x/2 - (size.x/2),
			y: window.getSize().y/2 - (size.y/2)
		};
		
		//make sure y isn't negative
		if (pos.y < 0)
			pos.y = 0;
		
		this.popup.setStyles({
			'top': pos.y + 'px',
			'left': pos.x + 'px'
		});
	},
	
	createPopup: function(){
		//calculate true size, take in account for sides & header
		this.size = {x: this.contentSize.x, y: this.contentSize.y + 24};
		
		var pos = {
			x: window.getSize().x/2 - (this.size.x/2),
			y: window.getSize().y/2 - (this.size.y/2)
		};
		
		//make sure y isn't negative
		if (pos.y < 0)
			pos.y = 0;
		
		//default size & pos
		this.popup = new Element('div', {
			'class': 'popup',
			'styles': {
				'height': this.size.y + 'px',
				'width': this.size.x + 'px',
				'top': pos.y + 'px',
				'left': pos.x + 'px'
			}
		});
		
		//add className if defined
		if ($defined(this.className))
			this.popup.addClass(this.className);
		
		//header element
		this.title = new Element('div', {'class': 'popup_title text12 light'}).inject(this.popup);
		this.nav = new Element('div', {'class': 'app_sub_menu popup_tabs'}).inject(this.popup);
		this.content = new Element('div', {'class': 'popup_content'}).inject(this.popup);

		//mouse drag border
		this.dragBorder = new Element('div', {'class': 'popup_drag', 'text': 'Drag this window to move it'}).inject(this.popup);
		//content wrapper
		
		//inject this popup element into the div popups
		//this must happen before events are attached
		this.popup.inject('popups');
		DomUtility.fadeIn(this.popup);
		
		//title stuff
		this.titleIcon = new Element('div').inject(this.title);
		this.titleText = new Element('div').inject(this.title);
		
		//buttons
		if (this.closable)
		{
			this.closeButton = new Element('div', {'class': 'button_nav right'}).adopt(
				new Element('div', {'class': 'action close'})
			).inject(this.title);
			this.closeButton.addEvent('click', this.close.bind(this));
		}
		
		if (this.dockable)
		{
			this.dockButton = new Element('div', {'class': 'button_nav right'}).adopt(
				new Element('div', {'class': 'action dock'})
			).inject(this.title);
			this.dockButton.addEvent('click', this.dock.bind(this));
		}

		//attach events
		this.drag = this.popup.makeDraggable({
			handle: this.title,
			onStart: this._startDrag.bind(this),
			onComplete: this._stopDrag.bind(this),
			onCancel: this._stopDrag.bind(this)
		});
		
		if (this.resizable)
		{
			this.resizeHandle = new Element('div', {'class': 'resize'}).inject(this.popup);
			this.popup.makeResizable({
				handle: this.resizeHandle,
				limit: {x: [400, 800], y: [300, 800]}
			});
		}
		
		this.popup.addEvent('mousedown', this._select.bind(this));
		//this.tabs.addEvent('click', this._select.bind(this));
		
		this._select();
		
		this._checkBounds();
		
		if ($defined(this.onCreate))
			this.onCreate();
	},
	
	close: function(){
		
		this.contents.each(function(content){
			content.close();
		});
		
		this.popup.destroy();
		
		if ($defined(this.onClose))
			this.onClose();
		
		delete(this);
		
	},
	
	dock: function(){
		//switch off current content
		this.navs.get(this.currentContent).off();
		this.contents.get(this.currentContent).off();
		
		this.dockButton = new Element('div', {'class': 'app_button_wrapper'}).adopt(
			this.getDockIcon(),
			this.nav
		);
		
		this.dockButton.inject('dock');
		//DomUtility.fadeIn(this.dockButton);
		
		DomUtility.fadeOut(this.popup);
			
		this.isDocked = true;
		
	},
	
	undock: function(){
		
		//move nav back
		this.nav.inject(this.popup);
		
		this.dockButton.destroy();
		//DomUtility.fadeOutDestroy(this.dockButton);
		
		DomUtility.fadeIn(this.popup);
		
		this.navs.get(this.currentContent).on();
		this.contents.get(this.currentContent).on();
		
		this.isDocked = false;
	},
	
	//selects window, brings it to top z-index
	_select: function(){
		this.popup.setStyle('z-index', DomUtility.getZ('popup'));
		
	//	this.unalert();
	},
	
	_startDrag: function(){
		if (this.noHide)
			return;
		
		DomUtility.hide(this.nav);
		DomUtility.show(this.dragBorder);
		this.contents.get(this.currentContent).off();
	},
	
	_stopDrag: function(){
		if (!this.noHide)
		{
		
			DomUtility.hide(this.dragBorder);
			DomUtility.show(this.nav);
			this.nav.set('style', '');
			this.contents.get(this.currentContent).on();
		
		}
		this._checkBounds();
	},
	
	//makes sure window is within boundries of screen, if not, reposition
	_checkBounds: function(){
		var pos = this.popup.getPosition();
		
		//calc min/max
		//make sure popup is within window bounds
		if (pos.x + this.popup.getSize().x > window.getSize().x)
			this.popup.setStyle('left', window.getSize().x - this.popup.getSize().x);
		
		if (pos.y + this.popup.getSize().y > window.getSize().y)
			this.popup.setStyle('top', window.getSize().y - this.popup.getSize().y);
		
		//get new position and check again
		pos = this.popup.getPosition();
		
		if (pos.x < 0)
			this.popup.setStyle('left', 0);
		
		if (pos.y < 0)
			this.popup.setStyle('top', 0);
	}
	
});//base class for popup content, meant for extending
var PopupContent = new Class({
	Extends: Base,
	
	init: function(options){
		
		if ($defined(this.onBeforeInit))
			options = this.onBeforeInit(options);
	
		this.isOn = false;
		
		this.destroy = $defined(options.destroy)? options.destroy: true;
	
		this.createContent();
		
		//again, for subclasses that need to process stuff after init
		if ($defined(this.onInit))
			this.onInit();
	},
	
	on: function(){
		
		Logger().log('turning on content');
		DomUtility.show(this.content);
		
		if ($defined(this.onShow))
			this.onShow();
		
		this.isOn = true;
	},
	
	off: function(){
		if ($defined(this.onHide))
			this.onHide();
		
		DomUtility.hide(this.content);
		this.isOn = false;
	},
	
	close: function(destroy){
		if (this.destroy || destroy)
			this.content.destroy();
		else
			this.content.dispose();
	},
	
	closeContent: function(){
		if ($defined(this.onClose))
			this.onClose();
	},
	
	createContent: function(){
		this.content = new Element('div', {'class': this.className});
		this.off();
	},
	
	toElement: function(){
		return this.content;
	}
	
});var AlertPopupContent = new Class({
	Extends: PopupContent,
	
	onBeforeInit: function(options){
		
		this.alertMessage = options.alertMessage;
		
		return options;
	},
	
	onInit: function(){
	
		new Element('div', {'class': 'text_section centered light', 'text': TextUtility.unescape(this.alertMessage)}).inject(this.content);
		
		//ACtIONS
		
	
		this.actionButton = new ButtonMedium({
			displayName: 'Close',
			className: 'dark',
			action: 'check'
		});
		$(this.actionButton).addEvent('click', this.closeContent.bind(this));
		
		this.actions = new Element('div', {'class': 'actions'}).adopt(
			$(this.actionButton)
		).inject(this.content);
		
	}
	
});var ConfirmPopupContent = new Class({
	Extends: PopupContent,
	
	onBeforeInit: function(options){
		
		this.confirmMessage = options.confirmMessage;
		this.confirmFunc = options.confirmFunc;
		
		return options;
	},
	
	onInit: function(){
	
		new Element('div', {'class': 'text_section centered light', 'text': TextUtility.unescape(this.confirmMessage)}).inject(this.content);
		
		//ACtIONS
		
		this.closeButton = new ButtonMedium({
			displayName: 'Cancel',
			className: 'dark',
			action: 'cross'
		});
		$(this.closeButton).addEvent('click', this.closeContent.bind(this));
		
		this.actionButton = new ButtonMedium({
			displayName: 'Confirm',
			className: 'dark',
			action: 'check'
		});
		$(this.actionButton).addEvent('click', this.confirmFunc);
		$(this.actionButton).addEvent('click', this.progressClose.bind(this));
		
		this.actions = new Element('div', {'class': 'actions'}).adopt(
			$(this.actionButton),
			$(this.closeButton)
		).inject(this.content);
		
	},
	
	progressClose: function(){
		this.actionButton.showProgress();
		this.closeContent.delay(1500, this);
	}
	
});var RoomStatusBox = new Class({
	Extends: Base,
	
	EventHandlers: [
	                'roomMembershipUpdated',
	                'roomStatusUpdated',
	                'roomStatusCleared'
	              //  'roomAdded',
	        		//'roomDeleted'
	                ],
	
	init: function(room){
		this.room = room;
		
		this.defaultStatus = 'has no current status';
		
		this.canPost = (this.room.status == 1);
		
		this.createStatusBox();
	},
	
	
	createStatusBox: function(){
		this.box = ItemUtility.createPostBubble('post status_message haspic');
		
		ItemUtility.createProfilePic(this.room, 32).inject(this.box);
		
		
		var buttonText = 'Write in ' + this.room.room_name;
		this.writeButton = new Element('div', {'class': 'button', 'text': TextUtility.unescape(buttonText)}).inject(this.box);
		this.writeButton.addEvent('click', this.fireEvent.bind(this, ['roomShareBoxShow', this.room]));
		
		this.setPostButton();
		
		//content element
		this.content = new Element('div', {'class': 'post_content'}).inject(this.box);
		
		var header = new Element('div', {'class': 'header'}).inject(this.content);
		
		//creator action
		StreamItemUtility.createStreamUserAction('status dark').inject(header);
		//creator name
		var creator = new Element('span', {'class': 'user_name clickable', 'text': TextUtility.unescapeQuotes(this.room.room_name)}).inject(header);
		//creator.addEvent('click', this.fireEvent.bind(this, ['showUser', this.user]));
		
		this.statusText = new Element('span', {'class': 'status'}).inject(header);
		
		
		this.editButton = new Element('span', {'class': 'edit_link', 'text': 'update'});
		this.editButton.addEvent('click', this.fireEvent.bind(this, ['showRoomStatusUpdatePopup', this.room]));
		
		this.clearButton = new Element('span', {'class': 'edit_link', 'text': 'clear'});
		this.clearButton.addEvent('click', this.clearStatus.bind(this));
		
		this.editSection = new Element('span').adopt(
			$(this.editButton),
			$(this.clearButton)
		).inject(header);
		
		this.setEditButton();
		
		//footer
		this.footer = StreamItemUtility.createStatusBoxFooter(this.room.status_message).inject(this.content);
		
		this.updateStatus();
	},
	
	roomMembershipUpdated: function(data){
		if (!$defined(data.username) || data.username != this.room.username)
			return;
		
		this.room.status = data.status;
		this.canPost = (this.room.status == 1);
		this.setPostButton();
		
	},
	
	setEditButton: function(){
		if (this.canPost)
			DomUtility.show(this.editSection, 'inline');
		else
			DomUtility.hide(this.editSection);
	},
	
	setPostButton: function(){
		
		if (this.canPost)
		{
			DomUtility.show(this.writeButton);
		}
		else
		{
			DomUtility.hide(this.writeButton);
		}
	},

	updateStatus: function(){
		
		this.isEmpty = this.room.status_message.status.trim() == '';
		
		if (this.isEmpty)
		{
			this.statusText.addClass('empty');
			
			this.statusText.set('text', ' ' + TextUtility.unescape(this.defaultStatus));
			
			DomUtility.hide(this.clearButton);
		}
		else
		{
			this.statusText.removeClass('empty');
			
			this.statusText.set('text', ' ' + TextUtility.unescape(this.room.status_message.status.trim()));
			
			DomUtility.show(this.clearButton, 'inline');
		}
		
		//update Timestamp
		this.footer.destroy();
		this.footer = StreamItemUtility.createStatusBoxFooter(this.room.status_message).inject(this.content);
	},
	
	clearStatus: function(){
		
		var params = {username: this.room.username, body: '', res: this.getSession()};
		
		this.call('pipio', 'publish_roomstatus', params);
	},
	
	roomStatusUpdated: function(data){
		
		if (!$defined(data.username) || data.username != this.room.username)	
			return;
		
		this.room.status_message = data.status_message;
		
		this.updateStatus();
	},
	
	roomStatusCleared: function(data){
		
		if (!$defined(data.username) || data.username != this.room.username)	
			return;
		
		this.room.status_message.status = '';
		this.room.status_message.creator = undefined;
		this.room.status_message.date_created = undefined;
		this.room.status_message.location = undefined;
		
		this.updateStatus();
	},
	
	toElement: function(){
		return this.box;
	}
	
	
});var StatusBox = new Class({
	Extends: Base,
	
	EventHandlers: [
	                'userStatusUpdated',
	                'userStatusCleared',
	                'contactAdded',
	        		'contactDeleted'
	                ],
	
	init: function(user){
		this.user = user;
		
		this.isSelf = this.user.user_id == this.getPrivateUser().user_id;
		this.defaultStatus = 'has no current status';
		
		this.canPost = $defined(this.getContact(this.user.username)) || this.isSelf;
		
		this.createStatusBox();
	},
	
	
	createStatusBox: function(){
		this.box = ItemUtility.createPostBubble('post status_message haspic');
		
		ItemUtility.createProfilePic(this.user, 32).inject(this.box);
		
		
		var buttonText = this.isSelf? 'Write in Your Stream' : 'Write in ' + this.user.first_name + '\'s Stream';
		this.writeButton = new Element('div', {'class': 'button', 'text': TextUtility.unescape(buttonText)}).inject(this.box);
		this.writeButton.addEvent('click', this.fireEvent.bind(this, ['shareBoxShow', this.user]));
		
		this.setPostButton();
		
		//content element
		this.content = new Element('div', {'class': 'post_content'}).inject(this.box);
		
		var header = new Element('div', {'class': 'header'}).inject(this.content);
		
		//creator action
		StreamItemUtility.createStreamUserAction('status dark').inject(header);
		//creator name
		var creator = new Element('span', {'class': 'user_name clickable', 'text': TextUtility.unescapeQuotes(this.user.fullname)}).inject(header);
		creator.addEvent('click', this.fireEvent.bind(this, ['showUser', this.user]));
		
		this.status = new Element('span', {'class': 'status'}).inject(header);
		
		if (this.isSelf)
		{
			this.editButton = new Element('span', {'class': 'edit_link', 'text': 'update'}).inject(header);
			this.editButton.addEvent('click', this.fireEvent.bind(this, ['showStatusUpdatePopup']));
			
			this.clearButton = new Element('span', {'class': 'edit_link', 'text': 'clear'}).inject(header);
			this.clearButton.addEvent('click', this.clearStatus.bind(this));
		}
		
		//footer
		this.footer = StreamItemUtility.createStatusBoxFooter(this.user.status_message).inject(this.content);
		
		this.updateStatus();
	},
	
	contactAdded: function(user){
		if (this.user.username != user.username)
			return;
		
		this.canPost = true;
		this.setPostButton();
	},
	
	contactDeleted: function(username){
		if (this.user.username != username)
			return;
		
		this.canPost = false;
		this.setPostButton();
	},
	
	setPostButton: function(){
		
		if (this.canPost)
		{
			DomUtility.show(this.writeButton);
		}
		else
		{
			DomUtility.hide(this.writeButton);
		}
	},
	
	updateStatus: function(){
		
		this.isEmpty = this.user.status_message.status.trim() == '';
		
		if (this.isEmpty)
		{
			this.status.addClass('empty');
			
			this.status.set('text', ' ' + TextUtility.unescape(this.defaultStatus));
			
			if (this.isSelf)
				DomUtility.hide(this.clearButton);
		}
		else
		{
			this.status.removeClass('empty');
			
			this.status.set('text', ' ' + TextUtility.unescape(this.user.status_message.status.trim()));
			
			if (this.isSelf)
				DomUtility.show(this.clearButton, 'inline');
		}
		
		//update Timestamp
		this.footer.destroy();
		this.footer = StreamItemUtility.createStatusBoxFooter(this.user.status_message).inject(this.content);
	},
	
	clearStatus: function(){
		
		var params = {body: '', res: this.getSession()};
		
		this.call('pipio', 'publish_status', params);
	},
	
	userStatusUpdated: function(data){
		
		if (!$defined(data.username) || data.username != this.user.username)	
			return;
		
		this.user.status_message = data.status_message;
		
		this.updateStatus();
	},
	
	userStatusCleared: function(data){
		
		if (!$defined(data.username) || data.username != this.user.username)	
			return;
		
		this.user.status_message.status = '';
		this.user.status_message.date_created = undefined;
		this.user.status_message.location = undefined;
		
		this.updateStatus();
	},
	
	toElement: function(){
		return this.box;
	}
	
	
});var AttachmentUtility = {
		
	parseLinkAttachment: function(attachment){
		if ($defined(attachment.processed))
			return attachment;
		
		attachment.processed = true;
		attachment.is_video = false;
		attachment.is_photo = false;
		
		if (attachment.url.contains('youtube.com'))
		{
			var youtubeId = attachment.url.replace(/^[^v]+v.(.{11}).*/,"$1"); 
			if (youtubeId != '')
			{
				attachment.is_video = true;
				attachment.video_type = 'youtube';
				attachment.youtubeId = youtubeId;
				return attachment;
			}
		}
		else if (attachment.url.contains('collegehumor.com/video:'))
		{
			var vimeoId = attachment.url.split('collegehumor.com/video:')[1];
			if (vimeoId != '')
			{
				attachment.is_video = true;
				attachment.video_type = 'ch';
				attachment.vimeoId = vimeoId;
				return attachment;
			}
		}
		else if  (attachment.url.contains('vimeo.com/'))
		{
			var vimeoId = attachment.url.split('vimeo.com/')[1];
			if (vimeoId != '')
			{
				attachment.is_video = true;
				attachment.video_type = 'vimeo';
				attachment.vimeoId = vimeoId;
				return attachment;
			
			}
		}
		else if (attachment.url.contains('hulu.com')) 
		{
			attachment.is_video = true;
			attachment.video_type = 'hulu';
			return attachment;
			
		}
		else if (attachment.url.contains('break.com')) 
		{
			attachment.is_video = true;
			attachment.video_type = 'break';
			return attachment;
		}
		else if (DataUtility.validatePhotoFile(attachment.url))
		{
			attachment.is_photo = true;
			return attachment;
		}
		
		return attachment;
		
	},
	
	parsePhotoAttachment: function(attachment){
		if ($defined(attachment.processed))
			return attachment;
	
		attachment.processed = true;
		
		attachment.is_photo = true;
		
		return attachment;
	}
		
};var DataUtility = {
	
	photoExtensions : ['.jpeg', '.jpg', '.gif', '.png', '.bmp'],
		
	validateUrl: function(url){
		var v = new RegExp(); 
		v.compile("^([A-Za-z]+:\/\/[A-Za-z0-9-_]+\.[A-Za-z0-9-_%&\?\/.=,+~!:]+(#[A-Za-z0-9-_%&\?\/.=,+~!:]+)?)$"); 
		return v.test(url);
	},
	
	validatePhotoFile: function(filename){
		//hack to check for .jpeg!  
		//TODO: change this to a regex
		if (filename.substring(filename.length-5).toLowerCase() == '.jpeg')
			return true;
			
		var ext = filename.substring(filename.length-4).toLowerCase();
		return DataUtility.photoExtensions.contains(ext);
	},
	
	getGeoString: function(location){
		if (!$defined(location))
			return false;
		
		var locationString = false;
		
		var locality = DataUtility.getGeoLocality(location);
		if (locality)
			locationString = locality;
		
		var label = DataUtility.getGeoLabel(location);
		if (label)
			locationString = label + ', ' + locality;
		
		return locationString;
		
	},
	
	getGeoLabel: function(location){
		if ($defined(location.label) && location.label != '')
			return location.label;
		else
			return false;
	},
	
	getGeoLocality: function(location){
		var local = '';
		
		if (location.country_code == 'US')
		{
			if ($defined(location.region) && $defined(location.city))
				local = location.city + ', ' + location.region;
			else
				return false;
		}
		else
		{
			if ($defined(location.city))
				local = location.city + ', ' + location.country;
			else
				return false;
		}
		
		return local;
	},
	
	getPipioUrl: function(url, hash){
		if ($defined(hash))
		{
			return "/" + hash;
		}
		else
		{
			return url;
		}
	},
	
	getFacebookPhotoUrl: function(url){
		return url.replace('_s.jpg', '_n.jpg');
	},
	
	sortUsers: function(a, b){
		return a.last_name.toLowerCase() < b.last_name.toLowerCase();
	},
	
	getCookie: function(c_name){
		if (document.cookie.length>0)
		{
			c_start=document.cookie.indexOf(c_name + "=");
			if (c_start!=-1)
			{
				c_start=c_start + c_name.length+1;
				c_end=document.cookie.indexOf(";",c_start);
				if (c_end==-1) c_end=document.cookie.length;
				return unescape(document.cookie.substring(c_start,c_end));
			}
		}
		return undefined;
	},
	
	getFileUrl: function(hash, file_id, filename){
		
		var serverId = file_id%16;
		
		return 'http://files' + serverId.toString(16) + '.pip.io/' + hash.substring(0,2) + '/' + hash.substring(2,3) + '/' + hash.substring(3,5) + '/' + hash.substring(5,8) + '/' + file_id + '/' + filename;
	}
	
	
};var DateUtility = {
	
	convertFromGMT: function(date)
	{
		var offsetMils = date.getTimezoneOffset() * 60 * 1000;
		date.setTime(date.getTime() - offsetMils);
		return date;
	},	
	
	getTimestamp: function(ts, format){
		if (!$defined(format))
			format = '%B %D, %I:%M%p';
		
		if (new Date().getTime() - ts.getTime() > 86400000) //one day
		{ //more than a day, use standard time
			return ts.format(format);
		}
		else
		{
			return ts.timeAgoInWords();
		}
		
	},
	
	convertFromTimestamp: function(ts){
		var date = new Date();
		date.setTime(ts * 1000);
		return date;
	}
	
};var DomUtility = {
	
	//toggles display of elements
	toggle: function(id){
		if ($(id).getStyle('display') == 'none')
			DomUtility.show(id);
		else
			DomUtility.hide(id);
	},
	
	hide: function(id){
		if ($(id) != null)
			$(id).setStyle('display', 'none');
	},
	
	show: function(id, display){
		if (!$defined(display))
			display = 'block';
		
		if ($(id) != null)
			$(id).setStyle('display', display);
	},
	
	//toggle sliding element open and close
	expand: function(id){
		var el = $(id);
		
		
		el.get('tween', {duration: 150}).start('height', el.getScrollSize().y).chain(function(){
			el.setStyle('height', 'auto');
		});
	},
	
	collapse: function(id){
		var el = $(id);
		
		el.setStyle('height', el.getSize().y);
		el.get('tween', {duration: 150}).start('height', 0);
	},
	
	setHeight: function(id, h)
	{
		var el = $(id);
		el.get('tween', {duration: 150}).start('height', h);
	},
	
	//toggle expand and fade in
	expandFade: function(id, h){
		var el = $(id);
		
		el.setStyle('opacity', 0);
		
		el.get('tween', {duration: 150}).start('height', h).chain(function(){
			el.get('tween', {duration: 800}).start('opacity', 1);
		});
	},
	
	collapseFade: function(id){
		var el = $(id);
		
		el.get('tween', {duration: 800}).start('opacity', 0).chain(function(){
			el.get('tween', {duration: 150}).start('height', 0);
		});
	},
	
	//toggle fade
	fadeOut: function(id, destroy){
		var el = $(id);
		el.setStyle('opacity', 1);
		el.get('tween', {duration: 500}).start('opacity', 0).chain(function(){
			if (destroy)
				el.destroy();
		});
	},
	
	fadeOutDestroy: function(id){
		DomUtility.fadeOut(id, true);
	},
	
	fadeIn: function(id, duration){
		var el = $(id);
		duration = duration || 500;
		el.setStyle('opacity', 0);
		el.get('tween', {duration: duration}).start('opacity', 1);
	},
	
	//fetches the next highest z-index # for a specific set of items
	getZ: function(name){
		var zName = name + 'Z';
		
		if (!$defined(window[zName]))
			window[zName] = 200;
			
		window[zName]++;
		
		return window[zName];	
	},
	
	//makes textarea taller when neccesary
	textareaAutoSize: function(el, min)
	{
		min = $defined(min) ? min : 16;
		el = $(el);
		
		if (el.value.trim().length < 30 && el.getSize().y > min) //if short, make default min height
		{
			el.setStyle('height', min);
			return true;
		}
		
		if (el.getScrollSize().y > min && el.getScrollSize().y > el.getSize().y)
		{
			el.setStyle('height', el.getScrollSize().y);
			return true;
		}
		
		return false;
	}
};var ItemUtility = {
	createProfilePic: function(user, size){
		var picSize = (size == '16a') ? 16: size;
		
		if ($defined(user.fullname))
		{
			var el = new Element('div', {'class': 'profile_pic_wrapper_' + size}).adopt(
				new Element('img', {
					'class': 'profile_pic profile_pic_' + picSize + '_' + user.username,
					'src': user['profile_pic_' + picSize],
					'title': TextUtility.unescape(user.fullname)
				}),
				new Element('div', {'class': 'online_status online_status_' + user.username, 'title': TextUtility.unescape(user.fullname)})
			);
			return el;
		}
		else if ($defined(user.room_name))
		{
			var el = new Element('div', {'class': 'profile_pic_wrapper_' + size}).adopt(
				new Element('img', {
					'class': 'profile_pic profile_pic_' + picSize + '_' + user.username,
					'src': user['profile_pic_' + picSize],
					'title': TextUtility.unescape(user.room_name)
				}),
				new Element('div', {'class': 'online_status online_status_' + user.username, 'title': TextUtility.unescape(user.room_name)})
			);
			return el;
		}
	},
	
	createItemPic: function(url){
		var picSize = 32;
		
	
		var el = new Element('div', {'class': 'profile_pic_wrapper_' + picSize}).adopt(
			new Element('img', {
				'class': 'profile_pic profile_pic_' + picSize,
				'src': url
			}),
			new Element('div', {'class': 'online_status'})
		);
		return el;
		
	},
	
	//chat post item
	createChatPostItem: function(msg, user){
		var el = ItemUtility.createPostBubble('post chat haspic');
		//pic
		if (!msg.outbound)
			user = msg.target;
			
		ItemUtility.createProfilePic(user, 20).inject(el);
		
		new Element('div', {'class': 'post_content'}).adopt(
			new Element('div', {
				'class': 'header',
				'html': TextUtility.replaceUrls(TextUtility.cleanText(msg.msg))
			})
		).inject(el);
		
		return el;
	},
	
	createChatPostHeading: function(user, date){
		
		var el = new Element('div', {'class': 'post chat heading'});
		
		var ts = $defined(date)? new Element('span', {
			'class': 'timestamp', 
			'text': DateUtility.getTimestamp(date, '%B %D, %I:%M%p'),
			'ts': date,
			'ts_format': '%B %D, %I:%M%p'
		}) : new Element('span');
		
		new Element('div', {'class': 'post_content'}).adopt(
			new Element('div', {'class': 'header'}).adopt(
				new Element('span', {'class': 'user_name', 'text': TextUtility.unescapeQuotes(user.fullname)}),
				ts
			)
		).inject(el);

		return el;
	},
	
	createChatTypingHeading: function(user){
		
		var el = new Element('div', {'class': 'post chat heading'});
		
		new Element('div', {'class': 'post_content'}).adopt(
			new Element('div', {'class': 'header'}).adopt(
				new Element('span', {'class': 'user_name', 'text': TextUtility.unescapeQuotes(user.fullname)}),
				new Element('span', {'class': 'status_text', 'text': 'is typing'})
			)
		).inject(el);

		return el;
	},
	
	createPostBubble: function(className, nocorner){
		
		if (nocorner)
		{
			var el = new Element('div', {'class': className}).adopt(
					new Element('div', {'class': 'bubble'}).adopt(
						new Element('div', {'class': 'bubble_inner'})
					)
				);
		}
		else
		{
			var el = new Element('div', {'class': className}).adopt(
				new Element('div', {'class': 'bubble'}).adopt(
					new Element('div', {'class': 'bubble_inner'}),
					new Element('div', {'class': 'corner'})
				)
			);
		}
		return el;
	}
		
};
var Logger = (function() {

	var LoggerSingleton = new Class({
		
		initialize: function() {
			this._log = new Log();	
			this._log.enableLog();
		},
		
		log: function(val){
			this._log.log(val);
		}
	});
	
	var singleton;

	return function() {
		return singleton ? singleton : singleton = new LoggerSingleton();
	};
	
})();var TextUtility = {
	unescapeQuotes: function(val)
	{
		if (!$defined(val))
			return '';
		val = val.toString();
		//unescape quote chars
		val = val.split("\\\\").join("\\");
		val = val.split("\\\'").join("'");
		val = val.split("\\\"").join('"');
		
		
		return val;
	},
	
	unescapeHtml: function(val){
		if (!$defined(val))
			return '';
		val = val.toString();
		//unescape quote chars
		val = val.split("&lt;").join("<");
		val = val.split("&gt;").join(">");
		val = val.split("&amp;").join("&");
		return val;
	},
	
	unescape: function(val){
		return TextUtility.unescapeQuotes(TextUtility.unescapeHtml(val));
	},
	
	escapeHtml: function(val){
		if (!$defined(val))
			return '';
		val = val.toString();
		//unescape quote chars
		val = val.split("<").join("&lt;");
		val = val.split(">").join("&gt;");
		//val = val.split("&").join("&amp;");
		return val;
	},
	
	stripHtml: function(val){
		if (!$defined(val))
			return '';
		val = val.toString();
		var re= /<\S[^><]*>/g;
		val.replace(re, "");
		return val;
	},
	
	cleanText: function(val){
		if (!$defined(val))
			return '';
		val = val.toString();
		val = TextUtility.unescapeQuotes(val);
		val = TextUtility.escapeHtml(val);
		return val;
	},
	
	pluralText: function(count, singular, plural){
		if (count == 1)
			return count + ' ' + singular;
		else
			return count + ' ' + plural;
	},
	
	replaceUrls: function(val){
		if (!$defined(val))
			return '';
			
		return val.replace(/([A-Za-z]+:\/\/[A-Za-z0-9-_]+\.[A-Za-z0-9-_%&\?\/.=,+~!:]+(#[A-Za-z0-9-_%&\?\/.=,+~!:]+)?)/g, "<a href=\"$1\" target=\"_blank\">$1</a>");
	},
	
	convertNewLine: function(val){
		if (!$defined(val))
			return '';
		val = val.toString();
		val = val.split("\n").join("<br/>");
		return val;
	}
		
};

//some extra stuff 
String.format = function(text){
    if ( arguments.length <= 1 )
        return text;
    //decrement to move to the second argument in the array
    var tokenCount = arguments.length - 2;
    for( var token = 0; token <= tokenCount; token++ )
    {
        //iterate through the tokens and replace their placeholders from the original text in order
        text = text.replace( new RegExp( "\\{" + token + "\\}", "gi"),
                                                arguments[ token + 1 ] );
    }
    return text;
};var UserUtility = {
	
	profilePicVersionSet: function(user_id, ver){
		if (!$defined(window.profilePicVer))
			window.profilePicVer = new Hash();
			
		window.profilePicVer.set(user_id, ver);
	},
	
	profilePicVersionGet: function(user_id){
		if (!$defined(window.profilePicVer))
			return 0;
			
		if (!window.profilePicVer.has(user_id))
			return 0;
			
		return window.profilePicVer.get(user_id);
	},
	
	getProfilePic: function(hash, user_id, size){
		if (!$defined(size))
			size = 26;
		
		var serverId = user_id%16;
		
		var version = UserUtility.profilePicVersionGet(user_id);
		
		//if (version == 0)
		//	return 'http://profilepics' + serverId.toString(16) + '.pip.io/' + hash.substring(0,2) + '/' + hash.substring(2,3) + '/' + hash.substring(3,5) + '/' + user_id + '/' + size + '.jpg';
		//else
			return 'http://profilepics' + serverId.toString(16) + '.pip.io/' + hash.substring(0,2) + '/' + hash.substring(2,3) + '/' + hash.substring(3,5) + '/' + user_id + '/' + size + '.jpg?' + version;
	},
	
	processLocation: function(username, location){
		
		if ($defined(location))
		{
			pipio.cacheLocation(username, location);
			pipio.dispatchEvent('userLocationUpdated', {username: username, location: location});
		}
			
		return location;
	},
	
	processAbout: function(about){
		
		about = !$defined(about)? {} : about;
		//set defaults
		if (!$defined(about.about))
			about.about = '';
		
		if (!$defined(about.url))
			about.url = '';
		
		return about;
	},
	
	processSource: function(source){
		if ($defined(source.user_id))
			return UserUtility.processUser(source);
		else if ($defined(source.room_id))
			return UserUtility.processRoom(source);
	},
	
	processRoom: function(room){
		
		var proom = {};
		
		proom.username = room.username.toLowerCase();
		
		if ($defined(pipio.getRoom(proom.username)))
		{
			//check if status / subscribed idffers, if so, fire events
			var existing = pipio.getRoom(proom.username);
			
			if ($defined(room.status_message))
			{
				if ($defined(room.status_message.status) && existing.status_message.status != room.status_message.status)
				{
					pipio.dispatchEvent('roomStatusUpdated', {username: room.username, status_message: room.status_message});
					existing.status_message = room.status_message;
				}
			}
			
			if ($defined(room.is_public))
				existing.is_public = room.is_public;
			
			if ($defined(room.status))
				existing.status = parseInt(room.status);
			
			return existing;
		}
		
		proom.room_id = room.room_id;
		proom.room_name = room.room_name;
		proom.room_description = $defined(room.room_description)? room.room_description : '';
		proom.room_hash = room.room_hash;
		proom.date_created = room.date_created;
		proom.is_public = room.is_public;
		proom.creator_id = $defined(room.creator_id)? room.creator_id : 0;
		proom.creator = $defined(room.creator)? UserUtility.processUser(room.creator): null;
		
		//status
		proom.status = $defined(room.status)? parseInt(room.status) : 0;
		proom.subscribed = $defined(room.subscribed)? parseInt(room.subscribed) : 0;
		
		proom.status_message = $defined(room.status_message) ? room.status_message : {status: ''};
		
		//process profile pic urls
		proom.profile_pic_16 = UserUtility.getProfilePic(room.room_hash, room.room_id, 16);
		proom.profile_pic_20 = UserUtility.getProfilePic(room.room_hash, room.room_id, 20);
		proom.profile_pic_26 = UserUtility.getProfilePic(room.room_hash, room.room_id, 26);
		proom.profile_pic_32 = UserUtility.getProfilePic(room.room_hash, room.room_id, 32);
		proom.profile_pic_42 = UserUtility.getProfilePic(room.room_hash, room.room_id, 42);
		proom.profile_pic_60 = UserUtility.getProfilePic(room.room_hash, room.room_id, 60);
		proom.profile_pic_100 = UserUtility.getProfilePic(room.room_hash, room.room_id, 100);
		proom.profile_pic_200 = UserUtility.getProfilePic(room.room_hash, room.room_id, 200);
		
		pipio.cacheRoom(proom);
			
		return proom;
	},
	
	processUser: function(user){
		//init processed user
		var puser = {};
		//normalize username
		puser.username = user.username.toLowerCase();
		
		//check location
		//if ($defined(user.current_location))
		//	UserUtility.processLocation(user.username, user.current_location);
		
		if ($defined(pipio.getUser(puser.username)))
		{
			//check if status / subscribed idffers, if so, fire events
			var existing = pipio.getUser(puser.username);
			
			if ($defined(user.status_message))
			{
				if ($defined(user.status_message.status) && existing.status_message.status != user.status_message.status)
				{
					pipio.dispatchEvent('userStatusUpdated', {username: user.username, status_message: user.status_message});
					existing.status_message = user.status_message;
				}
			}
			
			if ($defined(user.location_enabled))
				existing.location_enabled = user.location_enabled;
			
			if ($defined(user.is_public))
				existing.is_public = user.is_public;
			
			return existing;
		
		}
		//set default status_msg if not set
		puser.status_message = $defined(user.status_message) ? user.status_message : {status: ''};
	
		//set fullname
		puser.first_name = user.first_name;
		puser.last_name = user.last_name;
		puser.fullname = user.first_name + ' ' + user.last_name;
		
		//other data
		puser.user_hash = user.user_hash;
		puser.user_id = user.user_id;
		puser.group_id = $defined(user.group_id) ? user.group_id : 0;
		
		puser.about = UserUtility.processAbout(user.about);
		
		puser.is_public = user.is_public;
		puser.location_enabled = user.location_enabled;
		
		
		//xmpp status
		puser.online = false;
		puser.show = ''; // ''= avail 'away' = away 'dnd' = invis 'xa' = 'idle'
		puser.video = false;
		
		//process profile pic urls
		puser.profile_pic_16 = UserUtility.getProfilePic(user.user_hash, user.user_id, 16);
		puser.profile_pic_20 = UserUtility.getProfilePic(user.user_hash, user.user_id, 20);
		puser.profile_pic_26 = UserUtility.getProfilePic(user.user_hash, user.user_id, 26);
		puser.profile_pic_32 = UserUtility.getProfilePic(user.user_hash, user.user_id, 32);
		puser.profile_pic_42 = UserUtility.getProfilePic(user.user_hash, user.user_id, 42);
		puser.profile_pic_60 = UserUtility.getProfilePic(user.user_hash, user.user_id, 60);
		puser.profile_pic_100 = UserUtility.getProfilePic(user.user_hash, user.user_id, 100);
		puser.profile_pic_200 = UserUtility.getProfilePic(user.user_hash, user.user_id, 200);
		
		//send to cache
		pipio.cacheUser(puser);
		
		return puser;
	},
	
	updateStatusMessage: function(user, status_msg){
		var msgClass = '.status_msg_' + user.username;
		
		$$(msgClass).each(function(el){
			el.set('text', TextUtility.unescapeQuotes(status_msg));
		});
		
	},
	
	getInstalledApps: function() {
		var apps = new Array();
		if ($defined(user_data)) {
			user_data.apps.each(function(app) {
				apps[app.app_id] = app.external_user_id;
			}, this);
		}
		return apps;
	}
};var Background = new Class({
	Extends: Base,
	
	EventHandlers: [
		'userLoggedIn',
		'userLoggedOut'
	],
	
	init: function(){
		this.clouds = new Array();
		this.stars = new Array();
		this.container = $('background');
		this.getBounds();
		this.setupSky();
		
		//some constants
		this.sunrise = 6;
		this.sunset = 19;
		this.moonrise = 21;
		this.moonset = 4;
		
		this.maxStars = 30;
		this.starsStart = 19;
		this.starsEnd = 4;
		
		//var clouds = 6; //num of clouds
		for(var i = 0; i < 6; i++)
		{
			var cloud = this.createCloud().inject(this.container);
			this.clouds.push(cloud);
		}
		//cloud.get('tween', {duration: 1000*60*10}).start('left', this.xMax);
		this.run();
	},
	
	setupSky: function(){
		//setup sky
		this.sky = new Element('div', {
			'class': 'sky'
		}).inject(this.container);
		
		this.sun = new Element('div', {
			'class': 'celestial sun'
		}).inject(this.container);

		this.dusk = new Element('div', {
			'class': 'sky_dusk'
		}).inject(this.container);
				
		this.moon = new Element('div', {
			'class': 'celestial moon'
		}).inject(this.container);
		
		this.moveSky();
	},
	
	
	run: function(){
		this.step();
		this.anim = this.step.periodical(20000, this);
	},
	
	stop: function(){
		$clear(this.anim);
	},
	
	step: function(){
		this.moveClouds();
		this.moveSky();
	},
	
	getBounds: function(){
		this.xMin = -100;
		this.xMax = this.container.getSize().x;
		this.yMin = 0;
		this.yMax = this.container.getSize().y - 100;
	},
	
	userLoggedIn: function(){
	},
	
	userLoggedOut: function(){
	},
	
	setStars: function(){
		var time = this.getTime();
		if ((time > this.starsStart || time < this.starsEnd) && this.stars.length < this.maxStars)
		{
			var star = this.createStar();
			star.inject(this.container);
			star.setStyles({
				'top': $random(1, this.yMax),
				'left': $random(1, this.xMax)
			});
			this.stars.push(star);
				
		}
		else if (((time > this.starsEnd && time < this.starsStart) && this.stars.length > 0) || this.stars.length == this.maxStars)
		{
			this.removeStar();
		}
	},

	removeStar: function(){
		if (this.stars.length == 0)
			return;
			
		var i = $random(0, this.stars.length - 1);
		this.stars[i].destroy();
		this.stars.splice(i, 1);
	},
	
	moveClouds: function(){
		var containerSize = this.container.getSize();
		this.clouds.each(function(item){
			var pos = item.getPosition();
			var speed = $random(2,6);
			if (pos.x + speed > this.xMax)
			{
				item.setStyle('left', this.xMin);
			}
			else
			{
				item.get('tween', {duration: 1000}).start('left', pos.x + speed);
				//	item.setStyle('left', pos.x + speed);
			}
		}, this);
	},
	
	createStar: function(){
		var star = new Element('div', {
			'class': 'celestial stars star' + $random(1, 4)
		});
		
		return star;
	},
	
	createCloud: function(){
		var cloud = new Element('div', {
			'class': 'cloud cloud' + $random(1, 6)
		});
		
		cloud.setStyle('left', $random(this.xMin, this.xMax));
		cloud.setStyle('top', $random(this.yMin, this.yMax));
		
		return cloud;
	},
	
	moveSky: function(){
		this.sky.setStyle('top', this.getSkyPosition());
		this.dusk.setStyle('bottom', this.getDuskPosition());
		this.moveSun();
		this.moveMoon();
		this.setStars();
	},
	
	moveSun: function(){
		var pos = this.getSunPosition();
		this.sun.setStyles({
			'top': pos.y + '%',
			'left': pos.x + '%'
		});
	},
	
	moveMoon: function(){
		var pos = this.getMoonPosition();
		this.moon.setStyles({
			'top': pos.y + '%',
			'left': pos.x + '%'
		});
	},
	
	getSkyPosition: function(){
		//sky is 3000px tall bg.  
		//darkest is at top=0
		//lighters is at top = this.containerSize.y - 3000;
		var skyMax = 3000 - this.container.getSize().y;
		var time = this.getTime();
		
		if (time >= 1)
			time += 23;
		else
			time -= 1;
		
		return -  Math.sin(((time)/24) * Math.PI) * Math.sin(((time)/24) * Math.PI) * skyMax;
	},
	
	getGroundOpacity: function(){
		var opacityMax = 80;
		var time = this.getTime();	
		
		if (time >= 1)
			time += 23;
		else
			time -= 1;
			
		var dark = (1 - Math.sin(((time)/24) * Math.PI)) * opacityMax;
		return dark;
	},
	
	getDuskPosition: function(){
		
		var time = this.getTime();
		if (time > this.sunset - 1 && time < this.sunset + 1)
		{
			time = (time - (this.sunset - 1))/2;
			return -183 + Math.sin((time) * Math.PI) * 183;
		}
		else
		{
			return -183;
		}
	},
	
	
	getSunPosition: function(){
		var pos = {x: 90, y: 100};
		var time = this.getTime();
		if (time > this.sunrise && time < this.sunset) //daylight!  show the sun
		{
			time = (time - this.sunrise)/(this.sunset - this.sunrise);
			pos.y = 100 - Math.sin(time * Math.PI) * 100;
			pos.x = 90 - time * 100;
		}
		return pos;
	},
	
	getMoonPosition: function(){
		var pos = {x: 90, y: 100};
		var time = this.getTime();
		if (time < this.moonset)
			time += 24;
		if (time > this.moonrise && time < this.moonset + 24) // moontime!
		{	
			time = Math.abs((time - this.moonrise)/((this.moonset + 24) - this.moonrise));
			
			pos.y = 100 - Math.sin(time * Math.PI) * 100;
			pos.x = 90 - time * 100;
		}
		return pos;
	},
	
	getTime: function(){
		
		var date = new Date();
		return date.getHours() + date.getMinutes()/60;
		// testing code
		
	}
});

/*
var Weather = new Class({
	Extends: Module,	
	
	
	weatherClasses: {
		'Chance of Flurries': '4',
		'Chance of Rain': '1',
		'Chance of Freezing Rain': '8',
		'Chance of Sleet': '8',
		'Chance of Snow': '8',
		'Chance of Thunderstorms': '7',
		'Chance of a Thunderstorm': '7',
		'Clear': '4',
		'Cloudy': '3',
		'Flurries': '5',
		'Fog': '3',
		'Hazy': '3',
		'Mostly Cloudy': '9',
		'Mostly Sunny': '2',
		'Partly Cloudy': '2',
		'Partly Sunny': '9',
		'Freezing Rain': '8',
		'Rain': '1',
		'Sleet': '8',
		'Snow': '8',
		'Sunny': '4',
		'Thunderstorms': '7',
		'Unknown': '4',
		'Overcast': '3',	
		'Scattered Clouds': '2'
	},
	
	EventHandlers: [
		'windowResized'
	],
	
	init: function(){
		this.clouds = new Hash();
		this.stars = new Array();
		
		var clouds = 6; //num of clouds
		
		//these are hardcoded, ready to be pulled from weather service
		this.sunrise = 6;
		this.sunset = 19;
		this.moonrise = 21;
		this.moonset = 4;
		
		this.maxStars = 30;
		this.starsStart = 19;
		this.starsEnd = 4;
		this.container = $('weather');
		this.containerSize = this.container.getSize();
		this.setupSky();
		//if no container called 'weather', do not initialize it
		if (!$defined(this.container))
			return;
		
		var offset = this.container.getSize().x / clouds;
		
		for (var i=1; i<=clouds; i++)
		{
			var cloud = this.createCloud(i);
			cloud.inject(this.container);
			
			// add random x jiggle to position
			var left = offset * (i-1) + $random(20, 40);
			var width = cloud.getSize().x;
			var height = cloud.getSize().y;
			cloud.setStyle('left', left);
			cloud.store('left', left);
			cloud.store('speed', $random(1, 3)/10);
			cloud.store('width', width);
			cloud.setStyle('top', this.getCloudY(height));
			this.clouds.set('cloud_' + i, cloud);
		}
		
		//this.time = new Element('span', {
		//	'class': 'current_time'
		//});
		//this.time.inject(this.container);
		
		this.run();
	},

	
	getCloudY: function(h){
		//nofly zone = y 90-180 //and 100px ground buffer zone 
		var y = $random(0, this.containerSize.y - 90 - 100);
		if (y < 90) //above the nofly zone
		{
			return y - h;
		}
		else
		{
			return y+90;
		}
	},
	
	windowResized: function(){
		this.containerSize = this.container.getSize();
		this.moveSky();
	},
	
	snow: function(){
			
	},
	
	setupSky: function(){
		//setup sky
		this.sky = new Element('div', {
			'class': 'sky'
		});
		this.sky.inject(this.container);
		
		this.sun = new Element('div', {
			'class': 'celestial sun'
		});
		
		this.sun.inject(this.container);

		this.dusk = new Element('div', {
			'class': 'sky_dusk'
		});
		this.dusk.inject(this.container);
				
		this.moon = new Element('div', {
			'class': 'celestial moon'
		});
		this.moon.inject(this.container);
		
		//this.ground = new Element('div', {
	//		'class': 'ground'
		//});
		//this.ground.inject(this.container);
		
		this.moveSun();
		this.moveMoon();
		this.moveSky();
	},
	
	run: function()
	{
		//move clouds every sec
		this.anim = this.moveClouds.periodical(1000, this);
		//this.timeanim = this.setTime.periodical(1000, this);
		//move sky every 2 min
		this.skyanim = this.moveSky.periodical(1000 * 60 * 2, this);
		//this.skyanim = this.moveSky.periodical(100, this);
	},
	
	stop: function()
	{
		$clear(this.anim);
	},
	
	setStars: function(){
		var time = this.getTime();
		if ((time > this.starsStart || time < this.starsEnd) && this.stars.length < this.maxStars)
		{
			var star = this.createStar();
			star.inject(this.container);
			star.setStyles({
				'top': $random(1, this.containerSize.y),
				'left': $random(1, this.containerSize.x)
			});
			this.stars.push(star);
				
		}
		else if ((time > this.starsEnd && time < this.starsStart) && this.stars.length > 0)
		{
			for (var i=0; i< 1; i++)
				this.removeStar();
		}
	},
	
	setTime: function(){
		var now = new Date();
		var ampm = "am";
		var hours = now.getHours();
		var mins = now.getMinutes();
		var secs = now.getSeconds();
		
		if (hours > 12)
		{
			ampm = "pm";
			hours = hours - 12;
		}
		
		if (mins < 10)
		{
			mins = "0" + mins;
		}
		
		if (secs < 10)
		{
			secs = "0" + secs;
		}
		

		this.time.set('text', hours + ":" + mins + ":" + secs + ' ' + ampm);
	},
	
	removeStar: function(){
		if (this.stars.length == 0)
			return;
			
		var i = $random(0, this.stars.length - 1);
		this.stars[i].destroy();
		this.stars.splice(i, 1);
	},
	
	moveClouds: function(){
		var containerSize = this.container.getSize();
		this.clouds.each(function(item, key){
			
			if (item.retrieve('left') + item.retrieve('speed') > containerSize.x)
			{
				item.setStyle('left', - item.retrieve('width'));
				item.store('left', - item.retrieve('width'));
			}
			else
			{
				item.setStyle('left', item.retrieve('left') + item.retrieve('speed'));
				item.store('left', item.retrieve('left') + item.retrieve('speed'));
			}
		}, this);
	},
	
	
	moveSky: function(){
		this.sky.setStyle('top', this.getSkyPosition());
		this.dusk.setStyle('bottom', this.getDuskPosition());
		this.moveSun();
		this.moveMoon();
		this.setStars();
	},
	
	moveSun: function(){
		var pos = this.getSunPosition();
		this.sun.setStyles({
			'top': pos.y + '%',
			'left': pos.x + '%'
		});
	},
	
	moveMoon: function(){
		var pos = this.getMoonPosition();
		this.moon.setStyles({
			'top': pos.y + '%',
			'left': pos.x + '%'
		});
	},
	
	createCloud: function(cloudNum){
		var cloud = new Element('div', {
			'class': 'cloud cloud' + $random(1, 6)
		});
		
		return cloud;
	},

	createStar: function(){
		var star = new Element('div', {
			'class': 'celestial stars star' + $random(1, 4)
		});
		
		return star;
	},
	
	getSkyPosition: function(){
		//sky is 3000px tall bg.  
		//darkest is at top=0
		//lighters is at top = this.containerSize.y - 3000;
		var skyMax = 3000 - this.containerSize.y;
		var time = this.getTime();
		
		if (time >= 1)
			time += 23;
		else
			time -= 1;
		
		return -  Math.sin(((time)/24) * Math.PI) * Math.sin(((time)/24) * Math.PI) * skyMax;
	},
	
	getGroundOpacity: function(){
		var opacityMax = 80;
		var time = this.getTime();	
		
		if (time >= 1)
			time += 23;
		else
			time -= 1;
			
		var dark = (1 - Math.sin(((time)/24) * Math.PI)) * opacityMax;
		return dark;
	},
	
	getDuskPosition: function(){
		
		var time = this.getTime();
		if (time > this.sunset - 1 && time < this.sunset + 1)
		{
			time = (time - (this.sunset - 1))/2;
			return -183 + Math.sin((time) * Math.PI) * 183;
		}
		else
		{
			return -183;
		}
	},
	
	
	getSunPosition: function(){
		var pos = {x: 90, y: 100};
		var time = this.getTime();
		if (time > this.sunrise && time < this.sunset) //daylight!  show the sun
		{
			time = (time - this.sunrise)/(this.sunset - this.sunrise);
			pos.y = 100 - Math.sin(time * Math.PI) * 100;
			pos.x = 90 - time * 100;
		}
		return pos;
	},
	
	getMoonPosition: function(){
		var pos = {x: 90, y: 100};
		var time = this.getTime();
		if (time < this.moonset)
			time += 24;
		if (time > this.moonrise && time < this.moonset + 24) // moontime!
		{	
			time = Math.abs((time - this.moonrise)/((this.moonset + 24) - this.moonrise));
			
			pos.y = 100 - Math.sin(time * Math.PI) * 100;
			pos.x = 90 - time * 100;
		}
		return pos;
	},
	
	getTime: function(){
		
		var date = new Date();
		return date.getHours() + date.getMinutes()/60;
		// testing code
		
	}
});
*/var Chat = new Class({
	Extends: Base,
	
	EventHandlers: [
		'userSwitched',
		'chatStart',
		'chatMsgReceived',
		'chatTypingReceived'
	],
	
	init: function(){
		this.contents = $H();
		this.lastMsg = $H();
	},
	
	userSwitched: function(){
		if (this.isLoggedIn())
			this.userLoggedIn();
		else
			this.userLoggedOut();
	},
	
	userLoggedIn: function(){
	},
	
	userLoggedOut: function(){
		this.resetChatPopup();
		this.init();
	},
	
	chatStart: function(username, noSwitch){
		
		if (!$defined(this.popup))
			this.createChatPopup();
		
		var user = this.getContact(username);
		var content = this.addChatConvo(user);
		
		if (!$defined(noSwitch))
			this.popup.switchContent(username);
		
		return content;
	},
	
	chatTypingReceived: function(username){
		if (!this.contents.has(username))
			return;
		
		this.contents.get(username).showTyping();
	},
	
	chatMsgReceived: function(msg){
		var user = msg.target;
		
		var content = this.chatStart(user.username, true);
		
		//insert heading if neccesary
		if (!this.lastMsg.has(user.username) || this.lastMsg.get(user.username).outbound != msg.outbound || new Date().getTime() - this.lastMsg.get(user.username).timestamp.getTime() > 300000)
		{
			//no timestamp if msg within 10 min
			var ts = msg.timestamp;
			if (this.lastMsg.has(user.username) && msg.timestamp.getTime() - this.lastMsg.get(user.username).timestamp.getTime() < 300000)
				ts = null;
			
			var heading = (msg.outbound)? ItemUtility.createChatPostHeading(this.getPrivateUser(), ts):  ItemUtility.createChatPostHeading(user, ts);
			content.insertChatItem(heading);
		}
		this.lastMsg.set(user.username, msg);
		
		var item = ItemUtility.createChatPostItem(msg, this.getPrivateUser());
		content.insertChatItem(item);
		
	},
	
	resetChatPopup: function(){
		
		if ($defined(this.popup))
			this.popup.close();
		
		this.contents.each(function(content, key){
			content.close(true);
			this.contents.erase(key);
		}, this);
		
		
	},
	
	createChatPopup: function(){
		this.popup = new ChatPopup({
			size: {x: 400, y: 350},
			resizable: false,
			className: 'pipioChat',
			onClose: this.destroyChatPopup.bind(this)
		});
	},
	
	destroyChatPopup: function(){
		
		this.popup = null;
		this.isActive = false;
	},
	
	addChatConvo: function(user){
		
		if (this.contents.has(user.username))
		{
			var nav = new Nav({
				iconOptions: {user: user},
				displayName: user.fullname,
				closable: true
			});
			var content = this.contents.get(user.username);
			this.popup.addContent(user.username, nav, content);
			content.jumpToBottom();
			
			return content;
		}
		else
		{
			var nav = new Nav({
				iconOptions: {user: user},
				displayName: user.fullname,
				closable: true
			});
			
			var content = new ChatPopupContent({user: user});
			this.contents.set(user.username, content);
			this.popup.addContent(user.username, nav, content);
			
			return content;
			
		}
			
		
	}
});
	Chat.implement({
	createChatPostItem: function(msg){
		var item = new Element('div', {'class': 'post chat haspic'});
		
		
		
		
	}
});var ChatPopup = new Class({
	Extends: Popup,
	
	//special version fo setTitle just for chat popups
	setTitle: function(){
		if (this.hasTabs)
		{
			//if it has tabs, use generic chat icon
			var icon = new Icon20({iconName: 'rooms'});
			var titleText = new Element('div', {
				'class': 'title_text',
				'text': this.contents.getLength() + ' Chats'
			});
		}
		else
		{
			var nav = this.navs.get(this.currentContent);
			var icon = new Icon20(nav.iconOptions);
			var titleText = new Element('div', {
				'class': 'title_text',
				'text': 'Chat with ' + TextUtility.unescapeQuotes(nav.displayName)
			});
			
		}
		
		this.titleIcon = $(icon).replaces(this.titleIcon);
		this.titleText = titleText.replaces(this.titleText);
	}
});var ChatPopupContent = new Class({
	Extends: PopupContent,
	
	onBeforeInit: function(options){
		options.destroy = false;
		
		this.user = options.user;
		
		return options;
	},
	
	onInit: function(){
		
		this.lastOutbound = null;
		
		this.typingSent = false;
		
		//create the required divs
		this.chatContent = new Element('div', {'class': 'chatContent'}).inject(this.content);
		
		this.chatInput = new Element('textarea', {'maxlength': 2000});
		this.chatInput.addEvent('keyup', this.chatInputKeyUp.bindWithEvent(this));
		
		
		new Element('div', {'class': 'chatInput'}).adopt(
			new Element('div', {'class': 'textarea_wrapper'}).adopt(
				this.chatInput	
			)
		).inject(this.content);
		
		//setup scroller
		this.scroll = new Fx.Scroll(this.chatContent);
	},
	
	jumpToBottom: function(){
		this.scroll.set(0, this.chatContent.getScrollSize().y);
	},
	
	onShow: function(){
		if ($defined(this.scrollY))
			this.scroll.set(0, this.scrollY);
		
		this.focus();
		
		//clear msg
		var msg = this.user.first_name + ' says...';
		this.fireEvent('titleMessageDelete', msg);
	},
	
	onHide: function(){
		if ($defined(this.chatContent))
			this.scrollY = this.chatContent.getScroll().y;
	},
	
	focus: function(){
		this.chatInput.focus.delay(500, this.chatInput);
	},
	
	clearInput: function(){
		this.chatInput.value = '';
	},
	
	chatInputKeyUp: function(e){
		if (e.key == 'enter')
		{
			var msg = e.target.value.trim();
			//dont let empty msg get sent
			if (msg == '')
				return;
			
			this.fireEvent('sendIM', this.user, msg);
			this.resetTypingSent();
			this.clearInput();
			this.focus();
		}
		else
		{
			this.sendTyping();
		}
		
		DomUtility.textareaAutoSize(e.target, 16);
	},
	
	insertChatItem: function(el){
		this.hideTyping();
		el.inject(this.chatContent);
		this.scroll.toBottom();
		
		//should fire alert?
		
		if (!this.isOn)
		{
			var msg = this.user.first_name + ' says...';
			this.fireEvent('titleMessageAdd', msg);
			
		}
	},
	
	showTyping: function(){
		if ($defined(this.typingHeading))
			return;
			
		this.typingHeading = ItemUtility.createChatTypingHeading(this.user);
		this.typingHeading.inject(this.chatContent);
		this.scroll.toBottom();
		this.hideTyping.delay(6000, this);
	},
	
	hideTyping: function(){
		if ($defined(this.typingHeading))
		{
			this.typingHeading.destroy();
			this.typingHeading = null;
		}
	},
	
	sendTyping: function(){
		if (this.typingSent || !this.user.online)
			return;
		
		this.typingSent = true;
		this.fireEvent('sendTyping', this.user);
		this.resetTypingSent.delay(5000, this);
	},
	
	resetTypingSent: function(){
		this.typingSent = false;
	}
	
});var Invite = new Class({
	Extends: Base,
	
	EventHandlers: [
	                'userSwitched',
	                'searchUserShow',
	                'searchEmailShow',
	                'inviteUserShow'
	],
	
	userSwitched: function(){
		if (this.isLoggedIn())
			this.userLoggedIn();
		else
			this.userLoggedOut();
	},
	
	userLoggedIn: function(){
	},
	
	userLoggedOut: function(){
	},
	
	
	searchUserShow: function(){
		if ($defined(this.searchUserPopup))
		{
			this.searchUserPopup._select();
			return;
		}
		
		this.createSearchUserPopup();
	},
	
	closeSearchUserPopup: function(){
		if ($defined(this.searchUserPopup))
			this.searchUserPopup.close();
	},
	
	createSearchUserPopup: function(){
		this.searchUserPopup = new Popup({
			size: {x: 350, y: 110},
			resizable: false,
			dockable: false,
			className: 'userSearch',
			onClose: this.destroySearchUserPopup.bind(this)
		});
		
		var nav = new Nav({
			iconOptions: {iconName: 'search'},
			displayName: 'Search for Users on Pip.io',
			closable: true
		});
		
		var content = new UserSearchPopupContent({});
		
		this.searchUserPopup.addContent('login', nav, content);

	},
	
	destroySearchUserPopup: function(){
		this.searchUserPopup = null;
	},
	
	searchEmailShow: function(){
		if ($defined(this.searchEmailPopup))
		{
			this.searchEmailPopup._select();
			return;
		}
		
		this.createSearchEmailPopup();
	},
	
	closeSearchEmailPopup: function(){
		if ($defined(this.searchEmailPopup))
			this.searchEmailPopup.close();
	},
	
	createSearchEmailPopup: function(){
		this.searchEmailPopup = new Popup({
			size: {x: 350, y: 140},
			resizable: false,
			dockable: false,
			className: 'userSearch',
			onClose: this.destroySearchEmailPopup.bind(this)
		});
		
		var nav = new Nav({
			iconOptions: {iconName: 'search'},
			displayName: 'Search Contacts for Pip.io Users',
			closable: true
		});
		
		var content = new EmailSearchPopupContent({});
		
		this.searchEmailPopup.addContent('login', nav, content);

	},
	
	destroySearchEmailPopup: function(){
		this.searchEmailPopup = null;
	},
	
	inviteUserShow: function(email){
		if ($defined(this.inviteUserPopup))
		{
			this.inviteUserPopup._select();
			return;
		}
		
		this.createInviteUserPopup(email);
	},
	
	closeInviteUserPopup: function(){
		if ($defined(this.inviteUserPopup))
			this.inviteUserPopup.close();
	},
	
	createInviteUserPopup: function(email){
		this.inviteUserPopup = new Popup({
			size: {x: 350, y: 160},
			resizable: false,
			dockable: false,
			className: 'userSearch',
			onClose: this.destroyInviteUserPopup.bind(this)
		});
		
		var nav = new Nav({
			iconOptions: {iconName: 'users'},
			displayName: 'Invite Friend to Join Pip.io',
			closable: true
		});
		
		var content = new UserInvitePopupContent({email: email});
		
		this.inviteUserPopup.addContent('login', nav, content);

	},
	
	destroyInviteUserPopup: function(){
		this.inviteUserPopup = null;
	}
});var EmailSearchPopupContent = new Class({
	Extends: PopupContent,
	
	strings: {
		emailLabel: 'Email:',
		passwordLabel: 'Password:',
		userSearchMessage: 'Search your email contacts for Pip.io users'
	},
	
	onInit: function(){
		
		new Element('div', {'class': 'text_section centered light', 'text': this.strings.userSearchMessage}).inject(this.content);
		
		this.emailInput = new Element('input', {'type': 'text', 'maxlength': '50'});
		this.emailInput.addEvent('keyup', this.inputKeyUp.bindWithEvent(this));
		
		new Element('div', {'class': 'input_section email'}).adopt(
				new Element('div', {'class': 'label light', 'text': this.strings.emailLabel}),
				new Element('div', {'class': 'textarea_wrapper'}).adopt(
					this.emailInput
				)
			).inject(this.content);
		
		this.passwordInput = new Element('input', {'type': 'password', 'maxlength': '50'});
		this.passwordInput.addEvent('keyup', this.inputKeyUp.bindWithEvent(this));
		
		new Element('div', {'class': 'input_section email'}).adopt(
				new Element('div', {'class': 'label light', 'text': this.strings.passwordLabel}),
				new Element('div', {'class': 'textarea_wrapper'}).adopt(
					this.passwordInput
				)
			).inject(this.content);
		
		//ACTIONS
		
		this.closeButton = new ButtonMedium({
			displayName: 'Cancel',
			className: 'dark',
			action: 'cross'
		});
		$(this.closeButton).addEvent('click', this.closeContent.bind(this));
		
		this.actionButton = new ButtonMedium({
			displayName: 'Search',
			className: 'dark',
			action: 'check'
		});
		$(this.actionButton).addEvent('click', this.search.bind(this));
		
		
		this.actions = new Element('div', {'class': 'actions'}).adopt(
			$(this.actionButton),
			$(this.closeButton)
		).inject(this.content);
	},
	
	inputKeyUp: function(e){
		if (e.key == 'enter')
		{
			if (this.passwordInput.value.trim() != '' && this.emailInput.value.trim() != '')
			this.search();
		}
	},
	
	search: function(){
		
		var email_address = this.emailInput.value.trim();
		var password = this.passwordInput.value.trim();
		
		var params = {
				email_address: email_address,
				password: password
		};
		
		this.call('contacts', 'search_email', params, this.searchSuccess.bind(this), this.searchFail.bind(this));
		
		this.actionButton.showProgress();
	},
	
	searchSuccess: function(data){
		this.actionButton.hideProgress();
		
		if (!$defined(data.users) && !$defined(data.contacts))
			return;
		
		
		if (data.users.length == 0 && data.contacts.length == 0)
		{
			var name = 'search_user';
			var title = 'No Results';
			var message = 'Your search returned no results';

			this.fireEvent('showAlert', name, title, message);
		}
		else
		{
			this.showResultsPopup(data.users, data.contacts);
		}
		
	},
	
	searchFail: function(status){
		this.actionButton.hideProgress();
		
		var name = 'search_user';
		var title = 'Error';
		var message = status.message;

		this.fireEvent('showAlert', name, title, message);
	},
	
	showResultsPopup: function(users, contacts){
		if ($defined(this.resultsPopup))
		{
			this.resultsPopup.close();
		}
		
		this.createResultsPopup(users, contacts);
	},
	
	closeResultsPopup: function(){
		if ($defined(this.resultsPopup))
			this.resultsPopup.close();
	},
	
	createResultsPopup: function(users, contacts){
		this.resultsPopup = new Popup({
			size: {x: 350, y: 400},
			resizable: false,
			dockable: false,
			className: 'userSearch',
			onClose: this.destroyResultsPopup.bind(this)
		});
		
		var nav = new Nav({
			iconOptions: {iconName: 'search'},
			displayName: 'Search Results',
			closable: true
		});
		
		var content = new UserSearchResultPopupContent({users: users, contacts: contacts});
		
		this.resultsPopup.addContent('results', nav, content);

	},
	
	destroyResultsPopup: function(){
		this.resultsPopup = null;
	},
	
	onShow: function(){
		this.focus();
	},
	
	focus: function(){
		this.emailInput.focus.delay(500, this.emailInput);
	},
	
	onClose: function(){
		this.closeResultsPopup();
	}
	
});var UserInvitePopupContent = new Class({
	Extends: PopupContent,
	
	strings: {
		emailLabel: 'Email:',
		messageLabel: 'Message:',
		defaultMessage: 'Connect with me on Pip.io!'
	},

	onBeforeInit: function(options){
		
		this.email = options.email;
	
		return options;
	},
	
	onInit: function(){
		
		
		this.emailInput = new Element('input', {'type': 'text', 'maxlength': '50', 'value': this.email});
		
		new Element('div', {'class': 'input_section email'}).adopt(
			new Element('div', {'class': 'label light', 'text': this.strings.emailLabel}),
			new Element('div', {'class': 'textarea_wrapper'}).adopt(
				this.emailInput
			)
		).inject(this.content);
		
		this.messageInput = new Element('textarea', {'value': TextUtility.unescape(this.strings.defaultMessage)});
		
		new Element('div', {'class': 'input_section email message'}).adopt(
			new Element('div', {'class': 'label light', 'text': this.strings.messageLabel}),
			new Element('div', {'class': 'textarea_wrapper'}).adopt(
				this.messageInput
			)
		).inject(this.content);
		
		

		this.closeButton = new ButtonMedium({
			displayName: 'Cancel',
			className: 'dark',
			action: 'cross'
		});
		$(this.closeButton).addEvent('click', this.closeContent.bind(this));
		
		this.actionButton = new ButtonMedium({
			displayName: 'Send Invite',
			className: 'dark',
			action: 'check'
		});
		$(this.actionButton).addEvent('click', this.sendInvite.bind(this));
		
		this.message = new Element('div', {'class': 'message success'});
		
		this.actions = new Element('div', {'class': 'actions'}).adopt(
			this.message,
			$(this.actionButton),
			$(this.closeButton)
		).inject(this.content);
		
	},
	
	sendInvite: function(){
		
		var email = this.emailInput.value.trim();
		var message = this.messageInput.value.trim();
		
		if (email == '')
		{
			var name = 'invite_user';
			var title = 'Error';
			var message = 'Please enter an email address';

			this.fireEvent('showAlert', name, title, message);
			return;
		}
		
		var params = {
				email: email,
				message: message
				
		};
		
		this.call('contacts', 'invite_create', params, this.sendInviteSuccess.bind(this), this.sendInviteFail.bind(this));
		this.actionButton.showProgress();
		this.message.empty();
	},
	
	sendInviteSuccess: function(){
		this.actionButton.hideProgress();
		
		this.emailInput.value = '';
		this.message.set('text', 'Invite sent');
	},
	
	sendInviteFail: function(status){
		this.actionButton.hideProgress();
		var name = 'invite_user';
		var title = 'Error';
		var message = status.message;

		this.fireEvent('showAlert', name, title, message);
	}
	
});var UserSearchPopupContent = new Class({
	Extends: PopupContent,
	
	strings: {
		userSearchMessage: 'You can enter a name or email to search',
		errorMessage: 'There was an error creating this group'
	},
	
	onInit: function(){
		
		new Element('div', {'class': 'text_section centered light', 'text': this.strings.userSearchMessage}).inject(this.content);
		
		this.searchInput = new Element('input', {'type': 'text', 'maxlength': '50'});
		this.searchInput.addEvent('keyup', this.inputKeyUp.bindWithEvent(this));
		
		new Element('div', {'class': 'input_section'}).adopt(
				new Element('div', {'class': 'textarea_wrapper'}).adopt(
					this.searchInput
				)
			).inject(this.content);
		
		//ACTIONS
		
		this.closeButton = new ButtonMedium({
			displayName: 'Cancel',
			className: 'dark',
			action: 'cross'
		});
		$(this.closeButton).addEvent('click', this.closeContent.bind(this));
		
		this.actionButton = new ButtonMedium({
			displayName: 'Search',
			className: 'dark',
			action: 'check'
		});
		$(this.actionButton).addEvent('click', this.search.bind(this));
		
		
		this.actions = new Element('div', {'class': 'actions'}).adopt(
			$(this.actionButton),
			$(this.closeButton)
		).inject(this.content);
	},
	
	inputKeyUp: function(e){
		if (e.key == 'enter')
		{
			if (this.searchInput.value.trim() != '')
			this.search();
		}
	},
	
	search: function(){
		
		var query = this.searchInput.value.trim();
		var params = {query: query};
		
		this.call('contacts', 'search_user', params, this.searchSuccess.bind(this), this.searchFail.bind(this));
		
		this.actionButton.showProgress();
	},
	
	searchSuccess: function(data){
		this.actionButton.hideProgress();
		
		if (!$defined(data.users) && !$defined(data.contacts))
			return;
		
		
		if (data.users.length == 0 && data.contacts.length == 0)
		{
			var name = 'search_user';
			var title = 'No Results';
			var message = 'Your search returned no results';

			this.fireEvent('showAlert', name, title, message);
		}
		else
		{
			this.showResultsPopup(data.users, data.contacts);
		}
		
	},
	
	searchFail: function(status){
		this.actionButton.hideProgress();
		
		var name = 'search_user';
		var title = 'Error';
		var message = status.message;

		this.fireEvent('showAlert', name, title, message);
	},
	
	showResultsPopup: function(users, contacts){
		if ($defined(this.resultsPopup))
		{
			this.resultsPopup.close();
		}
		
		this.createResultsPopup(users, contacts);
	},
	
	closeResultsPopup: function(){
		if ($defined(this.resultsPopup))
			this.resultsPopup.close();
	},
	
	createResultsPopup: function(users, contacts){
		this.resultsPopup = new Popup({
			size: {x: 350, y: 400},
			resizable: false,
			dockable: false,
			className: 'userSearch',
			onClose: this.destroyResultsPopup.bind(this)
		});
		
		var nav = new Nav({
			iconOptions: {iconName: 'search'},
			displayName: 'Search Results',
			closable: true
		});
		
		var content = new UserSearchResultPopupContent({users: users, contacts: contacts});
		
		this.resultsPopup.addContent('results', nav, content);

	},
	
	destroyResultsPopup: function(){
		this.resultsPopup = null;
	},
	
	onShow: function(){
		this.focus();
	},
	
	focus: function(){
		this.searchInput.focus.delay(500, this.searchInput);
	},
	
	onClose: function(){
		this.closeResultsPopup();
	}
	
});var UserSearchResultPopupContent = new Class({
	Extends: PopupContent,
	
	strings: {
		searchResultMessage: 'Search results'
	},

	onBeforeInit: function(options){
		
		this.users = options.users;
		this.contacts = options.contacts;
	
		return options;
	},
	
	onInit: function(){
		
		this.list = new Element('div', {'class': 'itemList'}).inject(this.content);
		
		
		this.closeButton = new ButtonMedium({
			displayName: 'Close',
			className: 'dark',
			action: 'cross'
		});
		$(this.closeButton).addEvent('click', this.closeContent.bind(this));

		this.actions = new Element('div', {'class': 'actions'}).adopt(
				$(this.closeButton)
			).inject(this.content);
		
		
		if (this.users.length > 0)
		{
			this.insertUsers();
			this.insertSeperator();
		}
		
		this.insertContacts();
		
	},
	
	insertContacts: function(){
		$H(this.contacts).each(function(name, email){
			
			this.insertContact(name, email);
		}, this);
	},
	
	insertContact: function(name, email){
		var el = UserSearchUtility.createEmailItem(name, email);
		
		//add button for invite
		var button = new ButtonSmall({
			displayName: 'Send Invite',
			className: 'dark',
			action: 'check'
		});
		
		$(button).inject(el);
		$(button).addEvent('click', this.fireEvent.bind(this, ['inviteUserShow', email]));
		
		
		el.inject(this.list);
	},
	
	insertUsers: function(){
		this.users.each(function(user){
			user = UserUtility.processUser(user);
			this.insertUser(user);
			
		}, this);
	},
	
	insertUser: function(user){
		var el = UserSearchUtility.createUserItem(user);
		
		if (!this.hasContact(user.username))
		{
				
			//add button for invite
			var button = new ButtonSmall({
				displayName: 'Add Contact',
				className: 'dark',
				action: 'check'
			});
			
			$(button).inject(el);
			$(button).addEvent('click', this.contactRequest.bind(this, [user.username, el]));
		}
		else
			el.addClass('connected');
		
		el.inject(this.list);
		
	},

	insertSeperator: function(){
		new Element('div', {'class': 'listItem seperator'}).inject(this.list);
	},
	
	contactRequest: function(username, el){
			
		this.fireEvent('connectionRequestCreate', username);
		
		el.addClass('invited');
	}
});var UserSearchUtility = {
		
		
	createUserItem: function(user){
		var el = new Element('div', {'class': 'listItem'});
		var icon = new Icon20({user: user});
		$(icon).inject(el);
		var name = new Element('div', {'class': 'listText text12 light', 'text': TextUtility.cleanText(user.fullname)}).inject(el);
		
		//add properties for search
	//	el.set('val1', user.fullname.toLowerCase());
	//	el.set('val2', user.last_name.toLowerCase());
		name.addEvent('click', pipio.dispatchEvent.bind(pipio, ['showUser', user]));
		
		
		new Element('div', {'class': 'invited_text text11 success', 'text': 'Request Sent'}).inject(el);
		new Element('div', {'class': 'connected_text text11 success', 'text': 'Connected'}).inject(el);
		
		return el;
	},
	
	createEmailItem: function(name, email){
		var el = new Element('div', {'class': 'listItem'});
		var icon = new Icon20({iconName: 'users'});
		$(icon).inject(el);
		new Element('div', {'class': 'listText text12 light', 'text': TextUtility.cleanText(email)}).inject(el);
		
		//add properties for search
	//	el.set('val1', user.fullname.toLowerCase());
	//	el.set('val2', user.last_name.toLowerCase());
		
		//add button for invite
		var button = new ButtonSmall({
			displayName: 'Invite',
			className: 'dark',
			action: 'check'
		});
		
		$(button).inject(el);
		
		new Element('div', {'class': 'invited_text text11 success', 'text': 'Invited'}).inject(el);
		
		return el;
	}
};var UserMapMenuSection = new Class({
	Extends: Base,
	
	EventHandlers: [
	                'userLocationUpdated'
	                ],
	
	init: function(options){
	
		this.user = options.user;
		
		this.isSelf = this.user.user_id == this.getPrivateUser().user_id;
		
		this.location = this.getUserLocation(this.user.username);
		
		this.hasLocation = $defined(this.location) && this.isSelf;
		
		this.createMap();
	
	},
	
	createMap: function(){
		
		this.wrapper = new Element('div');
		this.map = this.generateMap().inject(this.wrapper);
		this.showLocationLabel();
	},
	
	generateMap: function(){
		
		if (this.hasLocation)
		{
	
			this.mapWrapper = new Element('div', {'class': 'gmap_content'});
			
			var map = new Element('div', {'class': 'map_section'}).adopt(
				new Element('div', {'class': 'gmap'}).adopt(
					new Element('div', {'class': 'gmap_border'}),
					this.mapWrapper
				)
			);
		
			this.gmap = new GMap2(this.mapWrapper, {
				size: new GSize(156, 156)
			});
			
			this.gmap.setCenter(new GLatLng(this.location.lat, this.location.lon), 11);
			
			//create marker
			this.profilePic = ItemUtility.createProfilePic(this.user, 60);
			this.marker = new Element('div', {'class': 'map_marker'}).adopt(
				new Element('div', {'class': 'profile_pic_wrapper'}).adopt(
					this.profilePic
				),
				new Element('div', {'class': 'point_down'}),
				new Element('div', {'class': 'spot'})
			).inject(map);
			
			return map;
		
		}
		else
		{
			return ItemUtility.createProfilePic(this.user, 100);
			
		}
		
	},
	
	userLocationUpdated: function(data){
		if (!$defined(data.username) || data.username != this.user.username)
			return;
		
		this.location = data.location;
		this.hasLocation = data.location_enabled == 1;
		var map = this.generateMap();
		map.replaces(this.map);
		this.map = map;
		
		this.showLocationLabel();
	},
	
	showLocationLabel: function(){
		if (this.hasLocation)
		{
			if ($defined(this.label))
				this.label.destroy();
			
			var locationLabel = DataUtility.getGeoLabel(this.location);
			var locationStr = DataUtility.getGeoLocality(this.location);
			
			if (locationLabel)
				locationStr = locationLabel + ', ' + locationStr;
				
			
			this.label = new Element('div', {'class': 'text_section light1 text11'}).adopt(
				new Element('span', {'class': 'bold', 'text': 'Location: '}),
				new Element('span', {'html': TextUtility.unescape(locationStr)})
			);
			
			this.label.inject(this.wrapper, 'top');
		}
		else
		{
			if ($defined(this.label))
				this.label.destroy();
		}
	},
	
	toElement: function(){
		return this.wrapper;
	}
	
});var Login = new Class({
	Extends: Base,
	
	EventHandlers: [
	                'userSwitched',
	                'loginShow',
	                'signupShow',
	                'logoutShow',
	                'passwordResetRequestShow'
	],
	
	userSwitched: function(){
		if (this.isLoggedIn())
			this.userLoggedIn();
		else
			this.userLoggedOut();
	},
	
	userLoggedIn: function(){
		this.closeLoginPopup();
		this.closePasswordResetRequestPopup();
		this.closePasswordResetPopup();
		this.closeSignupPopup();
	},
	
	userLoggedOut: function(){
		if (!this.checkCookie())
			this.loginShow();
	},
	
	checkCookie: function(){
		var destination = DataUtility.getCookie('destination');
		var token = DataUtility.getCookie('password_reset_token');
		var email = DataUtility.getCookie('password_reset_email');
		
		if (!$defined(destination))
			return false;
		
		
		if (destination == 'password_reset' && $defined(token) && $defined(email))
		{
			this.passwordResetShow(token, email);
			return true;
		}
		else if (destination == 'signup')
		{
			var access_key = DataUtility.getCookie('invite_access_key');
			if (!$defined(access_key))
				access_key = '';
			
			this.signupShow(access_key);
			return true;
		}
		else if (destination == 'email_block')
		{
			var token = DataUtility.getCookie('email_block_token');
			var email = DataUtility.getCookie('email_block_email');
			
			this.emailBlockShow(token, email);
			return true;
			
		}
		
		return false;
		
	},
	
	loginShow: function(){
		if ($defined(this.loginPopup))
		{
			this.loginPopup._select();
			return;
		}
		
		this.createLoginPopup();
	},
	
	closeLoginPopup: function(){
		if ($defined(this.loginPopup))
			this.loginPopup.close();
	},
	
	createLoginPopup: function(){
		this.loginPopup = new Popup({
			size: {x: 350, y: 226},
			resizable: false,
			dockable: false,
			className: 'login',
			onClose: this.destroyLoginPopup.bind(this)
		});
		
		var nav = new Nav({
			iconOptions: {iconName: 'power'},
			displayName: 'Log In to Pip.io',
			closable: true
		});
		
		var content = new LoginPopupContent({});
		
		this.loginPopup.addContent('login', nav, content);

	},
	
	destroyLoginPopup: function(){
		this.loginPopup = null;
	},
	
	logoutShow: function(){
		if ($defined(this.logoutPopup))
		{
			this.logoutPopup._select();
			return;
		}
		
		this.createLogoutPopup();
	},
	
	closeLogoutPopup: function(){
		if ($defined(this.logoutPopup))
			this.logoutPopup.close();
	},
	
	createLogoutPopup: function(){
		this.logoutPopup = new Popup({
			size: {x: 350, y: 70},
			resizable: false,
			dockable: false,
			onClose: this.destroyLogoutPopup.bind(this)
		});
		
		var nav = new Nav({
			iconOptions: {iconName: 'power'},
			displayName: 'Log Out ' + TextUtility.unescapeQuotes(this.getPrivateUser().fullname),
			closable: true
		});
		
		var content = new LogoutPopupContent({});
		
		this.logoutPopup.addContent('logout', nav, content);

	},
	
	destroyLogoutPopup: function(){
		this.logoutPopup = null;
	},
	
	passwordResetRequestShow: function(){
		if ($defined(this.passwordResetRequestPopup))
		{
			this.passwordResetRequestPopup.close();
		}
		
		this.createPasswordResetRequestPopup();
	},
	
	closePasswordResetRequestPopup: function(){
		if ($defined(this.passwordResetRequestPopup))
			this.passwordResetRequestPopup.close();
	},
	
	createPasswordResetRequestPopup: function(){
		this.passwordResetRequestPopup = new Popup({
			size: {x: 350, y: 112},
			resizable: false,
			dockable: false,
			className: 'login',
			onClose: this.destroyPasswordResetRequestPopup.bind(this)
		});
		
		var nav = new Nav({
			iconOptions: {iconName: 'pipio'},
			displayName: 'Password Reset',
			closable: true
		});
		
		var content = new PasswordResetRequestPopupContent({});
		
		this.passwordResetRequestPopup.addContent('login', nav, content);

	},
	
	destroyPasswordResetRequestPopup: function(){
		this.passwordResetRequestPopup = null;
	},
	
	passwordResetShow: function(token, email){
		if ($defined(this.passwordResetPopup))
		{
			this.passwordResetPopup.close();
		}
		
		this.createPasswordResetPopup(token, email);
	},
	
	closePasswordResetPopup: function(){
		if ($defined(this.passwordResetPopup))
			this.passwordResetPopup.close();
	},
	
	createPasswordResetPopup: function(token, email){
		this.passwordResetPopup = new Popup({
			size: {x: 350, y: 142},
			resizable: false,
			dockable: false,
			className: 'login',
			onClose: this.destroyPasswordResetPopup.bind(this)
		});
		
		var nav = new Nav({
			iconOptions: {iconName: 'pipio'},
			displayName: 'Password Reset',
			closable: true
		});
		
		var content = new PasswordResetPopupContent({
			token: token,
			email: email
		});
		
		this.passwordResetPopup.addContent('login', nav, content);

	},
	
	destroyPasswordResetPopup: function(){
		this.passwordResetPopup = null;
	},
	
	signupShow: function(access_key){
		if ($defined(this.signupPopup))
		{
			this.signupPopup._select();
			return;
		}
		
		this.createSignupPopup(access_key);
	},
	
	closeSignupPopup: function(){
		if ($defined(this.signupPopup))
			this.signupPopup.close();
	},
	
	createSignupPopup: function(access_key){
		this.signupPopup = new Popup({
			size: {x: 350, y: 366},
			resizable: false,
			dockable: false,
			className: 'login',
			onClose: this.destroySignupPopup.bind(this)
		});
		
		var nav = new Nav({
			iconOptions: {iconName: 'pipio'},
			displayName: 'Sign Up for Pip.io',
			closable: true
		});
		
		if (!$defined(access_key))
			access_key = '';
		var content = new SignupPopupContent({access_key: access_key});
		
		this.signupPopup.addContent('signup', nav, content);

	},
	
	destroySignupPopup: function(){
		this.signupPopup = null;
	},
	
	emailBlockShow: function(token, email){
		if ($defined(this.emailBlockPopup))
		{
			this.emailBlockPopup.close();
		}
		
		this.createEmailBlockPopup(token, email);
	},
	
	closeEmailBlockPopup: function(){
		if ($defined(this.signupPopup))
			this.emailBlockPopup.close();
	},
	
	createEmailBlockPopup: function(token, email){
		this.emailBlockPopup = new Popup({
			size: {x: 350, y: 110},
			resizable: false,
			dockable: false,
			className: 'login',
			onClose: this.destroyEmailBlockPopup.bind(this)
		});
		
		var nav = new Nav({
			iconOptions: {iconName: 'pipio'},
			displayName: 'Email Settings',
			closable: true
		});
		
		var content = new EmailBlockPopupContent({token: token, email: email});
		
		this.emailBlockPopup.addContent('emailblock', nav, content);

	},
	
	destroyEmailBlockPopup: function(){
		this.emailBlockPopup = null;
	}
	
});var EmailBlockPopupContent = new Class({
	Extends: PopupContent,
	
	strings: {
		emailBlockMessage: 'Stop all Pip.io emails for:'
	},
	
	onBeforeInit: function(options){
		
		this.token = options.token;
		this.email = options.email;
		
		return options;
	},
	
	onInit: function(){
		///MAIN SECTION OF POPUP///
		new Element('div', {'class': 'text_section centered light', 'text': TextUtility.unescape(this.strings.emailBlockMessage)}).inject(this.content);
		new Element('div', {'class': 'text_section centered light', 'text': TextUtility.unescape(this.email)}).inject(this.content);
		
		
		////THIS BLOCK CONTAINS THE FOOTER OF THE POPUP, WHICH HAS ACTION BUTTONS AND POSSIBLY A MESSAGE ELEMENT///
		this.closeButton = new ButtonMedium({
			displayName: 'Cancel',
			className: 'dark',
			action: 'cross'
		});
		$(this.closeButton).addEvent('click', this.closeContent.bind(this));
		
		this.actionButton = new ButtonMedium({
			displayName: 'Confirm',
			className: 'dark',
			action: 'check'
		});
		$(this.actionButton).addEvent('click', this.blockEmail.bind(this));
		
		this.actions = new Element('div', {'class': 'actions'}).adopt(
			$(this.actionButton),
			$(this.closeButton)
		).inject(this.content);
		
	},
	
	blockEmail: function(){
	
		
		var params = {
				'token': this.token,
				'email': this.email
		};
		
		this.call('pipio', 'email_block', params, this.resetRequestSuccess.bind(this), this.resetRequestFail.bind(this));
		
		this.actionButton.showProgress();
	},
	
	resetRequestSuccess: function(data){
		//show popup
		var name = 'email_block';
		var title = 'Email Block Successful';
		var message = 'You will no longer receive emails from Pip.io';

		this.fireEvent('showAlert', name, title, message);
		
		this.actionButton.hideProgress();
		this.closeContent();
	},
	
	resetRequestFail: function(){
		
		var name = 'email_block';
		var title = 'Email Block Failed';
		var message = 'There was an error processing your request.  Please contact support@pip.io';

		this.fireEvent('showAlert', name, title, message);
		
		this.actionButton.hideProgress();
	}
	
});var LoginPopupContent = new Class({
	Extends: PopupContent,
	
	strings: {
		loginMessage: 'Log in to Pip.io',
		signupMessage: 'Don\'t have an account?   Sign up now!',
		usernameLabel: 'Username or email:',
		passwordLabel: 'Password:',
		rememberMeLabel: 'Remember me:',
		errorMessage: 'Incorrect login',
		forgotPasswordMessage: 'Forgot your password?'
	},
	
	onInit: function(){
	
		new Element('div', {'class': 'text_section centered light', 'text': this.strings.loginMessage}).inject(this.content);
		var signupMsg = new Element('div', {'class': 'text_section centered short text11 light3 clickable', 'text': this.strings.signupMessage}).inject(this.content);
		signupMsg.addEvent('click', this.fireEvent.bind(this, ['signupShow']));
		
		this.usernameInput = new Element('input', {'type': 'text', 'maxlength': '32'});
		this.usernameInput.addEvent('keyup', this.inputKeyUp.bindWithEvent(this));
		
		new Element('div', {'class': 'input_section'}).adopt(
			new Element('div', {'class': 'label light', 'text': this.strings.usernameLabel}),
			new Element('div', {'class': 'textarea_wrapper'}).adopt(
				this.usernameInput
			)
		).inject(this.content);
		
		this.passwordInput = new Element('input', {'type': 'password', 'maxlength': '32'});
		this.passwordInput.addEvent('keyup', this.inputKeyUp.bindWithEvent(this));
		
		new Element('div', {'class': 'input_section'}).adopt(
			new Element('div', {'class': 'label light', 'text': this.strings.passwordLabel}),
			new Element('div', {'class': 'textarea_wrapper'}).adopt(
				this.passwordInput
			)
		).inject(this.content);
		
		this.rememberToggle = new Toggle(true);
		new Element('div', {'class': 'input_section'}).adopt(
				new Element('div', {'class': 'label light', 'text': this.strings.rememberMeLabel}),
				$(this.rememberToggle)
			).inject(this.content);
		
		var forgotPasswordMsg = new Element('div', {'class': 'text_section centered short text11 light3 clickable', 'text': this.strings.forgotPasswordMessage}).inject(this.content);
		forgotPasswordMsg.addEvent('click', this.fireEvent.bind(this, ['passwordResetRequestShow']));
		
		//ACtIONS
		
		this.closeButton = new ButtonMedium({
			displayName: 'Cancel',
			className: 'dark',
			action: 'cross'
		});
		$(this.closeButton).addEvent('click', this.closeContent.bind(this));
		
		this.actionButton = new ButtonMedium({
			displayName: 'Log In',
			className: 'dark',
			action: 'check'
		});
		$(this.actionButton).addEvent('click', this.login.bind(this));
		
		this.message = new Element('div', {'class': 'message error'});
		
		this.actions = new Element('div', {'class': 'actions'}).adopt(
			this.message,
			$(this.actionButton),
			$(this.closeButton)
		).inject(this.content);
		
	},
	
	login: function(){
		var username = this.usernameInput.value.trim();
		var password = this.passwordInput.value.trim();
		var remember_me = this.rememberToggle.toInt();
		
		//only if it has files
		//TODO: auto detect this based on a bit in the attachment obj
	
		var params = {
				'username': username,
				'password': password,
				'remember_me': remember_me
		};
		
		this.call('pipio', 'user_login', params, this.loginSuccess.bind(this), this.loginFail.bind(this));
		
		this.actionButton.showProgress();
		this.message.empty();
	},
	
	loginSuccess: function(data){
		this.fireEvent('userDataInit', data);
		this.actionButton.hideProgress();
		this.closeContent();
	},
	
	loginFail: function(){
		this.actionButton.hideProgress();
		this.message.set('text', this.strings.errorMessage);
	},
	
	inputKeyUp: function(e){
		if (e.key == 'enter')
		{
			if (this.usernameInput.value.trim() != '' && this.passwordInput.value.trim() != '')
				this.login();
		}
	},
	
	onShow: function(){
		this.focus();
	},
	
	focus: function(){
		this.usernameInput.focus.delay(500, this.usernameInput);
	}
	
});var LogoutPopupContent = new Class({
	Extends: PopupContent,
	
	strings: {
		logoutMessage: 'Are you sure you want to log out?'
	},
	
		onInit: function(){
	
		new Element('div', {'class': 'text_section centered light', 'text': this.strings.logoutMessage}).inject(this.content);
		
		
		//ACtIONS
		
		this.closeButton = new ButtonMedium({
			displayName: 'Cancel',
			className: 'dark',
			action: 'cross'
		});
		$(this.closeButton).addEvent('click', this.closeContent.bind(this));
		
		this.actionButton = new ButtonMedium({
			displayName: 'Log Out',
			className: 'dark',
			action: 'check'
		});
		$(this.actionButton).addEvent('click', this.logout.bind(this));
		
		this.message = new Element('div', {'class': 'message'});
		
		this.actions = new Element('div', {'class': 'actions'}).adopt(
			this.message,
			$(this.actionButton),
			$(this.closeButton)
		).inject(this.content);
		
	},
	
	logout: function(){
		
		this.call('pipio', 'user_logout', null, this.logoutSuccess.bind(this), this.logoutFail.bind(this));
		
		this.actionButton.showProgress();
	},
	
	logoutSuccess: function(data){
		this.fireEvent('userDataInit', data);
		this.actionButton.hideProgress();
		this.closeContent();
	},
	
	logoutFail: function(){
		this.actionButton.hideProgress();
		this.closeContent();
	}
	
});var PasswordResetPopupContent = new Class({
	Extends: PopupContent,
	
	strings: {
		passwordResetMessage: 'Reset Pip.io password for {0}',
		passwordLabel: 'New password:',
		verifyPasswordLabel: 'Verify password'
	},
	
	onBeforeInit: function(options){
		
		this.token = options.token;
		this.email = options.email;
		
		return options;
	},
	
	onInit: function(){
		///MAIN SECTION OF POPUP///
		new Element('div', {'class': 'text_section centered light', 'text': String.format(this.strings.passwordResetMessage, TextUtility.unescape(this.email))}).inject(this.content);
		
		
		this.passwordInput = new Element('input', {'type': 'password', 'maxlength': '32'});
		
		new Element('div', {'class': 'input_section'}).adopt(
			new Element('div', {'class': 'label light', 'text': this.strings.passwordLabel}),
			new Element('div', {'class': 'textarea_wrapper'}).adopt(
				this.passwordInput
			)
		).inject(this.content);
		
		this.verifyPasswordInput = new Element('input', {'type': 'password', 'maxlength': '32'});
		
		new Element('div', {'class': 'input_section'}).adopt(
			new Element('div', {'class': 'label light', 'text': this.strings.verifyPasswordLabel}),
			new Element('div', {'class': 'textarea_wrapper'}).adopt(
				this.verifyPasswordInput
			)
		).inject(this.content);
		
		////THIS BLOCK CONTAINS THE FOOTER OF THE POPUP, WHICH HAS ACTION BUTTONS AND POSSIBLY A MESSAGE ELEMENT///
		this.closeButton = new ButtonMedium({
			displayName: 'Cancel',
			className: 'dark',
			action: 'cross'
		});
		$(this.closeButton).addEvent('click', this.closeContent.bind(this));
		
		this.actionButton = new ButtonMedium({
			displayName: 'Set New Password',
			className: 'dark',
			action: 'check'
		});
		$(this.actionButton).addEvent('click', this.resetRequest.bind(this));
		
		this.actions = new Element('div', {'class': 'actions'}).adopt(
			$(this.actionButton),
			$(this.closeButton)
		).inject(this.content);
		
	},
	
	resetRequest: function(){
		var new_password = this.passwordInput.value.trim();
		var verify_password = this.verifyPasswordInput.value.trim();
		
		//validate
		if (new_password == '' || verify_password == '')
		{
			var name = 'reset_request';
			var title = 'Password Reset Request Failed';
			var message = 'Please enter a new password';

			this.fireEvent('showAlert', name, title, message);
			return;
		}
		
		if (new_password != verify_password)
		{
			var name = 'reset_request';
			var title = 'Password Reset Request Failed';
			var message = 'The passwords you entered do not match';

			this.fireEvent('showAlert', name, title, message);
			return;
		}
		
		var params = {
				'token': this.token,
				'email': this.email,
				'new_password': new_password,
				'verify_password': verify_password
		};
		
		this.call('pipio', 'user_password_reset', params, this.resetRequestSuccess.bind(this), this.resetRequestFail.bind(this));
		
		this.actionButton.showProgress();
	},
	
	resetRequestSuccess: function(data){
		//show popup
		var name = 'reset_request';
		var title = 'Password Reset Successful';
		var message = 'Your Pip.io password has been successfully reset';

		//show login box first
		this.fireEvent('loginShow');
		
		this.fireEvent('showAlert', name, title, message);
		
		this.actionButton.hideProgress();
		this.closeContent();
	},
	
	resetRequestFail: function(){
		
		var name = 'reset_request';
		var title = 'Password Reset Failed';
		var message = 'There was an error resetting your password.  Please contact support@pip.io';

		this.fireEvent('showAlert', name, title, message);
		
		this.actionButton.hideProgress();
	},
	
	onShow: function(){
		this.focus();
	},
	
	focus: function(){
		this.passwordInput.focus.delay(500, this.passwordInput);
	}
	
});var PasswordResetRequestPopupContent = new Class({
	Extends: PopupContent,
	
	strings: {
		passwordResetMessage: 'Forgot your password to Pip.io?',
		usernameLabel: 'Username or email:',
		forgotPasswordMessage: 'Forgot your password?'
	},
	
	onInit: function(){
		///MAIN SECTION OF POPUP///
		new Element('div', {'class': 'text_section centered light', 'text': this.strings.passwordResetMessage}).inject(this.content);
		
		this.usernameInput = new Element('input', {'type': 'text', 'maxlength': '32'});
		this.usernameInput.addEvent('keyup', this.inputKeyUp.bindWithEvent(this));
		
		new Element('div', {'class': 'input_section'}).adopt(
			new Element('div', {'class': 'label light', 'text': this.strings.usernameLabel}),
			new Element('div', {'class': 'textarea_wrapper'}).adopt(
				this.usernameInput
			)
		).inject(this.content);
		
		////THIS BLOCK CONTAINS THE FOOTER OF THE POPUP, WHICH HAS ACTION BUTTONS AND POSSIBLY A MESSAGE ELEMENT///
		this.closeButton = new ButtonMedium({
			displayName: 'Cancel',
			className: 'dark',
			action: 'cross'
		});
		$(this.closeButton).addEvent('click', this.closeContent.bind(this));
		
		this.actionButton = new ButtonMedium({
			displayName: 'Request Reset',
			className: 'dark',
			action: 'check'
		});
		$(this.actionButton).addEvent('click', this.resetRequest.bind(this));
		
		this.actions = new Element('div', {'class': 'actions'}).adopt(
			$(this.actionButton),
			$(this.closeButton)
		).inject(this.content);
		
	},
	
	resetRequest: function(){
		var username = this.usernameInput.value.trim();
		
		//validate
		if (username == '')
		{
			var name = 'reset_request';
			var title = 'Password Reset Request Failed';
			var message = 'Please enter your username or email';

			this.fireEvent('showAlert', name, title, message);
			return;
		}
		
		var params = {
				'username': username
		};
		
		this.call('pipio', 'user_password_resetrequest', params, this.resetRequestSuccess.bind(this), this.resetRequestFail.bind(this));
		
		this.actionButton.showProgress();
	},
	
	resetRequestSuccess: function(data){
		//show popup
		var name = 'reset_request';
		var title = 'Password Reset Request Sent';
		var message = 'Please check your email for directions to reset your password';

		this.fireEvent('showAlert', name, title, message);
		
		this.actionButton.hideProgress();
		this.closeContent();
	},
	
	resetRequestFail: function(){
		
		var name = 'reset_request';
		var title = 'Password Reset Request Failed';
		var message = 'We could not locate your account, please make sure the username or email you entered is correct';

		this.fireEvent('showAlert', name, title, message);
		
		this.actionButton.hideProgress();
	},
	
	inputKeyUp: function(e){
		if (e.key == 'enter')
		{
			if (this.usernameInput.value.trim() != '')
				this.resetRequest();
		}
	},
	
	onShow: function(){
		this.focus();
	},
	
	focus: function(){
		this.usernameInput.focus.delay(500, this.usernameInput);
	}
	
});var SignupPopupContent = new Class({
	Extends: PopupContent,
	
	strings: {
		signUpMessage: 'Sign up for Pip.io',
		signUpDetailMessage: 'It only takes a few seconds to create an account!',
		usernameLabel: 'Username:',
		passwordLabel: 'Password:',
		firstNameLabel: 'First Name:',
		lastNameLabel: 'Last Name:',
		emailLabel: 'Email:',
		dobLabel: 'Date of Birth:',
		inviteCodeLabel: 'Invite Code:',
		inviteCodeMessage: 'Leave this field blank if you do not have an invite code'
	},
	
	onBeforeInit: function(options){
		this.access_key = options.access_key;
		return options;
	},
	
	onInit: function(){
	
		new Element('div', {'class': 'text_section centered light', 'text': this.strings.signUpMessage}).inject(this.content);
		new Element('div', {'class': 'text_section centered light short text11 light3', 'text': this.strings.signUpDetailMessage}).inject(this.content);
		
		
		this.firstNameInput = new Element('input', {'type': 'text', 'maxlength': '50'});
		
		new Element('div', {'class': 'input_section'}).adopt(
			new Element('div', {'class': 'label light', 'text': this.strings.firstNameLabel}),
			new Element('div', {'class': 'textarea_wrapper'}).adopt(
				this.firstNameInput
			)
		).inject(this.content);
		
		this.lastNameInput = new Element('input', {'type': 'text', 'maxlength': '50'});
		
		new Element('div', {'class': 'input_section'}).adopt(
			new Element('div', {'class': 'label light', 'text': this.strings.lastNameLabel}),
			new Element('div', {'class': 'textarea_wrapper'}).adopt(
				this.lastNameInput
			)
		).inject(this.content);
		
		this.emailInput = new Element('input', {'type': 'text', 'maxlength': '32'});
		
		new Element('div', {'class': 'input_section'}).adopt(
			new Element('div', {'class': 'label light', 'text': this.strings.emailLabel}),
			new Element('div', {'class': 'textarea_wrapper'}).adopt(
				this.emailInput
			)
		).inject(this.content);
		
		this.usernameInput = new Element('input', {'type': 'text', 'maxlength': '32'});
		
		new Element('div', {'class': 'input_section'}).adopt(
			new Element('div', {'class': 'label light', 'text': this.strings.usernameLabel}),
			new Element('div', {'class': 'textarea_wrapper'}).adopt(
				this.usernameInput
			)
		).inject(this.content);
		
		this.passwordInput = new Element('input', {'type': 'password', 'maxlength': '32'});
		
		new Element('div', {'class': 'input_section'}).adopt(
			new Element('div', {'class': 'label light', 'text': this.strings.passwordLabel}),
			new Element('div', {'class': 'textarea_wrapper'}).adopt(
				this.passwordInput
			)
		).inject(this.content);
		
		this.monthInput = new Element('input', {'type': 'text', 'maxlength': '2', 'value': 'MM'});
		this.dayInput = new Element('input', {'type': 'text', 'maxlength': '2', 'value': 'DD'});
		this.yearInput = new Element('input', {'type': 'text', 'maxlength': '4', 'value': 'YYYY'});
		//event to clear defaults
		this.monthInput.addEvent('click', this.clearField.bind(this, [this.monthInput, 'MM']));
		this.dayInput.addEvent('click', this.clearField.bind(this, [this.dayInput, 'DD']));
		this.yearInput.addEvent('click', this.clearField.bind(this, [this.yearInput, 'YYYY']));
		
		new Element('div', {'class': 'input_section'}).adopt(
				new Element('div', {'class': 'label light', 'text': this.strings.dobLabel}),
				new Element('div', {'class': 'textarea_wrapper month'}).adopt(
					this.monthInput
				),
				new Element('div', {'class': 'textarea_wrapper day'}).adopt(
					this.dayInput
				),
				new Element('div', {'class': 'textarea_wrapper year'}).adopt(
					this.yearInput
				)
			).inject(this.content);
		
		
		
		this.inviteCodeInput = new Element('input', {'type': 'text', 'maxlength': '10', 'value': this.access_key});
		
		new Element('div', {'class': 'input_section'}).adopt(
			new Element('div', {'class': 'label light', 'text': this.strings.inviteCodeLabel}),
			new Element('div', {'class': 'textarea_wrapper'}).adopt(
				this.inviteCodeInput
			)
		).inject(this.content);
		
		new Element('div', {'class': 'text_section centered light short text11 light3', 'text': this.strings.inviteCodeMessage}).inject(this.content);
		
		
		
		//ACtIONS
		
		this.closeButton = new ButtonMedium({
			displayName: 'Cancel',
			className: 'dark',
			action: 'cross'
		});
		$(this.closeButton).addEvent('click', this.closeContent.bind(this));
		
		this.actionButton = new ButtonMedium({
			displayName: 'Sign Up',
			className: 'dark',
			action: 'check'
		});
		$(this.actionButton).addEvent('click', this.signup.bind(this));
		
		this.message = new Element('div', {'class': 'message success'});
		
		this.actions = new Element('div', {'class': 'actions'}).adopt(
			this.message,
			$(this.actionButton),
			$(this.closeButton)
		).inject(this.content);
		
	},
	
	clearField: function(el, def){
		if (el.value == def)
			el.value = '';
	},
	
	signup: function(){
		
		var first_name = this.firstNameInput.value.trim();
		var last_name = this.lastNameInput.value.trim();
		var username = this.usernameInput.value.trim();
		var password = this.passwordInput.value.trim();
		var email = this.emailInput.value.trim();
		var dob_month = this.monthInput.value.trim();
		var dob_day = this.dayInput.value.trim();
		var dob_year = this.yearInput.value.trim();
		var access_key = this.inviteCodeInput.value.trim();
		
		//validate
		if (first_name == '' || last_name == '')
		{
			this.signupFail({message: 'Please enter your name'});
			return;
		}
		
		if (email == '')
		{
			this.signupFail({message: 'Please enter your email'});
			return;
		}
		
		if (username == '')
		{
			this.signupFail({message: 'Please enter a username'});
			return;
		}
		
		if (password == '')
		{
			this.signupFail({message: 'Please enter a password'});
			return;
		}
		
		if (dob_month != parseInt(dob_month) || dob_month < 1 || dob_month > 12)
		{
			this.signupFail({message: 'Please enter a valid month'});
			return;
		}
		
		if (dob_day != parseInt(dob_day) || dob_day < 1 || dob_day > 31)
		{
			this.signupFail({message: 'Please enter a valid day'});
			return;
		}
		
		if (dob_year != parseInt(dob_year) || dob_year < 1900 || dob_year > 2010)
		{
			this.signupFail({message: 'Please enter a valid year'});
			return;
		}
		
		
		var params = {
				'first_name': first_name,
				'last_name': last_name,
				'username': username,
				'password': password,
				'email': email,
				'dob_month': dob_month,
				'dob_day': dob_day,
				'dob_year': dob_year,
				'access_key': access_key
		};
		
		this.call('pipio', 'user_register', params, this.signupSuccess.bind(this), this.signupFail.bind(this));
		
		this.actionButton.showProgress();
		this.message.set('text', 'please wait...');
	},
	
	signupSuccess: function(data){
		this.fireEvent('userDataInit', data);
		this.actionButton.hideProgress();
		this.closeContent();
		this.message.set('success!');
	},
	
	signupFail: function(status){
		this.actionButton.hideProgress();

		var name = 'user_register';
		var title = 'Error';
		var message = status.message;
		this.message.empty();

		this.fireEvent('showAlert', name, title, message);
	},
	
	onShow: function(){
		this.focus();
	},
	
	focus: function(){
		this.firstNameInput.focus.delay(500, this.firstNameInput);
	}
	
});var Notifications = new Class({
	Extends: Base,
	
	EventHandlers: [
	                'userSwitched',
	                'notificationAdd',
	                'titleMessageAdd',
	                'titleMessageDelete'
	                ],
	
	init: function(){
		this.notifications = $H();
		this.timers = $H();
		this.messages = []; //title messages
		this.messageTimer = 0; //timer for flashing messages
		
		this.createNotifications();
	},
	
	titleMessageAdd: function(message){
		if (this.messages.contains(message))
			return;
		
		this.messages.push(message);
		this.titleMessageCheck();
	},
	
	titleMessageDelete: function(message){
		if (this.messages.contains(message))
			this.messages.erase(message);
		
		this.titleMessageCheck();
	},
	
	titleMessageCheck: function(){
		if (this.messages.length > 0 && this.messageTimer == 0)//no timer on, start it
		{
			this.messageTimer = this.titleMessageRotate.periodical(2000, this);
			
		}
		else if (this.messages.length == 0 && this.messageTimer != 0) // empty, timer still on, stop it
		{
			$clear(this.messageTimer);
			document.title = this.getPageTitle();
		}
	},
	
	titleMessageRotate: function(){
		if (this.messages.length == 0)
			return;
		
		if (!$defined(this.currentTitle))
			this.currentTitle = this.getPageTitle();
		
		if (this.getPageTitle() == this.currentTitle)
		{
			//switch to next message
			var msg = this.messages.shift();
			this.messages.push(msg);
			
			document.title = msg;
			this.currentTitle = msg;
		}
		else
		{
			//switch to default
			document.title = this.getPageTitle();
			this.currentTitle = this.getPageTitle();
		}
	},
	
	userSwitched: function(){
		if (this.isLoggedIn())
			this.userLoggedIn();
		else
			this.userLoggedOut();
	},
	
	userLoggedIn: function(){
		this.notifications = $H();
	},
	
	userLoggedOut: function(){
		this.closeAll();
		this.notifications = $H();
	},
	
	closeAll: function(){
		this.notifications.getKeys().each(function(name){this.close(name);}, this);
	},
	
	close: function(name){
		if (!this.notifications.has(name))
			return;
		
		var notif = this.notifications.get(name);
		
		DomUtility.fadeOutDestroy($(notif));
		
		this.notifications.erase(name);
		
		$clear(this.timers.get(name));
		this.timers.erase(name);
		
		delete(notif);
		
		this.checkShow();
	},
	
	notificationAdd: function(name, notif){
		if (this.notifications.has(name))
			return;
		
		$(notif).inject(this.menu);
		DomUtility.fadeIn($(notif), 1000);
		
		this.notifications.set(name, notif);
		
		notif.onClose = this.close.bind(this, name);
		
		var timer = this.close.delay(notif.timeout, this, [name]);
		
		this.timers.set(name, timer);
		
		this.checkShow();
	},
	
	checkShow: function(){
		if (this.notifications.getLength() > 0)
			DomUtility.show(this.menu);
		else
			DomUtility.hide(this.menu);
	},
	
	createNotifications: function(){
		this.menu = new Element('div', {'class': 'menu flattop'});
		this.menu.inject('notifications');
		
		this.checkShow();
	}
	
});var Notification = new Class({
	Extends: Base,
	
	init: function(options){
		//this is for subclasses that need to process options before main init
		if ($defined(this.onBeforeInit))
			options = this.onBeforeInit(options);
	
		this.iconOptions = options.iconOptions;
		
		this.timeout = options.timeout || 5000;
		
		this.hasActions = options.hasActions || false;
		
		this.createNotification();
		
		//again, for subclasses that need to process stuff after init
		if ($defined(this.onInit))
			this.onInit();
	},
	
	createNotification: function(){
		this.notif = new Element('div', {'class': 'notification'});
		
		this.icon = new Icon20(this.iconOptions);
		$(this.icon).inject(this.notif);
		
		this.closeButton = new Element('div', {'class': 'button_nav show'}).adopt(
			new Element('div', {'class': 'action close'})
		).inject(this.notif);
		this.closeButton.addEvent('click', this.closeNotification.bind(this));
		
		this.getText().inject(this.notif);
		
		if (this.hasActions)
		{
			this.actions = new Element('div', {'class': 'actions'}).inject(this.notif);
		}
	},
	
	getText: function(){
		return new Element('div', {'class': 'notification_text text11 light1'});
	},
	
	closeNotification: function(){
		if ($defined(this.onClose))
			this.onClose();
	},
	
	toElement: function(){
		return this.notif;
	}
	
	
});var ShareBox = new Class({
	Extends: Base,
	
	EventHandlers: [
	                'userSwitched',
	                'shareBoxShow',
	                'replyShareBoxShow',
	                'forwardShareBoxShow',
	                'roomShareBoxShow'
	],
	
	init: function(){
		this.shareboxes = $H();
		
	},
	
	userSwitched: function(){
		if (!this.isLoggedIn())
			this.userLoggedOut();
	},
	
	userLoggedOut: function(){
		//close all share box
		this.shareboxes.each(function(sharebox, key){
			sharebox.close();
			this.shareboxes.erase(key);
		}, this);
		
		this.shareboxes = $H();
	},
	
	forwardShareBoxShow: function(forward_id, username, forward_body){
		if ($defined(this.sharebox))
		{
			this.sharebox.close();
		}
		
		this.createForwardShareBox(forward_id, username, forward_body);
	},
	
	createForwardShareBox: function(forward_id, username, forward_body){
		this.sharebox = new Popup({
			size: {x: 500, y: 160},
			resizable: false,
			dockable: false,
			className: 'sharebox',
			onClose: this.destroyShareBox.bind(this)
		});
		
		var displayName = 'Forward a Post';
		
		var nav = new Nav({
			iconOptions: {user: this.getPrivateUser()},
			displayName: displayName,
			closable: true
		});
		
		
		var forward_user = this.getUser(username);
		if (!$defined(forward_user))
			forward_user = this.getRoom(username);
		
		var content = new ShareBoxPopupContent({
			user: this.getPrivateUser(),
			forward_id: forward_id,
			forward_user: forward_user,
			forward_body: forward_body
		});
		
		this.sharebox.addContent('sharebox', nav, content);

	},
	
	replyShareBoxShow: function(reply_id){
		if ($defined(this.sharebox))
		{
			this.sharebox.close();
		}
		
		this.createReplyShareBox(reply_id);
	},
	
	createReplyShareBox: function(reply_id){
		this.sharebox = new Popup({
			size: {x: 500, y: 160},
			resizable: false,
			dockable: false,
			className: 'sharebox',
			onClose: this.destroyShareBox.bind(this)
		});
		
		var displayName = 'Reply to a Post';
		
		var nav = new Nav({
			iconOptions: {user: this.getPrivateUser()},
			displayName: displayName,
			closable: true
		});
		
		var content = new ShareBoxPopupContent({
			user: this.getPrivateUser(),
			reply_id: reply_id
		});
		
		this.sharebox.addContent('sharebox', nav, content);

	},
	
	shareBoxShow: function(user){
		if ($defined(this.sharebox))
		{
			this.sharebox.close();
		}
		
		this.createShareBox(user);
	},
	
	createShareBox: function(user){
		this.sharebox = new Popup({
			size: {x: 500, y: 160},
			resizable: false,
			dockable: false,
			className: 'sharebox',
			onClose: this.destroyShareBox.bind(this)
		});
		
		
		var displayName = (user.user_id == this.getPrivateUser().user_id)? 'Write in Your Stream': 'Write in ' + user.first_name + '\'s Stream';
		
		var nav = new Nav({
			iconOptions: {user: user},
			displayName: displayName,
			closable: true
		});
		
		var content = new ShareBoxPopupContent({
			user: user
		});
		
		this.sharebox.addContent('sharebox', nav, content);

	},
	
	destroyShareBox: function(name){
		this.sharebox = null;
	},
	
	roomShareBoxShow: function(room){
		if ($defined(this.sharebox))
		{
			this.sharebox.close();
		}
		
		this.createRoomShareBox(room);
	},
	
	createRoomShareBox: function(room){
		this.sharebox = new Popup({
			size: {x: 500, y: 160},
			resizable: false,
			dockable: false,
			className: 'sharebox',
			onClose: this.destroyShareBox.bind(this)
		});
		
		
		var displayName = 'Write in ' + room.room_name + '\'s Stream';
		
		var nav = new Nav({
			iconOptions: {user: room},
			displayName: displayName,
			closable: true
		});
		
		var content = new ShareBoxPopupContent({
			room: room
		});
		
		this.sharebox.addContent('sharebox', nav, content);

	}
	
});var AttachLinkPopupContent = new Class({
	Extends: PopupContent,
	
	defaultText: 'Please enter the URL below',
	errorText: 'The URL you entered is invalid',
	validText: 'Click attach to add this link to your post',
	
	onBeforeInit: function(options){
		this.attachFunc = options.attachFunc;
	
		return options;
	},

	onInit: function(){
		this.valid = false;
		this.url = '';
		
		
		this.text = new Element('div', {'class': 'help_text light', 'text': this.defaultText}).inject(this.content);
		
		
		this.input = new Element('input', {'type': 'text', 'value': 'http://', 'maxlength': 200});
		this.input.addEvent('change', this.validate.bind(this));
		this.input.addEvent('keyup', this.validate.bindWithEvent(this));
		
		new Element('div', {'class': 'textarea_wrapper'}).adopt(
				this.input
		).inject(this.content);
		
		this.attachButton = new ButtonMedium({
			displayName: 'Attach',
			className: 'dark',
			action: 'check'
		});
		$(this.attachButton).addEvent('click', this.attach.bind(this));
		$(this.attachButton).inject(this.content);
	},
	
	onShow: function(){
		this.focus();
	},
	
	focus: function(){
		this.input.focus.delay(500, this.input);
	},
	
	validate: function(e){
		
		if (e && e.key == 'enter')
		{
			this.attach();
			return;
		}
		
		this.url = this.input.value.trim();
		if (DataUtility.validateUrl(this.url))
		{
			this.text.removeClass('error');
			this.text.addClass('success');
			this.text.set('text', this.validText);
			this.valid = true;
			
		}
		else
		{
			this.text.removeClass('success');
			this.text.addClass('error');
			this.text.set('text', this.errorText);
			this.valid = false;
		}
	},
	
	attach: function(){
		if (!this.valid)
			return;
		
		var attachment = {
			type: AttachmentType.Link,
			text: this.url,
			url: this.url
		};
		
		this.attachFunc(attachment);
		
		this.closeContent();
	}
	
});var AttachPhotoPopupContent = new Class({
	Extends: PopupContent,
	
	defaultText: 'Please choose a photo',
	errorText: 'The file you selected is not a photo',
	validText: 'Click attach to add this photo to your post',
	
	onBeforeInit: function(options){
		this.attachFunc = options.attachFunc;
	
		return options;
	},

	onInit: function(){
		this.valid = false;
		this.url = '';
		
		
		this.text = new Element('div', {'class': 'help_text light', 'text': this.defaultText}).inject(this.content);
		
		
		this.input = new Element('input', {'type': 'text', 'value': 'click to choose a file'});
		this.fileInput = new Element('input', {'type': 'file', 'name': 'file'});
		this.form = new Element('form').adopt(this.fileInput);
		
		this.fileInput.addEvent('change', this.validate.bind(this));
		
		new Element('div', {'class': 'textarea_wrapper'}).adopt(
				this.input,
				this.form
		).inject(this.content);
		
		this.attachButton = new ButtonMedium({
			displayName: 'Attach',
			className: 'dark',
			action: 'check'
		});
		$(this.attachButton).addEvent('click', this.attach.bind(this));
		$(this.attachButton).inject(this.content);
	},
	
	validate: function(){
		Logger().log('validating file');
		if (DataUtility.validatePhotoFile(this.fileInput.value))
		{
			this.text.removeClass('error');
			this.text.addClass('success');
			this.text.set('text', this.validText);
			this.input.value = this.fileInput.value;
			this.valid = true;
			
		}
		else
		{
			this.text.removeClass('success');
			this.text.addClass('error');
			this.text.set('text', this.errorText);
			this.input.value = 'click to choose a file';
			this.valid = false;
		}
	},
	
	attach: function(){
		if (!this.valid)
			return;
		
		//put this form into the hidden div!
		this.form.inject('hidden');
		
		var attachment = {
			type: AttachmentType.Photo,
			text: this.fileInput.value
		};
		
		this.attachFunc(attachment, this.form);
		
		this.closeContent();
	}
	
});var AttachmentType = {
	None: 0,
	Photo: 1,
	Link: 2
};var ShareBoxPopupContent = new Class({
	Extends: PopupContent,
	
	onBeforeInit: function(options){
		
		if ($defined(options.user))
		{
			this.user = options.user;
			this.isRoom = false;
			this.source_id = this.user.user_id;
		}
		else
		{
			this.user = options.room;
			this.isRoom = true;
			this.source_id = this.user.room_id;
		}
		
		this.reply_id = options.reply_id || 0;
		
		
		
		this.source_type = (this.isRoom) ? 3 : 1;
		
		this.forward_id = options.forward_id || 0;
		this.forward_user = options.forward_user;
		this.forward_body = options.forward_body;
		//sanity check
		if (this.source_id != this.getPrivateUser().user_id || !$defined(this.forward_user))
			this.forward_id = 0; //can only forward to your own stream!
		
		
		//check if user can toggle permissions
		this.is_forward = this.forward_id != 0;
		this.is_reply = this.reply_id != 0;
		this.is_direct = this.source_id != this.getPrivateUser().user_id;
		return options;
	},
	
	reset: function(){
		Logger().log('resetting');
		this.closeContactPickerPopup();
		this.closeAttachmentPopup();
		this.content.empty();
		this.onInit();
		
	},
	
	onInit: function(){
	
		this.targetContacts = $H();
		this.targetGroups = $H();
		this.targetContactEls = $H();
		this.targetGroupEls = $H();
		this.targetAllContacts = false;
		this.allContactsEl = null;
		this.formEl = null;
		
		this.isPublic = true;
		this.readyToPost = false;
		this.attachment = {type: AttachmentType.None};
		
		//post values
		//this.source_id = this.user.user_id;
		//this.source_type = 1;
		//this.reply_id = 0;
		this.channel_id = 0;
		this.res = this.getSession();
		
		
		if (!this.is_direct && !this.is_reply)
		{
			this.privateButton = new ButtonSmall({
				displayName: 'Private',
				className: 'privacy red',
				action: 'lock dark'
			});
			$(this.privateButton).addEvent('click', this.togglePrivacy.bind(this));
			$(this.privateButton).inject(this.content);
			
			this.publicButton = new ButtonSmall({
				displayName: 'Public',
				className: 'privacy green',
				action: 'broadcast dark'
			});
			$(this.publicButton).addEvent('click', this.togglePrivacy.bind(this));
			$(this.publicButton).inject(this.content);
			
			
			//RECEPIENTS
			this.addRecipientButton = new ButtonSmall({
				displayName: 'Add Recipient',
				className: 'dark',
				action: 'status dark'
			});
			$(this.addRecipientButton).addEvent('click', this.showContactPickerPopup.bind(this));
			
			
			var selfRecip = new Element('div', {'class': 'target self'});
			var icon = new Icon20({user: this.getPrivateUser()});
			$(icon).inject(selfRecip);
			selfRecip.adopt(
				new Element('div', {'class': 'target_text text11 light', 'text': 'Yourself'})
			);
			
			this.norecipients = new Element('div', {'class': 'recipients', 'text': 'Everyone can view this post'}).inject(this.content);
			this.recipients = new Element('div', {'class': 'recipients'}).adopt(
				new Element('div', {'class': 'target', 'text': 'To: '}),
				selfRecip,
				$(this.addRecipientButton),
				new Element('div', {'style': 'clear: both'})
			).inject(this.content);
			
			this.setPrivacyView();
		}
		else if (this.is_direct)
		{
			this.norecipients = new Element('div', {'class': 'recipients', 'text': 'Everyone can view this post'}).inject(this.content);
		}
		else
		{
			this.norecipients = new Element('div', {'class': 'recipients', 'text': 'All recipients of the original can view this post'}).inject(this.content);
		}

		//INPUT
		this.input = new Element('textarea', {'maxlength': 2000});
		this.input.addEvent('keyup', this.inputKeyUp.bindWithEvent(this));
		
		new Element('div', {'class': 'textarea_wrapper'}).adopt(
				this.input
		).inject(this.content);
		
		//ATTACHMENTS
		//buttons
		this.linkButton = new ButtonSmall({
			displayName: 'Link',
			className: 'dark',
			action: 'link'
		});
		$(this.linkButton).addEvent('click', this.showAttachmentPopup.bind(this, ['link']));
	
		this.photoButton = new ButtonSmall({
			displayName: 'Photo',
			className: 'dark',
			action: 'photo'
		});
		$(this.photoButton).addEvent('click', this.showAttachmentPopup.bind(this, ['photo']));
		
		this.fileButton = new ButtonSmall({
			displayName: 'File',
			className: 'dark',
			action: 'file'
		});
		
		this.attachments = new Element('div', {'class': 'attachments'}).inject(this.content);
		this.attachmentSelect = new Element('div').adopt(
			new Element('div', {'class': 'attachment_text light', 'text': 'Attach:'}),
			$(this.linkButton),
			$(this.photoButton)
			//$(this.fileButton)
		).inject(this.attachments);
		this.attachmentItems = new Element('div').inject(this.attachments);
		
		//ACtIONS
		
		this.cancelButton = new ButtonMedium({
			displayName: 'Close',
			className: 'dark',
			action: 'cross'
		});
		$(this.cancelButton).addEvent('click', this.closeContent.bind(this));
		
		this.updateButton = new ButtonMedium({
			displayName: 'Post',
			className: 'dark',
			action: 'check'
		});
		$(this.updateButton).addEvent('click', this.post.bind(this));
		
		
		this.actions = new Element('div', {'class': 'actions'}).adopt(
			$(this.updateButton),
			$(this.cancelButton)
		).inject(this.content);
		
		this.focus();
		
		//show forward if set
		if (this.forward_id != 0)
			this.attachForward();
	},
	
	setPrivacyView: function(){
		if (this.isPublic)
		{
			DomUtility.show(this.publicButton);
			DomUtility.hide(this.privateButton);
			DomUtility.show(this.norecipients);
			DomUtility.hide(this.recipients);
		}
		else
		{
			DomUtility.hide(this.publicButton);
			DomUtility.show(this.privateButton);	
			DomUtility.hide(this.norecipients);
			DomUtility.show(this.recipients);
		}
		
	},
	
	togglePrivacy: function(){
		if (this.isPublic)
		{
			//toggle stuff
			this.showContactPickerPopup();
			
		}
		else
		{
			this.closeContactPickerPopup();
			
			
		}
		
		this.isPublic = !this.isPublic;
		this.setPrivacyView();
	},
	
	inputKeyUp: function(e){
		if (e.shift && e.key == 'enter')
		{
			e.preventDefault();
			this.post();
		}
		
		if (DomUtility.textareaAutoSize(e.target, 40))
			this.updateSize();
	},
	
	updateSize: function(){
		//resize popup when the size changes
		var h = (this.isPublic) ? this.norecipients.getSize().y : this.recipients.getSize().y;
		h += this.input.getSize().y + 10;
		h += this.attachments.getSize().y + 20;
		h += this.actions.getSize().y + 30;
		this.resizePopup(500, h);
	},
	
	//POST ACTION!
	parseTargets: function(){
		if (this.isPublic)
			return '';
		
		var targets = [this.getPrivateUser().user_id];
		
		//if targeting all contacts, do this
		if (this.targetAllContacts)
		{
			this.getContacts().each(function(user){
				targets.include(user.user_id);
				
			}, this);
			return targets.join(',');
		}
		
		//get all members of groups
		this.targetGroups.each(function(group){
			var contacts = this.getContactsByGroup(group.group_id);
			contacts.each(function(user){
				targets.include(user.user_id);
			}, this);
		}, this);
		
		this.targetContacts.each(function(user){
			targets.include(user.user_id);
		}, this);
		
		return targets.join(',');
	},
	
	
	post: function(){
		var targets = this.parseTargets();
		var body = this.input.value.trim();
		var is_public = (this.isPublic)? 1 : 0;
		Logger().log(targets);
		Logger().log(body);
		
		//only if it has files
		//TODO: auto detect this based on a bit in the attachment obj
	
		if (this.forward_id == 0)
		{
			//check make sure there's contnet
			if (body == '' && this.attachment.type == AttachmentType.None)
			{
				this.fireEvent('showAlert', 'empty_post', 'Problem Posting', 'Please enter some content to post.');
				return;
			}
			
			var params = {
					'body': body,
					'targets': targets,
					'source_id': this.source_id,
					'source_type': this.source_type,
					'reply_id': this.reply_id,
					'channel_id': this.channel_id,
					'is_public': is_public,
					'res': this.res,
					'attachment': JSON.encode(this.attachment)
				};
		
			this.call('home', 'publish', params, this.postSuccess.bind(this), this.postFail.bind(this), this.formEl);
		}
		else
		{
				
			var params = {
					'body': body,
					'targets': targets,
					'source_id': this.source_id,
					'source_type': this.source_type,
					'forward_id': this.forward_id,
					'channel_id': this.channel_id,
					'is_public': is_public,
					'res': this.res
				};
		
			this.call('home', 'forward', params, this.postSuccess.bind(this), this.postFail.bind(this));
		}
		
		this.updateButton.showProgress();
	},
	
	postSuccess: function(){
		this.updateButton.hideProgress();
		
		this.closeContent();
	},
	
	postFail: function(){
		this.updateButton.hideProgress();
	},
	
	addAllContacts: function(){
		if (this.isPublic || $defined(this.allContactsEl))
			return;
		
		var el = ShareBoxUtility.createAllContactsItem();
		el.addEvent('click', this.removeAllContacts.bind(this));
		el.inject($(this.addRecipientButton), 'before');
		this.allContactsEl = el;
		this.targetAllContacts = true;
		this.updateSize();
	},
	
	removeAllContacts: function(){
		if (!$defined(this.allContactsEl))
			return;
		
		this.allContactsEl.destroy();
		this.targetAllContacts = false;
		this.updateSize();
	},
	
	addTargetGroup: function(group){
		if (this.isPublic || this.targetGroupEls.has(group.group_id))
			return;
		
		var el = ShareBoxUtility.createGroupItem(group);
		el.addEvent('click', this.removeTargetGroup.bind(this, [group.group_id]));
		el.inject($(this.addRecipientButton), 'before');
		this.targetGroupEls.set(group.group_id, el);
		this.targetGroups.set(group.group_id, group);
		this.updateSize();
	},
	
	removeTargetGroup: function(group_id){
		if (!this.targetGroupEls.has(group_id))
			return;
		
		this.targetGroupEls.get(group_id).destroy();
		this.targetGroupEls.erase(group_id);
		this.targetGroups.erase(group_id);
		this.updateSize();
	},
	
	addTargetContact: function(user){
		if (this.isPublic || this.targetContactEls.has(user.username))
			return;
		
		var el = ShareBoxUtility.createContactItem(user);
		el.addEvent('click', this.removeTargetContact.bind(this, [user.username]));
		el.inject($(this.addRecipientButton), 'before');
		this.targetContactEls.set(user.username, el);
		this.targetContacts.set(user.username, user);
		this.updateSize();
	},
	
	removeTargetContact: function(username){
		if (!this.targetContactEls.has(username))
			return;
		
		this.targetContactEls.get(username).destroy();
		this.targetContactEls.erase(username);
		this.targetContacts.erase(username);
		this.updateSize();
	},
	
	showContactPickerPopup: function(){
		
		if ($defined(this.contactPicker))
		{
			this.contactPicker._select();
			return;
		}
		
		this.createContactPickerPopup();
		
	},
	
	closeContactPickerPopup: function(){
		if ($defined(this.contactPicker))
			this.contactPicker.close();
	},
	
	createContactPickerPopup: function(){
		this.contactPicker = new Popup({
			size: {x: 200, y: 350},
			resizable: false,
			dockable: false,
			className: 'contactPicker',
			onClose: this.destroyContactPickerPopup.bind(this)
		});
		
		var nav = new Nav({
			iconOptions: {iconName: 'users'},
			displayName: 'Select Recipients',
			closable: true
		});
		
		var content = new ContactPickerPopupContent({
			groups: this.getGroups(),
			contacts: this.getContacts(),
			groupAddFunc: this.addTargetGroup.bind(this),
			contactAddFunc: this.addTargetContact.bind(this),
			allContactsAddFunc: this.addAllContacts.bind(this)
		});
		
		this.contactPicker.addContent('contactPicker', nav, content);
	},
	
	destroyContactPickerPopup: function(){
		this.contactPicker = null;
	},
	
	//ATTACHMENTS
	showAttachmentPopup: function(type){
		if ($defined(this.attachmentPopup))
		{
			if (this.attachmentPopupType == type)
			{
				this.attachmentPopup._select();
				return;
			}
			else
			{
				this.closeAttachmentPopup();
			}
		}
		
		this.createAttachmentPopup(type);
	},
	
	closeAttachmentPopup: function(){
		if ($defined(this.attachmentPopup))
			this.attachmentPopup.close();
	},
	
	createAttachmentPopup: function(type){
		var popupClass;
		var typeName;
		
		switch(type)
		{
			case 'link':
				popupClass = AttachLinkPopupContent;
				typeName = 'Link';
			break;
			case 'photo':
				popupClass = AttachPhotoPopupContent;
				typeName = 'Photo';
			break;
		}
		
		this.attachmentPopup = new Popup({
			size: {x: 400, y: 74},
			resizable: false,
			dockable: false,
			className: 'attachments',
			onClose: this.destroyAttachmentPopup.bind(this)
		});
		
		var nav = new Nav({
			iconOptions: {iconName: type},
			displayName: 'Attach ' + typeName,
			closable: true
		});
		
		var content = new popupClass({attachFunc: this.attachContent.bind(this)});
		
		this.attachmentPopupType = type;
		this.attachmentPopup.addContent('attachment', nav, content);
	},
	
	destroyAttachmentPopup: function(){
		this.attachmentPopup = null;
		this.attachmentPopupType = null;
	},
	
	attachForward: function(){
		DomUtility.show(this.attachmentItems);
		DomUtility.hide(this.attachmentSelect);
		
		var attachElement = ShareBoxUtility.createForwardAttachmentItem(this.forward_user, this.forward_body);
		
		attachElement.inject(this.attachmentItems);
		
	},
	
	attachContent: function(attachment, formEl){
		this.attachment = attachment;
		if ($defined(formEl))
			this.formEl = formEl;
		
		Logger().log('attached ' + this.formEl);
		DomUtility.show(this.attachmentItems);
		DomUtility.hide(this.attachmentSelect);
		
		var attachElement = ShareBoxUtility.createAttachmentItem(attachment);
		
		var removeButton = new ButtonSmall({
			displayName: 'Remove',
			className: 'dark',
			action: 'cross'
		});
		$(removeButton).addEvent('click', this.removeAttachment.bind(this));
		$(removeButton).inject(attachElement);
		
		attachElement.inject(this.attachmentItems);
		
	},
	
	removeAttachment: function(){
		this.attachment = null;
		this.attachmentItems.empty();
		if ($defined(this.formEl))
			this.formEl.destroy();
		this.formEl = null;
		DomUtility.hide(this.attachmentItems);
		DomUtility.show(this.attachmentSelect);
		this.forward_id = 0;
	},
	
	onShow: function(){
		this.focus();
	},
	
	focus: function(){
		this.input.focus.delay(500, this.input);
	}
	
});var ShareBoxUtility = {
		
	//TODO:  this belongs in a diff utility class
	createContactRoomInviteItem: function(user){
		var el = new Element('div', {'class': 'listItem'});
		var icon = new Icon20({user: user});
		$(icon).inject(el);
		new Element('div', {'class': 'listText text12 light', 'text': TextUtility.cleanText(user.fullname)}).inject(el);
		
		//add properties for search
		el.set('val1', user.fullname.toLowerCase());
		el.set('val2', user.last_name.toLowerCase());
		
		//add button for invite
		var button = new ButtonSmall({
			displayName: 'Invite',
			className: 'dark',
			action: 'check'
		});
		
		$(button).inject(el);
		
		new Element('div', {'class': 'invited_text text11 success', 'text': 'Invited'}).inject(el);
		
		return el;
	},

	createContactPickerItem: function(user){
		var el = new Element('div', {'class': 'listItem'});
		var icon = new Icon20({user: user});
		$(icon).inject(el);
		new Element('div', {'class': 'listText text12 light', 'text': TextUtility.cleanText(user.fullname)}).inject(el);
		
		//add properties for search
		el.set('val1', user.fullname.toLowerCase());
		el.set('val2', user.last_name.toLowerCase());
		return el;
	},
	
	createGroupPickerItem: function(group){
		var el = new Element('div', {'class': 'listItem'});
		var icon = new Icon20({iconName: 'group'});
		$(icon).inject(el);
		new Element('div', {'class': 'listText text12 light', 'text': TextUtility.cleanText(group.name)}).inject(el);
		
		//add properties for search
		el.set('val1', group.name.toLowerCase());
		return el;
	},
	
	createAllContactsPickerItem: function(){
		var el = new Element('div', {'class': 'listItem'});
		var icon = new Icon20({iconName: 'contacts'});
		$(icon).inject(el);
		new Element('div', {'class': 'listText text12 light', 'text': 'All Contacts'}).inject(el);
		
		//add properties for search
		el.set('val1', 'all contacts');
		return el;
	},
	
	createContactItem: function(user){
		var el = new Element('div', {'class': 'target'});
		var icon = new Icon20({user: user});
		$(icon).inject(el);
		el.adopt(
			new Element('div', {'class': 'target_text text11 light', 'text': TextUtility.cleanText(user.first_name)}),
			new Element('div', {'class': 'action cross'})
		);
			
		return el;
	},
	
	createGroupItem: function(group){
		var el = new Element('div', {'class': 'target'});
		var icon = new Icon20({iconName: 'group'});
		$(icon).inject(el);
		el.adopt(
			new Element('div', {'class': 'target_text text11 light', 'text': TextUtility.cleanText(group.name)}),
			new Element('div', {'class': 'action cross'})
		);
		
		return el;
	},
	
	createAllContactsItem: function(){
		var el = new Element('div', {'class': 'target'});
		var icon = new Icon20({iconName: 'contacts'});
		$(icon).inject(el);
		el.adopt(
			new Element('div', {'class': 'target_text text11 light', 'text': 'All Contacts'}),
			new Element('div', {'class': 'action cross'})
		);
		
		return el;
	},
	
	createForwardAttachmentItem: function(forward_user, forward_body){
		var el = new Element('div', {'class': 'attachment'});
		var name = ($defined(forward_user.user_id))? forward_user.fullname: forward_user.room_name;
		
		el.adopt(
			new Element('span', {'class': 'indicator', 'html': '&raquo;'}),
			new Element('span', {'class': 'attachment_title light2'}).adopt(
				new Element('span', {'text': 'Forwarding: '}),
				new Element('span', {'class': 'user_name', 'text': TextUtility.unescape(name)}),
				new Element('span', {'text': ' ' + TextUtility.unescape(forward_body)})
			)
		);
		
		return el;
	},
	
	createAttachmentItem: function(attachment){
		var el = new Element('div', {'class': 'attachment'});
		el.adopt(
			new Element('span', {'class': 'indicator', 'html': '&raquo;'}),
			new Element('span', {'class': 'attachment_title light2', 'text': attachment.text})
		);
		
		return el;
	}
		
};var UI = new Class({
	Extends: Base,
	
	EventHandlers: [
		'userSwitched',
		'viewSwitch',
		'menuAdd',
		'contentAdd',
		'menuClose',
		'contentClose',
		'appClose',
		
		'showUser',
		'showRoom',
		'showConvo',
		
		'showConfirmation',
		'showAlert',
		
		'showVideoPopup',
		'showPhotoPopup'
	],
	
	init: function(){
	
		this.historyManager = new HistoryManager();
		this.historyManager.addEvent('onHistoryChange', this.historyChanged.bind(this));
		this.history = $H();
		
		
		this.navScrollBar = new ScrollBar('nav', 'nav_wrapper');
		this.idleTimeout = 10 * 60 * 1000; //10 min
		
		//start timer for timestamp updater
		this.updateTimestamps.periodical(1000 * 90, this);
		
		this.menus = $H();
		this.contents = $H();
		this.confirms = $H();
		this.alerts = $H();
		this.reset();
	},
	
	reset: function(){
		this.currentMenu = null;
		this.currentContent = null;
	},
	
	userSwitched: function(){
		if (this.isLoggedIn())
			this.userLoggedIn();
		else
			this.userLoggedOut();
	},
	
	userLoggedIn: function(){
		this.reset();
		this.setCurrentUser();
		this.toggleHeader(true);
		
		//HACK! check for rss app
		this.appsInstalled = UserUtility.getInstalledApps();
		// get user id from user_data
		if ($defined(this.appsInstalled[4]))
		{
			DomUtility.show('app_menu_item_4');
			DomUtility.hide('no_apps');	
		}
	},
	
	userLoggedOut: function(){
		this.reset();
		this.resetCurrentUser();
		this.toggleHeader(false);
	},
	
	appClose: function(app){
		
		this.contents.each(function(content, key){
			var appId = key.split('_', 1)[0];
			if (appId == app)
				this.contentCloseKey(key);
		}, this);
		
		this.menus.each(function(menu, key){
			var appId = key.split('_', 1)[0];
			if (appId == app)
				this.menuCloseKey(key);
		}, this);
		
	},
	
	//ui functions
	viewSwitch: function(appId, contentName, menuName){
		var menuKey = appId + '_' + menuName;
		var contentKey = appId + '_' + contentName;
		if (!this.contents.has(contentKey))
		{
			Logger().log('content ' + contentName + ' not found!');
			return;
		}
		
		if ($defined(menuName) && !this.menus.has(menuKey))
		{
			Logger().log('menu ' + menuName + ' not found!');
			return;
		}
		
		if ($defined(menuName))
		{
			this.contentSwitch(contentKey);
			this.menuSwitch(menuKey);
			$('content').addClass('hasmenu');
		}
		else
		{
			this.contentSwitch(contentKey);
			$('content').removeClass('hasmenu');
		}
		
		this.historyChange(appId, contentName, menuName);
		
	},
	
	historyChange: function(appId, contentName, menuName){
		
		if (parseInt(appId) == appId)
		{	//regular app
			var app = this.getAppById(appId);
			if (!app)
				return;
			
			var hash = app.name + '/' + contentName;
			this.history.set(hash, {appId: appId, contentName: contentName, menuName: menuName});
			this.historyManager.addState(hash);
		}
		else
		{
			var appParts = appId.split('_');
			var appName = appParts[0];
			var appParam = appParts[1];
			
			var hash = appName + '/' + appParam + '/' + contentName;
			this.history.set(hash, {appId: appId, contentName: contentName, menuName: menuName});
			this.historyManager.addState(hash);
			//instance app
			
		}
	},
	
	historyChanged: function(hash){
		
		Logger().log('historyChanged - ' + hash);
		//look up hash
		if (this.history.has(hash))
		{
			var state = this.history.get(hash);
			this.viewSwitch(state.appId, state.contentName, state.menuName);
		}
		
			
		
	},
	
	
	menuAdd: function(appId, menuName, menu){
		var menuKey = appId + '_' + menuName;
		Logger().log('adding menu ' + menuKey);
		if (this.menus.has(menuKey))
			return;
		
		$(menu).inject('menu_wrapper');
		menu.off();
		this.menus.set(menuKey, menu);
		
	},
	
	menuClose: function(appId, menuName){
		var menuKey = appId + '_' + menuName;
		this.menuCloseKey(menuKey);
	},
	
	//closes a menu using a key
	menuCloseKey: function(key){
		if (!this.menus.has(key))
			return;
		
		if (key == this.currentMenu)
			this.currentMenu = null;
		
		var menu = this.menus.get(key);
		menu.destroy();
		this.menus.erase(key);
		delete(menu);
	},
	
	contentAdd: function(appId, contentName, content){
		var contentKey = appId + '_' + contentName;
		Logger().log('adding content ' + contentKey);
		
		if (this.contents.has(contentKey))
			return;
		
		$(content).inject('content_wrapper');
		content.off();
		this.contents.set(contentKey, content);
	},
	
	contentClose: function(appId, contentName){
		var contentKey = appId + '_' + contentName;
		this.contentCloseKey(contentKey);
	},
	
	contentCloseKey: function(key){
		Logger().log(key);
		if (!this.contents.has(key))
			return;
		
		if (key == this.currentContent)
			this.currentContent = null;
		
		var content = this.contents.get(key);
		content.destroy();
		this.contents.erase(key);
		delete(content);
	},
	
	menuSwitch: function(menuKey){
		if (!this.menus.has(menuKey))
			return;
		
		if (menuKey == this.currentMenu)
			return;
		
		if ($defined(this.currentMenu))
		{
			this.menus.get(this.currentMenu).off();
		}
		
		this.menus.get(menuKey).on();
		this.currentMenu = menuKey;
	},
	
	contentSwitch: function(contentKey){
		if (!this.contents.has(contentKey))
			return;
		
		if (contentKey == this.currentContent)
			return;
		
		if ($defined(this.currentContent))
		{
			this.contents.get(this.currentContent).off();
		}
		
		this.contents.get(contentKey).on();
		
		//fire event to alert this content was activated
		this.fireEvent('contentSwitched', contentKey);
	
		this.currentContent = contentKey;
	},
	
	//toggles the self section of hte header
	toggleHeader: function(loggedIn){
		if (loggedIn)
		{
			DomUtility.show('self');
			$('header').get('tween').start('left', 201).chain(function(){
				$('self').setStyle('z-index', 6);
				
				$('header_loggedOut').get('tween').start('top', -36).chain(function(){
					$('header_loggedIn').get('tween').start('top', 0);
				});
			});
				
			$('nav_wrapper').get('tween').start('left', 0);
			$('content').get('tween').start('left', 201);
			
			//move dock
			$('dock').inject('dock_loggedIn');
		}
		else
		{
			$('self').setStyle('z-index', 4);
			$('header').get('tween').start('left', 0).chain(function(){
				DomUtility.hide('self');
			
				$('header_loggedIn').get('tween').start('top', -36).chain(function(){
					$('header_loggedOut').get('tween').start('top', 0);
				});
			});
			
			$('nav_wrapper').get('tween').start('left', -210);
			$('content').get('tween').start('left', 0);
			
			//move dock
			$('dock').inject('dock_loggedOut');
		}
		
	},
	
	setCurrentUser: function(){
		//set fullname
		$('self_name').set('text', this.getPrivateUser().fullname);
		//profile pic
		$('self_profile_pic_26').set('src', this.getPrivateUser().profile_pic_26);
		$('self_profile_pic_26').addClass('profile_pic_26_' + this.getPrivateUser().username);
		$('self_online_status').addClass('online_status_' + this.getPrivateUser().username);
	},
	
	resetCurrentUser: function(){
		$('self_name').set('text', '');
		$('self_profile_pic_26').set('src', '');
		$('self_profile_pic_26').set('class', 'profile_pic');
		$('self_online_status').set('class', 'online_status');
		
	},
	
	showUser: function(user){
		var options = {user: user};
		this.fireEvent('startAppInstance', 'user', user.username, options);
	},
	
	showRoom: function(room){
		var options = {room: room};
		this.fireEvent('startAppInstance', 'room', room.username, options);
	},
	
	showConvo: function(){
		
	},
	
	showConfirmation: function(name, title, message, confirmFunc){
		if (this.confirms.has(name))
		{
			this.confirms.get(name)._select();
			return;
		}
		
		this.createConfirmation(name, title, message, confirmFunc);
	},
	
	createConfirmation: function(name, title, message, confirmFunc){
		var popup = new Popup({
			size: {x: 350, y: 100},
			resizable: false,
			dockable: false,
			className: 'confirm',
			onClose: this.destroyConfirmation.bind(this, [name])
		});
		
		var nav = new Nav({
			iconOptions: {iconName: 'info'},
			displayName: title,
			closable: true
		});
		
		var content = new ConfirmPopupContent({
			confirmMessage: message,
			confirmFunc: confirmFunc
		});
		
		popup.addContent('confirm', nav, content);

		this.confirms.set(name, popup);
		
	},
	
	destroyConfirmation: function(name){
		this.confirms.erase(name);
	},
	
	showAlert: function(name, title, message){
		if (this.alerts.has(name))
		{
			this.alerts.get(name)._select();
			return;
		}
		
		this.createAlert(name, title, message);
	},
	
	createAlert: function(name, title, message){
		var popup = new Popup({
			size: {x: 350, y: 100},
			resizable: false,
			dockable: false,
			className: 'confirm',
			onClose: this.destroyAlert.bind(this, [name])
		});
		
		var nav = new Nav({
			iconOptions: {iconName: 'info'},
			displayName: title,
			closable: true
		});
		
		var content = new AlertPopupContent({
			alertMessage: message
		});
		
		popup.addContent('confirm', nav, content);

		this.alerts.set(name, popup);
		
	},
	
	destroyAlert: function(name){
		this.alerts.erase(name);
	},
	
	//this will find all .timestamp, and reparse the ts value
	updateTimestamps: function(){
		$$('.timestamp').each(function(el){
			if ($defined(el.get('ts')))
			{
				var ts = new Date(el.get('ts'));
				var format = el.get('ts_format');
				
				var timeStr = DateUtility.getTimestamp(ts, format);
					
				el.set('text', timeStr);
			}
		});
	}
	
	
	
});UI.implement({
	
	showVideoPopup: function(attachment){
		switch(attachment.video_type)
		{
			case 'youtube':
				this.createYoutubePopup(attachment);
				break;
			case 'vimeo':
				this.createVimeoPopup(attachment);
				break;
			case 'ch':
				this.createChPopup(attachment);
				break;
			case 'hulu':
				this.createHuluPopup(attachment);
				break;
			case 'break':
				this.createBreakPopup(attachment);
				break;
	
		}
	},
	
	showPhotoPopup: function(attachment){
		this.createPhotoPopup(attachment);
	},
	
	createPhotoPopup: function(attachment){
		var popup = new Popup({
			size: {x: 300, y: 100},
			resizable: false,
			dockable: false,
			closable: true,
			className: 'photoViewer'
		});
		
		var displayName = attachment.filename;
		
		var nav = new Nav({
			iconOptions: {iconName: 'photo'},
			displayName: displayName,
			closable: true
		});
		
		
		var content = new PhotoPopupContent({
			attachment: attachment
		});
		
		//attach resize methods
		content.resizePopup = popup.resizePopup.bind(popup);
		content.reCenter = popup.reCenter.bind(popup);
		
		popup.addContent('photo', nav, content);
		
	},
	
	
	createYoutubePopup: function(attachment){
		var popup = new Popup({
			size: {x: 425, y: 350},
			resizable: false,
			dockable: false,
			noHide: true,
			closable: true,
			className: 'videoPlayer'
		});
		
		var displayName = 'Youtube Video';
		
		var nav = new Nav({
			iconOptions: {iconName: 'youtube'},
			displayName: displayName,
			closable: true
		});
		
		
		var content = new YoutubePopupContent({
			attachment: attachment
		});
		
		popup.addContent('video', nav, content);
		
	},
	
	createVimeoPopup: function(attachment){
		var popup = new Popup({
			size: {x: 425, y: 350},
			resizable: false,
			dockable: false,
			noHide: true,
			closable: true,
			className: 'videoPlayer'
		});
		
		var displayName = 'Vimeo Video';
		
		var nav = new Nav({
			iconOptions: {iconName: 'tv'},
			displayName: displayName,
			closable: true
		});
		
		
		var content = new VimeoPopupContent({
			attachment: attachment
		});
		
		popup.addContent('video', nav, content);
		
	},
	
	createChPopup: function(attachment){
		var popup = new Popup({
			size: {x: 425, y: 350},
			resizable: false,
			dockable: false,
			noHide: true,
			closable: true,
			className: 'videoPlayer'
		});
		
		var displayName = 'College Humor Video';
		
		var nav = new Nav({
			iconOptions: {iconName: 'tv'},
			displayName: displayName,
			closable: true
		});
		
		
		var content = new ChPopupContent({
			attachment: attachment
		});
		
		popup.addContent('video', nav, content);
		
	},
	
	createBreakPopup: function(attachment){
		var popup = new Popup({
			size: {x: 425, y: 350},
			resizable: false,
			dockable: false,
			noHide: true,
			closable: true,
			className: 'videoPlayer'
		});
		
		var displayName = 'Break.com Video';
		
		var nav = new Nav({
			iconOptions: {iconName: 'tv'},
			displayName: displayName,
			closable: true
		});
		
		
		var content = new BreakPopupContent({
			attachment: attachment
		});
		
		popup.addContent('video', nav, content);
		
	},
	
	createHuluPopup: function(attachment){
		var popup = new Popup({
			size: {x: 512, y: 296},
			resizable: false,
			dockable: false,
			noHide: true,
			closable: true,
			className: 'videoPlayer'
		});
		
		var displayName = 'Hulu Video';
		
		var nav = new Nav({
			iconOptions: {iconName: 'tv'},
			displayName: displayName,
			closable: true
		});
		
		
		var content = new HuluPopupContent({
			attachment: attachment
		});
		
		popup.addContent('video', nav, content);
		
	}
	
});var BreakPopupContent = new Class({
	Extends: PopupContent,
	
	onBeforeInit: function(options){
		this.attachment = options.attachment;
	
		return options;
	},

	onInit: function(){
		this.call('home', 'parse_break_url', {url: this.attachment.url}, this.parseSuccess.bind(this), this.parseFail.bind(this));
	},
	
	parseSuccess: function(data){
		
		var swf = 'http://embed.break.com/' + data.id;
		var vid = new Swiff(swf, {
			id: swf,
			width: 425,
			height: 350,
			params: {
				movie: swf,
				allowfullscreen: true
			}
		});
		
		vid.inject(this.content);	
		
	},
	
	parseFail: function(){
		this.closeContent();
	}
	
});var ChPopupContent = new Class({
	Extends: PopupContent,
	
	onBeforeInit: function(options){
		this.attachment = options.attachment;
	
		return options;
	},

	onInit: function(){
		
		var swf = 'http://www.collegehumor.com/moogaloop/moogaloop.swf?clip_id=' + this.attachment.vimeoId + '&autoplay=1&fullscreen=1';
		var vid = new Swiff(swf, {
			id: swf,
			width: 425,
			height: 350,
			params: {
				movie: swf,
				allowfullscreen: true
			}
		});
		
		vid.inject(this.content);
		
	}
	
});var HuluPopupContent = new Class({
	Extends: PopupContent,
	
	onBeforeInit: function(options){
		this.attachment = options.attachment;
	
		return options;
	},

	onInit: function(){
		this.call('home', 'parse_hulu_url', {url: this.attachment.url}, this.parseSuccess.bind(this), this.parseFail.bind(this));
	},
	
	parseSuccess: function(data){
		
		var swf = 'http://www.hulu.com/embed/' + data.id;
		var vid = new Swiff(swf, {
			id: swf,
			width: 512,
			height: 296,
			params: {
				movie: swf,
				allowfullscreen: true
			}
		});
		
		vid.inject(this.content);	
		
	},
	
	parseFail: function(){
		this.closeContent();
	}
	
});var PhotoPopupContent = new Class({
	Extends: PopupContent,
	
	strings: {
		loadingMessage: 'Photo loading...'
	},
	
	onBeforeInit: function(options){
		
		this.attachment = options.attachment;
	
		return options;
	},

	onInit: function(){
		
		this.loadingMsg = new Element('div', {'class': 'loading_message'}).adopt(
			new Element('div', {'class': 'loading_text light', 'text': this.strings.loadingMessage}),
			new Element('div', {'class': 'progress'})
		).inject(this.content);
		
		Logger().log(this.attachment.url);
		
		this.image = new Asset.image(this.attachment.url, {
			title: this.attachment.filename, 
			onload: this.photoLoaded.bind(this)});
		
	},
	
	photoLoaded: function()
	{
		var imageW = this.image.width;
		var imageH = this.image.height;
		
		if (imageW >= imageH && imageW > 1000)
		{
			imageH = (1000/imageW) * imageH;
			imageW = 1000;
		}
		else if (imageW < imageH && imageH > 1000)
		{
			imageW = (1000/imageH) * imageW;
			imageH = 1000;
		}
		
		var w = imageW + 20;
		var h = imageH + 20;
		
		DomUtility.hide(this.loadingMsg);
		
		this.image.inject(this.content);
		
		this.resizePopup(w, h);
		this.reCenter();
	}
	
});var VimeoPopupContent = new Class({
	Extends: PopupContent,
	
	onBeforeInit: function(options){
		this.attachment = options.attachment;
	
		return options;
	},

	onInit: function(){
		
		var swf = 'http://vimeo.com/moogaloop.swf?clip_id=' + this.attachment.vimeoId + '&autoplay=1&fullscreen=1';
		
		Logger().log(swf);
		
		var vid = new Swiff(swf, {
			id: swf,
			width: 425,
			height: 350,
			params: {
				movie: swf,
				allowfullscreen: true
			}
		});
		
		vid.inject(this.content);
		
	}
	
});var YoutubePopupContent = new Class({
	Extends: PopupContent,
	
	onBeforeInit: function(options){
		this.attachment = options.attachment;
	
		return options;
	},

	onInit: function(){
			
		var swf = 'http://www.youtube.com/v/' + this.attachment.youtubeId + '&rel=1&autoplay=1';
		var vid = new Swiff(swf, {
			id: swf,
			width: 425,
			height: 350,
			params: {
				movie: swf,
				allowfullscreen: true
			}
		});
		
		vid.inject(this.content);
		
	}
	
});var VideoChat = new Class({
	Extends: Base,
	
	EventHandlers: [
	                'userSwitched',
	                'videoChatStart',
	                'videoChatReset',
	                'videoChatEnd',
	                'videoChatRequestReceived',
	                'videoChatRequestAccepted',
	                'videoChatAccept'
	                ],
	
	init: function(){
		//initialize video swf
		window.sim = new VideoChatInterface();
		
		this.callTimeout = 0;
	
		this.createVideoChatPopup();
	},
	
	userSwitched: function(){
		if (this.isLoggedIn())
			this.userLoggedIn();
		else
			this.userLoggedOut();
	},
	
	userLoggedIn: function(){
		window.sim.ChatReady();
	},
	
	userLoggedOut: function(){
		window.sim.ChatNotReady();
	},
	
	videoChatStart: function(user){
		if (!this.videoEnabled())
		{
			var name = 'video_chat';
			var title = 'Unable to Start Video Conference';
			var message = 'Your system does not support video conferencing';

			this.fireEvent('showAlert', name, title, message);
			return;
		}
		
		if (window.sim.SwfState == window.sim.SwfReady)
		{
			this.fireEvent('sendVideoChatInvite', user);
			window.sim.CallInitiate(this.getPrivateUser().username, user.username);
			this.callTimeout = this.videoChatEnd.delay(20000, this);
			
			this.showVideoChatPopup(user);
		}
	},
	
	videoChatAccept: function(user, stratusId){
		this.showVideoChatPopup(user);
		window.sim.CallAccept(stratusId, this.getPrivateUser().username, user.username);
		this.fireEvent('sendVideoChatAccept', user);
	},
	
	videoChatReset: function(){
		Logger().log('reset video chat');
		this.popup.close();
		this.fireEvent('videoChatEnable', true);
		this.fireEvent('videoInUse', false);
	    this.fireEvent('selfPresenceUpdate');
	    $clear(this.callTimeout);
	    
	    //reset the swf
	    //this.popup.resetSwf();
	},
	
	videoChatEnd: function(){
		Logger().log('reset video chat');
		window.sim.CallEnd();
		this.popup.close();
		this.fireEvent('videoChatEnable', true);
		this.fireEvent('videoInUse', false);
	    this.fireEvent('selfPresenceUpdate');
	    $clear(this.callTimeout);
	    
	},
	
	videoChatRequestAccepted: function(username, stratusId){
		$clear(this.callTimeout);
		window.sim.CallAccepted(stratusId, username);
	},
	
	videoChatRequestReceived: function(username, stratusId){
		var user = this.getContact(username);
		var notif = new VideoChatNotification({user: user, stratusId: stratusId});
		this.fireEvent('notificationAdd', 'videoChat_request_' + username, notif);
		window.sim.CallReceived(username);
	},
	
	showVideoChatPopup: function(user){
		this.popup.show(user);
	},
	
	createVideoChatPopup: function(){
		this.popup = new VideoChatPopup({
			size: {x: 400, y: 340},
			resizable: false,
			dockable: false,
			noHide: true,
			closable: false,
			className: 'pipioVideoChat'
		});
		
		var nav = new Nav({
			iconOptions: {iconName: 'video_chat'},
			displayName: 'Video Conference',
			closable: true
		});
		
		var content = new VideoChatPopupContent({});
		this.popup.addContent('video_chat', nav, content);
	}
	
	
	
});var VideoChatInterface = new Class({
	Extends: Base,
	
	EventHandlers: [],
	
	init: function(){
		this.flexApp = null;
		this.SwfState = null;
		
		this.SwfError = -1;
		this.SwfNotLoaded = 0; 
		this.SwfLoaded = 1;
		this.SwfNotReady = 2;
		this.SwfReady = 3;
		this.SwfCalling = 4;
		this.SwfRinging = 5;
		this.SwfConnecting = 6;
		this.SwfConnected = 7;
		this.SwfDisconnecting = 8;
	},

	// Connection functions
	CallInitiate: function(nearUsername, farUsername) {
		// XXX TODO call this when the user places a call
		if (this.SwfState == this.SwfReady) {
			this.flexApp.AttachOutgoingStream(nearUsername);
			this.SwfState = this.SwfCalling;
		}
		
	},
	CallReceived: function(farUsername) {
		// XXX TODO call this when a call comes in
		if (this.SwfState == this.SwfReady) {
			this.SwfState = this.SwfRinging;
		}
		
	},	
	CallAccept: function(farID, nearUsername, farUsername) {
		// XXX TODO call this on the callee client if the callee accepts the call
		if (this.SwfState == this.SwfRinging) {
			this.SwfState = this.SwfConnecting;
			this.flexApp.AttachIncomingStream(farID, farUsername);
			this.flexApp.AttachOutgoingStream(nearUsername);
			this.SwfState = this.SwfConnected;
		}
		
	},
	CallReject: function(farUsername) {
		// XXX TODO call this on the callee client if the callee rejects the call
		if (this.SwfState == this.SwfRinging) {
			this.SwfState = this.SwfDisconnecting;
			this.SwfState = this.SwfReady;
		}
		
	},
	CallAccepted: function(farID, farUsername) {
		// XXX TODO API should call this function when the call is accepted by farUsername
		if (this.SwfState == this.SwfCalling) {
			this.SwfState = this.SwfConnecting;
			this.flexApp.AttachIncomingStream(farID, farUsername);
			this.SwfState = this.SwfConnected;
		}
		
	},
	CallRejected: function(farID, farUsername) {
		// XXX TODO API should call this function when the call is rejected by farUsername
		if (this.SwfState == this.SwfCalling) {
			this.SwfState = this.SwfReady;
		}
		
	},
	CallEnd: function(farUsername) {
		// XXX TODO call this on the side of the client that ended a call
		// there is no need to call this if the SWF is being destroyed
		if ((this.SwfState == this.SwfCalling) ||
				(this.SwfState == this.SwfConnected) ||
				(this.SwfState == this.SwfConnecting)) {
			this.SwfState = this.SwfDisconnecting;
			this.flexApp.ClientReset();
			this.SwfState = this.SwfReady;
		}
		
	},
	ClientReset: function() {
		// this likely doesn't need to be called outside of this class
		if (this.SwfState >= this.SwfReady) {
			this.CallEnd();
		}
		this.flexApp.ClientReset();
		
	},
	
	// Misc functions
	GetLog: function(logging) {
		if (this.SwfState >= this.SwfLoaded) {
			if (logging == null) {
				logging = true;
			}
			return this.flexApp.GetLog(logging);
		}
	},
	GetFarID: function() {
		id = "";
		if (this.SwfState >= this.SwfLoaded) {
			id = this.flexApp.GetFarID();
		}
		return id;
	},
	GetNearID: function() {
		id = "";
		if (this.SwfState >= this.SwfLoaded) {
			id = this.flexApp.GetNearID();
		}
		return id;
	},
	
	// Control functions
	SetSpeakerVolume: function (volume) {
		if (this.SwfState != this.SwfError) {
			if (volume < 0) {
				volume = 0;
			} else if (volume > 1) {
				volume = 1;
			}
			this.flexApp.SetSpeakerVolume(volume);
		}
	},
	SetMicGain: function (gain) {
		if (this.SwfState != this.SwfError) {
			if (gain < 0) {
				gain = 0;
			} else if (gain > 100) {
				gain = 100;
			}
			this.flexApp.SetMicGain(gain);
		}
	},
	AudioMute: function() {
		if (this.SwfState == this.SwfConnected) {
			this.flexApp.AudioMute();
		}
	},
	AudioStart: function() {
		if (this.SwfState == this.SwfConnected) {
			this.flexApp.AudioStart();
		}
	},
	AudioToggle: function() {
		if (this.SwfState == this.SwfConnected) {
			this.flexApp.AudioToggle();
		}
	},
	VideoPause: function() {
		if (this.SwfState == this.SwfConnected) {
			this.flexApp.VideoPause();
		}
	},
	VideoStart: function() {
		if (this.SwfState == this.SwfConnected) {
			this.flexApp.VideoStart();
		}
	},
	VideoToggle: function() {
		if (this.SwfState == this.SwfConnected) {
			this.flexApp.VideoToggle();
		}
	},
	
//////////////////////////////////////////////////////////////
	// Callback functions
	LoginError: function() {
		this.SwfState = this.SwfError;
		Logger().log("LoginError()");
		
	},
	LoginNotConnected: function() {
		this.SwfState = this.SwfNotReady;
		Logger().log("LoginNotConnected()");
		
	},
	LoginConnecting: function() {
		this.SwfState = this.SwfNotReady;
		Logger().log("LoginConnecting()");
		
	},
	LoginConnected: function() {
		this.SwfState = this.SwfReady;
		Logger().log("LoginConnected()");
		Logger().log("Got Stratus ID of " + this.GetNearID());
		
	},
	LoginDisconnecting: function() {
		this.SwfState = this.SwfNotReady;
		$("loginstate").fireEvent("burn", "LoginDisconnecting()");
		
	},
	ChatError: function() {
		this.SwfState = this.SwfError;
		Logger().log("ChatError()");
		
	},
	ChatNotReady: function() {
		this.SwfState = this.SwfNotReady;
		Logger().log("ChatNotReady()");
		
	},
	ChatReady: function() {
		this.SwfState = this.SwfReady;
		Logger().log("ChatReady()");
		
	},
	ChatInitiated: function() {
		this.SwfState = this.SwfCalling; //XXX TODO
		Logger().log("ChatInitiated()");
		
	},
	ChatEstablished: function() {
		this.SwfState = this.SwfConnected;
		Logger().log("ChatEstablished()");
		
	},
	AudioError: function() {
		this.SwfState = this.SwfError;
		Logger().log("AudioError()");
		
	},
	AudioNotReady: function() {
		this.SwfState = this.SwfNotReady;
		Logger().log("AudioNotReady()");
		
	},
	AudioReady: function() {
		Logger().log("AudioReady()");
		
	},
	AudioTransmitting: function() {
		Logger().log("AudioTransmitting()");
		
	},
	AudioPaused: function() {
		Logger().log("AudioPaused()");
		
	},
	VideoError: function() {
		this.SwfState = this.SwfError;
		Logger().log("VideoError()");
		
	},
	VideoNotReady: function() {
		this.SwfState = this.SwfNotReady;
		Logger().log("VideoNotReady()");
		
	},
	VideoReady: function() {
		Logger().log("VideoReady()");
		Logger().log("Camera found.");
		//fire event to alert pip
		this.fireEvent('videoChatReset');
		
	},
	VideoTransmitting: function() {
		Logger().log("VideoTransmitting()");
		
	},
	VideoPaused: function() {
		Logger().log("VideoPaused()");
		
	},
	LogChanged: function() {
		//$("outputlog").fireEvent("burn", sim.GetLog(false));
	},
	VideoChatLoaded: function () {
	    this.SwfState = this.SwfLoaded;
	    this.flexApp = $("videochat");
	},
	SpeakerVolumeChanged: function () {
		
	},
	MicGainChanged: function () {
		
	}
});var VideoChatNotification = new Class({
	Extends: Notification,
	
	onBeforeInit: function(options){
		
		this.user = options.user;
		this.stratusId = options.stratusId;
		options.iconOptions = {user: this.user};
		options.timeout = 15000;
		options.hasActions = true;
		
		return options;
	},
	
	onInit: function(){
		
		this.acceptButton = new ButtonSmall({
			displayName: 'Accept',
			className: 'dark',
			action: 'check'
		});
		//$(this.ignoreButton).addEvent('click', this.closeNotification.bind(this));
		
		this.ignoreButton = new ButtonSmall({
			displayName: 'Ignore',
			className: 'dark',
			action: 'cross'
		});
		
		//TODO: use a this.addAction func, which will auto attach close event on click
		$(this.ignoreButton).addEvent('click', this.closeNotification.bind(this));
		$(this.acceptButton).addEvent('click', this.fireEvent.bind(this, ['videoChatAccept', this.user, this.stratusId]));
		
		$(this.acceptButton).addEvent('click', this.closeNotification.bind(this));
		
		$(this.acceptButton).inject(this.actions);
		$(this.ignoreButton).inject(this.actions);
	},
	
	getText: function(){
		var el = new Element('div', {'class': 'notification_text text11 light1'}).adopt(
			new Element('span', {'text': this.user.fullname})
		).appendText(' invited you to a video conference.');
		return el;
	}
	
});var VideoChatPopup = new Class({
	Extends: Popup,
	
	onCreate: function(){
		this.shown = true;
		this.close();
	},

	//this never closes!  jsut hides
	close: function(){
		if (!this.shown)
			return;
		
		this.left = this.popup.getStyle('left');
		this.popup.setStyle('left', '-5000px');
		this.shown = false;
	
	},
	
	show: function(user){
		if (this.shown)
			return;
		
		//set user data
		this.setTitleCustom(user); 
		
		this.popup.setStyle('left', this.left);
		this.shown = true;
		this._select();
	},
	
	setTitleCustom: function(user){
		var nav = this.navs.get(this.currentContent);
		var icon = new Icon20({user: user});
		var titleText = new Element('div', {
			'class': 'title_text',
			'text': TextUtility.unescapeQuotes('Video Conference with ' + user.fullname)
		});
		
		this.titleIcon = $(icon).replaces(this.titleIcon);
		this.titleText = titleText.replaces(this.titleText);
		
	},
	
	resetSwf: function(){
		this.contents.get(this.currentContent).resetSwf();
	}
	
});var VideoChatPopupContent = new Class({
	Extends: PopupContent,
	
	onBeforeInit: function(options){
		options.destroy = false;
		
		return options;
	},
	
	onInit: function(){
		
		this.videochatWrapper = new Element('div', {'id': 'videochatWrapper'}).inject(this.content);			
		this.actions = new Element('div', {'class': 'actions'}).inject(this.content);
		
		this.endButton = new ButtonMedium({
			displayName: 'End Conference',
			className: 'dark',
			action: 'cross'
		});
		$(this.endButton).inject(this.actions);
		$(this.endButton).addEvent('click', this.fireEvent.bind(this, ['videoChatEnd']));
	},
	
	onShow: function(){
		
		this.resetSwf();
	},
	
	resetSwf: function(){
		
		var flashvars = {
			bridgeName: "videochat"
		};
		var params = {
			menu: "false",
			allowscriptaccess: "always",
			play: "true",
			quality: "high",
			flashvars: "bridgeName=videochat",
			allowFullScreen: "true",
			wmode: "window"
		};
		var attributes = {
			id: "videochat",
			name: "videochat"
		};
			
			swfobject.embedSWF("/swf/VideoChat.swf", "videochatWrapper", "400", "300", "10.0.0", "expressInstall.swf", flashvars, params, attributes);

	},
	
	onHide: function(){
		//$('videochatWrapper').inject('body');
	}
	
});var Appstore = new Class({
	Extends: App,
	
	onStart: function(){
		//setup and create navs
		this.setupNav();
		
	},
	
	onStop: function(){
		
	},
	
	userLoggedIn: function(){

	},
	
	userLoggedOut: function(){
		this.stop();
	}
	
});Appstore.implement({
	requests: [
		{
			name: 'apps_get_all',
			url: '/api/apps/app/get_all'
		},
		{
			name: 'app_create',
			params: ['name', 'type', 'likes', 'state'],
			url: '/api/apps/app/create'
		},
		{
			name: 'app_update',
			params: ['app_id', 'name', 'type', 'likes', 'state'],
			url: '/api/apps/app/update'
		},
		{
			name: 'app_likes_increment',
			params: ['app_id'],
			url: '/api/apps/app/increment'
		},
		{
			name: 'app_likes_decrement',
			params: ['app_id'],
			url: '/api/apps/app/decrement'
		}
		
	]
});Appstore.implement({
	setupNav: function(){
	
		/*this.navAdd(new Nav({
			iconOptions: {
				iconName: 'updates'
			},
			displayName: 'My apps',
			name: 'installed_apps',
			onClick: this.fireEvent.bind(this, ['viewSwitch', this.appId, 'installed_apps'])
		}));*/
		
		this.navAdd(new Nav({
			iconOptions: {
				iconName: 'updates'
			},
			displayName: 'All Applications',
			name: 'apps',
			onClick: this.fireEvent.bind(this, ['viewSwitch', this.appId, 'apps', 'apps'])
		}));
		
		var menu = new AppstoreMenu({isDefault: true});
		this.menuAdd('apps', menu);
		
		var content = new AppstoreContent({isDefault: true});
		this.contentAdd('apps', content);
		
	}
	
	
});var AppstoreContent = new Class({
	Extends: Content,

	onBeforeInit: function(options){
		return options;
	},
	              
	onInit: function(){
	
		this.loaded = false;
		//create loader
		this.loader = new ItemLoader({
			idField: 'app_id',
			sortField: 'name',
			sortAlpha: true,
			createElementFunc: AppstoreItemUtility.createAppstoreItem,
			emptyEl: new Element('div', {'class': 'post empty', 'text': 'There are no apps to install'})
		});
		
		$(this.loader).inject(this.content);
		
		this.appsInstalled = UserUtility.getInstalledApps();
	
	},
	
	onShow: function(first){
		if (first)
			this.call('appstore', 'apps_get_all', null, this.loadAppsSuccess.bind(this), null);
	},
	
	onHide: function(){
		
	},
	
	loadAppsSuccess: function(data){
		
		if (!$defined(data.apps) || data.apps.length == 0) {
		}
		
		data.apps.each(function(app) {
			if ($defined(this.appsInstalled[app.app_id]))
				app.installed = true;
			else
				app.installed = false;
			this.loader.process(app);
		}, this);
		
	}

	
	
});var AppstoreMenu = new Class({
	Extends: Menu,
	
	EventHandlers: [
	                ],
	
	onBeforeInit: function(options){
		options.displayName = 'Pip.io App Store';
		return options;
	},
	
	onInit: function(){
		
		var appstoreText = 'You can install apps on your Pip.io account to add additional features.  More apps are coming very soon!';
		var appstoreText2 = 'If you\'re a developer and interested in creating an app for the Pip.io platform, please contact developers@pip.io';
		
		new Element('div', {'class': 'text_section light1', 'text': TextUtility.unescape(appstoreText)}).inject(this.menu);
		new Element('div', {'class': 'text_section light1 text11', 'text': TextUtility.unescape(appstoreText2)}).inject(this.menu);
		
	
	}
});var AppstoreItemUtility = {
	createAppstoreItem: function(app){
		var el = ItemUtility.createPostBubble('post app', true);
		
		el.set('id', 'appstore_item_' + app.app_id);
		
		var installMethod = app.name + 'Install';
		var uninstallMethod = app.name + 'Uninstall';
		
		new Element('img', {'class': 'app_icon', 'src': app.info.icon}).inject(el);
		new Element('div', {'class': 'description', 'text': TextUtility.unescape(app.info.description)}).inject(el);
		
		var appname = new Element('div', {'class': 'user_name clickable', 'text': TextUtility.unescape(app.display_name)}).inject(el);
		
		
		if (app.type == 3) {
			var installButton = new ButtonMedium({
				displayName: 'Coming Soon!',
				action: 'goto dark'
			});
			
			new Element('div', {'class': 'actions'}).adopt(
					$(installButton)
				).inject(el);
		}
		else
		{
			var uninstallButton = new ButtonMedium({
				className: 'uninstall',
				displayName: 'Uninstall Application',
				action: 'goto dark'
			});
			$(uninstallButton).addEvent('click', pipio.dispatchEvent.bind(pipio, [uninstallMethod, app]));
			
			var installButton = new ButtonMedium({
				className: 'install',
				displayName: 'Install Application',
				action: 'goto dark'
			});
			$(installButton).addEvent('click', pipio.dispatchEvent.bind(pipio, [installMethod, app]));
			
		
			if (app.installed)
				el.addClass('installed');
			else
				el.addClass('uninstalled');
			
			new Element('div', {'class': 'actions'}).adopt(
					$(installButton),
					$(uninstallButton)
				).inject(el);
		}
		
		
		
		
		return el; 
	}
		
		
};var Contacts = new Class({
	Extends: App,

	EventHandlers: [
	                'userSwitched',
	                'userStatusUpdated',
	                'userStatusCleared',
	                'userOnline',
	                'userOffline',
	                'userPresenceReceived',
	                'selfPresenceReceived',
	                'selfPresenceUpdate',
	                'userVideoEnabled',
	                'showStatusUpdatePopup',
	                'userProfilePicUpdated',
	                
	                'xmppConnected',
	                'xmppConnecting',
	                'xmppDisconnected',
	                
	                //contact management events
	                'contactAdded',
	                'contactGroupAdded',
	                'contactDeleted',
	                'contactGroupDeleted',
	                'contactGroupMoved',
	                
	                'contactRequestAdded',
	                'contactRequestDeleted',
	                
	                'connectionRequestAccept',
	                'connectionRequestDelete',
	                'connectionRequestCreate',
	                
	                'connectionDelete',
	                
	                'userSubscribe',
	                
	                'userUnsubscribe',
	                
	                'showGroupPickerPopup',
	                'showCreateGroupPopup'
	],
	
	onStart: function(){
		//setup and create navs
		this.setupNav();
	},
	
	onStop: function(){
	},
	
	userSwitched: function(){
		if (this.isLoggedIn())
			this.userLoggedIn();
		else
			this.userLoggedOut();
	},
	
	userLoggedIn: function(){
		this.start();
		this.initBuddylist();
	},
	
	userLoggedOut: function(){
		this.stop();
	},
	
	onCreate: function(){
		//dock button puts the app in docked mode
		//for contacts, minButton is inserted into "self" element
		this.minButtonAction = new Element('div', {'class': 'action pivot_up'});
		this.minButton = new Element('div', {'class': 'button_nav'}).adopt(
			this.minButtonAction
		).inject('self');
		this.minButton.addEvent('click', this.toggleNav.bind(this));
		
		//kill the header
		this.navHeader.destroy();
		
		//insert disconnected & connecting areas
		this.connecting = new Element('div', {'class': 'connect_state'}).adopt(
			new Element('div', {'class': 'connect_text light2', 'text': 'Connecting to Pip.io...'}),
			new Element('div', {'class': 'progress'})
		).inject('nav');
		DomUtility.hide(this.navSection);
	},
	
	onDestroy: function(){
		this.connecting.destroy();
	},
	
	toggleNav: function(){
		
		if (!this.isExpanded)
			this.expand();
		else
			this.collapse();
	},
	
	expand: function(){
		if (!this.isExpanded)
		{
			DomUtility.expand(this.navWrapper);
			this.minButtonAction.addClass('pivot_up');
			this.minButtonAction.removeClass('pivot_down');
			this.isExpanded = true;
		}
	},
	
	collapse: function(){
		if (this.isExpanded)
		{
			DomUtility.collapse(this.navWrapper);
			this.minButtonAction.addClass('pivot_down');
			this.minButtonAction.removeClass('pivot_up');
			this.isExpanded = false;
		}
	},
	
	xmppConnected: function(){
		DomUtility.show(this.navSection);
		DomUtility.hide(this.connecting);
	},
	
	xmppDisconnected: function(){
		DomUtility.show(this.connecting);
		DomUtility.hide(this.navSection);
	},
	
	xmppConnecting: function(){
		DomUtility.show(this.connecting);
		DomUtility.hide(this.navSection);
	},
	
	//contact request accept & delete
	connectionDelete: function(username){
		this.call('contacts', 'connection_delete', {username: username}, null, null);
	},
	
	connectionRequestCreate: function(username){
		this.call('contacts', 'connection_request_create', {username: username}, null, null);
	},
	
	connectionRequestAccept: function(username){
		this.call('contacts', 'connection_request_accept', {username: username}, null, null);
	},
	
	connectionRequestDelete: function(username){
		this.call('contacts', 'connection_request_delete', {username: username}, null, null);
	},
	
	userSubscribe: function(username){
		this.call('contacts', 'user_subscribe', {username: username}, null, null);
	},
	
	userUnsubscribe: function(username){
		this.call('contacts', 'user_unsubscribe', {username: username}, null, null);
	},
	
	userProfilePicUpdated: function(data){
		if (!$defined(data.username))
			return;
		
		var user = this.getUser(data.username);
		var user_id = user.user_id;	
		var username = user.username;
		
		var version = UserUtility.profilePicVersionGet(user_id);
		version++;
		UserUtility.profilePicVersionSet(user_id, version);
		
		user.profile_pic_16 = user.profile_pic_16.split('?')[0] + '?' + version;
		user.profile_pic_20 = user.profile_pic_20.split('?')[0] + '?' + version;
		user.profile_pic_26 = user.profile_pic_26.split('?')[0] + '?' + version;
		user.profile_pic_32 = user.profile_pic_32.split('?')[0] + '?' + version;
		user.profile_pic_42 = user.profile_pic_42.split('?')[0] + '?' + version;
		user.profile_pic_60 = user.profile_pic_60.split('?')[0] + '?' + version;
		user.profile_pic_100 = user.profile_pic_100.split('?')[0] + '?' + version;
		user.profile_pic_200 = user.profile_pic_200.split('?')[0] + '?' + version;	
			
		
		$$('.profile_pic_16_' + username).each(function(el){
			el.set('src', user.profile_pic_16);
		});
		
		$$('.profile_pic_20_' + username).each(function(el){
			el.set('src', user.profile_pic_20);
		});
		
		$$('.profile_pic_60_' + username).each(function(el){
			el.set('src', user.profile_pic_60);
		});
		
		$$('.profile_pic_26_' + username).each(function(el){
			el.set('src', user.profile_pic_26);
		});
		
		$$('.profile_pic_32_' + username).each(function(el){
			el.set('src', user.profile_pic_32);
		});
		
		$$('.profile_pic_42_' + username).each(function(el){
			el.set('src', user.profile_pic_42);
		});
		
		$$('.profile_pic_100_' + username).each(function(el){
			el.set('src', user.profile_pic_100);
		});
		
		$$('.profile_pic_200_' + username).each(function(el){
			el.set('src', user.profile_pic_200);
		});
		
	},
	//destroy all element and free up resources, this app is no longer running
	//special override ver for Contacts
	destroyApp: function(){
		this.contents.each(function(content){
			content.destroy();
		});
		this.contents.empty();
		
		this.menus.each(function(menu){
			menu.destroy();
		});
		this.menus.empty();
		
		this.navs.each(function(nav){
			nav.destroy();
		});
		this.navs.empty();
		
		this.navHeader.destroy();
		this.nav.destroy();
		this.navSection.destroy();
		
		this.fireEvent('appClose', this.appId);
		
		if ($defined(this.onDestroy))
			this.onDestroy();
	}
});Contacts.implement({
	initBuddylist: function(){
		//hashes to track elements
		this.contacts = $H();
		this.contactRequests = $H();
		this.groups = $H();
		this.css = new CSS();
	
		this.getGroups().each(function(group){
			this.groupAdd(group);
		}, this);
		
		this.getContacts().each(function(user){
			this.contactAdd(user);
		}, this);
		
		this.getContactRequests().each(function(user){
			this.contactRequestAdd(user);
		}, this);
	},
	
	contactAdded: function(user){
		if (this.contacts.has(user.username))
			return;
		
		this.contactAdd(user);
	},
	
	contactDeleted: function(username){
		this.contactDelete(username);
	},
	
	contactRequestAdded: function(user){
		if (this.contactRequests.has(user.username))
			return;
		
		this.contactRequestAdd(user);
	},
	
	contactRequestDeleted: function(username){
		if (!this.contactRequests.has(username))
			return;
		
		this.contactRequestDelete(username);
	},
	
	contactGroupAdded: function(group){
		if (this.groups.has(group.group_id))
			return;
		
		this.groupAdd(group);
	},
	
	contactGroupDeleted: function(group){
		if (!this.groups.has(group.group_id))
			return;
		
		this.groupDelete(group);
	},
	
	contactGroupMoved: function(user){
		this.contactDelete(user.username);
		Logger().log('contactGroupMoved ' + user.username + ' group_id ' + user.group_id);
		this.contactAdd(user);
		
	},
	
	selfPresenceUpdate: function(show){
		if ($defined(show) && this.getPrivateUser().show == show)
			return;
		
		if ($defined(show))
			this.getPrivateUser().show = show;
		this.p.xmpp.updateSelfPresence(this.getPrivateUser().show, true);
	},
	
	userVideoEnabled: function(username, videoEnabled){
		if (this.contacts.has(username))
		{
			this.contacts.get(username).videoEnabled(videoEnabled);
		}
	},
	
	userStatusUpdated: function(data){
		if ($defined(this.contacts) && this.contacts.has(data.username))
		{
			this.contacts.get(data.username).statusMsgUpdated(data.status_message.status);
		}
	},
	
	userStatusCleared: function(data){
		if ($defined(this.contacts) && this.contacts.has(data.username))
		{
			this.contacts.get(data.username).statusMsgUpdated('');
		}
	},
	
	userOnline: function(username){
		if (this.contacts.has(username))
		{
			//Logger().log(username + ' is online');
			var user = this.getContact(username);
			
			if (user.status_message.status == '')
				this.contacts.get(username).online(false);
			else
				this.contacts.get(username).online(true);
			
			if (!user.online)
			{
				user.online = true;
				//update group
				//this.groups.get(user.group_id).userOnline();
			}
		}
	},
	
	userOffline: function(username){
		if (this.contacts.has(username))
		{
			//Logger().log(username + ' offline');
			this.contacts.get(username).offline();
			var user = this.getContact(username);
			
			if (user.online)
			{
				user.online = false;
				//update group
				//this.groups.get(user.group_id).userOffline();
			}
		}
	},
	
	userPresenceReceived: function(username, show, online){
		if (online)
		{
			this.userOnline(username);
			if (show == '')
			{
				this.setUserOnlineClass(username);
			}
			else if (show == 'away')
			{
				this.setUserAwayClass(username);
			}
			else if (show == 'xa')
			{
				this.setUserIdleClass(username);
			}
			else if (show == 'dnd')
			{
				this.userOffline(username);
				this.setUserOfflineClass(username);
			}
		}
		else
		{
			this.userOffline(username);
			this.setUserOfflineClass(username);
		}
		
		//update show val in user obj
		this.getContact(username).show = show;
	},
	
	selfPresenceReceived: function(show, online){
		if (online)
		{
			if (show == '')
				this.setUserOnlineClass(this.getPrivateUser().username);
			else if (show == 'away')
				this.setUserAwayClass(this.getPrivateUser().username);
			else if (show == 'xa')
				this.setUserIdleClass(this.getPrivateUser().username);
			else if (show == 'dnd')
				this.setUserOfflineClass(this.getPrivateUser().username);
		}
		else
		{
			this.setUserOfflineClass(this.getPrivateUser().username);
		}
	},
	
	setUserOnlineClass: function(username){
		//Logger().log('setting .online_status_' + username + ' to online');
		//set profile pic status color
		this.css.add_rule(
			'.online_status_' + username, {
				'border-color': '#42a409 !important'
			}
		).refresh();
	},
	
	setUserIdleClass: function(username){
		//Logger().log('setting .online_status_' + username + ' to idle');
		//set profile pic status color
		this.css.add_rule(
			'.online_status_' + username, {
				'border-color': '#cc8513 !important'
			}
		).refresh();
	},
	
	setUserAwayClass: function(username){
		//Logger().log('setting .online_status_' + username + ' to away');
		//set profile pic status color
		this.css.add_rule(
				'.online_status_' + username, {
					'border-color': '#b0191f !important'
			}
		).refresh();
	},
	
	setUserOfflineClass: function(username){
		this.css.remove_rule('.online_status_' + username);
		this.css.refresh();
	}
});Contacts.implement({
	requests: [
		{
			name: 'connection_group_create',
			params: ['name'],
			url: '/api/user/connection/group/create'
		},
		{
			name: 'connection_group_delete',
			params: ['group_id'],
			url: '/api/user/connection/group/delete'
		},
		{
			name: 'connection_group_move',
			params: ['username', 'group_id'],
			url: '/api/user/connection/group/move'
		},
		{
			name: 'connection_delete',
			params: ['username'],
			url: '/api/user/connection/delete'
		},
		{
			name: 'connection_request_create',
			params: ['username'],
			url: '/api/user/connection/request/create'
		},
		{
			name: 'connection_request_accept',
			params: ['username'],
			url: '/api/user/connection/request/accept'
		},
		{
			name: 'connection_request_delete',
			params: ['username'],
			url: '/api/user/connection/request/delete'
		},
		{
			name: 'user_subscribe',
			params: ['username'],
			url: '/api/user/stream/subscribe'
		},
		{
			name: 'user_unsubscribe',
			params: ['username'],
			url: '/api/user/stream/unsubscribe'
		},
		{
			name: 'search_user',
			params: ['query'],
			url: '/api/user/search/user'
		},
		{
			name: 'search_email',
			params: ['email_address', 'password'],
			url: '/api/user/search/email'
		},
		{
			name: 'invite_create',
			params: ['email', 'message'],
			url: '/api/user/invite/create'
		}
	
	]
});Contacts.implement({
	setupNav: function(){
	
		this.navAdd(new Nav({
			iconOptions: {
				iconName: 'contacts',
				iconAction: 'add'
			},
			hasSubnavs: true,
			onClick: this.fireEvent.bind(this, ['viewSwitch', this.appId, 'contact_requests', 'contact_requests']),
			displayName: 'Contact Requests',
			defaultClosed: true,
			name: 'requests'
		}));
		
		//add Menu
		var contactRequestsMenu = new ContactRequestsMenu({displayName: 'Contact Requests'});
		this.menuAdd('contact_requests', contactRequestsMenu);
		
		var content = new ContactRequestsContent();
		this.contentAdd('contact_requests', content);
		
		
		this.navAdd(new Nav({
			iconOptions: {
				iconName: 'contacts',
				iconAction: 'add'
			},
			displayName: 'Create New Group...',
			onClick: this.fireEvent.bind(this, ['showCreateGroupPopup']),
			name: 'new_group',
			bottom: true
		}));
	},
	
	
	
	groupAdd: function(group){
		var groupNav = new GroupNav({
			group: group,
			onClick: this.fireEvent.bind(this, ['viewSwitch', this.appId, 'group_' + group.group_id, 'menu_' + group.group_id])
		});
		this.navAdd(groupNav);
		this.groups.set(group.group_id, groupNav);
		
		//add Menu
		if (group.group_id != 0)
			var groupMenu = new GroupMenu({displayName: group.name, group: group});
		else
			var groupMenu = new GroupMenu({displayName: group.name, group: group, unsorted: true});
		this.menuAdd('menu_' + group.group_id, groupMenu);
		
		var content = new GroupContent({group: group});
		this.contentAdd('group_' + group.group_id, content);
		
	},
	
	groupDelete: function(group){
		
		this.navDelete('group_' + group.group_id);
		
		this.menuClose('menu_' + group.group_id);
		this.contentClose('group_' + group.group_id);
	},
	
	contactAdd: function(user){
		//parent group
		var contactNav = new ContactNav({user: user});
		this.navAdd(contactNav);
		
		//drag
		/*$(contactNav).addEvent('click', (function(){
			this.makeDraggable();
			
		}).bind($(contactNav)));*/
		
		this.contacts.set(user.username, contactNav);
	},
	
	contactDelete: function(username){
		if (!this.contacts.has(username))
			return;
		
		this.contacts.get(username).destroy();
		this.navDelete('contact_' + username);
		this.contacts.erase(username);
		
	},
	
	contactRequestAdd: function(user){
		
		var contactNav = new ContactRequestNav({user: user});
		this.navAdd(contactNav);
		this.contactRequests.set(user.username, contactNav);
		
		this.fireEvent('alertAdd', '1_requests');
	},
	
	contactRequestDelete: function(username){
		if (!this.contactRequests.has(username))
			return;
		
		Logger().log('deleting contact request nav ' + username);
		this.contactRequests.get(username).destroy();
		this.contactRequests.erase(username);
		this.navDelete('contact_request_' + username);
		
		this.fireEvent('alertClear', '1_requests', 1);
	},
	
	//popups
	showStatusUpdatePopup: function(){
		if ($defined(this.statusUpdatePopup))
			this.statusUpdatePopup.close();
			
		this.createStatusUpdatePopup();
	},
	
	createStatusUpdatePopup: function(){
		this.statusUpdatePopup = new Popup({
			size: {x: 350, y: 100},
			resizable: false,
			dockable: false,
			className: 'statusUpdate',
			onClose: this.destroyStatusUpdatePopup.bind(this)
		});
		
		//add popup content
		var nav = new Nav({
			iconOptions: {user: this.getPrivateUser()},
			displayName: 'Update Your Status',
			closable: true
		});
		
		var content = new StatusUpdatePopupContent({user: this.getPrivateUser()});
		
		this.statusUpdatePopup.addContent('status_update', nav, content);
	},
	
	destroyStatusUpdatePopup: function(){
		this.statusUpdatePopup = null;
	},
	
	showGroupPickerPopup: function(user){
		if ($defined(this.groupPickerPopup))
			this.groupPickerPopup.close();

		this.createGroupPickerPopup(user);
	},
	
	createGroupPickerPopup: function(user){
		this.groupPickerPopup = new Popup({
			size: {x: 200, y: 250},
			resizable: false,
			dockable: false,
			className: 'groupPicker',
			onClose: this.destroyGroupPickerPopup.bind(this)
		});
		
		//add popup content
		var nav = new Nav({
			iconOptions: {user: user},
			displayName: 'Edit ' + TextUtility.unescape(user.fullname),
			closable: true
		});
		
		var content = new GroupPickerPopupContent({user: user});
		
		this.groupPickerPopup.addContent('group_picker', nav, content);
	},
	
	destroyGroupPickerPopup: function(){
		this.groupPickerPopup = null;
	},
	
	showCreateGroupPopup: function(){
		if ($defined(this.createGroupPopup))
			this.createGroupPopup.close();

		this.createCreateGroupPopup();
	},
	
	createCreateGroupPopup: function(){
		this.createGroupPopup = new Popup({
			size: {x: 350, y: 74},
			resizable: false,
			dockable: false,
			className: 'createGroup',
			onClose: this.destroyCreateGroupPopup.bind(this)
		});
		
		//add popup content
		var nav = new Nav({
			iconOptions: {iconName: 'contacts', iconAction: 'add'},
			displayName: 'Create New Group',
			closable: true
		});
		
		var content = new CreateGroupPopupContent({});
		
		this.createGroupPopup.addContent('create_group', nav, content);
	},
	
	destroyCreateGroupPopup: function(){
		this.createGroupPopup = null;
	}
	
	
	
	
	
});var ContactRequestsContent = new Class({
	Extends: Content,
	
	EventHandlers: [
	                	'contactRequestAdded',
	                	'contactRequestDeleted'
	                ],
	
	              
	onInit: function(){
	
		this.loaded = false;
		//create loader
		this.loader = new ItemLoader({
			idField: 'username',
			sortField: 'last_name',
			sortAlpha: true,
			createElementFunc: ContactItemUtility.createContactRequestItem,
			emptyEl: new Element('div', {'class': 'post empty', 'text': 'You do not have any contact requests'})
		});
		
		$(this.loader).inject(this.content);
		
		
	
	},
	
	onShow: function(first){
		if (first)
			this.populateContacts();
	},
	
	onHide: function(){
		
	},
	
	populateContacts: function(){
		
		var contacts = this.getContactRequests(); //user hash of group members
		contacts.each(function(user){
			this.loader.process(user);
		}, this);
		
		this.loaded = true;
	},
	
	contactRequestAdded: function(user){
		this.loader.process(user);
	},
	
	contactRequestDeleted: function(username){
		this.loader.remove(username);
	}
	
});var GroupContent = new Class({
	Extends: Content,
	
	EventHandlers: [
	                'contactAdded',
	                'contactDeleted',
	                'contactGroupMoved'
	                ],
	
	onBeforeInit: function(options){
		this.group = options.group;
		return options;
	},
	              
	onInit: function(){
	
		this.loaded = false;
		//create loader
		this.loader = new ItemLoader({
			idField: 'user_id',
			sortField: 'last_name',
			sortAlpha: true,
			createElementFunc: ContactItemUtility.createContactItem,
			emptyEl: new Element('div', {'class': 'post empty', 'text': 'There are no contacts in this group'})
		});
		
		$(this.loader).inject(this.content);
		
		
	
	},
	
	onShow: function(first){
		if (first)
			this.populateContacts();
	},
	
	onHide: function(){
		
	},
	
	populateContacts: function(){
		
		var contacts = this.group.users; //user hash of group members
		contacts.each(function(user){
			this.loader.process(user);
		}, this);
		
		this.loaded = true;
	},
	
	contactAdded: function(user){
		if (!this.loaded || user.group_id != this.group.group_id)
			return;
		
		this.loader.process(user);
	},
	
	contactDeleted: function(username, group_id){
		if (!this.loaded || group_id != this.group.group_id)
			return;
		
		var user = this.getUser(username);
		this.loader.remove(user.user_id);
	},
	
	contactGroupMoved: function(user){
		if (user.group_id == this.group.group_id)
			this.loader.process(user);
		else
			this.loader.remove(user.user_id);
	}
	
	
});var ContactRequestsMenu = new Class({
	Extends: Menu,
	
	strings: {
		contactRequestsText: 'These users requested to add you as their contact'
	
	},
	
	onInit: function(){
	
		new Element('div', {'class': 'text_section light1', 'text': this.strings.contactRequestsText}).inject(this.menu);
		
	
	}
	
});var GroupMenu = new Class({
	Extends: Menu,
	
	strings: {
		groupMenuText: 'These are contacts in your group "{0}"',
		unsortedGroupMenuText: 'These are contacts that have not been added to a group'
	},
	
	onBeforeInit: function(options){
		this.group = options.group;
		
		this.unsorted = options.unsorted || false;
		return options;
	},
	
	onInit: function(){
		
		if (this.unsorted)
			new Element('div', {'class': 'text_section light1', 'text': String.format(this.strings.unsortedGroupMenuText)}).inject(this.menu);
		else
			new Element('div', {'class': 'text_section light1', 'text': String.format(this.strings.groupMenuText, TextUtility.unescape(this.group.name))}).inject(this.menu);
		
		
		if (!this.unsorted)
		{
				
			//delete button
			this.deleteButton = new ButtonMedium({
				displayName: 'Delete Group',
				className: 'dark',
				action: 'cross'
			});
			
			var name = 'groupDelete_' + this.group.group_id;
			var title = 'Delete ' + this.group.name;
			var message = 'Are you sure you want to delete the group ' + this.group.name + '?';
			var func = this.deleteGroup.bind(this);
			$(this.deleteButton).addEvent('click', pipio.dispatchEvent.bind(pipio, ['showConfirmation', name, title, message, func]));
			
			
			//$(this.deleteButton).addEvent('click', this.deleteGroup.bind(this));
			
			new Element('div', {'class': 'button_section'}).adopt(
				$(this.deleteButton)
			).inject(this.menu);
	
		}
	},
	
	deleteGroup: function(){
		
		var params = {group_id: this.group.group_id};
		
		this.call('contacts', 'connection_group_delete', params, this.deleteGroupSuccess.bind(this), this.deleteGroupFail.bind(this));
		
		this.deleteButton.showProgress();
	},
	
	deleteGroupSuccess: function(data){
		this.deleteButton.hideProgress();
	},
	
	deleteGroupFail: function(){
		this.deleteButton.hideProgress();
	}
	
});var ContactNav = new Class({
	Extends: Nav,
	
	//custom extended Nav for each contact element in buddylist
	onBeforeInit: function(options){
		
		//custom properties
		this.user = options.user;
		this.isOnline = false;
		
		//setup the options
		options.name = 'contact_' + this.user.username;
		options.iconOptions = {user: this.user};
		options.onClick = this.fireEvent.bind(this, ['chatStart', this.user.username]);
		options.displayName = this.user.fullname;
		options.className = 'contact';
		options.parentName = 'group_' + this.user.group_id;
		
		return options;
	},
	
	onInit: function(){
		
		//replace icon w/ bigger one for contact nav
		$(this.icon).destroy();
		this.icon = new Icon20(this.iconOptions);
		$(this.icon).inject(this.nav);
		$(this.icon).addEvent('click', this.openUser.bindWithEvent(this));
		
		//add icon for video chat
		this.videoChatIcon = new Icon({
			iconName: 'video_chat'
		});
		$(this.videoChatIcon).inject(this.nav);
		$(this.videoChatIcon).addEvent('click', this.videoChatStart.bindWithEvent(this));
		
		//add div for status msg
		this.statusMsg = new Element('div', {
			'class': 'status_msg status_msg_' + this.user.username,
			'text': TextUtility.unescape(this.user.status_message.status)
		}).inject(this.nav);
		
		if (this.user.online)
		{
			if (this.user.status_message.status == '')
				this.online(false);
			else
				this.online(true);
		}
	},
	
	statusMsgUpdated: function(status_msg){
		this.statusMsg.set('text', TextUtility.unescape(status_msg));
		if (this.isOnline)
		{
			if (status_msg == '')
			{
				DomUtility.setHeight(this.nav, 22);
			}
			else
			{
				DomUtility.setHeight(this.nav, 36);
			}
		}
	},
	
	online: function(hasStatus){
		if (hasStatus)
			DomUtility.expandFade(this.nav, 36);
		else
			DomUtility.expandFade(this.nav, 22);
		
		this.navWrapper.addClass('online');
		this.isOnline = true;
	},
	
	offline: function(){
		DomUtility.collapseFade(this.nav);
		this.navWrapper.removeClass('online');
		this.isOnline = false;
	},
	
	videoEnabled: function(enabled){
		if (enabled)
			this.nav.addClass('videoEnabled');
		else
			this.nav.removeClass('videoEnabled');
	},
	
	openUser: function(e){
		e.stopPropagation();
		//TODO: this will fire event to open User app instance of this user
		this.fireEvent('showUser', this.user);
	},
	
	videoChatStart: function(e){
		e.stopPropagation();
		
		this.fireEvent('videoChatStart', this.user);
		
	}
	
	

});var ContactRequestNav = new Class({
	Extends: Nav,
	
	//custom extended Nav for each contact element in buddylist
	onBeforeInit: function(options){
		
		//custom properties
		this.user = options.user;
		
		//setup the options
		options.name = 'contact_request_' + this.user.username;
		options.iconOptions = {user: this.user};
		options.onClick = this.fireEvent.bind(this, ['showUser', this.user]);
		options.displayName = this.user.fullname;
		options.className = 'contact request';
		options.parentName = 'requests';
		
		return options;
	},
	
	onInit: function(){
		
		//replace icon w/ bigger one for contact nav
		$(this.icon).destroy();
		this.icon = new Icon20(this.iconOptions);
		$(this.icon).inject(this.nav);
		
	}
	

});var GroupNav = new Class({
	Extends: Nav,
	
	onBeforeInit: function(options){
		
		//custom properties
		this.group = options.group;
		
		options.name = 'group_' + this.group.group_id;
		options.iconOptions = {iconName: 'contacts'};
		options.displayName = this.group.name;
		options.className = 'group_nav';
		options.hasSubnavs = true;
		
		return options;
	},
	
	onInit: function(){
		this.onlineCount = new Element('div', {'class': 'online_count text11 light2'}).inject(this.nav);
		DomUtility.hide(this.onlineCount);
	},
	
	onExpand: function(){
		this.hideOnlineCount();
	},
	
	onCollapse: function(){
		this.showOnlineCount();
	},
	
	showOnlineCount: function(){
		this.onlineCount.set('text', this.getOnlineCount());
		DomUtility.show(this.onlineCount);
	},
	
	hideOnlineCount: function(){
		DomUtility.hide(this.onlineCount);
	},
	
	getOnlineCount: function(){
		return this.subnavItems.getChildren('.online').length + '/' + this.subnavItems.getChildren().length;
	}
	
	
});var SubscribersNav = new Class({
	Extends: Nav,
	
	onBeforeInit: function(options){
		
		this.users = $H();
		
		options.name = 'subscribers';
		options.iconOptions = {iconName: 'broadcast'};
		options.displayName = 'Subscribers';
		options.className = 'subscribers_nav';
		options.hasSubnavs = true;
		
		return options;
	},
	
	onInit: function(){
		this.clear = new Element('div', {'style': 'clear: both'});
		
		this.list = new Element('div', {'class': 'menu_list'}).adopt(this.clear).inject(this.subnavs);
		
	},
	
	addUsers: function(users){
		
		$splat(users).each(function(user){
			user = UserUtility.processUser(user);
			
			if (!this.users.has(user.username))
			{
				var pic = ItemUtility.createProfilePic(user, '16a');
				pic.addEvent('click', this.fireEvent.bind(this, ['showUser', user]));
				pic.inject(this.clear, 'before');
				
				this.users.set(user.username, pic);
			}
		}, this);
		
	},
	
	removeUser: function(username){
		if (!this.users.has(username))
			return;
		
		this.users.get(username).destroy();
		this.users.erase(username);
	}
	
});var SubscriptionsNav = new Class({
	Extends: Nav,
	
	onBeforeInit: function(options){
		
		this.users = $H();
		
		options.name = 'subscriptions';
		options.iconOptions = {iconName: 'broadcast'};
		options.displayName = 'Subscriptions';
		options.className = 'subscribers_nav';
		options.hasSubnavs = true;
		
		return options;
	},
	
	onInit: function(){
		this.clear = new Element('div', {'style': 'clear: both'});
		
		this.list = new Element('div', {'class': 'menu_list'}).adopt(this.clear).inject(this.subnavs);
		
	},
	
	addUsers: function(users){
		
		$splat(users).each(function(user){
			user = UserUtility.processSource(user);
			
			if (!this.users.has(user.username))
			{
				var pic = ItemUtility.createProfilePic(user, '16a');
				if ($defined(user.user_id))
					pic.addEvent('click', this.fireEvent.bind(this, ['showUser', user]));
				else if ($defined(user.room_id))
					pic.addEvent('click', this.fireEvent.bind(this, ['showRoom', user]));
				
				pic.inject(this.clear, 'before');
				
				this.users.set(user.username, pic);
			}
		}, this);
		
	},
	
	removeUser: function(username){
		if (!this.users.has(username))
			return;
		
		this.users.get(username).destroy();
		this.users.erase(username);
	}
	
});var ContactPickerPopupContent = new Class({
	Extends: PopupContent,
	
	onBeforeInit: function(options){
		
		this.contacts = options.contacts;
		this.contactAddFunc = options.contactAddFunc;

		this.hasGroups = options.hasGroups || true;
		if (this.hasGroups)
		{
			this.groups = options.groups;
			this.groupAddFunc = options.groupAddFunc;
		}
		
		this.hasAllContacts = options.hasAllContacts || true;
		if (this.hasAllContacts)
			this.allContactsAddFunc = options.allContactsAddFunc;
	
		return options;
	},
	
	onInit: function(){
		
		this.css = new CSS();
		//INPUT
		this.input = new Element('input', {'type': 'text', 'maxlength': 50});
		this.input.addEvent('keyup', this.inputKeyUp.bindWithEvent(this));
		
		
		new Element('div', {'class': 'textarea_wrapper'}).adopt(
			this.input
		).inject(this.content);
		
		this.list = new Element('div', {'class': 'itemList'}).inject(this.content);
		
		
		this.closeButton = new ButtonMedium({
			displayName: 'Close',
			className: 'dark',
			action: 'cross'
		});
		
		new Element('div', {'class': 'actions'}).adopt(
			$(this.closeButton)
		).inject(this.content);
		$(this.closeButton).addEvent('click', this.closeContent.bind(this));
		
		this.insertContacts();
		
		//setup scroller
		this.scroll = new Fx.Scroll(this.list);
		
	},
	
	insertContacts: function(){
		
		//all contacts
		if (this.hasAllContacts)
		{
			var el = ShareBoxUtility.createAllContactsPickerItem();
			el.addEvent('click', this.addAllContacts.bind(this, [el]));
			el.inject(this.list);
			
			this.insertSeperator();
		}
		
		if (this.hasGroups)
		{
			this.groups.each(function(group){
				var el = ShareBoxUtility.createGroupPickerItem(group);
				el.addEvent('click', this.addGroup.bind(this, [group, el]));
				el.inject(this.list);
			
			}, this);
		
			this.insertSeperator();
		}
		
		this.contacts.getValues().sort(DataUtility.sortUsers).each(function(user){
			var el = ShareBoxUtility.createContactPickerItem(user);
			el.addEvent('click', this.addContact.bind(this, [user, el]));
			el.inject(this.list);
			
		}, this);
		
	},
	
	insertSeperator: function(){
		new Element('div', {'class': 'listItem seperator'}).inject(this.list);
	},
	
	addAllContacts: function(el){
		this.allContactsAddFunc();
		el.destroy();
		this.closeContent();
	},
	
	addContact: function(user, el){
		this.contactAddFunc(user);
		el.destroy();
	},
	
	addGroup: function(group, el){
		this.groupAddFunc(group);
		el.destroy();
	},
	
	inputKeyUp: function(e){
		
		if (e.key == 'esc')
		{
			e.target.value = '';
		}
		var val = e.target.value.trim().toLowerCase();
		if (val.length < 2)
		{
			this.list.removeClass('search');
			return;
		}
		
		this.list.addClass('search');
		
		if ($defined(this.lastSelector))
			this.css.remove_rule(this.lastSelector);
		
		
		var selector = ".listItem[val1^='" + val + "'], .listItem[val2^='" + val + "']";
		this.css.add_rule(selector, {'display': 'block !important'}).refresh();

		this.lastSelector = selector;
		
		//process select & enters
		//if (e.key == 'down' || e.key == 'up' || e.key == 'enter')
		//{
		//	this.selectItem(e);
		//	return;
		//}
	},
	
	focus: function(){
		this.input.focus.delay(500, this.input);
	},
	
	onClose: function(){
		if ($defined(this.lastSelector))
			this.css.remove_rule(this.lastSelector).refresh();
	},
	
	onShow: function(){
		if ($defined(this.scrollY))
			this.scroll.set(0, this.scrollY);
		
		this.focus();
	},
	
	onHide: function(){
		if ($defined(this.list))
			this.scrollY = this.list.getScroll().y;
	}
	
});var CreateGroupPopupContent = new Class({
	Extends: PopupContent,
	
	strings: {
		groupNameLabel: 'Group Name:',
		errorMessage: 'There was an error creating this group'
	},
	
	onInit: function(){
		
		this.groupNameInput = new Element('input', {'type': 'text', 'maxlength': '32'});
		this.groupNameInput.addEvent('keyup', this.inputKeyUp.bindWithEvent(this));
		
		new Element('div', {'class': 'input_section'}).adopt(
				new Element('div', {'class': 'label light', 'text': this.strings.groupNameLabel}),
				new Element('div', {'class': 'textarea_wrapper'}).adopt(
					this.groupNameInput
				)
			).inject(this.content);
		
		//ACTIONS
		
		this.closeButton = new ButtonMedium({
			displayName: 'Cancel',
			className: 'dark',
			action: 'cross'
		});
		$(this.closeButton).addEvent('click', this.closeContent.bind(this));
		
		this.actionButton = new ButtonMedium({
			displayName: 'Create Group',
			className: 'dark',
			action: 'check'
		});
		$(this.actionButton).addEvent('click', this.createGroup.bind(this));
		
		this.message = new Element('div', {'class': 'message error'});
		
		this.actions = new Element('div', {'class': 'actions'}).adopt(
			this.message,
			$(this.actionButton),
			$(this.closeButton)
		).inject(this.content);
	},
	
	inputKeyUp: function(e){
		if (e.key == 'enter')
		{
			if (this.groupNameInput.value.trim() != '')
			this.createGroup();
		}
	},
	
	createGroup: function(){
		
		var name = this.groupNameInput.value.trim();
		var params = {name: name};
		
		this.call('contacts', 'connection_group_create', params, this.createGroupSuccess.bind(this), this.createGroupFail.bind(this));
		
		this.actionButton.showProgress();
	},
	
	createGroupSuccess: function(){
		this.actionButton.hideProgress();
		this.closeContent();
	},
	
	createGroupFail: function(){
		this.actionButton.hideProgress();
		this.closeContent();
	},
	
	onShow: function(){
		this.focus();
	},
	
	focus: function(){
		this.groupNameInput.focus.delay(500, this.groupNameInput);
	}
	
});var GroupPickerPopupContent = new Class({
	Extends: PopupContent,
	
	onBeforeInit: function(options){
		
		this.user = options.user;
	
		return options;
	},
	
	onInit: function(){
		
		this.list = new Element('div', {'class': 'itemList'}).inject(this.content);
	
		this.closeButton = new ButtonMedium({
			displayName: 'Close',
			className: 'dark',
			action: 'cross'
		});
		
		new Element('div', {'class': 'actions'}).adopt(
			$(this.closeButton)
		).inject(this.content);
		$(this.closeButton).addEvent('click', this.closeContent.bind(this));
		
		this.insertGroups();
	},
	
	insertGroups: function(){
		
		var groups = this.getGroups();
		
		
		groups.each(function(group){
			var el = ShareBoxUtility.createGroupPickerItem(group);
			
			if (this.user.group_id == group.group_id)
			{
				el.addClass('on');
				this.selectedEl = el;
			}
			else
				el.addEvent('click', this.groupMove.bind(this, [group, el]));
			el.inject(this.list);
			
		}, this);
	},
	
	groupMove: function(group, el){
		
		var params = {
			username: this.user.username,
			group_id: group.group_id
		};
		
		this.call('contacts', 'connection_group_move', params, this.groupMoveSucess.bind(this), this.groupMoveFail.bind(this));
		this.selectedEl.removeClass('on');
		new Element('div', {'class': 'progress'}).inject(el);
	},
	
	groupMoveSucess: function(data){
		this.closeContent();
	},
	
	groupMoveFail: function(){
		this.closeContent();
	}
	
	
});var StatusUpdatePopupContent = new Class({
	Extends: PopupContent,

	onInit: function(){
	
		this.statusInput = new Element('textarea', {'maxlength': 500});
		this.statusInput.addEvent('keyup', this.inputKeyUp.bindWithEvent(this));
		
		
		new Element('div', {'class': 'textarea_wrapper'}).adopt(
				this.statusInput
		).inject(this.content);
	
		
		this.cancelButton = new ButtonMedium({
			displayName: 'Cancel',
			className: 'dark',
			action: 'cross'
		});
		$(this.cancelButton).addEvent('click', this.closeContent.bind(this));
		
		this.updateButton = new ButtonMedium({
			displayName: 'Update Status',
			className: 'dark',
			action: 'check'
		});
		$(this.updateButton).addEvent('click', this.updateStatus.bind(this));
		
		
		new Element('div', {'class': 'actions'}).adopt(
			$(this.updateButton),
			$(this.cancelButton)
		).inject(this.content);
	},
	
	onShow: function(){
		this.focus();
	},
	
	focus: function(){
		this.statusInput.focus.delay(500, this.statusInput);
	},
	
	inputKeyUp: function(e){
		if (e.key == 'enter')
		{
			var msg = e.target.value.trim();
			//dont let empty msg get sent
			if (msg == '')
				return;
			
			this.updateStatus();
		}
	},
	
	updateStatus: function(){
		var params = {username: '', body: this.statusInput.value.trim(), res: this.getSession()};
		
		this.call('pipio', 'publish_status', params, this.updateStatusSuccess.bind(this) , this.updateStatusFail.bind(this));
		this.updateButton.showProgress();
	},
	
	updateStatusSuccess: function(){
		this.closeContent();
	},
	
	updateStatusFail: function(){
		alert('There was an error updating your status!');
	}
	
});var ContactItemUtility = {
	createContactItem: function(user){
		var el = ItemUtility.createPostBubble('post contact', true);
		ItemUtility.createProfilePic(user, 60).inject(el);
		
		var username = new Element('div', {'class': 'user_name clickable', 'text': TextUtility.unescape(user.fullname)}).inject(el);
		username.addEvent('click', pipio.dispatchEvent.bind(pipio, ['showUser', user]));
		
		var viewButton = new ButtonMedium({
			displayName: 'View Profile',
			action: 'goto dark'
		});
		$(viewButton).addEvent('click', pipio.dispatchEvent.bind(pipio, ['showUser', user]));
		
		var editButton = new ButtonMedium({
			displayName: 'Edit Group',
			action: 'status dark'
		});
		$(editButton).addEvent('click', pipio.dispatchEvent.bind(pipio, ['showGroupPickerPopup', user]));
		
		new Element('div', {'class': 'actions'}).adopt(
			$(viewButton),
			$(editButton)
		).inject(el);
		
				
		var deleteAction = new Element('div', {'class': 'post_action delete'});
		
		var name = 'contactDelete_' + user.username;
		var title = 'Delete ' + user.fullname;
		var message = 'Are you sure you want to delete ' + user.fullname + ' from your contacts?';
		var func = pipio.dispatchEvent.bind(pipio, ['connectionDelete', user.username]);
		$(deleteAction).addEvent('click', pipio.dispatchEvent.bind(pipio, ['showConfirmation', name, title, message, func]));
		
		new Element('div', {'class': 'post_actions'}).adopt(
			deleteAction
		).inject(el);
		
		return el; 
	},
	
	createContactRequestItem: function(user){
		var el = ItemUtility.createPostBubble('post contact', true);
		ItemUtility.createProfilePic(user, 60).inject(el);
		
		var username = new Element('div', {'class': 'user_name clickable', 'text': TextUtility.unescape(user.fullname)}).inject(el);
		username.addEvent('click', pipio.dispatchEvent.bind(pipio, ['showUser', user]));
		
		var viewButton = new ButtonMedium({
			displayName: 'View Profile',
			action: 'goto dark'
		});
		$(viewButton).addEvent('click', pipio.dispatchEvent.bind(pipio, ['showUser', user]));
		
		var acceptButton = new ButtonMedium({
			displayName: 'Accept Request',
			action: 'check'
		});
		$(acceptButton).addEvent('click', pipio.dispatchEvent.bind(pipio, ['connectionRequestAccept', user.username]));
		
		var deleteButton = new ButtonMedium({
			displayName: 'Delete Request',
			action: 'cross'
		});
		$(deleteButton).addEvent('click', pipio.dispatchEvent.bind(pipio, ['connectionRequestDelete', user.username]));
		
		
		new Element('div', {'class': 'actions'}).adopt(
			$(viewButton),
			$(acceptButton),
			$(deleteButton)
		).inject(el);
		
		return el; 
	}
		
		
};var Facebook = new Class({
	Extends: App,
	
	EventHandlers: [
		'facebookInstall', 
		'facebookUninstall'
	],
	
	onStart: function(){
		this.appId = 5;
		
		FB_RequireFeatures(["Connect"], function() { 
			FB.Facebook.init("d29a9328a49d798da896066499c0d9d8", "/xd_receiver.htm", {"forceBrowserPopupForLogin":true, 'doNotUseCachedConnectState': true}); 
		});
		
		this.appsInstalled = UserUtility.getInstalledApps();
		// get user id from user_data
		if ($defined(this.appsInstalled[this.appId]))
				this.external_user_id = this.appsInstalled[this.appId];
		
		//setup and create navs
		this.setupNav();
		
	},
	
	onStop: function(){
		
	},
	
	userLoggedIn: function(){

	},
	
	userLoggedOut: function(){
		this.stop();
	}, 
	
	facebookInstall: function(){
		this.facebookInstallPopup = new Popup({
			size: {x: 350, y: 176},
			resizable: false,
			dockable: false,
			className: 'facebookInstall',
			onClose: this.destroyFacebookInstallPopup.bind(this)
		});
		
		//add popup content
		var nav = new Nav({
			iconOptions: {iconName: 'facebook'},
			displayName: 'Install Facebook',
			closable: true
		});
		
		var content = new FacebookInstallPopupContent({});
		
		this.facebookInstallPopup.addContent('facebook_install', nav, content);		
	},
	
	destroyFacebookInstallPopup: function(){
		this.facebookInstallPopup = null;
	},
	
	facebookUninstall: function(app) {
		this.call('facebook', 'facebook_disable', null, this.facebookUninstallSuccess.bind(this), null);
	},
	
	facebookUninstallSuccess: function (data) {
		
		var name = 'facebook_uninstall';
		var title = 'Facebook Uninstalled';
		var message = 'You have successfully disconnected your Facebook account';
		
		this.fireEvent('showAlert', name, title, message);
		
	}
	
});Facebook.implement({
	requests: [
		{
			name: 'facebook_enable',
			params: ['sync_posts', 'session_key', 'uid'],
			url: '/api/app/facebook/account/enable'
		},
		{
			name: 'facebook_disable',
			url: '/api/app/facebook/account/disable'
		},
		{
			name: 'facebook_friends_info',
			params: ['uid', 'limit', 'sort_field', 'number'],
			url: '/api/app/facebook/friends/info'
		},
		{
			name: 'facebook_newsfeed_load',
			params: ['start_time', 'end_time'],
			url: '/api/app/facebook/newsfeed/load'
		},
		{
			name: 'facebook_user_show',
			params: ['user_id'],
			url: '/api/app/facebook/user/profile_load'
		},
		{
			name: 'facebook_feed',
			params: ['user_id', 'start_time', 'end_time'],
			url: '/api/app/facebook/user/newsfeed_load'
		},
		{
			name: 'facebook_settings_set',
			params: ['key', 'value'],
			url: '/api/app/facebook/settings/set'
		},
		{
			name: 'facebook_albums',
			params: ['user_id', 'num_photos'],
			url: '/api/app/facebook/albums/get'
		},
		{
			name: 'facebook_photos',
			params: ['user_id', 'aid'],
			url: '/api/app/facebook/photos/get'
		},
		{
			name: 'facebook_notification_send',
			params: ['user_id', 'message'],
			url: '/api/app/facebook/notification/send'
		},
		{
			name: 'facebook_post_text',
			params: ['body', 'post_id', 'target_id'],
			url: '/api/app/facebook/text/item_create'
		},
		{
			name: 'facebook_post_link',
			params: ['body', 'url', 'post_id', 'target_id'],
			url: '/api/app/facebook/link/item_create'
		},
		{
			name: 'facebook_post_photo',
			params: ['body', 'photo', 'post_id', 'target_id'],
			url: '/api/app/facebook/photo/item_create',
			multipart: true
		},
		{
			name: 'facebook_comments',
			params: ['post_id'],
			url: '/api/app/facebook/comments/get'
		},
		{
			name: 'facebook_feed_delete',
			params: ['post_id', 'created_time'],
			url: '/api/app/facebook/item/delete'
		},
		{
			name: 'facebook_settings_set',
			params: ['key', 'val'],
			url: '/api/app/facebook/settings/set'	
		},
		{
			name: 'facebook_settings_get',
			url: '/api/app/facebook/settings/get'
		},
		{
			name: 'facebook_contacts_list',
			params: ['uid', 'limit', 'sort_field', 'number'],
			url: '/api/app/facebook/friends/info'
		},
		{
			name: 'facebook_post_like',
			params: ['post_id', 'like_count'],
			url: '/api/app/facebook/like'
		},
		{
			name: 'facebook_post_unlike',
			params: ['post_id', 'like_count'],
			url: '/api/app/facebook/unlike'
		}
	]
});Facebook.implement({
	setupNav: function(){
	
		this.navAdd(new Nav({
			iconOptions: {
				iconName: 'updates'
			},
			displayName: 'News Feed',
			name: 'updates',
			onClick: this.fireEvent.bind(this, ['viewSwitch', this.appId, 'updates', 'self_menu'])
		}));
		

		this.navAdd(new Nav({
			iconOptions: {
				iconName: 'profile_pic'
			},
			displayName: 'My Wall',
			name: 'wall',
			onClick: this.fireEvent.bind(this, ['viewSwitch', this.appId, 'wall', 'self_menu'])
		}));
		
		this.navAdd(new Nav({
			iconOptions: {
				iconName: 'profile_pic'
			},
			displayName: 'Friends',
			name: 'friends',
			onClick: this.fireEvent.bind(this, ['viewSwitch', this.appId, 'friends', 'self_menu'])
		}));
		
		this.navAdd(new Nav({
			iconOptions: {
				iconName: 'photo'
			},
			displayName: 'My Albums',
			name: 'albums',
			onClick: this.fireEvent.bind(this, ['viewSwitch', this.appId, 'albums', 'self_menu'])
		}));
		
		this.navAdd(new Nav({
			iconOptions: {
				iconName: 'photo'
			},
			displayName: 'Photos of me',
			name: 'tagged',
			onClick: this.fireEvent.bind(this, ['viewSwitch', this.appId, 'tagged', 'self_menu'])
		}));
		
		//add Menu
		var fbSelfMenu = new FacebookSelfMenu({user: this.getPrivateUser(), isDefault: true});
		this.menuAdd('fb_self_menu', fbSelfMenu);
		
		var content = new Content({isDefault: true});
		this.contentAdd('updates', content);
		
	}
	
	
});var FacebookFeedContent = new Class({
	Extends: Content,
	
	EventHandlers: [
	                'facebookItemDelete',
	                'facebookItemLike',
	                'facebookCommentBoxShow'
	                ],
	
	onInit: function(){
	
		//status box
		this.statusBox = new StatusBox(this.getPrivateUser());
		$(this.statusBox).inject(this.content);
	
		//create loader
		this.loader = new ItemLoader({
			idField: 'post_id',
			sortField: 'created_time',
			createElementFunc: FacebookFeedItemUtility.createFacebookFeedItem,
			emptyEl: new Element('div', {'class': 'post empty', 'text': 'You have no updates'})
		});
		$(this.loader).inject(this.content);
		
		if ($defined(this.external_user_id)) {
			this.startPoll();
		}
	},
	
	onShow: function(first){
		if (first)
			this.loadMore();
	},
	
	onHide: function(){
		
	},
	
	startPoll: function() {
		if (!defined(this.pollTimer)) {
			this.pollTimer = this.loadMore.periodical(55*1000, this);
			this.loadMore();
		}
	},
		
	
	loadMore: function(){
		var startTime = this.loader.highestSortValue;
		var params = { start_time: startTime, end_time: 0 };
		
		this.call('facebook', 'facebook_newsfeed_load', params, this.loadMoreSuccess.bind(this), null);
	},
	
	loadMoreSuccess: function(data){
		if (!$defined(data) || data.length == 0)
		{
			this.atBottom = true;
			return;
		}
		
		data.each(function(item){
			this.loader.process(item);
		}, this);
			
		this.bottomFuncCalled = false;
	},
	
	bottomFunc: function(){
		Logger().log('facebook content bottom');
		this.loadMore();
	},
	
	facebookItemDelete: function (post_id, created_time) {
		var params = { 'post_id': post_id, 'created_time': created_time };
		this.call('facebook', 'facebook_feed_delete', params, this.facebookItemDeleteSuccess.bind(this), null);
	},
	
	facebookItemDeleteSuccess: function (data) {
		if (!$defined(data.post_id))
			return;
		
		this.loader.remove(data.post_id);
		
	},
	
	facebookItemLike: function (data) {
		if (!$defined(data.post_id))
			return;
		
		$$('.likes_' + post_id).each(function(el){
			
			el.set('likes', data.like_count);
			
			if(is_liked != -1)
				el.set('is_liked', is_liked);
		
			el.empty();
			new Element('div', {'class': 'text grey text11', 'text': likes, 'likes': likes}).inject(el);
			
			if (likes > 0)
				el.addClass('other_liked');
			else
				el.removeClass('other_liked');
			
			if(is_liked != -1)
			{
				if (is_liked == 1)
					el.addClass('on');
				else
					el.removeClass('on');
			}
		});
	},
	
	facebookCommentsShow: function(post_id) {
		var params = { 'post_id': post_id };
		this.call('facebook', 'facebook_comments', params, this.facebookCommentsSuccess.bind(this), null);
	},
	
	facebookCommentsShowSuccess: function(data) {
		
	}
});var FacebookSelfMenu = new Class({
	Extends: Menu,
	
	onBeforeInit: function(options){
		this.user = options.user;
		options.displayName = this.user.fullname;
		this.fb_uid = options.uid;
		return options;
	},
	
	onInit: function(){
		this.aboutSection = new Element('div', {'class': 'text_section light1'}).inject(this.menu);
		this.call('facebook', 'facebook_user_show', {user_id: this.fb_uid}, this.profileShowSuccess.bind(this), null);
	},
	
	profileShowSuccess: function(data) {
		if (!$defined(data) || data == '')
			return;
		this.user = data;
		
		if (data.pic_square == '')
			data.pic_square; //TODO: assign this to something cool
		this.pic = new Element('div', {'class': 'profile_pic_wrapper_50'}).adopt(
			new Element('img', {
				'class': 'profile_pic',
				'src': data.pic_square,
				'title': data.name,
				'alt': data.name
			})
		);
		
		this.nameSection = new Element('div', {'class': 'text_section bold', 'text': data.name});
		
		var location = '';
		if ($defined(data.current_location.city)) {
			location = data.current_location.city;
			if ($defined(data.current_location.state))
				location = location + ', ' + data.current_location.state;
		}
		else if ($defined(data.current_location.state))
			location = data.current_location.state;
		else if ($defined(data.current_location.country)) {
			location += ' ' + data.current_location.country;
		}
		if (location != '') {
			this.locSection = new Element('div', {
				'class': 'text_section light1 text11'}).adopt(
					new Element('span', {
						'class': 'bold',
						'text': 'Location: '
					}),
					new Element('span', {
						'text': location 
					})
			).inject(this.menu);
		}
		
		if ($defined(data.affiliations) && data.affiliations.length > 0) {
			var networks = '';
			for (var i = 0; i < data.affiliations.length - 1; i++) {
				networks += data.affiliations[i].name + ', ';
			}
			networks += data.affiliations[i].name;
			
			this.networksSection = new Element('div', {
				'class': 'text_section light1 text11'}).adopt(
					new Element('span', {
						'class': 'bold',
						'text': 'Networks: '
					}),
					new Element('span', {
						'text': networks 
					})
			).inject(this.menu);
		}
		
		if ($defined(data.sex) && data.sex != '') {
			this.genderSection = new Element('div', {
				'class': 'text_section light1 text11'}).adopt(
					new Element('span', {
						'class': 'bold',
						'text': 'Gender: '
					}),
					new Element('span', {
						'text': data.sex 
					})
			).inject(this.menu);
		}
		
		if ($defined(data.birthday) && data.birthday != '') {
			this.genderSection = new Element('div', {
				'class': 'text_section light1 text11'}).adopt(
					new Element('span', {
						'class': 'bold',
						'text': 'Birthday: '
					}),
					new Element('span', {
						'text': data.birthday 
					})
			).inject(this.menu);
		}
		
		if ($defined(data.relationship_status) && data.relationship_status != '') {
			this.relationSection = new Element('div', {
				'class': 'text_section light1 text11'}).adopt(
					new Element('span', {
						'class': 'bold',
						'text': 'Relationship Status: '
					}),
					new Element('span', {
						'text': data.relationship_status 
					})
			).inject(this.menu);
		}
		
		if ($defined(data.about_me)) {
			this.aboutSection = new Element('div', {
				'class': 'text_section light1 text11'}).adopt(
					new Element('span', {
						'text': data.about_me 
					})
			).inject(this.menu);
		}

		
	}
});var FacebookInstallPopupContent = new Class({
	Extends: PopupContent,
	
	strings: {
		postSyncLabel: 'Sync posts on Facebook',
		errorMessage: 'There was an error connecting to Facebook'
	},
	
	onInit: function(){
		
		this.appId = 5;
		FB_RequireFeatures(["Connect"], function() { 
			FB.Facebook.init("d29a9328a49d798da896066499c0d9d8", "/xd_receiver.htm", {"forceBrowserPopupForLogin":true, 'doNotUseCachedConnectState': true}); 
		});
		
		this.syncPostsToggle = new Toggle(true);
		new Element('div', {'class': 'input_section'}).adopt(
				new Element('div', {'class': 'label light', 'text': this.strings.postSyncLabel}),
				$(this.syncPostsToggle)
			).inject(this.content);
		
		//ACTIONS
		
		this.closeButton = new ButtonMedium({
			displayName: 'Cancel',
			className: 'dark',
			action: 'cross'
		});
		$(this.closeButton).addEvent('click', this.closeContent.bind(this));
		
		this.actionButton = new ButtonMedium({
			displayName: 'Connect to Facebook',
			className: 'dark',
			action: 'check'
		});
		$(this.actionButton).addEvent('click', this.facebookConnect.bind(this));
		
		this.message = new Element('div', {'class': 'message error'});
		
		this.actions = new Element('div', {'class': 'actions'}).adopt(
			this.message,
			$(this.actionButton),
			$(this.closeButton)
		).inject(this.content);
	},
	
	
	facebookConnect: function(){
		
		FB.Connect.requireSession(this.facebookGetPermission.bind(this), this.facebookConnectFail.bind(this));
	},
	
	facebookGetPermission: function(){
		FB.Connect.showPermissionDialog('offline_access,publish_stream,read_stream,status_update', this.facebookEnable.bind(this));
	},
	
	facebookEnable: function() {
		this.fb_session = FB.Facebook.apiClient.get_session();
		var sync = this.syncPostsToggle.toInt();
		var params = { 	sync_posts: this.syncPostsToggle.toInt(), 
						session_key: this.fb_session.session_key, 
						uid: this.fb_session.uid	
		};
		this.call('facebook', 'facebook_enable', params, this.facebookEnableSuccess.bind(this), null);
	},
	
	facebookEnableSuccess: function(){
		var app = {};
		app.app_id = this.appId;
		app.external_user_id = this.fb_session.uid;
		user_data.apps.push(app);
		
		this.fireEvent('startApp', 'facebook');
		
		this.closeContent();
	},
	
	facebookConnectFail: function() {
		var name = 'facebook_connect';
		var title = 'Error Connecting to Facebook';
		var message = this.strings.errorMessage;
		
		this.fireEvent('showAlert', name, title, message);
	}
	
});var FacebookFeedItemUtility = {
		
	createFacebookFeedItem: function(item){
		
		var el = ItemUtility.createPostBubble('post haspic');
		
		ItemUtility.createItemPic(item.profile_pic).inject(el);
		
		var content = new Element('div', {'class': 'post_content'}).inject(el);
	
		//header
		FacebookFeedItemUtility.createFacebookItemHeader(item).inject(content);
		
		FacebookFeedItemUtility.createFacebookItemBody(item).inject(content);
		FacebookFeedItemUtility.createFacebookItemFooter(item).inject(content);
		FacebookFeedItemUtility.createFacebookItemActions(item).inject(content);
		
		return el;
	},
	
	createFacebookItemHeader: function(item){
		var el = new Element('div', {'class': 'header'});
		
		//feed name
		var creatorName = new Element('span', {'class': 'user_name clickable', 'text': TextUtility.unescapeQuotes(item.name)}).inject(el);
		creatorName.addEvent('click', pipio.dispatchEvent.bind(pipio, ['facebookShowUserFeed', item.actor_id]));
		
		if (item.target_id > 0) {
			var targetName = new Element('span', {'class': 'user_name clickable', 'text': TextUtility.unescapeQuotes(item.target_name)}).inject(el);
			creatorName.addEvent('click', pipio.dispatchEvent.bind(pipio, ['facebookShowUserFeed', item.target_id]));
		}
		
		if ($defined(item.message) && item.message != '')
		{
			new Element('span', {'text': ' ' + TextUtility.replaceUrls(TextUtility.stripHtml(TextUtility.unescape(item.message)))}).inject(el);
		}
		
		return el;
	},
		
	createFacebookItemBody: function(item){
		var el = new Element('div', {'class': 'body'});
		var fbUrl;
		if (item.attachment != '' && $defined(item.attachment.media) && $defined(item.attachment.media.length)) {
			for (var i = 0, n = item.attachment.media.length; i < n; i++) {
				if (item.attachment.media[i].type == 'photo') {
					fbUrl = DataUtility.getFacebookPhotoUrl(item.attachment.media[i].src);
				}
				else {
					fbUrl = item.attachment.media[i].src;
				}
				var image = new Element('img', { 'src': item.attachment.media[i].src }).inject(el);
				image.addEvent('click', pipio.dispatchEvent.bind(pipio, ['showPhotoPopup', fbUrl]));
			}
		}
		if ($defined(item.attachment.name)) {
			new Element('a', { 'href': item.attachment.href, 'target': '_blank', 'text': item.attachment.name }).inject(el);
		}
		new Element('span', { 'text': item.attachment.description }).inject(el);
		
		return el;
		
	},
	
	createFacebookItemFooter: function(item) {
		var el = new Element('div', {'class': 'footer' });
		if (!$defined(item.date_created))
			return el;
		
		var date = DateUtility.convertFromTimestamp(item.created_time);
		
		var ts = new Element('span', {
			'class': 'timestamp',
			'text': DateUtility.getTimestamp(date, '%B %D at %I:%M%p'),
			'ts': date,
			'ts_format': '%B %D at %I:%M%p'
		}).inject(el);
		
		FacebookFeedItemUtility.createFacebookFooterActions(item, el);
		
		return el;
		
	},
	
	createFacebookFooterActions: function(item, el) {
		var replyAction = FacebookFeedItemUtility.createFacebookFooterAction('comment', 'Comment', true);
		replyAction.addEvent('click', pipio.dispatchEvent.bind(pipio, ['facebookCommentBoxShow', item.post_id]));
		replyAction.inject(el);
	},
	
	createFacebookItemActions: function(item) {
		var el = new Element('div', {'class': 'post_actions'});
		
		if (item.can_delete) {
			var deleteAction = new Element('div', {'class': 'post_action delete'}).inject(el);
			deleteAction.addEvent('click', pipio.dispatchEvent.bind(pipio, ['facebookItemDelete', item.post_id, item.created_time]));
		}
		
		var likeAction = new Element('div', {
			'class': 'post_action like likes_'+item.post_id,
			'is_liked': item.user_likes,
			'likes': item.like_count
			}).inject(el);
		likeAction.addEvent('click', pipio.dispatchEvent.bind(pipio, ['facebookItemLike', item.post_id, item.like_count]));
		
		//add likes count 
		new Element('div', {'class': 'text grey text11', 'text': item.like_count, 'likes': item.like_count}).inject(likeAction);
		if (item.like_count > 0)
			likeAction.addClass('other_liked');
		if (item.user_likes == 1)
			likeAction.addClass('on');

		return el;
	},
	
	createFacebookFooterAction: function(actionName, actionText, hide){
		var el = new Element('span', {'class': 'footer_action has_action'}).adopt(
			new Element('div', {'class': 'action ' + actionName})	
		);
		
		el.appendText(actionText);
		
		if (hide)
			el.addClass('hide');
		
		return el;
	}
};var Home = new Class({
	Extends: App,

	EventHandlers: [
	                'userSwitched',
	                'streamItemReplyCountUpdated',
	                'streamItemDelete',
	                'userStreamItemLiked',
	                'streamItemLike',
	                
	                'roomProfilePicUpdated',
	                
	                'feedRoomAdded',
	                'feedRoomDeleted',	                
	                'feedRoomDelete',
	                'showCreateRoomPopup',
	                'showRoomInvitePopup',
	                'showRoomStatusUpdatePopup',
	                'showConvo'
	],
	
	onStart: function(){
		//setup and create navs
		this.setupNav();
		this.initRooms();
		
		this.convos = $H();
	},
	
	onStop: function(){
	},
	
	userSwitched: function(){
		if (this.isLoggedIn())
			this.start();
		else
			this.stop();
	},
	
	onCreate: function(){
		//home app cannot be closed!
		this.closeButton.destroy();
		//attach action to home button
		$('app_button_2').addEvent('click', this.switchDefault.bind(this));
	},
	
	dock: function(){
		
		DomUtility.hide(this.navSection);
		
		$(this.alert).inject('app_button_2');
		$('app_button_2').addClass('has_submenu');
		this.navWrapper.inject('app_button_2', 'after');
		$('app_button_2').removeEvents('click');
		$('app_button_2').addEvent('click', this.undock.bind(this));
		
		this.isDocked = true;
		
	},
	
	undock: function(){
		
		this.navWrapper.inject(this.navSection);
		$('app_button_2').removeClass('has_submenu');
		$('app_button_2').removeEvents('click');
		$('app_button_2').addEvent('click', this.switchDefault.bind(this));
		
		DomUtility.show(this.navSection);
		
		this.isDocked = false;
		
		//switch to default content
		if ($defined(this.defaultContent))
			this.fireEvent('viewSwitch', this.appId, this.defaultContent, this.defaultMenu);
	},
	
	streamItemReplyCountUpdated: function(data){
		if (!$defined(data.item_id) || !$defined(data.count))
			return;
		
		StreamItemUtility.updateReplyCount(data.item_id, data.count);
	},
	
	userStreamItemLiked: function(data){
		if (!$defined(data.item) || !$defined(data.username))
			return;
		
		if (data.username == this.getPrivateUser().username)
			StreamItemUtility.updateItemLiked(data.item.item_id, data.likes, data.is_liked);
		else
			StreamItemUtility.updateItemLiked(data.item.item_id, data.likes, -1);
	},
	
	streamItemLike: function(item_id, el){ // likes, is_liked){
				
		this.call('home', 'stream_item_like', {item_id: item_id}, null, null);
		
		var is_liked = parseInt(el.get('is_liked'));
		var likes = parseInt(el.get('likes'));
		
		//Logger().log('is_liked ' + is_liked + ' likes ' + likes);
		
		if (is_liked == 1)
			StreamItemUtility.updateItemLiked(item_id, likes-1, 0);
		else
			StreamItemUtility.updateItemLiked(item_id, likes+1, 1);
	},
	
	streamItemDelete: function(item_id){
		var name = 'stream_item_delete';
		var title = 'Delete Post';
		var message = 'Are you sure you want to delete this post?';
		var func = this.streamItemDeleteCall.bind(this, [item_id]);
		this.fireEvent('showConfirmation', name, title, message, func);
		
	},
	
	
	
	streamItemDeleteCall: function(item_id){
		
		var params = {item_id: item_id};
		
		this.call('home', 'delete', params, null, null);
		
	},
	
	showRoom: function(room){
		
		if (this.rooms.has(room.username))
		{
			this.fireEvent('viewSwitch', this.appId, 'room_' + room.username, 'room_' + room.username);
		}
		else
		{
			var options = {room: room};
			this.fireEvent('startAppInstance', 'room', room.username, options);
		}	
	},
	
	roomProfilePicUpdated: function(data){
		if (!$defined(data.username))
			return;
		
		var room = this.getRoom(data.username);
		var room_id = room.room_id;	
		var username = room.username;
		
		var version = UserUtility.profilePicVersionGet(room_id);
		version++;
		UserUtility.profilePicVersionSet(room_id, version);
		
		room.profile_pic_16 = room.profile_pic_16.split('?')[0] + '?' + version;
		room.profile_pic_20 = room.profile_pic_20.split('?')[0] + '?' + version;
		room.profile_pic_26 = room.profile_pic_26.split('?')[0] + '?' + version;
		room.profile_pic_32 = room.profile_pic_32.split('?')[0] + '?' + version;
		room.profile_pic_42 = room.profile_pic_42.split('?')[0] + '?' + version;
		room.profile_pic_60 = room.profile_pic_60.split('?')[0] + '?' + version;
		room.profile_pic_100 = room.profile_pic_100.split('?')[0] + '?' + version;
		room.profile_pic_200 = room.profile_pic_200.split('?')[0] + '?' + version;	
	
		
		$$('.profile_pic_16_' + username).each(function(el){
			el.set('src', room.profile_pic_16);
		});
		
		$$('.profile_pic_20_' + username).each(function(el){
			el.set('src', room.profile_pic_20);
		});
		
		$$('.profile_pic_60_' + username).each(function(el){
			el.set('src', room.profile_pic_60);
		});
		
		$$('.profile_pic_26_' + username).each(function(el){
			el.set('src', room.profile_pic_26);
		});
		
		$$('.profile_pic_32_' + username).each(function(el){
			el.set('src', room.profile_pic_32);
		});
		
		$$('.profile_pic_42_' + username).each(function(el){
			el.set('src', room.profile_pic_42);
		});
		
		$$('.profile_pic_100_' + username).each(function(el){
			el.set('src', room.profile_pic_100);
		});
		
		$$('.profile_pic_200_' + username).each(function(el){
			el.set('src', room.profile_pic_200);
		});
		
	}
	
});Home.implement({
	requests: [
		{
			name: 'stream_in_load',
			params: ['date_created', 'item_id'],
			url: '/api/pipio/stream/in/load'
		},
		
		{
			name: 'stream_user_load',
			params: ['username', 'date_created', 'item_id'],
			url: '/api/pipio/stream/user/load'
		},
		

		{
			name: 'public_stream_user_load',
			params: ['username', 'date_created', 'item_id'],
			url: '/api/public/stream/user/load'
		},
		
		{
			name: 'stream_room_load',
			params: ['username', 'date_created', 'item_id'],
			url: '/api/pipio/stream/room/load'
		},
		{
			name: 'public_stream_room_load',
			params: ['username', 'date_created', 'item_id'],
			url: '/api/public/stream/room/load'
		},
		
		{
			name: 'stream_convo_load',
			params: ['convo_id', 'date_created', 'item_id'],
			url: '/api/pipio/stream/convo/load'
		},
		
		{
			name: 'stream_likes_load',
			params: ['username', 'date_created', 'item_id'],
			url: '/api/pipio/stream/likes/load'
		},
		
		{
			name: 'stream_item_like',
			params: ['item_id'],
			url: '/api/pipio/stream/like'
			
		},
		{
			name: 'publish',
			url: '/api/pipio/stream/publish',
			params: ['body', 'targets', 'source_id', 'source_type', 'reply_id', 'channel_id', 'is_public', 'res', 'attachment']
		},
		
		{
			name: 'forward',
			url: '/api/pipio/stream/forward',
			params: ['body', 'targets', 'source_id', 'source_type', 'forward_id', 'channel_id', 'is_public', 'res']
		},
		
		{
			name: 'delete',
			url: '/api/pipio/stream/delete',
			params: ['item_id']
		},
		
		{
			name: 'parse_hulu_url',
			url: '/api/pipio/link/parse/hulu',
			params: ['url']
		},
		
		{
			name: 'parse_break_url',
			url: '/api/pipio/link/parse/break',
			params: ['url']
		},
		
		//rooms calls
		{
			name: 'room_create',
			url: '/api/pipio/room/create',
			params: ['username', 'room_name', 'room_description', 'is_public']
		},
		
		{
			name: 'room_delete',
			url: '/api/pipio/room/delete',
			params: ['username']
		},
		
		{
			name: 'room_leave',
			url: '/api/pipio/room/leave',
			params: ['username']
		},
		
		{
			name: 'room_subscribe',
			url: '/api/pipio/room/subscribe',
			params: ['username']
		},
		
		{
			name: 'room_unsubscribe',
			url: '/api/pipio/room/unsubscribe',
			params: ['username']
		},
		
		{
			name: 'room_request_create',
			url: '/api/pipio/room/request/create',
			params: ['username']
		},
		
		{
			name: 'room_request_delete',
			url: '/api/pipio/room/request/delete',
			params: ['username', 'room_username']
		},
		
		{
			name: 'room_request_accept',
			url: '/api/pipio/room/request/accept',
			params: ['username', 'room_username']
		},
		
		{
			name: 'room_invite_create',
			url: '/api/pipio/room/invite/create',
			params: ['username', 'room_username']
		},
		
		{
			name: 'room_invite_accept',
			url: '/api/pipio/room/invite/accept',
			params: ['username']
		},
		
		{
			name: 'room_invite_delete',
			url: '/api/pipio/room/invite/delete',
			params: ['username', 'room_username']
		},
		
		{
			name: 'room_info_load',
			url: '/api/pipio/room/info/load',
			params: ['username']
		},
		
		{
			name: 'public_room_info_load',
			url: '/api/public/room/info/load',
			params: ['username']
		},
		

		{
			name: 'public_user_info_load',
			url: '/api/public/user/info/load',
			params: ['username']
		},
		
		{
			name: 'room_privacy_set',
			url: '/api/pipio/room/privacy/set',
			params: ['username', 'is_public']
		}

	]
});Home.implement({
	
	initRooms: function(){
		
		this.rooms = $H();
		this.roomInvites = $H();
	
		user_data.rooms.each(function(room){
			room = UserUtility.processRoom(room);
			this.roomAdd(room);
		}, this);
		
	},
	
	feedRoomAdded: function(room){
		if (!this.rooms.has(room.username))
			this.roomAdd(room);
	},
	
	feedRoomDelete: function(username){
		this.call('home', 'room_delete', {username: username}, null, null);
		
	},
	
	feedRoomDeleted: function(username){
		if (!this.rooms.has(username))
			return;
		
		this.roomDelete(username);
	},
	
	showCreateRoomPopup: function(){
		if ($defined(this.createRoomPopup))
			this.createRoomPopup.close();

		this.createCreateRoomPopup();
	},
	
	createCreateRoomPopup: function(){
		this.createRoomPopup = new Popup({
			size: {x: 350, y: 176},
			resizable: false,
			dockable: false,
			className: 'createRoom',
			onClose: this.destroyCreateRoomPopup.bind(this)
		});
		
		//add popup content
		var nav = new Nav({
			iconOptions: {iconName: 'rooms', iconAction: 'add'},
			displayName: 'Create New Room',
			closable: true
		});
		
		var content = new CreateRoomPopupContent({});
		
		this.createRoomPopup.addContent('create_room', nav, content);
	},
	
	destroyCreateRoomPopup: function(){
		this.createRoomPopup = null;
	}
	
});Home.implement({
	setupNav: function(){
	
		Logger().log('home setup');
		var homeNav = new Nav({
				iconOptions: {
				iconName: 'updates'
			},
			displayName: 'Updates',
			name: 'updates',
			onClick: this.fireEvent.bind(this, ['viewSwitch', this.appId, 'updates', 'self_menu'])
		});
		this.navAdd(homeNav);
		
		this.navAdd(new Nav({
			iconOptions: {
				iconName: 'star'
			},
			displayName: 'Favorites',
			name: 'likes',
			onClick: this.fireEvent.bind(this, ['viewSwitch', this.appId, 'likes', 'likes_menu'])
		}));
		
		this.navAdd(new Nav({
			iconOptions: {
				iconName: 'rooms'
			},
			hasSubnavs: true,
			displayName: 'Rooms',
			name: 'rooms'
		}));
		
		
		this.navAdd(new Nav({
			iconOptions: {
				iconName: 'rooms',
				iconAction: 'add'
			},
			displayName: 'Create New Room...',
			onClick: this.fireEvent.bind(this, ['showCreateRoomPopup']),
			name: 'new_room',
			parentName: 'rooms',
			bottom: true
		}));
		
		
		//add Menu
		var selfMenu = new SelfMenu({user: this.getPrivateUser(), isDefault: true});
		this.menuAdd('self_menu', selfMenu);
		
		var content = new HomeContent({isDefault: true, navId: homeNav.navId});
		this.contentAdd('updates', content);
		
		//likes content and menu
		var likesMenu = new LikesMenu({user: this.getPrivateUser()});
		this.menuAdd('likes_menu', likesMenu);
		
		var likesContent = new LikesContent({user: this.getPrivateUser()});
		this.contentAdd('likes', likesContent);
		
	},
	
	roomAdd: function(room){
		
		var nav = new Nav({
			iconOptions: {
				user: room
			},
			displayName: room.room_name,
			name: 'room_' + room.username,
			parentName: 'rooms',
			onClick: this.fireEvent.bind(this, ['showRoom', room])
		});
		
		//test
		this.navAdd(nav);
		
		this.rooms.set(room.username, nav);
		
	},
	
	roomDelete: function(username){
		if (!this.rooms.has(username))
			return;
		
		this.rooms.get(username).destroy();
		this.navDelete('room_' + username);
		this.rooms.erase(username);
		
		this.menuClose('room_' + username);
		this.contentClose('room_' + username);
		
	},
	
	showRoomInvitePopup: function(room){
		if ($defined(this.roomInvitePopup))
			this.roomInvitePopup.close();

		this.createRoomInvitePopup(room);
	},
	
	createRoomInvitePopup: function(room){
		this.roomInvitePopup = new Popup({
			size: {x: 250, y: 350},
			resizable: false,
			dockable: false,
			className: 'roomInvite',
			onClose: this.destroyCreateRoomPopup.bind(this)
		});
		
		//add popup content
		var nav = new Nav({
			iconOptions: {iconName: 'rooms', iconAction: 'add'},
			displayName: 'Invite Users to ' + room.room_name,
			closable: true
		});
		
		var content = new RoomInvitePopupContent({
			contacts: this.getContacts(),
			room: room
		});
		
		this.roomInvitePopup.addContent('create_room', nav, content);
	},
	
	destroyRoomInvitePopup: function(){
		this.roomInvitePopup = null;
	},
	
	//UPDATE ROOM STATUS POPUP
	showRoomStatusUpdatePopup: function(room){
		if ($defined(this.roomStatusUpdatePopup))
			this.roomStatusUpdatePopup.close();
		
		this.createRoomStatusUpdatePopup(room);
	},
	
	createRoomStatusUpdatePopup: function(room){
		this.roomStatusUpdatePopup = new Popup({
			size: {x: 350, y: 100},
			resizable: false,
			dockable: false,
			className: 'statusUpdate',
			onClose: this.destroyRoomStatusUpdatePopup.bind(this)
		});
		
		//add popup content
		var nav = new Nav({
			iconOptions: {user: room},
			displayName: 'Update Status for ' + room.room_name,
			closable: true
		});
		
		var content = new RoomStatusUpdatePopupContent({room: room});
		
		this.roomStatusUpdatePopup.addContent('status_update', nav, content);
	},
	
	destroyRoomStatusUpdatePopup: function(){
		this.roomStatusUpdatePopup = null;
	},
	
	showConvo: function(convo_id){
		if (this.convos.has(convo_id))
		{
			this.convos.get(convo_id)._select();
			return;
		}
		
		this.createConvoPopup(convo_id);
	},
	
	createConvoPopup: function(convo_id){
		var popup = new Popup({
			size: {x: 500, y: 400},
			resizable: false,
			dockable: true,
			className: 'convoViewer',
			onClose: this.destroyConvoPopup.bind(this, [convo_id])
		});
		
		var nav = new Nav({
			iconOptions: {iconName: 'convo'},
			displayName: 'View Conversation',
			closable: true
		});
		
		var content = new ConvoPopupContent({
			convo_id: convo_id
		});
		
		popup.addContent('convo', nav, content);

		this.convos.set(convo_id, popup);
	},
	
	destroyConvoPopup: function(convo_id){
		this.convos.erase(convo_id);
	}
	
	
	
	
	
});var HomeContent = new Class({
	Extends: Content,
	
	EventHandlers: [
	                'streamItemReceived',
	                'streamItemDeleted'
	                ],
	
	                
	onBeforeInit: function(options){
		
		this.navId = options.navId;
		return options;
	
	},

	onInit: function(){
		//status box
		this.statusBox = new StatusBox(this.getPrivateUser());
		$(this.statusBox).inject(this.content);
	
		//create loader
		this.loader = new StreamLoader({
			createElementFunc: StreamItemUtility.createInStreamItem,
			emptyEl: new Element('div', {'class': 'post empty', 'text': 'You have no updates'}),
			alertFunc: this.alertCheck.bind(this)
		});
		$(this.loader).inject(this.content);
		
	
	},
	
	onShow: function(first){
		if (first)
			this.loadMore();
	},
	
	onHide: function(){
		
	},
	
	streamItemReceived: function(data){
		
		if ($defined(data.item))
			this.loader.process(data.item);
		
		if (data.item.source_id != this.getPrivateUser().user_id && data.item.source_type == 3)
		{	//a userStreamItem as well, dispatch new event for that
			data.username = data.item.source.username;
			this.fireEvent('roomStreamItemReceived', data);
		}
	},
	
	alertCheck: function(item){
		if (item.creator_id != this.getPrivateUser().user_id)
			this.fireEvent('alertAdd', this.navId);
		
	},
	
	streamItemDeleted: function(data){
		if (!$defined(data.item_id))
			return;
		
		this.loader.remove(data.item_id);
		
		if ($defined(data.source_id) && data.source_id != this.getPrivateUser().user_id)
		{
			if (data.source_type == 1)
				this.fireEvent('userStreamItemDeleted', data);
			else if (data.source_type == 3)
				this.fireEvent('roomStreamItemDeleted', data);
			
			
		}
	},
	
	loadMore: function(){
		var params = {
				date_created: this.loader.oldestTimestamp,
				item_id: this.loader.oldestId
		};
		
		this.call('home', 'stream_in_load', params, this.loadMoreSuccess.bind(this), null);
	},
	
	loadMoreSuccess: function(data){
		if (!$defined(data.items) || data.items.length == 0)
		{
			this.atBottom = true;
			return;
		}
		
		data.items.each(function(item){
			this.loader.process(item);
		}, this);
			
		this.bottomFuncCalled = false;
	},
	
	bottomFunc: function(){
		Logger().log('home content bottom');
		this.loadMore();
	}
});var LikesContent = new Class({
	Extends: Content,
	
	EventHandlers: [
	                'userStreamItemLiked',
	                'streamItemLike'
	               // 'streamItemDeleted'
	                ],
	
	onBeforeInit: function(options){
		this.user = options.user;
	},
	           
	onInit: function(){
		//status box
		//create loader
		var createFunc = StreamItemUtility.createLikesStreamItem;
		if (this.user.user_id == this.getPrivateUser().user_id)
			createFunc = StreamItemUtility.createSelfLikesStreamItem;
			
		this.loader = new ItemLoader({
			createElementFunc: createFunc,
			idField: 'item_id',
			sortField: 'date_created',
			sortAscending: false,
			emptyEl: new Element('div', {'class': 'post empty', 'text': TextUtility.unescape(this.user.fullname) + ' has no favorites'}),
			errorEl: new Element('div', {'class': 'post empty', 'text': TextUtility.unescape(this.user.fullname) + '\'s stream is private'})
		});
		
		/*
		 * this.loader = new StreamLoader({
			createElementFunc: StreamItemUtility.createUserStreamItem,
			emptyEl: new Element('div', {'class': 'post empty', 'text': 'This user has no favorites'})
		});
		
		 */
		$(this.loader).inject(this.content);
		
	
	},
	
	onShow: function(first){
		if (first)
			this.loadMore();
	},
	
	onHide: function(){
		
	},
	
	userStreamItemLiked: function(data){
		if ($defined(data.item) && data.username == this.user.username)
		{
			if (data.is_liked == 1)
				this.loader.process(data.item);
			else
				this.loader.remove(data.item.item_id);
		}
	},
	
	streamItemLike: function(item_id, likes, is_liked){
		
		if (is_liked == 1)
			this.loader.remove(item_id);
	},
	
	loadMore: function(){
		var params = {
				username: this.user.username,
				date_created: this.loader.oldestTimestamp,
				item_id: this.loader.oldestId
		};
		
		this.call('home', 'stream_likes_load', params, this.loadMoreSuccess.bind(this), this.loadMoreFail.bind(this));
	},
	
	loadMoreSuccess: function(data){
		if (!$defined(data.items) || data.items.length == 0)
		{
			this.atBottom = true;
			return;
		}
		
		data.items.each(function(item){
			this.loader.process(item);
		}, this);
			
		this.bottomFuncCalled = false;
	},
	
	loadMoreFail: function(status){
		if (status.code == 2002)
			this.loader.showError();
	},
	
	bottomFunc: function(){
		this.loadMore();
	}
});var ConvoPopupContent = new Class({
	Extends: PopupContent,
	
	EventHandlers: [],
	
	onBeforeInit: function(options){
		
		this.convo_id = options.convo_id;
	
		return options;
	},
	
	onInit: function(){
		
		this.convoWrapper = new Element('div', {'class': 'convoWrapper'}).inject(this.content);
		//create loader
		this.loader = new ItemLoader({
			idField: 'item_id',
			sortField: 'date_created',
			createElementFunc: StreamItemUtility.createConvoStreamItem,
			emptyEl: new Element('div', {'class': 'post empty', 'text': 'This conversation is empty'})
		});
		
		this.convoWrapper.addEvent('mousewheel', this.checkScroll.bind(this));
		
		$(this.loader).inject(this.convoWrapper);
		
		//setup scroller
		this.scroll = new Fx.Scroll(this.convoWrapper);
		
		this.loadMore();
	},
	
	onShow: function(){
		if ($defined(this.scrollY))
			this.scroll.set(0, this.scrollY);
		
	},
	
	onHide: function(){
		if ($defined(this.convoWrapper))
			this.scrollY = this.convoWrapper.getScroll().y;
	},
	
	loadMore: function(){
		var params = {
			convo_id: this.convo_id,
			date_created: this.loader.highestSortValue,
			item_id: this.loader.highestId
		};
		this.call('home', 'stream_convo_load', params, this.loadMoreSuccess.bind(this), null);
	},
	
	loadMoreSuccess: function(data){
		if (!$defined(data.items) || data.items.length == 0)
		{
			this.atBottom = true;
			return;
		}
		
		data.items.each(function(item){
			this.loader.process(item);
		}, this);
			
		this.bottomFuncCalled = false;
	},
	
	bottomFunc: function(){
		Logger().log('home content bottom');
		this.loadMore();
	},
	
	checkScroll: function(){
		
		var maxY = this.convoWrapper.getScrollSize().y;
		var bottomY = this.convoWrapper.getScroll().y + this.convoWrapper.getSize().y + 100;
		
		if (bottomY > maxY && !this.bottomFuncCalled && !this.atBottom)
		{
			this.bottomFuncCalled = true;
			this.bottomFunc();
		}
	}
	
});var CreateRoomPopupContent = new Class({
	Extends: PopupContent,
	
	strings: {
		roomUsernameLabel: 'Nickname:',
		roomNameLabel: 'Room Name:',
		roomDescriptionLabel: 'Description:',
		publicLabel: 'Public:',
		errorMessage: 'There was an error creating this room'
	},
	
	onInit: function(){
		
		
		this.roomUsernameInput = new Element('input', {'type': 'text', 'maxlength': '32'});
		
		new Element('div', {'class': 'input_section'}).adopt(
				new Element('div', {'class': 'label light', 'text': this.strings.roomUsernameLabel}),
				new Element('div', {'class': 'textarea_wrapper'}).adopt(
					this.roomUsernameInput
				)
			).inject(this.content);
		
		
		this.roomNameInput = new Element('input', {'type': 'text', 'maxlength': '32'});
		
		new Element('div', {'class': 'input_section'}).adopt(
				new Element('div', {'class': 'label light', 'text': this.strings.roomNameLabel}),
				new Element('div', {'class': 'textarea_wrapper'}).adopt(
					this.roomNameInput
				)
			).inject(this.content);
		
		this.roomDescriptionInput = new Element('input', {'type': 'text', 'maxlength': '32'});
		
		new Element('div', {'class': 'input_section'}).adopt(
				new Element('div', {'class': 'label light', 'text': this.strings.roomDescriptionLabel}),
				new Element('div', {'class': 'textarea_wrapper'}).adopt(
					this.roomDescriptionInput
				)
			).inject(this.content);
		
		this.publicToggle = new Toggle(true);
		new Element('div', {'class': 'input_section'}).adopt(
				new Element('div', {'class': 'label light', 'text': this.strings.publicLabel}),
				$(this.publicToggle)
			).inject(this.content);
		
		//ACTIONS
		
		this.closeButton = new ButtonMedium({
			displayName: 'Cancel',
			className: 'dark',
			action: 'cross'
		});
		$(this.closeButton).addEvent('click', this.closeContent.bind(this));
		
		this.actionButton = new ButtonMedium({
			displayName: 'Create Room',
			className: 'dark',
			action: 'check'
		});
		$(this.actionButton).addEvent('click', this.createRoom.bind(this));
		
		this.message = new Element('div', {'class': 'message error'});
		
		this.actions = new Element('div', {'class': 'actions'}).adopt(
			this.message,
			$(this.actionButton),
			$(this.closeButton)
		).inject(this.content);
	},
	
	
	createRoom: function(){
		
		var username = this.roomUsernameInput.value.trim();
		var room_name = this.roomNameInput.value.trim();
		var room_description = this.roomDescriptionInput.value.trim();
		var is_public = this.publicToggle.toInt();
		var params = {
				username: username,
				room_name: room_name,
				room_description: room_description,
				is_public: is_public};
		
		this.call('home', 'room_create', params, this.createRoomSuccess.bind(this), this.createRoomFail.bind(this));
		
		this.actionButton.showProgress();
	},
	
	createRoomSuccess: function(){
		this.actionButton.hideProgress();
		this.closeContent();
	},
	
	createRoomFail: function(status){
		this.actionButton.hideProgress();
		var name = 'create_room';
		var title = 'Error Creating Room';
		var message = status.message;

		this.fireEvent('showAlert', name, title, message);
	},
	
	onShow: function(){
		this.focus();
	},
	
	focus: function(){
		this.roomUsernameInput.focus.delay(500, this.roomUsernameInput);
	}
	
});var RoomInvitePopupContent = new Class({
	Extends: PopupContent,
	
	onBeforeInit: function(options){
		
		this.contacts = options.contacts;
		this.room = options.room;
	
		return options;
	},
	
	onInit: function(){
		
		this.css = new CSS();
		//INPUT
		this.input = new Element('input', {'type': 'text', 'maxlength': 50});
		this.input.addEvent('keyup', this.inputKeyUp.bindWithEvent(this));
		
		
		new Element('div', {'class': 'textarea_wrapper'}).adopt(
			this.input
		).inject(this.content);
		
		this.list = new Element('div', {'class': 'itemList'}).inject(this.content);
		
		
		this.closeButton = new ButtonMedium({
			displayName: 'Close',
			className: 'dark',
			action: 'cross'
		});
		
		new Element('div', {'class': 'actions'}).adopt(
			$(this.closeButton)
		).inject(this.content);
		$(this.closeButton).addEvent('click', this.closeContent.bind(this));
		
		this.insertContacts();
		
		//setup scroller
		this.scroll = new Fx.Scroll(this.list);
		
	},
	
	insertContacts: function(){
		
		this.contacts.each(function(user){
			var el = ShareBoxUtility.createContactRoomInviteItem(user);
			el.addEvent('click', this.inviteUser.bind(this, [user, el]));
			el.inject(this.list);
			
		}, this);
		
	},
	
	insertSeperator: function(){
		new Element('div', {'class': 'listItem seperator'}).inject(this.list);
	},
	
	inviteUser: function(user, el){
		//this.contactAddFunc(user);
		var params = {
			username: user.username,
			room_username: this.room.username
		};
		
		this.call('home', 'room_invite_create', params, null, null);
		//el.destroy();
		el.addClass('invited');
	},

	inputKeyUp: function(e){
		
		if (e.key == 'esc')
		{
			e.target.value = '';
		}
		var val = e.target.value.trim().toLowerCase();
		if (val.length < 2)
		{
			this.list.removeClass('search');
			return;
		}
		
		this.list.addClass('search');
		
		if ($defined(this.lastSelector))
			this.css.remove_rule(this.lastSelector);
		
		
		var selector = ".listItem[val1^='" + val + "'], .listItem[val2^='" + val + "']";
		this.css.add_rule(selector, {'display': 'block !important'}).refresh();

		this.lastSelector = selector;
		
		//process select & enters
		//if (e.key == 'down' || e.key == 'up' || e.key == 'enter')
		//{
		//	this.selectItem(e);
		//	return;
		//}
	},
	
	focus: function(){
		this.input.focus.delay(500, this.input);
	},
	
	onClose: function(){
		if ($defined(this.lastSelector))
			this.css.remove_rule(this.lastSelector).refresh();
	},
	
	onShow: function(){
		if ($defined(this.scrollY))
			this.scroll.set(0, this.scrollY);
		
		this.focus();
	},
	
	onHide: function(){
		if ($defined(this.list))
			this.scrollY = this.list.getScroll().y;
	}
	
});var RoomStatusUpdatePopupContent = new Class({
	Extends: PopupContent,
	
	onBeforeInit: function(options){
		
		this.room = options.room;
		return options;
	},

	onInit: function(){
	
		this.statusInput = new Element('textarea', {'maxlength': 500});
		this.statusInput.addEvent('keyup', this.inputKeyUp.bindWithEvent(this));
		
		
		new Element('div', {'class': 'textarea_wrapper'}).adopt(
				this.statusInput
		).inject(this.content);
	
		
		this.cancelButton = new ButtonMedium({
			displayName: 'Cancel',
			className: 'dark',
			action: 'cross'
		});
		$(this.cancelButton).addEvent('click', this.closeContent.bind(this));
		
		this.updateButton = new ButtonMedium({
			displayName: 'Update Status',
			className: 'dark',
			action: 'check'
		});
		$(this.updateButton).addEvent('click', this.updateStatus.bind(this));
		
		
		new Element('div', {'class': 'actions'}).adopt(
			$(this.updateButton),
			$(this.cancelButton)
		).inject(this.content);
	},
	
	onShow: function(){
		this.focus();
	},
	
	focus: function(){
		this.statusInput.focus.delay(500, this.statusInput);
	},
	
	inputKeyUp: function(e){
		if (e.key == 'enter')
		{
			var msg = e.target.value.trim();
			//dont let empty msg get sent
			if (msg == '')
				return;
			
			this.updateStatus();
		}
	},
	
	updateStatus: function(){
		var params = {username: this.room.username, body: this.statusInput.value.trim(), res: this.getSession()};
		
		this.call('pipio', 'publish_roomstatus', params, this.updateStatusSuccess.bind(this) , this.updateStatusFail.bind(this));
		this.updateButton.showProgress();
	},
	
	updateStatusSuccess: function(){
		this.closeContent();
	},
	
	updateStatusFail: function(){
		alert('There was an error updating your status!');
	}
	
});var LikesMenu = new Class({
	Extends: Menu,
	
	EventHandlers: [
	                ],
	
	onBeforeInit: function(options){
		this.user = options.user;
		options.displayName = this.user.first_name + '\'s Favorites';
		return options;
	},
	
	onInit: function(){
		
		this.aboutText = new Element('span');
		var likesText = 'These are posts ' + this.user.first_name + ' likes';
		
		this.aboutSection = new Element('div', {'class': 'text_section light1', 'text': TextUtility.unescape(likesText)}).inject(this.menu);
		
	
	}
});var SelfMenu = new Class({
	Extends: Menu,
	
	EventHandlers: [
	                'userAboutUpdated'
	                ],
	
	onBeforeInit: function(options){
		this.user = options.user;
		options.displayName = this.user.fullname;
		return options;
	},
	
	onInit: function(){
		
		this.aboutText = new Element('span');
		this.aboutSection = new Element('div', {'class': 'text_section light1 text11'}).adopt(
			new Element('span', {'class': 'bold', 'text': 'Bio: '}),
			this.aboutText
		).inject(this.menu);
		
		this.urlText = new Element('a', {'target': '_blank'});
		this.urlSection = new Element('div', {'class': 'text_section light1 text11'}).adopt(
			new Element('span', {'class': 'bold', 'text': 'Web: '}),
			this.urlText	
		).inject(this.menu);
		
		this.map = new UserMapMenuSection({user: this.user});
		$(this.map).inject(this.menu);
		
		
		//profile pic upload button
		this.profilePicUploadButton = new ButtonMedium({
			displayName: 'Change Picture',
			className: 'dark',
			action: 'photo'
		});
		
		this.fileInput = new Element('input', {'type': 'file', 'name': 'file'});
		this.form = new Element('form').adopt(this.fileInput);
		
		this.fileInput.addEvent('change', this.profilePicUpload.bind(this));
		
		this.profilePicUploadSection = new Element('div', {'class': 'button_section'}).adopt(
			$(this.profilePicUploadButton).adopt(
				this.form
			)
		).inject(this.menu);
		
		
		this.locationEnabled = new Toggle(this.user.location_enabled == 1);
		
		$(this.locationEnabled).addEvent('click', this.locationToggle.bind(this));
		
		new Element('div', {'class': 'toggle_section'}).adopt(
			new Element('div', {'class': 'text light', 'text': 'Location:'}),
			$(this.locationEnabled)
		).inject(this.menu);
		
		
		this.privacy = new Toggle(this.user.is_public == 1);
		
		$(this.privacy).addEvent('click', this.privacyToggle.bind(this));
		
		new Element('div', {'class': 'toggle_section'}).adopt(
			new Element('div', {'class': 'text light', 'text': 'Public:'}),
			$(this.privacy)
		).inject(this.menu);
		
		new Element('div', {'class': 'text_section light centered text11', 'text': 'This is you'}).inject(this.menu);
		
		//profile pic upload button
		var searchUserButton = new ButtonMedium({
			displayName: 'Find Friends',
			className: 'dark',
			action: 'check'
		});
		
		$(searchUserButton).addEvent('click', this.fireEvent.bind(this, ['searchUserShow']));
		
		new Element('div', {'class': 'button_section'}).adopt(
			$(searchUserButton)
		).inject(this.menu);
		
		//invite user
		var inviteUserButton = new ButtonMedium({
			displayName: 'Invite Friends',
			className: 'dark',
			action: 'pivot_right'
		});
		
		$(inviteUserButton).addEvent('click', this.fireEvent.bind(this, ['inviteUserShow']));
		
		new Element('div', {'class': 'button_section'}).adopt(
			$(inviteUserButton)
		).inject(this.menu);
		
		
		var searchContactButton = new ButtonMedium({
			displayName: 'Search Contacts',
			className: 'dark',
			action: 'status'
		});
		
		$(searchContactButton).addEvent('click', this.fireEvent.bind(this, ['searchEmailShow']));
		
		new Element('div', {'class': 'button_section'}).adopt(
			$(searchContactButton)
		).inject(this.menu);
		
		this.updateUrl(this.user.about.url);
		this.updateAbout(this.user.about.about);
	},
	
	//CHANGE USER PRIVACY
	privacyToggle: function(){
		this.call('pipio', 'user_privacy_set', {is_public: this.privacy.toInt()});
		
	},
	

	//CHANGE USER PRIVACY
	locationToggle: function(){
		this.call('pipio', 'user_location_enabled', {location_enabled: this.locationEnabled.toInt()});
		
	},
	
	resetForm: function(){
		this.profilePicUploadSection.empty();
		
		//profile pic upload button
		this.profilePicUploadButton = new ButtonMedium({
			displayName: 'Change Picture',
			className: 'dark',
			action: 'photo'
		});
		
		this.fileInput = new Element('input', {'type': 'file', 'name': 'file'});
		this.form = new Element('form').adopt(this.fileInput);
		
		this.fileInput.addEvent('change', this.profilePicUpload.bind(this));
		
		this.profilePicUploadSection.adopt(
			$(this.profilePicUploadButton).adopt(
				this.form
			)		
		);
	},
	
	updateAbout: function(about){
		
		if (about == '')
			DomUtility.hide(this.aboutSection);
		else
		{
			this.aboutText.set('text', TextUtility.unescape(about));
			DomUtility.show(this.aboutSection);
		}
		
	},
	
	updateUrl: function(url){
		
		if (url == '')
			DomUtility.hide(this.urlSection);
		else
		{
			this.urlText.set('text', TextUtility.unescape(url));
			this.urlText.set('href', url);
			DomUtility.show(this.urlSection);
		}
		
	},
	
	userAboutUpdated: function(data){
		if (!$defined(data.username) || data.username != this.user.username)
			return;
		
		var about = UserUtility.processAbout(data.about);
		
		this.updateAbout(about.about);
		this.updateUrl(about.url);
		
	},
	
	profilePicUpload: function(){
		
		if (DataUtility.validatePhotoFile(this.fileInput.value))
		{
			this.call('pipio', 'user_profilepic_upload', {}, this.profilePicUploadSuccess.bind(this), this.profilePicUploadFail.bind(this), this.form);
			this.profilePicUploadButton.showProgress();
		}
		
	},
	
	profilePicUploadSuccess: function(){
		this.profilePicUploadButton.hideProgress();
		this.resetForm();
	},
	
	profilePicUploadFail: function(){
		this.profilePicUploadButton.hideProgress();
		this.resetForm();
	}
});var MembersNav = new Class({
	Extends: Nav,
	
	onBeforeInit: function(options){
		
		//custom properties
		this.user = options.user;
		
		this.users = $H();
		
		options.name = 'members_' + this.user.username;
		options.iconOptions = {iconName: 'contacts'};
		options.displayName = 'Members';
		options.className = 'members_nav';
		options.hasSubnavs = true;
		
		return options;
	},
	
	onInit: function(){
		this.clear = new Element('div', {'style': 'clear: both'});
		
		this.list = new Element('div', {'class': 'menu_list'}).adopt(this.clear).inject(this.subnavs);
		
	},
	
	addUsers: function(users){
		
		$splat(users).each(function(user){
			user = UserUtility.processUser(user);
			
			if (!this.users.has(user.username))
			{
				var pic = ItemUtility.createProfilePic(user, '16a');
				pic.addEvent('click', this.fireEvent.bind(this, ['showUser', user]));
				pic.inject(this.clear, 'before');
				
				this.users.set(user.username, pic);
			}
		}, this);
		
	},
	
	removeUser: function(username){
		if (!this.users.has(username))
			return;
		
		this.users.get(username).destroy();
		this.users.erase(username);
	}
	
});var StreamItemUtility = {
	
	createDateSeperatorItem: function(ts){
		var date = DateUtility.convertFromTimestamp(ts);
		var timetext = date.format('%B %D');
		
		var el = new Element('div', {'class': 'post seperator', 'ts': ts}).adopt(
			new Element('div', {
				'class': 'seperator_text',
				'text': timetext
			})
		);
		
		return el;
	},
	
	createInStreamItem: function(item){
		return StreamItemUtility.createStreamItem(item, true, false);
	},
	
	createUserStreamItem: function(item){
		return StreamItemUtility.createStreamItem(item, false, false);
	},
	
	createSelfLikesStreamItem: function(item){
		return StreamItemUtility.createStreamItem(item, true, true);
	},
	
	createLikesStreamItem: function(item){
		return StreamItemUtility.createStreamItem(item, false, true);
	},
	
	createStreamItem: function(item, inbound, likes_stream){
	
		var el = ItemUtility.createPostBubble('post haspic');
	
		//parse users
		item.creator = UserUtility.processSource(item.creator);
		item.source = UserUtility.processSource(item.source);
		if ($defined(item.post.reply_user))
			item.post.reply_user = UserUtility.processSource(item.post.reply_user);
		if ($defined(item.post.convo_user))
			item.post.convo_user = UserUtility.processSource(item.post.convo_user);
		if ($defined(item.post.forward_user))
			item.post.forward_user = UserUtility.processSource(item.post.forward_user);
		
		//set some bits
		//defaults 
		item.can_delete = false;
		
		if (!$defined(item.private_replies))
			item.private_replies = 0;
		
		var user_id = pipio.currentUser.user_id;
		if (inbound)
		{
			item.can_delete = true;
		}
		else
		{
			if (item.creator_id == user_id)
				item.can_delete = true;
		}
		
		//override if in likes stream
		if (likes_stream)
			item.can_delete = false;
		
		if (inbound)
			item.can_like = true;
		else
			item.can_like = false;
		
		//process profile pics
		ItemUtility.createProfilePic(item.creator, 32).inject(el);
		
		//process 2ndary pics
		if (item.reply_id != item.item_id && $defined(item.post.reply_user))
		{
			ItemUtility.createProfilePic(item.post.reply_user, 20).inject(el);
		}
		
		if (item.source_id != item.creator_id)
		{
			ItemUtility.createProfilePic(item.source, 20).inject(el);
		}
		//content element
		var content = new Element('div', {'class': 'post_content'}).inject(el);
		StreamItemUtility.createStreamItemPostActions(item).inject(el);
		
		//header
		StreamItemUtility.createStreamItemHeader(item).inject(content);
		
		var attachments = StreamItemUtility.createStreamItemAttachments(item);
		if (attachments)
			attachments.inject(content);
		
		
		StreamItemUtility.createStreamItemFooter(item).inject(content);
		
		return el;
	},
	
	createConvoStreamItem: function(item){
		
		var el = ItemUtility.createPostBubble('post haspic');
	
		//parse users
		item.creator = UserUtility.processSource(item.creator);
		item.source = UserUtility.processSource(item.source);
		if ($defined(item.post.reply_user))
			item.post.reply_user = UserUtility.processSource(item.post.reply_user);
		if ($defined(item.post.convo_user))
			item.post.convo_user = UserUtility.processSource(item.post.convo_user);
		if ($defined(item.post.forward_user))
			item.post.forward_user = UserUtility.processSource(item.post.forward_user);
		
		//set some bits
		//defaults 
		item.can_delete = false;
		
		//process profile pics
		ItemUtility.createProfilePic(item.creator, 32).inject(el);
		
		//process 2ndary pics
		if (item.reply_id != item.item_id && $defined(item.post.reply_user))
		{
			ItemUtility.createProfilePic(item.post.reply_user, 20).inject(el);
		}
		
		if (item.source_id != item.creator_id)
		{
			ItemUtility.createProfilePic(item.source, 20).inject(el);
		}
		//content element
		var content = new Element('div', {'class': 'post_content'}).inject(el);
		StreamItemUtility.createStreamItemPostActions(item).inject(el);
		
		//header
		StreamItemUtility.createStreamItemHeader(item).inject(content);
		
		var attachments = StreamItemUtility.createStreamItemAttachments(item);
		if (attachments)
			attachments.inject(content);
		
		
		StreamItemUtility.createConvoStreamItemFooter(item).inject(content);
		
		return el;
	},
	
	createStreamMemberRoomItem: function(item){
		return StreamItemUtility.createStreamRoomItem(item, true);
	},
	
	createStreamPublicRoomItem: function(item){
		return StreamItemUtility.createStreamRoomItem(item, false);
	},
	
	createStreamRoomItem: function(item, is_member){
		
		var el = ItemUtility.createPostBubble('post haspic');
	
		//parse users
		item.creator = UserUtility.processSource(item.creator);
		item.source = UserUtility.processSource(item.source);
		if ($defined(item.post.reply_user))
			item.post.reply_user = UserUtility.processSource(item.post.reply_user);
		if ($defined(item.post.convo_user))
			item.post.convo_user = UserUtility.processSource(item.post.convo_user);
		if ($defined(item.post.forward_user))
			item.post.forward_user = UserUtility.processSource(item.post.forward_user);
		
		//set some bits
		//defaults 
		item.can_delete = false;
		
		if (!$defined(item.private_replies))
			item.private_replies = 0;
		
		var user_id = pipio.currentUser.user_id;
		
		if (item.creator_id == user_id)
			item.can_delete = true;
	
		if (is_member && item.source_id == item.creator_id)
			item.can_delete = true;
		
		//process profile pics
		ItemUtility.createProfilePic(item.creator, 32).inject(el);
		
		//process 2ndary pics
		if (item.reply_id != item.item_id && $defined(item.post.reply_user))
		{
			ItemUtility.createProfilePic(item.post.reply_user, 20).inject(el);
		}
		
		//content element
		var content = new Element('div', {'class': 'post_content'}).inject(el);
		StreamItemUtility.createStreamItemPostActions(item).inject(el);
		
		//header
		StreamItemUtility.createStreamRoomItemHeader(item).inject(content);
		
		var attachments = StreamItemUtility.createStreamItemAttachments(item);
		if (attachments)
			attachments.inject(content);
		
		
		StreamItemUtility.createStreamItemFooter(item).inject(content);
		
		return el;
	},
	
	createStreamItemPostActions: function(item){
		var el = new Element('div', {'class': 'post_actions'});
		
		if (item.can_delete)
		{
			var deleteAction = new Element('div', {'class': 'post_action delete'}).inject(el);
			deleteAction.addEvent('click', pipio.dispatchEvent.bind(pipio, ['streamItemDelete', item.item_id]));
		}
		
		if (item.can_like)
		{
			
			var likeAction = new Element('div', {
				'class': 'post_action like likes_'+item.item_id,
				'is_liked': item.is_liked,
				'likes': item.likes
				}).inject(el);
			likeAction.addEvent('click', pipio.dispatchEvent.bind(pipio, ['streamItemLike', item.item_id, likeAction]));
			
			//add likes count 
			new Element('div', {'class': 'text grey text11', 'text': item.likes, 'likes': item.likes}).inject(likeAction);
			if (item.likes > 0)
				likeAction.addClass('other_liked');
			if (item.is_liked == 1)
				likeAction.addClass('on');
		}

		return el;
	},
	
	
	createStreamRoomItemHeader: function(item){
		var el = new Element('div', {'class': 'header'});
		
		var creator_action = 'broadcast dark';
		if (item.is_public == 0)
			creator_action = 'lock dark';
		else if (item.post_type == 4)
			creator_action = 'status dark';
		
		//creator action
		StreamItemUtility.createStreamUserAction(creator_action).inject(el);
		//creator name
		StreamItemUtility.createUsernameElement(item.creator).inject(el);
		
		if (item.post_type == 3)
		{
			//creator action
			StreamItemUtility.createStreamUserAction('forward').inject(el);
			//creator name
			StreamItemUtility.createUsernameElement(item.post.forward_user).inject(el);
		}
		
		if (item.reply_id != item.item_id)
		{
			//creator action
			StreamItemUtility.createStreamUserAction('reply').inject(el);
			//creator name
			StreamItemUtility.createUsernameElement(item.post.reply_user).inject(el);
		}
		
		if ($defined(item.post.body) && item.post.body != '')
		{
			var body = new Element('span', {'html': ' ' + TextUtility.replaceUrls(TextUtility.convertNewLine(TextUtility.unescape(item.post.body)))}).inject(el);
			if ($defined(item.post.forward_body) && item.post.forward_body != '')
				body.appendText(' - ' + TextUtility.replaceUrls(TextUtility.convertNewLine(TextUtility.unescape(item.post.forward_body))));
		}
		
		return el;
	},
	
	createUsernameElement: function(user){
		if ($defined(user.user_id))
		{
			var el = new Element('span', {'class': 'user_name clickable', 'text': TextUtility.unescapeQuotes(user.fullname)});
			el.addEvent('click', pipio.dispatchEvent.bind(pipio, ['showUser', user]));
			return el;
		}
		else if ($defined(user.room_id))
		{
			var el = new Element('span', {'class': 'user_name clickable', 'text': TextUtility.unescapeQuotes(user.room_name)});
			el.addEvent('click', pipio.dispatchEvent.bind(pipio, ['showRoom', user]));
			return el;
		}
	},
	
	createStreamItemHeader: function(item){
		var el = new Element('div', {'class': 'header'});
		
		var creator_action = 'broadcast dark';
		if (item.is_public == 0)
			creator_action = 'lock dark';
		else if (item.post_type == 4)
			creator_action = 'status dark';
		
		//creator action
		StreamItemUtility.createStreamUserAction(creator_action).inject(el);
		//creator name
		StreamItemUtility.createUsernameElement(item.creator).inject(el);
		
		if (item.post_type == 3)
		{
			//creator action
			StreamItemUtility.createStreamUserAction('forward').inject(el);
			//creator name
			StreamItemUtility.createUsernameElement(item.post.forward_user).inject(el);
		}
		
		if (item.reply_id != item.item_id)
		{
			//creator action
			StreamItemUtility.createStreamUserAction('reply').inject(el);
			//creator name
			StreamItemUtility.createUsernameElement(item.post.reply_user).inject(el);
		}
		else if (item.creator_id != item.source_id)
		{
			//creator action
			StreamItemUtility.createStreamUserAction('direct').inject(el);
			//creator name
			StreamItemUtility.createUsernameElement(item.source).inject(el);
		}
		
		if ($defined(item.post.body) && item.post.body != '')
		{
			var body = new Element('span', {'html': ' ' + TextUtility.replaceUrls(TextUtility.convertNewLine(TextUtility.unescape(item.post.body)))}).inject(el);
			if ($defined(item.post.forward_body) && item.post.forward_body != '')
				body.appendText(' - ' + TextUtility.replaceUrls(TextUtility.convertNewLine(TextUtility.unescape(item.post.forward_body))));
		}
		
		return el;
	},
	
	createStreamItemAttachments: function(item){
		if (item.attachment_type == 0)
			return false;
		
		var el = new Element('div', {'class': 'attachment'}).adopt(
			new Element('span', {'class': 'indicator', 'html': '&raquo;'})
		);
		
		switch(item.attachment_type)
		{
		
			case 1:
				
				var pipioUrl = DataUtility.getPipioUrl(null, item.attachment[0].hash);
				
				item.attachment[0] = AttachmentUtility.parsePhotoAttachment(item.attachment[0]);
				item.attachment[0].url = pipioUrl;
				
				new Element('a', {
					'target': '_blank',
					'href': pipioUrl,
					'text': TextUtility.unescape(item.attachment[0].filename)
				}).inject(el);
				

				if (item.attachment[0].is_photo)
				{
					var playButton = new ButtonSmall({
						displayName: 'View Photo',
						action: 'photo'
					});
					$(playButton).addEvent('click', pipio.dispatchEvent.bind(pipio, ['showPhotoPopup', item.attachment[0]]));
					$(playButton).inject(el);
				}
				
				break;
				
			case 2:
				
				var pipioUrl = DataUtility.getPipioUrl(item.attachment[0].url, item.attachment[0].hash);
				
				item.attachment[0] = AttachmentUtility.parseLinkAttachment(item.attachment[0]);
				
				new Element('a', {
					'target': '_blank',
					'href': pipioUrl,
					'text': TextUtility.unescape(item.attachment[0].url)
				}).inject(el);
				
				if (item.attachment[0].is_video)
				{
					var playButton = new ButtonSmall({
						displayName: 'Play Video',
						action: 'pivot_right dark'
					});
					$(playButton).addEvent('click', pipio.dispatchEvent.bind(pipio, ['showVideoPopup', item.attachment[0]]));
					$(playButton).inject(el);
				}
				else if (item.attachment[0].is_photo)
				{
					var playButton = new ButtonSmall({
						displayName: 'View Photo',
						action: 'photo'
					});
					$(playButton).addEvent('click', pipio.dispatchEvent.bind(pipio, ['showPhotoPopup', {filename: 'Photo', url: item.attachment[0].url}]));
					$(playButton).inject(el);
				}
				
				break;
		
		
		
		}
		
		return new Element('div', {'class': 'attachments'}).adopt(el);
	},
	
	createStreamItemFooter: function(item){
		var el = new Element('div', {'class': 'footer'});
		
		if (!$defined(item.date_created))
			return el;
		
		var date = DateUtility.convertFromTimestamp(item.date_created); 
		
		var ts = new Element('span', {
			'class': 'timestamp', 
			'text': DateUtility.getTimestamp(date, '%B %D at %I:%M%p'),
			'ts': date,
			'ts_format': '%B %D at %I:%M%p'
		}).inject(el);
		
		//check if location
		if ($defined(item.location) && $defined(item.geohash) && item.geohash != '')
		{
			var loc = DataUtility.getGeoString(item.location);
			if (loc)
			{
				el.appendText(' from ');
				new Element('span', {'text': TextUtility.unescape(loc)}).inject(el);
		
			}
		}
		
		//add actions
		StreamItemUtility.createFooterActions(item, el);
		
		return el;
		
	},

	createConvoStreamItemFooter: function(item){
		var el = new Element('div', {'class': 'footer'});
		
		if (!$defined(item.date_created))
			return el;
		
		var date = DateUtility.convertFromTimestamp(item.date_created); 
		
		var ts = new Element('span', {
			'class': 'timestamp', 
			'text': DateUtility.getTimestamp(date, '%B %D at %I:%M%p'),
			'ts': date,
			'ts_format': '%B %D at %I:%M%p'
		}).inject(el);
		
		//check if location
		if ($defined(item.location) && $defined(item.geohash) && item.geohash != '')
		{
			var loc = DataUtility.getGeoString(item.location);
			if (loc)
			{
				el.appendText(' from ');
				new Element('span', {'text': TextUtility.unescape(loc)}).inject(el);
		
			}
		}
		
		//add actions
		StreamItemUtility.createConvoFooterActions(item, el);
		
		return el;
		
	},
	
	createFooterActions: function(item, el){
		if (!pipio.isLoggedIn())
			return;
		
		if (item.convo_id != item.item_id)
		{
			var convoAction = StreamItemUtility.createFooterAction('convo', 'View Conversation', true);
			convoAction.addEvent('click', pipio.dispatchEvent.bind(pipio, ['showConvo', item.convo_id]));
			convoAction.inject(el);
		}
		else
		{
			var convoAction = StreamItemUtility.createRepliesAction(item);
			convoAction.addEvent('click', pipio.dispatchEvent.bind(pipio, ['showConvo', item.convo_id]));
			convoAction.inject(el);
		}
		
		var replyAction = StreamItemUtility.createFooterAction('reply', 'Reply', true);
		replyAction.addEvent('click', pipio.dispatchEvent.bind(pipio, ['replyShareBoxShow', item.item_id]));
		replyAction.inject(el);
		
		if (item.is_public == 1) //can only forward public items
		{
			var forward_id = (item.post_type == 3 && item.forward_id != 0) ? item.forward_id : item.item_id;
			var forwardAction = StreamItemUtility.createFooterAction('forward', 'Forward', true).inject(el);
			forwardAction.addEvent('click', pipio.dispatchEvent.bind(pipio, ['forwardShareBoxShow', forward_id, item.creator.username, item.post.body]));
		}
	},
	
	createConvoFooterActions: function(item, el){
		
		var replyAction = StreamItemUtility.createFooterAction('reply', 'Reply', true);
		replyAction.addEvent('click', pipio.dispatchEvent.bind(pipio, ['replyShareBoxShow', item.item_id]));
		replyAction.inject(el);
		
		if (item.is_public == 1) //can only forward public items
		{
			var forward_id = (item.forward_id != 0) ? item.forward_id : item.item_id;
			var forwardAction = StreamItemUtility.createFooterAction('forward', 'Forward', true).inject(el);
			forwardAction.addEvent('click', pipio.dispatchEvent.bind(pipio, ['forwardShareBoxShow', forward_id, item.creator.username, item.post.body]));
		}
	},
	
	
	createRepliesAction: function(item){
		var repliesClass = 'replies_' + item.item_id;
		
		var replies = item.replies + item.private_replies;
		
		var el = new Element('span', {'class': 'footer_action has_action ' + repliesClass, 'replies': replies}).adopt(
				new Element('div', {'class': 'action convo'})	
			);
		
		el.appendText(TextUtility.pluralText(replies, 'Reply', 'Replies'));
		return el;
	},
	
	updateReplyCount: function(item_id, count){
		$$('.replies_' + item_id).each(function(el){
			var replies = parseInt(el.get('replies')) + count;
			el.set('replies', replies);
			el.empty();
			el.adopt(
				new Element('div', {'class': 'action convo'})	
			);
			el.appendText(TextUtility.pluralText(replies, 'Reply', 'Replies'));
		});
		
	},
	
	updateItemLiked: function(item_id, likes, is_liked){
		Logger().log(item_id + '-' + likes + '-' + is_liked);
		
		$$('.likes_' + item_id).each(function(el){
			
			el.set('likes', likes);
			
			if(is_liked != -1)
				el.set('is_liked', is_liked);
		
			el.empty();
			new Element('div', {'class': 'text grey text11', 'text': likes, 'likes': likes}).inject(el);
			
			if (likes > 0)
				el.addClass('other_liked');
			else
				el.removeClass('other_liked');
			
			if(is_liked != -1)
			{
				if (is_liked == 1)
					el.addClass('on');
				else
					el.removeClass('on');
			}
		});
	},
	
	createFooterAction: function(actionName, actionText, hide){
		var el = new Element('span', {'class': 'footer_action has_action'}).adopt(
			new Element('div', {'class': 'action ' + actionName})	
		);
		
		el.appendText(actionText);
		
		if (hide)
			el.addClass('hide');
		
		return el;
	},
	
	createStreamUserAction: function(user_action){
		var el = new Element('span', {'class': 'user_action'}).adopt(
			new Element('div', {'class': 'action ' + user_action})
		);
		return el;
	},
	
	createStatusBoxFooter: function(status_message){
		var el = new Element('div', {'class': 'footer'});
		
		if (!$defined(status_message.date_created))
			return el;
		
		var date = DateUtility.convertFromTimestamp(status_message.date_created); 
		
		var ts = new Element('span', {
			'class': 'timestamp', 
			'text': DateUtility.getTimestamp(date, '%B %D at %I:%M%p'),
			'ts': date,
			'ts_format': '%B %D at %I:%M%p'
		}).inject(el);
		
		//check if location
		//check if location
		if ($defined(status_message.location) && $defined(status_message.location.geohash) && status_message.location.geohash != '')
		{
			var loc = DataUtility.getGeoString(status_message.location);
			if (loc)
			{
				el.appendText(' from ');
				new Element('span', {'text': TextUtility.unescape(loc)}).inject(el);
		
			}
		}
		
		return el;
		
	}
		
};var Room = new Class({
	Extends: AppInstance,
	
	EventHandlers: [
	                'roomClosed',
	                'userSwitched'
	                ],
	
	parseOptions: function(options){
		this.room = options.room;
		this.displayName = this.room.room_name;
		this.appId = 'room_' + this.room.username;
		this.iconOptions = {user: this.room};
	},
	
	userSwitched: function(){
		this.stop();
	},
	
	onStart: function(){
		//setup and create navs
		this.setupNav();
	},
	
	onStop: function(){
		
	},
	
	roomClosed: function(data){
		if (!$defined(data.username) || data.username != this.room.username)
			return;
		
		this.stop();
	}
	
});Room.implement({
	setupNav: function(){
	
		Logger().log('home setup');
		this.navAdd(new Nav({
			iconOptions: {
				iconName: 'updates'
			},
			displayName: 'Updates',
			name: 'updates',
			onClick: this.fireEvent.bind(this, ['viewSwitch', this.appId, 'updates', 'room_menu'])
		}));
		
		//add Menu
		var roomMenu = new RoomMenu({displayName: this.room.room_name, room: this.room, isDefault: true});
		this.menuAdd('room_menu', roomMenu);
		
		var content = new RoomContent({isDefault: true, room: this.room});
		this.contentAdd('updates', content);
		
	}
	
	
});var RoomContent = new Class({
	Extends: Content,
	
	EventHandlers: [
	                'streamItemReceived',
	                'roomStreamItemDeleted'
	               // 'streamItemDeleted'
	                ],
	
	onBeforeInit: function(options){
		this.room = options.room;
	},
	           
	onInit: function(){
		//status box
		this.statusBox = new RoomStatusBox(this.room);
		$(this.statusBox).inject(this.content);
	
		//create loader
		this.loader = new StreamLoader({
			createElementFunc: StreamItemUtility.createStreamMemberRoomItem,
			emptyEl: new Element('div', {'class': 'post empty', 'text': 'This room has no updates'}),
			errorEl: new Element('div', {'class': 'post empty', 'text': 'This room is private'})//,
			//alertFunc: this.alertCheck.bind(this)
		});
		$(this.loader).inject(this.content);
		
	
	},
	
	onShow: function(first){
		if (first)
			this.loadMore();
	},
	
	onHide: function(){
		
	},
	
	//DEPRECATED
	roomStreamItemReceived: function(data){
		if (!$defined(data.item) || !$defined(data.username) || data.username != this.room.username)
			return;
		
		Logger().log('roomItemRecevied, processing');
		this.loader.process(data.item);
	},
	
	streamItemReceived: function(data){
		if ($defined(data.item) && data.item.source.username == this.room.username && data.item.is_public == 1)
			this.loader.process(data.item);
	},
	
	roomStreamItemDeleted: function(data){
		if (!$defined(data.username) || data.username != this.room.username)
			return;
		
		this.loader.remove(data.item_id);
	},
	
	alertCheck: function(item){
		if (item.creator_id != this.getPrivateUser().user_id)
			this.fireEvent('alertAdd', '2_room_' + this.room.username);
	},
	
	loadMore: function(){
		var params = {
				username: this.room.username,
				date_created: this.loader.oldestTimestamp,
				item_id: this.loader.oldestId
		};
		
		if (this.isLoggedIn())
			this.call('home', 'stream_room_load', params, this.loadMoreSuccess.bind(this), this.loadMoreFail.bind(this));
		else
			this.call('home', 'public_stream_room_load', params, this.loadMoreSuccess.bind(this), this.loadMoreFail.bind(this));
	},
	
	loadMoreSuccess: function(data){
		if (!$defined(data.items) || data.items.length == 0)
		{
			this.atBottom = true;
			return;
		}
		
		data.items.each(function(item){
			this.loader.process(item);
		}, this);
			
		this.bottomFuncCalled = false;
	},
	
	loadMoreFail: function(status){
		if (status.code == 2002)
			this.loader.showError();
	},
	
	bottomFunc: function(){
		this.loadMore();
	}
});var RoomConnectionMenuSection = new Class({
	Extends: Base,
	
	EventHandlers: ['roomStreamSubscriptionAdded',
	                'roomStreamSubscriptionDeleted',
	                'roomMembershipUpdated'
	                ],
	
	init: function(options){
		
		this.room = options.room;
		this.subscribed = this.room.subscribed == 1;
		
		this.isCreator = this.room.creator_id == this.getPrivateUser().user_id;
		
		this.createSection();
	},
	
	createSection: function(){
		this.wrapper = new Element('div');
		
		this.generateSection();
	},
	
	setRoom: function(room){
		
		this.room = room;
		this.generateSection();
		
	},
	
	generateSection: function(){
		
		this.wrapper.empty();
		
		//insert subscription section
		this.generateSubscriptionSection();
		
		
		if (this.isCreator)
		{

			this.generatePrivacySection();
			new Element('div', {'class': 'text_section light centered text11', 'text': 'You are the creator'}).inject(this.wrapper);
			
			//add delete button
			this.button = new ButtonMedium({
				displayName: 'Close Room',
				className: 'dark',
				action: 'cross'
			});
			
			var name = 'roomDelete_' + this.room.username;
			var title = 'Close ' + this.room.room_name;
			var message = 'Are you sure you want to close the room ' + this.room.room_name + '?';
			var func = pipio.dispatchEvent.bind(pipio, ['feedRoomDelete', this.room.username]);
			
			$(this.button).addEvent('click', pipio.dispatchEvent.bind(pipio, ['showConfirmation', name, title, message, func]));
			
			new Element('div', {'class': 'button_section'}).adopt(
				$(this.button)
			).inject(this.wrapper);
		}
		else
		{
			switch(parseInt(this.room.status))
			{
			case 1:
				this.createConnectedSection();
				break;
			case 2:
				this.createRequestSection();
				break;
			case 3:
				this.createRequestOutSection();
				break;
			default:
				this.createNotConnectedSection();
				break;
			}
		}
		
		
	},
	
	createNotConnectedSection: function(){
		this.button = new ButtonMedium({
			displayName: 'Join Room',
			className: 'dark',
			action: 'check'
		});
		
		var name = 'roomRequest_' + this.room.username;
		var title = 'Join ' + this.room.room_name;
		var message = 'Send membership request to ' + this.room.room_name + '?';
		var func = this.roomRequestCreate.bind(this);
		$(this.button).addEvent('click', pipio.dispatchEvent.bind(pipio, ['showConfirmation', name, title, message, func]));
		
		new Element('div', {'class': 'button_section'}).adopt(
			$(this.button)
		).inject(this.wrapper);
		
	},
	
	createConnectedSection: function(){
		new Element('div', {'class': 'text_section light centered text11', 'text': 'You are a member'}).inject(this.wrapper);
		
		this.button = new ButtonMedium({
			displayName: 'Leave Room',
			className: 'dark',
			action: 'cross'
		});
		
		var name = 'roomLeave_' + this.room.username;
		var title = 'Leave ' + this.room.room_name;
		var message = 'Are you sure you want to leave ' + this.room.room_name + '?';
		var func = this.roomMembershipDelete.bind(this);
		$(this.button).addEvent('click', pipio.dispatchEvent.bind(pipio, ['showConfirmation', name, title, message, func]));
		
		new Element('div', {'class': 'button_section'}).adopt(
			$(this.button)
		).inject(this.wrapper);
	},
	
	createRequestSection: function(){
		this.button = new ButtonMedium({
			displayName: 'Accept Membership',
			className: 'dark',
			action: 'check'
		});
		
		$(this.button).addEvent('click', this.roomInviteAccept.bind(this));
		
		new Element('div', {'class': 'button_section'}).adopt(
			$(this.button)
		).inject(this.wrapper);
		
		this.button2 = new ButtonMedium({
			displayName: 'Reject Membership',
			className: 'dark',
			action: 'cross'
		});
		
		$(this.button2).addEvent('click', this.roomInviteDelete.bind(this));
		
		new Element('div', {'class': 'button_section'}).adopt(
			$(this.button2)
		).inject(this.wrapper);
	},
	
	roomInviteAccept: function(){
		this.call('home', 'room_invite_accept', {username: this.room.username}, null, null);
		this.button.showProgress();
	},
	
	roomInviteDelete: function(){
		var params = {
			username: this.getPrivateUser().username,
			room_username: this.room.username
				
		};
		this.call('home', 'room_invite_delete', params, null, null);
		this.button2.showProgress();
	},
	
	createRequestOutSection : function(){
		new Element('div', {'class': 'text_section light centered text11', 'text': 'Membership request pending'}).inject(this.wrapper);
	},
	
	roomRequestCreate: function(){
		this.call('home', 'room_request_create', {username: this.room.username}, null, null);
		this.button.showProgress();
	},
	
	roomMembershipDelete: function(){
		this.call('home', 'room_leave', {username: this.room.username}, null, null);
		this.button.showProgress();
	},

	generateSubscriptionSection: function(){
		
		this.subscription = new Toggle(this.subscribed);
		
		$(this.subscription).addEvent('click', this.subscriptionToggle.bind(this));
		
		new Element('div', {'class': 'toggle_section'}).adopt(
			new Element('div', {'class': 'text light', 'text': 'Subscribe:'}),
			$(this.subscription)
		).inject(this.wrapper);
	},
	
	generatePrivacySection: function(){
		
		this.privacy = new Toggle(this.room.is_public == 1);
		
		$(this.privacy).addEvent('click', this.privacyToggle.bind(this));
		
		new Element('div', {'class': 'toggle_section'}).adopt(
			new Element('div', {'class': 'text light', 'text': 'Public:'}),
			$(this.privacy)
		).inject(this.wrapper);
				
		
	},
	
	privacyToggle: function(){
		//this.privacy.switchToggle();
		this.call('home', 'room_privacy_set', {username: this.room.username, is_public: this.privacy.toInt()});
		
	},
	
	subscriptionToggle: function(){
		Logger().log(this.subscribed);
		if (this.subscribed)
			this.streamUnsubscribe();
		else
			this.streamSubscribe();
	},
	
	streamUnsubscribe: function(){
		this.subscription.off();
		this.call('home', 'room_unsubscribe', {username: this.room.username});
	},
	
	streamSubscribe: function(){
		this.subscription.on();
		this.call('home', 'room_subscribe', {username: this.room.username}, null, this.streamSubscribeFail.bind(this));
	},
	
	streamSubscribeFail: function(status){
		if (status.code == 2002)
		{
			this.subscription.off();
			
			var name = 'subscription';
			var title = 'Error';
			var message = status.message;

			this.fireEvent('showAlert', name, title, message);
		}
	},
	
	roomPrivacyUpdated: function(data){
		if (!$defined(data.username) || data.username != this.room.username, !$defined(data.is_public))
			return;
		
		if (data.is_public == 1)
			this.privacy.on();
		else
			this.privacy.off();
		
	},
	
	roomStreamSubscriptionAdded: function(data){
		if (!$defined(data.room) || data.room.username != this.room.username)
			return;
	
		this.subscribed = true;
		this.generateSection();
	},
	
	roomStreamSubscriptionDeleted: function(data){
		if (!$defined(data.username) || data.username != this.room.username)
			return;
		
		this.subscribed = false;
		this.generateSection();
	},
	
	roomMembershipUpdated: function(data){
		if (!$defined(data.username) || data.username != this.room.username)
			return;
		
		this.room.status = data.status;
		
		this.generateSection();
		
	},
	
	toElement: function(){
		return this.wrapper;
	}
	
});var RoomMenu = new Class({
	Extends: Menu,
	
	EventHandlers: [
	                'roomMemberAdded',
	                'roomMemberDeleted',
	                'roomSubscriberAdded',
	                'roomSubscriberDeleted'
	                ],
	
	strings: {
		groupMenuText: 'These are contacts in your group "{0}"',
		unsortedGroupMenuText: 'These are contacts that have not been added to a group'
	},
	
	onBeforeInit: function(options){
		this.room = options.room;
		
		this.isCreator = this.room.creator_id == this.getPrivateUser().user_id;
		
		return options;
	},
	
	onInit: function(){
		
		this.aboutText = new Element('span', {'text': TextUtility.unescape(this.room.room_description)});
		this.aboutSection = new Element('div', {'class': 'text_section light1 text11'}).adopt(
			new Element('span', {'class': 'bold', 'text': 'About: '}),
			this.aboutText
		).inject(this.menu);
		
		
		ItemUtility.createProfilePic(this.room, 100).inject(this.menu);
		if (this.isCreator)
		{

			//profile pic upload button
			this.profilePicUploadButton = new ButtonMedium({
				displayName: 'Change Picture',
				className: 'dark',
				action: 'photo'
			});
			
			this.fileInput = new Element('input', {'type': 'file', 'name': 'file'});
			this.form = new Element('form').adopt(this.fileInput);
			
			this.fileInput.addEvent('change', this.profilePicUpload.bind(this));
			
			this.profilePicUploadSection = new Element('div', {'class': 'button_section'}).adopt(
				$(this.profilePicUploadButton).adopt(
					this.form
				)
			).inject(this.menu);	
			
		}
		
		
		if (this.isLoggedIn())
		{
				//connection button & toggle
			this.connect = new RoomConnectionMenuSection({room: this.room});
			$(this.connect).inject(this.menu);
			
			if (this.isCreator)
			{
				//add invite button
				this.inviteButton = new ButtonMedium({
					displayName: 'Invite Members',
					className: 'dark',
					action: 'status'
				});
				
				$(this.inviteButton).addEvent('click', pipio.dispatchEvent.bind(pipio, ['showRoomInvitePopup', this.room]));
				
				new Element('div', {'class': 'button_section'}).adopt(
					$(this.inviteButton)
				).inject(this.menu);
			
			
			}
		
		}
		
		new Element('div', {'class': 'nav_seperator'}).inject(this.menu);
		
		this.members = new MembersNav({user: this.room});
		$(this.members).inject(this.menu);
		
		this.subscribers = new SubscribersNav({user: this.room});
		$(this.subscribers).inject(this.menu);
	},
	
	onShow: function(first){
		if (first)
			this.roomInfoLoad();
	},
	
	roomInfoLoad: function(){
		if (this.isLoggedIn())
			this.call('home', 'room_info_load', {username: this.room.username}, this.roomInfoLoadSuccess.bind(this), null);
		else
			this.call('home', 'public_room_info_load', {username: this.room.username}, this.roomInfoLoadSuccess.bind(this), null);
		
	},
	
	roomMemberAdded: function(data){
		if (!$defined(data.username) || !$defined(data.user) || data.username != this.room.username)
			return;
		
		this.members.addUsers(data.user);
	},
	
	roomMemberDeleted: function(data){
		if (!$defined(data.username) || !$defined(data.room_username) || data.room_username != this.room.username)
			return;
		
		this.members.removeUser(data.username);
	},
	
	roomSubscriberAdded: function(data){
		if (!$defined(data.username) || !$defined(data.user) || data.username != this.room.username)
			return;
		
		this.subscribers.addUsers(data.user);
	},
	
	roomSubscriberDeleted: function(data){
		if (!$defined(data.username) || !$defined(data.room_username) || data.room_username != this.room.username)
			return;
		
		this.subscribers.removeUser(data.username);
	},
	
	roomInfoLoadSuccess: function(data){
		if (!$defined(data.room))
			return;
		
		//this.fireEvent('roomInfoLoaded', data);
		///this.connect.setData(data.status, data.subscription_id, data.pending);
		
		//this.aboutText.set('text', TextUtility.unescape(data.room.room_description));
		
		this.connect.setRoom(data.room);
		this.subscribers.addUsers(data.subscribers);
		this.members.addUsers(data.members);
	},
	
	profilePicUpload: function(){
		
		if (DataUtility.validatePhotoFile(this.fileInput.value))
		{
			this.call('pipio', 'room_profilepic_upload', {username: this.room.username}, this.profilePicUploadSuccess.bind(this), this.profilePicUploadFail.bind(this), this.form);
			this.profilePicUploadButton.showProgress();
		}
		
	},
	
	profilePicUploadSuccess: function(){
		this.profilePicUploadButton.hideProgress();
		this.resetForm();
	},
	
	profilePicUploadFail: function(){
		this.profilePicUploadButton.hideProgress();
		this.resetForm();
	},
	
	resetForm: function(){
		this.profilePicUploadSection.empty();
		
		//profile pic upload button
		this.profilePicUploadButton = new ButtonMedium({
			displayName: 'Change Picture',
			className: 'dark',
			action: 'photo'
		});
		
		this.fileInput = new Element('input', {'type': 'file', 'name': 'file'});
		this.form = new Element('form').adopt(this.fileInput);
		
		this.fileInput.addEvent('change', this.profilePicUpload.bind(this));
		
		this.profilePicUploadSection.adopt(
			$(this.profilePicUploadButton).adopt(
				this.form
			)		
		);
	}
	
});var Rss = new Class({
	Extends: App,
	
	EventHandlers: [
	         'userSwitched',
	         
	         'rssInstall',
	         
	         'rssUninstall',
	     
	     	'rssFeedSubscribed',
     		'rssFeedUnsubscribed',
     		
     		'showAddRssFeedPopup'
	     ],
	
	onStart: function(){
		this.setupNav();
		this.feeds = $H();
		this.loadSubscriptions();
	},
	
	onStop: function(){
		
	},
	
	userSwitched: function(){
		if (!this.isLoggedIn())
			this.stop();
	},
	

	loadSubscriptions: function(){
		this.call('rss', 'rss_subscriptions', null, this.loadSubscriptionsSuccess.bind(this), null);
	},
	
	loadSubscriptionsSuccess: function(data){
		if (!$defined(data.subscriptions))
			return;
		
		data.subscriptions.each(function(subscription){
			
			this.rssFeedAdd(subscription);
			
		}, this);
	},
	
	rssFeedSubscribed: function(data){
		if (!$defined(data.subscription))
			return;
		
		this.rssFeedAdd(data.subscription);
		
	},
	
	rssFeedUnsubscribed: function(data){
		if (!$defined(data.atom_id))
			return;
		
		this.rssFeedDelete(data.atom_id);
	},
	
	rssUninstall: function() {
		this.call('rss', 'rss_uninstall', null, this.rssUninstallSuccess.bind(this), null);
	},
	
	rssUninstallSuccess: function(){
		DomUtility.hide('app_menu_item_4');
		DomUtility.show('no_apps');
		$('appstore_item_4').removeClass('installed');
		$('appstore_item_4').addClass('uninstalled');
		this.stop();
	},
	
	rssInstall: function() {
		this.call('rss', 'rss_install', null, this.rssInstallSuccess.bind(this), null);
	},
	
	rssInstallSuccess: function(){
		DomUtility.show('app_menu_item_4');
		DomUtility.hide('no_apps');
		$('appstore_item_4').addClass('installed');
		$('appstore_item_4').removeClass('uninstalled');
		this.start();
	}
	
	
});Rss.implement({
	requests: [

	           {
	        	   name: 'rss_subscriptions',
	        	   url: '/api/app/rss/subscriptions'
	           },
				{
					name: 'rss_subscriptions_add',
					params: ['feed_url'],
					url: '/api/app/rss/subscriptions/add'
				},
				{
					name: 'rss_subscriptions_delete',
					params: ['subscription_id'],
					url: '/api/app/rss/subscriptions/delete'
				},
				{
					name: 'rss_feed',
					params: ['atom_id', 'item_id'],
					url: '/api/app/rss/feed'
				},
				{
					name: 'rss_feed_all',
					url: '/api/app/rss/feed/all'
				},
				{
					name: 'rss_feed_subscribers',
					params: ['atom_id'],
					url: '/api/app/rss/feed/subscribers'
				},
				{
					name:	'rss_install',
					params: [],
					url: '/api/app/rss/install'
				},
				{
					name:	'rss_uninstall',
					params: [],
					url: '/api/app/rss/uninstall'
				}

	]
});Rss.implement({
	setupNav: function(){
	
		this.navAdd(new Nav({
			iconOptions: {
				iconName: 'updates'
			},
			displayName: 'All News Updates',
			name: 'updates',
			onClick: this.fireEvent.bind(this, ['viewSwitch', this.appId, 'updates', 'rss_menu'])
		}));
		
		this.navAdd(new Nav({
			iconOptions: {
				iconName: 'rss'
			},
			hasSubnavs: true,
			displayName: 'Feeds',
			name: 'feeds'
		}));
		
		this.navAdd(new Nav({
			iconOptions: {
				iconName: 'broadcast',
				iconAction: 'add'
			},
			displayName: 'Add New Feed...',
			onClick: this.showAddRssFeedPopup.bind(this),
			name: 'new_feed',
			parentName: 'feeds',
			bottom: true
		}));
		
		//add Menu
		var rssMenu = new RssMenu({isDefault: true});
		this.menuAdd('rss_menu', rssMenu);
		
		var content = new RssContent({isDefault: true});
		this.contentAdd('updates', content);
		
	},
	
	rssFeedAdd: function(feed){
		if (this.feeds.has(feed.atom_id))
			return;
		
		//create nav element 
		var nav = new Nav({
			iconOptions: {
				'iconName': 'rss'
			},
			displayName: feed.title,
			name: 'rss_' + feed.atom_id,
			parentName: 'feeds',
			onClick: this.fireEvent.bind(this, ['viewSwitch', this.appId, 'rss_' + feed.atom_id, 'rss_' + feed.atom_id])
		});
		//insert nav element
		this.navAdd(nav);
		//store subscription
		this.feeds.set(feed.atom_id, nav);
		
		var feedMenu = new RssFeedMenu({displayName: feed.title, feed: feed});
		this.menuAdd('rss_' + feed.atom_id, feedMenu);
		
		var content = new RssFeedContent({feed: feed});
		this.contentAdd('rss_' + feed.atom_id, content);
	},
	
	rssFeedDelete: function(atom_id){
		if (!this.feeds.has(atom_id))
			return;
		
		this.feeds.get(atom_id).destroy();
		this.navDelete('rss_' + atom_id);
		this.feeds.erase(atom_id);
		
		this.menuClose('rss_' + atom_id);
		this.contentClose('rss_' + atom_id);
		
	},
	

	showAddRssFeedPopup: function(){
		if ($defined(this.addFeedPopup))
			this.addFeedPopup.close();
			
		this.createAddRssFeedPopup();
	},
	
	createAddRssFeedPopup: function(){
		this.addFeedPopup = new Popup({
			size: {x: 350, y: 94},
			resizable: false,
			dockable: false,
			className: 'addFeed',
			onClose: this.destroyAddRssFeedPopup.bind(this)
		});
		
		//add popup content
		var nav = new Nav({
			iconOptions: {iconName: 'rss', iconAction: 'add'},
			displayName: 'Subscribe to a Feed',
			closable: true
		});
		
		var content = new AddRssFeedPopupContent({});
		
		this.addFeedPopup.addContent('add_feed', nav, content);
	},
	
	destroyAddRssFeedPopup: function(){
		this.addFeedPopup = null;
	}
});var RssContent = new Class({
	Extends: Content,
	
	EventHandlers: [
	                'rssItemReceived'
	],
	                
	onBeforeInit: function(options){
		this.feed = options.feed;
		return options;
	},
	
	onInit: function(){
		//status box
	
		//create loader
	/*	this.loader = new StreamLoader({
			createElementFunc: RssItemUtility.createRssItem,
			idField: 'atom_item_id',
			dateField: 'date_created',
			emptyEl: new Element('div', {'class': 'post empty', 'text': 'This feed has no items'})
		});
		*/
		
		this.loader = new ItemLoader({
			createElementFunc: RssItemUtility.createRssAllItem,
			idField: 'atom_item_id',
			sortField: 'date_created',
			sortAscending: false,
			emptyEl: new Element('div', {'class': 'post empty', 'text': 'This feed has no items'})
		});
		
		$(this.loader).inject(this.content);
		
	
	},
	
	onShow: function(first){
		if (first)
			this.loadMore();
	},
	
	onHide: function(){
		
	},
	
	rssItemReceived: function(data){
		if ($defined(data.item))
			this.loader.process(data.item);
	},
	
	loadMore: function(){
		var params = {
				date_created: this.loader.oldestTimestamp
		};
		
		this.call('rss', 'rss_feed_all', params, this.loadMoreSuccess.bind(this), null);
	},
	
	loadMoreSuccess: function(data){
		if (!$defined(data.items) || data.items.length == 0)
		{
			this.atBottom = true;
			return;
		}
		
		data.items.each(function(item){
			item.feed = this.feed;
			this.loader.process(item);
		}, this);
			
		this.bottomFuncCalled = false;
	},
	
	bottomFunc: function(){
		Logger().log('rss content bottom');
		//this.loadMore();
	}
});var RssFeedContent = new Class({
	Extends: Content,
	
	EventHandlers: [
	                'rssItemReceived'
	],
	                
	onBeforeInit: function(options){
		this.feed = options.feed;
		return options;
	},
	
	onInit: function(){
		//status box
	

		this.loader = new ItemLoader({
			createElementFunc: RssItemUtility.createRssFeedItem,
			idField: 'atom_item_id',
			sortField: 'date_created',
			sortAscending: false,
			emptyEl: new Element('div', {'class': 'post empty', 'text': 'This feed has no items'})
		});
		
		$(this.loader).inject(this.content);
		
	
	},
	
	onShow: function(first){
		if (first)
			this.loadMore();
	},
	
	onHide: function(){
		
	},
	
	rssItemReceived: function(data){
		
		if ($defined(data.item) && data.item.atom_id == this.feed.atom_id)
			this.loader.process(data.item);
	},
	
	loadMore: function(){
		var params = {
				atom_id: this.feed.atom_id,
				date_created: this.loader.oldestTimestamp
		};
		
		this.call('rss', 'rss_feed', params, this.loadMoreSuccess.bind(this), null);
	},
	
	loadMoreSuccess: function(data){
		if (!$defined(data.items) || data.items.length == 0)
		{
			this.atBottom = true;
			return;
		}
		
		data.items.each(function(item){
			item.feed = this.feed;
			this.loader.process(item);
		}, this);
			
		this.bottomFuncCalled = false;
	},
	
	bottomFunc: function(){
		Logger().log('rss content bottom');
		//this.loadMore();
	}
});var RssFeedMenu = new Class({
	Extends: Menu,
	
	onBeforeInit: function(options){
		
		this.feed = options.feed;
		
		return options;
	},
	
	onInit: function(){
		
		this.urlText = new Element('a', {'target': '_blank', 'href': this.feed.link, 'text': TextUtility.unescape(this.feed.link)});
		new Element('div', {'class': 'text_section nowrap text11 light1'}).adopt(
			new Element('span', {'class': 'bold', 'text': 'Web: '}),
			this.urlText	
		).inject(this.menu);
		
			
		new Element('div', {'class': 'text_section light1', 'text': TextUtility.unescape(this.feed.description)}).inject(this.menu);
		
			
		//delete button
		this.deleteButton = new ButtonMedium({
			displayName: 'Unsubscribe',
			className: 'dark',
			action: 'cross'
		});
		
		var name = 'rssFeedUnsubscribe_' + this.feed.atom_id;
		var title = 'Unsubscribe ' + this.feed.title;
		var message = 'Are you sure you want to unsubscribe from the feed ' + this.feed.title + '?';
		var func = this.rssUnsubscribe.bind(this);
		$(this.deleteButton).addEvent('click', pipio.dispatchEvent.bind(pipio, ['showConfirmation', name, title, message, func]));
		
		
		//$(this.deleteButton).addEvent('click', this.deleteGroup.bind(this));
		
		new Element('div', {'class': 'button_section'}).adopt(
			$(this.deleteButton)
		).inject(this.menu);
		
		
		new Element('div', {'class': 'nav_seperator'}).inject(this.menu);
		
		this.subscribers = new SubscribersNav({user: this.feed});
		$(this.subscribers).inject(this.menu);
	
	},
	
	onShow: function(first){
		if (first)
			this.subscribersLoad();
	},
	
	subscribersLoad: function(){
		
		this.call('rss', 'rss_feed_subscribers', {atom_id: this.feed.atom_id}, this.subscribersLoadSuccess.bind(this), null);
	},
	
	subscribersLoadSuccess: function(data){
		if (!$defined(data.subscribers))
			return;
			
		this.subscribers.addUsers(data.subscribers);
		
	},
	
	
	rssUnsubscribe: function(){
		
		var params = {subscription_id: this.feed.subscription_id};
		
		this.call('rss', 'rss_subscriptions_delete', params, this.rssUnsubscribeSuccess.bind(this), this.rssUnsubscribeFail.bind(this));
		
		this.deleteButton.showProgress();
	},
	
	rssUnsubscribeSuccess: function(data){
		this.deleteButton.hideProgress();
	},
	
	rssUnsubscribeFail: function(){
		this.deleteButton.hideProgress();
	}
	
});var RssMenu = new Class({
	Extends: Menu,
	
	onBeforeInit: function(options){
	
		options.displayName =  'News Reader';
		return options;
	},
	
	onInit: function(){
		
		new Element('div', {'class': 'text_section light1', 'text': TextUtility.unescape('All updates from your subscriptions will be visible here')}).inject(this.menu);
	
	}
});var AddRssFeedPopupContent = new Class({
	Extends: PopupContent,
	
	strings: {
		feedUrlLabel: 'Feed URL:',
		errorMessage: 'There was an error subscribing to this feed'
	},
	
	onInit: function(){
		
		this.feedUrlInput = new Element('textarea', {'maxlength': 200});
		this.feedUrlInput.addEvent('keyup', this.inputKeyUp.bindWithEvent(this));
		
		new Element('div', {'class': 'input_section'}).adopt(
				new Element('div', {'class': 'label light', 'text': this.strings.feedUrlLabel}),
				new Element('div', {'class': 'textarea_wrapper'}).adopt(
					this.feedUrlInput
				)
			).inject(this.content);
		
		//ACTIONS
		
		this.closeButton = new ButtonMedium({
			displayName: 'Cancel',
			className: 'dark',
			action: 'cross'
		});
		$(this.closeButton).addEvent('click', this.closeContent.bind(this));
		
		this.actionButton = new ButtonMedium({
			displayName: 'Subscribe',
			className: 'dark',
			action: 'check'
		});
		$(this.actionButton).addEvent('click', this.feedSubscribe.bind(this));
		
		this.message = new Element('div', {'class': 'message success'});
		
		this.actions = new Element('div', {'class': 'actions'}).adopt(
			this.message,
			$(this.actionButton),
			$(this.closeButton)
		).inject(this.content);
	},
	
	inputKeyUp: function(e){
		if (e.key == 'enter')
		{
			if (this.feedUrlInput.value.trim() != '')
			this.feedSubscribe();
		}
	},
	
	feedSubscribe: function(){
		
		var url = this.feedUrlInput.value.trim();
		var params = {feed_url: url};
		
		this.call('rss', 'rss_subscriptions_add', params, this.feedSubscribeSuccess.bind(this), this.feedSubscribeFail.bind(this));
		
		this.actionButton.showProgress();
		this.message.set('text', 'please wait...');
	},
	
	feedSubscribeSuccess: function(){
		this.actionButton.hideProgress();
		this.closeContent();
	},
	
	
	feedSubscribeFail: function(status){
		this.actionButton.hideProgress();

		var name = 'feed_subscribe';
		var title = 'Error Subscribing to Feed';
		var message = status.message;
		this.message.empty();

		this.fireEvent('showAlert', name, title, message);
	},
	
	onShow: function(){
		this.focus();
	},
	
	focus: function(){
		this.feedUrlInput.focus.delay(500, this.feedUrlInput);
	}
	
});var RssItemUtility = {
	
	createRssFeedItem: function(item){
		return RssItemUtility.createRssItem(item, false);
	},

	createRssAllItem: function(item){
		return RssItemUtility.createRssItem(item, true);
	},

	createRssItem: function(item, showFeed){
		
		var el = ItemUtility.createPostBubble('post haspic');
		
		ItemUtility.createItemPic('/images/v5/apps/rss/feed_icon.png').inject(el);
		
		var content = new Element('div', {'class': 'post_content'}).inject(el);
	
		//header
		RssItemUtility.createRssItemHeader(item, showFeed).inject(content);
		
		RssItemUtility.createRssItemFooter(item, showFeed).inject(content);
		
		return el;
	},
	
	createRssItemHeader: function(item, showFeed){
		var el = new Element('div', {'class': 'header'});
		
		//feed name
		//if (showFeed)
		//{
		//	var feedEl = new Element('span', {'class': 'user_name clickable', 'text': TextUtility.unescape(item.feed.title)}).inject(el);
		//	feedEl.addEvent('click', pipio.dispatchEvent.bind(pipio, ['viewSwitch', 4, 'rss_' + item.atom_id, 'rss_' + item.atom_id]));
		//	new Element('span', {'text': ' - '}).inject(el);
		//}
		
		new Element('span', {}).adopt(
			new Element('a', {'href': item.link, 'class': 'user_name clickable', 'text': TextUtility.unescape(item.title), 'target': '_blank'})
		).inject(el);
		
		if ($defined(item.summary) && item.summary != '')
		{
			new Element('span', {'html': ' ' + TextUtility.replaceUrls(TextUtility.stripHtml(TextUtility.unescape(item.summary)))}).inject(el);
		}
		
		return el;
	},
		
	createRssItemFooter: function(item){
		var el = new Element('div', {'class': 'footer'});
		
		if (!$defined(item.date_created))
			return el;
		
		var date = DateUtility.convertFromTimestamp(item.date_created); 
		
		var ts = new Element('span', {
			'class': 'timestamp', 
			'text': DateUtility.getTimestamp(date, '%B %D at %I:%M%p'),
			'ts': date,
			'ts_format': '%B %D at %I:%M%p'
		}).inject(el);
		
		
		
		//add actions
		//StreamItemUtility.createFooterActions(item, el);
		
		return el;
		
	}
};var Twitter = new Class({
	Extends: App,
	
	onStart: function(){
		//setup and create navs
		this.setupNav();
	},
	
	onStop: function(){
		
	},
	
	userLoggedIn: function(){

	},
	
	userLoggedOut: function(){
		this.stop();
	}
	
});Twitter.implement({
	requests: [
		{
			name: 'twitter_enable',
			params: ['sync_posts'],
			url: '/api/app/twitter/account/setup'
		},
		{
			name: 'twitter_request_auth',
			url: '/api/app/twitter/request/authorization'
		},
		{
			name: 'twitter_access_token',
			url: '/api/app/twitter/get/access_token'
		},
		{
			name: 'twitter_account_verify',
			url: '/api/app/twitter/account/verify'
		},
		{
			name: 'twitter_disable',
			url: '/api/app/twitter/account/disable'
		},
		{
			name: 'twitter_settings_set',
			params: ['key', 'value'],
			url: '/api/app/twitter/settings/set'	
		},
		{
			name: 'twitter_user_show',
			params: ['username'],
			url: '/api/app/twitter/users/show'
		},
		{
			name: 'twitter_friends',
			params: ['username', 'page'],
			url: '/api/app/twitter/friends'
		},
		{
			name: 'twitter_timeline',
			params: ['username', 'since_id', 'max_id', 'count', 'page'],
			url: '/api/app/twitter/statuses/timeline'
		},
		{
			name: 'twitter_followers',
			params: ['username', 'page'],
			url: '/api/app/twitter/followers'
		},
		{
			name: 'twitter_follow',
			params: ['screen_name'],
			url: '/api/app/twitter/follow'
		},
		{
			name: 'twitter_unfollow',
			params: ['screen_name'],
			url: '/api/app/twitter/unfollow'
		},
		{
			name: 'twitter_block',
			params: ['screen_name'],
			url: '/api/app/twitter/block'
		},
		{
			name: 'twitter_post_text',
			params: ['body', 'post_id'],
			url: '/api/app/twitter/text/item_create'
		},
		{
			name: 'twitter_post_link',
			params: ['body', 'url', 'post_id'],
			url: '/api/app/twitter/link/item_create'
		},
		{
			name: 'twitter_post_photo',
			params: ['body', 'photo', 'post_id'],
			url: '/api/app/twitter/photo/item_create',
			multipart: true
		},
		{
			name: 'twitter_item_delete',
			params: ['id'],
			url: '/api/app/twitter/item/delete'
		},
		{
			name: 'twitter_dm_inbox',
			url: '/api/app/twitter/dm/inbox'	
		},
		{
			name: 'twitter_dm_all',
			url: '/api/app/twitter/dm/all'	
		},
		{
			name: 'twitter_dm_delete',
			params: ['id'],
			url: '/api/app/twitter/dm/delete'	
		},
		{
			name: 'twitter_dm_send',
			params: ['recipient', 'message'],
			url: '/api/app/twitter/dm/send'	
		},
		{
			name: 'twitter_search',
			params: ['q', 'page'],
			url: '/api/app/twitter/search'	
		},
		{
			name: 'twitter_searches_saved',
			url: '/api/app/twitter/searches/saved'	
		},
		{
			name: 'twitter_searches_create',
			params: ['query'],
			url: '/api/app/twitter/searches/create'	
		},
		{
			name: 'twitter_searches_delete',
			params: ['id'],
			url: '/api/app/twitter/search'	
		},
		{
			name: 'twitter_trends',
			url: '/api/app/twitter/trends'	
		},
		{
			name: 'twitter_favorites',
			url: '/api/app/twitter/favorites'	
		},
		{
			name: 'twitter_favorites_create',
			params: ['id', 'element_id'],
			url: '/api/app/twitter/favorites/create'	
		},
		{
			name: 'twitter_favorites_delete',
			params: ['id', 'element_id'],
			url: '/api/app/twitter/favorites/delete'	
		},
		{
			name: 'twitter_lists',
			params: ['screen_name'],
			url: '/api/app/twitter/lists'	
		},
		{
			name: 'twitter_lists_members',
			url: '/api/app/twitter/lists/members'	
		},
		{
			name: 'twitter_lists_create',
			params: ['name', 'mode', 'description'],
			url: '/api/app/twitter/lists/create'	
		},
		{
			name: 'twitter_lists_delete',
			params: ['list_id'],
			url: '/api/app/twitter/lists/delete'	
		},
		{
			name: 'twitter_lists_statuses',
			params: ['screen_name', 'list_id', 'cursor', 'page'],
			url: '/api/app/twitter/lists/statuses'	
		},
		{
			name: 'twitter_lists_add_user',
			params: ['list_id', 'id'],
			url: '/api/app/twitter/lists/add_user'	
		},
		{
			name: 'twitter_lists_get_users',
			params: ['screen_name', 'list_id'],
			url: '/api/app/twitter/lists/get_users'	
		},
		{
			name: 'twitter_lists_delete_user',
			params: ['list_id', 'id'],
			url: '/api/app/twitter/lists/delete_user'	
		},
		{
			name: 'twitter_lists_subscribe',
			params: ['screen_name', 'list_id'],
			url: '/api/app/twitter/lists/subscribe'	
		},
		{
			name: 'twitter_lists_subscribers',
			params: ['screen_name', 'list_id'],
			url: '/api/app/twitter/lists/subscribers'	
		},
		{
			name: 'twitter_lists_unsubscribe',
			params: ['screen_name', 'list_id'],
			url: '/api/app/twitter/lists/unsubscribe'	
		},
		{
			name: 'twitter_lists_subscriptions',
			params: ['screen_name'],
			url: '/api/app/twitter/lists/subscriptions'	
		},
		{
			name: 'twitter_lists_memberships',
			params: ['screen_name'],
			url: '/api/app/twitter/lists/memberships'	
		},
	]
});Twitter.implement({
	setupNav: function(){
	
		this.navAdd(new Nav({
			iconOptions: {
				iconName: 'updates'
			},
			displayName: 'Home',
			name: 'updates',
			onClick: this.fireEvent.bind(this, ['viewSwitch', this.appId, 'updates', 'self_menu'])
		}));
		

		this.navAdd(new Nav({
			iconOptions: {
				iconName: 'at'
			},
			displayName: 'Mentions',
			name: 'mentions'
		}));
		
		//add Menu
		var selfMenu = new twitterSelfMenu({user: this.getPrivateUser(), isDefault: true});
		this.menuAdd('self_menu', selfMenu);
		
		var content = new Content({isDefault: true});
		this.contentAdd('updates', content);
		
	}
	
	
});var User = new Class({
	Extends: AppInstance,
	
	EventHandlers: [
	                'userInfoLoaded',
	                'userSwitched'
	                ],
	
	parseOptions: function(options){
		this.user = options.user;
		this.displayName = this.user.fullname;
		this.appId = 'user_' + this.user.username;
		this.iconOptions = {user: this.user};
	},
	
	userSwitched: function(){
		this.stop();
	},
	
	onStart: function(){
		//setup and create navs
		this.setupNav();
	},
	
	onStop: function(){
		
	}
	
	
});User.implement({
	requests: [
		{
			name: 'info_load',
			params: ['username'],
			url: 'api/user/info/load'
		}
	]
});User.implement({
	setupNav: function(){
		
		this.hasRooms = false;
		
		Logger().log('home setup');
		this.navAdd(new Nav({
			iconOptions: {
				iconName: 'updates'
			},
			displayName: 'Updates',
			name: 'updates',
			onClick: this.fireEvent.bind(this, ['viewSwitch', this.appId, 'updates', 'user_menu'])
		}));
		
		this.navAdd(new Nav({
			iconOptions: {
				iconName: 'star'
			},
			displayName: 'Favorites',
			name: 'likes',
			onClick: this.fireEvent.bind(this, ['viewSwitch', this.appId, 'likes', 'likes_menu'])
		}));
		
		//add Menu
		var userMenu = new UserMenu({user: this.user, isDefault: true});
		this.menuAdd('user_menu', userMenu);
		
		var content = new UserContent({isDefault: true, user: this.user});
		this.contentAdd('updates', content);
		
		//likes content and menu
		var likesMenu = new LikesMenu({user: this.user});
		this.menuAdd('likes_menu', likesMenu);
		
		var likesContent = new LikesContent({user: this.user});
		this.contentAdd('likes', likesContent);
	},
	
	
	roomAdd: function(room){
		
		if (!this.hasRooms)
		{
			this.navAdd(new Nav({
				iconOptions: {
					iconName: 'rooms'
				},
				hasSubnavs: true,
				displayName: 'Rooms',
				name: 'rooms'
			}));
			this.hasRooms = true;
		}
		
		var nav = new Nav({
			iconOptions: {
			iconName: 'rooms'
			},
			displayName: room.room_name,
			name: 'room_' + room.username,
			parentName: 'rooms',
			onClick: this.fireEvent.bind(this, ['showRoom', room])
		});
		
		this.navAdd(nav);
		
	},
	
	userInfoLoaded: function(data){
		if (!$defined(data.user) || data.user.username != this.user.username)
			return;
		
		data.rooms.each(function(room){
			room = UserUtility.processRoom(room);
			this.roomAdd(room);
		}, this);
	}
	
	
	
	
});var UserConnectionMenuSection = new Class({
	Extends: Base,
	
	EventHandlers: [
		'contactAdded',
		'contactDeleted',
		'contactRequestAdded',
		'contactRequestDeleted',
		'contactRequestOutAdded',
		'contactRequestOutDeleted',
		'streamSubscriptionAdded',
		'streamSubscriptionDeleted'
		],
	
	init: function(options){
	
		this.user = options.user;
		
		this.subscribed = false;
		
		this.isSelf = this.user.user_id == this.getPrivateUser().user_id;
		
		this.createSection();
	},
	
	createSection: function(){
		this.wrapper = new Element('div');
		
		this.generateSection();
	},
	
	contactAdded: function(user){
		if (this.user.username != user.username)
			return;
		
		this.generateSection();
	},
	
	contactDeleted: function(username){
		if (this.user.username != username)
			return;
		
		this.generateSection();
	},
	
	contactRequestAdded: function(user){
		if (this.user.username != user.username)
			return;
		
		this.generateSection();
	},
	
	contactRequestDeleted: function(username){
		if (this.user.username != username)
			return;
		
		this.generateSection();
	},
	
	contactRequestOutAdded: function(user){
		if (this.user.username != user.username)
			return;
		
		this.generateSection();
	},
	
	contactRequestOutDeleted: function(username){
		if (this.user.username != username)
			return;
		
		this.generateSection();
	},
	
	generateSection: function(){
		
		this.wrapper.empty();
		if (this.isSelf)
		{
			new Element('div', {'class': 'text_section light centered text11', 'text': 'This is you'}).inject(this.wrapper);
			
		}
		else
		{
			
			this.generateSubscriptionSection();
			
			
			var connectionState = this.getConnectionState(this.user.username);
			
			switch(connectionState)
			{
			case 0:
				this.createNotConnectedSection();
				break;
			case 1:
				this.createConnectedSection();
				break;
			case 2:
				this.createRequestSection();
				break;
			case 3:
				this.createRequestOutSection();
				break;
			}
		
			
		}
	},
	
	setSubscription: function(subscribed){
		this.subscribed = subscribed;
		
		this.generateSection();
	},
	
	generateSubscriptionSection: function(){
		this.subscription = new Toggle(this.subscribed);
		
		$(this.subscription).addEvent('click', this.subscriptionToggle.bind(this));
		
		new Element('div', {'class': 'toggle_section'}).adopt(
			new Element('div', {'class': 'text light', 'text': 'Subscribe:'}),
			$(this.subscription)
		).inject(this.wrapper);
		
	},
	
	subscriptionToggle: function(){
		if (this.subscribed)
			this.streamUnsubscribe();
		else
			this.streamSubscribe();
	},
	
	streamUnsubscribe: function(){
		this.subscription.off();
		this.fireEvent('userUnsubscribe', this.user.username);
	},
	
	streamSubscribe: function(){
		this.subscription.on();
		this.call('contacts', 'user_subscribe', {username: this.user.username}, null, this.streamSubscribeFail.bind(this));
	},
	
	streamSubscribeFail: function(status){
		if (status.code == 2002)
		{
			this.subscription.off();
			
			var name = 'subscription';
			var title = 'Error';
			var message = status.message;

			this.fireEvent('showAlert', name, title, message);
		}
	},
	
	streamSubscriptionAdded: function(data){
		if (!$defined(data.user) || data.user.username != this.user.username)
			return;
	
		this.subscribed = true;
		this.generateSection();
	},
	
	streamSubscriptionDeleted: function(data){
		if (!$defined(data.username) || data.username != this.user.username)
			return;
		
		this.subscribed = false;
		this.generateSection();
	},
	createNotConnectedSection: function(){
		var button = new ButtonMedium({
			displayName: 'Add Contact',
			className: 'dark',
			action: 'check'
		});
		
		var name = 'contactRequest_' + this.user.username;
		var title = 'Add ' + this.user.fullname + ' as a Contact';
		var message = 'Send contact request to ' + this.user.fullname + '?';
		var func = this.sendContactRequest.bind(this);
		$(button).addEvent('click', pipio.dispatchEvent.bind(pipio, ['showConfirmation', name, title, message, func]));
		
		new Element('div', {'class': 'button_section'}).adopt(
			$(button)
		).inject(this.wrapper);
	},
	
	createConnectedSection: function(){
		new Element('div', {'class': 'text_section light centered text11', 'text': TextUtility.unescape(this.user.first_name) + ' is your contact'}).inject(this.wrapper);
	},
	
	createRequestOutSection : function(){
		new Element('div', {'class': 'text_section light centered text11', 'text': 'Contact request pending'}).inject(this.wrapper);
	},
	
	createRequestSection: function(){
		var button = new ButtonMedium({
			displayName: 'Accept Request',
			className: 'dark',
			action: 'check'
		});
		
		$(button).addEvent('click', this.fireEvent.bind(this, ['connectionRequestAccept', this.user.username]));
		
		new Element('div', {'class': 'button_section'}).adopt(
			$(button)
		).inject(this.wrapper);
	},
	
	toElement: function(){
		
		return this.wrapper;
	},
	
	sendContactRequest: function(){
		this.fireEvent('connectionRequestCreate', this.user.username);
	}
	
});var UserContent = new Class({
	Extends: Content,
	
	EventHandlers: [
	                'streamItemReceived',
	                'userStreamItemDeleted'
	                ],
	
	onBeforeInit: function(options){
		this.user = options.user;
	
	},

	onInit: function(){
		//status box
		this.statusBox = new StatusBox(this.user);
		$(this.statusBox).inject(this.content);
	
		//create loader
		this.loader = new StreamLoader({
			createElementFunc: StreamItemUtility.createUserStreamItem,
			emptyEl: new Element('div', {'class': 'post empty', 'text': TextUtility.unescape(this.user.fullname) + ' has no updates'}),
			errorEl: new Element('div', {'class': 'post empty', 'text': TextUtility.unescape(this.user.fullname) + '\'s stream is private'})
		});
		$(this.loader).inject(this.content);
		
	
	},
	
	onShow: function(first){
		if (first)
			this.loadMore();
	},
	
	onHide: function(){
		
	},
	
	streamItemReceived: function(data){
		if ($defined(data.item) && data.item.source.username == this.user.username && data.item.is_public == 1)
			this.loader.process(data.item);
	},
	
	userStreamItemDeleted: function(data){
		if (!$defined(data.username) || data.username != this.room.username)
			return;
		
		this.loader.remove(data.item_id);
	},
	
	loadMore: function(){
		var params = {
				username: this.user.username,
				date_created: this.loader.oldestTimestamp,
				item_id: this.loader.oldestId
		};
		
		if (this.isLoggedIn())
			this.call('home', 'stream_user_load', params, this.loadMoreSuccess.bind(this), this.loadMoreFail.bind(this));
		else
			this.call('home', 'public_stream_user_load', params, this.loadMoreSuccess.bind(this), this.loadMoreFail.bind(this));

	},
	
	loadMoreSuccess: function(data){
		if (!$defined(data.items) || data.items.length == 0)
		{
			this.atBottom = true;
			return;
		}
		
		data.items.each(function(item){
			this.loader.process(item);
		}, this);
			
		this.bottomFuncCalled = false;
	},
	
	loadMoreFail: function(status){
		if (status.code == 2002)
			this.loader.showError();
	},
	
	bottomFunc: function(){
		this.loadMore();
	}
});var UserMenu = new Class({
	Extends: Menu,
	
	EventHandlers: [
	                'userAboutUpdated',
	                'userSubscriberAdded',
	                'userSubscriberDeleted',
	                'userSubscriptionAdded',
	                'userSubscriptionDeleted'
	],
	
	onBeforeInit: function(options){
		this.user = options.user;
		options.displayName = this.user.fullname;
		return options;
	},
	
	onInit: function(){
	
		this.aboutText = new Element('span');
		this.aboutSection = new Element('div', {'class': 'text_section light1 text11'}).adopt(
			new Element('span', {'class': 'bold', 'text': 'Bio: '}),
			this.aboutText
		).inject(this.menu);
		
		this.urlText = new Element('a', {'target': '_blank'});
		this.urlSection = new Element('div', {'class': 'text_section light1 text11'}).adopt(
			new Element('span', {'class': 'bold', 'text': 'Web: '}),
			this.urlText	
		).inject(this.menu);
		
		this.map = new UserMapMenuSection({user: this.user});
		$(this.map).inject(this.menu);
		
		if (this.isLoggedIn())
		{
			this.connect = new UserConnectionMenuSection({user: this.user});
			$(this.connect).inject(this.menu);
		
		}
		
		new Element('div', {'class': 'nav_seperator'}).inject(this.menu);
		
		
		this.subscriptions = new SubscriptionsNav({user: this.user});
		$(this.subscriptions).inject(this.menu);
		//subscribers
		this.subscribers = new SubscribersNav({user: this.user});
		$(this.subscribers).inject(this.menu);
		
		
		this.updateUrl(this.user.about.url);
		this.updateAbout(this.user.about.about);
	
	},
	
	onShow: function(first){
		if (first)
			this.userInfoLoad();
	},
	
	updateAbout: function(about){
		
		if (about == '')
			DomUtility.hide(this.aboutSection);
		else
		{
			this.aboutText.set('text', TextUtility.unescape(about));
			DomUtility.show(this.aboutSection);
		}
		
	},
	
	updateUrl: function(url){
		
		if (url == '')
			DomUtility.hide(this.urlSection);
		else
		{
			this.urlText.set('text', TextUtility.unescape(url));
			this.urlText.set('href', url);
			DomUtility.show(this.urlSection);
		}
		
	},
	
	userAboutUpdated: function(data){
		if (!$defined(data.username) || data.username != this.user.username)
			return;
		
		var about = UserUtility.processAbout(data.about);
		
		this.updateAbout(about.about);
		this.updateUrl(about.url);
		
	},
	
	userInfoLoad: function(){
		if (this.isLoggedIn())
			this.call('user', 'info_load', {username: this.user.username}, this.userInfoLoadSuccess.bind(this), null);
		else
			this.call('home', 'public_user_info_load', {username: this.user.username}, this.userInfoLoadSuccess.bind(this), null);
	},
	
	userInfoLoadSuccess: function(data){
		if (!$defined(data.user))
			return;
			
		this.fireEvent('userInfoLoaded', data);
		//user valid, process
		Logger().log('processing user');
		var user = UserUtility.processUser(data.user);
		
		this.subscribers.addUsers(data.subscribers);
		this.subscriptions.addUsers(data.subscriptions);
		
		var subscribed = (data.subscription_id != 0);
		
		this.connect.setSubscription(subscribed);
	},
	
	userSubscriberAdded: function(data){
		if (!$defined(data.source_username) || !$defined(data.user) || data.source_username != this.user.username)
			return;
		
		this.subscribers.addUsers(data.user);
	},
	
	userSubscriberDeleted: function(data){
		if (!$defined(data.source_username) || !$defined(data.username) || data.source_username != this.user.username)
			return;
		
		this.subscribers.removeUser(data.username);
	},
	
	userSubscriptionAdded: function(data){
		if (!$defined(data.subscriber) || !$defined(data.user) || data.subscriber != this.user.username)
			return;
		
		this.subscriptions.addUsers(data.user);
	},
	
	userSubscriptionDeleted: function(data){
		if (!$defined(data.subscriber) || !$defined(data.username) || data.subscriber != this.user.username)
			return;
		
		this.subscriptions.removeUser(data.username);
	}
});//core Pipio class
var Pipio = new Class({
	Implements: Events,
	
	initialize: function(){
		//this.requests = new Pipio.requests();
		//init api calling
		this.initApi();	
		this.initVideo();
		this.registerApis();
		this.eventHandlers = new Hash();

		//page title
		this.pageTitle = 'Pip.io';
		
		
		this.loggedIn = false;
		
		//window.addEvent('resize', this.windowResized.bind(this));
		window.addEvent('domready', this.initCore.bind(this));
		
		//other events
		this.registerHandler('userDataInit', this.userDataInit.bind(this));
		this.registerHandler('videoChatEnable', this.videoChatEnable.bind(this));
		this.registerHandler('videoInUse', this.videoChatInUse.bind(this));
		this.registerHandler('startApp', this.startApp.bind(this));
		this.registerHandler('startAppInstance', this.startAppInstance.bind(this));
		
		//this.registerHandler('pingReceived', this.pingReceived.bind(this));
		///this.registerHandler('onHistoryChange', this.processHistory.bind(this));
		//switch to the right app
	},
	
	//initializes the core components, this is fired when dom is ready to go
	initCore: function(){
		
		//this.router = new Pipio.router('router', Pipio.router, this);
		
		this.xmpp = new Xmpp();
		this.initConnections();
		this.initConnectionEventHandlers();
		this.initModules();
		this.initApps();
		this.setSessionTimestamp();
		
		
		//initial load parses whether user is logged in from the generated user_data
		this.userDataInit();
		this.checkCookie();
		
	},
	
	checkCookie: function(){
		var destination = DataUtility.getCookie('destination');
		if (destination != 'room' && destination != 'user')
			return;
		
		var userval = DataUtility.getCookie('user');
		var user = JSON.decode(userval);
		if ($defined(user))
		{
			if (destination == 'user')
			{
				user = UserUtility.processUser(user);
				this.dispatchEvent('showUser', user);
			}
			else 
			{
				user = UserUtility.processRoom(user);
				this.dispatchEvent('showRoom', user);
			}
		}
		
	},
	
	initModules: function(){
		this.modules = new Pipio.modules(this);
	},
	
	initApps: function(){
		this.apps = new Pipio.apps(this);
	},
	
	//this timestamp is when the current session started
	setSessionTimestamp: function(){
		this.sessionTimestamp = new Date();
	},
	
	checkTimestamp: function(time){
		return time > this.sessionTimestamp.getTime()/1000;
	},
	
	userDataInit: function(data){
		if ($defined(data))
			user_data = data;
		
		if (user_data.logged_in == 1)
			this.userLoggedIn();
		else
			this.userLoggedOut();
	},
	
	userLoggedIn: function(){
		this.initLoggedInConnections();
		
		this.currentUser = UserUtility.processUser(user_data.user);
		this.loggedIn = true; 
		
		this.currentLocation = UserUtility.processLocation(this.currentUser.username, user_data.location);
		this.dispatchEvent('userSwitched');
	},
	
	userLoggedOut: function(){
		this.initLoggedOutConnections();
	
		this.currentUser = {
			user_id: 0
		};
		this.currentLocation = null;
		this.loggedIn = false;
		
		this.dispatchEvent('userSwitched');
	},
	
	
	isConnected: function(username){
		return this.connections.connsByUsername.has(username);
	},
	
	isRequestPending: function (user_id){
		return this.connections.requestsById.has(user_id) || this.connections.requestsOutById.has(user_id);
	},
	
	isLoggedIn: function(){
		return this.loggedIn;
	},
	
	dispatchEvent: function(){
		var eventName = arguments[0];
		var args = [];
		
		for(var i = 1; i < arguments.length; i++)
		{
			args.push(arguments[i]);
		}
		Logger().log('fired event ' + eventName + ' - ' + args.join(','));
		// mpmetrics.track('eventFire', {'name': eventName);
		this.fireEvent(eventName, args);
	},
	
	registerHandler: function(eventName, func){
		if (this.eventHandlers.has(eventName))
		{
			this.eventHandlers.get(eventName).push(func);
			this.addEvent(eventName, func);		
		}
		else
		{
			this.eventHandlers.set(eventName, [func]);			
			this.addEvent(eventName, func);
		}	
	},
	
	
	getSession: function(){
		return this.xmpp.clientName;
	}
});Pipio.implement({
	
	initApi: function(){
		this.apiMethods = new Hash();
	},
	
	registerCall: function(app, call){
		var apiKey = app + '_' + call.name;
		this.apiMethods.set(apiKey, call);
	},
	
	//New method for calling api
	call: function(app, name, params, callbackSuccess, callbackFail, form){
		//retrieve api def
		var apiKey = app + '_' + name;
		if (!this.apiMethods.has(apiKey))
		{
			Logger().log('api call not found!');
			return;
		}
		
		var call = this.apiMethods.get(apiKey);
	
		var paramList = $defined(call.params)? call.params: [];
		var url = call.url;
		var method = $defined(call.method)? call.method: 'post';
		
		var multipart = $defined(form);
		
		Logger().log('multipart ' + multipart);
		
		//parse the parameters
		var parsedParams = new Hash();
		paramList.each(function(paramName){
			parsedParams.set(paramName, params[paramName]);
		});
		
		if (!multipart)
		{
	
			var req = new Request({
				method: method,
				url: url,
				onFailure: function(){
					var res = {
						status: {
							code: 6000,
							message: 'request failed'
						}
					};
					
					if ($defined(callbackFail))
						callbackFail(res.status);
					//if ($defined(additionalFailFunc))
					//	additionalFailFunc(res.status);
				},
				onSuccess: function(response){
					var res = response;
					if (res.status == null)
					{
						//try to process response
						//if garbage, manually set error
						try {
							res = JSON.decode(response);
						}
						catch(err){
							res = {
								status: {
									code: 6000,
									message: 'request failed'
								}
							}
							
							if ($defined(callbackFail))
								callbackFail(res.status);
						}						
					}	
						
					if ($defined(res) && $defined(res.status) && res.status.code == 0)//success
					{
						if ($defined(callbackSuccess))
						{
							if ($defined(res.data))							
								callbackSuccess(res.data);
							else
								callbackSuccess();
						}
						
					}	
					else //failed
					{
						if (!$defined(res) || !$defined(res.status))
						{
							res = {
								status: {
									code: 6000,
									message: 'request failed'
								}
							};
						}
						if ($defined(callbackFail))
							callbackFail(res.status);
					}
				}
			});
			
			if (method == 'post')
				req.send(parsedParams.toQueryString());
			else
				req.send();
		}
		else
		{
			var iframeId = 'postIframe' + $random(10000, 99999);
			form.set('target', iframeId);
			form.set('action', url);
			form.set('method', 'post');
			form.set('enctype', 'multipart/form-data');
			
			//add other params
			parsedParams.each(function(val, name){
				new Element('input', {'type': 'hidden', 'name': name, 'value': val}).inject(form);
			}, this);
			
			var iframe = new IFrame({
				'id': iframeId,
				'events': {
					load: function(){
						var response = window.frames[iframeId].document.body.innerHTML;
						
						if (response == '')
						{
							return;
						}
						
						Logger().log(response);
						
						var res = JSON.decode(response);
						
						if ($defined(res) && $defined(res.status) && res.status.code == 0)//success
						{
							if ($defined(callbackSuccess))
							{
								if ($defined(res.data))							
									callbackSuccess(res.data);
								else
									callbackSuccess();
							}
						}	
						else //failed
						{
							if (!$defined(res) || !$defined(res.status))
								res = {
									status: {
										code: 6000,
										message: 'request failed'
									}
								};
							
							if ($defined(callbackSuccess))
								callbackFail(res.status);
						}
					}
				}
			});
			
			iframe.inject('hidden');
				
			form.submit();
		}
	}
	
	
});//apps controller
Pipio.implement({
		
	moduleList: [
		{
			name: 'ui',
			classRef: UI
		},
		{
			name: 'login',
			classRef: Login
		},
		{
			name: 'background',
			classRef: Background
		},
		{
			name: 'chat',
			classRef: Chat
		},
		{
			name: 'videochat',
			classRef: VideoChat
		},
		{
			name: 'notifications',
			classRef: Notifications
		},
		{
			name: 'sharebox',
			classRef: ShareBox
		},
		{
			name: 'invite',
			classRef: Invite
		}
	],
	
	initModules: function(){
		
		this.modules = new Hash();
		this.moduleList.each(function(module){
			this.initModule(module.name, module.classRef);
		}, this);
	},
	
	initModule: function(name, classRef){
		var o = new classRef();
		this.modules.set(name, o);
	}
	
});Pipio.implement({
	initVideo: function(){
		this.videoEnabled = false;
		this.videoInUse = false;
	},
	
	//this is called by the videochat swf to set whether or not video is enabled
	videoChatEnable: function(enabled){
		this.videoEnabled = enabled;
	},
	
	videoChatInUse: function(inUse){
		if (!this.videoEnabled)
			return;
		
		this.videoInUse = inUse;
	}
	
	
});//this handles the xmpp connection
var Xmpp = new Class({
	Extends: Base,
	
	EventHandlers: [
	                'userSwitched',
	                'sendIM',
	                'sendTyping',
	                'sendVideoChatInvite',
	                'sendVideoChatAccept'
	],
	
	init: function(){
		
		this.newSession();
		this.domain = 'pip.io';
		
		this.userPresences = new Hash(); //this tracks count of contact sessions that are online.  For multiple login.
		
		//other servces
		this.roomsDomain = 'rooms.pip.io';
		this.multicastDomain = 'multicast.pip.io';
		this.pubsubDomain = 'pubsub.pip.io';
		
		var connArgs = {
			httpbase:'/pipio-tubes',
			timerval: 60*20
		};
		
		this.conn = new JSJaCHttpBindingConnection(connArgs);
		this.setupListeners();
		
		//TODO: reimplement ping
		//setup ping 
		//var now = new Date();
		//this.lastPing = now.getTime();
		//this.pingTimer = 0;
		//this.reconnectThreshold = 90*1000;
		
		this.isConnecting = false;
	},
	
	userSwitched: function(){
		if (this.isLoggedIn())
			this.userLoggedIn();
		else
			this.userLoggedOut();
	},
	
	userLoggedIn: function(){
		this.newSession();
		this.fireEvent('xmppConnecting');
		this.connect.delay(1000, this);
	},
	
	userLoggedOut: function(){
		this.disconnect();
	},
	
	newSession: function(){
		this.sessionId = $random(1000, 9999);
		this.clientName = 'pipio' + this.sessionId;
	},
	
	//initiate xmpp connection, first fetch xmpp token
	connect: function(){
		if (this.isConnecting)
			return;
		
		if ($defined(this.connectTimer))
			$clear(this.connectTimer);
			
		this.isConnecting = true;
		this.call('pipio', 'token_get', {}, this.tokenGetSuccess.bind(this) , this.tokenGetFail.bind(this));
	
	},
	
	disconnect: function(){
		this.isConnecting = false;
		this.updateSelfPresence('', false);
  		this.conn.disconnect();
  		this.fireEvent('xmppDisconnected');
	},
	
	//token success, now acutally connect
	tokenGetSuccess: function(data){
		Logger().log('xmpp token success - ' + data.token + ', connecting...');
		this.username = this.getPrivateUser().username;
		this.password = data.token;
		
		this.isConnecting = false;
		
		var args = {
			domain: this.domain,
		    username: this.username,
		    resource: this.clientName,
		    pass: this.password,
		    register: false
		};
		
	    this.conn.connect(args);
	},
	
	//token failed, wait 10 sec, then retry
	tokenGetFail: function(){
		//token failed, retry
		this.isConnecting = false;
		this.connectTimer = this.connect.delay(10 * 1000, this);
	},
	
	//sends a presence packet
	updateSelfPresence: function(show, online){
		//show can be '' for avaiable, 'away', or 'chat' for available with video
		//TODO: switch to using xmpp caps for capabilites
		var pres = new JSJaCPresence();
		
		pres.setShow(show);
		
		var video = this.videoEnabled();
		
		if (video)
			pres.setPriority(10);
		else
			pres.setPriority(5);
		
		if (online)
			pres.setType('');
		else
			pres.setType('unavailable');
		
		//add caps for whether or not client has video capability
		var videoEnabled = (video) ? 1 : 0;
	
		var pip = pres.getDoc().createElementNS('http://pip.io/videochat','pip');
		pip.setAttribute('video', videoEnabled);
		pres.appendNode(pip);
		
		this.conn.send(pres);
	},
	
	userPresenceReceived: function(username, show, online, session, video){
		
		Logger().log(username + ' ' + show + ' - ' + session);
		var trueOnline = online;
		
		if (online)
		{	//add a new session for that user
			if (!this.userPresences.has(username))
				this.userPresences.set(username, new Hash());
			
			this.userPresences.get(username).set(session, video);
		}
		else
		{
			if (this.userPresences.has(username))
				this.userPresences.get(username).erase(session);
			
			if (this.userPresences.get(username).getLength() == 0)
				this.userPresences.erase(username);
		}
		
		//now check if user has active session
		if (this.userPresences.has(username))
			trueOnline = true;
		else
			trueOnline = false;
		
		Logger().log('firing ' + username + ' online-' + trueOnline + ' show -' + show);
		//handle multi sessions by tracking each session Id
		this.fireEvent('userPresenceReceived', username, show, trueOnline);
		
		//check for video capability
		if (trueOnline)
		{	
			var videoEnabled = false;
			this.userPresences.get(username).each(function(videoExists){
				if (videoExists)
						videoEnabled = true;
			});
			
			this.fireEvent('userVideoEnabled', username, videoEnabled);
		}
	},
	
	selfPresenceReceived: function(show, online){
		this.fireEvent('selfPresenceReceived', show, online);
	},
	
	sendVideoChatInvite: function(user){
		if (!$defined(user))
			return;
		
		if (!this.videoEnabled())
			return;
		
		var stratusId = sim.GetNearID();
		var userJID = new JSJaCJID(this.getJID(user));
	    var aMsg = new JSJaCMessage();
	    aMsg.setTo(userJID);
	    aMsg.setBody(stratusId);
		aMsg.setType('videoChat');
	    var success = this.conn.send(aMsg);
	    
	    this.fireEvent('videoInUse', true);
	    this.fireEvent('selfPresenceUpdate');
	},
	
	sendVideoChatAccept: function(user){
		if (!$defined(user))
			return;
		
		if (!this.videoEnabled())
			return;
		
		var stratusId = sim.GetNearID();
		var userJID = new JSJaCJID(this.getJID(user));
	    var aMsg = new JSJaCMessage();
	    aMsg.setTo(userJID);
	    aMsg.setBody(stratusId);
		aMsg.setType('videoChatAccept');
	    var success = this.conn.send(aMsg);
	    
	    this.fireEvent('videoInUse', true);
	    this.fireEvent('selfPresenceUpdate');
	},
	
	sendIM: function(user, msg){
		if (!$defined(user) || !$defined(msg))
			return;
			
		var userJID = new JSJaCJID(this.getJID(user));
	    var aMsg = new JSJaCMessage();
	    aMsg.setTo(userJID);
	    aMsg.setBody(msg);
		aMsg.setType('chat');
	    var success = this.conn.send(aMsg);
		if (success)
		{
			var msg = this.buildMessage(user, true, msg, new Date());
			this.fireEvent('chatMsgReceived', msg);
		}
	},
	
	sendTyping: function(user){
		if (!$defined(user))
			return;
		
		var userJID = new JSJaCJID(this.getJID(user));
	    var aMsg = new JSJaCMessage();
	    aMsg.setTo(userJID);
		aMsg.setType('typing');
	    var success = this.conn.send(aMsg);
	},
	
	buildMessage: function(target, outbound, msg, date){
		return {
			target: target,
			outbound: outbound,
			msg: msg,
			timestamp: date
		};
	},
	
	//get the body node content
	getBody: function(packet)  
   	{  
   		var bodyNode = packet.getChild('body');
   		if(!bodyNode) return '';  
   		if(typeof(bodyNode.textContent) != "undefined") return bodyNode.textContent;  
   		return bodyNode.firstChild.nodeValue;  
   	},
	
	//get the item node content
	getItem: function(packet)  
   	{  
   		var itemNode = packet.getChild('item');
   		if(!itemNode) return '';  
   		if(typeof(itemNode.textContent) != "undefined") return itemNode.textContent;  
   		return itemNode.firstChild.nodeValue;  
   	},
   	
	handleMessage: function(packet) {
		if (packet.getType() == 'chat')
		{
			//this is a IM message
			
			//parse timestamp
			var timestamp = new Date();
			
			var x = packet.getChild('x');
			if ($defined(x))
			{
				timestamp = DateUtility.convertFromGMT(Date.parse(x.getAttribute('stamp')));
			}
			
			var username = packet.getFromJID().getNode();
			var user = this.getContact(username);
			var msg = this.buildMessage(user, false, packet.getBody(), timestamp);
			this.fireEvent('chatMsgReceived', msg);
		}
		else if (packet.getType() == 'videoChat')
		{
			var username = packet.getFromJID().getNode();
			var stratusId = packet.getBody();
			this.fireEvent('videoChatRequestReceived', username, stratusId);
		}
		else if (packet.getType() == 'videoChatAccept')
		{
			var username = packet.getFromJID().getNode();
			var stratusId = packet.getBody();
			this.fireEvent('videoChatRequestAccepted', username, stratusId);
		}
		else if (packet.getType() == 'typing')
		{
			 //event signifying user is typing
			var username = packet.getFromJID().getNode();
			this.fireEvent('chatTypingReceived', username);
		}
		else if (packet.getType() == 'event' && packet.getFromJID().getNode() == 'server')
		{	//standard message from server
			var msg = this.getBody(packet);
			
			var item = JSON.decode(msg);
			//var item = JSON.decode(Base64.decode(msg));
			Logger().log('unicast/multicast received');
			if ($defined(item.eventName) && $defined(item.data))
			{
				this.fireEvent(item.eventName, item.data);
			}
			else
			{
				Logger().log('unknown event captured');
			}
			
		}
		else if (packet.getChild('event') != null)
		{
			Logger().log('broadcast received');
			var msg = this.getItem(packet);
			if (msg != '')
			{
				var item = JSON.decode(msg);
				//var item = JSON.decode(Base64.decode(msg));
				
				if ($defined(item.eventName) && $defined(item.data))
				{
					this.fireEvent(item.eventName, item.data);
				}
				else
				{
					Logger().log('unknown event captured');
				}
			}
		}
	  	
	},
	
	handlePresence: function(packet) {
		//parse packet data
		var jid = packet.getFromJID();
		var username = jid.getNode();
		var session = jid.getResource();
		
		if (jid.getDomain() == this.domain) 
		{
		//only handle user presence from this domain, otherwise ignore
		//TODO:  this will need to be updated for extern transports
			Logger().log(username + ' presence received');
			
			if (!packet.getType()) 
			{
				if (username == this.getPrivateUser().username)
				{
					var show = packet.getShow();
					this.selfPresenceReceived(show, true);
					return;
				}	
					
				var show = packet.getShow();
				var video = false;
				//parse pip node
				var pip = packet.getChild('pip', '*');
				if (pip)
				{
					if (pip.getAttribute('video') == 1)
						video = true;
				}
				//
				//update presence to online
				this.userPresenceReceived(username, show, true, session, video);
				//this.updatePresence(username, true, show, status, session);
			}
			else if (packet.getType() == 'unavailable') 
			{
				var username = jid.getNode();
				if (username == this.getPrivateUser().username)
					return;
				
				var session = jid.getResource();
				//update presence to offline
				//this.updatePresence(username, false, null, null, session);
				this.userPresenceReceived(username, '', false, session, false);
			}
			
		}
		
	},
	
	handleConnected: function(){
		Logger().log('connected');
		this.isConnecting = false;
		this.fireEvent('xmppConnected');
		this.updateSelfPresence('', true, false);
		//attach unload event 
		window.onunload = this.disconnect.bind(this);
		
	},
	
	handleDisconnected: function(){
		Logger().log('disconnected');
		this.isConnecting = false;
		this.fireEvent('xmppDisconnected');
		this.connectTimer = this.connect.delay(3000, this);
	},
	
	handleError: function(e) {
		this.reconnect();
    	Logger().log(e.xml()); 
	},
	
	handleInPacket: function(aJSJaCPacket) {
		Logger().log("in");
		Logger().log("packet:" + aJSJaCPacket.xml());	
	},
	handleOutPacket: function(aJSJaCPacket) {
		Logger().log("out");
		Logger().log("packet:" + aJSJaCPacket.xml());	
	},
	
	setupListeners: function(){
		this.conn.registerHandler('message', this.handleMessage.bind(this));
	    this.conn.registerHandler('presence', this.handlePresence.bind(this));
	    this.conn.registerHandler('onconnect', this.handleConnected.bind(this));
	    this.conn.registerHandler('onerror', this.handleError.bind(this));
	    this.conn.registerHandler('ondisconnect', this.handleDisconnected.bind(this));
		//this.conn.registerHandler('packet_in', this.handleInPacket.bind(this));
		//this.conn.registerHandler('packet_out', this.handleOutPacket.bind(this));
	},
	
	getJID: function(user){
		if ($defined(user))
			return user.username + '@' + this.domain;
		else
			return this.username + '@' + this.domain;
			//return this.username + '@' + this.domain + '/' + this.clientName;
	}
});//apps controller
Pipio.implement({
		
	appList: [
		{
			id: 1,
			name: 'contacts',
			displayName: 'Contacts',
			iconOptions: {
				iconName: 'pipio'
			},
			classRef: Contacts
		},
		{
			id: 2,
			name: 'home',
			displayName: 'Home',
			iconOptions: {
				iconName: 'pipio'
			},
			classRef: Home
		},
		{
			id: 3,
			name: 'appstore',
			displayName: 'Pip.io App Store',
			iconOptions: {
				iconName: 'applications'
			},
			classRef: Appstore
		},
		{
			id: 4,
			name: 'rss',
			displayName: 'News Reader',
			iconOptions: {
				iconName: 'rss'
			},
			classRef: Rss
		},
		{
			id: 5,
			name: 'facebook',
			displayName: 'Facebook',
			iconOptions: {
				iconName: 'facebook'
			},
			classRef: Facebook
		},
		{
			id: 6,
			name: 'twitter',
			displayName: 'Twitter',
			iconOptions: {
				iconName: 'twitter'
			},
			classRef: Twitter
		}
		
		
		/*{
			name: 'weather',
			classRef: Weather
		},
		{
			name: 'buddylist',
			classRef: Buddylist
		},
		{
			name: 'chat',
			classRef: Chat
		},
		{
			name: 'windowAlert',
			classRef: WindowAlert
		},
		{
			name: 'location',
			classRef: Location
		},
		{
			name: 'notifications',
			classRef: Notifications
		}*/
	],
	
	instanceAppList: {
		user: {
			name: 'user',
			classRef: User
		},
		
		room: {
			name: 'room',
			classRef: Room
		}  
	           
	},
	
	initApps: function(){
		
		this.apps = new Hash();
		this.appsById = $H();
		this.appInstances = new Hash();
		this.appList.each(function(app){
			this.initApp(app);
		}, this);
	},
	
	getAppById: function(appId){
		if (this.appsById.has(appId))
			return this.appsById.get(appId);
		else
			return false;
	},
	
	initApp: function(app){
		var o = new app.classRef(app);
		this.apps.set(app.name, o);
		this.appsById.set(app.id, app);
	},
	
	startApp: function(appName, options){
		if (!this.apps.has(appName))
			return;
		
		this.apps.get(appName).start(options);
	},
	
	startAppInstance: function(appName, instanceName, options){
		if (!$defined(this.instanceAppList[appName]))
			return;
		
		var appKey = appName + '_' + instanceName;
		
		if (this.appInstances.has(appKey))
		{
			this.appInstances.get(appKey).start();
			return;
		}
		
		//no instance, start new one
		var app = this.instanceAppList[appName];
		var o = new app.classRef(app, options);
		this.appInstances.set(appKey, o);
		o.start();
	}
	
});//connections handling part 
Pipio.implement({
	initConnectionEventHandlers: function(){
		//attach connection handling events
		this.registerHandler('connectionGroupAdded', this.connectionGroupAdded.bind(this));
		this.registerHandler('connectionGroupDeleted', this.connectionGroupDeleted.bind(this));
		this.registerHandler('connectionGroupMoved', this.connectionGroupMoved.bind(this));
		
		this.registerHandler('connectionAdded', this.connectionAdded.bind(this));
		this.registerHandler('connectionDeleted', this.connectionDeleted.bind(this));
		
		this.registerHandler('connectionRequestAdded', this.connectionRequestAdded.bind(this));
		this.registerHandler('connectionRequestDeleted', this.connectionRequestDeleted.bind(this));
	
		this.registerHandler('connectionRequestOutAdded', this.connectionRequestOutAdded.bind(this));
		this.registerHandler('connectionRequestOutDeleted', this.connectionRequestOutDeleted.bind(this));
		
		this.registerHandler('roomAdded', this.roomAdded.bind(this));
		this.registerHandler('roomDeleted', this.roomDeleted.bind(this));
		this.registerHandler('roomClosed', this.roomDeleted.bind(this));
		this.registerHandler('roomMembershipUpdated', this.roomMembershipUpdated.bind(this));
		this.registerHandler('roomStreamSubscriptionDeleted', this.roomStreamSubscriptionDeleted.bind(this));
		this.registerHandler('roomStreamSubscriptionAdded', this.roomStreamSubscriptionAdded.bind(this));
		
		this.registerHandler('userAboutUpdated', this.userAboutUpdated.bind(this));
		
	},
	
	initConnections: function(){
		this.connsByUsername = $H();
		this.userCache = $H(); //caches all user that get accessed
		this.groups = $H();
		this.requestsByUsername = $H();
		this.requestsOutByUsername = $H();
		this.locationsByUsername = $H();
		//rooms
		this.roomsByUsername = $H();
		this.roomCache = $H();
	},
	
	initLoggedInConnections: function(){
		this.initConnections();
		this.processConnections();
	},
	
	initLoggedOutConnections: function(){
		this.initConnections();
	},
	
	//update user info from events
	userAboutUpdated: function(data){
		if (!$defined(data.username))
			return;
		
		if (this.connsByUsername.has(data.username))
		{
			this.connsByUsername.get(data.username).about = data.about;
		}
		
		if (this.userCache.has(data.username))
		{
			this.userCache.get(data.username).about = data.about;
		}
		
	},
	
	processConnections: function(){
		
		Logger().log('processing user connections');
		
		user_data.groups.each(function(group)
		{
			this.processGroup(group);
		}, this);
		
		//insert the default groups
		var unsorted = {group_id: 0, name: "Unsorted"};
		this.processGroup(unsorted);

		
		//process connections
		user_data.connections.each(function(user){
			this.processConnection(user);	
		}, this);
		
		//process requests
		user_data.connection_requests.each(function(user){
			this.processConnectionRequest(user);
		}, this);
		
		user_data.connection_requests_out.each(function(user){
			this.processConnectionRequestOut(user);
		}, this);
		
		//process rooms
		user_data.rooms.each(function(room){
			this.processRoom(room);
		}, this);
	},
	
	processGroup: function(group){	
		group.users = new Hash();
		this.groups.set(parseInt(group.group_id), group);
		return group;
	},
	
	processRoom: function(room){
		room = UserUtility.processRoom(room);
		
		this.roomsByUsername.set(room.username, room);
		
		return room;
	},
	
	processConnection: function(user){
		user = UserUtility.processUser(user);
		//store in hash
		this.connsByUsername.set(user.username, user);	
		//store in user hash by groupId
		var group = this.getGroup(user.group_id);
		this.addUserToGroup(user, group);
		
		return user;
	},
	
	processConnectionRequest: function(user){
		user = UserUtility.processUser(user);
		
		this.requestsByUsername.set(user.username, user);
		
		return user;
	},
	
	processConnectionRequestOut: function(user){
		
		Logger().log('procesing out request ' + user.username);
		user = UserUtility.processUser(user);
		
		this.requestsOutByUsername.set(user.username, user);
		
		return user;
	},
	
	roomAdded: function(data){
		if (!$defined(data.room))
			return;
		
		var room = this.processRoom(data.room);
		this.dispatchEvent('feedRoomAdded', room);
	},
	
	roomDeleted: function(data){
		if (!$defined(data.username))
			return;
		
		this.roomsByUsername.erase(data.username);
		
		this.roomCache.erase(data.username);
		
		this.dispatchEvent('feedRoomDeleted', data.username);
		
	},
	
	roomMembershipUpdated: function(data){
		if (!$defined(data.username) || !this.roomsByUsername.has(data.username))
			return;
		
		this.roomsByUsername.get(data.username).status = data.status;
	},
	
	roomStreamSubscriptionAdded: function(data){
		if (!$defined(data.room) || !this.roomsByUsername.has(data.room.username))
			return;
		this.roomsByUsername.get(data.room.username).subscribed = 1;
	},
	
	roomStreamSubscriptionDeleted: function(data){
		if (!$defined(data.username) || !this.roomsByUsername.has(data.username))
			return;
		this.roomsByUsername.get(data.username).subscribed = 0;
	},
	
	connectionAdded: function(data){
		if (!$defined(data.user))
			return;
		
		var user = this.processConnection(data.user);
		
		this.dispatchEvent('contactAdded', user);
		
		//delete user requests and requets out
		if (this.requestsByUsername.has(user.username))
		{
			this.requestsByUsername.erase(user.username);
			this.dispatchEvent('contactRequestDeleted', user.username);
		}
		
		if (this.requestsOutByUsername.has(user.username))
		{
			this.requestsOutByUsername.erase(user.username);
		}
			
	},
	
	connectionDeleted: function(data){
		if (!$defined(data.username))
			return;
		
		//delete contact
		var group = this.getGroup(data.group_id);
		
		group.users.erase(data.username);
		
		this.connsByUsername.erase(data.username);
		
		this.dispatchEvent('contactDeleted', data.username, data.group_id);
	},
	
	connectionRequestAdded: function(data){
		if (!$defined(data.user))
			return;
		
		var user = this.processConnectionRequest(data.user);
		
		this.dispatchEvent('contactRequestAdded', user);
	},
	
	connectionRequestDeleted: function(data){
		if (!$defined(data.username))
			return;
		
		this.requestsByUsername.erase(data.username);
		
		this.dispatchEvent('contactRequestDeleted', data.username);
	},
	
	connectionRequestOutAdded: function(data){
		if (!$defined(data.user))
			return;
		
		var user = this.processConnectionRequestOut(data.user);
		
		this.dispatchEvent('contactRequestOutAdded', user);
	},
	
	connectionRequestOutDeleted: function(data){
		if (!$defined(data.username))
			return;
		
		this.requestsOutByUsername.erase(data.username);
		
		this.dispatchEvent('contactRequestOutDeleted', data.username);
	},
	
	connectionGroupAdded: function(data){
		if (!$defined(data.group))
			return;
		
		var group = this.processGroup(data.group);
		
		this.fireEvent('contactGroupAdded', group);
	},
	
	connectionGroupDeleted: function(data){
		if (!$defined(data.group_id) || !this.groups.has(data.group_id))
			return;
		
		var group = this.getGroup(data.group_id);
		
		//move all members to unsorted
		group.users.each(function(user){
			this.connectionGroupMoved({
				username: user.username,
				group_id: 0,
				old_group_id: data.group_id
			});
			
			group.users.erase(user.username);
		}, this);
		
		this.groups.erase(data.group_id);
		
		this.fireEvent('contactGroupDeleted', group);
	},
	
	connectionGroupMoved: function(data){
		if (!$defined(data.group_id) || !$defined(data.username) || !$defined(data.old_group_id))
			return;
		
		var user = this.getContact(data.username);
		
		user.group_id = data.group_id;
		
		this.fireEvent('contactGroupMoved', user);
		
	},
	
	addUserToGroup: function(user, group){
		if (!group.users.has(user.username))
			group.users.set(user.username, user);
	},
	
	getGroup: function(group_id){
		return this.groups.get(parseInt(group_id));	
	},
	
	getContact: function(username){
		return this.connsByUsername.get(username);
	},
	
	getRoom: function(username){
		if (this.roomsByUsername.has(username))
			return this.roomsByUsername.get(username);
		else if (this.roomCache.has(username))
			return this.roomCache.get(username);
		else
			return null;
	},
	
	getContactsByGroup: function(group_id){
		if (!this.groups.has(group_id))
			return;
		
		var group = this.getGroup(group_id);
		return group.users;
			
	},
	
	getUser: function(username){
		if (this.userCache.has(username))
			return this.userCache.get(username);
		else
			return null;
	},
	
	cacheUser: function(user){
		this.userCache.set(user.username, user);
	},
	
	cacheRoom: function(room){
		this.roomCache.set(room.username, room);
	},
	
	cacheLocation: function(username, location){
		this.locationsByUsername.set(username, location);
	}
});Pipio.implement({
	
	registerApis: function(){
		this.requests.each(function(request){
			this.registerCall('pipio', request);
		}, this);
	
	
	},

	requests: [
		{
			name: 'user_login',
			params: ['username', 'password', 'remember_me'],
			url: '/api/auth/user/login'
		},
		{
			name: 'user_logout',
			url: '/api/auth/user/logout'
		},
		{
			name: 'user_register',
			params: ['username', 'password', 'first_name', 'last_name', 'email', 'dob_month', 'dob_year', 'dob_day', 'access_key'],
			url: '/api/auth/user/register'
		},
		{
			name: 'user_password_resetrequest',
			params: ['username'],
			url: '/api/auth/user/password/resetrequest'
		},
		{
			name: 'user_password_reset',
			params: ['token', 'new_password', 'verify_password', 'email'],
			url: '/api/auth/user/password/reset'
		},
		{
			name: 'user_privacy_set',
			params: ['is_public'],
			url: '/api/user/privacy/set'
		},
		{
			name: 'user_location_enabled',
			params: ['location_enabled'],
			url: '/api/user/location/enabled'
		},
		{
			name: 'token_get',
			url: '/api/user/token/get'
		},
		{
			name: 'publish_status',
			url: '/api/pipio/stream/publish/status',
			params: ['body', 'res']
		},
		{
			name: 'publish_roomstatus',
			url: '/api/pipio/stream/publish/roomstatus',
			params: ['username', 'body', 'res']
		},
		{
			name: 'user_profilepic_upload',
			url: '/api/user/profilepic/upload',
			params: []
		},
		
		{
			name: 'room_profilepic_upload',
			url: '/api/pipio/room/profilepic/upload',
			params: ['username']
		},
		{
			name: 'email_block',
			url: '/api/auth/email/block',
			params: ['token', 'email']
		}
		/*
		ping: {
			params: ['app_id', 'version'],
			url: '/api/ping'
		},
		*/
	]
	
});