'use strict'; /* globals define, app, templates, translator, socket, bootbox, config, ajaxify, RELATIVE_PATH, utils */ define(['forum/pagination', 'forum/topic/threadTools', 'forum/topic/postTools', 'navigator'], function(pagination, threadTools, postTools, navigator) { var Topic = {}, infiniteLoaderActive = false, scrollingToPost = false, currentUrl = ''; function showBottomPostBar() { if($('#post-container .post-row').length > 1 || !$('#post-container li[data-index="0"]').length) { $('.bottom-post-bar').removeClass('hide'); } } $(window).on('action:ajaxify.start', function(ev, data) { if(data.url.indexOf('topic') !== 0) { navigator.hide(); $('.header-topic-title').find('span').text('').hide(); app.removeAlert('bookmark'); } }); Topic.init = function() { var tid = ajaxify.variables.get('topic_id'), thread_state = { locked: ajaxify.variables.get('locked'), deleted: ajaxify.variables.get('deleted'), pinned: ajaxify.variables.get('pinned') }, currentPage = parseInt(ajaxify.variables.get('currentPage'), 10), pageCount = parseInt(ajaxify.variables.get('pageCount'), 10); Topic.postCount = ajaxify.variables.get('postcount'); $(window).trigger('action:topic.loading'); function hidePostToolsForDeletedPosts() { $('#post-container li.deleted').each(function() { toggle_post_tools($(this).attr('data-pid'), true); }); } $(function() { utils.addCommasToNumbers($('.topic .formatted-number')); app.enterRoom('topic_' + tid); showBottomPostBar(); if (thread_state.locked === '1') { set_locked_state(true); } if (thread_state.deleted === '1') { set_delete_state(true); } if (thread_state.pinned === '1') { set_pinned_state(true); } postTools.init(tid, thread_state); threadTools.init(tid, thread_state); hidePostToolsForDeletedPosts(); enableInfiniteLoading(); var bookmark = localStorage.getItem('topic:' + tid + ':bookmark'); if (window.location.hash) { Topic.scrollToPost(window.location.hash.substr(1), true); } else if (bookmark && (!config.usePagination || (config.usePagination && pagination.currentPage === 1)) && Topic.postCount > 1) { app.alert({ alert_id: 'bookmark', message: '[[topic:bookmark_instructions]]', timeout: 0, type: 'info', clickfn : function() { Topic.scrollToPost(parseInt(bookmark, 10), true); }, closefn : function() { localStorage.removeItem('topic:' + tid + ':bookmark'); } }); } if (!config.usePagination) { navigator.init('.posts > .post-row', Topic.postCount, Topic.navigatorCallback); } $('#post-container').on('mouseenter', '.favourite-tooltip', function(e) { if (!$(this).data('users-loaded')) { $(this).data('users-loaded', "true"); var pid = $(this).parents('.post-row').attr('data-pid'); var el = $(this).attr('title', "Loading..."); socket.emit('posts.getFavouritedUsers', pid, function(err, usernames) { el.attr('title', usernames).tooltip('show'); }); } }); }); function enableInfiniteLoading() { if(!config.usePagination) { app.enableInfiniteLoading(function(direction) { if (!infiniteLoaderActive && $('#post-container').children().length) { var after = 0; var el = null; if(direction > 0) { el = $('#post-container .post-row').last(); after = parseInt(el.attr('data-index'), 10) + 1; } else { el = $('#post-container .post-row').first(); after = parseInt(el.attr('data-index'), 10); after -= config.postsPerPage; if(after < 0) { after = 0; } } var offset = el.offset().top - $('#header-menu').offset().top + $('#header-menu').height(); loadMorePosts(tid, after, function() { hidePostToolsForDeletedPosts(); if(direction < 0 && el) { Topic.scrollToPost(el.attr('data-pid'), false, 0, offset); } }); } }); } else { navigator.hide(); pagination.init(currentPage, pageCount); } } ajaxify.register_events([ 'event:voted', 'event:favourited', 'event:new_post', 'get_users_in_room', 'event:topic_deleted', 'event:topic_restored', 'event:topic:locked', 'event:topic_unlocked', 'event:topic_pinned', 'event:topic_unpinned', 'event:topic_moved', 'event:post_edited', 'event:post_deleted', 'event:post_restored', 'posts.favourite', 'user.isOnline', 'posts.upvote', 'posts.downvote', 'event:topic.replyStart', 'event:topic.replyStop' ]); function createUserIcon(uid, picture, userslug, username) { if(!$('.thread_active_users').find('[data-uid="' + uid + '"]').length) { var div = $('
'); div.find('a').tooltip({ placement: 'top', title: username }); return div; } } socket.on('get_users_in_room', function(data) { if(data && data.room.indexOf('topic') !== -1) { var activeEl = $('.thread_active_users'); // remove users that are no longer here activeEl.find('a').each(function(index, element) { if(element) { var uid = $(element).attr('data-uid'); var absent = data.users.every(function(user) { return parseInt(user.uid, 10) !== parseInt(uid, 10); }); if (absent) { $(element).remove(); } } }); var i=0, icon; // add self for(i = 0; i 8) { break; } } var remainingUsers = data.users.length - 9; remainingUsers = remainingUsers < 0 ? 0 : remainingUsers; var anonymousCount = parseInt(data.anonymousCount, 10); activeEl.find('.anonymous-box').remove(); if(anonymousCount || remainingUsers) { var anonLink = $('
'); activeEl.append(anonLink); var title = ''; if(remainingUsers && anonymousCount) { title = remainingUsers + ' more user(s) and ' + anonymousCount + ' guest(s)'; } else if(remainingUsers) { title = remainingUsers + ' more user(s)'; } else { title = anonymousCount + ' guest(s)'; } anonLink.tooltip({ placement: 'top', title: title }); } // Get users who are currently replying to the topic entered socket.emit('modules.composer.getUsersByTid', ajaxify.variables.get('topic_id'), function(err, uids) { if (uids && uids.length) { for(var x=0;x 0) { ajaxify.go('topic/' + data.tid); } }); socket.on('event:post_edited', function(data) { var editedPostEl = $('#content_' + data.pid), editedPostTitle = $('#topic_title_' + data.pid); if (editedPostTitle.length) { editedPostTitle.fadeOut(250, function() { editedPostTitle.html(data.title); editedPostTitle.fadeIn(250); }); } editedPostEl.fadeOut(250, function() { editedPostEl.html(data.content); editedPostEl.find('img').addClass('img-responsive'); editedPostEl.fadeIn(250); }); }); socket.on('posts.upvote', function(pid) { toggleUpvoteDownvote(pid, true, false); }); socket.on('posts.downvote', function(pid) { toggleUpvoteDownvote(pid, false, true); }); socket.on('posts.unvote', function(pid) { toggleUpvoteDownvote(pid, false, false); }); function toggleUpvoteDownvote(pid, upvote, downvote) { var post = $('li[data-pid="' + pid + '"]'); post.find('.upvote').toggleClass('btn-primary upvoted', upvote); post.find('.downvote').toggleClass('btn-primary downvoted', downvote); } socket.on('posts.favourite', function(pid) { toggleFavourite(pid, true); }); socket.on('posts.unfavourite', function(pid) { toggleFavourite(pid, false); }); function toggleFavourite(pid, isFavourited) { var favBtn = $('li[data-pid="' + pid + '"] .favourite'); if(favBtn.length) { favBtn.addClass('btn-warning') .attr('data-favourited', isFavourited); var icon = favBtn.find('i'); var className = icon.attr('class'); if (isFavourited ? className.indexOf('-o') !== -1 : className.indexOf('-o') === -1) { icon.attr('class', isFavourited ? className.replace('-o', '') : className + '-o'); } } } socket.on('event:post_deleted', function(data) { if (data.pid) { toggle_post_delete_state(data.pid); } }); socket.on('event:post_restored', function(data) { if (data.pid) { toggle_post_delete_state(data.pid); } }); socket.on('event:topic.replyStart', function(uid) { $('.thread_active_users [data-uid="' + uid + '"]').addClass('replying'); }); socket.on('event:topic.replyStop', function(uid) { $('.thread_active_users [data-uid="' + uid + '"]').removeClass('replying'); }); function updatePostVotesAndUserReputation(data) { var votes = $('li[data-pid="' + data.post.pid + '"] .votes'), reputationElements = $('.reputation[data-uid="' + data.post.uid + '"]'); votes.html(data.post.votes).attr('data-votes', data.post.votes); reputationElements.html(data.user.reputation).attr('data-reputation', data.user.reputation); } function updateFavouriteCount(pid, value) { $('li[data-pid="' + pid + '"] .favouriteCount').html(value).attr('data-favourites', value); } function set_locked_state(locked, alert) { translator.translate(' [[topic:thread_tools.' + (locked ? 'un': '') + 'lock]]', function(translated) { $('.lock_thread').html(translated); }); $('#post-container .post_reply').html(locked ? 'Locked ' : 'Reply '); $('#post-container').find('.quote, .edit, .delete').toggleClass('none', locked); $('.topic-main-buttons .post_reply').attr('disabled', locked).html(locked ? 'Locked ' : 'Reply'); if (alert) { app.alert({ 'alert_id': 'thread_lock', type: 'success', title: 'Thread ' + (locked ? 'Locked' : 'Unlocked'), message: 'Thread has been successfully ' + (locked ? 'locked' : 'unlocked'), timeout: 5000 }); } thread_state.locked = locked ? '1' : '0'; } function set_delete_state(deleted) { var threadEl = $('#post-container'); translator.translate(' [[topic:thread_tools.' + (deleted ? 'restore' : 'delete') + ']]', function(translated) { $('.delete_thread span').html(translated); }); threadEl.toggleClass('deleted', deleted); thread_state.deleted = deleted ? '1' : '0'; if(deleted) { translator.translate('[[topic:deleted_message]]', function(translated) { $('
' + translated + '
').insertBefore(threadEl); }); } else { $('#thread-deleted').remove(); } } function set_pinned_state(pinned, alert) { translator.translate(' [[topic:thread_tools.' + (pinned ? 'unpin' : 'pin') + ']]', function(translated) { $('.pin_thread').html(translated); if (alert) { app.alert({ 'alert_id': 'thread_pin', type: 'success', title: 'Thread ' + (pinned ? 'Pinned' : 'Unpinned'), message: 'Thread has been successfully ' + (pinned ? 'pinned' : 'unpinned'), timeout: 5000 }); } thread_state.pinned = pinned ? '1' : '0'; }); } function toggle_post_delete_state(pid) { var postEl = $('#post-container li[data-pid="' + pid + '"]'); if (postEl.length) { postEl.toggleClass('deleted'); toggle_post_tools(pid, postEl.hasClass('deleted')); updatePostCount(); } } function toggle_post_tools(pid, isDeleted) { var postEl = $('#post-container li[data-pid="' + pid + '"]'); postEl.find('.quote, .favourite, .post_reply, .chat').toggleClass('none', isDeleted); translator.translate(isDeleted ? ' [[topic:restore]]' : ' [[topic:delete]]', function(translated) { postEl.find('.delete').find('span').html(translated); }); } $(window).on('scroll', updateTopicTitle); $(window).trigger('action:topic.loaded'); }; function updateTopicTitle() { if($(window).scrollTop() > 50) { $('.header-topic-title').find('span').text(ajaxify.variables.get('topic_name')).show(); } else { $('.header-topic-title').find('span').text('').hide(); } } Topic.navigatorCallback = function(element) { var pid = element.attr('data-pid'); var currentBookmark = localStorage.getItem('topic:' + ajaxify.variables.get('topic_id') + ':bookmark'); if (!currentBookmark || parseInt(pid, 10) >= parseInt(currentBookmark, 10)) { localStorage.setItem('topic:' + ajaxify.variables.get('topic_id') + ':bookmark', pid); app.removeAlert('bookmark'); } if (!scrollingToPost) { var newUrl = window.location.href.replace(window.location.hash, '') + '#' + pid; if (newUrl !== currentUrl) { if (history.replaceState) { history.replaceState({ url: window.location.pathname.slice(1) + (window.location.search ? window.location.search : '' ) + '#' + pid }, null, newUrl); } currentUrl = newUrl; } } }; Topic.scrollToPost = function(pid, highlight, duration, offset) { if (!pid) { return; } if(!offset) { offset = 0; } scrollingToPost = true; if($('#post_anchor_' + pid).length) { return scrollToPid(pid); } if(config.usePagination) { socket.emit('posts.getPidPage', pid, function(err, page) { if(err) { return; } if(parseInt(page, 10) !== pagination.currentPage) { pagination.loadPage(page, function() { scrollToPid(pid); }); } else { scrollToPid(pid); } }); } else { socket.emit('posts.getPidIndex', pid, function(err, index) { if(err) { return; } var tid = $('#post-container').attr('data-tid'); $('#post-container').empty(); var after = index - config.postsPerPage + 1; if(after < 0) { after = 0; } loadMorePosts(tid, after, function() { scrollToPid(pid); }); }); } function scrollToPid(pid) { var scrollTo = $('#post_anchor_' + pid), tid = $('#post-container').attr('data-tid'); function animateScroll() { $("html, body").animate({ scrollTop: (scrollTo.offset().top - $('#header-menu').height() - offset) + "px" }, duration !== undefined ? duration : 400, function() { scrollingToPost = false; navigator.update(); highlightPost(); }); } function highlightPost() { if (highlight) { scrollTo.parent().find('.topic-item').addClass('highlight'); setTimeout(function() { scrollTo.parent().find('.topic-item').removeClass('highlight'); }, 5000); } } if (tid && scrollTo.length) { if($('#post-container li.post-row[data-pid="' + pid + '"]').attr('data-index') !== '0') { animateScroll(); } else { navigator.update(); highlightPost(); } } } }; function onNewPostPagination(data) { var posts = data.posts; socket.emit('topics.getPageCount', ajaxify.variables.get('topic_id'), function(err, newPageCount) { pagination.recreatePaginationLinks(newPageCount); if(pagination.currentPage === pagination.pageCount) { createNewPosts(data); } else if(data.posts && data.posts.length && parseInt(data.posts[0].uid, 10) === parseInt(app.uid, 10)) { pagination.loadPage(pagination.pageCount); } }); } function createNewPosts(data, callback) { if(!data || (data.posts && !data.posts.length)) { return; } function removeAlreadyAddedPosts() { data.posts = data.posts.filter(function(post) { return $('#post-container li[data-pid="' + post.pid +'"]').length === 0; }); } var after = null, before = null; function findInsertionPoint() { var firstPid = parseInt(data.posts[0].pid, 10); $('#post-container li[data-pid]').each(function() { if(firstPid > parseInt($(this).attr('data-pid'), 10)) { after = $(this); if(after.next().length && after.next().hasClass('post-bar')) { after = after.next(); } } else { return false; } }); if(!after) { var firstPost = $('#post-container .post-row').first(); if(firstPid < parseInt(firstPost.attr('data-pid'), 10)) { before = firstPost; } } } removeAlreadyAddedPosts(); if(!data.posts.length) { return; } findInsertionPoint(); data.title = ajaxify.variables.get('topic_name'); data.viewcount = ajaxify.variables.get('viewcount'); parseAndTranslatePosts(data, function(translatedHTML) { var translated = $(translatedHTML); if(after) { translated.insertAfter(after); } else if(before) { translated.insertBefore(before); } else { $('#post-container').append(translated); } translated.hide().fadeIn('slow'); onNewPostsLoaded(translated, data.posts); if(typeof callback === 'function') { callback(); } }); } function parseAndTranslatePosts(data, callback) { ajaxify.loadTemplate('topic', function(topicTemplate) { var html = templates.parse(templates.getBlock(topicTemplate, 'posts'), data); translator.translate(html, callback); }); } function onNewPostsLoaded(html, posts) { function getPostPrivileges(pid) { socket.emit('posts.getPrivileges', pid, function(err, privileges) { if(err) { return app.alertError(err.message); } toggle_mod_tools(html, privileges); }); } for (var x = 0, numPosts = posts.length; x < numPosts; x++) { getPostPrivileges(posts[x].pid); } infiniteLoaderActive = false; app.populateOnlineUsers(); app.createUserTooltips(); utils.addCommasToNumbers(html.find('.formatted-number')); utils.makeNumbersHumanReadable(html.find('.human-readable-number')); html.find('span.timeago').timeago(); html.find('.post-content img').addClass('img-responsive'); updatePostCount(); showBottomPostBar(); } function toggle_mod_tools(postHtml, privileges) { postHtml.find('.edit, .delete').toggleClass('none', !privileges.editable); postHtml.find('.move').toggleClass('none', !privileges.move); } function updatePostCount() { socket.emit('topics.postcount', ajaxify.variables.get('topic_id'), function(err, postcount) { if(!err) { Topic.postCount = postcount; $('#topic-post-count').html(Topic.postCount); navigator.setCount(Topic.postCount); } }); } function loadMorePosts(tid, after, callback) { var indicatorEl = $('.loading-indicator'); if (infiniteLoaderActive || !$('#post-container').length) { return; } if(after === 0 && $('#post-container li.post-row[data-index="0"]').length) { return; } infiniteLoaderActive = true; indicatorEl.fadeIn(); socket.emit('topics.loadMore', { tid: tid, after: after }, function (err, data) { indicatorEl.fadeOut(function() { infiniteLoaderActive = false; }); if(err) { return app.alertError(err.message); } if (data && data.posts && data.posts.length) { createNewPosts(data, callback); } else { navigator.update(); if (typeof callback === 'function') { callback(data.posts); } } }); } return Topic; });