mirror of
https://github.com/cnodejs/nodeclub.git
synced 2025-12-08 19:55:55 +00:00
508 lines
16 KiB
HTML
508 lines
16 KiB
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_id)) { %>
|
||
<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>
|