emojiBar.js 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. var EMOJI_BAR = (function() {
  2. var dom = document.createElement("div")
  3. ,overlay = document.createElement("div")
  4. ,emojisDom = document.createElement("div")
  5. ,unicodeEmojis = document.createElement("ul")
  6. ,customEmojis = document.createElement("ul")
  7. ,searchBar = document.createElement("input")
  8. ,emojiCache = { unicode: {}, custom: {}}
  9. ,emojiDetail = document.createElement("div")
  10. ,emojiDetailImg = document.createElement("span")
  11. ,emojiDetailName = document.createElement("span")
  12. /** @type {function((string|null))|undefined} */
  13. ,emojiSelectedHandler
  14. /** @type {function():boolean} */
  15. ,isSupported = function() {
  16. return ("searchEmojis" in window);
  17. }
  18. ,makeHeader = function(imgSrc) {
  19. var img = document.createElement("img")
  20. ,dom = document.createElement("div");
  21. img.src = imgSrc;
  22. dom.appendChild(img);
  23. dom.className = R.klass.emojibar.header;
  24. return dom;
  25. }
  26. /** @type {function(string, Element):{dom:Element,visible:boolean}} */
  27. ,wrapEmojiLi = function(emojiName, emojiDom) {
  28. var dom = document.createElement("li");
  29. dom.appendChild(emojiDom);
  30. dom.className = R.klass.emojibar.item;
  31. dom.id = "emojibar-" +emojiName;
  32. return {
  33. visible: false
  34. ,dom: dom
  35. };
  36. }
  37. /** @type {function(string, *):{dom:Element,visible:boolean}} */
  38. ,makeUnicodeEmojiLi = function(emojiName, emoji) {
  39. var domEmoji = window['makeEmoji'](emoji)
  40. ,domParent = document.createElement("span");
  41. domParent.appendChild(domEmoji);
  42. domParent.className = R.klass.emoji.medium;
  43. return wrapEmojiLi(emojiName, domParent);
  44. }
  45. /** @type {function(string, string):{dom:Element,visible:boolean}} */
  46. ,makeCustomEmoji = function(emojiName, emojiSrc) {
  47. var domEmoji = document.createElement("span")
  48. ,domParent = document.createElement("span");
  49. domEmoji.className = R.klass.emoji.emoji +' ' +R.klass.emoji.custom;
  50. domEmoji.style.backgroundImage = 'url("' +emojiSrc +'")';
  51. domParent.appendChild(domEmoji);
  52. domParent.className = R.klass.emoji.medium;
  53. return wrapEmojiLi(emojiName, domParent);
  54. }
  55. /** @type {function(Object.<string,*>, Object.<string, number>):Array.<{name:string, pos:number, count:number}>} */
  56. ,sortEmojis = function(emojiObj, favoriteEmojis) {
  57. var names = []
  58. ,index = 0;
  59. for (var i in emojiObj)
  60. names.push({
  61. name: i,
  62. pos: index,
  63. count: favoriteEmojis[i] || 0
  64. });
  65. names = names.sort(function(a, b) {
  66. var diff = b.count -a.count;
  67. if (diff) return diff;
  68. return a.pos -b.pos;
  69. });
  70. return names;
  71. }
  72. /** @type function(string=):number */
  73. ,search = function(queryString) {
  74. var emojiCount = 0
  75. ,toRemove = []
  76. /** @type {Array.<{name:string, pos:number, count:number}>} */
  77. ,sortedEmojiNames;
  78. queryString = queryString === undefined ? searchBar.value : queryString;
  79. // Service emojis
  80. if (isSupported()) {
  81. /** @type {Object.<string, *>} */
  82. var foundEmojis = window['searchEmojis'](queryString);
  83. sortedEmojiNames = sortEmojis(foundEmojis, SLACK.context.self.prefs.favoriteEmojis);
  84. for (var i in emojiCache.unicode) {
  85. if (emojiCache.unicode[i].visible) {
  86. // We remove every item to reorder them (add them in order)
  87. emojiCache.unicode[i].visible = false;
  88. unicodeEmojis.removeChild(emojiCache.unicode[i].dom);
  89. }
  90. }
  91. for (var i =0, nbEmojis = sortedEmojiNames.length; i < nbEmojis; i++) {
  92. var emojiName = sortedEmojiNames[i].name
  93. ,e = emojiCache.unicode[emojiName];
  94. if (!e)
  95. e = emojiCache.unicode[emojiName] = makeUnicodeEmojiLi(emojiName, foundEmojis[emojiName]);
  96. if (!e.visible) {
  97. e.visible = true;
  98. unicodeEmojis.appendChild(e.dom);
  99. }
  100. emojiCount++;
  101. }
  102. }
  103. // Custom emojis
  104. for (var i in emojiCache.custom) {
  105. if (emojiCache.custom[i].visible) {
  106. emojiCache.custom[i].visible = false;
  107. customEmojis.removeChild(emojiCache.custom[i].dom);
  108. }
  109. }
  110. sortedEmojiNames = sortEmojis(SLACK.context.emojis, SLACK.context.self.prefs.favoriteEmojis);
  111. for (var i =0, nbEmojis = sortedEmojiNames.length; i < nbEmojis; i++) {
  112. var emojiName = sortedEmojiNames[i].name;
  113. if ((queryString === '' || emojiName.substr(0, queryString.length) === queryString) && SLACK.context.emojis[emojiName].substr(0, 6) !== 'alias:') {
  114. var e = emojiCache.custom[emojiName];
  115. if (!e)
  116. e = emojiCache.custom[emojiName] = makeCustomEmoji(emojiName, SLACK.context.emojis[emojiName]);
  117. if (!e.visible) {
  118. e.visible = true;
  119. customEmojis.appendChild(e.dom);
  120. }
  121. emojiCount++;
  122. }
  123. }
  124. return emojiCount;
  125. }
  126. /** @type function(Element, function((string|null))=):boolean */
  127. ,spawn = function(domParent, handler) {
  128. if (isSupported()) {
  129. emojiSelectedHandler = handler;
  130. domParent.appendChild(overlay);
  131. domParent.appendChild(dom);
  132. searchBar.value = "";
  133. search();
  134. searchBar.focus();
  135. return true;
  136. }
  137. return false;
  138. }
  139. /** @type {function():boolean} */
  140. ,doClose = function() {
  141. if (dom.parentElement) {
  142. dom.parentElement.removeChild(overlay);
  143. dom.parentElement.removeChild(dom);
  144. return true;
  145. }
  146. return false;
  147. }
  148. /** @type {function():boolean} */
  149. ,close = function() {
  150. var closed = doClose();
  151. if (!closed)
  152. return false;
  153. if (emojiSelectedHandler)
  154. emojiSelectedHandler(null);
  155. return true;
  156. }
  157. ,onEmojiSelected = function(emojiName) {
  158. if (doClose() && emojiSelectedHandler)
  159. emojiSelectedHandler(emojiName);
  160. }
  161. ;
  162. overlay.addEventListener("click", function(e) {
  163. var bounds = dom.getBoundingClientRect();
  164. if (e.screenY < bounds.top || e.screenY > bounds.bottom ||
  165. e.screenX < bounds.left || e.screenX > bounds.right)
  166. close();
  167. });
  168. overlay.className = R.klass.emojibar.overlay;
  169. dom.className = R.klass.emojibar.container;
  170. emojisDom.className = R.klass.emojibar.emojis;
  171. emojiDetail.className = R.klass.emojibar.detail.container;
  172. emojiDetailImg.className = R.klass.emojibar.detail.img;
  173. emojiDetailName.className = R.klass.emojibar.detail.name;
  174. unicodeEmojis.className = customEmojis.className = R.klass.emojibar.list;
  175. searchBar.className = R.klass.emojibar.search;
  176. emojiDetail.appendChild(emojiDetailImg);
  177. emojiDetail.appendChild(emojiDetailName);
  178. emojisDom.appendChild(makeHeader(window['emojiProviderHeader']));
  179. emojisDom.appendChild(unicodeEmojis);
  180. emojisDom.appendChild(makeHeader("emojicustom.png"));
  181. emojisDom.appendChild(customEmojis);
  182. dom.appendChild(emojisDom);
  183. dom.appendChild(emojiDetail);
  184. dom.appendChild(searchBar);
  185. searchBar.addEventListener("keyup", function() {
  186. search();
  187. });
  188. var makeDelegate = function(e, cb) {
  189. var target = e.target;
  190. while (target !== dom && target && target.nodeName !== "LI")
  191. target = target.parentElement;
  192. if (target && target.nodeName === "LI" && target.id && target.id.substr(0, "emojibar-".length) === "emojibar-") {
  193. var emojiId = target.id.substr("emojibar-".length);
  194. return cb(emojiId);
  195. }
  196. cb(null);
  197. };
  198. dom.addEventListener("mousemove", function(e) {
  199. makeDelegate(e, function(emoji) {
  200. var emojiCached = emoji ? (emojiCache.unicode[emoji] || emojiCache.custom[emoji]) : null;
  201. if (emojiCached) {
  202. emojiDetailImg.innerHTML = emojiCached.dom.outerHTML;
  203. emojiDetailName.textContent = ':' +emoji +':';
  204. } else {
  205. emojiDetailImg.textContent = "";
  206. emojiDetailName.textContent = "";
  207. }
  208. });
  209. });
  210. dom.addEventListener("click", function(e) {
  211. makeDelegate(e, function(emoji) {
  212. if (emoji) onEmojiSelected(emoji);
  213. });
  214. });
  215. return {
  216. isSupported: isSupported
  217. ,spawn: spawn
  218. ,search: search
  219. ,close: close
  220. };
  221. })();