'use strict'; /* globals define, socket, app, config, ajaxify, utils, translator, templates, bootbox */ define('composer', [ 'taskbar', 'composer/controls', 'composer/uploads', 'composer/formatting', 'composer/drafts', 'composer/tags', 'composer/categoryList', 'composer/preview', 'composer/resize' ], function(taskbar, controls, uploads, formatting, drafts, tags, categoryList, preview, resize) { var composer = { active: undefined, posts: {}, bsEnvironment: undefined, formatting: [] }; $(window).off('resize', onWindowResize).on('resize', onWindowResize); $(window).on('popstate', function(ev, data) { var env = utils.findBootstrapEnvironment(); if (composer.active && (env === 'xs' || env ==='sm')) { if (!composer.posts[composer.active].modified) { discard(composer.active); return; } translator.translate('[[modules:composer.discard]]', function(translated) { bootbox.confirm(translated, function(confirm) { if (confirm) { discard(composer.active); } else { history.pushState({}, '', '#compose'); } }); }); } }); $(window).on('action:composer.topics.post', function(ev, data) { localStorage.removeItem('category:' + data.data.cid + ':bookmark'); localStorage.removeItem('category:' + data.data.cid + ':bookmark:clicked'); ajaxify.go('topic/' + data.data.slug); }); // Query server for formatting options socket.emit('modules.composer.getFormattingOptions', function(err, options) { composer.formatting = options; }); function onWindowResize() { if (composer.active !== undefined) { resize.reposition($('#cmp-uuid-' + composer.active)); } } function alreadyOpen(post) { // If a composer for the same cid/tid/pid is already open, return the uuid, else return bool false var type, id; if (post.hasOwnProperty('cid')) { type = 'cid'; } else if (post.hasOwnProperty('tid')) { type = 'tid'; } else if (post.hasOwnProperty('pid')) { type = 'pid'; } id = post[type]; // Find a match for(var uuid in composer.posts) { if (composer.posts[uuid].hasOwnProperty(type) && id === composer.posts[uuid][type]) { return uuid; } } // No matches... return false; } function push(post) { var uuid = utils.generateUUID(), existingUUID = alreadyOpen(post); if (existingUUID) { taskbar.updateActive(existingUUID); return composer.load(existingUUID); } translator.translate('[[topic:composer.new_topic]]', function(newTopicStr) { taskbar.push('composer', uuid, { title: post.title ? post.title : newTopicStr }); }); // Construct a save_id if (0 !== parseInt(app.user.uid, 10)) { if (post.hasOwnProperty('cid')) { post.save_id = ['composer', app.user.uid, 'cid', post.cid].join(':'); } else if (post.hasOwnProperty('tid')) { post.save_id = ['composer', app.user.uid, 'tid', post.tid].join(':'); } else if (post.hasOwnProperty('pid')) { post.save_id = ['composer', app.user.uid, 'pid', post.pid].join(':'); } } composer.posts[uuid] = post; composer.load(uuid); var env = utils.findBootstrapEnvironment(); if (env === 'xs' || env ==='sm') { history.pushState({}, '', '#compose'); } } function composerAlert(message) { $('[data-action="post"]').removeAttr('disabled'); app.alert({ type: 'danger', timeout: 3000, title: '', message: message, alert_id: 'post_error' }); } composer.addButton = function(iconClass, onClick) { formatting.addButton(iconClass, onClick); }; composer.newTopic = function(cid) { socket.emit('categories.isModerator', cid, function(err, isMod) { if (err) { return app.alertError(err.message); } push({ cid: cid, title: '', body: '', modified: false, isMain: true, isMod: isMod }); }); }; composer.addQuote = function(tid, topicSlug, postIndex, pid, title, username, text) { var uuid = composer.active; if (uuid === undefined) { composer.newReply(tid, pid, title, '[[modules:composer.user_said, ' + username + ']]\n' + text); return; } var postContainer = $('#cmp-uuid-' + uuid); var bodyEl = postContainer.find('textarea'); var prevText = bodyEl.val(); if (parseInt(tid, 10) !== parseInt(composer.posts[uuid].tid, 10)) { var link = '[' + title + '](/topic/' + topicSlug + '/' + (parseInt(postIndex, 10) + 1) + ')'; translator.translate('[[modules:composer.user_said_in, ' + username + ', ' + link + ']]\n', config.defaultLang, onTranslated); } else { translator.translate('[[modules:composer.user_said, ' + username + ']]\n', config.defaultLang, onTranslated); } function onTranslated(translated) { composer.posts[uuid].body = (prevText.length ? prevText + '\n\n' : '') + translated + text; bodyEl.val(composer.posts[uuid].body); focusElements(postContainer); preview.render(postContainer); } }; composer.newReply = function(tid, pid, title, text) { socket.emit('topics.isModerator', tid, function(err, isMod) { if (err) { return app.alertError(err.message); } translator.translate(text, config.defaultLang, function(translated) { push({ tid: tid, toPid: pid, title: title, body: translated, modified: false, isMain: false, isMod: isMod }); }); }); }; composer.editPost = function(pid) { socket.emit('modules.composer.push', pid, function(err, threadData) { if(err) { return app.alertError(err.message); } push({ pid: pid, uid: threadData.uid, handle: threadData.handle, title: $('
').html(threadData.title).text(), body: threadData.body, modified: false, isMain: threadData.isMain, topic_thumb: threadData.topic_thumb, tags: threadData.tags }); }); }; composer.load = function(post_uuid) { var postContainer = $('#cmp-uuid-' + post_uuid); if (postContainer.length) { activate(post_uuid); resize.reposition(postContainer); focusElements(postContainer); } else { createNewComposer(post_uuid); } startNotifyTyping(composer.posts[post_uuid]); }; function startNotifyTyping(postData) { function emit() { socket.emit('modules.composer.notifyTyping', { tid: postData.tid, uid: app.user.uid }); } if (!parseInt(postData.tid, 10)) { return; } stopNotifyInterval(postData); emit(); postData.notifyTypingIntervalId = setInterval(emit, 5000); } function stopNotifyTyping(postData) { if (!parseInt(postData.tid, 10)) { return; } socket.emit('modules.composer.stopNotifyTyping', { tid: postData.tid, uid: app.user.uid }); } function stopNotifyInterval(postData) { if (postData.notifyTypingIntervalId) { clearInterval(postData.notifyTypingIntervalId); postData.notifyTypingIntervalId = 0; } } function createNewComposer(post_uuid) { var postData = composer.posts[post_uuid]; var allowTopicsThumbnail = config.allowTopicsThumbnail && postData.isMain && (config.hasImageUploadPlugin || config.allowFileUploads), isTopic = postData ? !!postData.cid : false, isMain = postData ? !!postData.isMain : false, isEditing = postData ? !!postData.pid : false, isGuestPost = postData ? parseInt(postData.uid, 10) === 0 : false; composer.bsEnvironment = utils.findBootstrapEnvironment(); var data = { mobile: composer.bsEnvironment === 'xs' || composer.bsEnvironment === 'sm', allowTopicsThumbnail: allowTopicsThumbnail, showTags: isTopic || isMain, minimumTagLength: config.minimumTagLength, maximumTagLength: config.maximumTagLength, isTopic: isTopic, isEditing: isEditing, showHandleInput: config.allowGuestHandles && (app.user.uid === 0 || (isEditing && isGuestPost && app.user.isAdmin)), handle: postData ? postData.handle || '' : undefined, formatting: composer.formatting, isAdminOrMod: app.user.isAdmin || postData.isMod }; parseAndTranslate('composer', data, function(composerTemplate) { if ($('#cmp-uuid-' + post_uuid).length) { return; } composerTemplate = $(composerTemplate); composerTemplate.attr('id', 'cmp-uuid-' + post_uuid); $(document.body).append(composerTemplate); var postContainer = $(composerTemplate[0]), bodyEl = postContainer.find('textarea'), draft = drafts.getDraft(postData.save_id); tags.init(postContainer, composer.posts[post_uuid]); categoryList.init(postContainer, composer.posts[post_uuid]); updateTitle(postData, postContainer); activate(post_uuid); resize.reposition(postContainer); if (config.allowFileUploads || config.hasImageUploadPlugin) { uploads.initialize(post_uuid); } formatting.addHandler(postContainer); if (allowTopicsThumbnail) { uploads.toggleThumbEls(postContainer, composer.posts[post_uuid].topic_thumb || ''); } postContainer.on('change', 'input, textarea', function() { composer.posts[post_uuid].modified = true; }); postContainer.on('click', '[data-action="post"]', function() { $(this).attr('disabled', true); post(post_uuid); }); postContainer.on('click', '[data-action="post-lock"]', function() { $(this).attr('disabled', true); post(post_uuid, {lock: true}); }); postContainer.on('click', '[data-action="discard"]', function() { if (!composer.posts[post_uuid].modified) { discard(post_uuid); return; } var btn = $(this).prop('disabled', true); translator.translate('[[modules:composer.discard]]', function(translated) { bootbox.confirm(translated, function(confirm) { if (confirm) { discard(post_uuid); } btn.prop('disabled', false); }); }); }); postContainer.on('click', function() { if (!taskbar.isActive(post_uuid)) { taskbar.updateActive(post_uuid); } }); bodyEl.on('input propertychange', function() { preview.render(postContainer); }); bodyEl.on('scroll', function() { preview.matchScroll(postContainer); }); bodyEl.val(draft ? draft : postData.body); preview.render(postContainer, function() { preview.matchScroll(postContainer); }); drafts.init(postContainer, postData); resize.handleResize(postContainer); handleHelp(postContainer); handleTogglePreview(postContainer); $(window).trigger('action:composer.loaded', { post_uuid: post_uuid }); formatting.addComposerButtons(); focusElements(postContainer); }); } function parseAndTranslate(template, data, callback) { templates.parse(template, data, function(composerTemplate) { translator.translate(composerTemplate, callback); }); } function handleHelp(postContainer) { var helpBtn = postContainer.find('.help'); socket.emit('modules.composer.renderHelp', function(err, html) { if (!err && html && html.length > 0) { helpBtn.removeClass('hidden'); helpBtn.on('click', function() { bootbox.alert(html); }); } }); } function handleTogglePreview(postContainer) { var showBtn = postContainer.find('.write-container .toggle-preview'), hideBtn = postContainer.find('.preview-container .toggle-preview'); hideBtn.on('click', function() { $('.preview-container').addClass('hide'); $('.write-container').addClass('maximized'); showBtn.removeClass('hide'); $('.write').focus(); }); showBtn.on('click', function() { $('.preview-container').removeClass('hide'); $('.write-container').removeClass('maximized'); showBtn.addClass('hide'); $('.write').focus(); }); } function updateTitle(postData, postContainer) { var titleEl = postContainer.find('.title'); if (parseInt(postData.tid, 10) > 0) { titleEl.translateVal('[[topic:composer.replying_to, "' + postData.title + '"]]'); titleEl.prop('disabled', true); } else if (parseInt(postData.pid, 10) > 0) { titleEl.val(postData.title); titleEl.prop('disabled', true); socket.emit('modules.composer.editCheck', postData.pid, function(err, editCheck) { if (!err && editCheck.titleEditable) { titleEl.prop('disabled', false); } }); } else { titleEl.val(postData.title); titleEl.prop('disabled', false); } } function activate(post_uuid) { if(composer.active && composer.active !== post_uuid) { composer.minimize(composer.active); } composer.active = post_uuid; } function focusElements(postContainer) { var title = postContainer.find('.title'), bodyEl = postContainer.find('textarea'); if (title.is(':disabled')) { bodyEl.focus().putCursorAtEnd(); } else { title.focus(); } } function post(post_uuid, options) { var postData = composer.posts[post_uuid], postContainer = $('#cmp-uuid-' + post_uuid), handleEl = postContainer.find('.handle'), titleEl = postContainer.find('.title'), bodyEl = postContainer.find('textarea'), thumbEl = postContainer.find('input#topic-thumb-url'); options = options || {}; titleEl.val(titleEl.val().trim()); bodyEl.val(bodyEl.val().trim()); if (thumbEl.length) { thumbEl.val(thumbEl.val().trim()); } var checkTitle = parseInt(postData.cid, 10) || parseInt(postData.pid, 10); if (uploads.inProgress[post_uuid] && uploads.inProgress[post_uuid].length) { return composerAlert('[[error:still-uploading]]'); } else if (checkTitle && titleEl.val().length < parseInt(config.minimumTitleLength, 10)) { return composerAlert('[[error:title-too-short, ' + config.minimumTitleLength + ']]'); } else if (checkTitle && titleEl.val().length > parseInt(config.maximumTitleLength, 10)) { return composerAlert('[[error:title-too-long, ' + config.maximumTitleLength + ']]'); } else if (checkTitle && !utils.slugify(titleEl.val()).length) { return composerAlert('[[error:invalid-title]]'); } else if (bodyEl.val().length < parseInt(config.minimumPostLength, 10)) { return composerAlert('[[error:content-too-short, ' + config.minimumPostLength + ']]'); } else if (bodyEl.val().length > parseInt(config.maximumPostLength, 10)) { return composerAlert('[[error:content-too-long, ' + config.maximumPostLength + ']]'); } var composerData = {}, action; if (parseInt(postData.cid, 10) > 0) { action = 'topics.post'; composerData = { handle: handleEl ? handleEl.val() : undefined, title: titleEl.val(), content: bodyEl.val(), topic_thumb: thumbEl.val() || '', category_id: postData.cid, tags: tags.getTags(post_uuid), lock: options.lock || false }; } else if (parseInt(postData.tid, 10) > 0) { action = 'posts.reply'; composerData = { tid: postData.tid, handle: handleEl ? handleEl.val() : undefined, content: bodyEl.val(), toPid: postData.toPid, lock: options.lock || false }; } else if (parseInt(postData.pid, 10) > 0) { action = 'posts.edit'; composerData = { pid: postData.pid, handle: handleEl ? handleEl.val() : undefined, content: bodyEl.val(), title: titleEl.val(), topic_thumb: thumbEl.val() || '', tags: tags.getTags(post_uuid) }; } socket.emit(action, composerData, function (err, data) { $('[data-action="post"]').removeAttr('disabled'); if (err) { if (err.message === '[[error:email-not-confirmed]]') { return app.showEmailConfirmWarning(err); } return app.alertError(err.message); } discard(post_uuid); drafts.removeDraft(postData.save_id); $(window).trigger('action:composer.' + action, {composerData: composerData, data: data}); }); } function discard(post_uuid) { if (composer.posts[post_uuid]) { $('#cmp-uuid-' + post_uuid).remove(); drafts.removeDraft(composer.posts[post_uuid].save_id); stopNotifyInterval(composer.posts[post_uuid]); stopNotifyTyping(composer.posts[post_uuid]); delete composer.posts[post_uuid]; composer.active = undefined; taskbar.discard('composer', post_uuid); $('body').css({'margin-bottom': 0}); $('[data-action="post"]').removeAttr('disabled'); $('html').removeClass('composing mobile'); } } composer.minimize = function(post_uuid) { var postContainer = $('#cmp-uuid-' + post_uuid); postContainer.css('visibility', 'hidden'); composer.active = undefined; taskbar.minimize('composer', post_uuid); stopNotifyInterval(composer.posts[post_uuid]); stopNotifyTyping(composer.posts[post_uuid]); }; return composer; });