1
0

emojiBar.js 10.0 KB

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