emojiBar.js 9.0 KB

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