glitch-soc/mastodon

View on GitHub
app/javascript/flavours/glitch/features/emoji/emoji_mart_search_light.js

Summary

Maintainability
F
1 wk
Test Coverage
// This code is largely borrowed from:
// https://github.com/missive/emoji-mart/blob/5f2ffcc/src/utils/emoji-index.js

import * as data from './emoji_mart_data_light';
import { getData, getSanitizedData, uniq, intersect } from './emoji_utils';

let originalPool = {};
let index = {};
let emojisList = {};
let emoticonsList = {};
let customEmojisList = [];

for (let emoji in data.emojis) {
  let emojiData = data.emojis[emoji];
  let { short_names, emoticons } = emojiData;
  let id = short_names[0];

  if (emoticons) {
    emoticons.forEach(emoticon => {
      if (emoticonsList[emoticon]) {
        return;
      }

      emoticonsList[emoticon] = id;
    });
  }

  emojisList[id] = getSanitizedData(id);
  originalPool[id] = emojiData;
}

function clearCustomEmojis(pool) {
  customEmojisList.forEach((emoji) => {
    let emojiId = emoji.id || emoji.short_names[0];

    delete pool[emojiId];
    delete emojisList[emojiId];
  });
}

function addCustomToPool(custom, pool) {
  if (customEmojisList.length) clearCustomEmojis(pool);

  custom.forEach((emoji) => {
    let emojiId = emoji.id || emoji.short_names[0];

    if (emojiId && !pool[emojiId]) {
      pool[emojiId] = getData(emoji);
      emojisList[emojiId] = getSanitizedData(emoji);
    }
  });

  customEmojisList = custom;
  index = {};
}

function search(value, { emojisToShowFilter, maxResults, include, exclude, custom } = {}) {
  if (custom !== undefined) {
    if (customEmojisList !== custom)
      addCustomToPool(custom, originalPool);
  } else {
    custom = [];
  }

  maxResults = maxResults || 75;
  include = include || [];
  exclude = exclude || [];

  let results = null,
    pool = originalPool;

  if (value.length) {
    if (value === '-' || value === '-1') {
      return [emojisList['-1']];
    }

    let values = value.toLowerCase().split(/[\s|,\-_]+/),
      allResults = [];

    if (values.length > 2) {
      values = [values[0], values[1]];
    }

    if (include.length || exclude.length) {
      pool = {};

      data.categories.forEach(category => {
        let isIncluded = include && include.length ? include.indexOf(category.name.toLowerCase()) > -1 : true;
        let isExcluded = exclude && exclude.length ? exclude.indexOf(category.name.toLowerCase()) > -1 : false;
        if (!isIncluded || isExcluded) {
          return;
        }

        category.emojis.forEach(emojiId => pool[emojiId] = data.emojis[emojiId]);
      });

      if (custom.length) {
        let customIsIncluded = include && include.length ? include.indexOf('custom') > -1 : true;
        let customIsExcluded = exclude && exclude.length ? exclude.indexOf('custom') > -1 : false;
        if (customIsIncluded && !customIsExcluded) {
          addCustomToPool(custom, pool);
        }
      }
    }

    const searchValue = (value) => {
      let aPool = pool,
        aIndex = index,
        length = 0;

      for (let charIndex = 0; charIndex < value.length; charIndex++) {
        const char = value[charIndex];
        length++;

        aIndex[char] = aIndex[char] || {};
        aIndex = aIndex[char];

        if (!aIndex.results) {
          let scores = {};

          aIndex.results = [];
          aIndex.pool = {};

          for (let id in aPool) {
            let emoji = aPool[id],
              { search } = emoji,
              sub = value.slice(0, length),
              subIndex = search.indexOf(sub);

            if (subIndex !== -1) {
              let score = subIndex + 1;
              if (sub === id) score = 0;

              aIndex.results.push(emojisList[id]);
              aIndex.pool[id] = emoji;

              scores[id] = score;
            }
          }

          aIndex.results.sort((a, b) => {
            let aScore = scores[a.id],
              bScore = scores[b.id];

            return aScore - bScore;
          });
        }

        aPool = aIndex.pool;
      }

      return aIndex.results;
    };

    if (values.length > 1) {
      results = searchValue(value);
    } else {
      results = [];
    }

    allResults = values.map(searchValue).filter(a => a);

    if (allResults.length > 1) {
      allResults = intersect.apply(null, allResults);
    } else if (allResults.length) {
      allResults = allResults[0];
    }

    results = uniq(results.concat(allResults));
  }

  if (results) {
    if (emojisToShowFilter) {
      results = results.filter((result) => emojisToShowFilter(data.emojis[result.id]));
    }

    if (results && results.length > maxResults) {
      results = results.slice(0, maxResults);
    }
  }

  return results;
}

export { search };