You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

153 lines
4.1 KiB
JavaScript

"use strict";
/* globals socket, app, define, ajaxify, config */
define('admin/modules/search', ['mousetrap'], function (mousetrap) {
var search = {};
function find(dict, term) {
var html = dict.filter(function (elem) {
return elem.translations.toLowerCase().includes(term);
}).map(function (params) {
var namespace = params.namespace;
var translations = params.translations;
var title = params.title;
var results = translations
// remove all lines without a match
.replace(new RegExp('^(?:(?!' + term + ').)*$', 'gmi'), '')
// remove lines that only match the title
.replace(new RegExp('(^|\\n).*?' + title + '.*?(\\n|$)', 'g'), '')
// get up to 25 characters of context on both sides of the match
// and wrap the match in a `.search-match` element
.replace(
new RegExp('^[\\s\\S]*?(.{0,25})(' + term + ')(.{0,25})[\\s\\S]*?$', 'gmi'),
'...$1<span class="search-match">$2</span>$3...<br>'
)
// collapse whitespace
.replace(/(?:\n ?)+/g, '\n')
.trim();
title = title.replace(
new RegExp('(^.*?)(' + term + ')(.*?$)', 'gi'),
'$1<span class="search-match">$2</span>$3'
);
return '<li role="presentation" class="result">' +
'<a role= "menuitem" href= "' + config.relative_path + '/' + namespace + '" >' +
title +
'<br>' + (!results ? '' :
('<small><code>' +
results +
'</small></code>')) +
'</a>' +
'</li>';
}).join('');
return html;
}
search.init = function () {
socket.emit('admin.getSearchDict', {}, function (err, dict) {
if (err) {
app.alertError(err);
throw err;
}
setupACPSearch(dict);
});
};
function setupACPSearch(dict) {
var dropdown = $('#acp-search .dropdown');
var menu = $('#acp-search .dropdown-menu');
var input = $('#acp-search input');
if (!config.searchEnabled) {
menu.addClass('search-disabled');
}
input.on('keyup', function () {
dropdown.addClass('open');
});
$('#acp-search').parents('form').on('submit', function (ev) {
var selected = menu.find('li.result > a.focus').attr('href');
if (!selected.length) {
selected = menu.find('li.result > a').first().attr('href');
}
var href = selected ? selected : config.relative_path + '/search/' + input.val();
ajaxify.go(href.replace(/^\//, ''));
setTimeout(function () {
dropdown.removeClass('open');
input.blur();
}, 150);
ev.preventDefault();
return false;
});
mousetrap(input[0]).bind(['up', 'down'], function (ev, key) {
var next;
if (key === 'up') {
next = menu.find('li.result > a.focus').removeClass('focus').parent().prev('.result').children();
if (!next.length) {
next = menu.find('li.result > a').last();
}
next.addClass('focus');
if (menu[0].getBoundingClientRect().top > next[0].getBoundingClientRect().top) {
next[0].scrollIntoView(true);
}
} else if (key === 'down') {
next = menu.find('li.result > a.focus').removeClass('focus').parent().next('.result').children();
if (!next.length) {
next = menu.find('li.result > a').first();
}
next.addClass('focus');
if (menu[0].getBoundingClientRect().bottom < next[0].getBoundingClientRect().bottom) {
next[0].scrollIntoView(false);
}
}
ev.preventDefault();
});
var prevValue;
input.on('keyup focus', function () {
var value = input.val().toLowerCase();
if (value === prevValue) {
return;
}
prevValue = value;
menu.children('.result').remove();
var len = value.length;
var results;
menu.toggleClass('state-start-typing', len === 0);
menu.toggleClass('state-keep-typing', len > 0 && len < 3);
if (len >= 3) {
menu.prepend(find(dict, value));
results = menu.children('.result').length;
menu.toggleClass('state-no-results', !results);
menu.toggleClass('state-yes-results', !!results);
menu.find('.search-forum')
.not('.divider')
.find('a')
.attr('href', config.relative_path + '/search/' + value)
.find('strong')
.html(value);
} else {
menu.removeClass('state-no-results state-yes-results');
}
});
}
return search;
});