You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

258 lines
5.8 KiB

  1. // This code is largely borrowed from:
  2. // https://github.com/missive/emoji-mart/blob/5f2ffcc/src/utils/index.js
  3. import data from './emoji_mart_data_light';
  4. const buildSearch = (data) => {
  5. const search = [];
  6. let addToSearch = (strings, split) => {
  7. if (!strings) {
  8. return;
  9. }
  10. (Array.isArray(strings) ? strings : [strings]).forEach((string) => {
  11. (split ? string.split(/[-|_|\s]+/) : [string]).forEach((s) => {
  12. s = s.toLowerCase();
  13. if (search.indexOf(s) === -1) {
  14. search.push(s);
  15. }
  16. });
  17. });
  18. };
  19. addToSearch(data.short_names, true);
  20. addToSearch(data.name, true);
  21. addToSearch(data.keywords, false);
  22. addToSearch(data.emoticons, false);
  23. return search.join(',');
  24. };
  25. const _String = String;
  26. const stringFromCodePoint = _String.fromCodePoint || function () {
  27. let MAX_SIZE = 0x4000;
  28. let codeUnits = [];
  29. let highSurrogate;
  30. let lowSurrogate;
  31. let index = -1;
  32. let length = arguments.length;
  33. if (!length) {
  34. return '';
  35. }
  36. let result = '';
  37. while (++index < length) {
  38. let codePoint = Number(arguments[index]);
  39. if (
  40. !isFinite(codePoint) || // `NaN`, `+Infinity`, or `-Infinity`
  41. codePoint < 0 || // not a valid Unicode code point
  42. codePoint > 0x10FFFF || // not a valid Unicode code point
  43. Math.floor(codePoint) !== codePoint // not an integer
  44. ) {
  45. throw RangeError('Invalid code point: ' + codePoint);
  46. }
  47. if (codePoint <= 0xFFFF) { // BMP code point
  48. codeUnits.push(codePoint);
  49. } else { // Astral code point; split in surrogate halves
  50. // http://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae
  51. codePoint -= 0x10000;
  52. highSurrogate = (codePoint >> 10) + 0xD800;
  53. lowSurrogate = (codePoint % 0x400) + 0xDC00;
  54. codeUnits.push(highSurrogate, lowSurrogate);
  55. }
  56. if (index + 1 === length || codeUnits.length > MAX_SIZE) {
  57. result += String.fromCharCode.apply(null, codeUnits);
  58. codeUnits.length = 0;
  59. }
  60. }
  61. return result;
  62. };
  63. const _JSON = JSON;
  64. const COLONS_REGEX = /^(?:\:([^\:]+)\:)(?:\:skin-tone-(\d)\:)?$/;
  65. const SKINS = [
  66. '1F3FA', '1F3FB', '1F3FC',
  67. '1F3FD', '1F3FE', '1F3FF',
  68. ];
  69. function unifiedToNative(unified) {
  70. let unicodes = unified.split('-'),
  71. codePoints = unicodes.map((u) => `0x${u}`);
  72. return stringFromCodePoint.apply(null, codePoints);
  73. }
  74. function sanitize(emoji) {
  75. let { name, short_names, skin_tone, skin_variations, emoticons, unified, custom, imageUrl } = emoji,
  76. id = emoji.id || short_names[0],
  77. colons = `:${id}:`;
  78. if (custom) {
  79. return {
  80. id,
  81. name,
  82. colons,
  83. emoticons,
  84. custom,
  85. imageUrl,
  86. };
  87. }
  88. if (skin_tone) {
  89. colons += `:skin-tone-${skin_tone}:`;
  90. }
  91. return {
  92. id,
  93. name,
  94. colons,
  95. emoticons,
  96. unified: unified.toLowerCase(),
  97. skin: skin_tone || (skin_variations ? 1 : null),
  98. native: unifiedToNative(unified),
  99. };
  100. }
  101. function getSanitizedData() {
  102. return sanitize(getData(...arguments));
  103. }
  104. function getData(emoji, skin, set) {
  105. let emojiData = {};
  106. if (typeof emoji === 'string') {
  107. let matches = emoji.match(COLONS_REGEX);
  108. if (matches) {
  109. emoji = matches[1];
  110. if (matches[2]) {
  111. skin = parseInt(matches[2]);
  112. }
  113. }
  114. if (data.short_names.hasOwnProperty(emoji)) {
  115. emoji = data.short_names[emoji];
  116. }
  117. if (data.emojis.hasOwnProperty(emoji)) {
  118. emojiData = data.emojis[emoji];
  119. }
  120. } else if (emoji.id) {
  121. if (data.short_names.hasOwnProperty(emoji.id)) {
  122. emoji.id = data.short_names[emoji.id];
  123. }
  124. if (data.emojis.hasOwnProperty(emoji.id)) {
  125. emojiData = data.emojis[emoji.id];
  126. skin = skin || emoji.skin;
  127. }
  128. }
  129. if (!Object.keys(emojiData).length) {
  130. emojiData = emoji;
  131. emojiData.custom = true;
  132. if (!emojiData.search) {
  133. emojiData.search = buildSearch(emoji);
  134. }
  135. }
  136. emojiData.emoticons = emojiData.emoticons || [];
  137. emojiData.variations = emojiData.variations || [];
  138. if (emojiData.skin_variations && skin > 1 && set) {
  139. emojiData = JSON.parse(_JSON.stringify(emojiData));
  140. let skinKey = SKINS[skin - 1],
  141. variationData = emojiData.skin_variations[skinKey];
  142. if (!variationData.variations && emojiData.variations) {
  143. delete emojiData.variations;
  144. }
  145. if (variationData[`has_img_${set}`]) {
  146. emojiData.skin_tone = skin;
  147. for (let k in variationData) {
  148. let v = variationData[k];
  149. emojiData[k] = v;
  150. }
  151. }
  152. }
  153. if (emojiData.variations && emojiData.variations.length) {
  154. emojiData = JSON.parse(_JSON.stringify(emojiData));
  155. emojiData.unified = emojiData.variations.shift();
  156. }
  157. return emojiData;
  158. }
  159. function uniq(arr) {
  160. return arr.reduce((acc, item) => {
  161. if (acc.indexOf(item) === -1) {
  162. acc.push(item);
  163. }
  164. return acc;
  165. }, []);
  166. }
  167. function intersect(a, b) {
  168. const uniqA = uniq(a);
  169. const uniqB = uniq(b);
  170. return uniqA.filter(item => uniqB.indexOf(item) >= 0);
  171. }
  172. function deepMerge(a, b) {
  173. let o = {};
  174. for (let key in a) {
  175. let originalValue = a[key],
  176. value = originalValue;
  177. if (b.hasOwnProperty(key)) {
  178. value = b[key];
  179. }
  180. if (typeof value === 'object') {
  181. value = deepMerge(originalValue, value);
  182. }
  183. o[key] = value;
  184. }
  185. return o;
  186. }
  187. // https://github.com/sonicdoe/measure-scrollbar
  188. function measureScrollbar() {
  189. const div = document.createElement('div');
  190. div.style.width = '100px';
  191. div.style.height = '100px';
  192. div.style.overflow = 'scroll';
  193. div.style.position = 'absolute';
  194. div.style.top = '-9999px';
  195. document.body.appendChild(div);
  196. const scrollbarWidth = div.offsetWidth - div.clientWidth;
  197. document.body.removeChild(div);
  198. return scrollbarWidth;
  199. }
  200. export {
  201. getData,
  202. getSanitizedData,
  203. uniq,
  204. intersect,
  205. deepMerge,
  206. unifiedToNative,
  207. measureScrollbar,
  208. };