summaryrefslogtreecommitdiff
path: root/app/assets/javascripts/emoji/index.js
blob: b9b3b34452473794e03552140dddb1df686e5580 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
import _ from 'underscore';
import createFlash from '~/flash';
import { s__ } from '~/locale';
import emojiAliases from 'emojis/aliases.json';
import axios from '../lib/utils/axios_utils';
import csrf from '../lib/utils/csrf';

import AccessorUtilities from '../lib/utils/accessor';

let emojiMap = null;
let validEmojiNames = null;

export const EMOJI_VERSION = '1';
const EMOJI_VERSION_LOCALSTORAGE = `EMOJIS_${EMOJI_VERSION}`;

const isLocalStorageAvailable = AccessorUtilities.isLocalStorageAccessSafe();

export function initEmojiMap() {
  return new Promise((resolve, reject) => {
    if (emojiMap) {
      resolve(emojiMap);
    } else if (isLocalStorageAvailable && window.localStorage.getItem(EMOJI_VERSION_LOCALSTORAGE)) {
      emojiMap = JSON.parse(window.localStorage.getItem(EMOJI_VERSION_LOCALSTORAGE));
      validEmojiNames = [...Object.keys(emojiMap), ...Object.keys(emojiAliases)];
      resolve(emojiMap);
    } else {
      // We load the JSON from server
      const axiosInstance = axios.create();

      // If the static JSON file is on a CDN we don't want to send the default CSRF token
      if (gon.asset_host) {
        delete axiosInstance.defaults.headers.common[csrf.headerKey];
      }

      axiosInstance
        .get(`${gon.relative_url_root || ''}/-/emojis/${EMOJI_VERSION}/emojis.json`)
        .then(({ data }) => {
          emojiMap = data;
          validEmojiNames = [...Object.keys(emojiMap), ...Object.keys(emojiAliases)];
          resolve(emojiMap);
          if (isLocalStorageAvailable) {
            window.localStorage.setItem(EMOJI_VERSION_LOCALSTORAGE, JSON.stringify(emojiMap));
          }
        })
        .catch(err => {
          createFlash(s__('Emojis|Something went wrong while loading emojis.'));
          reject(err);
        });
    }
  });
}

export function normalizeEmojiName(name) {
  return Object.prototype.hasOwnProperty.call(emojiAliases, name) ? emojiAliases[name] : name;
}

export function getValidEmojiNames() {
  return validEmojiNames;
}

export function isEmojiNameValid(name) {
  return validEmojiNames.indexOf(name) >= 0;
}

export function filterEmojiNames(filter) {
  const match = filter.toLowerCase();
  return validEmojiNames.filter(name => name.indexOf(match) >= 0);
}

export function filterEmojiNamesByAlias(filter) {
  return _.uniq(filterEmojiNames(filter).map(name => normalizeEmojiName(name)));
}

let emojiCategoryMap;
export function getEmojiCategoryMap() {
  if (!emojiCategoryMap) {
    emojiCategoryMap = {
      activity: [],
      people: [],
      nature: [],
      food: [],
      travel: [],
      objects: [],
      symbols: [],
      flags: [],
    };
    Object.keys(emojiMap).forEach(name => {
      const emoji = emojiMap[name];
      if (emojiCategoryMap[emoji.c]) {
        emojiCategoryMap[emoji.c].push(name);
      }
    });
  }
  return emojiCategoryMap;
}

export function getEmojiInfo(query) {
  let name = normalizeEmojiName(query);
  let emojiInfo = emojiMap[name];

  // Fallback to question mark for unknown emojis
  if (!emojiInfo) {
    name = 'grey_question';
    emojiInfo = emojiMap[name];
  }

  return { ...emojiInfo, name };
}

export function emojiFallbackImageSrc(inputName) {
  const { name } = getEmojiInfo(inputName);
  return `${gon.asset_host || ''}${gon.relative_url_root ||
    ''}/-/emojis/${EMOJI_VERSION}/${name}.png`;
}

export function emojiImageTag(name, src) {
  return `<img class="emoji" title=":${name}:" alt=":${name}:" src="${src}" width="20" height="20" align="absmiddle" />`;
}

export function glEmojiTag(inputName, options) {
  const opts = { sprite: false, forceFallback: false, ...options };
  const name = normalizeEmojiName(inputName);

  const fallbackSpriteClass = `emoji-${name}`;

  const classList = [];
  if (opts.forceFallback && opts.sprite) {
    classList.push('emoji-icon');
    classList.push(fallbackSpriteClass);
  }
  const classAttribute = classList.length > 0 ? `class="${classList.join(' ')}"` : '';

  const fallbackSpriteAttribute = opts.sprite
    ? `data-fallback-sprite-class="${fallbackSpriteClass}"`
    : '';
  const forceFallbackAttribute = opts.forceFallback ? 'data-force-fallback="true"' : '';

  return `
    <gl-emoji
      ${classAttribute}
      data-name="${name}"
      ${fallbackSpriteAttribute}
      ${forceFallbackAttribute}
    >
    </gl-emoji>
  `;
}