import $ from 'jquery'; import 'at.js'; import _ from 'underscore'; import glRegexp from './lib/utils/regexp'; import AjaxCache from './lib/utils/ajax_cache'; import { spriteIcon } from './lib/utils/common_utils'; function sanitize(str) { return str.replace(/<(?:.|\n)*?>/gm, ''); } export function membersBeforeSave(members) { return _.map(members, member => { const GROUP_TYPE = 'Group'; let title = ''; if (member.username == null) { return member; } title = member.name; if (member.count && !member.mentionsDisabled) { title += ` (${member.count})`; } const autoCompleteAvatar = member.avatar_url || member.username.charAt(0).toUpperCase(); const rectAvatarClass = member.type === GROUP_TYPE ? 'rect-avatar' : ''; const imgAvatar = `${member.username}`; const txtAvatar = `
${autoCompleteAvatar}
`; const avatarIcon = member.mentionsDisabled ? spriteIcon('notifications-off', 's16 vertical-align-middle prepend-left-5') : ''; return { username: member.username, avatarTag: autoCompleteAvatar.length === 1 ? txtAvatar : imgAvatar, title: sanitize(title), search: sanitize(`${member.username} ${member.name}`), icon: avatarIcon, }; }); } export const defaultAutocompleteConfig = { emojis: true, members: true, issues: true, mergeRequests: true, epics: true, milestones: true, labels: true, snippets: true, }; class GfmAutoComplete { constructor(dataSources) { this.dataSources = dataSources || {}; this.cachedData = {}; this.isLoadingData = {}; } setup(input, enableMap = defaultAutocompleteConfig) { // Add GFM auto-completion to all input fields, that accept GFM input. this.input = input || $('.js-gfm-input'); this.enableMap = enableMap; this.setupLifecycle(); } setupLifecycle() { this.input.each((i, input) => { const $input = $(input); $input.off('focus.setupAtWho').on('focus.setupAtWho', this.setupAtWho.bind(this, $input)); $input.on('change.atwho', () => input.dispatchEvent(new Event('input'))); // This triggers at.js again // Needed for quick actions with suffixes (ex: /label ~) $input.on('inserted-commands.atwho', $input.trigger.bind($input, 'keyup')); $input.on('clear-commands-cache.atwho', () => this.clearCache()); }); } setupAtWho($input) { if (this.enableMap.emojis) this.setupEmoji($input); if (this.enableMap.members) this.setupMembers($input); if (this.enableMap.issues) this.setupIssues($input); if (this.enableMap.milestones) this.setupMilestones($input); if (this.enableMap.mergeRequests) this.setupMergeRequests($input); if (this.enableMap.labels) this.setupLabels($input); if (this.enableMap.snippets) this.setupSnippets($input); // We don't instantiate the quick actions autocomplete for note and issue/MR edit forms $input.filter('[data-supports-quick-actions="true"]').atwho({ at: '/', alias: 'commands', searchKey: 'search', skipSpecialCharacterTest: true, skipMarkdownCharacterTest: true, data: GfmAutoComplete.defaultLoadingData, displayTpl(value) { const cssClasses = []; if (GfmAutoComplete.isLoading(value)) return GfmAutoComplete.Loading.template; // eslint-disable-next-line no-template-curly-in-string let tpl = '
  • /${name}'; if (value.aliases.length > 0) { tpl += ' (or /<%- aliases.join(", /") %>)'; } if (value.params.length > 0) { tpl += ' <%- params.join(" ") %>'; } if (value.warning && value.icon && value.icon === 'confidential') { tpl += '