Files
NodeBB/public/src/client/topic/postTools.js

558 lines
15 KiB
JavaScript
Raw Normal View History

'use strict';
2015-10-13 16:36:43 -04:00
/* globals define, app, ajaxify, bootbox, socket, templates, utils, config */
2015-04-01 23:28:21 -04:00
define('forum/topic/postTools', ['share', 'navigator', 'components', 'translator'], function(share, navigator, components, translator) {
2016-02-18 18:32:08 +02:00
var PostTools = {};
2016-06-30 11:38:33 +03:00
var staleReplyAnyway = false;
PostTools.init = function(tid) {
2016-06-30 11:38:33 +03:00
staleReplyAnyway = false;
2015-10-24 20:50:43 -04:00
renderMenu();
addPostHandlers(tid);
2016-02-18 18:32:08 +02:00
share.addShareHandlers(ajaxify.data.title);
addVoteHandler();
PostTools.updatePostCount(ajaxify.data.postcount);
};
2015-10-24 20:50:43 -04:00
function renderMenu() {
$('[component="topic"]').on('show.bs.dropdown', '.moderator-tools', function() {
var $this = $(this);
var dropdownMenu = $this.find('.dropdown-menu');
if (dropdownMenu.html()) {
return;
}
var postEl = $this.parents('[data-pid]');
var pid = postEl.attr('data-pid');
var index = parseInt(postEl.attr('data-index'), 10);
2016-03-01 18:16:51 -05:00
2015-10-24 20:50:43 -04:00
socket.emit('posts.loadPostTools', {pid: pid, cid: ajaxify.data.cid}, function(err, data) {
if (err) {
return app.alertError(err);
}
data.posts.display_move_tools = data.posts.display_move_tools && index !== 0;
2016-03-08 14:38:44 +02:00
2015-10-24 20:50:43 -04:00
templates.parse('partials/topic/post-menu-list', data, function(html) {
translator.translate(html, function(html) {
dropdownMenu.html(html);
2016-03-01 18:16:51 -05:00
$(window).trigger('action:post.tools.load');
2015-10-24 20:50:43 -04:00
});
});
});
});
}
PostTools.toggle = function(pid, isDeleted) {
var postEl = components.get('post', 'pid', pid);
postEl.find('[component="post/quote"], [component="post/favourite"], [component="post/reply"], [component="post/flag"], [component="user/chat"]')
.toggleClass('hidden', isDeleted);
postEl.find('[component="post/delete"]').toggleClass('hidden', isDeleted);
postEl.find('[component="post/restore"]').toggleClass('hidden', !isDeleted);
postEl.find('[component="post/purge"]').toggleClass('hidden', !isDeleted);
postEl.find('.dropdown-menu').html('');
};
PostTools.updatePostCount = function(postCount) {
var postCountEl = components.get('topic/post-count');
postCountEl.html(postCount).attr('title', postCount);
utils.makeNumbersHumanReadable(postCountEl);
navigator.setCount(postCount);
};
function addVoteHandler() {
2016-03-23 09:14:35 -04:00
components.get('topic').on('mouseenter', '[data-pid] [component="post/vote-count"]', loadDataAndCreateTooltip);
2016-03-24 12:53:29 +02:00
components.get('topic').on('mouseout', '[data-pid] [component="post/vote-count"]', function() {
var el = $(this).parent();
el.on('shown.bs.tooltip', function() {
$('.tooltip').tooltip('destroy');
el.off('shown.bs.tooltip');
});
$('.tooltip').tooltip('destroy');
});
}
2016-03-24 12:53:29 +02:00
function loadDataAndCreateTooltip(e) {
e.stopPropagation();
2016-03-23 09:14:35 -04:00
2016-03-24 12:53:29 +02:00
var $this = $(this);
var el = $this.parent();
var pid = el.parents('[data-pid]').attr('data-pid');
$('.tooltip').tooltip('destroy');
2016-03-23 09:14:35 -04:00
$this.off('mouseenter', loadDataAndCreateTooltip);
2016-03-24 12:53:29 +02:00
2015-01-19 17:06:43 -05:00
socket.emit('posts.getUpvoters', [pid], function(err, data) {
2016-03-24 12:53:29 +02:00
if (err) {
return app.alertError(err.message);
}
2016-03-23 09:14:35 -04:00
2016-03-24 12:53:29 +02:00
if (data.length) {
createTooltip(el, data[0]);
}
$this.off('mouseenter').on('mouseenter', loadDataAndCreateTooltip);
});
2016-03-24 12:53:29 +02:00
return false;
}
function createTooltip(el, data) {
2015-10-10 18:06:48 -04:00
function doCreateTooltip(title) {
el.attr('title', title).tooltip('fixTitle').tooltip('show');
}
var usernames = data.usernames;
if (!usernames.length) {
return;
}
if (usernames.length + data.otherCount > 6) {
usernames = usernames.join(', ').replace(/,/g, '|');
translator.translate('[[topic:users_and_others, ' + usernames + ', ' + data.otherCount + ']]', function(translated) {
translated = translated.replace(/\|/g, ',');
2015-10-10 18:06:48 -04:00
doCreateTooltip(translated);
});
} else {
usernames = usernames.join(', ');
2015-10-10 18:06:48 -04:00
doCreateTooltip(usernames);
}
}
function addPostHandlers(tid) {
2015-03-17 14:09:08 -04:00
var postContainer = components.get('topic');
postContainer.on('click', '[component="post/quote"]', function() {
2016-02-18 18:32:08 +02:00
onQuoteClicked($(this), tid);
});
postContainer.on('click', '[component="post/reply"]', function() {
2016-02-18 18:32:08 +02:00
onReplyClicked($(this), tid);
2015-03-20 16:13:34 -04:00
});
$('.topic').on('click', '[component="topic/reply"]', function() {
2016-02-18 18:32:08 +02:00
onReplyClicked($(this), tid);
});
2015-12-21 12:51:49 +02:00
$('.topic').on('click', '[component="topic/reply-as-topic"]', function() {
2016-02-26 22:40:22 +02:00
translator.translate('[[topic:link_back, ' + ajaxify.data.titleRaw + ', ' + config.relative_path + '/topic/' + ajaxify.data.slug + ']]', function(body) {
2016-02-23 12:55:46 -05:00
$(window).trigger('action:composer.topic.new', {
cid: ajaxify.data.cid,
body: body
});
2015-12-21 12:51:49 +02:00
});
});
postContainer.on('click', '[component="post/favourite"]', function() {
favouritePost($(this), getData($(this), 'data-pid'));
});
postContainer.on('click', '[component="post/upvote"]', function() {
return toggleVote($(this), '.upvoted', 'posts.upvote');
});
postContainer.on('click', '[component="post/downvote"]', function() {
return toggleVote($(this), '.downvoted', 'posts.downvote');
});
postContainer.on('click', '[component="post/vote-count"]', function() {
2014-11-15 12:37:22 -05:00
showVotes(getData($(this), 'data-pid'));
});
postContainer.on('click', '[component="post/flag"]', function() {
2015-10-13 16:36:43 -04:00
var pid = getData($(this), 'data-pid');
require(['forum/topic/flag'], function(flag) {
flag.showFlagModal(pid);
});
});
2015-10-13 16:36:43 -04:00
postContainer.on('click', '[component="post/edit"]', function() {
2015-04-01 23:28:21 -04:00
var btn = $(this);
2016-05-21 19:26:06 +03:00
var timestamp = parseInt(getData(btn, 'data-timestamp'), 10);
var postEditDuration = parseInt(ajaxify.data.postEditDuration, 10);
2016-06-28 12:34:09 +03:00
if (checkDuration(postEditDuration, timestamp, 'post-edit-duration-expired')) {
$(window).trigger('action:composer.post.edit', {
pid: getData(btn, 'data-pid')
});
}
});
postContainer.on('click', '[component="post/delete"]', function() {
var btn = $(this);
var timestamp = parseInt(getData(btn, 'data-timestamp'), 10);
var postDeleteDuration = parseInt(ajaxify.data.postDeleteDuration, 10);
if (checkDuration(postDeleteDuration, timestamp, 'post-delete-duration-expired')) {
togglePostDelete($(this), tid);
}
});
function checkDuration(duration, postTimestamp, languageKey) {
if (!ajaxify.data.privileges.isAdminOrMod && duration && Date.now() - postTimestamp > duration * 1000) {
var numDays = Math.floor(duration / 86400);
var numHours = Math.floor((duration % 86400) / 3600);
var numMinutes = Math.floor(((duration % 86400) % 3600) / 60);
var numSeconds = ((duration % 86400) % 3600) % 60;
var msg = '[[error:' + languageKey + ', ' + duration + ']]';
2016-05-21 19:26:06 +03:00
if (numDays) {
if (numHours) {
2016-06-28 12:34:09 +03:00
msg = '[[error:' + languageKey + '-days-hours, ' + numDays + ', ' + numHours + ']]';
2016-05-21 19:26:06 +03:00
} else {
2016-06-28 12:34:09 +03:00
msg = '[[error:' + languageKey + '-days, ' + numDays + ']]';
}
2016-05-21 19:26:06 +03:00
} else if (numHours) {
if (numMinutes) {
2016-06-28 12:34:09 +03:00
msg = '[[error:' + languageKey + '-hours-minutes, ' + numHours + ', ' + numMinutes + ']]';
2016-05-21 19:26:06 +03:00
} else {
2016-06-28 12:34:09 +03:00
msg = '[[error:' + languageKey + '-hours, ' + numHours + ']]';
}
2016-05-21 19:26:06 +03:00
} else if (numMinutes) {
if (numSeconds) {
2016-06-28 12:34:09 +03:00
msg = '[[error:' + languageKey + '-minutes-seconds, ' + numMinutes + ', ' + numSeconds + ']]';
2016-05-21 19:26:06 +03:00
} else {
2016-06-28 12:34:09 +03:00
msg = '[[error:' + languageKey + '-minutes, ' + numMinutes + ']]';
}
2016-05-21 19:26:06 +03:00
}
2016-06-28 12:34:09 +03:00
app.alertError(msg);
return false;
2016-05-21 19:26:06 +03:00
}
2016-06-28 12:34:09 +03:00
return true;
}
2015-10-13 16:36:43 -04:00
postContainer.on('click', '[component="post/restore"]', function() {
togglePostDelete($(this), tid);
});
2015-10-13 16:36:43 -04:00
postContainer.on('click', '[component="post/purge"]', function() {
purgePost($(this), tid);
});
2015-10-13 16:36:43 -04:00
postContainer.on('click', '[component="post/move"]', function() {
openMovePostModal($(this));
});
2015-10-13 16:36:43 -04:00
postContainer.on('click', '[component="post/chat"]', function() {
openChat($(this));
});
}
2016-02-18 18:32:08 +02:00
function onReplyClicked(button, tid) {
2016-06-30 11:38:33 +03:00
var selectedText = getSelectedText(button);
2016-06-30 11:38:33 +03:00
showStaleWarning(function() {
2016-06-21 13:37:17 +03:00
var username = getUserName(button);
if (getData(button, 'data-uid') === '0' || !getData(button, 'data-userslug')) {
username = '';
}
2016-06-21 13:37:17 +03:00
var toPid = button.is('[component="post/reply"]') ? getData(button, 'data-pid') : null;
if (selectedText) {
$(window).trigger('action:composer.addQuote', {
tid: tid,
slug: ajaxify.data.slug,
index: getData(button, 'data-index'),
pid: toPid,
topicName: ajaxify.data.titleRaw,
username: username,
text: selectedText
});
} else {
$(window).trigger('action:composer.post.new', {
tid: tid,
pid: toPid,
topicName: ajaxify.data.titleRaw,
text: username ? username + ' ' : ''
});
}
});
}
2016-02-18 18:32:08 +02:00
function onQuoteClicked(button, tid) {
2016-06-30 11:38:33 +03:00
var selectedText = getSelectedText(button);
2016-06-21 13:37:17 +03:00
showStaleWarning(function() {
2015-10-06 05:28:05 -04:00
2016-06-20 13:06:08 +03:00
function quote(text) {
$(window).trigger('action:composer.addQuote', {
tid: tid,
slug: ajaxify.data.slug,
index: getData(button, 'data-index'),
pid: pid,
username: username,
topicName: ajaxify.data.titleRaw,
text: text
});
}
2016-06-21 13:37:17 +03:00
var username = getUserName(button);
var pid = getData(button, 'data-pid');
2016-06-30 11:38:33 +03:00
2016-06-21 13:37:17 +03:00
if (selectedText) {
return quote(selectedText);
}
socket.emit('posts.getRawPost', pid, function(err, post) {
if (err) {
return app.alertError(err.message);
2016-06-20 13:06:08 +03:00
}
2016-06-21 13:37:17 +03:00
quote(post);
});
});
}
2016-06-20 13:06:08 +03:00
function getSelectedText(button) {
var selectionText = '';
var selection = window.getSelection ? window.getSelection() : document.selection.createRange();
var content = button.parents('[component="post"]').find('[component="post/content"]').get(0);
if (selection && selection.containsNode && content && selection.containsNode(content, true)) {
var bounds = document.createRange();
bounds.selectNodeContents(content);
var range = selection.getRangeAt(0).cloneRange();
if (range.compareBoundaryPoints(Range.START_TO_START, bounds) < 0) {
range.setStart(bounds.startContainer, bounds.startOffset);
}
if (range.compareBoundaryPoints(Range.END_TO_END, bounds) > 0) {
range.setEnd(bounds.endContainer, bounds.endOffset);
}
bounds.detach();
selectionText = range.toString();
range.detach();
}
return selectionText;
}
function favouritePost(button, pid) {
var method = button.attr('data-favourited') === 'false' ? 'posts.favourite' : 'posts.unfavourite';
socket.emit(method, {
pid: pid,
room_id: app.currentRoom
}, function(err) {
if (err) {
app.alertError(err.message);
}
});
return false;
}
function toggleVote(button, className, method) {
var post = button.parents('[data-pid]'),
currentState = post.find(className).length;
socket.emit(currentState ? 'posts.unvote' : method , {
pid: post.attr('data-pid'),
room_id: app.currentRoom
}, function(err) {
2015-12-10 09:00:46 +02:00
if (err) {
if (err.message === 'self-vote') {
showVotes(post.attr('data-pid'));
} else {
app.alertError(err.message);
}
}
});
return false;
}
2014-11-15 12:37:22 -05:00
function showVotes(pid) {
2015-07-06 14:33:43 -04:00
socket.emit('posts.getVoters', {pid: pid, cid: ajaxify.data.cid}, function(err, data) {
2014-11-15 12:37:22 -05:00
if (err) {
2015-12-04 11:30:18 -05:00
if (err.message === '[[error:no-privileges]]') {
return;
}
// Only show error if it's an unexpected error.
2014-11-15 12:37:22 -05:00
return app.alertError(err.message);
}
templates.parse('partials/modals/votes_modal', data, function(html) {
translator.translate(html, function(translated) {
var dialog = bootbox.dialog({
title: 'Voters',
message: translated,
className: 'vote-modal',
show: true
});
dialog.on('click', function() {
dialog.modal('hide');
});
2014-11-15 12:37:22 -05:00
});
});
});
}
function getData(button, data) {
return button.parents('[data-pid]').attr(data);
}
function getUserName(button) {
2016-03-08 14:38:44 +02:00
var username = '';
var post = button.parents('[data-pid]');
2015-09-26 18:29:27 -04:00
if (button.attr('component') === 'topic/reply') {
return username;
}
2016-03-08 14:38:44 +02:00
if (post.length) {
username = post.attr('data-username').replace(/\s/g, '-');
}
if (post.length && post.attr('data-uid') !== '0') {
username = '@' + username;
}
return username;
}
function togglePostDelete(button, tid) {
2016-06-28 12:34:09 +03:00
var pid = getData(button, 'data-pid');
var postEl = components.get('post', 'pid', pid);
var action = !postEl.hasClass('deleted') ? 'delete' : 'restore';
postAction(action, pid, tid);
}
function purgePost(button, tid) {
postAction('purge', getData(button, 'data-pid'), tid);
}
function postAction(action, pid, tid) {
translator.translate('[[topic:post_' + action + '_confirm]]', function(msg) {
bootbox.confirm(msg, function(confirm) {
if (!confirm) {
return;
}
socket.emit('posts.' + action, {
pid: pid,
tid: tid
}, function(err) {
2015-04-02 23:01:47 -04:00
if (err) {
app.alertError(err.message);
}
});
});
});
}
function openMovePostModal(button) {
2015-09-26 01:55:13 -04:00
parseMoveModal(function(html) {
var moveModal = $(html);
2015-09-26 01:55:13 -04:00
var moveBtn = moveModal.find('#move_post_commit'),
topicId = moveModal.find('#topicId');
2015-09-26 01:55:13 -04:00
moveModal.on('hidden.bs.modal', function() {
moveModal.remove();
});
showMoveModal(moveModal);
moveModal.find('.close, #move_post_cancel').on('click', function() {
moveModal.addClass('hide');
});
topicId.on('keyup change', function() {
2016-02-18 18:32:08 +02:00
moveBtn.attr('disabled', !topicId.val());
2015-09-26 01:55:13 -04:00
});
moveBtn.on('click', function() {
movePost(button.parents('[data-pid]'), getData(button, 'data-pid'), topicId.val(), function() {
moveModal.modal('hide');
topicId.val('');
});
});
});
2015-09-26 01:55:13 -04:00
}
2015-09-26 01:55:13 -04:00
function parseMoveModal(callback) {
templates.parse('partials/move_post_modal', {}, function(html) {
translator.translate(html, callback);
});
}
2015-09-26 01:55:13 -04:00
function showMoveModal(modal) {
modal.modal('show')
.css("position", "fixed")
2015-09-26 01:55:13 -04:00
.css("left", Math.max(0, (($(window).width() - modal.outerWidth()) / 2) + $(window).scrollLeft()) + "px")
.css("top", "0px")
.css("z-index", "2000");
}
2015-09-26 01:55:13 -04:00
function movePost(post, pid, tid, callback) {
socket.emit('posts.movePost', {pid: pid, tid: tid}, function(err) {
2015-04-02 23:01:47 -04:00
if (err) {
2015-09-26 01:55:13 -04:00
app.alertError(err.message);
return callback();
}
post.fadeOut(500, function() {
post.remove();
});
app.alertSuccess('[[topic:post_moved]]');
2015-09-26 01:55:13 -04:00
callback();
});
}
function openChat(button) {
2015-04-02 17:11:55 +08:00
var post = button.parents('[data-pid]');
app.newChat(post.attr('data-uid'));
button.parents('.btn-group').find('.dropdown-toggle').click();
return false;
}
function showStaleWarning(callback) {
2016-06-30 11:38:33 +03:00
if (staleReplyAnyway || ajaxify.data.lastposttime >= (Date.now() - (1000 * 60 * 60 * 24 * ajaxify.data.topicStaleDays))) {
2016-06-21 13:37:17 +03:00
return callback();
}
translator.translate('[[topic:stale.warning]]', function(translated) {
var warning = bootbox.dialog({
title: '[[topic:stale.title]]',
message: translated,
buttons: {
reply: {
label: '[[topic:stale.reply_anyway]]',
className: 'btn-link',
callback: function() {
2016-06-30 11:38:33 +03:00
staleReplyAnyway = true;
2016-06-21 13:37:17 +03:00
callback();
}
},
create: {
label: '[[topic:stale.create]]',
className: 'btn-primary',
callback: function() {
translator.translate('[[topic:link_back, ' + ajaxify.data.title + ', ' + config.relative_path + '/topic/' + ajaxify.data.slug + ']]', function(body) {
$(window).trigger('action:composer.topic.new', {
cid: ajaxify.data.cid,
body: body
});
2016-06-21 13:37:17 +03:00
});
}
}
2016-06-21 13:37:17 +03:00
}
});
2016-06-21 13:37:17 +03:00
warning.modal();
});
2015-10-06 05:28:05 -04:00
}
return PostTools;
});