Adding float-no-zero branch hosted build
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
// Copyright 2007 The Closure Library Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS-IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
/**
|
||||
* @fileoverview Emoji implementation.
|
||||
*
|
||||
*/
|
||||
|
||||
goog.provide('goog.ui.emoji.Emoji');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Creates an emoji.
|
||||
*
|
||||
* A simple wrapper for an emoji.
|
||||
*
|
||||
* @param {string} url URL pointing to the source image for the emoji.
|
||||
* @param {string} id The id of the emoji, e.g., 'std.1'.
|
||||
* @constructor
|
||||
*/
|
||||
goog.ui.emoji.Emoji = function(url, id) {
|
||||
/**
|
||||
* The URL pointing to the source image for the emoji
|
||||
*
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
this.url_ = url;
|
||||
|
||||
/**
|
||||
* The id of the emoji
|
||||
*
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
this.id_ = id;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* The name of the goomoji attribute, used for emoji image elements.
|
||||
* @type {string}
|
||||
*/
|
||||
goog.ui.emoji.Emoji.ATTRIBUTE = 'goomoji';
|
||||
|
||||
|
||||
/**
|
||||
* @return {string} The URL for this emoji.
|
||||
*/
|
||||
goog.ui.emoji.Emoji.prototype.getUrl = function() {
|
||||
return this.url_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {string} The id of this emoji.
|
||||
*/
|
||||
goog.ui.emoji.Emoji.prototype.getId = function() {
|
||||
return this.id_;
|
||||
};
|
||||
@@ -0,0 +1,288 @@
|
||||
// Copyright 2007 The Closure Library Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS-IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
/**
|
||||
* @fileoverview Emoji Palette implementation. This provides a UI widget for
|
||||
* choosing an emoji from a palette of possible choices. EmojiPalettes are
|
||||
* contained within EmojiPickers.
|
||||
*
|
||||
* See ../demos/popupemojipicker.html for an example of how to instantiate
|
||||
* an emoji picker.
|
||||
*
|
||||
* Based on goog.ui.ColorPicker (colorpicker.js).
|
||||
*
|
||||
*/
|
||||
|
||||
goog.provide('goog.ui.emoji.EmojiPalette');
|
||||
|
||||
goog.require('goog.events.EventType');
|
||||
goog.require('goog.net.ImageLoader');
|
||||
goog.require('goog.ui.Palette');
|
||||
goog.require('goog.ui.emoji.Emoji');
|
||||
goog.require('goog.ui.emoji.EmojiPaletteRenderer');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A page of emoji to be displayed in an EmojiPicker.
|
||||
*
|
||||
* @param {Array.<Array>} emoji List of emoji for this page.
|
||||
* @param {?string=} opt_urlPrefix Prefix that should be prepended to all URL.
|
||||
* @param {goog.ui.PaletteRenderer=} opt_renderer Renderer used to render or
|
||||
* decorate the palette; defaults to {@link goog.ui.PaletteRenderer}.
|
||||
* @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper.
|
||||
* @extends {goog.ui.Palette}
|
||||
* @constructor
|
||||
*/
|
||||
goog.ui.emoji.EmojiPalette = function(emoji,
|
||||
opt_urlPrefix,
|
||||
opt_renderer,
|
||||
opt_domHelper) {
|
||||
goog.ui.Palette.call(this,
|
||||
null,
|
||||
opt_renderer ||
|
||||
new goog.ui.emoji.EmojiPaletteRenderer(null),
|
||||
opt_domHelper);
|
||||
/**
|
||||
* All the different emoji that this palette can display. Maps emoji ids
|
||||
* (string) to the goog.ui.emoji.Emoji for that id.
|
||||
*
|
||||
* @type {Object}
|
||||
* @private
|
||||
*/
|
||||
this.emojiCells_ = {};
|
||||
|
||||
/**
|
||||
* Map of emoji id to index into this.emojiCells_.
|
||||
*
|
||||
* @type {Object}
|
||||
* @private
|
||||
*/
|
||||
this.emojiMap_ = {};
|
||||
|
||||
/**
|
||||
* List of the animated emoji in this palette. Each internal array is of type
|
||||
* [HTMLDivElement, goog.ui.emoji.Emoji], and represents the palette item
|
||||
* for that animated emoji, and the Emoji object.
|
||||
*
|
||||
* @type {Array.<Array.<(HTMLDivElement|goog.ui.emoji.Emoji)>>}
|
||||
* @private
|
||||
*/
|
||||
this.animatedEmoji_ = [];
|
||||
|
||||
this.urlPrefix_ = opt_urlPrefix || '';
|
||||
|
||||
/**
|
||||
* Palette items that are displayed on this page of the emoji picker. Each
|
||||
* item is a div wrapped around a div or an img.
|
||||
*
|
||||
* @type {Array.<HTMLDivElement>}
|
||||
* @private
|
||||
*/
|
||||
this.emoji_ = this.getEmojiArrayFromProperties_(emoji);
|
||||
|
||||
this.setContent(this.emoji_);
|
||||
};
|
||||
goog.inherits(goog.ui.emoji.EmojiPalette, goog.ui.Palette);
|
||||
|
||||
|
||||
/**
|
||||
* Indicates a prefix that should be prepended to all URLs of images in this
|
||||
* emojipalette. This provides an optimization if the URLs are long, so that
|
||||
* the client does not have to send a long string for each emoji.
|
||||
*
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.emoji.EmojiPalette.prototype.urlPrefix_ = '';
|
||||
|
||||
|
||||
/**
|
||||
* Whether the emoji images have been loaded.
|
||||
*
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.emoji.EmojiPalette.prototype.imagesLoaded_ = false;
|
||||
|
||||
|
||||
/**
|
||||
* Image loader for loading animated emoji.
|
||||
*
|
||||
* @type {goog.net.ImageLoader}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.emoji.EmojiPalette.prototype.imageLoader_;
|
||||
|
||||
|
||||
/**
|
||||
* Helps create an array of emoji palette items from an array of emoji
|
||||
* properties. Each element will be either a div with background-image set to
|
||||
* a sprite, or an img element pointing directly to an emoji, and all elements
|
||||
* are wrapped with an outer div for alignment issues (i.e., this allows
|
||||
* centering the inner div).
|
||||
*
|
||||
* @param {Object} emojiGroup The group of emoji for this page.
|
||||
* @return {Array.<HTMLDivElement>} The emoji items.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.emoji.EmojiPalette.prototype.getEmojiArrayFromProperties_ =
|
||||
function(emojiGroup) {
|
||||
var emojiItems = [];
|
||||
|
||||
for (var i = 0; i < emojiGroup.length; i++) {
|
||||
var url = emojiGroup[i][0];
|
||||
var id = emojiGroup[i][1];
|
||||
var spriteInfo = emojiGroup[i][2];
|
||||
var displayUrl = spriteInfo ? spriteInfo.getUrl() :
|
||||
this.urlPrefix_ + url;
|
||||
|
||||
var item = this.getRenderer().createPaletteItem(
|
||||
this.getDomHelper(), id, spriteInfo, displayUrl);
|
||||
emojiItems.push(item);
|
||||
|
||||
var emoji = new goog.ui.emoji.Emoji(url, id);
|
||||
this.emojiCells_[id] = emoji;
|
||||
this.emojiMap_[id] = i;
|
||||
|
||||
// Keep track of sprited emoji that are animated, for later loading.
|
||||
if (spriteInfo && spriteInfo.isAnimated()) {
|
||||
this.animatedEmoji_.push([item, emoji]);
|
||||
}
|
||||
}
|
||||
|
||||
// Create the image loader now so that tests can access it before it has
|
||||
// started loading images.
|
||||
if (this.animatedEmoji_.length > 0) {
|
||||
this.imageLoader_ = new goog.net.ImageLoader();
|
||||
}
|
||||
|
||||
this.imagesLoaded_ = true;
|
||||
return emojiItems;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sends off requests for all the animated emoji and replaces their static
|
||||
* sprites when the images are done downloading.
|
||||
*/
|
||||
goog.ui.emoji.EmojiPalette.prototype.loadAnimatedEmoji = function() {
|
||||
if (this.animatedEmoji_.length > 0) {
|
||||
for (var i = 0; i < this.animatedEmoji_.length; i++) {
|
||||
var paletteItem = /** @type {Element} */ (this.animatedEmoji_[i][0]);
|
||||
var emoji =
|
||||
/** @type {goog.ui.emoji.Emoji} */ (this.animatedEmoji_[i][1]);
|
||||
var url = this.urlPrefix_ + emoji.getUrl();
|
||||
|
||||
this.imageLoader_.addImage(emoji.getId(), url);
|
||||
}
|
||||
|
||||
this.getHandler().listen(this.imageLoader_, goog.events.EventType.LOAD,
|
||||
this.handleImageLoad_);
|
||||
this.imageLoader_.start();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handles image load events from the ImageLoader.
|
||||
*
|
||||
* @param {goog.events.Event} e The event object.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.emoji.EmojiPalette.prototype.handleImageLoad_ = function(e) {
|
||||
var id = e.target.id;
|
||||
var url = e.target.src;
|
||||
// Just to be safe, we check to make sure we have an id and src url from
|
||||
// the event target, which the ImageLoader sets to an Image object.
|
||||
if (id && url) {
|
||||
var item = this.emoji_[this.emojiMap_[id]];
|
||||
if (item) {
|
||||
this.getRenderer().updateAnimatedPaletteItem(item, e.target);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the image loader that this palette uses. Used for testing.
|
||||
*
|
||||
* @return {goog.net.ImageLoader} the image loader.
|
||||
*/
|
||||
goog.ui.emoji.EmojiPalette.prototype.getImageLoader = function() {
|
||||
return this.imageLoader_;
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.emoji.EmojiPalette.prototype.disposeInternal = function() {
|
||||
goog.ui.emoji.EmojiPalette.superClass_.disposeInternal.call(this);
|
||||
|
||||
if (this.imageLoader_) {
|
||||
this.imageLoader_.dispose();
|
||||
this.imageLoader_ = null;
|
||||
}
|
||||
this.animatedEmoji_ = null;
|
||||
this.emojiCells_ = null;
|
||||
this.emojiMap_ = null;
|
||||
this.emoji_ = null;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns a goomoji id from an img or the containing td, or null if none
|
||||
* exists for that element.
|
||||
*
|
||||
* @param {Element} el The element to get the Goomoji id from.
|
||||
* @return {?string} A goomoji id from an img or the containing td, or null if
|
||||
* none exists for that element.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.emoji.EmojiPalette.prototype.getGoomojiIdFromElement_ = function(el) {
|
||||
if (!el) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var item = this.getRenderer().getContainingItem(this, el);
|
||||
return item ? item.getAttribute(goog.ui.emoji.Emoji.ATTRIBUTE) : null;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {goog.ui.emoji.Emoji} The currently selected emoji from this palette.
|
||||
*/
|
||||
goog.ui.emoji.EmojiPalette.prototype.getSelectedEmoji = function() {
|
||||
var elem = /** @type {Element} */ (this.getSelectedItem());
|
||||
var goomojiId = this.getGoomojiIdFromElement_(elem);
|
||||
return this.emojiCells_[goomojiId];
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {number} The number of emoji managed by this palette.
|
||||
*/
|
||||
goog.ui.emoji.EmojiPalette.prototype.getNumberOfEmoji = function() {
|
||||
return this.emojiCells_.length;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the index of the specified emoji within this palette.
|
||||
*
|
||||
* @param {string} id Id of the emoji to look up.
|
||||
* @return {number} The index of the specified emoji within this palette.
|
||||
*/
|
||||
goog.ui.emoji.EmojiPalette.prototype.getEmojiIndex = function(id) {
|
||||
return this.emojiMap_[id];
|
||||
};
|
||||
@@ -0,0 +1,208 @@
|
||||
// Copyright 2008 The Closure Library Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS-IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
/**
|
||||
* @fileoverview Emoji Palette renderer implementation.
|
||||
*
|
||||
*/
|
||||
|
||||
goog.provide('goog.ui.emoji.EmojiPaletteRenderer');
|
||||
|
||||
goog.require('goog.a11y.aria');
|
||||
goog.require('goog.dom.NodeType');
|
||||
goog.require('goog.dom.classes');
|
||||
goog.require('goog.style');
|
||||
goog.require('goog.ui.PaletteRenderer');
|
||||
goog.require('goog.ui.emoji.Emoji');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Renders an emoji palette.
|
||||
*
|
||||
* @param {?string} defaultImgUrl Url of the img that should be used to fill up
|
||||
* the cells in the emoji table, to prevent jittering. Will be stretched
|
||||
* to the emoji cell size. A good image is a transparent dot.
|
||||
* @constructor
|
||||
* @extends {goog.ui.PaletteRenderer}
|
||||
*/
|
||||
goog.ui.emoji.EmojiPaletteRenderer = function(defaultImgUrl) {
|
||||
goog.ui.PaletteRenderer.call(this);
|
||||
|
||||
this.defaultImgUrl_ = defaultImgUrl;
|
||||
};
|
||||
goog.inherits(goog.ui.emoji.EmojiPaletteRenderer, goog.ui.PaletteRenderer);
|
||||
|
||||
|
||||
/**
|
||||
* Globally unique ID sequence for cells rendered by this renderer class.
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.emoji.EmojiPaletteRenderer.cellId_ = 0;
|
||||
|
||||
|
||||
/**
|
||||
* Url of the img that should be used for cells in the emoji palette that are
|
||||
* not filled with emoji, i.e., after all the emoji have already been placed
|
||||
* on a page.
|
||||
*
|
||||
* @type {?string}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.emoji.EmojiPaletteRenderer.prototype.defaultImgUrl_ = null;
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.emoji.EmojiPaletteRenderer.getCssClass = function() {
|
||||
return goog.getCssName('goog-ui-emojipalette');
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Creates a palette item from the given emoji data.
|
||||
*
|
||||
* @param {goog.dom.DomHelper} dom DOM helper for constructing DOM elements.
|
||||
* @param {string} id Goomoji id for the emoji.
|
||||
* @param {goog.ui.emoji.SpriteInfo} spriteInfo Spriting info for the emoji.
|
||||
* @param {string} displayUrl URL of the image served for this cell, whether
|
||||
* an individual emoji image or a sprite.
|
||||
* @return {HTMLDivElement} The palette item for this emoji.
|
||||
*/
|
||||
goog.ui.emoji.EmojiPaletteRenderer.prototype.createPaletteItem =
|
||||
function(dom, id, spriteInfo, displayUrl) {
|
||||
var el;
|
||||
|
||||
if (spriteInfo) {
|
||||
var cssClass = spriteInfo.getCssClass();
|
||||
if (cssClass) {
|
||||
el = dom.createDom('div', cssClass);
|
||||
} else {
|
||||
el = this.buildElementFromSpriteMetadata(dom, spriteInfo, displayUrl);
|
||||
}
|
||||
} else {
|
||||
el = dom.createDom('img', {'src': displayUrl});
|
||||
}
|
||||
|
||||
var outerdiv =
|
||||
dom.createDom('div', goog.getCssName('goog-palette-cell-wrapper'), el);
|
||||
outerdiv.setAttribute(goog.ui.emoji.Emoji.ATTRIBUTE, id);
|
||||
return /** @type {HTMLDivElement} */ (outerdiv);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Modifies a palette item containing an animated emoji, in response to the
|
||||
* animated emoji being successfully downloaded.
|
||||
*
|
||||
* @param {Element} item The palette item to update.
|
||||
* @param {Image} animatedImg An Image object containing the animated emoji.
|
||||
*/
|
||||
goog.ui.emoji.EmojiPaletteRenderer.prototype.updateAnimatedPaletteItem =
|
||||
function(item, animatedImg) {
|
||||
// An animated emoji is one that had sprite info for a static version and is
|
||||
// now being updated. See createPaletteItem for the structure of the palette
|
||||
// items we're modifying.
|
||||
|
||||
var inner = /** @type {Element} */ (item.firstChild);
|
||||
|
||||
// The first case is a palette item with a CSS class representing the sprite,
|
||||
// and an animated emoji.
|
||||
var classes = goog.dom.classes.get(inner);
|
||||
if (classes && classes.length == 1) {
|
||||
inner.className = '';
|
||||
}
|
||||
|
||||
goog.style.setStyle(inner, {
|
||||
'width': animatedImg.width,
|
||||
'height': animatedImg.height,
|
||||
'background-image': 'url(' + animatedImg.src + ')',
|
||||
'background-position': '0 0'
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Builds the inner contents of a palette item out of sprite metadata.
|
||||
*
|
||||
* @param {goog.dom.DomHelper} dom DOM helper for constructing DOM elements.
|
||||
* @param {goog.ui.emoji.SpriteInfo} spriteInfo The metadata to create the css
|
||||
* for the sprite.
|
||||
* @param {string} displayUrl The URL of the image for this cell.
|
||||
* @return {HTMLDivElement} The inner element for a palette item.
|
||||
*/
|
||||
goog.ui.emoji.EmojiPaletteRenderer.prototype.buildElementFromSpriteMetadata =
|
||||
function(dom, spriteInfo, displayUrl) {
|
||||
var width = spriteInfo.getWidthCssValue();
|
||||
var height = spriteInfo.getHeightCssValue();
|
||||
var x = spriteInfo.getXOffsetCssValue();
|
||||
var y = spriteInfo.getYOffsetCssValue();
|
||||
|
||||
var el = dom.createDom('div');
|
||||
goog.style.setStyle(el, {
|
||||
'width': width,
|
||||
'height': height,
|
||||
'background-image': 'url(' + displayUrl + ')',
|
||||
'background-repeat': 'no-repeat',
|
||||
'background-position': x + ' ' + y
|
||||
});
|
||||
|
||||
return /** @type {HTMLDivElement} */ (el);
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.emoji.EmojiPaletteRenderer.prototype.createCell = function(node, dom) {
|
||||
// Create a cell with the default img if we're out of items, in order to
|
||||
// prevent jitter in the table. If there's no default img url, just create an
|
||||
// empty div, to prevent trying to fetch a null url.
|
||||
if (!node) {
|
||||
var elem = this.defaultImgUrl_ ?
|
||||
dom.createDom('img', {'src': this.defaultImgUrl_}) :
|
||||
dom.createDom('div');
|
||||
node = dom.createDom('div', goog.getCssName('goog-palette-cell-wrapper'),
|
||||
elem);
|
||||
}
|
||||
|
||||
var cell = dom.createDom('td', {
|
||||
'class': goog.getCssName(this.getCssClass(), 'cell'),
|
||||
// Cells must have an ID, for accessibility, so we generate one here.
|
||||
'id': this.getCssClass() + '-cell-' +
|
||||
goog.ui.emoji.EmojiPaletteRenderer.cellId_++
|
||||
}, node);
|
||||
goog.a11y.aria.setRole(cell, 'gridcell');
|
||||
return cell;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the item corresponding to the given node, or null if the node is
|
||||
* neither a palette cell nor part of a palette item.
|
||||
* @param {goog.ui.Palette} palette Palette in which to look for the item.
|
||||
* @param {Node} node Node to look for.
|
||||
* @return {Node} The corresponding palette item (null if not found).
|
||||
* @override
|
||||
*/
|
||||
goog.ui.emoji.EmojiPaletteRenderer.prototype.getContainingItem =
|
||||
function(palette, node) {
|
||||
var root = palette.getElement();
|
||||
while (node && node.nodeType == goog.dom.NodeType.ELEMENT && node != root) {
|
||||
if (node.tagName == 'TD') {
|
||||
return node.firstChild;
|
||||
}
|
||||
node = node.parentNode;
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
@@ -0,0 +1,804 @@
|
||||
// Copyright 2007 The Closure Library Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS-IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
/**
|
||||
* @fileoverview Emoji Picker implementation. This provides a UI widget for
|
||||
* choosing an emoji from a grid of possible choices.
|
||||
*
|
||||
* @see ../demos/popupemojipicker.html for an example of how to instantiate
|
||||
* an emoji picker.
|
||||
*
|
||||
* Based on goog.ui.ColorPicker (colorpicker.js).
|
||||
*
|
||||
* @see ../../demos/popupemojipicker.html
|
||||
*/
|
||||
|
||||
goog.provide('goog.ui.emoji.EmojiPicker');
|
||||
|
||||
goog.require('goog.log');
|
||||
goog.require('goog.style');
|
||||
goog.require('goog.ui.Component');
|
||||
goog.require('goog.ui.TabPane');
|
||||
goog.require('goog.ui.emoji.Emoji');
|
||||
goog.require('goog.ui.emoji.EmojiPalette');
|
||||
goog.require('goog.ui.emoji.EmojiPaletteRenderer');
|
||||
goog.require('goog.ui.emoji.ProgressiveEmojiPaletteRenderer');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new, empty emoji picker. An emoji picker is a grid of emoji, each
|
||||
* cell of the grid containing a single emoji. The picker may contain multiple
|
||||
* pages of emoji.
|
||||
*
|
||||
* When a user selects an emoji, by either clicking or pressing enter, the
|
||||
* picker fires a goog.ui.Component.EventType.ACTION event with the id. The
|
||||
* client listens on this event and in the handler can retrieve the id of the
|
||||
* selected emoji and do something with it, for instance, inserting an image
|
||||
* tag into a rich text control. An emoji picker does not maintain state. That
|
||||
* is, once an emoji is selected, the emoji picker does not remember which emoji
|
||||
* was selected.
|
||||
*
|
||||
* The emoji picker is implemented as a tabpane with each tabpage being a table.
|
||||
* Each of the tables are the same size to prevent jittering when switching
|
||||
* between pages.
|
||||
*
|
||||
* @param {string} defaultImgUrl Url of the img that should be used to fill up
|
||||
* the cells in the emoji table, to prevent jittering. Should be the same
|
||||
* size as the emoji.
|
||||
* @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper.
|
||||
* @extends {goog.ui.Component}
|
||||
* @constructor
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker = function(defaultImgUrl, opt_domHelper) {
|
||||
goog.ui.Component.call(this, opt_domHelper);
|
||||
|
||||
this.defaultImgUrl_ = defaultImgUrl;
|
||||
|
||||
/**
|
||||
* Emoji that this picker displays.
|
||||
*
|
||||
* @type {Array.<Object>}
|
||||
* @private
|
||||
*/
|
||||
this.emoji_ = [];
|
||||
|
||||
/**
|
||||
* Pages of this emoji picker.
|
||||
*
|
||||
* @type {Array.<goog.ui.emoji.EmojiPalette>}
|
||||
* @private
|
||||
*/
|
||||
this.pages_ = [];
|
||||
|
||||
/**
|
||||
* Keeps track of which pages in the picker have been loaded. Used for delayed
|
||||
* loading of tabs.
|
||||
*
|
||||
* @type {Array.<boolean>}
|
||||
* @private
|
||||
*/
|
||||
this.pageLoadStatus_ = [];
|
||||
|
||||
/**
|
||||
* Tabpane to hold the pages of this emojipicker.
|
||||
*
|
||||
* @type {goog.ui.TabPane}
|
||||
* @private
|
||||
*/
|
||||
this.tabPane_ = null;
|
||||
|
||||
this.getHandler().listen(this, goog.ui.Component.EventType.ACTION,
|
||||
this.onEmojiPaletteAction_);
|
||||
};
|
||||
goog.inherits(goog.ui.emoji.EmojiPicker, goog.ui.Component);
|
||||
|
||||
|
||||
/**
|
||||
* Default number of rows per grid of emoji.
|
||||
*
|
||||
* @type {number}
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker.DEFAULT_NUM_ROWS = 5;
|
||||
|
||||
|
||||
/**
|
||||
* Default number of columns per grid of emoji.
|
||||
*
|
||||
* @type {number}
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker.DEFAULT_NUM_COLS = 10;
|
||||
|
||||
|
||||
/**
|
||||
* Default location of the tabs in relation to the emoji grids.
|
||||
*
|
||||
* @type {goog.ui.TabPane.TabLocation}
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker.DEFAULT_TAB_LOCATION =
|
||||
goog.ui.TabPane.TabLocation.TOP;
|
||||
|
||||
|
||||
/**
|
||||
* Number of rows per grid of emoji.
|
||||
*
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker.prototype.numRows_ =
|
||||
goog.ui.emoji.EmojiPicker.DEFAULT_NUM_ROWS;
|
||||
|
||||
|
||||
/**
|
||||
* Number of columns per grid of emoji.
|
||||
*
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker.prototype.numCols_ =
|
||||
goog.ui.emoji.EmojiPicker.DEFAULT_NUM_COLS;
|
||||
|
||||
|
||||
/**
|
||||
* Whether the number of rows in the picker should be automatically determined
|
||||
* by the specified number of columns so as to minimize/eliminate jitter when
|
||||
* switching between tabs.
|
||||
*
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker.prototype.autoSizeByColumnCount_ = true;
|
||||
|
||||
|
||||
/**
|
||||
* Location of the tabs for the picker tabpane.
|
||||
*
|
||||
* @type {goog.ui.TabPane.TabLocation}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker.prototype.tabLocation_ =
|
||||
goog.ui.emoji.EmojiPicker.DEFAULT_TAB_LOCATION;
|
||||
|
||||
|
||||
/**
|
||||
* Whether the component is focusable.
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker.prototype.focusable_ = true;
|
||||
|
||||
|
||||
/**
|
||||
* Url of the img that should be used for cells in the emoji picker that are
|
||||
* not filled with emoji, i.e., after all the emoji have already been placed
|
||||
* on a page.
|
||||
*
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker.prototype.defaultImgUrl_;
|
||||
|
||||
|
||||
/**
|
||||
* If present, indicates a prefix that should be prepended to all URLs
|
||||
* of images in this emojipicker. This provides an optimization if the URLs
|
||||
* are long, so that the client does not have to send a long string for each
|
||||
* emoji.
|
||||
*
|
||||
* @type {string|undefined}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker.prototype.urlPrefix_;
|
||||
|
||||
|
||||
/**
|
||||
* If true, delay loading the images for the emojipalettes until after
|
||||
* construction. This gives a better user experience before the images are in
|
||||
* the cache, since other widgets waiting for construction of the emojipalettes
|
||||
* won't have to wait for all the images (which may be a substantial amount) to
|
||||
* load.
|
||||
*
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker.prototype.delayedLoad_ = false;
|
||||
|
||||
|
||||
/**
|
||||
* Whether to use progressive rendering in the emojipicker's palette, if using
|
||||
* sprited imgs. If true, then uses img tags, which most browsers render
|
||||
* progressively (i.e., as the data comes in). If false, then uses div tags
|
||||
* with the background-image, which some newer browsers render progressively
|
||||
* but older ones do not.
|
||||
*
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker.prototype.progressiveRender_ = false;
|
||||
|
||||
|
||||
/**
|
||||
* Whether to require the caller to manually specify when to start loading
|
||||
* animated emoji. This is primarily for unittests to be able to test the
|
||||
* structure of the emojipicker palettes before and after the animated emoji
|
||||
* have been loaded.
|
||||
*
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker.prototype.manualLoadOfAnimatedEmoji_ = false;
|
||||
|
||||
|
||||
/**
|
||||
* Index of the active page in the picker.
|
||||
*
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker.prototype.activePage_ = -1;
|
||||
|
||||
|
||||
/**
|
||||
* Adds a group of emoji to the picker.
|
||||
*
|
||||
* @param {string|Element} title Title for the group.
|
||||
* @param {Array.<Array.<string>>} emojiGroup A new group of emoji to be added
|
||||
* Each internal array contains [emojiUrl, emojiId].
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker.prototype.addEmojiGroup =
|
||||
function(title, emojiGroup) {
|
||||
this.emoji_.push({title: title, emoji: emojiGroup});
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets the number of rows per grid in the emoji picker.
|
||||
*
|
||||
* @return {number} number of rows per grid.
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker.prototype.getNumRows = function() {
|
||||
return this.numRows_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets the number of columns per grid in the emoji picker.
|
||||
*
|
||||
* @return {number} number of columns per grid.
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker.prototype.getNumColumns = function() {
|
||||
return this.numCols_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the number of rows per grid in the emoji picker. This should only be
|
||||
* called before the picker has been rendered.
|
||||
*
|
||||
* @param {number} numRows Number of rows per grid.
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker.prototype.setNumRows = function(numRows) {
|
||||
this.numRows_ = numRows;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the number of columns per grid in the emoji picker. This should only be
|
||||
* called before the picker has been rendered.
|
||||
*
|
||||
* @param {number} numCols Number of columns per grid.
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker.prototype.setNumColumns = function(numCols) {
|
||||
this.numCols_ = numCols;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets whether to automatically size the emojipicker based on the number of
|
||||
* columns and the number of emoji in each group, so as to reduce jitter.
|
||||
*
|
||||
* @param {boolean} autoSize Whether to automatically size the picker.
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker.prototype.setAutoSizeByColumnCount =
|
||||
function(autoSize) {
|
||||
this.autoSizeByColumnCount_ = autoSize;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the location of the tabs in relation to the emoji grids. This should
|
||||
* only be called before the picker has been rendered.
|
||||
*
|
||||
* @param {goog.ui.TabPane.TabLocation} tabLocation The location of the tabs.
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker.prototype.setTabLocation = function(tabLocation) {
|
||||
this.tabLocation_ = tabLocation;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets whether loading of images should be delayed until after dom creation.
|
||||
* Thus, this function must be called before {@link #createDom}. If set to true,
|
||||
* the client must call {@link #loadImages} when they wish the images to be
|
||||
* loaded.
|
||||
*
|
||||
* @param {boolean} shouldDelay Whether to delay loading the images.
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker.prototype.setDelayedLoad = function(shouldDelay) {
|
||||
this.delayedLoad_ = shouldDelay;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets whether to require the caller to manually specify when to start loading
|
||||
* animated emoji. This is primarily for unittests to be able to test the
|
||||
* structure of the emojipicker palettes before and after the animated emoji
|
||||
* have been loaded. This only affects sprited emojipickers with sprite data
|
||||
* for animated emoji.
|
||||
*
|
||||
* @param {boolean} manual Whether to load animated emoji manually.
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker.prototype.setManualLoadOfAnimatedEmoji =
|
||||
function(manual) {
|
||||
this.manualLoadOfAnimatedEmoji_ = manual;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns true if the component is focusable, false otherwise. The default
|
||||
* is true. Focusable components always have a tab index and allocate a key
|
||||
* handler to handle keyboard events while focused.
|
||||
* @return {boolean} Whether the component is focusable.
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker.prototype.isFocusable = function() {
|
||||
return this.focusable_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets whether the component is focusable. The default is true.
|
||||
* Focusable components always have a tab index and allocate a key handler to
|
||||
* handle keyboard events while focused.
|
||||
* @param {boolean} focusable Whether the component is focusable.
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker.prototype.setFocusable = function(focusable) {
|
||||
this.focusable_ = focusable;
|
||||
for (var i = 0; i < this.pages_.length; i++) {
|
||||
if (this.pages_[i]) {
|
||||
this.pages_[i].setSupportedState(goog.ui.Component.State.FOCUSED,
|
||||
focusable);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the URL prefix for the emoji URLs.
|
||||
*
|
||||
* @param {string} urlPrefix Prefix that should be prepended to all URLs.
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker.prototype.setUrlPrefix = function(urlPrefix) {
|
||||
this.urlPrefix_ = urlPrefix;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the progressive rendering aspect of this emojipicker. Must be called
|
||||
* before createDom to have an effect.
|
||||
*
|
||||
* @param {boolean} progressive Whether this picker should render progressively.
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker.prototype.setProgressiveRender =
|
||||
function(progressive) {
|
||||
this.progressiveRender_ = progressive;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Logger for the emoji picker.
|
||||
*
|
||||
* @type {goog.log.Logger}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker.prototype.logger_ =
|
||||
goog.log.getLogger('goog.ui.emoji.EmojiPicker');
|
||||
|
||||
|
||||
/**
|
||||
* Adjusts the number of rows to be the maximum row count out of all the emoji
|
||||
* groups, in order to prevent jitter in switching among the tabs.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker.prototype.adjustNumRowsIfNecessary_ = function() {
|
||||
var currentMax = 0;
|
||||
|
||||
for (var i = 0; i < this.emoji_.length; i++) {
|
||||
var numEmoji = this.emoji_[i].emoji.length;
|
||||
var rowsNeeded = Math.ceil(numEmoji / this.numCols_);
|
||||
if (rowsNeeded > currentMax) {
|
||||
currentMax = rowsNeeded;
|
||||
}
|
||||
}
|
||||
|
||||
this.setNumRows(currentMax);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Causes the emoji imgs to be loaded into the picker. Used for delayed loading.
|
||||
* No-op if delayed loading is not set.
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker.prototype.loadImages = function() {
|
||||
if (!this.delayedLoad_) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Load the first page only
|
||||
this.loadPage_(0);
|
||||
this.activePage_ = 0;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @override
|
||||
* @suppress {deprecated} Using deprecated goog.ui.TabPane.
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker.prototype.createDom = function() {
|
||||
this.setElementInternal(this.getDomHelper().createDom('div'));
|
||||
|
||||
if (this.autoSizeByColumnCount_) {
|
||||
this.adjustNumRowsIfNecessary_();
|
||||
}
|
||||
|
||||
if (this.emoji_.length == 0) {
|
||||
throw Error('Must add some emoji to the picker');
|
||||
}
|
||||
|
||||
// If there is more than one group of emoji, we construct a tabpane
|
||||
if (this.emoji_.length > 1) {
|
||||
// Give the tabpane a div to use as its content element, since tabpane
|
||||
// overwrites the CSS class of the element it's passed
|
||||
var div = this.getDomHelper().createDom('div');
|
||||
this.getElement().appendChild(div);
|
||||
this.tabPane_ = new goog.ui.TabPane(div,
|
||||
this.tabLocation_,
|
||||
this.getDomHelper(),
|
||||
true /* use MOUSEDOWN */);
|
||||
}
|
||||
|
||||
this.renderer_ = this.progressiveRender_ ?
|
||||
new goog.ui.emoji.ProgressiveEmojiPaletteRenderer(this.defaultImgUrl_) :
|
||||
new goog.ui.emoji.EmojiPaletteRenderer(this.defaultImgUrl_);
|
||||
|
||||
for (var i = 0; i < this.emoji_.length; i++) {
|
||||
var emoji = this.emoji_[i].emoji;
|
||||
var page = this.delayedLoad_ ?
|
||||
this.createPlaceholderEmojiPage_(emoji) :
|
||||
this.createEmojiPage_(emoji, i);
|
||||
this.pages_.push(page);
|
||||
}
|
||||
|
||||
this.activePage_ = 0;
|
||||
this.getElement().tabIndex = 0;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Used by unittests to manually load the animated emoji for this picker.
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker.prototype.manuallyLoadAnimatedEmoji = function() {
|
||||
for (var i = 0; i < this.pages_.length; i++) {
|
||||
this.pages_[i].loadAnimatedEmoji();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Creates a page if it has not already been loaded. This has the side effects
|
||||
* of setting the load status of the page to true.
|
||||
*
|
||||
* @param {Array.<Array.<string>>} emoji Emoji for this page. See
|
||||
* {@link addEmojiGroup} for more details.
|
||||
* @param {number} index Index of the page in the emojipicker.
|
||||
* @return {goog.ui.emoji.EmojiPalette} the emoji page.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker.prototype.createEmojiPage_ = function(emoji, index) {
|
||||
// Safeguard against trying to create the same page twice
|
||||
if (this.pageLoadStatus_[index]) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var palette = new goog.ui.emoji.EmojiPalette(emoji,
|
||||
this.urlPrefix_,
|
||||
this.renderer_,
|
||||
this.getDomHelper());
|
||||
if (!this.manualLoadOfAnimatedEmoji_) {
|
||||
palette.loadAnimatedEmoji();
|
||||
}
|
||||
palette.setSize(this.numCols_, this.numRows_);
|
||||
palette.setSupportedState(goog.ui.Component.State.FOCUSED, this.focusable_);
|
||||
palette.createDom();
|
||||
palette.setParent(this);
|
||||
|
||||
this.pageLoadStatus_[index] = true;
|
||||
|
||||
return palette;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns an array of emoji whose real URLs have been replaced with the
|
||||
* default img URL. Used for delayed loading.
|
||||
*
|
||||
* @param {Array.<Array.<string>>} emoji Original emoji array.
|
||||
* @return {Array.<Array.<string>>} emoji array with all emoji pointing to the
|
||||
* default img.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker.prototype.getPlaceholderEmoji_ = function(emoji) {
|
||||
var placeholderEmoji = [];
|
||||
|
||||
for (var i = 0; i < emoji.length; i++) {
|
||||
placeholderEmoji.push([this.defaultImgUrl_, emoji[i][1]]);
|
||||
}
|
||||
|
||||
return placeholderEmoji;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Creates an emoji page using placeholder emoji pointing to the default
|
||||
* img instead of the real emoji. Used for delayed loading.
|
||||
*
|
||||
* @param {Array.<Array.<string>>} emoji Emoji for this page. See
|
||||
* {@link addEmojiGroup} for more details.
|
||||
* @return {goog.ui.emoji.EmojiPalette} the emoji page.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker.prototype.createPlaceholderEmojiPage_ =
|
||||
function(emoji) {
|
||||
var placeholderEmoji = this.getPlaceholderEmoji_(emoji);
|
||||
|
||||
var palette = new goog.ui.emoji.EmojiPalette(placeholderEmoji,
|
||||
null, // no url prefix
|
||||
this.renderer_,
|
||||
this.getDomHelper());
|
||||
palette.setSize(this.numCols_, this.numRows_);
|
||||
palette.setSupportedState(goog.ui.Component.State.FOCUSED, this.focusable_);
|
||||
palette.createDom();
|
||||
palette.setParent(this);
|
||||
|
||||
return palette;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* EmojiPickers cannot be used to decorate pre-existing html, since the
|
||||
* structure they build is fairly complicated.
|
||||
* @param {Element} element Element to decorate.
|
||||
* @return {boolean} Returns always false.
|
||||
* @override
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker.prototype.canDecorate = function(element) {
|
||||
return false;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @override
|
||||
* @suppress {deprecated} Using deprecated goog.ui.TabPane.
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker.prototype.enterDocument = function() {
|
||||
goog.ui.emoji.EmojiPicker.superClass_.enterDocument.call(this);
|
||||
|
||||
for (var i = 0; i < this.pages_.length; i++) {
|
||||
this.pages_[i].enterDocument();
|
||||
var pageElement = this.pages_[i].getElement();
|
||||
|
||||
// Add a new tab to the tabpane if there's more than one group of emoji.
|
||||
// If there is just one group of emoji, then we simply use the single
|
||||
// page's element as the content for the picker
|
||||
if (this.pages_.length > 1) {
|
||||
// Create a simple default title containg the page number if the title
|
||||
// was not provided in the emoji group params
|
||||
var title = this.emoji_[i].title || (i + 1);
|
||||
this.tabPane_.addPage(new goog.ui.TabPane.TabPage(
|
||||
pageElement, title, this.getDomHelper()));
|
||||
} else {
|
||||
this.getElement().appendChild(pageElement);
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize listeners. Note that we need to initialize this listener
|
||||
// after createDom, because addPage causes the goog.ui.TabPane.Events.CHANGE
|
||||
// event to fire, but we only want the handler (which loads delayed images)
|
||||
// to run after the picker has been constructed.
|
||||
if (this.tabPane_) {
|
||||
this.getHandler().listen(
|
||||
this.tabPane_, goog.ui.TabPane.Events.CHANGE, this.onPageChanged_);
|
||||
|
||||
// Make the tabpane unselectable so that changing tabs doesn't disturb the
|
||||
// cursor
|
||||
goog.style.setUnselectable(this.tabPane_.getElement(), true);
|
||||
}
|
||||
|
||||
this.getElement().unselectable = 'on';
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.emoji.EmojiPicker.prototype.exitDocument = function() {
|
||||
goog.ui.emoji.EmojiPicker.superClass_.exitDocument.call(this);
|
||||
for (var i = 0; i < this.pages_.length; i++) {
|
||||
this.pages_[i].exitDocument();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.emoji.EmojiPicker.prototype.disposeInternal = function() {
|
||||
goog.ui.emoji.EmojiPicker.superClass_.disposeInternal.call(this);
|
||||
|
||||
if (this.tabPane_) {
|
||||
this.tabPane_.dispose();
|
||||
this.tabPane_ = null;
|
||||
}
|
||||
|
||||
for (var i = 0; i < this.pages_.length; i++) {
|
||||
this.pages_[i].dispose();
|
||||
}
|
||||
this.pages_.length = 0;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {string} CSS class for the root element of EmojiPicker.
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker.prototype.getCssClass = function() {
|
||||
return goog.getCssName('goog-ui-emojipicker');
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the currently selected emoji from this picker. If the picker is
|
||||
* using the URL prefix optimization, allocates a new emoji object with the
|
||||
* full URL. This method is meant to be used by clients of the emojipicker,
|
||||
* e.g., in a listener on goog.ui.component.EventType.ACTION that wants to use
|
||||
* the just-selected emoji.
|
||||
*
|
||||
* @return {goog.ui.emoji.Emoji} The currently selected emoji from this picker.
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker.prototype.getSelectedEmoji = function() {
|
||||
return this.urlPrefix_ ?
|
||||
new goog.ui.emoji.Emoji(this.urlPrefix_ + this.selectedEmoji_.getId(),
|
||||
this.selectedEmoji_.getId()) :
|
||||
this.selectedEmoji_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the number of emoji groups in this picker.
|
||||
*
|
||||
* @return {number} The number of emoji groups in this picker.
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker.prototype.getNumEmojiGroups = function() {
|
||||
return this.emoji_.length;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns a page from the picker. This should be considered protected, and is
|
||||
* ONLY FOR TESTING.
|
||||
*
|
||||
* @param {number} index Index of the page to return.
|
||||
* @return {goog.ui.emoji.EmojiPalette?} the page at the specified index or null
|
||||
* if none exists.
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker.prototype.getPage = function(index) {
|
||||
return this.pages_[index];
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns all the pages from the picker. This should be considered protected,
|
||||
* and is ONLY FOR TESTING.
|
||||
*
|
||||
* @return {Array.<goog.ui.emoji.EmojiPalette>?} the pages in the picker or
|
||||
* null if none exist.
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker.prototype.getPages = function() {
|
||||
return this.pages_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the tabpane if this is a multipage picker. This should be considered
|
||||
* protected, and is ONLY FOR TESTING.
|
||||
*
|
||||
* @return {goog.ui.TabPane} the tabpane if it is a multipage picker,
|
||||
* or null if it does not exist or is a single page picker.
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker.prototype.getTabPane = function() {
|
||||
return this.tabPane_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {goog.ui.emoji.EmojiPalette} The active page of the emoji picker.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker.prototype.getActivePage_ = function() {
|
||||
return this.pages_[this.activePage_];
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handles actions from the EmojiPalettes that this picker contains.
|
||||
*
|
||||
* @param {goog.ui.Component.EventType} e The event object.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker.prototype.onEmojiPaletteAction_ = function(e) {
|
||||
this.selectedEmoji_ = this.getActivePage_().getSelectedEmoji();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handles changes in the active page in the tabpane.
|
||||
*
|
||||
* @param {goog.ui.TabPaneEvent} e The event object.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker.prototype.onPageChanged_ = function(e) {
|
||||
var index = /** @type {number} */ (e.page.getIndex());
|
||||
this.loadPage_(index);
|
||||
this.activePage_ = index;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Loads a page into the picker if it has not yet been loaded.
|
||||
*
|
||||
* @param {number} index Index of the page to load.
|
||||
* @private
|
||||
* @suppress {deprecated} Using deprecated goog.ui.TabPane.
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker.prototype.loadPage_ = function(index) {
|
||||
if (index < 0 || index > this.pages_.length) {
|
||||
throw Error('Index out of bounds');
|
||||
}
|
||||
|
||||
if (!this.pageLoadStatus_[index]) {
|
||||
var oldPage = this.pages_[index];
|
||||
this.pages_[index] = this.createEmojiPage_(this.emoji_[index].emoji,
|
||||
index);
|
||||
this.pages_[index].enterDocument();
|
||||
var pageElement = this.pages_[index].getElement();
|
||||
if (this.pages_.length > 1) {
|
||||
this.tabPane_.removePage(index);
|
||||
var title = this.emoji_[index].title || (index + 1);
|
||||
this.tabPane_.addPage(new goog.ui.TabPane.TabPage(
|
||||
pageElement, title, this.getDomHelper()), index);
|
||||
this.tabPane_.setSelectedIndex(index);
|
||||
} else {
|
||||
var el = this.getElement();
|
||||
el.appendChild(pageElement);
|
||||
}
|
||||
if (oldPage) {
|
||||
oldPage.dispose();
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,410 @@
|
||||
// Copyright 2007 The Closure Library Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS-IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
/**
|
||||
* @fileoverview Popup Emoji Picker implementation. This provides a UI widget
|
||||
* for choosing an emoji from a grid of possible choices. The widget is a popup,
|
||||
* so it is suitable for a toolbar, for instance the TrogEdit toolbar.
|
||||
*
|
||||
* @see ../demos/popupemojipicker.html for an example of how to instantiate
|
||||
* an emoji picker.
|
||||
*
|
||||
* See goog.ui.emoji.EmojiPicker in emojipicker.js for more details.
|
||||
*
|
||||
* Based on goog.ui.PopupColorPicker (popupcolorpicker.js).
|
||||
*
|
||||
* @see ../../demos/popupemojipicker.html
|
||||
*/
|
||||
|
||||
goog.provide('goog.ui.emoji.PopupEmojiPicker');
|
||||
|
||||
goog.require('goog.events.EventType');
|
||||
goog.require('goog.positioning.AnchoredPosition');
|
||||
goog.require('goog.positioning.Corner');
|
||||
goog.require('goog.ui.Component');
|
||||
goog.require('goog.ui.Popup');
|
||||
goog.require('goog.ui.emoji.EmojiPicker');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a popup emoji picker widget.
|
||||
*
|
||||
* @param {string} defaultImgUrl Url of the img that should be used to fill up
|
||||
* the cells in the emoji table, to prevent jittering. Should be the same
|
||||
* size as the emoji.
|
||||
* @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper.
|
||||
* @extends {goog.ui.Component}
|
||||
* @constructor
|
||||
*/
|
||||
goog.ui.emoji.PopupEmojiPicker =
|
||||
function(defaultImgUrl, opt_domHelper) {
|
||||
goog.ui.Component.call(this, opt_domHelper);
|
||||
|
||||
this.emojiPicker_ = new goog.ui.emoji.EmojiPicker(defaultImgUrl,
|
||||
opt_domHelper);
|
||||
this.addChild(this.emojiPicker_);
|
||||
|
||||
this.getHandler().listen(this.emojiPicker_,
|
||||
goog.ui.Component.EventType.ACTION, this.onEmojiPicked_);
|
||||
};
|
||||
goog.inherits(goog.ui.emoji.PopupEmojiPicker, goog.ui.Component);
|
||||
|
||||
|
||||
/**
|
||||
* Instance of an emoji picker control.
|
||||
* @type {goog.ui.emoji.EmojiPicker}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.emoji.PopupEmojiPicker.prototype.emojiPicker_ = null;
|
||||
|
||||
|
||||
/**
|
||||
* Instance of goog.ui.Popup used to manage the behavior of the emoji picker.
|
||||
* @type {goog.ui.Popup}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.emoji.PopupEmojiPicker.prototype.popup_ = null;
|
||||
|
||||
|
||||
/**
|
||||
* Reference to the element that triggered the last popup.
|
||||
* @type {Element}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.emoji.PopupEmojiPicker.prototype.lastTarget_ = null;
|
||||
|
||||
|
||||
/**
|
||||
* Whether the emoji picker can accept focus.
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.emoji.PopupEmojiPicker.prototype.focusable_ = true;
|
||||
|
||||
|
||||
/**
|
||||
* If true, then the emojipicker will toggle off if it is already visible.
|
||||
* Default is true.
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.emoji.PopupEmojiPicker.prototype.toggleMode_ = true;
|
||||
|
||||
|
||||
/**
|
||||
* Adds a group of emoji to the picker.
|
||||
*
|
||||
* @param {string|Element} title Title for the group.
|
||||
* @param {Array.<Array>} emojiGroup A new group of emoji to be added. Each
|
||||
* internal array contains [emojiUrl, emojiId].
|
||||
*/
|
||||
goog.ui.emoji.PopupEmojiPicker.prototype.addEmojiGroup =
|
||||
function(title, emojiGroup) {
|
||||
this.emojiPicker_.addEmojiGroup(title, emojiGroup);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets whether the emoji picker should toggle if it is already open.
|
||||
* @param {boolean} toggle The toggle mode to use.
|
||||
*/
|
||||
goog.ui.emoji.PopupEmojiPicker.prototype.setToggleMode = function(toggle) {
|
||||
this.toggleMode_ = toggle;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets whether the emojipicker is in toggle mode
|
||||
* @return {boolean} toggle.
|
||||
*/
|
||||
goog.ui.emoji.PopupEmojiPicker.prototype.getToggleMode = function() {
|
||||
return this.toggleMode_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets whether loading of images should be delayed until after dom creation.
|
||||
* Thus, this function must be called before {@link #createDom}. If set to true,
|
||||
* the client must call {@link #loadImages} when they wish the images to be
|
||||
* loaded.
|
||||
*
|
||||
* @param {boolean} shouldDelay Whether to delay loading the images.
|
||||
*/
|
||||
goog.ui.emoji.PopupEmojiPicker.prototype.setDelayedLoad =
|
||||
function(shouldDelay) {
|
||||
if (this.emojiPicker_) {
|
||||
this.emojiPicker_.setDelayedLoad(shouldDelay);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets whether the emoji picker can accept focus.
|
||||
* @param {boolean} focusable Whether the emoji picker should accept focus.
|
||||
*/
|
||||
goog.ui.emoji.PopupEmojiPicker.prototype.setFocusable = function(focusable) {
|
||||
this.focusable_ = focusable;
|
||||
if (this.emojiPicker_) {
|
||||
// TODO(user): In next revision sort the behavior of passing state to
|
||||
// children correctly
|
||||
this.emojiPicker_.setFocusable(focusable);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the URL prefix for the emoji URLs.
|
||||
*
|
||||
* @param {string} urlPrefix Prefix that should be prepended to all URLs.
|
||||
*/
|
||||
goog.ui.emoji.PopupEmojiPicker.prototype.setUrlPrefix = function(urlPrefix) {
|
||||
this.emojiPicker_.setUrlPrefix(urlPrefix);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the location of the tabs in relation to the emoji grids. This should
|
||||
* only be called before the picker has been rendered.
|
||||
*
|
||||
* @param {goog.ui.TabPane.TabLocation} tabLocation The location of the tabs.
|
||||
*/
|
||||
goog.ui.emoji.PopupEmojiPicker.prototype.setTabLocation =
|
||||
function(tabLocation) {
|
||||
this.emojiPicker_.setTabLocation(tabLocation);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the number of rows per grid in the emoji picker. This should only be
|
||||
* called before the picker has been rendered.
|
||||
*
|
||||
* @param {number} numRows Number of rows per grid.
|
||||
*/
|
||||
goog.ui.emoji.PopupEmojiPicker.prototype.setNumRows = function(numRows) {
|
||||
this.emojiPicker_.setNumRows(numRows);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the number of columns per grid in the emoji picker. This should only be
|
||||
* called before the picker has been rendered.
|
||||
*
|
||||
* @param {number} numCols Number of columns per grid.
|
||||
*/
|
||||
goog.ui.emoji.PopupEmojiPicker.prototype.setNumColumns = function(numCols) {
|
||||
this.emojiPicker_.setNumColumns(numCols);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the progressive rendering aspect of this emojipicker. Must be called
|
||||
* before createDom to have an effect.
|
||||
*
|
||||
* @param {boolean} progressive Whether the picker should render progressively.
|
||||
*/
|
||||
goog.ui.emoji.PopupEmojiPicker.prototype.setProgressiveRender =
|
||||
function(progressive) {
|
||||
if (this.emojiPicker_) {
|
||||
this.emojiPicker_.setProgressiveRender(progressive);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the number of emoji groups in this picker.
|
||||
*
|
||||
* @return {number} The number of emoji groups in this picker.
|
||||
*/
|
||||
goog.ui.emoji.PopupEmojiPicker.prototype.getNumEmojiGroups = function() {
|
||||
return this.emojiPicker_.getNumEmojiGroups();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Causes the emoji imgs to be loaded into the picker. Used for delayed loading.
|
||||
*/
|
||||
goog.ui.emoji.PopupEmojiPicker.prototype.loadImages = function() {
|
||||
if (this.emojiPicker_) {
|
||||
this.emojiPicker_.loadImages();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.emoji.PopupEmojiPicker.prototype.createDom = function() {
|
||||
goog.ui.emoji.PopupEmojiPicker.superClass_.createDom.call(this);
|
||||
|
||||
this.emojiPicker_.createDom();
|
||||
|
||||
this.getElement().className = goog.getCssName('goog-ui-popupemojipicker');
|
||||
this.getElement().appendChild(this.emojiPicker_.getElement());
|
||||
|
||||
this.popup_ = new goog.ui.Popup(this.getElement());
|
||||
this.getElement().unselectable = 'on';
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.emoji.PopupEmojiPicker.prototype.disposeInternal = function() {
|
||||
goog.ui.emoji.PopupEmojiPicker.superClass_.disposeInternal.call(this);
|
||||
this.emojiPicker_ = null;
|
||||
this.lastTarget_ = null;
|
||||
if (this.popup_) {
|
||||
this.popup_.dispose();
|
||||
this.popup_ = null;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Attaches the popup emoji picker to an element.
|
||||
*
|
||||
* @param {Element} element The element to attach to.
|
||||
*/
|
||||
goog.ui.emoji.PopupEmojiPicker.prototype.attach = function(element) {
|
||||
// TODO(user): standardize event type, popups should use MOUSEDOWN, but
|
||||
// currently apps are using click.
|
||||
this.getHandler().listen(element, goog.events.EventType.CLICK, this.show_);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Detatches the popup emoji picker from an element.
|
||||
*
|
||||
* @param {Element} element The element to detach from.
|
||||
*/
|
||||
goog.ui.emoji.PopupEmojiPicker.prototype.detach = function(element) {
|
||||
this.getHandler().unlisten(element, goog.events.EventType.CLICK, this.show_);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {goog.ui.emoji.EmojiPicker} The emoji picker instance.
|
||||
*/
|
||||
goog.ui.emoji.PopupEmojiPicker.prototype.getEmojiPicker = function() {
|
||||
return this.emojiPicker_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether the Popup dismisses itself when the user clicks outside of
|
||||
* it.
|
||||
* @return {boolean} Whether the Popup autohides on an external click.
|
||||
*/
|
||||
goog.ui.emoji.PopupEmojiPicker.prototype.getAutoHide = function() {
|
||||
return !!this.popup_ && this.popup_.getAutoHide();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets whether the Popup dismisses itself when the user clicks outside of it -
|
||||
* must be called after the Popup has been created (in createDom()),
|
||||
* otherwise it does nothing.
|
||||
*
|
||||
* @param {boolean} autoHide Whether to autohide on an external click.
|
||||
*/
|
||||
goog.ui.emoji.PopupEmojiPicker.prototype.setAutoHide = function(autoHide) {
|
||||
if (this.popup_) {
|
||||
this.popup_.setAutoHide(autoHide);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the region inside which the Popup dismisses itself when the user
|
||||
* clicks, or null if it was not set. Null indicates the entire document is
|
||||
* the autohide region.
|
||||
* @return {Element} The DOM element for autohide, or null if it hasn't been
|
||||
* set.
|
||||
*/
|
||||
goog.ui.emoji.PopupEmojiPicker.prototype.getAutoHideRegion = function() {
|
||||
return this.popup_ && this.popup_.getAutoHideRegion();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the region inside which the Popup dismisses itself when the user
|
||||
* clicks - must be called after the Popup has been created (in createDom()),
|
||||
* otherwise it does nothing.
|
||||
*
|
||||
* @param {Element} element The DOM element for autohide.
|
||||
*/
|
||||
goog.ui.emoji.PopupEmojiPicker.prototype.setAutoHideRegion = function(element) {
|
||||
if (this.popup_) {
|
||||
this.popup_.setAutoHideRegion(element);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the {@link goog.ui.PopupBase} from this picker. Returns null if the
|
||||
* popup has not yet been created.
|
||||
*
|
||||
* NOTE: This should *ONLY* be called from tests. If called before createDom(),
|
||||
* this should return null.
|
||||
*
|
||||
* @return {goog.ui.PopupBase?} The popup, or null if it hasn't been created.
|
||||
*/
|
||||
goog.ui.emoji.PopupEmojiPicker.prototype.getPopup = function() {
|
||||
return this.popup_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {Element} The last element that triggered the popup.
|
||||
*/
|
||||
goog.ui.emoji.PopupEmojiPicker.prototype.getLastTarget = function() {
|
||||
return this.lastTarget_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {goog.ui.emoji.Emoji} The currently selected emoji.
|
||||
*/
|
||||
goog.ui.emoji.PopupEmojiPicker.prototype.getSelectedEmoji = function() {
|
||||
return this.emojiPicker_.getSelectedEmoji();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handles click events on the element this picker is attached to and shows the
|
||||
* emoji picker in a popup.
|
||||
*
|
||||
* @param {goog.events.BrowserEvent} e The browser event.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.emoji.PopupEmojiPicker.prototype.show_ = function(e) {
|
||||
if (this.popup_.isOrWasRecentlyVisible() && this.toggleMode_ &&
|
||||
this.lastTarget_ == e.currentTarget) {
|
||||
this.popup_.setVisible(false);
|
||||
return;
|
||||
}
|
||||
|
||||
this.lastTarget_ = /** @type {Element} */ (e.currentTarget);
|
||||
this.popup_.setPosition(new goog.positioning.AnchoredPosition(
|
||||
this.lastTarget_, goog.positioning.Corner.BOTTOM_LEFT));
|
||||
this.popup_.setVisible(true);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handles selection of an emoji.
|
||||
*
|
||||
* @param {goog.events.Event} e The event object.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.emoji.PopupEmojiPicker.prototype.onEmojiPicked_ = function(e) {
|
||||
this.popup_.setVisible(false);
|
||||
};
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
// Copyright 2008 The Closure Library Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS-IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
/**
|
||||
* @fileoverview Progressive Emoji Palette renderer implementation.
|
||||
*
|
||||
*/
|
||||
|
||||
goog.provide('goog.ui.emoji.ProgressiveEmojiPaletteRenderer');
|
||||
|
||||
goog.require('goog.style');
|
||||
goog.require('goog.ui.emoji.EmojiPaletteRenderer');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Progressively renders an emoji palette. The progressive renderer tries to
|
||||
* use img tags instead of background-image for sprited emoji, since most
|
||||
* browsers render img tags progressively (i.e., as the data comes in), while
|
||||
* only very new browsers render background-image progressively.
|
||||
*
|
||||
* @param {string} defaultImgUrl Url of the img that should be used to fill up
|
||||
* the cells in the emoji table, to prevent jittering. Will be stretched
|
||||
* to the emoji cell size. A good image is a transparent dot.
|
||||
* @constructor
|
||||
* @extends {goog.ui.emoji.EmojiPaletteRenderer}
|
||||
*/
|
||||
goog.ui.emoji.ProgressiveEmojiPaletteRenderer = function(defaultImgUrl) {
|
||||
goog.ui.emoji.EmojiPaletteRenderer.call(this, defaultImgUrl);
|
||||
};
|
||||
goog.inherits(goog.ui.emoji.ProgressiveEmojiPaletteRenderer,
|
||||
goog.ui.emoji.EmojiPaletteRenderer);
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.emoji.ProgressiveEmojiPaletteRenderer.prototype.
|
||||
buildElementFromSpriteMetadata = function(dom, spriteInfo, displayUrl) {
|
||||
var width = spriteInfo.getWidthCssValue();
|
||||
var height = spriteInfo.getHeightCssValue();
|
||||
var x = spriteInfo.getXOffsetCssValue();
|
||||
var y = spriteInfo.getYOffsetCssValue();
|
||||
// Need this extra div for proper vertical centering.
|
||||
var inner = dom.createDom('img', {'src': displayUrl});
|
||||
var el = /** @type {HTMLDivElement} */ (dom.createDom('div',
|
||||
goog.getCssName('goog-palette-cell-extra'), inner));
|
||||
goog.style.setStyle(el, {
|
||||
'width': width,
|
||||
'height': height,
|
||||
'overflow': 'hidden',
|
||||
'position': 'relative'
|
||||
});
|
||||
goog.style.setStyle(inner, {
|
||||
'left': x,
|
||||
'top': y,
|
||||
'position': 'absolute'
|
||||
});
|
||||
|
||||
return el;
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.emoji.ProgressiveEmojiPaletteRenderer.prototype.
|
||||
updateAnimatedPaletteItem = function(item, animatedImg) {
|
||||
// Just to be safe, we check for the existence of the img element within this
|
||||
// palette item before attempting to modify it.
|
||||
var img;
|
||||
var el = item.firstChild;
|
||||
while (el) {
|
||||
if ('IMG' == el.tagName) {
|
||||
img = /** @type {Element} */ (el);
|
||||
break;
|
||||
}
|
||||
el = el.firstChild;
|
||||
}
|
||||
if (!el) {
|
||||
return;
|
||||
}
|
||||
|
||||
img.width = animatedImg.width;
|
||||
img.height = animatedImg.height;
|
||||
goog.style.setStyle(img, {
|
||||
'left': 0,
|
||||
'top': 0
|
||||
});
|
||||
img.src = animatedImg.src;
|
||||
};
|
||||
@@ -0,0 +1,212 @@
|
||||
// Copyright 2008 The Closure Library Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS-IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
/**
|
||||
* @fileoverview SpriteInfo implementation. This is a simple wrapper class to
|
||||
* hold CSS metadata needed for sprited emoji.
|
||||
*
|
||||
* @see ../demos/popupemojipicker.html or emojipicker_test.html for examples
|
||||
* of how to use this class.
|
||||
*
|
||||
*/
|
||||
goog.provide('goog.ui.emoji.SpriteInfo');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Creates a SpriteInfo object with the specified properties. If the image is
|
||||
* sprited via CSS, then only the first parameter needs a value. If the image
|
||||
* is sprited via metadata, then the first parameter should be left null.
|
||||
*
|
||||
* @param {?string} cssClass CSS class to properly display the sprited image.
|
||||
* @param {string=} opt_url Url of the sprite image.
|
||||
* @param {number=} opt_width Width of the image being sprited.
|
||||
* @param {number=} opt_height Height of the image being sprited.
|
||||
* @param {number=} opt_xOffset Positive x offset of the image being sprited
|
||||
* within the sprite.
|
||||
* @param {number=} opt_yOffset Positive y offset of the image being sprited
|
||||
* within the sprite.
|
||||
* @param {boolean=} opt_animated Whether the sprite is animated.
|
||||
* @constructor
|
||||
*/
|
||||
goog.ui.emoji.SpriteInfo = function(cssClass, opt_url, opt_width, opt_height,
|
||||
opt_xOffset, opt_yOffset, opt_animated) {
|
||||
if (cssClass != null) {
|
||||
this.cssClass_ = cssClass;
|
||||
} else {
|
||||
if (opt_url == undefined || opt_width === undefined ||
|
||||
opt_height === undefined || opt_xOffset == undefined ||
|
||||
opt_yOffset === undefined) {
|
||||
throw Error('Sprite info is not fully specified');
|
||||
}
|
||||
|
||||
this.url_ = opt_url;
|
||||
this.width_ = opt_width;
|
||||
this.height_ = opt_height;
|
||||
this.xOffset_ = opt_xOffset;
|
||||
this.yOffset_ = opt_yOffset;
|
||||
}
|
||||
|
||||
this.animated_ = !!opt_animated;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Name of the CSS class to properly display the sprited image.
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.emoji.SpriteInfo.prototype.cssClass_;
|
||||
|
||||
|
||||
/**
|
||||
* Url of the sprite image.
|
||||
* @type {string|undefined}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.emoji.SpriteInfo.prototype.url_;
|
||||
|
||||
|
||||
/**
|
||||
* Width of the image being sprited.
|
||||
* @type {number|undefined}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.emoji.SpriteInfo.prototype.width_;
|
||||
|
||||
|
||||
/**
|
||||
* Height of the image being sprited.
|
||||
* @type {number|undefined}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.emoji.SpriteInfo.prototype.height_;
|
||||
|
||||
|
||||
/**
|
||||
* Positive x offset of the image being sprited within the sprite.
|
||||
* @type {number|undefined}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.emoji.SpriteInfo.prototype.xOffset_;
|
||||
|
||||
|
||||
/**
|
||||
* Positive y offset of the image being sprited within the sprite.
|
||||
* @type {number|undefined}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.emoji.SpriteInfo.prototype.yOffset_;
|
||||
|
||||
|
||||
/**
|
||||
* Whether the emoji specified by the sprite is animated.
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.emoji.SpriteInfo.prototype.animated_;
|
||||
|
||||
|
||||
/**
|
||||
* Returns the css class of the sprited image.
|
||||
* @return {?string} Name of the CSS class to properly display the sprited
|
||||
* image.
|
||||
*/
|
||||
goog.ui.emoji.SpriteInfo.prototype.getCssClass = function() {
|
||||
return this.cssClass_ || null;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the url of the sprite image.
|
||||
* @return {?string} Url of the sprite image.
|
||||
*/
|
||||
goog.ui.emoji.SpriteInfo.prototype.getUrl = function() {
|
||||
return this.url_ || null;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether the emoji specified by this sprite is animated.
|
||||
* @return {boolean} Whether the emoji is animated.
|
||||
*/
|
||||
goog.ui.emoji.SpriteInfo.prototype.isAnimated = function() {
|
||||
return this.animated_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the width of the image being sprited, appropriate for a CSS value.
|
||||
* @return {string} The width of the image being sprited.
|
||||
*/
|
||||
goog.ui.emoji.SpriteInfo.prototype.getWidthCssValue = function() {
|
||||
return goog.ui.emoji.SpriteInfo.getCssPixelValue_(this.width_);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the height of the image being sprited, appropriate for a CSS value.
|
||||
* @return {string} The height of the image being sprited.
|
||||
*/
|
||||
goog.ui.emoji.SpriteInfo.prototype.getHeightCssValue = function() {
|
||||
return goog.ui.emoji.SpriteInfo.getCssPixelValue_(this.height_);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the x offset of the image being sprited within the sprite,
|
||||
* appropriate for a CSS value.
|
||||
* @return {string} The x offset of the image being sprited within the sprite.
|
||||
*/
|
||||
goog.ui.emoji.SpriteInfo.prototype.getXOffsetCssValue = function() {
|
||||
return goog.ui.emoji.SpriteInfo.getOffsetCssValue_(this.xOffset_);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the positive y offset of the image being sprited within the sprite,
|
||||
* appropriate for a CSS value.
|
||||
* @return {string} The y offset of the image being sprited within the sprite.
|
||||
*/
|
||||
goog.ui.emoji.SpriteInfo.prototype.getYOffsetCssValue = function() {
|
||||
return goog.ui.emoji.SpriteInfo.getOffsetCssValue_(this.yOffset_);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns a string appropriate for use as a CSS value. If the value is zero,
|
||||
* then there is no unit appended.
|
||||
*
|
||||
* @param {number|undefined} value A number to be turned into a
|
||||
* CSS size/location value.
|
||||
* @return {string} A string appropriate for use as a CSS value.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.emoji.SpriteInfo.getCssPixelValue_ = function(value) {
|
||||
return !value ? '0' : value + 'px';
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns a string appropriate for use as a CSS value for a position offset,
|
||||
* such as the position argument for sprites.
|
||||
*
|
||||
* @param {number|undefined} posOffset A positive offset for a position.
|
||||
* @return {string} A string appropriate for use as a CSS value.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.emoji.SpriteInfo.getOffsetCssValue_ = function(posOffset) {
|
||||
var offset = goog.ui.emoji.SpriteInfo.getCssPixelValue_(posOffset);
|
||||
return offset == '0' ? offset : '-' + offset;
|
||||
};
|
||||
Reference in New Issue
Block a user