(function() {

var Events = 'ajaxStart, ajaxStop, ajaxComplete, ajaxError, ajaxSuccess, ajaxSend, blur, focus, load, resize, scroll, unload, beforeunload, click, dblclick, mousedown, mouseup, mousemove, mouseover, mouseout, mouseenter, mouseleave, change, select, submit, keydown, keypress, keyup, error, liveupdate'.split(', ');
var components = [];
var boundComponents = [];

function is(dom, component) {
	return dom.is(component.selector);
}

function get(dom, component) {
	if (is(dom, component)) {
		return dom.filter(component.selector);
	}
	var results = dom;
	if ((results = dom.find(component.selector)).length) {
		return results;
	}
	if ((results = dom.parents(component.selector)).length) {
		return results;
	}
	return dom.parent().length ? dom.siblings(component.selector) : [];
}

function create(id, meta, parent, ancestor, model) {
	if (!meta || typeof meta.selector != 'string') {
		return;
	}
	var component = {
		id:             id,
		selector:       meta.selector,
		events:         {},
		eventCount:     0,
		parent:         parent,
		ancestor:       ancestor,
		children:       [],
		descendants:    []
	};
	$.each(meta, function(key, value) {
		if (key == 'children' || key == 'descendants') {
			$.each(value, function(subID, subMeta) {
				if (!subMeta) {
					return;
				}
				var sub = (key == 'children' ? create(subID, subMeta, component, null, model) : create(subID, subMeta, null, component, model));
				if (sub) {
					component[key].push(sub);
				}
			});
		} else if ($.inArray(key, Events) >= 0) {
			if (typeof value == 'string' && model) {
				value = model[value];
			}
			if ($.isFunction(value)) {
				component.events[key] = value;
				component.eventCount++;
			}
		}
	});
	createHelpers(component);
	return component;
}

function createHelpers(component) {
	$.fn['is' + component.id] = function() {
		return is(this, component);
	};
	$.fn['get' + component.id] = function() {
		return get(this, component);
	};
}

function hasEvents(component) {
	if (component.eventCount) {
		return true;
	}
	for (var i = 0; i < component.children.length; i++) {
		if (hasEvents(component.children[i])) {
			return true;
		}
	}
	for (var i = 0; i < component.descendants.length; i++) {
		if (hasEvents(component.descendants[i])) {
			return true;
		}
	}
	return false;
}

function bindEvents(dom, component) {
	if (!dom.length) {
		return;
	}
	if (component.eventCount) {
		$.each(component.events, function(type, handler) {
			dom.bind(type, handler);
		});
	}
	$.each(component.children, function() {
		bindEvents(dom.children(this.selector), this);
	});
	$.each(component.descendants, function() {
		bindEvents(dom.find(this.selector), this);
	});
}

function bindAll(dom, bound, search) {
	$.each(bound, function() {
		bindEvents(dom[search ? 'find' : 'filter' ](this.selector), this);
	});
}

var Model = window.Model = function(model) {
	if (!model) {
		return;
	}
	var hasUI = !!model.ui;
	var ui = model.ui || model;
	var currentBound = [];
	$.each(ui, function(id, meta) {
		var args = [id, meta, null, null];
		if (hasUI) {
			args.push(model);
		}
		var component = create.apply(arguments.caller, args);
		if (hasEvents(component)) {
			currentBound.push(component);
		}
		components.push(component);
	});
	Array.prototype.push.apply(boundComponents, currentBound);
	return {
		bind: function(dom, search) {
			bindAll(dom, currentBound, search);
			return dom;
		}
	};
};

Model.bind = function() {
	var dom = $($.map(boundComponents, function(c) { return c.selector; }).join(','));
	bindAll(dom, boundComponents);
	return dom;
};

})();

(function($) {
	$('body').ajaxError(function(event, xhr) {
		if (xhr.responseText.length)
			StatusBar.display($(xhr.responseText).getStatus());
		switch(xhr.status) {
			case 403:
				var login = $(xhr.responseText);
				Login.bind(login.wrapAll('<div id="login_dialog"></div>').parent());
				DialogHelper.show(login.parent(), '100px');
				break;
			case 404:
				StatusBar.display('没找到您所请求的页面');
				break;
			case 500:
				StatusBar.display('服务器错误');
				break;
			case 422:
				StatusBar.display('对不起,您没有此权限');
				break;
		}
	});
	$.extend({
		ajaxManager: function(s) {
			if (typeof(s.name) !== 'string' || s.name == '') {
				throw "property 'name' should be assigned with a non-empty string to identify an Ajax request type";
			}
			// timeout can only be detected in local error handler
			s.error =  function(xhr, textStatus) {
				if(textStatus == 'timeout') {
					StatusBar.display('请求超时,请稍后重试');
				}
			}
			var allR = $.ajaxSettings.allRequests = $.ajaxSettings.allRequests || [];
			if (!allR[s.name]) {
				allR[s.name] = [];
			}
			if (!s.queue && allR[s.name].length) {
				return false;
			} else {
				s.complete = function() {
					allR[s.name].shift();
					if (allR[s.name].length)
						$.ajax(allR[s.name][0]);
				}
				allR[s.name].push(s);
				if (allR[s.name].length == 1) {
					$.ajax(s);
				}
			}
		}
	});
})(jQuery);

var DefaultText = Model ({
	ui: {
		TextInput: {
			selector: 'input[type="text"], textarea',
			focus: 'hideDefaultText',
			blur: 'showDefaultText'
		},
		SubmitBtn: {
			selector: 'input[type="submit"]',
			click: 'removeDefaultText'
		}
	},
	hideDefaultText: function() {
		if ($(this).val() == $(this).attr('placeholder'))
			$(this).val('');
	},
	showDefaultText: function() {
		if ($(this).val() == '')
			$($(this).val($(this).attr('placeholder')));
	},
	removeDefaultText: function() {
		$(this).closest('form').getTextInput().each(function() {
			if ($(this).val() == $(this).attr('placeholder'))
				$(this).val('');
		});
	}

});

var Comment = Model({
	ui: {
		CommentForm: {
			selector: 'form.new_comment,form.edit_comment',
			descendants: {
				CommentContent: {
					selector: '#comment_content',
					keypress: 'triggerSubmit'
				},
				CommentCancel: {
					selector: '.cancel_comment',
					click: 'cancel'
				}
			},
			submit: 'save'
		},
		NewCommentForm: {
			selector: 'form.new_comment'
		},
		EditCommentForm: {
			selector: 'form.edit_comment'
		},
		NewComment: {
			selector: 'a.new_comment',
			click: 'new'
		},
		EditComment: {
			selector: 'a.edit_comment',
			click: 'edit'
		},
		DeleteComment: {
			selector: 'a.delete_comment',
			click: 'destroy'
		},
		Comments: {
			selector: '.comments'
		},
		Comment: {
			selector: 'li.comment',
			mouseenter: 'showOptions',
			children: {
				CommentAuthor: {
					selector: 'a.user:last'
				},
				CommentOptions: {
					selector: '.comment_options'
				}
			}
		},
		MoreComments: {
			selector: 'a.more_comments',
			click: 'more'
		}
	},
	'new': function() {
		var commentForm = $(this).parent().getComments().getNewCommentForm();
		if (commentForm.children().length) {
			commentForm.getCommentCancel().click();
			return false;
		}
		$.ajax({
			type:       'GET',
			url:        this.href,
			beforeSend: function() {
				commentForm.addClass('loading');
			},
			success:    function(data, textStatus) {
				var form = $(data);
				if (form.isNewCommentForm()) {
					commentForm.replaceWith(form);
					Comment.bind(form).getCommentContent().select();
				}
				commentForm.removeClass('loading');
			}
		});
		return false;
	},
	edit: function() {
		var comment = $(this).getComment();
		var form = comment.getEditCommentForm();
		if (form.length) {
			comment.children('div').hide();
			form.show().getCommentContent().select();
			return false;
		}
		$.ajax({
			type:       'GET',
			url:        this.href,
			success:    function(data, textStatus) {
				var form = $(data);
				if (form.isEditCommentForm() && !comment.getEditCommentForm().length) {
					comment.append(form).children('div').hide();
					Comment.bind(form).getCommentContent().select();
				}
			}
		});
		return false;
	},
	cancel: function() {
		var form = $(this).getCommentForm();
		var comment = $(this).getComment();
		if (form.isNewCommentForm()) {
			form.getCommentContent().val('').focus();
			return false;
		} else if (form.isEditCommentForm()) {
			var wrapper = comment.children('div');
			if (wrapper.length) {
				form.hide();
				wrapper.show();
			} else {
				$.ajax({
					type:       'GET',
					url:        form.get(0).action,
					success:    function(data, textStatus) {
						var wrapper = $(data).children();
						form.hide();
						comment.prepend(Comment.bind(wrapper, true));
					}
				});
			}
			return false;
		}
	},
	save: function() {
		var form = $(this);
		var content = form.getCommentContent();
		if (content.val().match(/^\s*$/)) {
			alert('回复内容不能为空');
			content.select();
			return false;
		}
		$.ajaxManager({
			name:       'saveComment',
			queue:      false,
			type:       this.method,
			url:        this.action,
			data:       form.serializeArray(),
			success:    function(data, textStatus) {
				var result = $(data);
				if (result.isCommentForm()) {
					form.replaceWith(Comment.bind(result));
				} else if (form.isNewCommentForm()) {
					var comments = form.getComments();
					if (comments.hasClass('empty')) {
						comments.removeClass('empty');
					}
/*					# To be modified when implementing real-time mode togglling
					comments.append(result)
					Comment.bind(result, true);
					Popup.bind(comments, true);
*/					comments.getNewComment().click();
					Analytics.trackEvent('comment', 'publish', form.parents('.entry_view').attr('uuid'));
				} else if (form.isEditCommentForm()) {
/*					form.parent().replaceWith(result);
					Comment.bind(result, true);
					Popup.bind(result.parent(), true);
*/				}
			}
		});
		return false;
	},
	destroy: function() {
		if (confirm('您确定要删除这个回复？')) {
			var comment = $(this).getComment();
			$.ajaxManager({
				name:       'destroyComment',
				queue:      false,
				type:       'POST',
				url:        this.href,
				data:       { _method: 'delete' },
				success:    function(data, textStatus) {
/*					var comments = comment.getComments();
					comment.remove();
					if (comments.getComment().length == 0) {
						comments.addClass('empty');
					}
*/				}
			});
		}
		return false;
	},
	more: function() {
		var comments = $(this).parent().getComments();
		$.ajaxManager({
			name:       'moreComments',
			queue:      false,
			type:       'GET',
			url:        this.href,
			success:    function(data){
				var result = $(data);
				comments.contents().remove();
				comments.append(result);
				Comment.bind(comments, true);
				Popup.bind(comments, true);
			}
		});
		return false;
	},
	triggerSubmit: function(e) {
		if ((e.keycode || e.which) == $.ui.keyCode.ENTER) {
			$(this).getCommentForm().submit();
			return false;
		}
	},
	showOptions: function() {
		$(this).getComments().getCommentOptions().removeClass('visible');
		if ($.cookie('username') == $(this).getCommentAuthor().attr('name')) {
			$(this).getCommentOptions().addClass('visible');
		}
	}
});

var StatusBar = Model({
	ui: {
		Status: {
			selector: 'p#status'
		},
		StatusBar: {
			selector:    '#status_bar',
			ajaxStart:   'showLoader',
			ajaxSuccess: 'showFlash'
		}
	},
	showLoader: function() {
		StatusBar.display('加载中...', false).addClass('loading').ajaxComplete(function() {
			$(this).remove();
		});
	},
	showFlash: function(event, xhr) {
		// Show Rails flash status only if content type is 'text/html'
		if (xhr.getResponseHeader('Content-Type').indexOf('text/html') >= 0 && $.trim(xhr.responseText).length)
			StatusBar.display($(xhr.responseText).getStatus());
	}
});

var Login = Model({
	ui: {
		Login: {
			selector:  '#login_dialog',
			descendants: {
				LoginForm: {
					selector: '#login form',
					submit: 'login'
				},
				Close: {
					selector: '.close',
					click: 'close'
				}
			}
		}
	},
	login: function() {
		var form = $(this);
		$.ajaxManager({
			name:     'login',
			queue:    false,
			type:	  this.method,
			url:      this.action,
			data:     form.serializeArray(),
			success:  function(data, textStatus) {
				if (data == '') {
					DialogHelper.close();
				}
			}
		})
		return false;
	},
	close: function() {
		DialogHelper.close();
	}
});

var UserActions = Model({
	ui: {
		Following: {
			selector: 'form.following',
			submit: 'follow'
		},
		FollowingRequest: {
			selector: 'li.following_request',
			descendants: {
				FollowingRequest: {
					selector: 'form.following_request_handle',
					submit: 'followingRequest'
				}
			}
		},
		CreateTab: {
			selector: 'form.create_tab',
			submit: 'createTab'
		}
	},
	follow: function() {
		var form = $(this);
		$.ajaxManager({
			name:       'following',
			queue:      true,
			type:       this.method,
			url:        this.action,
			data:       form.serializeArray(),
			success:    function(data, textStatus) {
				var result = $(data);
				if (result.isFollowing()) {
					form.parent('.user_actions').html(result);
					UserActions.bind(result);
					// refresh the following list on side panel
					if ($(document).getExpandable().length) {
						var tabSelected = $(document).getExpandable().getFriendsTab().filter('.selected');
						if (tabSelected.attr('id') == 'followings') {
							tabSelected.removeClass('selected');
							tabSelected.click();
						}
					}
					var status = result.getStatus();
					if (!$('body').hasClass('userpage') && $(document).getEntryList().length && (status.hasClass('following_created') || status.hasClass('following_destroyed'))) {
						// refresh entry list
						if (EntryList.currentTab().attr('name') == 'friends') {
							EntryList.switchTo('friends');
						}
						// remove possible custom tabs for the related user
						if (status.hasClass('following_destroyed')) {
							$('#entry_panel .tabs .tab[name=' + result.find('#name').val() + ']').remove();
							result.next('.create_tab').remove();
						}
					}
				}
			}
		});
		return false;
	},
	followingRequest: function() {
		var form = $(this);
		$.ajaxManager({
			name:      'followingRequest',
			queue:     true,
			type:      this.method,
			url:       this.action,
			data:      form.serializeArray(),
			success:   function(data, textStatus) {
				var result = $(data);
				form.getFollowingRequest().remove();
			}
		});
		return false;
	},
	createTab: function() {
		var form = $(this);
		$.ajaxManager({
			name: 'createTab',
			type: this.method,
			data: form.serializeArray(),
			url:  this.action,
			success: function(data) {
				form.remove();
				if ($('body').attr('id') == 'homepage') {
					$(data).filter('li.tab').insertAfter($(document).getTab().filter(':last'));
					EntryList.bind($(document).getTab());
				}
			}
		});
		return false;
	}
});

var Favorite = Model({
	ui: {
		Favorites: {
			selector: '.followers',
			descendants: {
				FavoriteForm: {
					selector: 'form.favorite'
				},
				Favorite: {
					selector: '.favorite span',
					click: 'favorite'
				}
			}
		}
	},
	favorite: function() {
			var form = $(this).getFavoriteForm();
			var favorites = form.getFavorites();
			var view = favorites.parents('.entry_view');
			$.ajaxManager({
				name:       'favorite',
				queue:      false,
				type:       'POST',
				url:        form.attr('action'),
				data:       form.serializeArray(),
				success:    function(data, textStatus) {
					favorites.html($(data).html());
					Favorite.bind(view, true);
					Popup.bind(favorites, true);
					CopyEntryUrl.bind(favorites, true);
					EntryDelete.bind(favorites, true);
					Analytics.trackEvent('favorite', 'save', view.attr('uuid'));
				}
			});
			return false;
	}
});

var CopyEntryUrl = Model({
	CopyEntryUrl: {
		selector: '.copy_url',
		click: function() {
			if ($(this).parent().next('.entry_url').remove().length) { return }
			var url = $('<div class="entry_url"></div>')
			$('<input type="text" value="' + $(this).attr('link') + '" />').appendTo(url);
			$('<a class="jump_to_entry" href="' + $(this).attr('link') + '" target="_blank">跳转</a>').appendTo(url);
			url.insertAfter($(this).parent());
			url.click(function() { $(this).find('input').select(); });
		}
	}
});

var EntryDelete = Model({
	EntryDeleteForm: {
		selector: 'form.entry_delete'
	},
	EntryDelete: {
		selector: '.entry_delete span',
		click: function() {
			if (!(confirm('你确认删除该条目？'))) {
				return;
			}
			var uuid = $(this).attr('uuid');
			var form = $(this).getEntryDeleteForm();
			$.ajaxManager({
				name:     'deleteEntry',
				queue:    false,
				type:     'POST',
				url:      form.attr('action'),
				data:     form.serializeArray()
			})
			return false;
		}
	}
});

var Entry = Model({
	ui: {
		EntryForm: {
			selector: 'form.new_entry',
			submit: 'save',
			descendants: {
				EntryTo: {
					selector: 'input#entry_to'
				},
				EntryGroup: {
					selector: 'input#entry_group_id'
				},
				EntryTitle: {
					selector: '#entry_title',
					keypress: 'entrySubmit'
				},
				EntryLink: {
					selector: '#entry_link',
					keypress: 'entrySubmit'
				},
				EntryPrivate: {
					selector: 'input#entry_private'
				}
			}
		},
		Bubble: {
			selector: '#sharepanel .bubble',
			click: 'new',
			descendants: {
				Close: {
					selector: '.close',
					click: 'close'
				}
			}
		},
		BubbleTrigger: {
			selector: '.new_entry_link',
			click: 'entryTrigger'
		}
	},
	'new': function(event) {
		var bubble = $(this);
		if (!bubble.hasClass('closed')) {
			return;
		}
		var form = $('body').getEntryForm();
		var query = '';
		if (EntryList.currentTabAttr().cat != 'fixed') {
			query = '?recipient_id=' + EntryList.currentTabAttr().entity_id + '&' + 'recipient_cat=' + EntryList.currentTabAttr().cat;
		}
		$.ajaxManager({
			name:       'newEntry',
			queue:      false,
			type:       'GET',
			url:        '/entries/new' + query,
			success:    function(data, textStatus) {
				bubble.removeClass('closed');
				var result = $(data);
				if (form.length > 0) {
					form.replaceWith(result);
				} else {
					bubble.find('.body').append(result);
				}
				Entry.bind(result, true);
				AvailableRecipients.bind(result, true);
				Dialog.bind(result, true);
				DefaultText.bind(result, true);
				result.getEntryTitle().focus();
			}
		});
		return false;
	},
	save: function() {
		var form = $(this);
		// When a link is provided alone, copy it as title
		if (form.getEntryLink().val() && !form.getEntryTitle().val()) {
			form.getEntryTitle().val(form.getEntryLink().val());
		}

		// Take a snapshot of recipients and save
		var entryTo = $(document).getEntryTo().val('');
		var recipients = $(document).getEntryRecipients().getRecipient();
		if (recipients.length) {
			if (recipients.attr('uid')) {
				recipients.each(function() { entryTo.val(entryTo.val() + $(this).attr('uid') + ' ') });
			} else {
				$(document).getEntryGroup().val(recipients.attr('gid'));
			}
		}

		$.ajaxManager({
			name:       'saveEntry',
			queue:      false,
			type:       this.method,
			url:        this.action,
			data:       form.serializeArray(),
			success:    function(data, textStatus) {
				var result = $(data);
				if (result.getEntryForm().length > 0) {
					form.parent().replaceWith(result);
					Entry.bind(result, true);
					AvailableRecipients.bind(result, true);
					Dialog.bind(result, true);
					DefaultText.bind(result, true);
				} else {
/*					EntryList.bind($(document).getEntryList().scrollLeft(0).prepend(result));
					EntryList.bindDraggable();
					Comment.bind(result, true);
					Favorite.bind(result, true);
					CopyEntryUrl.bind(result, true);
*/					Entry.bind($(document).getBubble().addClass('closed'));
					form.parent().remove();
					Analytics.trackEvent('entry', 'publish');
				}
			}
		});
		return false;
	},
	close: function(event) {
		event.stopPropagation();
		$(this).getBubble().addClass('closed');
	},
	entryTrigger: function() {
		$(document).getBubble().click();
		return false;
	},
	entrySubmit: function(e) {
		if (!e.metaKey && (e.keyCode || e.which) == $.ui.keyCode.ENTER) {
			var form = $(this).getEntryForm();
			form.getTextInput().each(function() {
				if ($(this).val() == $(this).attr('placeholder'))
					$(this).val('');
			});
			form.submit();
			return false;
		}
	}
});

var AvailableRecipients = Model({
	ui: {
		AvailableRecipients: {
			selector: '#available_recipients',
			keypress: 'keyAction',
			scroll:   'prevent',
			children: {
				Recipient: {
					selector: 'li.recipient',
					mouseover: 'mouseSelect',
					click: 'addRecipient'
				}
			}
		},
		RemoveRecipient: {
			selector: '.remove_recipient',
			click: 'RemoveRecipient'
		},
		EntryRecipients: {
			selector: '#entry_recipients',
			click:    'wakeUpRecipientInput',
			children: {
				RecipientInput: {
					selector:  '#recipient_input',
					focus:     'getRecipients',
					keydown:   'operateList'
				},
				RecipientTip: {
					selector:  '#recipient_tip'
				}
			}
		},
		EntryRecipientType: {
			selector: "#entry_recipient_type li",
			click: "switchRecipientType"
		},
		Body: {
			selector: 'body',
			click: 'clearCacheAndTimer'
		}
	},
	keyAction: function(event, e) {
		event.preventDefault();
		var current = $(this).find('li.selected');
		switch(e.which) {
		case $.ui.keyCode.DOWN:
			current.next().length ? current.next().mouseover() : $(this).find('li:first').mouseover();
			break;
		case $.ui.keyCode.UP:
			current.prev().length ? current.prev().mouseover() : $(this).find('li:last').mouseover();
			break;
		}
	},
	mouseSelect: function() {
		$(this).addClass('selected').siblings('li.selected').removeClass('selected');
	},
	prevent: function(e) {
		e.preventDefault();
		$(document).getRecipientInput().focus();
	},
	addRecipient: function() {
		var recipient = $(this).clone().prepend($('<div class="remove_recipient">X</div>'));

		// only one group can be associated
		if (recipient.attr('gid')) { $(document).getEntryRecipients().find('[gid]').remove() }

		$(document).getRecipientInput().val('').before(recipient).focus().removeData('prevInput');
		AvailableRecipients.bind(recipient, true);
		return false;
	},
	RemoveRecipient: function() {
		$(this).parent().remove();
	},
	getRecipients: function(e) {
		var inputField = $(this);
		if (inputField.data('recipients') !== undefined) { return }

		$.ajaxManager({
			name: 'getRecipients',
			url: $(document).getEntryRecipientType().filter('.selected').attr('link'),
			dataType: 'json',
			success: function(data) {
				inputField.data('recipients', data);
				AvailableRecipients.scan(inputField);
				inputField.removeData('prevInput');
			}
		});
	},
	operateList: function(e) {
		switch (e.which) {
		case $.ui.keyCode.DOWN:
		case $.ui.keyCode.UP:
			$(document).getAvailableRecipients().trigger('keypress', e);
			e.preventDefault();
			break;
		case $.ui.keyCode.TAB:
			if ($(document).getAvailableRecipients().length) {
				$(document).getRecipient().filter('.selected').click();
				e.preventDefault();
			}
			break;
		case $.ui.keyCode.ENTER:
			$(document).getRecipient().filter('.selected').click();
			e.preventDefault();
			break;
		case $.ui.keyCode.BACKSPACE:
		// input field cleaned, remove related data
			if ($(this).val() == '') {
				$(this).removeData('prevInput').prev('li').remove();
				$('#available_recipients').remove();
			}
			break;
		}
	},
	wakeUpRecipientInput: function() {
		$(this).getRecipientInput().focus();
		$(this).getRecipientTip().hide();
	},
	clearCacheAndTimer: function(event) {
		var inputField = $(document).getRecipientInput();
		if (!inputField.length || $(event.target).isRecipient() || $(event.target).isRecipientInput()) { return }

		inputField.removeData('recipients');
		clearInterval(inputField.data('scannerID'));
		$('#available_recipients').remove();
	},
	switchRecipientType: function() {
		if ($(this).hasClass('selected')) {
			$(document).getRecipientInput().focus();
			return false;
		}
		$('#available_recipients').remove();
		$(document).getRecipientTip().hide();
		$(this).addClass('selected').siblings().removeClass('selected');
		$(document).getRecipientInput().attr('link', $(this).attr('link')).removeData('recipients').focus().prevAll('li').remove();
	}
});

$.extend(AvailableRecipients, {
	scan: function(inputField) {
		function stringfy(item) {
			var result = '';
			$.each(item, function(k, v) {
				if (k != 'id') { result += v + ' ' }
			});
			return result;
		}

		inputField.data('scannerID', setInterval(function() {

			inputField.data('')
			var input = inputField.val();

			// input field does not change
			if (input == inputField.data('prevInput')) { return }

			// search in the cached recipients
			inputField.data('prevInput', input);
			var recipients = $('<ul id="available_recipients"></ul>');
			$.each(inputField.data('recipients'), function(i, o) {
				if ((input == '' || (stringfy(o)).indexOf(input) !== -1) && inputField.prevAll('[uid=' + o.id + '], [gid=' + o.id + ']').length == 0) {
					recipients.append(AvailableRecipients.composeDom(o));
				}
			});

			// no recipient match the current input
			if (recipients.find('li').length == 0) {
				$('#available_recipients').remove();
				return;
			}

			AvailableRecipients.display(recipients, inputField.offset());
		}, 100));
	},
	composeDom: function(item) {
		if (typeof item.name === 'string') {
			return $('<li class="recipient" gid=' + item.id + '>' + item.name + '</li>');
		} else {
			return $('<li class="recipient" uid=' + item.id + '>' + item.display_name + ' (' + item.username + ')</li>');
		}
	},
	display: function(recipients, position) {
		recipients.find('li:first').addClass('selected');
		$('#available_recipients').remove();
		recipients.appendTo($('body')).css(position);
		AvailableRecipients.bind(recipients);
	}
});

var Service = Model({
	ui: {
		ServiceForm: {
			selector: 'form.new_service',
			descendants: {
				Input: {
					selector: '#service_user_key,#service_base_url'
				},
				Cancel: {
					selector: '.cancel_service',
					click: 'cancel'
				}
			},
			submit: 'save'
		},
		NewServiceList: {
			selector: 'ul.new_service_list'
		},
		NewSerivce: {
			selector: 'a.new_service',
			click: 'new'
		},
		ServiceList: {
			selector: 'ul.service_list'
		},
		DeleteService: {
			selector: 'a.delete_service',
			click: 'destroy'
		}
	},
	'new': function() {
		var newServiceList = $(this).getNewServiceList();
		var service = $(this).parent();
		newServiceList.getServiceForm().remove();
		$.ajaxManager({
			name:      'newService',
			queue:     false,
			type:      'GET',
			url:       this.href,
			success:   function(data, textStatus) {
				newServiceList.hide();
				var form = $(data).getServiceForm();
				form.insertBefore(newServiceList);
				Service.bind(form);
				form.getInput().select();
			}
		});
		return false;
	},
	save: function() {
		var form = $(this);
		var content = form.getInput();
		var saved_services = $(document).getServiceList();
		if (content.val().match(/^\s*$/)) {
			content.select();
			return false;
		}
		$.ajaxManager({
			name:       'saveService',
			queue:      false,
			type:       this.method,
			url:        this.action,
			data:       form.serializeArray(),
			success:    function(data, textStatus) {
				var result = $(data);
				if (result.isServiceForm()) {
					form.replaceWith(result);
					Service.bind(result);
				} else {
					form.remove();
					saved_services.replaceWith(result.getServiceList());
					Service.bind(result, true);
					EntryList.switchTo('self');
					$(document).getNewServiceList().show();
				}
			}
		});
		return false;
	},
	cancel: function() {
		$(this).getServiceForm().remove();
		$(document).getNewServiceList().show();
		return false;
	},
	destroy: function() {
		if (confirm('相关的条目和评论都会被删除，您是否确认？')) {
			var link = $(this);
			var service = $(this).parent();
			$.ajaxManager({
			        name:     'destroyService',
				queue:    false,
				type:     'POST',
				url:      this.href,
				data:     { _method: 'delete' },
				success:  function(data, textStatus) {
					service.remove();
					EntryList.switchTo('self');
				}
			});
		}
		return false;
	}
});

var EntryList = Model({
	ui: {
		EntryList: {
			selector: '#entry_list',
			load: 'liveUpdate'
		},
		Unit: {
			selector: '.entry_unit'
		},
		Entry: {
			selector: '.entry',
			click: 'pop',
			descendants: {
				HeatBar: {
					selector: '.heat_bar',
					liveupdate: 'changeWidth'
				}
			}
		},
		View: {
			selector: '.entry_view',
			mousedown: 'activate',
			descendants: {
				Close: {
					selector: '.ctrl_bar .close',
					click: 'close'
				},
				UsersList: {
					selector: 'ul.comments, .followers',
					click: 'add'
				}
			}
		},
		User: {
			selector: '.user',
			click: 'add'
		},
		PrivateUser: {
			selector: '.private'
		},
		CloseBtn: {
			selector: '#entry_panel .close_all_btn',
			click: 'closeAll'
		},
		Tab: {
			selector: '#entry_panel .tabs .tab:not(.removable)',
			click: 'switch',
			descendants: {
				TabBody: {
					selector: '.tab_body'
				}
			}
		},
		RemoveTab: {
			selector: '#entry_panel div.remove_tab',
			click: 'removeTab'
		},
		EditTab: {
			selector: '#entry_panel #edit_tab',
			click: 'editTab'
		},
		EditComplete: {
			selector: '#entry_panel #edit_tab_complete',
			click: 'editComplete'
		}
	},
	liveUpdate: function() {
		LiveUpdate.start();
	},
	add: function(event) {
		if ($(event.target).isUser()) {
			var user = $(event.target);
			var tabs = $(document).getTab();
			if (tabs.filter("[attr=" + user.attr('href') + "]").length) {
				return false;
			}
			var newTab = tabs.filter('.selected').clone().attr('attr', user.attr('href')).attr('id', 'el_account_user');
			newTab.addClass('ad_hoc').getTabBody().html(user.html());
			tabs.filter('.ad_hoc').remove();
			tabs.removeClass('selected').parent().append(newTab);
			EntryList.ajaxLoad({
				action: 'replace',
				url: user.attr('href')
			})
			return false;
		}
	},
	pop: function(event) {
		var target = $(event.target);
		if (target.is('a')) { return }

		$(this).addClass('down');
		var uuid = $(this).attr('uuid');

		var opened = $('#entry_views .entry_view[uuid=' + uuid + ']');
		var listed = $('#entry_panel .entry_view[uuid=' + uuid + ']');

		if (listed.length) {
			if (opened.length) {
				position = opened.position();
				opened.replaceWith(listed.show().addClass('active').css('left', position.left).css('top', position.top));
			} else {
				appendView(listed);
			}
			listed.getNewComment().click();
		} else if (opened.length) {
			opened.getClose().click();
		}
	},
	'switch': function() {
		if ($(this).hasClass('selected')) {
			return false;
		}
		$(document).getTab().removeClass('selected').filter('.ad_hoc').remove();
		$(this).addClass('selected').removeClass('updated');
		EntryListCache.removeEntry({name: $(this).attr('name'), cat: $(this).attr('cat')});
		EntryList.ajaxLoad({
			action: 'replace',
			url: $(this).attr('attr'),
			queue: true
		});
	},
	activate: function(event) {
		var entryView = $(this);
		entryView.removeClass('lite');

		if (!entryView.hasClass('active')) {
			$('.entry_view.active').removeClass('active');
			entryView.addClass('active').getCommentForm().getCommentContent().focus();
		}
		return;
	},
	close: function() {
		var entryView = $(this).getView();
		var uuid = entryView.attr('uuid');
		var entry = $(document).getEntryList().getEntry().filter('[uuid=' + uuid + ']');
		if ($(document).getEntryList().getView().filter("[uuid='" + uuid + "']").length || !entry.length) {
			entryView.remove();
		} else {
			entryView.removeClass('active').hide().insertAfter(entry.removeClass('down'));
		}
		// activate the latest opened entry view
		$('#entry_views .entry_view:last').mousedown();
		EntryList.readEntry(entryView)
	},
	closeAll: function() {
		if ($('#entry_views .entry_view').length) { $('#entry_views .entry_view').getClose().click(); }
	},
	changeWidth: function(event, deleted) {
		var heatBarWidth = parseFloat($(this).css('width'));
		if (!deleted && heatBarWidth < 188) {
			$(this).css('width', heatBarWidth + 188/20 + 'px');
		} else if (deleted && heatBarWidth > 0) {
			$(this).css('width', heatBarWidth - 188/20 + 'px');
		}
	},
	editTab: function() {
		EntryList.reloadTab($(this).attr('edit_link'));
	},
	editComplete: function() {
		$('#entry_panel .tabs .tab.removable').removeClass('removable');
		$('#entry_panel div.remove_tab').remove();
		$(this).unbind().attr('id', 'edit_tab').find('.body').text('编辑标签');
		EntryList.bind($(this));
		EntryList.bind($(document).getTab());
		return false;
	},
	removeTab: function() {
		var form = $(this).next('form');
		$.ajaxManager({
			name: 'removeTab',
			type: form.attr('method'),
			url:  form.attr('url'),
			data: form.serializeArray(),
			success: function() {
				form.closest('li.tab').remove();
			}
		});
	}
});

var EntryListCache = $.extend({}, Model({
	ui: {
		EntryListCache: {
			selector: '#entry_list_cache',
			load: 'highlight'
		}
	},
	highlight: function() {
		EntryList.getTab().removeClass('updated');
		$(this).getEntry().each(function() {
			var tab = EntryList.getTab({ name: $(this).attr('name'), cat: $(this).attr('cat')});
			if (tab.length) {
				tab.addClass('updated')
			} else if ($(this).attr('cat') != 'group') {
				EntryList.getTab({name: 'friends', cat: 'fixed'}).addClass('updated');
			}
		})
	}
}), {
	removeEntry: function(opt) {
		if (typeof opt == 'string') {
			$('#entry_list_cache').find('[uuid=' + opt + ']').remove();
		} else if (opt.name && opt.cat) {
			$('#entry_list_cache').find('[name=' + opt.name + '][cat=' + opt.cat + ']').remove();
		} else {
			return false;
		}
	},
	showAll: function() {
		var entryUnits = $('#entry_list_cache').getUnit();
		if (!entryUnits.length) { return false }

		bindEntryUnits(entryUnits);
		entryUnits.getView().each(function() {
			appendView($(this).addClass('updated lite'));
			$(this).getNewComment().click();
		});
		entryUnits.getEntry().remove();
	},
	load: function() {
		$('#entry_list_cache').load();
	}
});

var Friends = Model({
	ui: {
		Expandable: {
			selector: '#expandable',
			descendants: {
				Friends: {
					selector: '#users_list'
				},
				FriendsTab: {
					selector: '.tab',
					click: 'switch'
				},
				MoreFriends: {
					selector: '.more_friends a'
				}
			}
		},
		Accordion: {
			selector: '.accordion_btn',
			click: 'toggle'
		}
	},
	'switch': function() {
		if ($(this).hasClass('selected')) {
			return false;
		}
		var link = $(this).attr('attr');
		var id = $(this).attr('id');
		var tabs = $(this).parent();
		$(this).parent().getFriendsTab().removeClass('selected');
		$(this).addClass('selected');
		$.ajaxManager({
			name: 'switchFriends',
			queue: true,
			type: 'GET',
			url:  link + '?avatar=icon',
			success: function(data) {
				tabs.getFriends().replaceWith($(data).getFriends());
				Popup.bind(tabs.getFriends(), true);
				var moreFriends = tabs.parent().getMoreFriends().attr('href',link);
				moreFriends.attr('setting_type', moreFriends.attr('setting_type').split('_')[0] + '_' + id);
			}
		});
	},
	toggle: function() {
			$(this).toggleClass('down');
			$(document).getExpandable().toggleClass('down')
	}
});

var Dialog = Model({
	ui: {
		DialogWindow: {
			selector: '#dialog',
			descendants: {
				DialogContents: {
					selector: '.contents',
					descendants: {
						UploadFrame: {
							selector: 'iframe[name="upload_target"]',
							load: 'refresh'
						},
						EditUserForm: {
							selector: 'form.edit_user, form.new_pending_email, form.users_search, form.groups_search, form.alerts',
							submit:   'update'
						},
						Pagination: {
							selector: '.pagination li a',
							click: 'page'
						},
						InvitesForm: {
							selector: '#account_invites form#new_pending_user',
							submit: 'invite'
						},
						GroupForm: {
							selector: 'form.new_group, form.edit_group',
							descendants: {
								PrivateCheck: {
									selector: 'input#group_private',
									click: 'privateChecked'
								}
							}
						},
						GroupNavLink: {
							selector: '.dialog_nav a, button.edit_group',
							click: 'groupNav'
						},
						GroupActionLink: {
							selector: 'button.group_invites, button.group_add',
							click: 'groupAction'
						},
						GroupActionForm: {
							selector: 'form.group_leave, form.group_invites, form.group_request, form.group_add',
							submit:   'groupActionSubmit'
						},
						GroupRequestForm: {
							selector: 'form.group_request_approve, form.group_request_decline',
							submit:   'groupRequestSubmit'
						},
						Invitee: {
							selector: 'form.group_invites dl',
							click: 'selectInvitee',
							descendants: {
								InviteeCheckbox: {
									selector: 'input[type=checkbox]'
								}
							}
						},
						SelectAllInvitees: {
							selector: 'form.group_invites input#select_all',
							click: 'selectAllInvitees'
						}
					}
				},
				DialogTab: {
					selector: '.tab',
					click: 'switch'
				},
				DialogClose: {
					selector: '.close',
					click: 'close'
				}
			}
		},
		DialogTrigger: {
			selector: '#sidepanel .settings a, #service_setting, #sidepanel .more_friends a, a.pending_followers, .friends_pending a, .friends_search_link, .service_setting_link, #info_links a#friends_invite, #info_links a#groups_setting, .groups_requests a, .group_members a',
			click: 'load'
		}
	},
	'switch': function() {
		if ($(this).hasClass('selected')) {
			return false;
		}
		var tab = $(this);
		$(this).parent().getDialogTab().removeClass('selected');
		$(this).addClass('selected');
		var contents = $(document).getDialogWindow().getDialogContents();
		contents.children().remove();
		if (contents.children('#' + $(this).attr('id')).show().length == 0) {
			$.ajaxManager({
				name: 'switchDialog',
				queue: true,
				type: 'GET',
				url: $(this).attr('attr'),
				beforeSend: function() {
					contents.addClass('loading');
				},
				success: function(data) {
					contents.children().hide();
					contents.removeClass('loading').append($(data));
					UserActions.bind(contents, true);
					Dialog.bind($(document).getDialogWindow());
					Service.bind($(document).getDialogWindow(), true);
					EntryList.bind($(document).getDialogWindow(), true);
					Analytics.trackPage(tab.attr('attr'));
				}
			});
			return false;
		}
	},
	refresh: function() {
		if ($(this).contents().find('head').find('title').html() == "413 Request Entity Too Large") {
			StatusBar.display($("<p id='status' class='error'>你上传的文件过大</p>"));
			return false;
		}
		var iframeAccountContent = $(this).contents().find('#account_avatar');
		var iframeGroupContent = $(this).contents().find('#groups_created');
		// Clone the iframe content, or implicit referencing error will occur in Chrome
		if ($.browser.safari) { iframeAccountContent = iframeAccountContent.clone(); }
		if (iframeAccountContent.length) {
			if ($('#profile').find('img').length) {
				$('#profile').find('img').attr('src', iframeAccountContent.find('img').attr('src') + '3'); //add random string to get rid of cache in Chrome
			}
				// Do not replace with iFrameContent as a whole, will cause problem in both IE and Chrome
				$('#account_avatar img').replaceWith(iframeAccountContent.find('img'));
				$('#account_avatar input[type=file]').val('');
			StatusBar.display(iframeAccountContent.getStatus());
			Dialog.bind($(document).getDialogWindow());
		}
		if (iframeGroupContent.length) {
			if (iframeGroupContent.find('.fieldWithErrors, .formError').length == 0) {
				$(this).getDialogContents().getGroupNavLink().click();
				EntryList.reloadTab();
			} else {
				$(this).getDialogContents().html(iframeGroupContent);
			}
			StatusBar.display(iframeGroupContent.getStatus());
			Dialog.bind($(document).getDialogWindow());
		}
	},
	update: function() {
		var form = $(this);
		$.ajaxManager({
			name:    'updateDialog',
			queue:   false,
			type:    this.method,
			url:     this.action,
			data:    form.serializeArray(),
			success: function(data) {
				form.getDialogContents().html($(data));
				if ($(data).attr('id') == 'account_info' && $(data).find('.fieldWithErrors').length == 0) {
					$('#sidepanel #profile .bio').html($(data).find('#user_bio').val().slice(0,100));
					$('#sidepanel #profile a#display_name').html($(data).find('#user_display_name').val());
				}
				Dialog.bind($(document).getDialogWindow());
				UserActions.bind($(document).getDialogWindow(), true);
			}
		});
		return false;
	},
	load: function() {
		var settingType = $(this).attr("setting_type") || "account_info";
		var dialogType = settingType.split("_")[0];
		var param = '?type=' + dialogType + ($(this).attr('name') ? '&name=' + $(this).attr('name') : "");
		$.ajaxManager({
			name:  'loadDialog',
			queue: false,
			type:  'GET',
			url:   '/home/dialog' + param,
			success: function(data) {
				var dialog = $(data);
				dialog.find(".tab." + dialogType).removeClass("hidden");
				DialogHelper.show(dialog);
				Dialog.bind(dialog);
				dialog.find('li#' + settingType).click();
			}
		});
		return false;
	},
	invite: function() {
		var form = $(this);
		form.find('#pending_user_email_confirmation').val(form.find('#pending_user_email').val());
		$.ajaxManager({
			name:    'postInvite',
			type:    this.method,
			url:     this.action,
			data:    form.serializeArray(),
			success: function(data, textStatus) {
				var result = $(data);
				form.replaceWith(result);
				Dialog.bind($(document).getDialogWindow());
				Analytics.trackEvent('user', 'invite');
			}
		})
		return false;
	},
	page: function() {
		var contents = $(this).parent().getDialogContents();
		$.ajaxManager({
			name: 'pageDialog',
			queue: true,
			type: 'GET',
			url: this.href,
			success: function(data) {
				contents.html($(data)).scrollTop(0);
				UserActions.bind(contents, true);
				Dialog.bind($(document).getDialogWindow());
				EntryList.bind($(document).getDialogWindow(), true);
			}
		});
		return false;
	},
	privateChecked: function() {
		if($(this).is(':checked')){
			$(this).getGroupForm().find('input#group_invite_type_0').attr('disabled', true).attr('checked', false);
		}else{
			$(this).getGroupForm().find('input#group_invite_type_0').attr('disabled', false);
		}
	},
	groupNav: function() {
		var contents = $(this).parent().getDialogContents();
		$.ajaxManager({
			name:  'groupUpdate',
			queue: false,
			type:  'GET',
			url:   $(this).attr('link') || $(this).attr('href'),
			success: function(data) {
				contents.html($(data).scrollTop(0));
				Dialog.bind($(document).getDialogWindow());
				UserActions.bind($(document).getDialogWindow(), true);
			}
		});
		return false;
	},
	groupAction: function() {
		var link  = $(this);
		var group = link.parent().parent();
		if(group.getGroupActionForm().filter('.group_invites, .group_request').length) {
			group.getGroupActionForm().filter('.group_invites, .group_request').remove();
			link.removeClass('gray');
			return false;
		}
		$.ajaxManager({
			name:    'groupAction',
			type:    'GET',
			url:     link.attr('link'),
			success: function(data) {
				link.addClass('gray');
				$(data).appendTo(group);
				Dialog.bind($(document).getDialogWindow());
			}
		});
		return false;
	},
	groupActionSubmit: function() {
		var form = $(this);
		if (form.is('.group_leave') && !confirm('确认要退出该群组？')) { return false; }
		var group = form.parent();
		if (!group.is('li'))
		   group = group.parent();
		$.ajaxManager({
			name:    'groupAction',
			queue:   false,
			type:    this.method,
			url:     this.action,
			data:    form.serializeArray(),
			success: function(data) {
				group.replaceWith($(data));
				Dialog.bind($(document).getDialogWindow());
				if (form.is('.group_leave, .group_add')) {
					EntryList.reloadTab();
				}
			}
		});
		return false;
	},
	groupRequestSubmit: function() {
		var form = $(this);
		var group = form.parent().parent();
		$.ajaxManager({
			name:    'groupRequest',
			queue:   false,
			type:    this.method,
			url:     this.action,
			data:    $(this).serializeArray(),
			success: function(data) {
				group.remove();
				Dialog.bind($(document).getDialogWindow());
				if (form.hasClass('group_request_approve')) {
					EntryList.reloadTab();
				}
			}
		});
		return false;
	},
	selectInvitee: function(event) {
		var checkbox = $(this).getInviteeCheckbox();
		if (checkbox.is(':checked')) {
			checkbox.attr('checked', false).parents('dl').removeClass('selected');
		} else {
			checkbox.attr('checked', true).parents('dl').addClass('selected');
		}
		$(this).siblings(':first').find('input').attr('checked', false);
		return false;
	},
	selectAllInvitees: function() {
		if ($(this).is(':checked')) {
			$(this).parents('.list_wraper').find('dl').addClass('selected');
			$(this).parents('.list_wraper').find(':checkbox').attr('checked', true);
		} else {
			$(this).parents('.list_wraper').find('dl').removeClass('selected');
			$(this).parents('.list_wraper').find(':checkbox').attr('checked', false);
		}
	},
	close: function() {
		DialogHelper.close();
	}
});

var Intro = Model({
	ui: {
		IntroFrame: {
			selector: 'div#ui_intro',
			descendants: {
				NextIntro: {
					selector: 'li.next_intro',
					click: 'nextIntro'
				},
				FinishIntro: {
					selector: '#finish_intro',
					click: 'finishIntro'
				},
				IntroStep: {
					selector: '.ui_intro_step'
				}
			}
		}
	},
	nextIntro: function() {
		$(this).getIntroStep().hide();
		$(document).getIntroFrame().find('#' + $(this).attr('step')).show();
		switch ($(this).attr('step')) {
			case 'ui_intro_1':
				EntryList.switchTo('all');
				break;
			case 'ui_intro_2':
				$(document).getEntry().eq(0).click();
				break;
			case 'ui_intro_3':
				$('#entry_views').getView().getClose().click();
				$(document).getBubble().click();
				break;
		}
		$(document).getNextIntro().each(function() {
			$($(this).attr('widget')).css('z-index', 20);
		});
		$(document).find($(this).attr('widget')).css({ 'z-index': 1050, 'position': 'absolute' });
		return false;
	},
	finishIntro: function() {
		$.ajaxManager({
			name:     'finishIntro',
			url:      $(this).attr('href'),
			method:   'POST',
			data:     { _method: 'delete' },
			success:  function() {
				window.location.reload();
			}
		})
		return false;
	}

});

var DialogHelper = Model({
	ui: {
		DialogHelper: {
			selector: '#overlay, #dialog_container',
			mousedown: 'close'
		}
	},
	close: function(event) {
		if ($(event.target).isDialogHelper()) {
			DialogHelper.close();
		}
	}
});

var Popup = Model({
	ui: {
		PopupDialog: {
			selector: '#popup'
		},
		PopupLink: {
			selector: '.user, .p_profile',
			mouseenter: 'popup'
		},
		PopupProfile: {
			selector: '.p_profile',
			mouseleave: 'close'
		}
	},
	popup: function(event){
		var link = $(this);
		if (link.isPopupProfile()) {
			clearTimeout(Popup.leaveTimeout[link.attr('href')]);
			return false;
		}
		link.addClass('p_profile');
		Popup.bind($(document).getPopupProfile());
		var timeoutKey = link.attr('href');
		var timeoutArray = new Array();
		Popup.enterTimeout[timeoutKey] = timeoutArray;
		if(Popup.enterTimeout[timeoutKey]['timeout']){
			clearTimeout(Popup.enterTimeout[timeoutKey]['timeout']);
		}
		timeoutArray['flag'] = true;
		timeoutArray['timeout'] = setTimeout(function() {
			$.ajaxManager({
				name: 'popup' + timeoutKey,
				queue: false,
				action: 'GET',
				url: timeoutKey + '/popup',
				success: function(data) {
					if (Popup.enterTimeout[timeoutKey]['flag']) {
						var popup = $(document).getPopupDialog();
						if(popup.length) {
							popup.remove();
						}
						var result = $(data);
						var linkTop = link.offset().top;
						var linkHeight = link.height();
						var linkLeft = link.offset().left;
						UserActions.bind(result, true);
						result.addClass('p_profile').attr('href', timeoutKey).hide().appendTo($('body'));
						Popup.bind(result);
						var resultHeight = result.height();
						var resultWidth = result.width();
						linkTop < resultHeight ? result.css('top', linkTop + linkHeight) : result.css('top', linkTop - resultHeight);
						resultWidth + linkLeft > document.body.offsetWidth ? result.css('right', 0) : result.css('left', linkLeft - 10);
						result.show();
					}
				}
			});
			return false;
		}, 750);
	},
	close: function(event) {
		var timeoutKey = $(this).attr('href');
		Popup.enterTimeout[timeoutKey]['flag'] = false;
		Popup.leaveTimeout[timeoutKey] = setTimeout(function() {
			if(!$(event.relatedTarget).isPopupProfile()) {
				var popup = $(document).getPopupDialog();
				var popupProfile = $(document).getPopupProfile();
				if(popup.length)
					popup.remove();
				if(popupProfile.length)
					popupProfile.removeClass('p_profile');
				clearTimeout(Popup.enterTimeout[timeoutKey]['timeout']);
			}
		}, 5);
	}
});

$.extend(Popup, {
	enterTimeout: [],
	leaveTimeout: []
});

$.extend(DialogHelper, {
	show: function(dom) {
		var top = arguments[1] || '40px';
		if ($(document).getDialogHelper().length)
			$(document).getDialogHelper().remove();
		$('body').prepend('<div id="overlay"></div>');
		dom.insertAfter($(document).getDialogHelper());
		dom.wrapAll('<div id="dialog_container"></div>').parent().css('top', top);
		DialogHelper.bind($(document).getDialogHelper());
	},
	close: function() {
	if ($(document).getDialogHelper().length)
		$(document).getDialogHelper().hide();
	}
});


$.extend(EntryList, {
	ajaxLoad: function(options) {
		if (options.url == null) {
			return false;
		}
		options.offset = options.offset || 1;
		options.queue = options.queue || false;
		var entryList = $(document).getEntryList();
		$.ajaxManager({
			name: 'loadEntryList',
			queue: options.queue,
			type: 'GET',
			url:  options.url + '?start=' + options.offset,
			beforeSend: function() {
				var loader = $("<li id='loader'><img src='/images/loader.png' /></li>");
				switch(options.action) {
					case 'replace':
						$(document).getEntryList().empty();
						entryList.scrollLeft(0);
						entryList.append(loader.addClass('center'));
						break;
					case 'append' :
						entryList.append(loader);
						break;
				}
			},
			success: function(data) {
				if ($.trim(data).length) {
					var result = $(data);
					switch(options.action) {
						case 'append':
							var prevPage = entryList.getEntry();
							result.getUnit().each(function() {
								if (prevPage.filter("[uuid='" + $(this).attr('uuid') + "']").length == 0) {
									entryList.append($(this));
								}
							});
							break;
						case 'replace':
							entryList.html(result);
							if(!result.getEntry().length) {
								$('.' + ($(document).getTab().filter('.selected').attr('id') + '_empty')).show();
								Entry.bind(entryList, true);
								Dialog.bind(entryList, true);
							}
							UserActions.bind(entryList.getPrivateUser(), true);
							Notifier.removeDuplicate();
							break;
					}
					bindEntryUnits(entryList.getUnit());
				}
				EntryList.isLastPage = !($(data).getEntry().length == EntryList.ENTRIES_PER_PAGE);
				entryList.find('#loader').remove();
				entryList.trigger('load');
				Analytics.trackEvent('entry_list', options.action, options.url, options.offset);
			}
		});
		return false;
	},
	switchTo: function(tabName) {
		if (!$(document).getEntryList().length) { return false; }
		$(document).getTab().removeClass('selected').filter("[name=" + tabName + "]").click();
	},
	currentTab: function() {
		if (EntryList.exists()) {
			return $(document).getTab().filter('.selected');
		} else {
			return false;
		}
	},
	currentTabAttr: function() {
		if (!EntryList.currentTab().length) {
			return {};
		} else {
			return { name: EntryList.currentTab().attr('name'), cat: EntryList.currentTab().attr('cat'), entity_id: EntryList.currentTab().attr('entity_id') };
		}
	},
	getTab: function(tabName) {
		if (typeof tabName == 'object') {
			return $(document).getTab().filter('[name="' + tabName.name + '"][cat="' + tabName.cat + '"]');
		} else {
			return $(document).getTab();
		}
	},
	reloadTab: function(url) {
		if (!EntryList.exists()) { return false; }

		var currentTabAttr = EntryList.currentTabAttr();
		$.ajaxManager({
			name: 'reloadTab',
			type: 'GET',
			url: url || $(document).getEditTab().attr('reload_link'),
			success: function(data) {
				if ($(data).filter('#status').length) {
					return false;
				}

				var tabsAndEditBtn = $(document).getTab().siblings().not(".close_all_btn");
				var tabBar = tabsAndEditBtn.parent();
				tabsAndEditBtn.remove();
				tabBar.prepend($(data));
				EntryList.bind($(document).getTab());

				if ($(data).find('.remove_tab').length) {
					EntryList.bind($(document).getRemoveTab());
					EntryList.bind($(document).getEditComplete());
				} else {
					EntryList.getTab(currentTabAttr).addClass('selected');
					EntryList.bind($(document).getEditTab());
				}
			}
		});
		return false;
	},
	exists: function() {
		return (!!$(document).getEntryList().length);
	},
	getMoreEntry: function() {
		var entryCount = EntryList.count();
		if (entryCount < EntryList.ENTRIES_PER_PAGE)
			return false;

		var entryList = $('#entry_list');
		if (EntryList.isLastPage || entryCount - Math.round(entryList.scrollLeft() / EntryList.ENTRY_WIDTH) > 1.5 * EntryList.entriesPerScreen)
			return false;

		EntryList.ajaxLoad({
			action: 'append',
			url: $(document).getTab().filter('.selected').attr('attr'),
			offset: entryCount + 1
		});
	},
	readEntry: function(entryOrView) {
		if (!entryOrView.hasClass('updated')) { return false; }

		$.ajaxManager({
			name:   'readEntry',
			queue:  true,
			type:   'POST',
			global: false,
			url:    '/entries/read/' + entryOrView.attr('uuid'),
			data:   { _method: 'put' },
			success: function() {
				$('[uuid=' + entryOrView.attr('uuid') + ']').removeClass('updated');
			}
		});
	},
	checkForUnread: function() {
		setInterval(function() {
			$('#entry_views .entry_view.updated.active').each(function() {
				EntryList.readEntry($(this));
			})
		}, 3000);
	}
});

$.extend(StatusBar, {
	display: function(message) {
		if (typeof message === 'string' && message.length) {
			var status = $('<li>' + message + '</li>');
		} else if (typeof message === 'object' && $(message).text().length) {
			var status = $(message).wrap('<li></li>').parent();
		} else {
			return $([]);
		}
		var displayTime = typeof arguments[1] === 'number' && arguments[1] >= 0 || arguments[1] === false ?
			arguments[1] : 5000;
		if (typeof displayTime === 'number') {
			setTimeout(function() { status.fadeOut(); }, displayTime);
		}
		if (displayTime === false) {
			status.addClass('sticky');
			$(document).getStatusBar().find('li:not(".sticky")').remove();
		}
		status.appendTo($(document).getStatusBar().find('ul')).show();
		return status;
	},
	clear: function() {
		$(document).getStatusBar().find('li').remove();
	}
});


var LiveUpdate = {
	opts: {
		url: '/pusher',
		failureCount: 0
	},
	clientID: function() {
		if (!$(document).data('clientID')) {
			$(document).data('clientID', Math.floor(Math.random() * 1000000));
		}
		return $(document).data('clientID');
	},
	start: function() {
		var entryViews = $('.entry_view');

		if (!entryViews.length && !EntryList.exists()) { return false }

		var channelKeys = [];
		$.each(entryViews, function() {
			channelKeys.push($(this).attr('uuid').slice(0, 8));
		});

		var currentTabName = EntryList.currentTab() && EntryList.currentTab().attr('name');
		this.stop(); // stop current long polling request
		this.xhr = $.ajax({
			type:     'GET',
			url:      this.opts.url + '?transport=long_poll&channel_keys=' + channelKeys.join('%26') + '&tab=' + currentTabName + '&client_id=' + this.clientID(),
			dataType: 'json',
			cache:    false,
			global:   false, // Do not trigger global ajax event handler
			success: function(data) {
				LiveUpdate._processData(data);
				LiveUpdate.start();
			},
			error: function(xhr) {
				// Server side timeout cause jQuery fires error callback, though status code is 200
				(xhr.status == 200) ? LiveUpdate.start() : LiveUpdate.ping();
			}
		});
	},
	stop: function() {
		if (!this.xhr) { return }

		var deleteQueue = arguments[0] || false;
		if (this.xhr) { this.xhr.abort(); }
		if (deleteQueue) {
			$.ajax({
				async: false,
				type: 'POST',
				data: { _method: 'delete' },
				url:  this.opts.url + '?client_id=' + this.clientID()
			});
		}
	},
	ping: function() {
		$.ajax({
			type:    'GET',
			url:     LiveUpdate.opts.url + '/ping',
			global:  false,
			cache:   false,
			timeout: 3000,
			success: function() {
				StatusBar.clear();
				LiveUpdate.opts.failureCount = 0;
				LiveUpdate.start();
			},
			error:   function() {
				StatusBar.clear();
				StatusBar.display('<p id="status" class="error offline">无法与服务器连接</p>', false);
				LiveUpdate.opts.failureCount ++;
				setTimeout(LiveUpdate.ping, 5000 * LiveUpdate.opts.failureCount);
			}
		});
	},
	_processData: function(data) {
		function isDirect(data) {
			return (data.hasOwnProperty('fixed') && data.fixed == 'direct');
		}

		function isGroup(data) {
			return (data.hasOwnProperty('group'));
		}

		function isPublic(data) {
			return (!isGroup(data) && !isDirect(data));
		}

		function getCategory(data) {
			if (data.user) {
				return { cat: 'user', name: data.user };
			} else if (data.group) {
				return { cat: 'group', name: data.group };
			} else if (data.fixed == 'direct') {
				return { cat: 'fixed', name: 'direct' };
			} else {
				return {};
			}
		}

		function atProperTab(data) {
			return ((EntryList.currentTabAttr().name == 'friends' && isPublic(data)) || (EntryList.currentTabAttr().name == data[EntryList.currentTabAttr().cat]) || ((EntryList.currentTabAttr().name == 'direct') && isDirect(data)));
		}

		switch (data.type) {
		case 'comment':
			var entryView = $('.entry_view[uuid="' + data.entry_uuid + '"]');
			if (entryView.size()) {
				var comments = entryView.getComments(),
				    comment  = comments.contents().filter("#" + data.comment_id);

				var heatBar  = EntryList.exists() && $(document).getEntry().filter("[uuid=" + data.entry_uuid + "]").getHeatBar();
				if (comment.size()) {
					comment.replaceWith($(data.html));
					if (!$(data.html).getUser().length && heatBar.length) {
						heatBar.trigger('liveupdate', true);
					}
				} else {
					comments.append($(data.html));
					comments.scrollTop(comments.attr('scrollHeight') - 100);
					heatBar.length && heatBar.trigger('liveupdate');
				}
				Comment.bind(comments, true);
				Popup.bind(comments, true);
				entryView.addClass('updated');
				EntryListCache.load();
				Notifier.trigger(data);
			} else {
				// Fetch the corresponding entry if it doesn't exists
				LiveUpdate._fetchEntry(data.entry_uuid, data);
			}
			break;
		case 'entry':
			if ($('[uuid=' + data.entry_uuid + ']').length) {
				$('[uuid=' + data.entry_uuid + ']').remove();
				$('[entry_uuid=' + data.entry_uuid + ']').remove();
				return;
			}

			var entry = $(data.html).clone();
			if (atProperTab(data)) {
				$(document).getEntryList().prepend(entry);
			} else {
				entry.attr(getCategory(data));
				$(document).getEntryListCache().prepend(entry).load();
			}
			Notifier.trigger(data);
			bindEntryUnits(entry);
			break;
		}
	},
	_fetchEntry: function(uuid, orignal) {
		$.ajax({
			type:    'GET',
			url:     '/entries/' + uuid,
			success: function(data, opt) {
				opt = opt || {};
				LiveUpdate._processData($.extend({}, orignal, {
					type:       'entry',
					entry_uuid: uuid,
					html:       data
				}));
			}
		})
	}
}

var Notifier = $.extend({}, Model({
	ui: {
		Notifier: {
			selector: '#notifier',
			liveupdate: 'show'
		},
		History: {
			selector: '#notifier_toggle',
			click: 'toggleHistory'
		}
	},
	show: function(event, data) {
		// return if the related entry view is already opened
		if ($('#entry_views .entry_view').filter('[uuid=' + data.entry_uuid + ']').size() > 0 || !$(data.html).getUser().size()) { return }

		switch (data.type) {
		case 'entry':
			var avatar  = $(data.html).getView().find('.body .avatar:first img').addClass('avatar');
			var content = $(data.html).getEntry().find('.body').clone().removeClass('body').addClass('entry');
			break;
		case 'comment':
			var content = $(data.html).clone().addClass('comment');
			content.getCommentOptions().remove();
			content.find('a.user').replaceWith("<span class='user'>" + content.find('a').text() + "</span>");
			break;
		}

		content.addClass('content')
		.wrap("<div class='item'><div class='left'><div class='right'><div class='body'></div></div></div></div>")
		.parents('.item').attr('entry_uuid', data.entry_uuid)
		.prepend($("<div class='tl'><div class='tr'><div class='top'></div></div></div>"))
		.append($("<div class='bl'><div class='br'><div class='bottom'></div></div></div>"))
		.appendTo($(this)).fadeIn();

		if (avatar) { content.before(avatar); }

		var entry = $(document).getEntry().filter('[uuid=' + data.entry_uuid + ']');
		content.click(function() {
			if (!$('#entry_views .entry_view').filter('[uuid=' + data.entry_uuid + ']').size()) {
				  entry.click();

				  EntryListCache.removeEntry(data.entry_uuid);
				  EntryListCache.load();
			}
			$(this).getNotifier().find('[entry_uuid=' + data.entry_uuid + ']').remove();

			// if notifier was empty, exit both history mode and stop blinking
			if (!$('#notifier .item').length) {
				$(this).getNotifier().removeClass('history');
				$('#notifier_toggle').removeClass('pressed unread');
			}
		});

		if (!$(this).hasClass('history')) {
			content.parents('.item').data('timer', setTimeout(function() {
				content.parents('.item').fadeOut();
				Notifier.startBlink();
			}, 10000));
		}
		if ($.cookie('username') != data.author ) { $('title').trigger('liveupdate'); }
	},
	toggleHistory: function() {
		var items = $('#notifier .item');
		var notifier = $('#notifier');
		if (!items.length) {
			StatusBar.clear();
			StatusBar.display('没有未查看的提醒');
			return false;
		}
		if (!notifier.hasClass('history')) {
			items.each(function() { clearTimeout($(this).data('timer')); });
			items.slideDown();
			$(this).addClass('pressed');
			notifier.addClass('history');
			Notifier.stopBlink();
		} else {
			items.slideUp();
			$(this).removeClass('pressed');
			notifier.removeClass('history');
		}
	}
}), {
	startBlink: function() {
		if ($('#notifier_toggle').queue().length || !$('#notifier .item').length) { return false; }

		$('#notifier_toggle').addClass('unread').fadeOut('slow').fadeIn('slow', function() {
			if ($(this).hasClass('unread')) { Notifier.startBlink(); }
		})
	},
	stopBlink: function() {
		$('#notifier_toggle').removeClass('unread');
	},
	removeDuplicate: function() {
		var toBeRemoved = [];
		$('#notifier .item').each(function () {
			if ($('#entry_list .entry').filter('[uuid=' + $(this).attr('entry_uuid') + ']').length) {
				toBeRemoved.push($(this).attr('entry_uuid'));
			}
		});
		$.each(toBeRemoved, function(index, value) { $('#notifier .item[entry_uuid=' + value + ']').remove() });
	},
	trigger: function(data) {
		if ($(document).getNotifier().length) {
			$(document).getNotifier().trigger('liveupdate', data);
		}
	}
});

var PageTitle = Model({
	ui: {
		PageTitle: {
			selector: 'head title',
			liveupdate: 'startBlink'
		}
	},
	startBlink: function() {
		if ($(this).data('blinkID')) { return }

		var title        = $(this),
		    originalText = title.text(),
		    message      = '接收到新内容';

		title.data('blinkID', setInterval(function() {
			document.title = document.title == message ? originalText : message;
		}, 1000));
	        $(document).mousemove(function() {
			clearInterval(title.data('blinkID'));
			document.title = originalText;
			title.removeData('blinkID').unbind('mousemove');
		});
	}
});

function appendView(newView) {
	var currentPos = $('#entry_views .entry_view.active').position();
	var newPos = { left: $(document).width() * 0.30, top: $(document).height() * 0.1 };
	if (currentPos !== undefined) {
		var radius = 0.15 * $(document).width();
		var offset = $(document).width() * 0.015;
		var distance = Math.floor(Math.sqrt(Math.pow(currentPos.left - newPos.left, 2) + Math.pow(currentPos.top - newPos.top, 2)));

		if (currentPos && distance < radius) {
			if (currentPos.top + newView.height() + offset + $('#entry_panel').height() < $(document).height()) {
				newPos.top = currentPos.top + offset;
			}
			newPos.left = currentPos.left + offset;
		}
	}
	newView.css('left', newPos.left).css('top', newPos.top).appendTo($('#entry_views')).mousedown().show();
	if ($.cookie('username') == newView.attr('author')) { newView.addClass('removeble'); }

	Analytics.trackEvent('entry', 'view', newView.attr('uuid'));
}

function bindEntryUnits(entryUnits) {
	Entry.bind(entryUnits);
	Comment.bind(entryUnits, true);
	Favorite.bind(entryUnits, true);
	Popup.bind(entryUnits, true);
	CopyEntryUrl.bind(entryUnits, true);
	EntryDelete.bind(entryUnits, true);
	EntryList.bind(entryUnits);
	EntryList.bindDraggable();
}

function fixBackgroundPos() {
	$('body').height($(document).height());
}

var Analytics = {
	init: function() {
		_gaq.push(['_setAccount', $('#google_analytics').attr('attr')]);
		this.trackPage();
	},
	trackPage: function(uri) {
		_gaq.push(['_trackPageview', uri]);
	},
	trackEvent: function(category, action, opt_label, opt_value) {
		_gaq.push(['_trackEvent', category, action, opt_label, opt_value]);
	}
}

var _gaq = _gaq || [];

$(document).ready(function() {

	Model.bind();

	EntryList.ENTRY_WIDTH = 200;
	EntryList.entriesPerScreen = Math.round($('#entry_list').width() / EntryList.ENTRY_WIDTH);
	EntryList.ENTRIES_PER_PAGE = 30;
	EntryList.isLastPage = false;
	EntryList.count = function() { return $(document).getEntry().length; }

	fixBackgroundPos();
	$(window).resize(function() {
		fixBackgroundPos();
	});

	(EntryList.bindDraggable = function() {
		$('.entry_view:not(".single")').draggable({
			containment: 'document',
			handle: '.tl'
		});
	})();

	$("#back_to_head").mousedown(function() {
		$(this).addClass('down');
		$('#entry_list').scrollLeft(0);
		$(this).bind('mouseout mouseup', function() {
			$(this).removeClass('down');
		})
	});

	$('#entry_panel li.tab:first').addClass('selected');

	$('#screen.loading').fadeOut();
	StatusBar.display($(document).getStatusBar().getStatus(), false).find('a').click(function() { $(this).parents('li').remove(); });

	LiveUpdate.start();
	$(window).unload(function() {
		LiveUpdate.stop(true);
	})

	EntryListCache.showAll();
	Analytics.init();
	EntryList.checkForUnread();
});

jQuery(function($) {
	function params() {
		if ($.browser.msie) {
			return { acceleration: -9650, interval: 20, speed: -3300 }
		} else if ($.browser.mozilla) {
			return { acceleration: -8000, interval: 15, speed: -2400 }
		} else if ($.browser.safari) {
			return { acceleration: -9200, interval: 15, speed: -2500 }
		} else {
			return { acceleration: -8650, interval: 20, speed: -3300 }
		}
	}

	var scrollLeft = $('#left'), scrollRight = $('#right'), scrollButtons = scrollLeft.add(scrollRight);
	scrollButtons.mousedown(function(event) {
		var button = $(this);
		if (!event.button || typeof event.button === 'number') { button.addClass('down'); }
		var speed = button.is('#left') ? params().speed : params().speed * (-1);
		entryList.scrollWithDeceleration(speed);
		EntryList.getMoreEntry();
	}).bind('mouseout mouseup', function() {
		$(this).removeClass('down');
	});

	$(document).keydown(function(event) {
		var key = event.keyCode || event.which;
		if ($(event.target).is('textarea') || $(event.target).is('input')) {
			return;
		}
		if (key == $.ui.keyCode.LEFT) {
			scrollLeft.mousedown();
		}
		if (key == $.ui.keyCode.RIGHT) {
			scrollRight.mousedown();
		}
		if (event.metaKey && key == $.ui.keyCode.ENTER) {
			var bubble = $(document).getBubble()
			if (bubble.hasClass('closed'))
				bubble.click();
			else
				bubble.getEntryTitle().focus();
		}
	});

	$(document).keyup(function(event) {
		var key = event.keyCode || event.which;
		if ($(event.target).is('textarea') || $(event.target).is('input')) {
			return;
		}
		if (key == $.ui.keyCode.LEFT) {
			scrollLeft.removeClass('down');
		}
		if (key == $.ui.keyCode.RIGHT) {
			scrollRight.removeClass('down');
		}
	});

	var entryList = $.extend($('#entry_list'), {
		scrollWithDeceleration: (function(acceleration, interval) {
			function S(v, t, a) {
				return Math.round(v * t + 0.5 * a * t * t);
			}

			function movable(speed, left, max) {
				return speed && (speed < 0 || left < max) && (speed > 0 || left > 0);
			}

			function scroll(speed, callback) {
				if (timers.length) { timers.cancelAll(); }
				if (!speed) { speed = curSpeed; }

				var displacement = 0,
					left = newLeft = this.scrollLeft(),
					max = this[0].scrollWidth - this.innerWidth(),
					a = (speed >= 0 ? acceleration : -acceleration),
					nextSpeed = speed + a * interval / 1000;

				if (nextSpeed * speed <= 0) { nextSpeed = 0; }

				if (movable(speed, left, max)) {
					displacement = S(speed, interval / 1000, a);
					newLeft += displacement;
					this.scrollLeft(newLeft);
					displacement = this.scrollLeft() - left;
				}

				if (displacement && movable(nextSpeed, newLeft, max)) {
					var entryList = this,
						handler = function() { scroll.call(entryList, null, callback); };
					timers.push(window.setTimeout(handler, interval));
				} else {
					nextSpeed = 0;
				}
				curSpeed = nextSpeed;

				if (typeof callback === 'function') callback(speed, nextSpeed, displacement);
				return displacement;
			}

			var curSpeed = 0,
				timers = $.extend([], {
					cancelAll: function() {
						while (this.length) { window.clearTimeout(this.pop()); }
					}
				});
			if (acceleration > 0) { acceleration = -acceleration; }
			return scroll;
		})(params().acceleration, params().interval)
	}).mousewheel(function(event, delta) {
		var speed = Math.round(params().speed * (delta / Math.abs(delta))), e = event.originalEvent;
		if (e && (e.wheelDeltaX || e.wheelDeltaY)) {
			speed = -2 * (Math.abs(e.wheelDeltaX) > Math.abs(e.wheelDeltaY) ? e.wheelDeltaX : e.wheelDeltaY);
		}
		var speedTracker = function(speed, nextSpeed, displacement) {
			scrollButtons.removeClass('down');
			if (nextSpeed && displacement) {
				if (speed > 0) {
					scrollRight.addClass('down');
				} else if (speed < 0) {
					scrollLeft.addClass('down');
				}
			}
		};
		// In Safari and Chrome, scrolling stops too quickly by direct calling scrollWithDeceleration()
		$.browser.safari ? (delta < 0 ? scrollRight.mousedown() : scrollLeft.mousedown()) : entryList.scrollWithDeceleration(speed, speedTracker);
		EntryList.getMoreEntry();
	});
});
