Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'app/assets/javascripts/gfm_auto_complete.js')
-rw-r--r--app/assets/javascripts/gfm_auto_complete.js145
1 files changed, 54 insertions, 91 deletions
diff --git a/app/assets/javascripts/gfm_auto_complete.js b/app/assets/javascripts/gfm_auto_complete.js
index cf9ff87f25e..d209a971c39 100644
--- a/app/assets/javascripts/gfm_auto_complete.js
+++ b/app/assets/javascripts/gfm_auto_complete.js
@@ -1,19 +1,23 @@
import $ from 'jquery';
import '~/lib/utils/jquery_at_who';
import { escape, template } from 'lodash';
+import * as Emoji from '~/emoji';
+import axios from '~/lib/utils/axios_utils';
import { s__ } from '~/locale';
-import SidebarMediator from '~/sidebar/sidebar_mediator';
import { isUserBusy } from '~/set_status_modal/utils';
-import glRegexp from './lib/utils/regexp';
+import SidebarMediator from '~/sidebar/sidebar_mediator';
import AjaxCache from './lib/utils/ajax_cache';
-import axios from '~/lib/utils/axios_utils';
import { spriteIcon } from './lib/utils/common_utils';
-import * as Emoji from '~/emoji';
+import glRegexp from './lib/utils/regexp';
function sanitize(str) {
return str.replace(/<(?:.|\n)*?>/gm, '');
}
+function createMemberSearchString(member) {
+ return `${member.name.replace(/ /g, '')} ${member.username}`;
+}
+
export function membersBeforeSave(members) {
return members.map((member) => {
const GROUP_TYPE = 'Group';
@@ -40,7 +44,7 @@ export function membersBeforeSave(members) {
username: member.username,
avatarTag: autoCompleteAvatar.length === 1 ? txtAvatar : imgAvatar,
title: sanitize(title),
- search: sanitize(`${member.username} ${member.name}`),
+ search: sanitize(createMemberSearchString(member)),
icon: avatarIcon,
availability: member?.availability,
};
@@ -186,59 +190,43 @@ class GfmAutoComplete {
}
setupEmoji($input) {
- const self = this;
- const { filter, ...defaults } = this.getDefaultCallbacks();
+ const fetchData = this.fetchData.bind(this);
// Emoji
$input.atwho({
at: ':',
- displayTpl(value) {
- let tmpl = GfmAutoComplete.Loading.template;
- if (value && value.name) {
- tmpl = GfmAutoComplete.Emoji.templateFunction(value.name);
- }
- return tmpl;
- },
+ displayTpl: GfmAutoComplete.Emoji.templateFunction,
insertTpl: GfmAutoComplete.Emoji.insertTemplateFunction,
skipSpecialCharacterTest: true,
data: GfmAutoComplete.defaultLoadingData,
callbacks: {
- ...defaults,
+ ...this.getDefaultCallbacks(),
matcher(flag, subtext) {
const regexp = new RegExp(`(?:[^${glRegexp.unicodeLetters}0-9:]|\n|^):([^:]*)$`, 'gi');
const match = regexp.exec(subtext);
return match && match.length ? match[1] : null;
},
- filter(query, items, searchKey) {
- const filtered = filter.call(this, query, items, searchKey);
- if (query.length === 0 || GfmAutoComplete.isLoading(items)) {
- return filtered;
+ filter(query, items) {
+ if (GfmAutoComplete.isLoading(items)) {
+ fetchData(this.$inputor, this.at);
+ return items;
}
- // map from value to "<value> is <field> of <emoji>", arranged by emoji
- const emojis = {};
- filtered.forEach(({ name: value }) => {
- self.emojiLookup[value].forEach(({ emoji: { name }, kind }) => {
- let entry = emojis[name];
- if (!entry) {
- entry = {};
- emojis[name] = entry;
- }
- if (!(kind in entry) || value.localeCompare(entry[kind]) < 0) {
- entry[kind] = value;
- }
- });
- });
+ return GfmAutoComplete.Emoji.filter(query);
+ },
+ sorter(query, items) {
+ this.setting.highlightFirst = this.setting.alwaysHighlightFirst || query.length > 0;
+ if (GfmAutoComplete.isLoading(items)) {
+ this.setting.highlightFirst = false;
+ return items;
+ }
- // collate results to list, prefering name > unicode > alias > description
- const results = [];
- Object.values(emojis).forEach(({ name, unicode, alias, description }) => {
- results.push(name || unicode || alias || description);
- });
+ if (query.length === 0) {
+ return items;
+ }
- // return to the form atwho wants
- return results.map((name) => ({ name }));
+ return GfmAutoComplete.Emoji.sorter(items);
},
},
});
@@ -298,9 +286,7 @@ class GfmAutoComplete {
// Cache assignees list for easier filtering later
assignees =
- SidebarMediator.singleton?.store?.assignees?.map(
- (assignee) => `${assignee.username} ${assignee.name}`,
- ) || [];
+ SidebarMediator.singleton?.store?.assignees?.map(createMemberSearchString) || [];
const match = GfmAutoComplete.defaultMatcher(flag, subtext, this.app.controllers);
return match && match.length ? match[1] : null;
@@ -672,32 +658,7 @@ class GfmAutoComplete {
async loadEmojiData($input, at) {
await Emoji.initEmojiMap();
- // All the emoji
- const emojis = Emoji.getAllEmoji();
-
- // Add all of the fields to atwho's database
- this.loadData($input, at, [
- ...Object.keys(emojis), // Names
- ...Object.values(emojis).flatMap(({ aliases }) => aliases), // Aliases
- ...Object.values(emojis).map(({ e }) => e), // Unicode values
- ...Object.values(emojis).map(({ d }) => d), // Descriptions
- ]);
-
- // Construct a lookup that can correlate a value to "<value> is the <field> of <emoji>"
- const lookup = {};
- const add = (key, kind, emoji) => {
- if (!(key in lookup)) {
- lookup[key] = [];
- }
- lookup[key].push({ kind, emoji });
- };
- Object.values(emojis).forEach((emoji) => {
- add(emoji.name, 'name', emoji);
- add(emoji.d, 'description', emoji);
- add(emoji.e, 'unicode', emoji);
- emoji.aliases.forEach((a) => add(a, 'alias', emoji));
- });
- this.emojiLookup = lookup;
+ this.loadData($input, at, ['loaded']);
GfmAutoComplete.glEmojiTag = Emoji.glEmojiTag;
}
@@ -770,36 +731,38 @@ GfmAutoComplete.typesWithBackendFiltering = ['vulnerabilities'];
GfmAutoComplete.isTypeWithBackendFiltering = (type) =>
GfmAutoComplete.typesWithBackendFiltering.includes(GfmAutoComplete.atTypeMap[type]);
-function findEmoji(name) {
- return Emoji.searchEmoji(name, { match: 'contains', raw: true }).sort((a, b) => {
- if (a.index !== b.index) {
- return a.index - b.index;
- }
- return a.field.localeCompare(b.field);
- });
-}
-
// Emoji
GfmAutoComplete.glEmojiTag = null;
GfmAutoComplete.Emoji = {
insertTemplateFunction(value) {
- const results = findEmoji(value.name);
- if (results.length) {
- return `:${results[0].emoji.name}:`;
- }
- return `:${value.name}:`;
+ return `:${value.emoji.name}:`;
},
- templateFunction(name) {
- // glEmojiTag helper is loaded on-demand in fetchData()
- if (!GfmAutoComplete.glEmojiTag) return `<li>${name}</li>`;
+ templateFunction(item) {
+ if (GfmAutoComplete.isLoading(item)) {
+ return GfmAutoComplete.Loading.template;
+ }
- const results = findEmoji(name);
- if (!results.length) {
- return `<li>${name} ${GfmAutoComplete.glEmojiTag(name)}</li>`;
+ const escapedFieldValue = escape(item.fieldValue);
+ if (!GfmAutoComplete.glEmojiTag) {
+ return `<li>${escapedFieldValue}</li>`;
}
- const { field, emoji } = results[0];
- return `<li>${field} ${GfmAutoComplete.glEmojiTag(emoji.name)}</li>`;
+ return `<li>${escapedFieldValue} ${GfmAutoComplete.glEmojiTag(item.emoji.name)}</li>`;
+ },
+ filter(query) {
+ if (query.length === 0) {
+ return Object.values(Emoji.getAllEmoji())
+ .map((emoji) => ({
+ emoji,
+ fieldValue: emoji.name,
+ }))
+ .slice(0, 20);
+ }
+
+ return Emoji.searchEmoji(query);
+ },
+ sorter(items) {
+ return Emoji.sortEmoji(items);
},
};
// Team Members