emojiBar.js 9.5 KB

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