about summary refs log tree commit diff
path: root/app/javascript/mastodon/features/emoji/emoji_utils.js
diff options
context:
space:
mode:
authorNolan Lawson <nolan@nolanlawson.com>2017-10-07 03:17:02 -0700
committerEugen Rochko <eugen@zeonfederated.com>2017-10-07 12:17:02 +0200
commit057db0ecd0049c76c113cbe5412e098d686f0700 (patch)
tree0079a13c4b50d505554a1412f9459091b005f8b1 /app/javascript/mastodon/features/emoji/emoji_utils.js
parent11436358b4091c58532603adcd0f8b9d2e9e7775 (diff)
Update emoji-mart to v2.1.1 (#5256)
Diffstat (limited to 'app/javascript/mastodon/features/emoji/emoji_utils.js')
-rw-r--r--app/javascript/mastodon/features/emoji/emoji_utils.js190
1 files changed, 154 insertions, 36 deletions
diff --git a/app/javascript/mastodon/features/emoji/emoji_utils.js b/app/javascript/mastodon/features/emoji/emoji_utils.js
index 2742185d9..dbf725c1f 100644
--- a/app/javascript/mastodon/features/emoji/emoji_utils.js
+++ b/app/javascript/mastodon/features/emoji/emoji_utils.js
@@ -1,11 +1,9 @@
 // This code is largely borrowed from:
-// https://github.com/missive/emoji-mart/blob/bbd4fbe/src/utils/index.js
+// https://github.com/missive/emoji-mart/blob/5f2ffcc/src/utils/index.js
 
 import data from './emoji_mart_data_light';
 
-const COLONS_REGEX = /^(?:\:([^\:]+)\:)(?:\:skin-tone-(\d)\:)?$/;
-
-function buildSearch(thisData) {
+const buildSearch = (data) => {
   const search = [];
 
   let addToSearch = (strings, split) => {
@@ -24,19 +22,68 @@ function buildSearch(thisData) {
     });
   };
 
-  addToSearch(thisData.short_names, true);
-  addToSearch(thisData.name, true);
-  addToSearch(thisData.keywords, false);
-  addToSearch(thisData.emoticons, false);
+  addToSearch(data.short_names, true);
+  addToSearch(data.name, true);
+  addToSearch(data.keywords, false);
+  addToSearch(data.emoticons, false);
 
-  return search;
-}
+  return search.join(',');
+};
+
+const _String = String;
+
+const stringFromCodePoint = _String.fromCodePoint || function () {
+  let MAX_SIZE = 0x4000;
+  let codeUnits = [];
+  let highSurrogate;
+  let lowSurrogate;
+  let index = -1;
+  let length = arguments.length;
+  if (!length) {
+    return '';
+  }
+  let result = '';
+  while (++index < length) {
+    let codePoint = Number(arguments[index]);
+    if (
+      !isFinite(codePoint) ||       // `NaN`, `+Infinity`, or `-Infinity`
+      codePoint < 0 ||              // not a valid Unicode code point
+      codePoint > 0x10FFFF ||       // not a valid Unicode code point
+      Math.floor(codePoint) !== codePoint // not an integer
+    ) {
+      throw RangeError('Invalid code point: ' + codePoint);
+    }
+    if (codePoint <= 0xFFFF) { // BMP code point
+      codeUnits.push(codePoint);
+    } else { // Astral code point; split in surrogate halves
+      // http://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae
+      codePoint -= 0x10000;
+      highSurrogate = (codePoint >> 10) + 0xD800;
+      lowSurrogate = (codePoint % 0x400) + 0xDC00;
+      codeUnits.push(highSurrogate, lowSurrogate);
+    }
+    if (index + 1 === length || codeUnits.length > MAX_SIZE) {
+      result += String.fromCharCode.apply(null, codeUnits);
+      codeUnits.length = 0;
+    }
+  }
+  return result;
+};
+
+
+const _JSON = JSON;
+
+const COLONS_REGEX = /^(?:\:([^\:]+)\:)(?:\:skin-tone-(\d)\:)?$/;
+const SKINS = [
+  '1F3FA', '1F3FB', '1F3FC',
+  '1F3FD', '1F3FE', '1F3FF',
+];
 
 function unifiedToNative(unified) {
   let unicodes = unified.split('-'),
     codePoints = unicodes.map((u) => `0x${u}`);
 
-  return String.fromCodePoint(...codePoints);
+  return stringFromCodePoint.apply(null, codePoints);
 }
 
 function sanitize(emoji) {
@@ -70,11 +117,11 @@ function sanitize(emoji) {
   };
 }
 
-function getSanitizedData(emoji) {
-  return sanitize(getData(emoji));
+function getSanitizedData() {
+  return sanitize(getData(...arguments));
 }
 
-function getData(emoji) {
+function getData(emoji, skin, set) {
   let emojiData = {};
 
   if (typeof emoji === 'string') {
@@ -83,6 +130,9 @@ function getData(emoji) {
     if (matches) {
       emoji = matches[1];
 
+      if (matches[2]) {
+        skin = parseInt(matches[2]);
+      }
     }
 
     if (data.short_names.hasOwnProperty(emoji)) {
@@ -92,17 +142,6 @@ function getData(emoji) {
     if (data.emojis.hasOwnProperty(emoji)) {
       emojiData = data.emojis[emoji];
     }
-  } else if (emoji.custom) {
-    emojiData = emoji;
-
-    emojiData.search = buildSearch({
-      short_names: emoji.short_names,
-      name: emoji.name,
-      keywords: emoji.keywords,
-      emoticons: emoji.emoticons,
-    });
-
-    emojiData.search = emojiData.search.join(',');
   } else if (emoji.id) {
     if (data.short_names.hasOwnProperty(emoji.id)) {
       emoji.id = data.short_names[emoji.id];
@@ -110,31 +149,110 @@ function getData(emoji) {
 
     if (data.emojis.hasOwnProperty(emoji.id)) {
       emojiData = data.emojis[emoji.id];
+      skin = skin || emoji.skin;
+    }
+  }
+
+  if (!Object.keys(emojiData).length) {
+    emojiData = emoji;
+    emojiData.custom = true;
+
+    if (!emojiData.search) {
+      emojiData.search = buildSearch(emoji);
     }
   }
 
   emojiData.emoticons = emojiData.emoticons || [];
   emojiData.variations = emojiData.variations || [];
 
+  if (emojiData.skin_variations && skin > 1 && set) {
+    emojiData = JSON.parse(_JSON.stringify(emojiData));
+
+    let skinKey = SKINS[skin - 1],
+      variationData = emojiData.skin_variations[skinKey];
+
+    if (!variationData.variations && emojiData.variations) {
+      delete emojiData.variations;
+    }
+
+    if (variationData[`has_img_${set}`]) {
+      emojiData.skin_tone = skin;
+
+      for (let k in variationData) {
+        let v = variationData[k];
+        emojiData[k] = v;
+      }
+    }
+  }
+
   if (emojiData.variations && emojiData.variations.length) {
-    emojiData = JSON.parse(JSON.stringify(emojiData));
+    emojiData = JSON.parse(_JSON.stringify(emojiData));
     emojiData.unified = emojiData.variations.shift();
   }
 
   return emojiData;
 }
 
+function uniq(arr) {
+  return arr.reduce((acc, item) => {
+    if (acc.indexOf(item) === -1) {
+      acc.push(item);
+    }
+    return acc;
+  }, []);
+}
+
 function intersect(a, b) {
-  let set;
-  let list;
-  if (a.length < b.length) {
-    set = new Set(a);
-    list = b;
-  } else {
-    set = new Set(b);
-    list = a;
+  const uniqA = uniq(a);
+  const uniqB = uniq(b);
+
+  return uniqA.filter(item => uniqB.indexOf(item) >= 0);
+}
+
+function deepMerge(a, b) {
+  let o = {};
+
+  for (let key in a) {
+    let originalValue = a[key],
+      value = originalValue;
+
+    if (b.hasOwnProperty(key)) {
+      value = b[key];
+    }
+
+    if (typeof value === 'object') {
+      value = deepMerge(originalValue, value);
+    }
+
+    o[key] = value;
   }
-  return Array.from(new Set(list.filter(x => set.has(x))));
+
+  return o;
+}
+
+// https://github.com/sonicdoe/measure-scrollbar
+function measureScrollbar() {
+  const div = document.createElement('div');
+
+  div.style.width = '100px';
+  div.style.height = '100px';
+  div.style.overflow = 'scroll';
+  div.style.position = 'absolute';
+  div.style.top = '-9999px';
+
+  document.body.appendChild(div);
+  const scrollbarWidth = div.offsetWidth - div.clientWidth;
+  document.body.removeChild(div);
+
+  return scrollbarWidth;
 }
 
-export { getData, getSanitizedData, intersect };
+export {
+  getData,
+  getSanitizedData,
+  uniq,
+  intersect,
+  deepMerge,
+  unifiedToNative,
+  measureScrollbar,
+};