emojipalette.js 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  1. // Copyright 2007 The Closure Library Authors. All Rights Reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS-IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. /**
  15. * @fileoverview Emoji Palette implementation. This provides a UI widget for
  16. * choosing an emoji from a palette of possible choices. EmojiPalettes are
  17. * contained within EmojiPickers.
  18. *
  19. * See ../demos/popupemojipicker.html for an example of how to instantiate
  20. * an emoji picker.
  21. *
  22. * Based on goog.ui.ColorPicker (colorpicker.js).
  23. *
  24. */
  25. goog.provide('goog.ui.emoji.EmojiPalette');
  26. goog.require('goog.events.EventType');
  27. goog.require('goog.net.ImageLoader');
  28. goog.require('goog.ui.Palette');
  29. goog.require('goog.ui.emoji.Emoji');
  30. goog.require('goog.ui.emoji.EmojiPaletteRenderer');
  31. /**
  32. * A page of emoji to be displayed in an EmojiPicker.
  33. *
  34. * @param {Array<Array<?>>} emoji List of emoji for this page.
  35. * @param {?string=} opt_urlPrefix Prefix that should be prepended to all URL.
  36. * @param {goog.ui.PaletteRenderer=} opt_renderer Renderer used to render or
  37. * decorate the palette; defaults to {@link goog.ui.PaletteRenderer}.
  38. * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper.
  39. * @extends {goog.ui.Palette}
  40. * @constructor
  41. * @final
  42. */
  43. goog.ui.emoji.EmojiPalette = function(
  44. emoji, opt_urlPrefix, opt_renderer, opt_domHelper) {
  45. goog.ui.Palette.call(
  46. this, null, opt_renderer || new goog.ui.emoji.EmojiPaletteRenderer(null),
  47. opt_domHelper);
  48. /**
  49. * All the different emoji that this palette can display. Maps emoji ids
  50. * (string) to the goog.ui.emoji.Emoji for that id.
  51. *
  52. * @type {Object}
  53. * @private
  54. */
  55. this.emojiCells_ = {};
  56. /**
  57. * Map of emoji id to index into this.emojiCells_.
  58. *
  59. * @type {Object}
  60. * @private
  61. */
  62. this.emojiMap_ = {};
  63. /**
  64. * List of the animated emoji in this palette. Each internal array is of type
  65. * [HTMLDivElement, goog.ui.emoji.Emoji], and represents the palette item
  66. * for that animated emoji, and the Emoji object.
  67. *
  68. * @type {Array<Array<(HTMLDivElement|goog.ui.emoji.Emoji)>>}
  69. * @private
  70. */
  71. this.animatedEmoji_ = [];
  72. this.urlPrefix_ = opt_urlPrefix || '';
  73. /**
  74. * Palette items that are displayed on this page of the emoji picker. Each
  75. * item is a div wrapped around a div or an img.
  76. *
  77. * @type {Array<HTMLDivElement>}
  78. * @private
  79. */
  80. this.emoji_ = this.getEmojiArrayFromProperties_(emoji);
  81. this.setContent(this.emoji_);
  82. };
  83. goog.inherits(goog.ui.emoji.EmojiPalette, goog.ui.Palette);
  84. /**
  85. * Indicates a prefix that should be prepended to all URLs of images in this
  86. * emojipalette. This provides an optimization if the URLs are long, so that
  87. * the client does not have to send a long string for each emoji.
  88. *
  89. * @type {string}
  90. * @private
  91. */
  92. goog.ui.emoji.EmojiPalette.prototype.urlPrefix_ = '';
  93. /**
  94. * Whether the emoji images have been loaded.
  95. *
  96. * @type {boolean}
  97. * @private
  98. */
  99. goog.ui.emoji.EmojiPalette.prototype.imagesLoaded_ = false;
  100. /**
  101. * Image loader for loading animated emoji.
  102. *
  103. * @type {goog.net.ImageLoader}
  104. * @private
  105. */
  106. goog.ui.emoji.EmojiPalette.prototype.imageLoader_;
  107. /**
  108. * Helps create an array of emoji palette items from an array of emoji
  109. * properties. Each element will be either a div with background-image set to
  110. * a sprite, or an img element pointing directly to an emoji, and all elements
  111. * are wrapped with an outer div for alignment issues (i.e., this allows
  112. * centering the inner div).
  113. *
  114. * @param {Object} emojiGroup The group of emoji for this page.
  115. * @return {!Array<!HTMLDivElement>} The emoji items.
  116. * @private
  117. */
  118. goog.ui.emoji.EmojiPalette.prototype.getEmojiArrayFromProperties_ = function(
  119. emojiGroup) {
  120. var emojiItems = [];
  121. for (var i = 0; i < emojiGroup.length; i++) {
  122. var url = emojiGroup[i][0];
  123. var id = emojiGroup[i][1];
  124. var spriteInfo = emojiGroup[i][2];
  125. var displayUrl = spriteInfo ? spriteInfo.getUrl() : this.urlPrefix_ + url;
  126. var item = this.getRenderer().createPaletteItem(
  127. this.getDomHelper(), id, spriteInfo, displayUrl);
  128. emojiItems.push(item);
  129. var emoji = new goog.ui.emoji.Emoji(url, id);
  130. this.emojiCells_[id] = emoji;
  131. this.emojiMap_[id] = i;
  132. // Keep track of sprited emoji that are animated, for later loading.
  133. if (spriteInfo && spriteInfo.isAnimated()) {
  134. this.animatedEmoji_.push([item, emoji]);
  135. }
  136. }
  137. // Create the image loader now so that tests can access it before it has
  138. // started loading images.
  139. if (this.animatedEmoji_.length > 0) {
  140. this.imageLoader_ = new goog.net.ImageLoader();
  141. }
  142. this.imagesLoaded_ = true;
  143. return emojiItems;
  144. };
  145. /**
  146. * Sends off requests for all the animated emoji and replaces their static
  147. * sprites when the images are done downloading.
  148. */
  149. goog.ui.emoji.EmojiPalette.prototype.loadAnimatedEmoji = function() {
  150. if (this.animatedEmoji_.length > 0) {
  151. for (var i = 0; i < this.animatedEmoji_.length; i++) {
  152. var emoji =
  153. /** @type {goog.ui.emoji.Emoji} */ (this.animatedEmoji_[i][1]);
  154. var url = this.urlPrefix_ + emoji.getUrl();
  155. this.imageLoader_.addImage(emoji.getId(), url);
  156. }
  157. this.getHandler().listen(
  158. this.imageLoader_, goog.events.EventType.LOAD, this.handleImageLoad_);
  159. this.imageLoader_.start();
  160. }
  161. };
  162. /**
  163. * Handles image load events from the ImageLoader.
  164. *
  165. * @param {goog.events.Event} e The event object.
  166. * @private
  167. */
  168. goog.ui.emoji.EmojiPalette.prototype.handleImageLoad_ = function(e) {
  169. var id = e.target.id;
  170. var url = e.target.src;
  171. // Just to be safe, we check to make sure we have an id and src url from
  172. // the event target, which the ImageLoader sets to an Image object.
  173. if (id && url) {
  174. var item = this.emoji_[this.emojiMap_[id]];
  175. if (item) {
  176. this.getRenderer().updateAnimatedPaletteItem(item, e.target);
  177. }
  178. }
  179. };
  180. /**
  181. * Returns the image loader that this palette uses. Used for testing.
  182. *
  183. * @return {goog.net.ImageLoader} the image loader.
  184. */
  185. goog.ui.emoji.EmojiPalette.prototype.getImageLoader = function() {
  186. return this.imageLoader_;
  187. };
  188. /** @override */
  189. goog.ui.emoji.EmojiPalette.prototype.disposeInternal = function() {
  190. goog.ui.emoji.EmojiPalette.superClass_.disposeInternal.call(this);
  191. if (this.imageLoader_) {
  192. this.imageLoader_.dispose();
  193. this.imageLoader_ = null;
  194. }
  195. this.animatedEmoji_ = null;
  196. this.emojiCells_ = null;
  197. this.emojiMap_ = null;
  198. this.emoji_ = null;
  199. };
  200. /**
  201. * Returns a goomoji id from an img or the containing td, or null if none
  202. * exists for that element.
  203. *
  204. * @param {Element} el The element to get the Goomoji id from.
  205. * @return {?string} A goomoji id from an img or the containing td, or null if
  206. * none exists for that element.
  207. * @private
  208. */
  209. goog.ui.emoji.EmojiPalette.prototype.getGoomojiIdFromElement_ = function(el) {
  210. if (!el) {
  211. return null;
  212. }
  213. var item = this.getRenderer().getContainingItem(this, el);
  214. if (item) {
  215. return item.getAttribute(goog.ui.emoji.Emoji.ATTRIBUTE) != '' ?
  216. item.getAttribute(goog.ui.emoji.Emoji.ATTRIBUTE) :
  217. item.getAttribute(goog.ui.emoji.Emoji.DATA_ATTRIBUTE);
  218. }
  219. return null;
  220. };
  221. /**
  222. * @return {goog.ui.emoji.Emoji} The currently selected emoji from this palette.
  223. */
  224. goog.ui.emoji.EmojiPalette.prototype.getSelectedEmoji = function() {
  225. var elem = /** @type {Element} */ (this.getSelectedItem());
  226. var goomojiId = this.getGoomojiIdFromElement_(elem);
  227. return this.emojiCells_[goomojiId];
  228. };
  229. /**
  230. * @return {number} The number of emoji managed by this palette.
  231. */
  232. goog.ui.emoji.EmojiPalette.prototype.getNumberOfEmoji = function() {
  233. return this.emojiCells_.length;
  234. };
  235. /**
  236. * Returns the index of the specified emoji within this palette.
  237. *
  238. * @param {string} id Id of the emoji to look up.
  239. * @return {number} The index of the specified emoji within this palette.
  240. */
  241. goog.ui.emoji.EmojiPalette.prototype.getEmojiIndex = function(id) {
  242. return this.emojiMap_[id];
  243. };