views/topic/index.html
<div id='sidebar'>
<div class='panel'>
<div class='header'>
<span class='col_fade'>作者</span>
</div>
<div class='inner'>
<%- partial('../user/card', { object: topic.author, as: 'user' }) %>
</div>
</div>
<% if (!current_user || !current_user.isAdvanced) { %>
<%- partial('../_ads') %>
<% } %>
<div class='panel'>
<div class='header'>
<span class='col_fade'>作者其它话题</span>
</div>
<div class='inner'>
<% if (typeof(author_other_topics) === 'undefined' || author_other_topics.length > 0) { %>
<ul class='unstyled'>
<%- partial('../topic/small', { collection: author_other_topics, as: 'topic' }) %>
</ul>
<% } else { %>
<p>无</p>
<% } %>
</div>
</div>
<div class='panel'>
<div class='header'>
<span class='col_fade'>无人回复的话题</span>
</div>
<div class='inner'>
<% if (typeof(no_reply_topics) !== 'undefined' && no_reply_topics.length > 0) { %>
<ul class='unstyled'>
<%- partial('../topic/small', { collection: no_reply_topics, as: 'topic' }) %>
</ul>
<% } else { %>
<p>无</p>
<% } %>
</div>
</div>
</div>
<div id='content'>
<div class='panel'>
<div class='header topic_header'>
<span class="topic_full_title">
<%- partial('./_top_good', {topic: topic}) %>
<%= topic.title %>
</span>
<div class="changes">
<span>
发布于 <%= topic.create_at_ago() %>
</span>
<span>
作者 <a href="/user/<%= topic.author.loginname %>"><%= topic.author.loginname %></a>
</span>
<span>
<%= topic.visit_count %> 次浏览
</span>
<% if (topic.create_at_ago() != topic.update_at_ago()) { %>
<span>
最后一次编辑是 <%= topic.update_at_ago() %>
</span>
<% } %>
<% if (topic.tab) { %>
<span> 来自 <%= topic.tabName %></span>
<%}%>
<% if (current_user) { %>
<input class="span-common <%= is_collect ? '' : 'span-success' %> pull-right collect_btn" type="submit" value="<%= is_collect ? '取消收藏' : '收藏' %>" action="<%= is_collect ? 'de_collect' : 'collect' %>">
<%}%>
</div>
<% if (current_user) { %>
<div id="manage_topic">
<% if (current_user.is_admin) { %>
<a href='/topic/<%= topic._id %>/top' data-method="post">
<% if (topic.top) { %>
<i class="fa fa-lg fa-star-o" title='取消置顶'></i>
<% } else { %>
<i class="fa fa-lg fa-star" title='置顶'/></i>
<% } %>
</a>
<a href='/topic/<%= topic._id %>/good' data-method="post">
<% if (topic.good) { %>
<i class="fa fa-lg fa-heart-o" title="取消精华"></i>
<% } else { %>
<i class="fa fa-lg fa-heart" title="加精华"></i>
<% } %>
</a>
<a href='/topic/<%= topic._id %>/lock' data-method="post">
<% if (topic.lock) { %>
<i class="fa fa-lg fa-unlock" title='取消锁定'></i>
<% } else { %>
<i class="fa fa-lg fa-lock" title='锁定(不可再回复)'/></i>
<% } %>
</a>
<a href='/topic/<%= topic._id %>/edit'>
<i class="fa fa-lg fa-pencil-square-o" title='编辑'></i></a>
<a href='javascript:;'
data-id="<%= topic._id %>"
class='delete_topic_btn'>
<i class="fa fa-lg fa-trash" title='删除'></i></a>
<% } else { %>
<% if (current_user._id.equals(topic.author)) { %>
<a href='/topic/<%= topic._id %>/edit'>
<i class="fa fa-lg fa-pencil-square-o" title='编辑'></i></a>
<a href='javascript:;'
data-id="<%= topic._id %>"
class='delete_topic_btn'>
<i class="fa fa-lg fa-trash" title='删除'></i></a>
<% } %>
<% } %>
</div>
<% } %>
</div>
<div class='inner topic'>
<div class='topic_content'>
<%- markdown(topic.linkedContent) %>
</div>
</div>
</div>
<% if (topic.replies && topic.replies.length > 0) { %>
<div class='panel'>
<div class='header'>
<span class='col_fade'><%= topic.replies.length %> 回复</span>
</div>
<%- partial('../reply/reply', topic.replies) %>
</div>
<% } %>
<% if (current_user && typeof(topic) !== 'undefined') { %>
<div class='panel'>
<div class='header'>
<span class='col_fade'>添加回复</span>
</div>
<div class='inner reply'>
<form id='reply_form' action='/<%= topic._id %>/reply' method='post'>
<div class='markdown_editor in_editor'>
<div class='markdown_in_editor'>
<textarea class='editor' name='r_content' rows='8'></textarea>
<div class='editor_buttons'>
<input class='span-primary submit_btn' type="submit" data-loading-text="回复中.." value="回复<%= topic.lock ? '(此主题已锁定)' : ''%>" <%= topic.lock ? 'disabled="disabled"' : ''%>>
</div>
</div>
</div>
<input type='hidden' name='_csrf' id="_csrf" value='<%= csrf %>'/>
</form>
</div>
</div>
<% } %>
</div>
<div class="replies_history">
<div class="inner_content"></div>
<div class="anchor"></div>
</div>
<!-- 预览模态对话框 -->
<div class="modal fade" id="preview-modal">
<div class="modal-body" style="max-height: initial;">
<img src="" alt="点击内容或者外部自动关闭图片预览" id="preview-image">
</div>
</div>
<% if (current_user && typeof(topic) !== 'undefined') { %>
<!-- markdown editor -->
<%- partial('../includes/editor') %>
<script>
$(document).ready(function () {
// 获取所有回复者name
var allNames = $('.reply_author').map(function (idx, ele) {
return $(ele).text().trim();
}).toArray();
allNames.push($('.user_card .user_name').text())
allNames = _.uniq(allNames);
// END 获取所有回复者name
// 编辑器相关
$('textarea.editor').each(function(){
var editor = new Editor({
status: []
});
var $el = $(this);
editor.render(this);
//绑定editor
$(this).data('editor', editor);
var $input = $(editor.codemirror.display.input);
$input.keydown(function(event){
if (event.keyCode === 13 && (event.ctrlKey || event.metaKey)) {
event.preventDefault();
$el.closest('form').submit();
}
});
// at.js 配置
var codeMirrorGoLineUp = CodeMirror.commands.goLineUp;
var codeMirrorGoLineDown = CodeMirror.commands.goLineDown;
var codeMirrorNewlineAndIndent = CodeMirror.commands.newlineAndIndent;
$input.atwho({
at: '@',
data: allNames
})
.on('shown.atwho', function () {
CodeMirror.commands.goLineUp = _.noop;
CodeMirror.commands.goLineDown = _.noop;
CodeMirror.commands.newlineAndIndent = _.noop;
})
.on('hidden.atwho', function () {
CodeMirror.commands.goLineUp = codeMirrorGoLineUp;
CodeMirror.commands.goLineDown = codeMirrorGoLineDown;
CodeMirror.commands.newlineAndIndent = codeMirrorNewlineAndIndent;
});
// END at.js 配置
});
// END 编辑器相关
// 评论回复
$('#content').on('click', '.reply2_btn', function (event) {
var $btn = $(event.currentTarget);
var parent = $btn.closest('.reply_area');
var editorWrap = parent.find('.reply2_form');
parent.find('.reply2_area').prepend(editorWrap);
var textarea = editorWrap.find('textarea.editor');
var user = $btn.closest('.author_content').find('.reply_author').text().trim();
var editor = textarea.data('editor');
editorWrap.show('fast', function () {
var cm = editor.codemirror;
cm.focus();
if(cm.getValue().indexOf('@' + user) < 0){
editor.push('@' + user + ' ');
}
});
});
$('#content').on('click', '.reply2_at_btn', function (event) {
var $btn = $(event.currentTarget);
var editorWrap = $btn.closest('.reply2_area').find('.reply2_form');
$btn.closest('.reply2_item').after(editorWrap);
var textarea = editorWrap.find('textarea.editor');
var user = $btn.closest('.reply2_item').find('.reply_author').text().trim();
var editor = textarea.data('editor');
editorWrap.show('fast', function () {
var cm = editor.codemirror;
cm.focus();
if(cm.getValue().indexOf('@' + user) < 0){
editor.push('@' + user + ' ');
}
});
});
// END 评论回复
// 加入收藏
$('.collect_btn').click(function () {
var $me = $(this);
var action = $me.attr('action');
var data = {
topic_id: '<%= topic._id %>',
_csrf: '<%= csrf %>'
};
var $countSpan = $('.collect-topic-count');
$.post('/topic/' + action, data, function (data) {
if (data.status === 'success') {
if (action == 'collect') {
$me.val('取消收藏');
$me.attr('action', 'de_collect');
} else {
$me.val('收藏');
$me.attr('action', 'collect');
}
$me.toggleClass('span-success');
}
}, 'json');
});
// END 加入收藏
// 删除回复
$('#content').on('click', '.delete_reply_btn, .delete_reply2_btn', function (event) {
var $me = $(event.currentTarget);
if (confirm('确定要删除此回复吗?')) {
var reply_id = null;
if ($me.hasClass('delete_reply_btn')) {
reply_id = $me.closest('.reply_item').attr('reply_id');
}
if ($me.hasClass('delete_reply2_btn')) {
reply_id = $me.closest('.reply2_item').attr('reply_id');
}
var data = {
reply_id: reply_id,
_csrf: "<%- csrf %>"
};
$.post('/reply/' + reply_id + '/delete', data, function (data) {
if (data.status === 'success') {
if ($me.hasClass('delete_reply_btn')) {
$me.closest('.reply_item').remove();
}
if ($me.hasClass('delete_reply2_btn')) {
$me.closest('.reply2_item').remove();
}
}
}, 'json');
}
return false;
});
// END 删除回复
// 删除话题
$('.delete_topic_btn').click(function () {
var topicId = $(this).data('id');
if (topicId && confirm('确定要删除此话题吗?')) {
$.post('/topic/' + topicId + '/delete', { _csrf: $('#_csrf').val() }, function (result) {
if (!result.success) {
alert(result.message);
} else {
location.href = '/';
}
});
}
return false;
});
// END 删除话题
// 用户 hover 在回复框时才显示点赞按钮
$('.reply_area').hover(
function () {
$(this).find('.up_btn').removeClass('invisible');
},
function () {
var $this = $(this);
if ($this.find('.up-count').text().trim() === '') {
$this.find('.up_btn').addClass('invisible');
}
});
// END 用户 hover 在回复框时才显示点赞按钮
});
</script>
<% } %>
<script type="text/javascript">
(function(){
var timer = null; //对话框延时定时器
// 初始化 $('.replies_history')
var $repliesHistory = $('.replies_history');
var $repliesHistoryContent = $repliesHistory.find('.inner_content');
$repliesHistory.hide();
// END
// 鼠标移入对话框清除隐藏定时器;移出时隐藏对话框
$repliesHistory.on('mouseenter', function(){
clearTimeout(timer);
}).on('mouseleave', function(){
$repliesHistory.fadeOut('fast');
});
// 显示被 at 用户的本页评论
if ($('.reply2_item').length === 0) {
// 只在流式评论布局中使用
$('#content').on('mouseenter', '.reply_content a', function (e) {
clearTimeout(timer);
var $this = $(this);
if ($this.text()[0] === '@') {
var thisText = $this.text().trim();
var loginname = thisText.slice(1);
var offset = $this.offset();
var width = $this.width();
var mainOffset = $('#main').offset();
$repliesHistory.css('left', offset.left-mainOffset.left+width+10); // magic number
$repliesHistory.css('top', offset.top-mainOffset.top-10); // magic number
$repliesHistory.css({
'z-index': 1,
});
$repliesHistoryContent.empty();
var chats = [];
var replyToId = $this.closest('.reply_item').attr('reply_to_id');
while (replyToId) {
var $replyItem = $('.reply_item[reply_id=' + replyToId + ']');
var replyContent = $replyItem.find('.reply_content').text().trim();
if (replyContent.length > 0) {
chats.push([
$($replyItem.find('.user_avatar').html()).attr({
height: '30px',
width: '30px',
}), // avatar
(replyContent.length>300?replyContent.substr(0,300)+'...':replyContent), // reply content
'<a href="#'+replyToId+'" class="scroll_to_original" title="查看原文">↑</a>'
]);
}
replyToId = $replyItem.attr('reply_to_id');
}
if(chats.length > 0) {
chats.reverse();
$repliesHistoryContent.append('<div class="title">查看对话</div>');
chats.forEach(function (pair, idx) {
var $chat = $repliesHistoryContent.append('<div class="item"></div>');
$chat.append(pair[0]); // 头像
$chat.append($('<span>').text(pair[1])); // 内容
$chat.append(pair[2]); // 查看原文 anchor
});
$repliesHistory.fadeIn('fast');
}else{
$repliesHistory.hide();
}
}
}).on('mouseleave', '.reply_content a', function (e) {
timer = setTimeout(function(){
$repliesHistory.fadeOut('fast');
}, 500);
});
}
// END 显示被 at 用户的本页评论
})();
// 点赞
$('.up_btn').click(function (e) {
var $this = $(this);
var replyId = $this.closest('.reply_area').attr('reply_id');
$.ajax({
url: '/reply/' + replyId + '/up',
method: 'POST',
}).done(function (data) {
if (data.success) {
$this.removeClass('invisible');
var currentCount = Number($this.next('.up-count').text().trim()) || 0;
if (data.action === 'up') {
$this.next('.up-count').text(currentCount + 1);
$this.addClass('uped');
} else {
if (data.action === 'down') {
$this.next('.up-count').text(currentCount - 1);
$this.removeClass('uped');
}
}
} else {
alert(data.message);
}
}).fail(function (xhr) {
if (xhr.status === 403) {
alert('请先登录,登陆后即可点赞。');
}
});
});
// END 点赞
// 图片预览
(function(){
var $previewModal = $('#preview-modal');
var $previewImage = $('#preview-image');
var $body = $('body'); // cache
$(document).on('click', '.markdown-text img', function(e) {
var $img = $(this);
// 图片被a标签包裹时,不显示弹层
if ($img.parent('a').length > 0) {
return;
}
showModal($img.attr('src'));
});
$previewModal.on('click', hideModal);
$previewModal.on('hidden.bs.modal', function() {
// 在预览框消失之后恢复 body 的滚动能力
$body.css('overflow-y', 'scroll');
})
$previewModal.on('shown.bs.modal', function() {
// 修复上次滚动留下的痕迹,可能会导致短暂的闪烁,不过可以接受
// TODO: to be promote
$previewModal.scrollTop(0);
})
function showModal(src) {
$previewImage.attr('src', src);
$previewModal.modal('show');
// 禁止 body 滚动
$body.css('overflow-y', 'hidden');
}
function hideModal() {
$previewModal.modal('hide');
}
})()
// END 图片预览
</script>