/* eslint-disable class-methods-use-this */ /* global Flash */ import Cookies from 'js-cookie'; import emojiMap from 'emojis/digests.json'; import emojiAliases from 'emojis/aliases.json'; import { glEmojiTag } from './behaviors/gl_emoji'; import isEmojiNameValid from './behaviors/gl_emoji/is_emoji_name_valid'; const animationEndEventString = 'animationend webkitAnimationEnd MSAnimationEnd oAnimationEnd'; const transitionEndEventString = 'transitionend webkitTransitionEnd oTransitionEnd MSTransitionEnd'; const requestAnimationFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.setTimeout; const FROM_SENTENCE_REGEX = /(?:, and | and |, )/; // For separating lists produced by ruby's Array#toSentence let categoryMap = null; const categoryLabelMap = { activity: 'Activity', people: 'People', nature: 'Nature', food: 'Food', travel: 'Travel', objects: 'Objects', symbols: 'Symbols', flags: 'Flags', }; function buildCategoryMap() { return Object.keys(emojiMap).reduce((currentCategoryMap, emojiNameKey) => { const emojiInfo = emojiMap[emojiNameKey]; if (currentCategoryMap[emojiInfo.category]) { currentCategoryMap[emojiInfo.category].push(emojiNameKey); } return currentCategoryMap; }, { activity: [], people: [], nature: [], food: [], travel: [], objects: [], symbols: [], flags: [], }); } function renderCategory(name, emojiList, opts = {}) { return `
We encountered an error while adding the remaining categories
'); throw new Error(`Error occurred in addRemainingEmojiMenuCategories: ${err.message}`); }); } positionMenu($menu, $addBtn) { const position = $addBtn.data('position'); // The menu could potentially be off-screen or in a hidden overflow element // So we position the element absolute in the body const css = { top: `${$addBtn.offset().top + $addBtn.outerHeight()}px`, }; if (position === 'right') { css.left = `${($addBtn.offset().left - $menu.outerWidth()) + 20}px`; $menu.addClass('is-aligned-right'); } else { css.left = `${$addBtn.offset().left}px`; $menu.removeClass('is-aligned-right'); } return $menu.css(css); } addAward( votesBlock, awardUrl, emoji, checkMutuality, callback, ) { const normalizedEmoji = this.normalizeEmojiName(emoji); const $emojiButton = this.findEmojiIcon(votesBlock, normalizedEmoji).parent(); this.postEmoji($emojiButton, awardUrl, normalizedEmoji, () => { this.addAwardToEmojiBar(votesBlock, normalizedEmoji, checkMutuality); return typeof callback === 'function' ? callback() : undefined; }); $('.emoji-menu').removeClass('is-visible'); $('.js-add-award.is-active').removeClass('is-active'); } addAwardToEmojiBar( votesBlock, emoji, checkForMutuality, ) { if (checkForMutuality || checkForMutuality === null) { this.checkMutuality(votesBlock, emoji); } this.addEmojiToFrequentlyUsedList(emoji); const normalizedEmoji = this.normalizeEmojiName(emoji); const $emojiButton = this.findEmojiIcon(votesBlock, normalizedEmoji).parent(); if ($emojiButton.length > 0) { if (this.isActive($emojiButton)) { this.decrementCounter($emojiButton, normalizedEmoji); } else { const counter = $emojiButton.find('.js-counter'); counter.text(parseInt(counter.text(), 10) + 1); $emojiButton.addClass('active'); this.addYouToUserList(votesBlock, normalizedEmoji); this.animateEmoji($emojiButton); } } else { votesBlock.removeClass('hidden'); this.createEmoji(votesBlock, normalizedEmoji); } } getVotesBlock() { const currentBlock = $('.js-awards-block.current'); let resultantVotesBlock = currentBlock; if (currentBlock.length === 0) { resultantVotesBlock = $('.js-awards-block').eq(0); } return resultantVotesBlock; } getAwardUrl() { return this.getVotesBlock().data('award-url'); } checkMutuality(votesBlock, emoji) { const awardUrl = this.getAwardUrl(); if (emoji === 'thumbsup' || emoji === 'thumbsdown') { const mutualVote = emoji === 'thumbsup' ? 'thumbsdown' : 'thumbsup'; const $emojiButton = votesBlock.find(`[data-name="${mutualVote}"]`).parent(); const isAlreadyVoted = $emojiButton.hasClass('active'); if (isAlreadyVoted) { this.addAward(votesBlock, awardUrl, mutualVote, false); } } } isActive($emojiButton) { return $emojiButton.hasClass('active'); } isUserAuthored($button) { return $button.hasClass('js-user-authored'); } decrementCounter($emojiButton, emoji) { const counter = $('.js-counter', $emojiButton); const counterNumber = parseInt(counter.text(), 10); if (counterNumber > 1) { counter.text(counterNumber - 1); this.removeYouFromUserList($emojiButton); } else if (emoji === 'thumbsup' || emoji === 'thumbsdown') { $emojiButton.tooltip('destroy'); counter.text('0'); this.removeYouFromUserList($emojiButton); if ($emojiButton.parents('.note').length) { this.removeEmoji($emojiButton); } } else { this.removeEmoji($emojiButton); } return $emojiButton.removeClass('active'); } removeEmoji($emojiButton) { $emojiButton.tooltip('destroy'); $emojiButton.remove(); const $votesBlock = this.getVotesBlock(); if ($votesBlock.find('.js-emoji-btn').length === 0) { $votesBlock.addClass('hidden'); } } getAwardTooltip($awardBlock) { return $awardBlock.attr('data-original-title') || $awardBlock.attr('data-title') || ''; } toSentence(list) { let sentence; if (list.length <= 2) { sentence = list.join(' and '); } else { sentence = `${list.slice(0, -1).join(', ')}, and ${list[list.length - 1]}`; } return sentence; } removeYouFromUserList($emojiButton) { const awardBlock = $emojiButton; const originalTitle = this.getAwardTooltip(awardBlock); const authors = originalTitle.split(FROM_SENTENCE_REGEX); authors.splice(authors.indexOf('You'), 1); return awardBlock .closest('.js-emoji-btn') .removeData('title') .removeAttr('data-title') .removeAttr('data-original-title') .attr('title', this.toSentence(authors)) .tooltip('fixTitle'); } addYouToUserList(votesBlock, emoji) { const awardBlock = this.findEmojiIcon(votesBlock, emoji).parent(); const origTitle = this.getAwardTooltip(awardBlock); let users = []; if (origTitle) { users = origTitle.trim().split(FROM_SENTENCE_REGEX); } users.unshift('You'); return awardBlock .attr('title', this.toSentence(users)) .tooltip('fixTitle'); } createAwardButtonForVotesBlock(votesBlock, emojiName) { const buttonHtml = ` `; const $emojiButton = $(buttonHtml); $emojiButton.insertBefore(votesBlock.find('.js-award-holder')).find('.emoji-icon').data('name', emojiName); this.animateEmoji($emojiButton); $('.award-control').tooltip(); votesBlock.removeClass('current'); } animateEmoji($emoji) { const className = 'pulse animated once short'; $emoji.addClass(className); this.registerEventListener('on', $emoji, animationEndEventString, (e) => { $(e.currentTarget).removeClass(className); }); } createEmoji(votesBlock, emoji) { if ($('.emoji-menu').length) { this.createAwardButtonForVotesBlock(votesBlock, emoji); } this.createEmojiMenu(() => { this.createAwardButtonForVotesBlock(votesBlock, emoji); }); } postEmoji($emojiButton, awardUrl, emoji, callback) { if (this.isUserAuthored($emojiButton)) { this.userAuthored($emojiButton); } else { $.post(awardUrl, { name: emoji, }, (data) => { if (data.ok) { callback(); } }).fail(() => new Flash('Something went wrong on our end.')); } } findEmojiIcon(votesBlock, emoji) { return votesBlock.find(`.js-emoji-btn [data-name="${emoji}"]`); } userAuthored($emojiButton) { const oldTitle = this.getAwardTooltip($emojiButton); const newTitle = 'You cannot vote on your own issue, MR and note'; gl.utils.updateTooltipTitle($emojiButton, newTitle).tooltip('show'); // Restore tooltip back to award list return setTimeout(() => { $emojiButton.tooltip('hide'); gl.utils.updateTooltipTitle($emojiButton, oldTitle); }, 2800); } scrollToAwards() { const options = { scrollTop: $('.awards').offset().top - 110, }; return $('body, html').animate(options, 200); } normalizeEmojiName(emoji) { return Object.prototype.hasOwnProperty.call(this.aliases, emoji) ? this.aliases[emoji] : emoji; } addEmojiToFrequentlyUsedList(emoji) { if (isEmojiNameValid(emoji)) { this.frequentlyUsedEmojis = _.uniq(this.getFrequentlyUsedEmojis().concat(emoji)); Cookies.set('frequently_used_emojis', this.frequentlyUsedEmojis.join(','), { expires: 365 }); } } getFrequentlyUsedEmojis() { return this.frequentlyUsedEmojis || (() => { const frequentlyUsedEmojis = _.uniq((Cookies.get('frequently_used_emojis') || '').split(',')); this.frequentlyUsedEmojis = frequentlyUsedEmojis.filter( inputName => isEmojiNameValid(inputName), ); return this.frequentlyUsedEmojis; })(); } setupSearch() { const $search = $('.js-emoji-menu-search'); this.registerEventListener('on', $search, 'input', (e) => { const term = $(e.target).val().trim(); this.searchEmojis(term); }); const $menu = $('.emoji-menu'); this.registerEventListener('on', $menu, transitionEndEventString, (e) => { if (e.target === e.currentTarget) { // Clear the search this.searchEmojis(''); } }); } searchEmojis(term) { const $search = $('.js-emoji-menu-search'); $search.val(term); // Clean previous search results $('ul.emoji-menu-search, h5.emoji-search-title').remove(); if (term.length > 0) { // Generate a search result block const h5 = $('').text('Search results'); const foundEmojis = this.findMatchingEmojiElements(term).show(); const ul = $('