cm.prototype = {
	components: {},
	forms: {},
	viewer: null, // CM_Model_User
	options: {},
	ready: function() {
		this.window.ready();
		this.date.ready();
	},

	/**
	 * @param string className
	 * @return CM_Component_Abstract|null
	 */
	component: function(className) {
		if (!className) {
			for (componentId in cm.components) {
				if (!cm.components[componentId].getParent()) {
					return cm.components[componentId];	// Root
				}
			}
			cm.error.trigger('Cannot find root component');
		}
		return _.find(this.components, function(component) {
			return component._class == className;
		}) || null;
	},

	/**
	 * @param int min
	 * @param int max
	 * @return int
	 */
	rand: function(min, max) {
		return min + Math.floor(Math.random() * (max - min + 1));
	},

	error: {
		_callbacks: {_all: []},
		/**
		 * @param string msg
		 * @param string type OPTIONAL
		 * @param bool isPublic OPTIONAL
		 */
		trigger: function(msg, type, isPublic) {
			for (var i = 0; i < this._callbacks._all.length; i++) {
				if (false === this._callbacks._all[i](msg, type, isPublic)) {
					return;
				}
			}
			if (this._callbacks[type]) {
				for (var i = 0; i < this._callbacks[type].length; i++) {
					if (false === this._callbacks[type][i](msg, type, isPublic)) {
						return;
					}
				}
			}
			if (isPublic) {
				cm.window.hint(msg);
			} else {
				if (type) {
					msg = type + ': ' + msg;
				}
				cm.window.hint(msg);
				if (window.console && console.error) {
					console.error(msg);
				}
			}
		},
		/**
		 * @param function callback fn(msg, type, isPublic)
		 */
		bind: function(callback) {
			this.bindType('_all', callback);
		},
		/**
		 * @param string type
		 * @param function callback fn(msg, type, isPublic)
		 */
		bindType: function(type, callback) {
			if (!this._callbacks[type]) {
				this._callbacks[type] = [];
			}
			this._callbacks[type].push(callback);
		}
	},

	debug: {
		/**
		 * @param string component OPTIONAL
		 * @param int indentation OPTIONAL
		 */
		componentTree: function(component, indentation) {
			component = component || cm.component();
			indentation = indentation || 0;
			console.log(new Array(indentation + 1).join("  ") + component._class + " (", component.$(), ")");
			_.each(component.getChildren(), function(child) {
				cm.debug.componentTree(child, indentation + 1);
			});
		}
	},

	string: {
		padLeft: function(str, length, char) {
			char = char || ' ';
			string = String(str);
			return new Array(length - string.length + 1).join(char) + string;
		}
	},

	date: {
		ready: function() {
			$.timeago.settings.allowFuture = true;
		},
		/**
		 * @return int Unix-timestamp
		 */
		timestamp: function() {
			return (new Date()).getTime();
		},
		/**
		 * @param Date date
		 * @return string
		 */
		iso8601: function(date) {
			return date.getUTCFullYear() + '-' + cm.string.padLeft(date.getUTCMonth() + 1, 2, '0') + '-' + cm.string.padLeft(date.getUTCDate(), 2, '0') + 'T' + cm.string.padLeft(date.getUTCHours(), 2, '0') + ':' + cm.string.padLeft(date.getUTCMinutes(), 2, '0') + ':' + cm.string.padLeft(date.getUTCSeconds(), 2, '0') + '.' + cm.string.padLeft(date.getUTCMilliseconds(), 3, '0') + 'Z';
		},
		/**
		 * @return jQuery
		 */
		$timeago: function() {
			var date = new Date();
			var print = date.toLocaleString();
			var iso8601 = this.iso8601(date);
			return $('<abbr class="timeago" title="' + iso8601 + '">' + print + '</abbr>').timeago();
		}
	},
	language: {
		_keys: {},

		/**
		 * @param string path
		 * @param string value
		 */
		set: function(path, value) {
			this._keys[path] = value;
		},

		/**
		 * @param string path
		 * @param object variables OPTIONAL
		 */
		get: function(path, variables) {
			if (this._keys[path] === undefined) {
				cm.error.trigger('Language path `' + path + '` not found.');
			}
			var text = this._keys[path];
			if (variables) {
				for (key in variables) {
					text = text.replace('{$' + key + '}', variables[key]);
				}
			}
			return text;
		}
	},

	cache: {
		_values: {},

		/**
		 * @param string key
		 * @return mixed|false
		 */
		get: function(key) {
			if (key in this._values) {
				return this._values[key];
			}
			return false;
		},

		/**
		 * @param string key
		 * @param mixed value
		 */
		set: function(key, value) {
			this._values[key] = value;
		}
	},

	ui: {
		/**
		 * @param string question
		 * @param function callbackOk fn()
		 */
		confirm: function(question, callback) {
			var $ok = $('<input type="button" />').val(cm.language.get('%interface.ok'))
			var $cancel = $('<input type="button" />').val(cm.language.get('%interface.cancel'));
			var $html = $('<div><div class="box_cap"><h3></h3></div><div class="box_body"></div><div class="box_bottom"></div></div>');
			$html.find('.box_cap h3').text(cm.language.get('%interface.confirmation_title'));
			$html.find('.box_body').text(question);
			$html.find('.box_bottom').append($ok, $cancel);

			$html.floatOut();
			$ok.click(function() {
				$html.floatIn();
				callback();
			});
			$cancel.click(function() {
				$html.floatIn();
			});
		}
	},

	window: {
		_hasFocus: true,
		_$hidden: null,

		ready: function() {
			var handler = this;
			$(window).focus(
				function() {
					handler._hasFocus = true;
				}).blur(function() {
					handler._hasFocus = false;
				});
			this.title.ready();
		},

		hasFocus: function() {
			return this._hasFocus;
		},

		appendHidden: function(html) {
			if (!this._$hidden) {
				this._$hidden = $('<div style="display:none;" />').appendTo('body');
			}
			this._$hidden.append(html);
		},

		hint: function(content) {
			$.windowHint(content);
		},

		title: {
			_messageStop: function() {
			},
			_messageTimeout: null,

			ready: function() {
				var handler = this;
				$(window).focus(function() {
					handler._messageStop();
				});
			},

			message: function(msg) {
				if (cm.window.hasFocus()) {
					return;
				}
				var handler = this;
				var sleeper = function(offset) {
					offset += 4;
					if (offset >= msg.length) {
						handler._messageStop();
					} else {
						document.title = msg.substring(offset, msg.length);
						handler._messageTimeout = setTimeout(function() {
							sleeper(offset);
						}, 400);
					}

				}

				this._messageStop();
				var originalTitle = document.title;
				document.title = msg;
				this._messageTimeout = setTimeout(function() {
					sleeper(0);
				}, 1500);
				this._messageStop = function() {
					document.title = originalTitle;
					clearTimeout(handler._messageTimeout);
				};
			}
		}
	},

	player: {
		/**
		 * @see http://jplayer.org/latest/developer-guide/#jPlayer-methods
		 * @return jQuery
		 */
		audio: function(mp3Path) {
			var options = {
				'swfPath': cm.options.urlStatic + 'swf',
				ready: function() {
					$(this).jPlayer('setMedia', {
						'mp3': cm.options.urlStatic + 'audio/' + mp3Path
					});
				}
			};
			$player = $('<div class="jplayer" />').appendTo($('body'));
			return $player.jPlayer(options);
		}
	},

	storage: {
		/**
		 * @param string key
		 * @param mixed value
		 */
		set: function(key, value) {
			$.jStorage.set(key, value);
		},

		/**
		 * @param string key
		 * @return mixed
		 */
		get: function(key) {
			return $.jStorage.get(key);
		},

		/**
		 * @param string key
		 */
		del: function(key) {
			$.jStorage.deleteKey(key);
		}
	},

	/**
	 * @param string type
	 * @param mixed data
	 * @param object callbacks
	 * @param bool cache
	 */
	ajax: function(type, data, callbacks, cache) {
		var url = '/' + type + '/' + this.options.siteId;
		var cacheKey = 'ajaxRequest-' + url + '-' + JSON.stringify(data);
		if (cache && false !== cm.cache.get(cacheKey)) {
			if (callbacks.success) {
				callbacks.success(cm.cache.get(cacheKey));
			}
			if (callbacks.complete) {
				callbacks.complete();
			}
			return;
		}
		var errorHandler = function(msg, type, isPublic, callback) {
			if (!callback || callback(msg, type, isPublic) !== false) {
				cm.error.trigger(msg, type, isPublic);
			}
		};
		return $.ajax(url, {
			data: JSON.stringify(data),
			type: 'POST',
			dataType: 'json',
			contentType: 'application/json',
			cache: false,
			success: function(response) {
				if (response.error) {
					errorHandler(response.error.msg, response.error.type, response.error.isPublic, callbacks.error);
				} else if (response.success) {
					if (cache) {
						cm.cache.set(cacheKey, response.success);
					}
					if (callbacks.success) {
						callbacks.success(response.success);
					}
				}
			},
			error: function(xhr, textStatus) {
				if (xhr.status == 0) {
					return; // Ignore interrupted ajax-request caused by leaving a page
				}
				var msg = xhr.responseText || textStatus;
				errorHandler(msg, 'XHR', false, callbacks.error);
			},
			complete: function() {
				if (callbacks.complete) {
					callbacks.complete();
				}
			}
		});
	},

	rpc: function(method, params, callbacks, cache) {
		this.ajax('rpc', {method: method, params: params}, {
			success: function(response) {
				if (typeof(response.result) === 'undefined') {
					cm.error.trigger('RPC response has undefined result');
				}
				if (callbacks.success) {
					callbacks.success(response.result);
				}
			},
			error: callbacks.error,
			complete: callbacks.complete
		}, cache);
	},

	stream: {
		_connected: false,
		_dispatcher: _.clone(Backbone.Events),

		/**
		 * @param string namespace for which callback should be used
		 * @param function callback fn(array data)
		 * @param object context OPTIONAL
		 */
		bind: function(namespace, callback, context) {
			if (!this._connected) {
				if (typeof cm.options.stream.channel == 'undefined') {
					return;	// No viewer
				}
				this.connect(cm.options.stream.channel);
				this._connected = true;
			}
			this._dispatcher.on(namespace, callback, context);
		},
		/**
		 * @param string namespace
		 * @param function callback
		 * @param object context OPTIONAL
		 */
		unbind: function(namespace, callback, context) {
			this._dispatcher.off(namespace, callback, context);
		},
		/**
		 * @param string channel
		 */
		connect: function(channel) {
			if (!cm.options.stream.enabled) {
				return;
			}
			var handler = this;
			var server = cm.options.stream.server;
			var callback = function(message) {
				handler._dispatcher.trigger(message.namespace, message.data);
			};
			if (cm.options.stream.adapter == 'CM_StreamAdapter_Socketio') {
				var socket = io.connect("http://" + server.host + ":" + server.port);
				socket.on('connect', function() {
					socket.emit('subscribe', {'channel': channel, 'start': cm.options.renderStamp});
				});
				socket.on('message', callback);
			} else if (cm.options.stream.adapter == 'CM_StreamAdapter_Apache') {
				$.stream('http://' + server.host + ':' + server.port + '/longpolling/' + cm.options.siteId + '/', channel, callback);
			} else {
				cm.error.trigger('Cannot understand stream adapter `' + cm.options.stream.adapter + '`')
			}
		}
	},

	model: {
		types: {
		}
	},

	action: {
		types: {
		},
		_registered: false,
		_dispatcher: _.clone(Backbone.Events),

		/**
		 * @param int actionType
		 * @param int modelType
		 * @param function callback fn(CM_Action_Abstract action, CM_Model_Abstract model, array data)
		 * @param object context OPTIONAL
		 */
		bind: function(actionType, modelType, callback, context) {
			if (!this._registered) {
				cm.stream.bind('CM_Action_Abstract', function(response) {
					this._dispatcher.trigger(response.action.type + ':' + response.model.type, response.action, response.model, response.data);
				}, this);
				this._registered = true;
			}
			this._dispatcher.on(actionType + ':' + modelType, callback, context);
		},
		/**
		 * @param int actionType
		 * @param int modelType
		 * @param function callback
		 * @param object context OPTIONAL
		 */
		unbind: function(actionType, modelType, callback, context) {
			this._dispatcher.off(actionType + ':' + modelType, callback, context);
		}
	}
};

function cm() {
	var self = this;
	$(function() {
		self.ready();
	});
}

var cm = new cm();
;
cm.model.types = {"SK_User":13,"CM_Model_SmileySet":15,"SK_Entity_Profile":1,"SK_Entity_Photo":2,"SK_Entity_Video":3,"SK_Entity_Blogpost":4,"SK_Entity_Chat":5,"SK_Entity_Conversation":6,"SK_Entity_ConversationMessage_Text":7,"SK_Entity_ConversationMessage_Gift":8,"SK_Entity_TextFormatterImage":9,"SK_Entity_Status":10,"SK_ActionLimit_Bruteforce":11,"SK_ActionLimit_Daily":12,"SK_ServiceBundle":14};
var CM_Renderable_Abstract = Backbone.View.extend({_class:"CM_Renderable_Abstract"});
var CM_Component_Abstract = CM_Renderable_Abstract.extend({_class:"CM_Component_Abstract",
_children: [],
_forms: [],

initialize: function() {
	this._children = [];
	this._forms = [];
	
	if (this.getParent()) {
		this.getParent().registerChild(this);
	}

	if (this.actions) {
		this._bindActions(this.actions);
	}
	if (this.streams) {
		this._bindStreams(this.streams);
	}
},

/**
 * Called when all components are loaded
 */
ready: function() {
},

_ready: function() {
	this.ready();

	this.$(".timeago").timeago();
	this.$().placeholder();
	this.$('button[title]').qtip({
		position: {my: 'bottom center', at: 'top center'},
		style: {classes: 'ui-tooltip-tipped'}
	});

	_.each(this.getForms(), function(form) {
		form.ready();
	});
	_.each(this.getChildren(), function(child) {
		child._ready();
	});
},

/**
 * Called on popOut()
 */
repaint: function() {
},

/**
 * @return string
 */
getAutoId: function() {
	return this.options.autoId;
},

/**
 * @return object
 */
getParams: function() {
	return this.options.params;
},

/**
 * @return CM_Component_Abstract[]
 */
getChildren: function() {
	return this._children;
},

/**
 * @param string className
 * @return CM_Component_Abstract|null
 */
findChild: function(className){
	return _.find(this.getChildren(), function(child) {
		return child._class == className;
	}) || null;
},

/**
 * @param CM_Component_Abstract child
 */
registerChild: function(child) {
	this._children.push(child);
},

/**
 * @return CM_Component_Abstract|null
 */
getParent: function() {
	if (this.options.parent) {
		return this.options.parent;
	}
	return null;
},

/**
 * @return CM_Form_Abstract[]
 */
getForms: function() {
	return this._forms;
},

/**
 * @param string name
 * @return CM_Form_Abstract|null
 */
findForm: function(name) {
	return _.find(this.getForms(), function(form) {
		return form._class == name;
	}) || null;
},

/**
 * @param CM_Form_Abstract form
 */
registerForm: function(form) {
	this._forms.push(form);
},

/**
 * @return jQuery
 */
$: function(selector) {
	if (!selector) {
		return this.$el;
	}
	selector = selector.replace('#', '#'+this.getAutoId()+'-');
	return $(selector, this.el);
},

disable: function() {
	this.$().disable();
},

enable: function() {
	this.$().enable();
},

popOut: function(options) {
	this.$().floatOut(options);
	this.repaint();
},

popIn: function() {
	this.$().floatIn();
},

/**
 * @param string message
 */
error: function(message) {
	cm.window.hint(message);
},

/**
 * @param string message
 */
message: function(message) {
	cm.window.hint(message);
},

/**
 * @param string event
 * @param function callback fn(array data)
 */
bindStream: function(event, callback) {
	var namespace = this._class + ':' + event;
	cm.stream.bind(namespace, callback, this);
	this.on('destruct', function() {
		cm.stream.unbind(namespace, callback, this);
	});
},

/**
 * @param int actionTypes
 * @param int modelType
 * @param function callback fn(CM_Action_Abstract action, CM_Model_Abstract model, array data)
 */
bindAction: function(actionType, modelType, callback) {
	cm.action.bind(actionType, modelType, callback, this);
	this.on('destruct', function() {
		cm.action.unbind(actionType, modelType, callback, this);
	});
},

/**
 * @param string functionName
 * @param array|null params
 * @param object|null callbacks
 * @param bool|null cache
 * @return jqXHR
 */
ajax: function(functionName, params, callbacks, cache) {
	callbacks = callbacks || {};
	params = params || {};
	var handler = this;
	var xhr = cm.ajax('ajax', {component:this._getArray(), method:functionName, params:params}, {
		success: function(response) {
			if (response.exec) {
				var exec = new Function(response.exec);
				exec.call(handler);
			}
			if (callbacks.success) {
				return callbacks.success.call(handler, response.data);
			}
		},
		error: function(msg, type) {
			if (callbacks.error) {
				return callbacks.error.call(handler, msg, type);
			}
		},
		complete: function() {
			if (callbacks.complete) {
				return callbacks.complete.call(handler);
			}
		}
	}, cache);
	this.on('destruct', function() {
		xhr.abort();
	});
	return xhr;
},

/**
 * @return jqXHR
 */
ajaxModal: function(apply_func, params, callbacks) {
	callbacks = callbacks || {};
	var handler = this;
	var callbackComplete = callbacks.complete;
	callbacks.complete = function() {
		handler.enable();
		if (callbackComplete) {
			return callbackComplete(handler);
		}
	};
	this.disable();
	this.ajax(apply_func, params, callbacks);
},

load: function(className, params, options) {
	var handler = this;
	options = options || {};
	params = params || {};
	params.component = className;
	var successPopOut = options.successPopOut || function() {};
	var successPre = options.success ? options.success :  function() { this.popOut(); };
	var successPost = options.success ? function() {} : function() { successPopOut.call(this); }
	options.success = function(componentId) {
		var handlerNew = cm.components[componentId];
		successPre.call(handlerNew);
		handlerNew._ready();
		successPost.call(handlerNew);
	};
	return this.ajaxModal('load', params, options);
},

/**
 * @return XMLHttpRequest
 */
reload: function(params) {
	return this.ajaxModal('reload', params);
},

remove: function(skipDomRemoval) {
	if (this.getParent()) {
		var siblings = this.getParent().getChildren();
		for (var i = 0, sibling; sibling = siblings[i]; i++) {
			if (sibling.getAutoId() == this.getAutoId()) {
				siblings.splice(i, 1);
			}
		}
	}

	_.each(this.getChildren(), function(child) {
		child.remove();
	});

	_.each(this.getForms(), function(form) {
		delete cm.forms[form.getAutoId()];
	});
	this.trigger("destruct");
	delete cm.components[this.getAutoId()];


	if (!skipDomRemoval) {
		this.$().remove();
	}
},

/**
 * @param string key
 * @param mixed key
 */
storageSet: function(key, value) {
	cm.storage.set(this._class + '_' + key, value);
},

/**
 * @param string key
 * @return mixed
 */
storageGet: function(key) {
	return cm.storage.get(this._class + '_' + key);
},

/**
 * @param string key
 */
storageDelete: function(key) {
	cm.storage.del(this._class + '_' + key);
},

/**
 * @return object
 */
_getArray: function() {
	return {
		className: this._class,
		id: this.getAutoId(),
		params: this.getParams(),
		parentId: this.getParent() ? this.getParent().getAutoId() : null
	};
},

/**
 * @param object
 */
_bindActions: function(actions) {
	for (key in actions) {
		var callback = actions[key];
		var match = key.match(/^(\S+)\s+(.+)$/);
		var modelType = cm.model.types[match[1]];
		var actionNames = match[2].split(/\s*,\s*/);
		_.each(actionNames, function(actionName) {
			var actionType = cm.action.types[actionName];
			this.bindAction(actionType, modelType, callback);
		}, this);
	}
},

/**
 * @param object
 */
_bindStreams: function(streams) {
	for (key in streams) {
		var callback = streams[key];
		this.bindStream(key, callback);
	}
}
});
var SK_Component_Abstract = CM_Component_Abstract.extend({_class:"SK_Component_Abstract"});
var FB_Component_BlogpostView = SK_Component_Abstract.extend({_class:"FB_Component_BlogpostView",
blogpost: null,

events: {
	'click .edit': 'editBlogpost'
},

editBlogpost: function() {
	var handler = this;
	this.load('FB_Component_BlogpostEdit', {'blogpost': this.blogpost}, {
		successPopOut: function() {
			this.registerDependency(handler);
		}
	});
}
});
var FB_Component_ChangePassword = SK_Component_Abstract.extend({_class:"FB_Component_ChangePassword"});
var FB_Component_Chat = SK_Component_Abstract.extend({_class:"FB_Component_Chat",
contacts: {matches: {}, friends:{}},
visible: false,
audio: null,
_audioPlayer: null,
_msgSectionTimeout: null,

events: {
	'click .menu .panel.offline': 'setVisible',
	'click .menu .panel:not[.offline]': 'toggleMenu',
	'click .menu .header .icon.offline': 'setInvisible',
	'click .menu .header .icon.audio': 'toggleAudio'
},

actions: {
	'SK_User VISIBLE': function(action, model, data) {
		if (!this.visible) {
			if (action.actor.id == cm.viewer.id) {
				this.reload();
			}
			return;
		}
		if (action.actor.id != cm.viewer.id) {
			if (data.isFriend) {
				this._addContact('friends', model, true);
			} else {
				this._addContact('matches', model, true);
			}
			this.$('.chat .header .member[data-user-id=' + model.id + ']').removeClass('offline');
			this.$('.chat .panel .member[data-user-id=' + model.id + ']').removeClass('offline');
		}
	},
	'SK_User INVISIBLE, DELETE': function(action, model, data) {
		if (!this.visible) {
			return;
		}
		if (action.actor.id == cm.viewer.id) {
			this.reload();
		} else {
			this._deleteContact(model);
			this.$('.chat .header .member[data-user-id=' + model.id + ']').addClass('offline');
			this.$('.chat .panel .member[data-user-id=' + model.id + ']').addClass('offline');
		}
	},
	'SK_User CONNECT': function(action, model, data) {
		if (!this.visible) {
			return;
		}
		var user = (action.actor.id == cm.viewer.id) ? model : action.actor;
		if (user.visible) {
			this._addContact('friends', user, true);
		}
	},
	'SK_User DISCONNECT': function(action, model, data) {
		if (!this.visible) {
			return;
		}
		var user = (action.actor.id == cm.viewer.id) ? model : action.actor;
		this._deleteContact(user);
	}
},

streams: {
	'openChat': function(response) {
		if (!this.visible) {
			return;
		}
		if (!this._getChatDOM(response.chat).length) {
			this._addChatDOM(response.html);
			this._setupChat(response.chat);
		}
	},
	'message': function(response) {
		if (!this.visible) {
			return;
		}
		this._openChat(response.chat, null, function($chat) {
			if (response.user.id != cm.viewer.id) {
				if (!$chat.hasClass('active')) {
					$chat.addClass('unread');
				}
				if (this.audio && !cm.window.hasFocus()) {
					this._audioPlayer.jPlayer('play');
				}
			}
			this._addMessage(response.chat, response.content, response.user, response.id, response.createStamp);
		});
	},
	'message-premiumrequired': function(response) {
		if (!this.visible) {
			return;
		}
		this._openChat(response.chat, null, function($chat) {
			if (!$chat.hasClass('active') && response.user.id != cm.viewer.id) {
				$chat.addClass('unread');
			}
			this._addNotice(response.chat, '<span class="username">'+response.user.displayName+'</span> sent you a message. Get a <a href="/account/premium">Premium Account</a> to read it!');
		});
	},
	'addMember': function(response) {
		if (!this.visible) {
			return;
		}
		this._addMember(response.user, response.chat);
	},
	'closeChat': function(response) {
		if (!this.visible) {
			return;
		}
		this._closeChat(response.chat);
	},
	'removeMember': function(response) {
		if (!this.visible) {
			return;
		}
		this._deleteMember(response.user, response.chat);
	},
	'prependChat': function(response) {
		if (!this.visible) {
			return;
		}
		this._openChat(response.chat, null, function($chat) {
			$chat.prependTo(this.$('.chats'));
			$chat.filter('.active').find('textarea').focus();
		}, true);
	},
	'setAudio': function(response) {
		if (!this.visible) {
			return;
		}
		this._setAudio(response.state);
	}
},

ready: function() {
	var handler = this;

	$(window).resize(function() {
		handler.repaint();
	});
	$.each(this.contacts.friends, function(index, item) {
		handler._addContact('friends', item);
	});
	$.each(this.contacts.matches, function(index, item) {
		handler._addContact('matches', item);
	});

	var activeChatId = this.storageGet('activeChat'); 
	if (activeChatId) {
		this.$('.chat[data-chat-id=' + activeChatId + ']').addClass('active')
		.find('.window').show().find('.content').scrollBottom();
	}
	
	this._audioPlayer = cm.player.audio('notification.mp3');

	window.setInterval(function() {
		handler.ajax('getMatches', {}, {
			'success': function (response) {
				handler._updateContactsType('matches', response, handler.contacts.matches);
			}
		});
	}, 30000);
	
	this.repaint();
},

/**
 * @param SK_User|int user
 */
requestChat: function(user) {
	var userId = user.id ? user.id : user;
	var $chatItem = this.$('.chat:has(.panel .member[data-user-id=' + userId + '] .username:visible)');

	if ($chatItem.length) {
		var chat = {id: $chatItem.attr('data-chat-id')};
		this._openChat(chat, null, function() {
			this._activateChat(chat);
		});
	} else {
		this.ajax('openChat', {'user': user}, {
			success: function(response) {
				if (response) {
					this._openChat(response.chat, response.html, function() {
						this._activateChat(response.chat);
					});
				}
			},
			error: function(msg, type) {
				this.$('.menu .window .contact_'+userId).remove();
			}
		});
	}
},

toggleAudio: function() {
	var handler = this;
	this.ajax('setAudio', {state: !handler.audio}, {
		success: function(state) {
			handler._setAudio(state);
		}
	});
},

toggleMenu: function() {
	this.$('.menu .window').toggleModal();
},

setVisible: function() {
	this._setVisible(true);
},

setInvisible: function() {
	this._setVisible(false);
},

repaint: function() {
	while (this.$('.chat:visible').length && this._getBarWidth() > $(window).width()){
		this.$('.chat:visible:last').hide();
	}
},

/**
 * @param boolean state
 */
_setVisible: function(state) {
	this.ajaxModal('setVisible', {state: state});
},

/**
 * @param SK_Entity_Chat chat
 * @param string html OPTIONAL
 * @param function callback OPTIONAL fn(jQuery $chat)
 * @param boolean skipStreamPrepend OPTIONAL 
 */
_openChat: function(chat, html, callback, skipStreamPrepend) {
	var $chat = this._getChatDOM(chat);
	if (!$chat.length && !html) {
		this.ajax('renderChat', {'chat': chat}, {
			success: function(html) {
				this._openChat(chat, html, callback);
			}
		});
		return;
	}
	if ($chat.length) {
		if (!$chat.is(':visible')) {
			$chat.prependTo(this.$('.chats'));
			if (!skipStreamPrepend) {
				this.ajax('prependChat', {'chat': chat});
			}
		}
	} else {
		$chat = this._addChatDOM(html);
		this._setupChat(chat);
	}
	$chat.show();
	if (callback) {
		callback.call(this, $chat);
	}
	this.repaint();
},

/**
 * @param SK_Entity_Chat chat
 */
_closeChat: function(chat) {
	this._getChatDOM(chat).remove();
},

/**
 * @param SK_Entity_Chat chat
 * @param string content
 * @param SK_User user
 * @param int msgId
 * @param int createStamp
 */
_addMessage: function(chat, content, user, msgId, createStamp){
	var handler = this;
	var $content = this._getChatDOM(chat).find('.content');
	if ($content.is(':has(p[data-id="' + msgId + '"])')) {
		return;
	}
	
	var $lastEntry = $content.find('> :last');
	if (!$lastEntry.is('.section') || ($lastEntry.data('user-id') != user.id) || $lastEntry.find('> :last').data('create-stamp') < createStamp-this._msgSectionTimeout) {
		var $username = $('<span />').text(user.displayName + ":");
		if (user.id == cm.viewer.id) {
			$username.text('You:'); 
		}
		var date = new Date();
		var time = date.getHours() + ':' + ((date.getMinutes()<10)?'0':'') + date.getMinutes();
		$username.addClass('username');
		var $section = $('<div class="section" data-user-id="' + user.id + '"></div>');
		$section.append(cm.date.$timeago(), '<div class="clr"></div><a href="' + user.link + '"><img src="'+user.thumb+'"/></a>');
		$section.find('img').after($username);
		if (user.id == cm.viewer.id) {
			$section.addClass('You');
		}
		$content.append($section);
	}
	var $userLast = $content.find('.section:last');
	var $line = $('<p data-id="' + msgId + '" data-create-stamp="' + createStamp + '">').html(content);
	$userLast.append($line);
	$content.scrollBottom();
	if (user.id != cm.viewer.id) {
		cm.window.title.message(user.displayName + " has just sent you a message!");
	}
},

/**
 * @param SK_Entity_Chat chat
 * @param string html
 * @param boolean isNotice OPTIONAL
 */
_addInfo: function(chat, html, isNotice) {
	var $msg = $('<p class="info' + (isNotice?' notice':'') + '">' + html + '</p>');
	this._getChatDOM(chat).find('.window .content').append($msg).scrollBottom();
},

/**
 * @param SK_Entity_Chat chat
 * @param string html
 */
_addNotice: function(chat, html) {
	this._addInfo(chat, html, true);
},

/**
 * @param SK_User user
 * @param SK_Entity_Chat chat
 */
_addMember: function(user, chat) {
	var $chat = this._getChatDOM(chat);
	var $members = $chat.find('.header .members');
	var $panel = $chat.find('.panel');
	var username = this._escapeText(user.displayName);
	if ($panel.is(':has(.member[data-user-id="'+user.id+'"])')) {
		return;
	}
	$members.append('<div class="member" data-user-id="' + user.id + '" ><a class="username" href="'+user.link+'">'+username+'</a><a class="report" href="javascript:;">(report)</a>');
	$panel.append('<div class="member" data-user-id="'+user.id+'"><img src="'+user.thumb+'" title="'+username+'"/><p class="username nowrap">'+username+'</p></div>');
	if (chat.members.length > 2) {
		$panel.find('.username').hide();
		$members.find('.report').hide();
	}
	this._bindAddMember(chat);
	this._bindReportMember(chat, user);
	this._addInfo(chat, '<span class="username">'+username+'</span> has joined this chat.');
},

/**
 * @param SK_User user
 * @param SK_Entity_Chat chat
 */
_deleteMember: function(user, chat) {
	var $chat = this._getChatDOM(chat);
	var $members = $chat.find('.header .members');
	var $panel = $chat.find('.panel');
	var username = this._escapeText(user.displayName);
	$members.find('.member[data-user-id="'+user.id+'"]').remove();
	$panel.find('.member[data-user-id="'+user.id+'"]').remove();
	if (chat.members.length <= 2) {
		$panel.find('.username').show();
		$members.find('.report').show();
	}
	this._bindAddMember(chat);
	this._addInfo(chat, '<span class="username">'+username+'</span> has left this chat.');
},

/**
 * @param string type
 * @param SK_User user
 * @param boolean fadeIn
 */
_addContact: function(type, user, fadeIn) {
	var handler = this;
	if (this.$('.menu .content .' + type + ' .contact_' + user.id).length) {
		return;
	}
	var $listEntry = $('<li class="clearfix">')
		.append('<img src="' + user.thumb  + '"/><p class="nowrap">' + handler._escapeText(user.displayName) + '</p>')
		.addClass('contact_'+user.id);
	var $menuWindow = this.$('.menu .window');
	
	// creates a sorted array of all usernames and determines the index for the new list item.
	var list = new Array();
	$.each($menuWindow.find('ul.'+type).find('li'), function(index, item) {
		list.push($(item).find('a').text().toLowerCase());
	});
	if (list.length == 0) {
		$menuWindow.find('ul.'+type).append($listEntry);
	} else {
		list.push($listEntry.find('a').text().toLowerCase());
		list.sort();
		var index = $.inArray($listEntry.find('a').text().toLowerCase(), list);
		if (index == list.length - 1 || index == -1) {
			$menuWindow.find('ul.'+type).append($listEntry);
		} else {
			$menuWindow.find('ul.'+type + ' li:eq('+index+')').before($listEntry);
		}
	}
	
	if (fadeIn) {
		$listEntry.hide().fadeIn(1200);
	}
	$listEntry.click(function() {
		handler.requestChat(user);
	});
	this.$('.menu .panel').text('Chat ('+$menuWindow.find('.friends li').length+')');
},

/**
 * @param SK_User user
 */
_deleteContact: function(user) {
	var handler = this;
	var $menuWindow = this.$('.menu .window');
	var $delContact = $menuWindow.find('.contact_' + user.id);
	$delContact.hide('slow', function () {
		$delContact.remove();
	});
	this.$('.menu .panel').text('Chat ('+$menuWindow.find('.friends li:not(.contact_'+user.id+')').length+')');
},

_setAudio: function(state) {
	this.audio = state;
	$('.menu .header .icon.audio').toggleClass('active', this.audio);
},

_getChatDOM: function(chat) {
	return this.$('.chats .chat[data-chat-id=' + chat.id + ']');
},

_addChatDOM: function(html) {
	var $html = $(html).hide();
	this.$('.chats').prepend($html);
	return $html;
},

_setupChat: function(chat) {
	var handler = this;
	var $container = this.$('.chats');
	var $chat = handler._getChatDOM(chat);

	if ($chat.length != 1) {
		return;
	}
	$chat.find('.panel, .header').click(function (e) {
		if ($(e.target).is('a')) {
			return;
		}
		if ($chat.hasClass('active')) {
			handler._deactivateChat(chat);
		} else {
			handler._activateChat(chat);
		}
		handler.repaint();
	});
	$chat.find('textarea').focus(function() {
		$chat.addClass('focus');
	}).blur(function() {
		$chat.removeClass('focus');
	});
	handler._bindAddMember(chat);
	for (var i = 0; i < chat.members.length; i++) {
		if (chat.members[i].id != cm.viewer.id) {
			this._bindReportMember(chat, chat.members[i]);
		}
	}
	$chat.find('.close').click(function() {
		handler.ajax('closeChat', {'chat': chat});
		handler._closeChat(chat);
	});
	$chat.find('.content').click(function() {
		$chat.find('textarea').focus();
	});
	$chat.find('textarea').bind('keypress',function(e) {
		if (e.keyCode == 13) {
			if (this.value) {
				var message = this.value;
				this.value = '';
				var h = this;
				var chatNew = chat;
				handler.ajax('message', {'content': message, 'chat': chat}, {
					success: function(response) {
						handler._addMessage(chat, response.content, cm.viewer, response.id, response.createStamp);
						$chat.find('.window').scrollBottom();
					},
					error: function(msg, type) {
						handler._addNotice(chatNew, msg);
						return false;
					}
				});
			}
			return false;
		}
	});
	$chat.find('textarea').autoResize({
		'extraSpace': 0,
		'limit': 100
	});
	if (chat.members.length > 2) {
		$chat.find('.panel .username').hide();
		$chat.find('.header .report').hide();
	}
	if (0 == $chat.find('.content *').length && 1 == cm.rand(1,10)) {
		this._addInfo(chat, "Attention: Be aware of spammers and scammers! Don't give away your credit card details to anyone.");
	}
},

_bindReportMember: function(chat, user) {
	var handler = this;
	this._getChatDOM(chat).find('.header .member[data-user-id="'+user.id+'"] .report').click(function() {
		handler.load('FB_Component_Report', {user: user});
	});
},

_bindAddMember: function(chat) {
	var handler = this;
	this._getChatDOM(chat).find('.addMember').unbind().click(function() {
		handler.load('FB_Component_Selector_User_FriendsVisible', {'exclude': chat.members}, {
			success: function() {
				var selector = this;
				this.bind('select', function(user) {
					handler.ajax('inviteMember', {'chat':chat, 'user':user}, {
						success: function(chat) {
							handler._addMember(user, chat);
							selector.popIn();
						}
					});
				});	
				this.popOut();
			},
			error: function(msg, type) {
				handler._addNotice(chat, msg);
				return false;
			}
		});
		return false;
	});
},

_getBarWidth: function() {
	var width = this.$('.menu').outerWidth(true);
	this.$('.chat:visible').each(function(i, v) {
		width += $(this).outerWidth(true);
	});
	width += 20;	// Cmp padding
	return width;
},

_subtractUserArrayById: function(array1, array2) {
	var changes = [];
	$.each(array1, function(index1, item1) {
		var inArray = false;
		$.each(array2, function(index2, item2) {
			if (item1.id == item2.id) {
				inArray = true;
			}
		});
		if (!inArray) {
			changes.push(item1);
		}
	});
	return changes;
},

_escapeText: function(string) {
	return $('<p>').text(string).html();
},

_updateContactsType: function(type, updateType, contactsType) {
	var add = this._subtractUserArrayById(updateType, contactsType);
	var del = this._subtractUserArrayById(contactsType, updateType);
	var handler = this;
	$.each(add, function(i, user) {
		handler._addContact(type, user, true);
	});
	$.each(del, function(i, user) {
		handler._deleteContact(user);
	});
},

_activateChat: function(chat) {
	var $chat = this._getChatDOM(chat);
	var $chats = $chat.siblings();
	$chats.removeClass('active').find('.window').hide();
	var $window = $chat.show().removeClass('unread').addClass('active').find('.window').show();
	$window.find('.content').scrollBottom();
	$window.find('textarea').focus();
	this.storageSet('activeChat', chat.id);
},

_deactivateChat: function(chat) {
	var $chat = this._getChatDOM(chat);
	var $window = $chat.removeClass('active').find('.window').hide();
	this.storageDelete('activeChat');
}
});
var FB_Component_Codeofhonor = SK_Component_Abstract.extend({_class:"FB_Component_Codeofhonor"});
var FB_Component_Comments = SK_Component_Abstract.extend({_class:"FB_Component_Comments",
entity: null,

events: {
	"focus textarea": "inputFocus",
	"blur textarea": "inputBlur",
	"click .comment button.delete": "deleteComment"
},

inputFocus: function() {
	this.toggleAddActive(true);
},

inputBlur: function(e) {
	if ($(e.currentTarget).val() == '') {
		this.toggleAddActive(false);
	}
},

deleteComment: function(e) {
	var commentId = $(e.currentTarget).closest(".comment").data("comment-id");
	this.ajaxModal('deleteComment', {'commentId': commentId, entity: this.entity});
},

toggleAddActive: function(state) {
	var heightChange = this.$('.add').outerHeight(true);
	this.$('.add').toggleClass('active', state);
	heightChange = this.$('.add').outerHeight(true) - heightChange;
	if (this.$('.list').css('overflow') != 'visible') {
		var maxHeight = this.$('.list').css('max-height');
		if (maxHeight) {
			this.$('.list').css('max-height', parseInt(maxHeight) - heightChange);
		} else {
			this.$('.list').height(this.$('.list').height() - heightChange);
		}
	}
}
});
var FB_Component_EmailVerify = SK_Component_Abstract.extend({_class:"FB_Component_EmailVerify"});
var FB_Component_EntityItemTagNavigator = SK_Component_Abstract.extend({_class:"FB_Component_EntityItemTagNavigator"});
var FB_Component_Example = SK_Component_Abstract.extend({_class:"FB_Component_Example",
events: {
	"click .loadSelector": "loadSelector",
	"click .loadList": "loadList"
},

loadSelector: function() {
	this.load('FB_Component_Selector_User_Friends', {}, {
		success: function() {
			this.bind("select", function(friend) {
				this.message("Selected: " + friend.displayName);
				this.popIn();
			});
			this.popOut();
		}
	});
},

loadList: function() {
	this.load('FB_Component_UserList_Friends', {template:'mini'});
}
});
var FB_Component_Faq = SK_Component_Abstract.extend({_class:"FB_Component_Faq",
ready: function() {
	var handler = this;
	this.$("dd").hide();
	this.$("dt").click(function() {
		$(this).next("dd").slideToggle(100);
		$(this).toggleClass("active");
	});
}
});
var FB_Component_Feed = SK_Component_Abstract.extend({_class:"FB_Component_Feed",
user: null,
pageNext: null,
showedMore: false,
tab: null,

events: {
	"click .showMore": "showMore",
	"click .tab > a": "changeTab"

},

ready: function() {
	var handler = this;
	this.$().on('click-no-meta', '.item_'+cm.model.types.SK_Entity_Photo+'-'+cm.action.types.CREATE+' a[data-photo-id]', function(e) {
		var photoId = $(this).data("photo-id");
		handler.load('FB_Component_PhotoList_UserGallery', {'photo':photoId});
		e.preventDefault();
	});
	
	this.$('.clipSlide').clipSlide();
	
	$(window).off('scrollEnd').on('scrollEnd', function() {
		handler.showMore();
	});
},

showMore: function() {
	if (this.showedMore) {
		return;	// @todo: Cannot only bind "one", track http://bugs.jquery.com/ticket/10984
	}
	this.showedMore = true;
	if (!this.pageNext) {
		return;
	}
	var handler = this;
	this.load('FB_Component_Feed', {user: this.user, page: this.pageNext, tab: this.tab}, {
		success: function() {
			var $content = this.$('.box_body:first').children();
			this.$('.box_cap').remove();
			handler.$('.box_bottom').remove();
			handler.$().after(this.$());
		}
	});
},

changeTab: function(event) {
	this.reload({user: this.user, tab: $(event.currentTarget).attr('rel')});
}
});
var FB_Component_ForgotPassword = SK_Component_Abstract.extend({_class:"FB_Component_ForgotPassword"});
var FB_Component_HotRate = SK_Component_Abstract.extend({_class:"FB_Component_HotRate",
nextPhoto: function() {
	var handler = this;
	var sex = this.$(".action select").val();
	handler.reload({'sex':sex});
},

ready: function() {
	var handler = this;
	this.findChild('FB_Component_Rating').bind('rate', function() {
		handler.nextPhoto();
		return false;
	});
	
	this.$(".action select").change(function(){
		handler.nextPhoto();
	});
	
	this.$(".skip").click(function(){
		handler.nextPhoto();
	});
	
}
});
var FB_Component_Index = SK_Component_Abstract.extend({_class:"FB_Component_Index",
events: {
	"click .whitebox,.feature": "showSignup"
},

ready: function() {
	
	this.$('.whitebox').hover(function() {
		$(this).addClass('active');
		$(this).find('.content').slideDown(200);
	}, function() {
		var $whitebox = $(this);
		$whitebox.find('.content').slideUp(200, function() {
			$whitebox.removeClass('active');
		});
	});
},

showSignup: function() {
	var signup = this.findChild("FB_Component_SignUp");
	signup.popOut();
}
});
var FB_Component_InviteFriends = SK_Component_Abstract.extend({_class:"FB_Component_InviteFriends",
float_box:{},

$import_container :{},

contacts:{},

construct : function(){
	var handler = this;
							
	this.$("#open_box_btn").click(function(){
		handler.showImportContactsBox();
	});
	
	this.$import_container = this.$('.import_container');
	
},

showImportContactsBox: function(){
	var handler = this;
	this.$("#open_box_btn").unbind().click(function() {
		handler.$import_container.slideToggle();
	});
	this.$import_container.slideDown();
	var $list_node = this.$import_container.find('.contact_list');
	$list_node.find('input[name=add_contact]').unbind().click(function(){
				$checked = $list_node.find('input:checked');
				if ($checked.length>0){
					var checked_contacts = [];
					$.each($checked, function(i, obj){
						checked_contacts[i] = $(obj).val();
					});
					handler.addContacts(checked_contacts);
				}
			});	
			
	var $import_form = this.$import_container.find("form");
	$import_form.submit(function(){
		handler.loadContacts($import_form);	
		return false;
	});	

	$list_node.find('.contact_item .info').unbind().click(function(){
		var $checkbox = $(this).parent().find(':checkbox');
		$checkbox.prop('checked',!$checkbox.prop('checked'));
	});
	
	$list_node.find('.check_all input:checkbox').unbind().click(function(){
		$list_node.find('input:checkbox').prop('checked', $(this).prop('checked'));
	});
			
},

loadContacts: function($form){
	var handler = this;
	var email = $form.get(0).email.value;
	var password = $form.get(0).password.value;
	var provider = $form.get(0).provider.value;
	
	this.clearList();
	var $preloader = $('<div class="preloader" style="display:none">').prependTo(this.$import_container.find('.auth_cont'));
	
	$preloader.css('height',this.$import_container.height()); 
	$preloader.fadeIn('normal');
	
	this.ajax('loadContacts', {email: email, password: password, provider: provider},{success: function(){
		$preloader.fadeOut('fast',function(){$preloader.remove();});
	}});
},

drawContact: function(name, address){
	if (!this.$list_note) {
		this.$list_note = this.$import_container.find('.contact_list');
		this.$prototype_node = this.$list_note.find('.prototype_node');
		this.$contact_node_parent = this.$list_note.find('.prototype_node').parent();
	}
			
	if ( (name==undefined || name=="") || (address==undefined || address=="") ) {
		return false;
	}
	var handler = this;
				
	var $list_node = this.$list_note;
	var $contact_node = this.$prototype_node.clone();
	var $contact_node_parent = this.$contact_node_parent;
	$contact_node.removeClass('prototype_node');
	var $checkbox = $contact_node.find(".check :checkbox");
	$checkbox.val(name);
	$contact_node.find('.info').unbind().click(function(){
		$checkbox.prop('checked',!$checkbox.prop('checked'));
	});
	$contact_node.find(".name").text(name);
	
	$contact_node.find(".address").text(address);
	
	this.cicle = !this.cicle;
	
	this.contacts[name] = {
		name: name,
		address: address,
		$node: $contact_node 
	};
	
	$contact_node_parent.append($contact_node);

},

drawList: function(items){
	var handler = this;
	
	$.each(items, function(i, item){
		handler.drawContact(item.name, item.address);
	});
	
	this.$import_container.find('.contact_list table tr:odd').addClass('list_odd');
	this.$import_container.find('.contact_list .list_content').show();	
	this.showList();

},

showList: function(){
	
	var handler = this;
	var $list_node = this.$import_container.find('.contact_list').show();
	var $auth_cont = this.$import_container.find('.auth_cont');
	$list_node.find(".empty_list").hide();
							
	if (this.contacts.length == 0 ) {
		$list_node.find(".empty_list").show();
	}
	
	$auth_cont.slideUp('fast');
},

hideList: function() {
	var handler = this;
	var $list_node = this.$import_container.find('.contact_list');
	var $auth_cont = this.$import_container.find('.auth_cont');	
	$list_node.find(".empty_list").hide();
		
	$auth_cont.slideDown('fast');
},

addContacts : function(names) {
	var handler = this;
	var $addr_field = this.$('.invite_form_cont').find("form textarea[name=email_addr]");
	for(var i=0 ; i < names.length ; i++){
		
		var name = names[i];
		if (this.contacts[name]!=undefined) {
			
			var last_val = $addr_field.val();
			last_val += this.contacts[name].address+"\n"
			$addr_field.val(last_val);
			$addr_field.focus();
		}
	}
	this.$import_container.slideUp();
	
	this.$('.contact_list').slideDown();
},

clearList:function(){
	for(var key in this.contacts){
		this.contacts[key].$node.remove();
	}
}
});
var FB_Component_Layout = SK_Component_Abstract.extend({_class:"FB_Component_Layout"});
var FB_Component_ManageProfile = SK_Component_Abstract.extend({_class:"FB_Component_ManageProfile",
events: {
	"click .changeBackground": "changeBackground"
},

changeBackground: function() {
	this.load('FB_Component_ProfileBackground');
}
});
var FB_Component_MemberFeedback = SK_Component_Abstract.extend({_class:"FB_Component_MemberFeedback"});
var FB_Component_MemberHome = SK_Component_Abstract.extend({_class:"FB_Component_MemberHome"});
var FB_Component_MenuContext = SK_Component_Abstract.extend({_class:"FB_Component_MenuContext",
ready: function() {
	var handler = this;

	var $menu = this.$('.opener.context .window');
	this.$('.opener.context').click(function() {
		var $button = $(this).children('.panel');
		$menu.toggleModal(function() {
			$(this).toggle();
			$button.toggleClass('active');
		});
	});
}
});
var FB_Component_Notfound = SK_Component_Abstract.extend({_class:"FB_Component_Notfound"});
var FB_Component_PaymentSelection = SK_Component_Abstract.extend({_class:"FB_Component_PaymentSelection",
ready: function() {
	var handler = this;
	this.$(".plans li:first-child .plan").addClass("active");
	this.$(".plan input").change(function() {
		handler.$('.plan').removeClass("active");
		$(this).closest('.plan').addClass("active");
	});
}
});
var FB_Component_PhotoView = SK_Component_Abstract.extend({_class:"FB_Component_PhotoView",
photo: null,

events: {
	"click .FB_Component_MenuContext_Entity .delete": "deletePhoto"
},

ready: function() {
	var handler = this;
	this.$('.preview img').click(function() {
		handler.getParent().next();
		return false;
	});
	this.$('.rotate_cw').click(function() {
		handler.rotate(90);
	});

	this.$('.rotate_ccw').click(function() {
		handler.rotate(270);
	});
},

rotate: function(angle){
	this.ajaxModal('rotate', {'photo':this.photo, 'angle':angle});
},

deletePhoto: function() {
	var handler = this;
	this.ajax('delete', {'photo': this.photo}, {
		success: function() {
			handler.getParent().getParent().reload();
			handler.popIn();
		}
	});
}
});
var FB_Component_Privacy = SK_Component_Abstract.extend({_class:"FB_Component_Privacy"});
var FB_Component_ProfileBackground = SK_Component_Abstract.extend({_class:"FB_Component_ProfileBackground",
ready: function(){
	var handler = this;
	$.each(this.$('table.form tr td.value'), function(i, item) {
		$(this).click(function() {
			handler.$('input[name="mode"][value=' + i + ']').prop('checked', true);
		});
	});
}
});
var FB_Component_ProfileBriefInfo = SK_Component_Abstract.extend({_class:"FB_Component_ProfileBriefInfo",
user: null,
image: null,
color: null,

events: {
	'click .toggleBookmark': 'toggleBookmark',
	'click .toggleBlock': 'toggleBlock',
	'click .toggleFriend': 'toggleFriend',
	'click .report': 'report'
},

ready: function() {
	var handler = this;

	this.$('.chat').click(function() {
		if (!cm.viewer) {
			cm.component().load('FB_Component_SignUp');
			return;
		}
		cm.component('FB_Component_Chat').requestChat(handler.user.id);
	});

	this.$('.sendGift').click(function () {
		handler.load('FB_Component_SendConversationMessage', {recipient: handler.user, tab: 'gift'}, {
			success: function() {
				this.popOut({width: 500});
			}
		});
	});

	this.$('.sendMessage').click(function () {
		handler.load('FB_Component_SendConversationMessage', {recipient: handler.user, tab: 'text'}, {
			success: function() {
				this.popOut({width: 600});
			}
		});
	});

	if (handler.image != null) {
		$('body').css({backgroundImage:'url('+handler.image+')', backgroundRepeat:'repeat'});
	} else if (handler.color != null && handler.color != '' ) {
		$('body').css({backgroundColor:handler.color, backgroundImage:'none'});
	}
},


toggleBookmark: function() {
	this.ajaxModal('toggleBookmark', {'user': this.user});
},

toggleBlock: function() {
	this.ajaxModal('toggleBlock', {'user': this.user});
},

toggleFriend: function() {
	this.ajaxModal('toggleFriend', {'user': this.user});
},

report: function() {
	this.load('FB_Component_Report', {user: this.user});
}
});
var FB_Component_ProfileDetails = SK_Component_Abstract.extend({_class:"FB_Component_ProfileDetails",
ready: function() {
	this.$('.clipSlide').clipSlide();
}
});
var FB_Component_ProfileEdit = SK_Component_Abstract.extend({_class:"FB_Component_ProfileEdit",
ready: function() {
	var handler = this;
	this.$('.tab > a').click(function() {
		handler.reload({tab: $(this).attr('rel')});
	});	
}
});
var FB_Component_ProfileMenu = SK_Component_Abstract.extend({_class:"FB_Component_ProfileMenu",
user: null,
image: null,

events: {
	'click .thumb .editPhoto': 'selectThumb'
},

selectThumb: function() {
	var handler = this;
	this.load('FB_Component_Selector_Photo', {'allowEmpty': true}, {
		success: function() {
			this.bind('select', function(photo) {
				handler.setThumb(photo);
				this.popIn();
			});
			this.popOut();
		}
	});
},

setThumb: function(photo) {
	this.ajax('setThumb', {'photo': photo}, {
		success: function(path) {
			var profileZone = cm.component('FB_Component_ProfileZone');
			if (profileZone) {
				profileZone.changeThumb(path);
			}
		}
	});
}
});
var FB_Component_ProfileZone = SK_Component_Abstract.extend({_class:"FB_Component_ProfileZone",
events: {
	'click .opener.account': 'toggleAccount',
	'click .opener.search': 'toggleSearch'
},

actions: {
	'SK_User CONNECT_REQUEST, CONNECT_UNREQUEST': function(action, model, data) {
		this.changeFriendCount(data.friendRequestCount);
	},
	'SK_Entity_ConversationMessage_Text CREATE, VIEW': function(action, model, data) {
		this.changeMessageCount(data.conversationUnreadCount);
	},
	'SK_Entity_ConversationMessage_Gift CREATE, VIEW': function(action, model, data) {
			this.changeMessageCount(data.conversationUnreadCount);
		},
	'SK_User CONNECT_REQUEST': function(action, model, data) {
		if (model.id == cm.viewer.id) {
			this.alert(action.actor, action.actor.link, action.actor.displayName+' wants to be your friend.');
		}
	},
	'SK_User CONNECT': function(action, model, data) {
		if (model.id == cm.viewer.id) {
			this.alert(action.actor, action.actor.link, action.actor.displayName+' accepted your friend request.');
		}
	},
	'SK_Entity_ConversationMessage_Text CREATE': function(action, model, data) {
		if (action.actor.id != cm.viewer.id) {
			this.alert(action.actor, model.link, action.actor.displayName+' sent you a message.');
		}
	},
	'SK_Entity_ConversationMessage_Gift CREATE': function(action, model, data) {
		if (action.actor.id != cm.viewer.id) {
			this.alert(action.actor, model.link, action.actor.displayName+' sent you a gift.');
		}
	}
},

toggleAccount: function(e) {
	var $window = $(e.currentTarget).children('.window');
	$window.toggleModal(function() {
		$(this).toggle();
		$(this).siblings('.panel').toggleClass('active');
	});
},

toggleSearch: function(e) {
	var $window = $(e.currentTarget).children('.window');
	$window.toggleModal(function() {
		$(this).toggle();
		$(this).siblings('.panel').toggleClass('active');
		if ($(this).is(':visible')) {
			$(this).find('input[type="text"]').focus();
		}
	});
},

alert: function(user, link, msg) {
	var $msg = $('<li><a href="'+link+'" class="clearfix"><img src="'+user.thumb+'" />'+msg+'</a><a class="icon close" href="javascript:;"></a></li>');
	$msg.hide().prependTo(this.$(".alerts")).slideDown(300, function() {
		$(this).delay(8000).slideUp(300, function() { $(this).remove(); });
	});
	$msg.find('.close').click(function() {
		$(this).closest('li').stop().slideUp(100, function() { $(this).remove(); });
	});
},

changeFriendCount: function(count) {
	this.$('.friend').text(count).hide();
	if (count < 1) {
		this.$('.friend.empty').show();
	} else {
		this.$('.friend:not(.empty)').show();
	}
},

changeMessageCount: function(count) {
	this.$('.message').text(count).hide();
	if (count < 1) {
		this.$('.message.empty').show();
	} else {
		this.$('.message:not(.empty)').show();
	}
},

changeThumb: function(src) {
	this.$(".profile_thumb img").attr("src", src);
}
});
var FB_Component_Rating = SK_Component_Abstract.extend({_class:"FB_Component_Rating",
avg: null,
entity: null,

ready: function(){		
	var handler = this;
	
	this.$('.buttons > *').mouseenter(function() {
		handler._show($(this).index() + 1);
	}).click(function() {
		handler.rate($(this).index() + 1);
		return false;
	});
	
	this.$('.buttons').mouseleave(function() {
		handler._show(handler.avg);
	});
},

rate: function(rate) {
	var handler = this;
	this.ajaxModal('updateRate', {score: rate, entity: this.entity}, {
		success: function() {
			handler.trigger('rate');
		}
	});
},

_show: function(rate){
	var handler = this;
	var width = this.$('.bar').width() / 5 * rate;
	this.$('.bar-display').width(width);
}
});
var FB_Component_Report = SK_Component_Abstract.extend({_class:"FB_Component_Report",
ready: function () {
	var handler = this;
	
	this.$('form.report tr:has([name=reason])').hide();
	this.$('form.report li. input').change(function() {
		if (handler.$('form.report li.reportType_value_1 input').is(':checked')) {
			handler.$('form.report tr:has([name=reason])').show();
		} else {
			handler.$('form.report tr:has([name=reason])').hide();
		}
	});
}
});
var FB_Component_ResetPassword = SK_Component_Abstract.extend({_class:"FB_Component_ResetPassword"});
var FB_Component_SearchUser = SK_Component_Abstract.extend({_class:"FB_Component_SearchUser",
ready: function() {
	var handler = this;
	handler.$('.filters > ul > li > a').each(function(i) {
		var $tr = handler.$('tr.extended:eq(' + i + ')');
		var $a = $(this);
		
		if ($tr.find(":checkbox").is(":checked")) {
			$tr.show();
			$a.closest("li").addClass('active');
		}
		
		$a.click(function() {
			$tr.toggle()
			$a.closest("li").toggleClass('active');
			if (!$tr.is(":visible")) {
				$tr.find(":checkbox").prop("checked", false);
			}
		});
	}); 
}
});
var FB_Component_SendConversationMessage = SK_Component_Abstract.extend({_class:"FB_Component_SendConversationMessage",
ready: function() {
	var handler = this;

	var $tpl_list = this.$(".gifts_list .tpl_box");
	$tpl_list.click(function() {
		$tpl_list.removeClass("selected");
		$(this).addClass("selected");
		handler.findForm("FB_Form_ConversationMessage").$("input[name=giftId]").val($(this).find(".tpl_id").text());
		handler.findForm("FB_Form_ConversationMessage").$("button").enable();
	});
	if ($tpl_list.length) {
		handler.findForm("FB_Form_ConversationMessage").$("button").disable();
	}
	
	this.$('.tab > a').click(function() {
		handler.reload({tab: $(this).attr('rel')});
	});	
}
});
var FB_Component_Share = SK_Component_Abstract.extend({_class:"FB_Component_Share",
ready: function() {
	var handler = this;
	this.$('.tab > a').click(function() {
		handler.reload({tab: $(this).attr('rel')});
	});

	_.each(this.getForms(), function(form) {
		form.bind('success', function() {
			this.reset();
			var feed = cm.component('FB_Component_Feed');
			if (feed) {
				feed.reload();
			}
		});
	});
}
});
var FB_Component_ShareSocial = SK_Component_Abstract.extend({_class:"FB_Component_ShareSocial"});
var FB_Component_SignIn = SK_Component_Abstract.extend({_class:"FB_Component_SignIn",
ready: function() {
	var handler = this;
	this.$('.forgotPW').click(function() {
		handler.load('FB_Component_ForgotPassword');
	});
	this.$('.signup').click(function() {
		handler.load('FB_Component_SignUp');
	});
}
});
var FB_Component_SignUp = SK_Component_Abstract.extend({_class:"FB_Component_SignUp"});
var FB_Component_SiteAnnouncements = SK_Component_Abstract.extend({_class:"FB_Component_SiteAnnouncements"});
var FB_Component_TagNavigator = SK_Component_Abstract.extend({_class:"FB_Component_TagNavigator"});
var FB_Component_Terms = SK_Component_Abstract.extend({_class:"FB_Component_Terms"});
var FB_Component_TextFormatter = SK_Component_Abstract.extend({_class:"FB_Component_TextFormatter",
targetName: null,

ready: function() {
	var self = this;
	var form = this.$().closest('form').get(0);
	this.$target = $(form.elements[this.targetName]);

	this.$('.control-bold').click(function(){
		self.addTag('b');
	});
	this.$('.control-italic').click(function(){
		self.addTag('i');
	});
	this.$('.control-underline').click(function(){
		self.addTag('u');
	});
	this.$('.control-quote').click(function(){
		self.addTag('q');
	});
	this.$('.control-link').click(function(){
		self.$('.control-link .panel .add').unbind().click(function(){
			var url = self.$('.control-link .panel input[name="textformatter-url"]').val();
			var label = self.$('.control-link .panel input[name="textformatter-title"]').val();
			if (!$.trim(url)) {
				$(this.url).focus();
				self.error('Please indicate URL');
				return false;
			}
			if (!$.trim(label)) {
				$(this.title).focus();
				self.error('Please indicate title');
				return false;
			}
			self.addTag('a', {content: label, attrs: {href: url}});
			self.$('.control-link .panel').toggleModalClose();
			return false;
		});
		self.$('.control-link .panel').toggleModal();
	});
	this.$('.control-emoticon').click(function(){
		self.$('.control-emoticon .panel img.smiley').unbind().click(function() {
			self.addText($(this).attr("title"));
			self.$('.control-emoticon .panel').toggleModalClose();
		});
		self.$('.control-emoticon .panel .tab a').unbind().click(function(e) {
			$(this).parent("li").addClass("active").siblings("li").removeClass("active");
			var i = $(this).parent("li").index();
			self.$('.control-emoticon .panel .section:eq('+i+')').show().siblings(".section").hide();
		});
		self.$('.control-emoticon .panel').toggleModal();
	});
	this.$('.control-image').click(function(){
		self.load('FB_Component_Selector_TextFormatterImage', {'allowEmpty': true}, {
			success: function() {
				this.bind('select', function(textformatterimage) {
					self.addTag('img', {single: true, attrs: {src: textformatterimage.url}});
					this.popIn();
				});	
				this.popOut();
			}
		});
	});
},

addTag: function(name, options) {
	options = $.extend({}, {
		content: this.$target.selection(),
		attrs: {},
		single: false
	}, options || {});
	
	var attrsStr = '';
	$.each(options.attrs, function(name, value){
		var foo = name + '="' + value + '"';
		foo = ' ' + foo;
		attrsStr = attrsStr + foo;
	});
	var tagString = options.single ? "<" + name + attrsStr + " />" : "<" + name + attrsStr + ">" + options.content + "</" + name + ">";

	this.$target.selection(tagString);
},

addText: function(text) {
	var content = this.$target.selection()
	this.$target.selection(content + text);
}
});
var FB_Component_UserPreference = SK_Component_Abstract.extend({_class:"FB_Component_UserPreference"});
var FB_Component_VideoAlbum = SK_Component_Abstract.extend({_class:"FB_Component_VideoAlbum"});
var FB_Component_VideoCategories = SK_Component_Abstract.extend({_class:"FB_Component_VideoCategories"});
var FB_Component_VideoEdit = SK_Component_Abstract.extend({_class:"FB_Component_VideoEdit",
ready: function() {
	var handler = this;
	this.findForm('FB_Form_Video').bind('success', function() {
		handler.popIn();
	});
},

registerDependency: function(component) {
	this.findForm('FB_Form_Video').bind('success', function() {
		component.reload();
	});
}
});
var FB_Component_VideoShareDetails = SK_Component_Abstract.extend({_class:"FB_Component_VideoShareDetails",
ready: function() {
	this.$('input').click(function() {
		this.select();
	});
}
});
var FB_Component_VideoView = SK_Component_Abstract.extend({_class:"FB_Component_VideoView",
video: null,

ready: function() {
	var handler = this;

	this.$(".edit").click(function() {
		handler.load('FB_Component_VideoEdit', {"video": handler.video}, {
			successPopOut: function() {
				this.registerDependency(handler);
			}
		});
	});
}
});
var FB_Component_Selector_Abstract = SK_Component_Abstract.extend({_class:"FB_Component_Selector_Abstract",
items: [],
ready: function() {
	handler = this;
	this.$('li').each(function(index, item) {
		$(this).click(function() {
			handler.trigger('select', handler.items[index]);
			return false;
		});
	});
	this.$('li .delete').each(function(index, item) {
		$(this).click(function() {
			handler.ajax('delete', {item:handler.items[index]});
			return false;
		});
	});
}
});
var FB_Component_Selector_Photo = FB_Component_Selector_Abstract.extend({_class:"FB_Component_Selector_Photo"});
var FB_Component_Selector_TextFormatterImage = FB_Component_Selector_Abstract.extend({_class:"FB_Component_Selector_TextFormatterImage"});
var FB_Component_Selector_User = FB_Component_Selector_Abstract.extend({_class:"FB_Component_Selector_User"});
var FB_Component_Selector_User_Friends = FB_Component_Selector_User.extend({_class:"FB_Component_Selector_User_Friends"});
var FB_Component_Selector_User_FriendsVisible = FB_Component_Selector_User.extend({_class:"FB_Component_Selector_User_FriendsVisible"});
var FB_Component_ConversationMessageList_Abstract = SK_Component_Abstract.extend({_class:"FB_Component_ConversationMessageList_Abstract",
ready: function() {
	$(document).scrollTo(this.$(".conversations > *:last-child"));
}
});
var FB_Component_ConversationMessageList_Conversation = FB_Component_ConversationMessageList_Abstract.extend({_class:"FB_Component_ConversationMessageList_Conversation"});
var FB_Component_MenuContext_Entity = FB_Component_MenuContext.extend({_class:"FB_Component_MenuContext_Entity",
entity: null,

events: {
	'click .report': 'report',
	'click .block': 'block'
},

report: function() {
	this.load('FB_Component_Report', {entity: this.entity});
},

block: function() {
	var handler = this;
	this.ajax('blockUser', {entity: this.entity}, {
		success: function() {
			handler.getParent().reload();
		}
	});
}
});
var FB_Component_BlogpostList_Abstract = SK_Component_Abstract.extend({_class:"FB_Component_BlogpostList_Abstract",
ready: function() {
	var handler = this;

	this.$(".add").click(function() {
		handler.load('FB_Component_BlogpostEdit');
	});

	this.$(".blogpost .edit").click(function() {
		var blogpostId = $(this).closest(".blogpost").data("blogpost-id");
		handler.load('FB_Component_BlogpostEdit', {"blogpost": blogpostId}, {
			successPopOut: function() {
				this.registerDependency(handler);
			}
		});
		return false;
	});

	this.$(".blogpost .delete").click(function() {
		var blogpostId = $(this).closest(".blogpost").data("blogpost-id");
		cm.ui.confirm('Are you sure you want to delete this post?', function() {
			handler.ajax('delete', {"blogpost": blogpostId});
		});
		return false;
	});
}
});
var FB_Component_BlogpostList_Latest = FB_Component_BlogpostList_Abstract.extend({_class:"FB_Component_BlogpostList_Latest"});
var FB_Component_BlogpostList_MostDiscussed = FB_Component_BlogpostList_Abstract.extend({_class:"FB_Component_BlogpostList_MostDiscussed"});
var FB_Component_BlogpostList_MostViewed = FB_Component_BlogpostList_Abstract.extend({_class:"FB_Component_BlogpostList_MostViewed"});
var FB_Component_BlogpostList_Tag = FB_Component_BlogpostList_Abstract.extend({_class:"FB_Component_BlogpostList_Tag"});
var FB_Component_BlogpostList_TopRated = FB_Component_BlogpostList_Abstract.extend({_class:"FB_Component_BlogpostList_TopRated"});
var FB_Component_BlogpostList_User = FB_Component_BlogpostList_Abstract.extend({_class:"FB_Component_BlogpostList_User"});
var FB_Component_VideoList_Abstract = SK_Component_Abstract.extend({_class:"FB_Component_VideoList_Abstract",
ready: function() {
	var handler = this;
	
	this.$(".add").click(function() {
		handler.load('FB_Component_VideoEdit');
	});
	
	this.$(".video .edit").click(function() {
		var videoId = $(this).closest(".video").data("video-id");
		handler.load('FB_Component_VideoEdit', {"video": videoId}, {
			successPopOut: function() {
				this.registerDependency(handler);
			}
		});
		return false;
	});
	
	this.$(".video .delete").click(function() {
		var videoId = $(this).closest(".video").data("video-id");
		cm.ui.confirm('Are you sure you want to delete this video?', function() {
			handler.ajax('delete', {"video": videoId});
		});
		return false;
	});
}
});
var FB_Component_VideoList_Category = FB_Component_VideoList_Abstract.extend({_class:"FB_Component_VideoList_Category"});
var FB_Component_VideoList_Latest = FB_Component_VideoList_Abstract.extend({_class:"FB_Component_VideoList_Latest"});
var FB_Component_VideoList_MostDiscussed = FB_Component_VideoList_Abstract.extend({_class:"FB_Component_VideoList_MostDiscussed"});
var FB_Component_VideoList_MostViewed = FB_Component_VideoList_Abstract.extend({_class:"FB_Component_VideoList_MostViewed"});
var FB_Component_VideoList_Tag = FB_Component_VideoList_Abstract.extend({_class:"FB_Component_VideoList_Tag"});
var FB_Component_VideoList_TopRated = FB_Component_VideoList_Abstract.extend({_class:"FB_Component_VideoList_TopRated"});
var FB_Component_VideoList_User = FB_Component_VideoList_Abstract.extend({_class:"FB_Component_VideoList_User"});
var FB_Component_PhotoList_Abstract = SK_Component_Abstract.extend({_class:"FB_Component_PhotoList_Abstract",
ready: function() {
	var handler = this;
	
	this.$(".photo").hover(function() {
		$(this).find(".details").show();
	}, function() {
		$(this).find(".details").hide();
	});
	
	this.$(".photo a:has(img)").on('click-no-meta', function(e) {
		var photoId = $(this).closest(".photo").data("photo-id");
		handler.show(photoId);
		e.preventDefault();
	});
	
	this.$(".photo .delete").click(function() {
		var photoId = $(this).closest(".photo").data("photo-id");
		handler.ajax('delete', {'photo': photoId});
	});
},

show: function(photo) {
	this.load('FB_Component_PhotoList_UserGallery', {'photo':photo});
}
});
var FB_Component_PhotoList_Latest = FB_Component_PhotoList_Abstract.extend({_class:"FB_Component_PhotoList_Latest"});
var FB_Component_PhotoList_MostDiscussed = FB_Component_PhotoList_Abstract.extend({_class:"FB_Component_PhotoList_MostDiscussed"});
var FB_Component_PhotoList_MostViewed = FB_Component_PhotoList_Abstract.extend({_class:"FB_Component_PhotoList_MostViewed"});
var FB_Component_PhotoList_TopRated = FB_Component_PhotoList_Abstract.extend({_class:"FB_Component_PhotoList_TopRated"});
var FB_Component_PhotoList_User = FB_Component_PhotoList_Abstract.extend({_class:"FB_Component_PhotoList_User"});
var FB_Component_PhotoList_UserGallery = FB_Component_PhotoList_Abstract.extend({_class:"FB_Component_PhotoList_UserGallery",
photo: null,
carousel: null,

ready: function() {
	var handler = this;

	var i = this.$('#carousel li[data-photo-id='+this.photo.id+']').index();
	this.$('#carousel').carousel({
		start: i,
		itemWidth: 54,
		height: 54,
		onSelect: function() {
			handler.findChild('FB_Component_PhotoView').reload({photo: $(this).data('photo-id')});
		}
	});
	this.carousel = this.$('#carousel').data('carousel');

	if (this.carousel) {
		this.$('#carousel li').on('click-no-meta', function(e) {
			var i = $(this).index();
			handler.carousel.select(i);
			e.preventDefault();
		});
	}
},

repaint: function() {
	if (this.carousel) {
		this.carousel.repaint();
	}
},

show: function(photo) {
	if (this.carousel) {
		var i = this.$('#carousel li[data-photo-id=' + photo.id + ']').index();
		this.carousel.select(i);
	}
},

next: function() {
	if (this.carousel) {
		this.carousel.next();
	}
},

prev: function() {
	if (this.carousel) {
		this.carousel.prev();
	}
}
});
var FB_Component_ConversationList_Abstract = SK_Component_Abstract.extend({_class:"FB_Component_ConversationList_Abstract"});
var FB_Component_ConversationList_Mailbox = FB_Component_ConversationList_Abstract.extend({_class:"FB_Component_ConversationList_Mailbox"});
var FB_Component_UserList_Abstract = SK_Component_Abstract.extend({_class:"FB_Component_UserList_Abstract"});
var FB_Component_UserList_Bookmarks = FB_Component_UserList_Abstract.extend({_class:"FB_Component_UserList_Bookmarks"});
var FB_Component_UserList_Friends = FB_Component_UserList_Abstract.extend({_class:"FB_Component_UserList_Friends"});
var FB_Component_UserList_FriendsVisible = FB_Component_UserList_Abstract.extend({_class:"FB_Component_UserList_FriendsVisible",
events: {
	'click .icon.chat': '_openChat'
},

actions: {
	'SK_User VISIBLE': function(action, model, data) {
		if (action.actor.id != cm.viewer.id && data.isFriend) {
			this._addContact(action.actor);
		}
	},
	'SK_User INVISIBLE, DELETE': function(action, model, data) {
		if (action.actor.id != cm.viewer.id) {
			this._deleteContact(action.actor);
		}
	},
	'SK_User CONNECT': function(action, model, data) {
		var user = (action.actor.id == cm.viewer.id) ? model : action.actor;
		if (user.visible) {
			this._addContact(user);
		}
	},
	'SK_User DISCONNECT': function(action, model, data) {
		var user = (action.actor.id == cm.viewer.id) ? model : action.actor;
		this._deleteContact(user);
	}
},

_openChat: function(e) {
	var userId = $(e.currentTarget).closest('.user').attr('data-user-id');
	cm.component('FB_Component_Chat').requestChat(userId);
},

/**
 * @param SK_User user
 */
_addContact: function(user) {
	if (this.$('.user[data-user-id=' + user.id + ']').length) {
		return;
	}
	this.$('.no_content').hide();
	var $user = this.$('.user.template').clone().removeClass('template');
	$user.attr('data-user-id', user.id);
	$user.find('.link').attr('href', user.link);
	$user.find('.thumb').attr('src', user.thumb);
	$user.find('.username').text(user.displayName);
	$user.prependTo(this.$('.users')).slideDown();
},

/**
 * @param SK_User user
 */
_deleteContact: function(user) {
	var handler = this;
	var $user = this.$('.user[data-user-id=' + user.id + ']');
	$user.slideUp(function() {
		$(this).remove();
		if (0 == handler.$('.user:visible').length) {
			handler.$('.no_content').show();
		}
	});
}
});
var FB_Component_UserList_Latest = FB_Component_UserList_Abstract.extend({_class:"FB_Component_UserList_Latest"});
var FB_Component_UserList_Matches = FB_Component_UserList_Abstract.extend({_class:"FB_Component_UserList_Matches"});
var FB_Component_UserList_Online = FB_Component_UserList_Abstract.extend({_class:"FB_Component_UserList_Online"});
var FB_Component_UserList_Search = FB_Component_UserList_Abstract.extend({_class:"FB_Component_UserList_Search"});
var FB_Component_UserList_ViewHistory = FB_Component_UserList_Abstract.extend({_class:"FB_Component_UserList_ViewHistory"});
var CM_Form_Abstract = CM_Renderable_Abstract.extend({_class:"CM_Form_Abstract",
_fields: {},

initialize: function() {
	this._fields = {};
	_.each(this.options.fields, function(fieldInfo, name) {
		// Lazy construct
		var $field = this.$("#"+name);
		if ($field.length) {
			var fieldClass = window[fieldInfo.className];
			this._fields[name] = new fieldClass({"el": $field, "form": this, "name": name, "options": fieldInfo.options});
		}
	}, this);
	_.each(this._fields, function(field, name) {
		field.ready();
	}, this);
	
	var handler = this;
	
	_.each(this.options.actions, function(action, name) {
		var $btn = $('#'+this.getAutoId()+'-'+name+'-button');
		$btn.on('click', {action: name}, function(event) {
			handler.submit(event.data.action);
			return false;
		});
	}, this);

	if (this.options.default_action) {
		this.$().submit(function(event) {
			handler.submit(handler.default_action);
			return false;
		});
	}
	
	this.getComponent().registerForm(this);
},


ready: function() {
},


_ready: function() {
	this.ready();
	this.trigger('ready');
},

/**
 * @return string
 */
getAutoId: function() {
	return this.options.autoId;
},

/**
 * @return CM_Component
 */
getComponent: function() {
	return this.options.component;
},

/**
 * @return CM_FormField_Abstract|null
 */
getField: function(name) {
	if (!this._fields[name]) {
		return null;
	}
	return this._fields[name];
},

/**
 * @return jQuery
 */
$: function(selector) {
	if (!selector) {
		return this.$el;
	}
	selector = selector.replace('#', '#'+this.getAutoId()+'-');
	return $(selector, this.el);
},

collectData: function(action_name) {
	var form_data = this.$().serializeArray();
	var action = this.options.actions[action_name];

	var data = {};
	var regex = /^([\w\-]+)(\[([^\]]+)?\])?(\[([^\]]+)?\])?/i;
	var name, match, key;

	for (var i = 0, item; item = form_data[i]; i++) {
		// parsing html name
		match = regex.exec(item.name);
		name = match[1];
		item.value = item.value || '';

		if (typeof action.fields[name] == 'undefined') {
			continue;
		}

		if (match[2]) {
			if (match[2] == '[]') {
				if (data[name] === undefined) {
					data[name] = [];
				}
				data[name].push(item.value);
			} else if (match[3]) {
				key = match[3];
				if (typeof data[name] == 'undefined') {
					data[name] = {length: 0};
				}

				data[name].length++;

				//second brackets
				if (match[4] == '[]') {
					if (data[name][key] === undefined) {
						data[name][key] = [];
					}
					data[name][key].push(item.value);
				} else if (match[5]) {
					var sub_key = match[5];
					if (typeof data[name][key] == 'undefined') {
						data[name][key] = {length: 0};
					}
					data[name][key][sub_key] = item.value;
				} else {
					data[name][key] = item.value;
				}

			}
		} else { // if there are no brackets
			data[name] = item.value;
		}
	}

	var errors = [];
	var field, required;

	var focusSet = false;
	for (key in action.fields) {
		required = action.fields[key];
		field = this._fields[key];

		if ( data[key] && (data[key].length !== 0) ) {
			try {
				field.validate(data[key], required);
			} catch (e) {
				var err_msg = cm.language.get('%forms._errors.illegal_value');
				if (e.message) {
					err_msg = e.message;
				}
				errors.push({msg: err_msg, key: key});
			}
		} else if (required) {
			var err_msg = 'Required';
			var $labels = $('label[for="' +this.getAutoId()+ '-' +key+ '-input"]');
			if ($labels.length) {
				err_msg = cm.language.get('%forms._errors.required', {label:$labels.eq(0).text()});
			}
			errors.push({msg: err_msg, key: key});
		}

		if (data[key] && data[key].length !== undefined) {
			delete(data[key]['length']);
		}
	}

	if (errors.length) {
		for (var i = errors.length-1, err; err = errors[i]; i--) {
			this.error(err.msg, err.key);
		}
		return false;
	}
	return data;
},

submit: function(action_name, confirmed, data, callbacks) {
	confirmed = confirmed || false;
	callbacks = callbacks || {};
	action_name = action_name || this.options.default_action;
	
	if (!confirmed) {
		$('.form_field_error', this.$())
			.next('br').remove()
			.andSelf().remove();
	}

	data = data || this.collectData(action_name);

	if (data) {
		var handler = this;
		if (this.options.actions[action_name].confirm_msg && !confirmed) {
			cm.ui.confirm(cm.language.get(this.options.actions[action_name].confirm_msg), function() {
				handler.submit(action_name, true, data);
			});
			return false;
		}
		
		this.disable()
		this.trigger('submit', [data]);
		cm.ajax('form', {component:this.getComponent()._getArray(), className:this._class, actionName:action_name, data:data}, {
			success: function(response) {
				if (response.errors) {
					for (var i = response.errors.length-1, error; error = response.errors[i]; i--) {
						if (error.constructor == Array) {
							handler.error(error[0], error[1]);
						} else {
							handler.error(error);
						}
					}
					handler.trigger('error');
				}

				if (response.exec) {
					handler.evaluation = new Function(response.exec);
					handler.evaluation();
				}
					
				if (callbacks.success) {
					callbacks.success(response.data);
				}
				
				if (response.messages) {
					for (var i = 0, msg; msg = response.messages[i]; i++) {
						handler.message(msg);
					}
				}

				if (!response.errors) {
					handler.trigger('success', [response.data]);
				}
			},
			complete: function() {
				handler.enable();
				handler.trigger('complete');
			}
		});
	}
},

reset: function() {
	this.$().get(0).reset();
},

disable: function() {
	this.$().disable();
},

enable: function() {
	this.$().enable();
},

error: function(message, field_key) {
	var field = field_key ? this.getField(field_key) : null;
	if (field) {
		var $container = field.$('.messages');
		$container.html("");

		if (message) {
			$container.append('<div class="form_field_error" style="display:none"></div><br clear="all" />')
			.children('.form_field_error').html(message).fadeIn('fast');
			
			this.getField(field_key).$('input, select, textarea').focus();
		}
	} else {
		cm.window.hint(message);
	}
},

message: function(message) {
	cm.window.hint(message);
}
});
var FB_Form_Blogpost = CM_Form_Abstract.extend({_class:"FB_Form_Blogpost"});
var FB_Form_ChangeBackground = CM_Form_Abstract.extend({_class:"FB_Form_ChangeBackground"});
var FB_Form_ChangePassword = CM_Form_Abstract.extend({_class:"FB_Form_ChangePassword"});
var FB_Form_CommentAdd = CM_Form_Abstract.extend({_class:"FB_Form_CommentAdd"});
var FB_Form_ConversationMessage = CM_Form_Abstract.extend({_class:"FB_Form_ConversationMessage"});
var FB_Form_EmailVerify = CM_Form_Abstract.extend({_class:"FB_Form_EmailVerify"});
var FB_Form_Example = CM_Form_Abstract.extend({_class:"FB_Form_Example"});
var FB_Form_ForgotPassword = CM_Form_Abstract.extend({_class:"FB_Form_ForgotPassword"});
var FB_Form_FriendRequest = CM_Form_Abstract.extend({_class:"FB_Form_FriendRequest"});
var FB_Form_InviteFriends = CM_Form_Abstract.extend({_class:"FB_Form_InviteFriends"});
var FB_Form_MemberFeedback = CM_Form_Abstract.extend({_class:"FB_Form_MemberFeedback"});
var FB_Form_PaymentSelection = CM_Form_Abstract.extend({_class:"FB_Form_PaymentSelection"});
var FB_Form_PhotoEdit = CM_Form_Abstract.extend({_class:"FB_Form_PhotoEdit"});
var FB_Form_PhotoUpload = CM_Form_Abstract.extend({_class:"FB_Form_PhotoUpload",
ready: function() {
	var form = this;
	this.getField("photo").bind("uploadComplete", function() {
		form.submit();
	});
}
});
var FB_Form_ProfileEditMatch = CM_Form_Abstract.extend({_class:"FB_Form_ProfileEditMatch"});
var FB_Form_ProfileEditMe = CM_Form_Abstract.extend({_class:"FB_Form_ProfileEditMe"});
var FB_Form_ProfileStatus = CM_Form_Abstract.extend({_class:"FB_Form_ProfileStatus"});
var FB_Form_Report = CM_Form_Abstract.extend({_class:"FB_Form_Report"});
var FB_Form_ResetPassword = CM_Form_Abstract.extend({_class:"FB_Form_ResetPassword"});
var FB_Form_SearchContent = CM_Form_Abstract.extend({_class:"FB_Form_SearchContent"});
var FB_Form_SearchProfile = CM_Form_Abstract.extend({_class:"FB_Form_SearchProfile"});
var FB_Form_SignIn = CM_Form_Abstract.extend({_class:"FB_Form_SignIn"});
var FB_Form_SignUp = CM_Form_Abstract.extend({_class:"FB_Form_SignUp"});
var FB_Form_TextFormatterImage = CM_Form_Abstract.extend({_class:"FB_Form_TextFormatterImage",
ready: function() {
	var form = this;
	this.getField("image").bind("uploadComplete", function() {
		form.submit();
	});
}
});
var FB_Form_UnregisterProfile = CM_Form_Abstract.extend({_class:"FB_Form_UnregisterProfile"});
var FB_Form_UserPreference = CM_Form_Abstract.extend({_class:"FB_Form_UserPreference"});
var FB_Form_Video = CM_Form_Abstract.extend({_class:"FB_Form_Video"});
var FB_Component_About = SK_Component_Abstract.extend({_class:"FB_Component_About"});
var CM_FormField_Abstract = CM_Renderable_Abstract.extend({_class:"CM_FormField_Abstract",
ready: function() {
},

/**
 * @param mixed value
 * @param boolean required
 */
validate: function(value, required) {
},

/**
 * @return CM_Form_Abstract
 */
getForm: function() {
	return this.options.form;
},

/**
 * @return jQuery
 */
$: function(selector) {
	if (!selector) {
		return this.$el;
	}
	return $(selector, this.el);
},

/**
 * @return string
 */
getName: function() {
	return this.options.name;
},

/**
 * @return object
 */
getOptions: function() {
	return this.options.options;
},

/**
 * @param string name
 * @return mixed|null
 */
getOption: function(name) {
	var options = this.getOptions();
	if (!options[name]) {
		return null;
	}
	return options[name];
},

/**
 * @param string message
 */
error: function(message) {
	this.getForm().error(message, this.getName());
}
});
var SK_FormField_AgeRange = CM_FormField_Abstract.extend({_class:"SK_FormField_AgeRange"});
var CM_FormField_Text = CM_FormField_Abstract.extend({_class:"CM_FormField_Text"});
var CM_FormField_Textarea = CM_FormField_Text.extend({_class:"CM_FormField_Textarea"});
var CM_FormField_File = CM_FormField_Abstract.extend({_class:"CM_FormField_File",
fileUploader: null,

ready: function() {
	var field = this;
	
	this.fileUploader = new qq.FileUploader({
		element: field.$(".file-uploader").get(0),
		action: "/upload/" + cm.options.siteId + "/?field=" + field._class,
		multiple: !field.getOption("cardinality") || field.getOption("cardinality") > 1,
		allowedExtensions: field.getOption("allowedExtensions"),
		template: field.$(".file-uploader").html(),
		fileTemplate: $.trim(field.$(".previewsTemplate").html()),
		listElement: field.$(".previews").get(0),
		onComplete: function(id, fileName, response) {
			var $item = field.$(".previews").children().filter(function(i) {
				return this.qqFileId == id;
			});
			if (response.success) {
				this.showMessage(null);
				while (field.getOption("cardinality") && field.getOption("cardinality") < field.getCountUploaded()) {
					$item.siblings().first().remove();
				}
				$item.html(response.success.preview + "<input type=\"hidden\" name=\"" +field.getName()+ "[]\" value=\"" +response.success.id+ "\"/>");
			} else {
				$item.remove();	
			}
			if (field.fileUploader.getInProgress() == 0 && field.getCountUploaded() > 0) {
				field.trigger("uploadComplete");
			}
		},
		showMessage: function(message) {
			if (message && message.msg) {
				message = message.msg;
			}
			field.error(message);
		}
	});
	
	this.$(".previews").on("click", ".delete", function() {
		$(this).parent("li").remove();
	});
},

getCountUploaded: function() {
	return this.$(".previews .qq-upload-success").length;
}
});
var CM_FormField_Select = CM_FormField_Abstract.extend({_class:"CM_FormField_Select"});
var CM_FormField_Set = CM_FormField_Abstract.extend({_class:"CM_FormField_Set"});
var SK_FormField_Username = CM_FormField_Text.extend({_class:"SK_FormField_Username"});
var CM_FormField_Suggest = CM_FormField_Abstract.extend({_class:"CM_FormField_Suggest",
ready: function() {
	var field = this;
	var prePopulate = this.$(".prePopulate");
	prePopulate = prePopulate.length ? JSON.parse(prePopulate.val()) : null;
	var $input = this.$('input[type="text"]');
	$input.tokenInput(
		function (query, handle_results) {
			cm.rpc(field._class + ".suggest", [query, field.getOptions()], {
				success: function(results) {
					handle_results(query, results);
				}
			});
		},{
		resultsFormatter: function(item){
			var output = "<p>" + item.name + "</p>";
			if (item.description) {
				output += "<small>" + item.description + "</small>";
			}
			if (item.img) {
				output = "<img src=\"" + item.img + "\" />" + output;
			}
			return "<li>" + output + "</li>";
		},
		tokenFormatter: function(item) {
			var output = "<p>" + item.name + "</p>";
			if (item.img) {
				output = "<img src=\"" + item.img + "\" />" + output;
			}					
			return "<li>" + output + "</li>";
		},
		onAdd: function(item) {
			field.onAdd(item);
			field.onChange($input.tokenInput("get"));
		},
		onDelete: function(item) {
			field.onDelete(item);
			field.onChange($input.tokenInput("get"));
		},
		animateDropdown: false,
		preventDuplicates: true,
		hintText: null,
		searchDelay: 0,
		tokenLimit: field.getOption("cardinality"),
		prePopulate: prePopulate
	});
	this.getForm().$().bind("reset", function() {
		$input.tokenInput("clear");
	});
	if (this.getOption("cardinality") == 1) {
		this.$(".token-input-list").click(function() {
			$input.tokenInput("clear");
		});
	}
	this.onChange($input.tokenInput("get"));
},

onAdd: function(item) {
},

onDelete: function(item) {
},

onChange: function(items) {
}
});
var SK_FormField_UsernameSearch = CM_FormField_Suggest.extend({_class:"SK_FormField_UsernameSearch",
onAdd: function(item) {
	location.href = item.link;
	this.getForm().disable();
}
});
var FB_Component_BlogpostEdit = SK_Component_Abstract.extend({_class:"FB_Component_BlogpostEdit",
ready: function() {
	var handler = this;
	this.findForm('FB_Form_Blogpost').bind('success', function() {
		handler.popIn();
	});
},

registerDependency: function(component) {
	this.findForm('FB_Form_Blogpost').bind('success', function() {
		component.reload();
	});
}
});
var FB_Component_Account = SK_Component_Abstract.extend({_class:"FB_Component_Account",
ready: function() {
	var handler = this;

	this.$('.changePassword').click(function() {
		handler.load('FB_Component_ChangePassword');
	});
}
});
var CM_Component_Debug = CM_Component_Abstract.extend({_class:"CM_Component_Debug",
ready: function() {
	var handler = this;
	this.$('.buttons > a').each(function() {
		$(this).click(function() {
			var name = $(this).attr('class');
			handler.$('.containers div:not(.' + name + ')').hide();
			handler.$('.containers div.' + name).toggle();
		});
	});
	this.$('.clearCache').click(function() {
		handler.ajax('clearCache', {
			'CM_Cache': handler.$('#CM_Cache').is(':checked'),
			'CM_CacheLocal': handler.$('#CM_CacheLocal').is(':checked')
		}, {
			success: function() {
				location.reload();
			}
		});
	});

	_.each(cm.model.types, function(modelType, modelName) {
		_.each(cm.action.types, function(actionType, actionName) {
			handler.bindAction(actionType, modelType, function(action, model, data) {
				var msg = "ACTION: <[ACTOR:" + (action.actor ? action.actor.id : null) + "] , " + actionName + " , " + "[" + modelName + ":" + JSON.stringify(model._id) + "]>";
				msg += " (" + JSON.stringify(data) + ")";
				handler.alert(msg);
			});
		});
	});
},

alert: function(msg) {
	var $msg = $("<li>" + msg + "</li>");
	this.$(".alerts").append($msg);
	$msg.delay(8000).slideUp(200, function() {
		$(this).remove();
	});
}
});
var CM_Component_Example = CM_Component_Abstract.extend({_class:"CM_Component_Example",
uname: null,
profile: null,
pingCount: 0,

events: {
	'click .reload': 'reloadChinese',
	'click .remove': 'myRemove',
	'click .popout': 'popOut',
	'click .popin': 'popIn',
	'click .load': 'loadExample',
	'click .load_callback': 'loadExampleInline',
	'click .call': 'callAjax',
	'click .rpc': 'callRpc',
	'click .error_500_text_callback': 'error_500_text_callback',
	'click .error_599_text': 'error_599_text',
	'click .error_CM_Exception_public_callback': 'error_CM_Exception_public_callback',
	'click .error_CM_Exception_public': 'error_CM_Exception_public',
	'click .error_CM_Exception': 'error_CM_Exception',
	'click .error_CM_Exception_AuthRequired_public_callback': 'error_CM_Exception_AuthRequired_public_callback',
	'click .error_CM_Exception_AuthRequired_public': 'error_CM_Exception_AuthRequired_public',
	'click .error_CM_Exception_AuthRequired': 'error_CM_Exception_AuthRequired',
	'click .stream .ping': 'ping'
},

streams: {
	'ping': function(response) {
		this.$('.stream .output').append(response.number + ': ' + response.message + '<br />').scrollBottom();
	}
},

ready: function() {
	this.message("Component ready, uname: " + this.uname);
},

reloadChinese: function() {
	this.reload({foo:'some chinese.. 百度一下，你就知道 繁體字!'});
},

myRemove: function() {
	this.remove();
},

loadExample: function() {
	this.load('CM_Component_Example', {foo:'value2'});
},

loadExampleInline: function() {
	var handler = this;
	this.load('CM_Component_Example', {foo:'value3'}, {
		success: function() {
			this.$().hide()
				.insertBefore(handler.$())
				.slideDown(600);
		}
	});
},

callAjax: function() {
	this.ajax('test', {x:'myX'}, {
		success: function(data) {
			this.message('ajax_test(): ' + data);
		}
	});
},

callRpc: function() {
	cm.rpc('CM_Component_Example.time', [], {
		success: function(timestamp) {
			cm.window.hint("Time: "+timestamp);
		}
	});
},

error_500_text_callback: function() {
	this.ajax('error', {status:500, text:'Errortext'}, {
		error: function(msg, type) {
			this.error('callback( type:' + type + ', msg:' + msg + ' )');
			return false;
		}
	});
},
error_599_text: function() {
	this.ajax('error', {status:599, text:'Errortext'});
},
error_CM_Exception_public_callback: function() {
	this.ajax('error', {exception:'CM_Exception', text:'Errortext', 'public':true}, {
		error: function(msg, type) {
			this.error('callback( type:' + type + ', msg:' + msg + ' )');
			return false;
		}
	});
},
error_CM_Exception_public: function() {
	this.ajax('error', {exception:'CM_Exception', text:'Errortext', 'public':true});
},
error_CM_Exception: function() {
	this.ajax('error', {exception:'CM_Exception', text:'Errortext'});
},
error_CM_Exception_AuthRequired_public_callback: function() {
	this.ajax('error', {exception:'CM_Exception_AuthRequired', text:'Errortext', 'public':true}, {
		error: function(msg, type) {
			this.error('callback( type:' + type + ', msg:' + msg + ' )');
			return false;
		}
	});
},
error_CM_Exception_AuthRequired_public: function() {
	this.ajax('error', {exception:'CM_Exception_AuthRequired', text:'Errortext', 'public':true});
},
error_CM_Exception_AuthRequired: function() {
	this.ajax('error', {exception:'CM_Exception_AuthRequired', text:'Errortext'});
},

ping: function() {
	this.ajax('ping', {number: this.pingCount});
	this.pingCount++;
}
});
var CM_FormField_Date = CM_FormField_Abstract.extend({_class:"CM_FormField_Date"});
var CM_FormField_Boolean = CM_FormField_Abstract.extend({_class:"CM_FormField_Boolean"});
var CM_FormField_Captcha = CM_FormField_Abstract.extend({_class:"CM_FormField_Captcha",
ready: function() {
	var field = this;
	this.$(".captcha_container:eq(0)").find(".reload").click(function(){
		field.refresh();
	});
	this.getForm().bind("error", function(){
		field.refresh();
	});
	
},

refresh: function(){
	var field = this;
	cm.rpc(this._class + ".createNumber", [], {
		success: function(id) {
			var $container = field.$(".captcha_container:eq(0)");
			var $img = $container.find("img");
			$img.attr("src", $img.attr("src").replace(/\?[^\?]+$/, '?id=' + id));
			$container.find("input[name=\'captcha[id]\']").val(id);
			$container.find("input[name=\'captcha[value]\']").val("").focus();
		}
	});
}
});
var CM_FormField_Color = CM_FormField_Abstract.extend({_class:"CM_FormField_Color",
ready: function() {
	/*
	 * mColorPicker's "replace" option cannot be set to false here (too late)
	 * Set it within mColorPicker!
	 */
	$.fn.mColorPicker.init.replace = false;
	$.fn.mColorPicker.init.allowTransparency = false;
	$.fn.mColorPicker.init.showLogo = false;
	$.fn.mColorPicker.init.enhancedSwatches = false;
	this.$('input').mColorPicker({
		"imageFolder": cm.options.urlStatic + "img/jquery.mColorPicker/"
	});
	$("#mColorPickerFooter").remove();
	$("#mColorPicker").height(158);
}
});
var SK_FormField_Age = CM_FormField_Date.extend({_class:"SK_FormField_Age"});
var CM_FormField_Integer = CM_FormField_Abstract.extend({_class:"CM_FormField_Integer",
ready: function() {
	var field = this;
	var $slider = this.$(".slider");
	var $input = this.$("input");
	$slider.slider({
		value: $input.val(),
		min: field.getOption("min"),
		max: field.getOption("max"),
		step: field.getOption("step"),
		slide: function(event, ui) {
			$input.val(ui.value);
			$(this).children(".ui-slider-handle").text(ui.value);
		},
		change: function(event, ui) {
			$input.val(ui.value);
			$(this).children(".ui-slider-handle").text(ui.value);
		}
	});
	$slider.children(".ui-slider-handle").text($input.val());
	$input.watch("disabled", function (propName, oldVal, newVal) {
		$slider.slider("option", "disabled", newVal);
		$slider.toggleClass("disabled", newVal);
	});
	$input.changetext(function() {
		$slider.slider("option", "value", $(this).val());
	});
}
});
var CM_FormField_Email = CM_FormField_Text.extend({_class:"CM_FormField_Email"});
var CM_FormField_FileImage = CM_FormField_File.extend({_class:"CM_FormField_FileImage"});
var SK_FormField_PhotoUpload = CM_FormField_FileImage.extend({_class:"SK_FormField_PhotoUpload"});
var CM_FormField_Hidden = CM_FormField_Abstract.extend({_class:"CM_FormField_Hidden"});
var CM_FormField_Distance = CM_FormField_Integer.extend({_class:"CM_FormField_Distance"});
var CM_FormField_SuggestOne = CM_FormField_Suggest.extend({_class:"CM_FormField_SuggestOne"});
var CM_FormField_Password = CM_FormField_Text.extend({_class:"CM_FormField_Password"});
var SK_FormField_Sex = CM_FormField_Select.extend({_class:"SK_FormField_Sex"});
var SK_FormField_SexSet = CM_FormField_Set.extend({_class:"SK_FormField_SexSet"});
var SK_FormField_UsernameFriends = CM_FormField_Suggest.extend({_class:"SK_FormField_UsernameFriends"});
var CM_FormField_Location = CM_FormField_SuggestOne.extend({_class:"CM_FormField_Location",
getDistanceField: function() {
	if (!this.getOption("distanceName")) {
		return null;
	}
	return this.getForm().getField(this.getOption("distanceName"));
},

onChange: function(items) {
	if (this.getDistanceField()) {
		var distanceEnabled = false;
		if (items.length > 0) {
			distanceEnabled = items[0].id.split(".")[0] >= this.getOption("distanceLevelMin");
		}
		this.getDistanceField().$("input").prop("disabled", !distanceEnabled);
	}
}
});
var CM_FormField_Tags = CM_FormField_Textarea.extend({_class:"CM_FormField_Tags"});
var SK_FormField_Embedvideo = CM_FormField_Textarea.extend({_class:"SK_FormField_Embedvideo"});
var SK_FormField_EmailList = CM_FormField_Textarea.extend({_class:"SK_FormField_EmailList"});
var FB_Form_Account = CM_Form_Abstract.extend({_class:"FB_Form_Account"});
var CM_Form_Example = CM_Form_Abstract.extend({_class:"CM_Form_Example"});

