diff --git a/public/src/app.js b/public/src/app.js index d6f34dc2d3..3374f1b68c 100644 --- a/public/src/app.js +++ b/public/src/app.js @@ -75,10 +75,6 @@ app.cacheBuster = null; app.load = function () { handleStatusChange(); - if (config.searchEnabled) { - app.handleSearch(); - } - $('body').on('click', '#new_topic', function (e) { e.preventDefault(); app.newTopic(); @@ -103,17 +99,19 @@ app.cacheBuster = null; 'forum/pagination', 'translator', 'messages', + 'search', 'forum/unread', 'forum/header/notifications', 'forum/header/chat', 'timeago/jquery.timeago', - ], function (taskbar, helpers, pagination, translator, messages, unread, notifications, chat) { + ], function (taskbar, helpers, pagination, translator, messages, search, unread, notifications, chat) { notifications.prepareDOM(); chat.prepareDOM(); translator.prepareDOM(); taskbar.init(); helpers.register(); pagination.init(); + search.init(); if (app.user.uid > 0) { unread.initUnreadTopics(); @@ -404,218 +402,24 @@ app.cacheBuster = null; } app.enableTopicSearch = function (options) { - if (!config.searchEnabled || !app.user.privileges['search:content']) { - return; - } - /* eslint-disable-next-line */ - const searchOptions = Object.assign({ in: config.searchDefaultInQuick || 'titles' }, options.searchOptions); - const quickSearchResults = options.searchElements.resultEl; - const inputEl = options.searchElements.inputEl; - let oldValue = inputEl.val(); - const 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) { - const 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'); - hooks.fire('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) { - const text = $('
' + p.content + '
').text(); - const query = inputEl.val().toLowerCase().replace(/^in:topic-\d+/, ''); - const start = Math.max(0, text.toLowerCase().indexOf(query) - 40); - p.snippet = utils.escapeHTML((start > 0 ? '...' : '') + - text.slice(start, start + 80) + - (text.length - start > 80 ? '...' : '')); - }); - app.parseAndTranslate('partials/quick-search-results', data, function (html) { - if (html.length) { - html.find('.timeago').timeago(); - } - quickSearchResults.toggleClass('hidden', !html.length || !inputEl.is(':focus')) - .find('.quick-search-results-container') - .html(html.length ? html : ''); - const highlightEls = quickSearchResults.find( - '.quick-search-results .quick-search-title, .quick-search-results .snippet' - ); - search.highlightMatches(options.searchOptions.term, highlightEls); - hooks.fire('action:search.quick.complete', { - data: data, - options: options, - }); - }); - }); - }); - } - - quickSearchResults.find('.filter-category input[type="checkbox"]').on('change', function () { - inputEl.focus(); - doSearch(); - }); - - inputEl.off('keyup').on('keyup', utils.debounce(function () { - if (inputEl.val().length < 3) { - quickSearchResults.addClass('hidden'); - oldValue = inputEl.val(); - return; - } - if (inputEl.val() === oldValue) { - return; - } - oldValue = inputEl.val(); - if (!inputEl.is(':focus')) { - return quickSearchResults.addClass('hidden'); - } - doSearch(); - }, 500)); - - let mousedownOnResults = false; - quickSearchResults.on('mousedown', function () { - $(window).one('mouseup', function () { - quickSearchResults.addClass('hidden'); - }); - mousedownOnResults = true; - }); - inputEl.on('blur', function () { - if (!inputEl.is(':focus') && !mousedownOnResults && !quickSearchResults.hasClass('hidden')) { - quickSearchResults.addClass('hidden'); - } - }); - - let ajaxified = false; - require(['hooks'], function (hooks) { - hooks.on('action:ajaxify.end', function () { - if (!ajaxify.isCold()) { - ajaxified = true; - } - }); - }); - - inputEl.on('focus', function () { - mousedownOnResults = false; - const query = inputEl.val(); - oldValue = query; - if (query && quickSearchResults.find('#quick-search-results').children().length) { - updateCategoryFilterName(); - if (ajaxified) { - doSearch(); - ajaxified = false; - } else { - quickSearchResults.removeClass('hidden'); - } - inputEl[0].setSelectionRange( - query.startsWith('in:topic') ? query.indexOf(' ') + 1 : 0, - query.length - ); - } - }); - - inputEl.off('refresh').on('refresh', function () { - doSearch(); + console.warn('[deprecated] app.enableTopicSearch is deprecated, please use search.enableQuickSearch(options)'); + require(['search'], function (search) { + search.enableQuickSearch(options); }); }; app.handleSearch = function (searchOptions) { - searchOptions = searchOptions || { in: config.searchDefaultInQuick || 'titles' }; - const searchButton = $('#search-button'); - const searchFields = $('#search-fields'); - const searchInput = $('#search-fields input'); - const quickSearchContainer = $('#quick-search-container'); - - $('#search-form .advanced-search-link').off('mousedown').on('mousedown', function () { - ajaxify.go('/search'); - }); - - $('#search-form').off('submit').on('submit', function () { - searchInput.blur(); - }); - searchInput.off('blur').on('blur', dismissSearch); - searchInput.off('focus'); - - const searchElements = { - inputEl: searchInput, - resultEl: quickSearchContainer, - }; - - app.enableTopicSearch({ - searchOptions: searchOptions, - searchElements: searchElements, - }); - - function dismissSearch() { - setTimeout(function () { - if (!searchInput.is(':focus')) { - searchFields.addClass('hidden'); - searchButton.removeClass('hidden'); - } - }, 200); - } - - searchButton.off('click').on('click', function (e) { - if (!config.loggedIn && !app.user.privileges['search:content']) { - app.alert({ - message: '[[error:search-requires-login]]', - timeout: 3000, - }); - ajaxify.go('login'); - return false; - } - e.stopPropagation(); - - app.prepareSearch(); - return false; - }); - - $('#search-form').off('submit').on('submit', function () { - const input = $(this).find('input'); - require(['search'], function (search) { - const data = search.getSearchPreferences(); - data.term = input.val(); - hooks.fire('action:search.submit', { - searchOptions: data, - searchElements: searchElements, - }); - search.query(data, function () { - input.val(''); - }); - }); - return false; + console.warn('[deprecated] app.handleSearch is deprecated, please use search.init(options)'); + require(['search'], function (search) { + search.init(searchOptions); }); }; app.prepareSearch = function () { - $('#search-fields').removeClass('hidden'); - $('#search-button').addClass('hidden'); - $('#search-fields input').focus(); + console.warn('[deprecated] app.prepareSearch is deprecated, please use search.showAndFocusInput()'); + require(['search'], function (search) { + search.showAndFocusInput(); + }); }; function handleStatusChange() { diff --git a/public/src/client/topic.js b/public/src/client/topic.js index 03bdeb0736..47dfdba236 100644 --- a/public/src/client/topic.js +++ b/public/src/client/topic.js @@ -69,12 +69,12 @@ define('forum/topic', [ function handleTopicSearch() { if (config.topicSearchEnabled) { - require(['mousetrap'], function (mousetrap) { + require(['mousetrap', 'search'], function (mousetrap, search) { mousetrap.bind(['command+f', 'ctrl+f'], function (e) { if (ajaxify.data.template.topic) { e.preventDefault(); $('#search-fields input').val('in:topic-' + ajaxify.data.tid + ' '); - app.prepareSearch(); + search.showAndFocusInput(); } }); }); diff --git a/public/src/client/topic/merge.js b/public/src/client/topic/merge.js index ba716d0738..4264838786 100644 --- a/public/src/client/topic/merge.js +++ b/public/src/client/topic/merge.js @@ -1,7 +1,7 @@ 'use strict'; -define('forum/topic/merge', function () { +define('forum/topic/merge', ['search'], function (search) { const Merge = {}; let modal; let mergeBtn; @@ -30,7 +30,7 @@ define('forum/topic/merge', function () { mergeTopics(mergeBtn); }); - app.enableTopicSearch({ + search.enableQuickSearch({ searchElements: { inputEl: modal.find('.topic-search-input'), resultEl: modal.find('.quick-search-container'), diff --git a/public/src/modules/search.js b/public/src/modules/search.js index cd53fac2e9..1249435c69 100644 --- a/public/src/modules/search.js +++ b/public/src/modules/search.js @@ -1,11 +1,219 @@ 'use strict'; - -define('search', ['navigator', 'translator', 'storage', 'hooks'], function (nav, translator, storage, hooks) { +define('search', ['translator', 'storage', 'hooks'], function (translator, storage, hooks) { const Search = { current: {}, }; + Search.init = function (searchOptions) { + if (!config.searchEnabled) { + return; + } + + searchOptions = searchOptions || { in: config.searchDefaultInQuick || 'titles' }; + const searchButton = $('#search-button'); + const searchFields = $('#search-fields'); + const searchInput = $('#search-fields input'); + const quickSearchContainer = $('#quick-search-container'); + + $('#search-form .advanced-search-link').off('mousedown').on('mousedown', function () { + ajaxify.go('/search'); + }); + + $('#search-form').off('submit').on('submit', function () { + searchInput.blur(); + }); + searchInput.off('blur').on('blur', function dismissSearch() { + setTimeout(function () { + if (!searchInput.is(':focus')) { + searchFields.addClass('hidden'); + searchButton.removeClass('hidden'); + } + }, 200); + }); + searchInput.off('focus'); + + const searchElements = { + inputEl: searchInput, + resultEl: quickSearchContainer, + }; + + Search.enableQuickSearch({ + searchOptions: searchOptions, + searchElements: searchElements, + }); + + searchButton.off('click').on('click', function (e) { + if (!config.loggedIn && !app.user.privileges['search:content']) { + app.alert({ + message: '[[error:search-requires-login]]', + timeout: 3000, + }); + ajaxify.go('login'); + return false; + } + e.stopPropagation(); + + Search.showAndFocusInput(); + return false; + }); + + $('#search-form').off('submit').on('submit', function () { + const input = $(this).find('input'); + const data = Search.getSearchPreferences(); + data.term = input.val(); + hooks.fire('action:search.submit', { + searchOptions: data, + searchElements: searchElements, + }); + Search.query(data, function () { + input.val(''); + }); + + return false; + }); + }; + + Search.enableQuickSearch = function (options) { + if (!config.searchEnabled || !app.user.privileges['search:content']) { + return; + } + + const searchOptions = Object.assign({ in: config.searchDefaultInQuick || 'titles' }, options.searchOptions); + const quickSearchResults = options.searchElements.resultEl; + const inputEl = options.searchElements.inputEl; + let oldValue = inputEl.val(); + const filterCategoryEl = quickSearchResults.find('.filter-category'); + + function updateCategoryFilterName() { + if (ajaxify.data.template.category) { + translator.translate('[[search:search-in-category, ' + ajaxify.data.name + ']]', function (translated) { + const name = $('
').html(translated).text(); + filterCategoryEl.find('.name').text(name); + }); + } + filterCategoryEl.toggleClass('hidden', !ajaxify.data.template.category); + } + + function doSearch() { + 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'); + hooks.fire('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) { + const text = $('
' + p.content + '
').text(); + const query = inputEl.val().toLowerCase().replace(/^in:topic-\d+/, ''); + const start = Math.max(0, text.toLowerCase().indexOf(query) - 40); + p.snippet = utils.escapeHTML((start > 0 ? '...' : '') + + text.slice(start, start + 80) + + (text.length - start > 80 ? '...' : '')); + }); + app.parseAndTranslate('partials/quick-search-results', data, function (html) { + if (html.length) { + html.find('.timeago').timeago(); + } + quickSearchResults.toggleClass('hidden', !html.length || !inputEl.is(':focus')) + .find('.quick-search-results-container') + .html(html.length ? html : ''); + const highlightEls = quickSearchResults.find( + '.quick-search-results .quick-search-title, .quick-search-results .snippet' + ); + Search.highlightMatches(options.searchOptions.term, highlightEls); + hooks.fire('action:search.quick.complete', { + data: data, + options: options, + }); + }); + }); + } + + quickSearchResults.find('.filter-category input[type="checkbox"]').on('change', function () { + inputEl.focus(); + doSearch(); + }); + + inputEl.off('keyup').on('keyup', utils.debounce(function () { + if (inputEl.val().length < 3) { + quickSearchResults.addClass('hidden'); + oldValue = inputEl.val(); + return; + } + if (inputEl.val() === oldValue) { + return; + } + oldValue = inputEl.val(); + if (!inputEl.is(':focus')) { + return quickSearchResults.addClass('hidden'); + } + doSearch(); + }, 500)); + + let mousedownOnResults = false; + quickSearchResults.on('mousedown', function () { + $(window).one('mouseup', function () { + quickSearchResults.addClass('hidden'); + }); + mousedownOnResults = true; + }); + inputEl.on('blur', function () { + if (!inputEl.is(':focus') && !mousedownOnResults && !quickSearchResults.hasClass('hidden')) { + quickSearchResults.addClass('hidden'); + } + }); + + let ajaxified = false; + hooks.on('action:ajaxify.end', function () { + if (!ajaxify.isCold()) { + ajaxified = true; + } + }); + + inputEl.on('focus', function () { + mousedownOnResults = false; + const query = inputEl.val(); + oldValue = query; + if (query && quickSearchResults.find('#quick-search-results').children().length) { + updateCategoryFilterName(); + if (ajaxified) { + doSearch(); + ajaxified = false; + } else { + quickSearchResults.removeClass('hidden'); + } + inputEl[0].setSelectionRange( + query.startsWith('in:topic') ? query.indexOf(' ') + 1 : 0, + query.length + ); + } + }); + + inputEl.off('refresh').on('refresh', function () { + doSearch(); + }); + }; + + Search.showAndFocusInput = function () { + $('#search-fields').removeClass('hidden'); + $('#search-button').addClass('hidden'); + $('#search-fields input').focus(); + }; + Search.query = function (data, callback) { callback = callback || function () {}; ajaxify.go('search?' + createQueryString(data)); diff --git a/src/meta/js.js b/src/meta/js.js index 7d5fc7fb42..742d27d61d 100644 --- a/src/meta/js.js +++ b/src/meta/js.js @@ -75,6 +75,7 @@ JS.scripts = { 'public/src/modules/storage.js', 'public/src/modules/handleBack.js', 'public/src/modules/messages.js', + 'public/src/modules/search.js', ], admin: [