'use strict'; app = window.app || {}; app.isFocused = true; app.currentRoom = null; app.widgets = {}; app.flags = {}; app.cacheBuster = null; (function () { var params = utils.params(); var showWelcomeMessage = !!params.loggedin; var registerMessage = params.register; var isTouchDevice = utils.isTouchDevice(); app.cacheBuster = config['cache-buster']; bootbox.setDefaults({ locale: config.userLang, }); $(document).ready(function () { ajaxify.parseData(); app.load(); }); app.handleEarlyClicks = function () { /** * Occasionally, a button or anchor (not meant to be ajaxified) is clicked before * ajaxify is ready. Capture that event and re-click it once NodeBB is ready. * * e.g. New Topic/Reply, post tools */ if (document.body) { var earlyQueue = []; // once we can ES6, use Set instead var earlyClick = function (ev) { var btnEl = ev.target.closest('button'); var anchorEl = ev.target.closest('a'); if (!btnEl && anchorEl && (anchorEl.getAttribute('data-ajaxify') === 'false' || anchorEl.href === '#')) { btnEl = anchorEl; } if (btnEl && !earlyQueue.includes(btnEl)) { earlyQueue.push(btnEl); ev.stopImmediatePropagation(); ev.preventDefault(); } }; document.body.addEventListener('click', earlyClick); $(window).on('action:ajaxify.end', function () { document.body.removeEventListener('click', earlyClick); earlyQueue.forEach(function (el) { el.click(); }); }); } else { setTimeout(app.handleEarlyClicks, 50); } }; app.handleEarlyClicks(); app.load = function () { overrides.overrideTimeago(); handleStatusChange(); if (config.searchEnabled) { app.handleSearch(); } $('body').on('click', '#new_topic', function (e) { e.preventDefault(); app.newTopic(); }); $('#header-menu .container').on('click', '[component="user/logout"]', function () { app.logout(); return false; }); Visibility.change(function (event, state) { app.isFocused = state === 'visible'; }); createHeaderTooltips(); app.showEmailConfirmWarning(); app.showCookieWarning(); registerServiceWorker(); require(['taskbar', 'helpers', 'forum/pagination'], function (taskbar, helpers, pagination) { taskbar.init(); helpers.register(); pagination.init(); $(window).trigger('action:app.load'); }); }; app.logout = function (redirect) { redirect = redirect === undefined ? true : redirect; $(window).trigger('action:app.logout'); $.ajax(config.relative_path + '/logout', { type: 'POST', headers: { 'x-csrf-token': config.csrf_token, }, beforeSend: function () { app.flags._logout = true; }, success: function (data) { $(window).trigger('action:app.loggedOut', data); if (redirect) { if (data.next) { window.location.href = data.next; } else { window.location.reload(); } } }, }); return false; }; app.alert = function (params) { require(['alerts'], function (alerts) { alerts.alert(params); }); }; app.removeAlert = function (id) { require(['alerts'], function (alerts) { alerts.remove(id); }); }; app.alertSuccess = function (message, timeout) { app.alert({ alert_id: utils.generateUUID(), title: '[[global:alert.success]]', message: message, type: 'success', timeout: timeout || 5000, }); }; app.alertError = function (message, timeout) { message = message.message || message; if (message === '[[error:invalid-session]]') { app.logout(false); return app.handleInvalidSession(); } app.alert({ alert_id: utils.generateUUID(), title: '[[global:alert.error]]', message: message, type: 'danger', timeout: timeout || 10000, }); }; app.handleInvalidSession = function () { if (app.flags._logout) { return; } socket.disconnect(); bootbox.alert({ title: '[[error:invalid-session]]', message: '[[error:invalid-session-text]]', closeButton: false, callback: function () { window.location.reload(); }, }); }; app.enterRoom = function (room, callback) { callback = callback || function () { }; if (socket && app.user.uid && app.currentRoom !== room) { var previousRoom = app.currentRoom; app.currentRoom = room; socket.emit('meta.rooms.enter', { enter: room, }, function (err) { if (err) { app.currentRoom = previousRoom; return app.alertError(err.message); } callback(); }); } }; app.leaveCurrentRoom = function () { if (!socket) { return; } var previousRoom = app.currentRoom; app.currentRoom = ''; socket.emit('meta.rooms.leaveCurrent', function (err) { if (err) { app.currentRoom = previousRoom; return app.alertError(err.message); } }); }; function highlightNavigationLink() { $('#main-nav li') .removeClass('active') .find('a') .filter(function (i, x) { return window.location.pathname.startsWith(x.getAttribute('href')); }) .parent() .addClass('active'); } app.createUserTooltips = function (els, placement) { if (isTouchDevice) { return; } els = els || $('body'); els.find('.avatar,img[title].teaser-pic,img[title].user-img,div.user-icon,span.user-icon').each(function () { $(this).tooltip({ placement: placement || $(this).attr('title-placement') || 'top', title: $(this).attr('title'), container: '#content', }); }); }; app.createStatusTooltips = function () { if (!isTouchDevice) { $('body').tooltip({ selector: '.fa-circle.status', placement: 'top', }); } }; app.processPage = function () { highlightNavigationLink(); $('.timeago').timeago(); utils.makeNumbersHumanReadable($('.human-readable-number')); utils.addCommasToNumbers($('.formatted-number')); app.createUserTooltips($('#content')); app.createStatusTooltips(); // Scroll back to top of page if (!ajaxify.isCold()) { window.scrollTo(0, 0); } }; app.showMessages = function () { var messages = { login: { format: 'alert', title: '[[global:welcome_back]] ' + app.user.username + '!', message: '[[global:you_have_successfully_logged_in]]', }, register: { format: 'modal', }, }; function showAlert(type, message) { switch (messages[type].format) { case 'alert': app.alert({ type: 'success', title: messages[type].title, message: messages[type].message, timeout: 5000, }); break; case 'modal': require(['translator'], function (translator) { translator.translate(message || messages[type].message, function (translated) { bootbox.alert({ title: messages[type].title, message: translated, }); }); }); break; } } if (showWelcomeMessage) { showWelcomeMessage = false; $(document).ready(function () { showAlert('login'); }); } if (registerMessage) { $(document).ready(function () { showAlert('register', utils.escapeHTML(decodeURIComponent(registerMessage))); registerMessage = false; }); } }; app.openChat = function (roomId, uid) { if (!app.user.uid) { return app.alertError('[[error:not-logged-in]]'); } require(['chat'], function (chat) { function loadAndCenter(chatModal) { chat.load(chatModal.attr('data-uuid')); chat.center(chatModal); chat.focusInput(chatModal); } if (chat.modalExists(roomId)) { loadAndCenter(chat.getModal(roomId)); } else { socket.emit('modules.chats.loadRoom', { roomId: roomId, uid: uid || app.user.uid }, function (err, roomData) { if (err) { return app.alertError(err.message); } roomData.users = roomData.users.filter(function (user) { return user && parseInt(user.uid, 10) !== parseInt(app.user.uid, 10); }); roomData.uid = uid || app.user.uid; roomData.isSelf = true; chat.createModal(roomData, loadAndCenter); }); } }); }; app.newChat = function (touid, callback) { function createChat() { socket.emit('modules.chats.newRoom', { touid: touid }, function (err, roomId) { if (err) { return app.alertError(err.message); } if (!ajaxify.data.template.chats) { app.openChat(roomId); } else { ajaxify.go('chats/' + roomId); } callback(false, roomId); }); } callback = callback || function () { }; if (!app.user.uid) { return app.alertError('[[error:not-logged-in]]'); } if (parseInt(touid, 10) === parseInt(app.user.uid, 10)) { return app.alertError('[[error:cant-chat-with-yourself]]'); } socket.emit('modules.chats.isDnD', touid, function (err, isDnD) { if (err) { return app.alertError(err.message); } if (!isDnD) { return createChat(); } bootbox.confirm('[[modules:chat.confirm-chat-with-dnd-user]]', function (ok) { if (ok) { createChat(); } }); }); }; app.toggleNavbar = function (state) { var navbarEl = $('.navbar'); if (navbarEl) { navbarEl[state ? 'show' : 'hide'](); } }; function createHeaderTooltips() { var env = utils.findBootstrapEnvironment(); if (env === 'xs' || env === 'sm' || isTouchDevice) { return; } $('#header-menu li a[title]').each(function () { $(this).tooltip({ placement: 'bottom', trigger: 'hover', title: $(this).attr('title'), }); }); $('#search-form').tooltip({ placement: 'bottom', trigger: 'hover', title: $('#search-button i').attr('title'), }); $('#user_dropdown').tooltip({ placement: 'bottom', trigger: 'hover', title: $('#user_dropdown').attr('title'), }); } app.enableTopicSearch = function (options) { /* eslint-disable-next-line */ var searchOptions = Object.assign({ in: 'titles' }, options.searchOptions); var quickSearchResults = options.searchElements.resultEl; var inputEl = options.searchElements.inputEl; var searchTimeoutId = 0; var oldValue = inputEl.val(); var filterCategoryEl = quickSearchResults.find('.filter-category'); function updateCategoryFilterName() { if (ajaxify.data.template.category) { require(['translator'], function (translator) { translator.translate('[[search:search-in-category, ' + ajaxify.data.name + ']]', function (translated) { var name = $('
').html(translated).text(); filterCategoryEl.find('.name').text(name); }); }); } filterCategoryEl.toggleClass('hidden', !ajaxify.data.template.category); } function doSearch() { require(['search'], function (search) { /* eslint-disable-next-line */ options.searchOptions = Object.assign({}, searchOptions); options.searchOptions.term = inputEl.val(); updateCategoryFilterName(); if (ajaxify.data.template.category) { if (filterCategoryEl.find('input[type="checkbox"]').is(':checked')) { options.searchOptions.categories = [ajaxify.data.cid]; options.searchOptions.searchChildren = true; } } quickSearchResults.removeClass('hidden').find('.quick-search-results-container').html(''); quickSearchResults.find('.loading-indicator').removeClass('hidden'); $(window).trigger('action:search.quick.start', options); options.searchOptions.searchOnly = 1; search.api(options.searchOptions, function (data) { quickSearchResults.find('.loading-indicator').addClass('hidden'); if (options.hideOnNoMatches && !data.posts.length) { return quickSearchResults.addClass('hidden').find('.quick-search-results-container').html(''); } data.posts.forEach(function (p) { var text = $('