From 01092075acc154cec350da4a71925e644cb28bc2 Mon Sep 17 00:00:00 2001 From: lilia Date: Tue, 10 Mar 2015 17:04:10 -0700 Subject: [PATCH] Add js-emoji as a component --- bower.json | 9 +- components/emojijs/emoji.js | 1248 ++++++++++++++++++++++++++++++++++ js/components.js | 1250 ++++++++++++++++++++++++++++++++++- 3 files changed, 2504 insertions(+), 3 deletions(-) create mode 100644 components/emojijs/emoji.js diff --git a/bower.json b/bower.json index 06f2a69f..1e638738 100644 --- a/bower.json +++ b/bower.json @@ -21,7 +21,8 @@ "backbone.typeahead": "mojotech/backbone.typeahead", "blueimp-load-image": "~1.13.0", "blueimp-canvas-to-blob": "~2.1.1", - "twemoji": "~1.2.1" + "twemoji": "~1.2.1", + "emojijs": "iamcal/js-emoji" }, "devDependencies": { "mocha": "~2.0.1", @@ -103,6 +104,9 @@ "72x72/*", "36x36/*", "26x26/*" + ], + "emojijs": [ + "emoji.js" ] }, "concat": { @@ -122,7 +126,8 @@ "backbone.typeahead", "blueimp-load-image", "blueimp-canvas-to-blob", - "twemoji" + "twemoji", + "emojijs" ], "libtextsecure": [ "jquery", diff --git a/components/emojijs/emoji.js b/components/emojijs/emoji.js new file mode 100644 index 00000000..caecbd76 --- /dev/null +++ b/components/emojijs/emoji.js @@ -0,0 +1,1248 @@ +;(function() { + +/** + * @global + * @namespace + */ +function emoji(){} + /** + * The set of images to use got graphical emoji. + * + * @memberof emoji + * @type {string} + */ + emoji.img_set = 'apple'; + + /** + * Configuration details for different image sets. This includes a path to a directory containing the + * individual images (`path`( and a URL to sprite sheets (`sheet`). All of these images can be found + * in the [emoji-data repository]{@link https://github.com/iamcal/emoji-data}. Using a CDN for these + * is not a bad idea. + * + * @memberof emoji + * @type { + */ + emoji.img_sets = { + 'apple' : {'path' : '/emoji-data/img-apple-64/' , 'sheet' : '/emoji-data/sheet_apple_64.png' }, + 'google' : {'path' : '/emoji-data/img-google-64/' , 'sheet' : '/emoji-data/sheet_google_64.png' }, + 'twitter' : {'path' : '/emoji-data/img-twitter-64/' , 'sheet' : '/emoji-data/sheet_twitter_64.png' }, + 'emojione' : {'path' : '/emoji-data/img-emojione-64/', 'sheet' : '/emoji-data/sheet_emojione_64.png' } + }; + + /** + * Use a CSS class instead of specifying a sprite or background image for + * the span representing the emoticon. This requires a CSS sheet with + * emoticon data-uris. + * + * @memberof emoji + * @type bool + * @todo document how to build the CSS stylesheet this requires. + */ + emoji.use_css_imgs = false; + + /** + * Instead of replacing emoticons with the appropriate representations, + * replace them with their colon string representation. + * @memberof emoji + * @type bool + */ + emoji.colons_mode = false; + emoji.text_mode = false; + + /** + * If true, sets the "title" property on the span or image that gets + * inserted for the emoticon. + * @memberof emoji + * @type bool + */ + emoji.include_title = false; + + /** + * If the platform supports native emoticons, use those instead + * of the fallbacks. + * @memberof emoji + * @type bool + */ + emoji.allow_native = true; + + /** + * Set to true to use CSS sprites instead of individual images on + * platforms that support it. + * + * @memberof emoji + * @type bool + */ + emoji.use_sheet = false; + + // Keeps track of what has been initialized. + /** @private */ + emoji.inits = {}; + emoji.map = {}; + + /** + * @memberof emoji + * @param {string} str A string potentially containing ascii emoticons + * (ie. `:)`) + * + * @returns {string} A new string with all emoticons in `str` + * replaced by a representatation that's supported by the current + * environtment. + */ + emoji.replace_emoticons = function(str){ + emoji.init_emoticons(); + return str.replace(emoji.rx_emoticons, function(m, $1, $2){ + var val = emoji.map.emoticons[$2]; + return val ? $1+emoji.replacement(val, $2) : m; + }); + }; + + /** + * @memberof emoji + * @param {string} str A string potentially containing ascii emoticons + * (ie. `:)`) + * + * @returns {string} A new string with all emoticons in `str` + * replaced by their colon string representations (ie. `:smile:`) + */ + emoji.replace_emoticons_with_colons = function(str){ + emoji.init_emoticons(); + return str.replace(emoji.rx_emoticons, function(m, $1, $2){ + var val = emoji.data[emoji.map.emoticons[$2]][3][0]; + return val ? $1+':'+val+':' : m; + }); + }; + + /** + * @memberof emoji + * @param {string} str A string potentially containing colon string + * representations of emoticons (ie. `:smile:`) + * + * @returns {string} A new string with all colon string emoticons replaced + * with the appropriate representation. + */ + emoji.replace_colons = function(str){ + emoji.init_colons(); + return str.replace(emoji.rx_colons, function(m){ + var idx = m.substr(1, m.length-2); + var val = emoji.map.colons[idx]; + return val ? emoji.replacement(val, idx, ':') : m; + }); + }; + + /** + * @memberof emoji + * @param {string} str A string potentially containing unified unicode + * emoticons. (ie. 😄) + * + * @returns {string} A new string with all unicode emoticons replaced with + * the appropriate representation for the current environment. + */ + emoji.replace_unified = function(str){ + emoji.init_unified(); + return str.replace(emoji.rx_unified, function(m){ + var val = emoji.map.unified[m]; + return val ? emoji.replacement(val) : m; + }); + }; + + // Does the actual replacement of a character with the appropriate + /** @private */ + emoji.replacement = function(idx, actual, wrapper){ + wrapper = wrapper || ''; + if (emoji.colons_mode) return ':'+emoji.data[idx][3][0]+':'; + var text_name = (actual) ? wrapper+actual+wrapper : emoji.data[idx][8] || wrapper+emoji.data[idx][3][0]+wrapper; + if (emoji.text_mode) return text_name; + emoji.init_env(); + if (emoji.replace_mode == 'unified' && emoji.allow_native && emoji.data[idx][0][0]) return emoji.data[idx][0][0]; + if (emoji.replace_mode == 'softbank' && emoji.allow_native && emoji.data[idx][1]) return emoji.data[idx][1]; + if (emoji.replace_mode == 'google' && emoji.allow_native && emoji.data[idx][2]) return emoji.data[idx][2]; + var img = emoji.data[idx][9] || emoji.img_sets[emoji.img_set].path+idx+'.png'; + var title = emoji.include_title ? ' title="'+(actual || emoji.data[idx][3][0])+'"' : ''; + var text = emoji.include_text ? wrapper+(actual || emoji.data[idx][3][0])+wrapper : ''; + if (emoji.supports_css) { + var px = emoji.data[idx][4]; + var py = emoji.data[idx][5]; + if (emoji.use_sheet && px != null && py != null){ + var mul = 100 / (emoji.sheet_size - 1); + var style = 'background: url('+emoji.img_sets[emoji.img_set].sheet+');background-position:'+(mul*px)+'% '+(mul*py)+'%;background-size:'+emoji.sheet_size+'00%'; + return ''+text+''; + }else if (emoji.use_css_imgs){ + return ''+text+''; + }else{ + return ''+text+''; + } + } + return ''; + }; + + // Initializes the text emoticon data + /** @private */ + emoji.init_emoticons = function(){ + if (emoji.inits.emoticons) return; + emoji.init_colons(); // we require this for the emoticons map + emoji.inits.emoticons = 1; + + var a = []; + emoji.map.emoticons = {}; + for (var i in emoji.emoticons_data){ + // because we never see some characters in our text except as entities, we must do some replacing + var emoticon = i.replace(/\&/g, '&').replace(/\/g, '>'); + + if (!emoji.map.colons[emoji.emoticons_data[i]]) continue; + + emoji.map.emoticons[emoticon] = emoji.map.colons[emoji.emoticons_data[i]]; + a.push(emoji.escape_rx(emoticon)); + } + emoji.rx_emoticons = new RegExp(('(^|\\s)('+a.join('|')+')(?=$|[\\s|\\?\\.,!])'), 'g'); + }; + + // Initializes the colon string data + /** @private */ + emoji.init_colons = function(){ + if (emoji.inits.colons) return; + emoji.inits.colons = 1; + emoji.rx_colons = new RegExp('\:[a-zA-Z0-9-_+]+\:', 'g'); + emoji.map.colons = {}; + for (var i in emoji.data){ + for (var j=0; j":"laughing", + ":->":"laughing", + ";)":"wink", + ";-)":"wink", + ":)":"blush", + "(:":"blush", + ":-)":"blush", + "8)":"sunglasses", + ":|":"neutral_face", + ":-|":"neutral_face", + ":\\\\":"confused", + ":-\\\\":"confused", + ":\/":"confused", + ":-\/":"confused", + ":p":"stuck_out_tongue", + ":-p":"stuck_out_tongue", + ":P":"stuck_out_tongue", + ":-P":"stuck_out_tongue", + ":b":"stuck_out_tongue", + ":-b":"stuck_out_tongue", + ";p":"stuck_out_tongue_winking_eye", + ";-p":"stuck_out_tongue_winking_eye", + ";b":"stuck_out_tongue_winking_eye", + ";-b":"stuck_out_tongue_winking_eye", + ";P":"stuck_out_tongue_winking_eye", + ";-P":"stuck_out_tongue_winking_eye", + "):":"disappointed", + ":(":"disappointed", + ":-(":"disappointed", + ">:(":"angry", + ">:-(":"angry", + ":'(":"cry", + "D:":"anguished", + ":o":"open_mouth", + ":-o":"open_mouth" + }; + + if (typeof exports === 'object'){ + module.exports = emoji; + }else if (typeof define === 'function' && define.amd){ + define(function() { return emoji; }); + }else{ + this.emoji = emoji; + } + +}).call(function(){ + return this || (typeof window !== 'undefined' ? window : global); +}()); diff --git a/js/components.js b/js/components.js index badeb1a5..acd1c22f 100644 --- a/js/components.js +++ b/js/components.js @@ -28417,4 +28417,1252 @@ var twemoji = (function ( return r.join(sep || '-'); } -}()); \ No newline at end of file +}()); +;(function() { + +/** + * @global + * @namespace + */ +function emoji(){} + /** + * The set of images to use got graphical emoji. + * + * @memberof emoji + * @type {string} + */ + emoji.img_set = 'apple'; + + /** + * Configuration details for different image sets. This includes a path to a directory containing the + * individual images (`path`( and a URL to sprite sheets (`sheet`). All of these images can be found + * in the [emoji-data repository]{@link https://github.com/iamcal/emoji-data}. Using a CDN for these + * is not a bad idea. + * + * @memberof emoji + * @type { + */ + emoji.img_sets = { + 'apple' : {'path' : '/emoji-data/img-apple-64/' , 'sheet' : '/emoji-data/sheet_apple_64.png' }, + 'google' : {'path' : '/emoji-data/img-google-64/' , 'sheet' : '/emoji-data/sheet_google_64.png' }, + 'twitter' : {'path' : '/emoji-data/img-twitter-64/' , 'sheet' : '/emoji-data/sheet_twitter_64.png' }, + 'emojione' : {'path' : '/emoji-data/img-emojione-64/', 'sheet' : '/emoji-data/sheet_emojione_64.png' } + }; + + /** + * Use a CSS class instead of specifying a sprite or background image for + * the span representing the emoticon. This requires a CSS sheet with + * emoticon data-uris. + * + * @memberof emoji + * @type bool + * @todo document how to build the CSS stylesheet this requires. + */ + emoji.use_css_imgs = false; + + /** + * Instead of replacing emoticons with the appropriate representations, + * replace them with their colon string representation. + * @memberof emoji + * @type bool + */ + emoji.colons_mode = false; + emoji.text_mode = false; + + /** + * If true, sets the "title" property on the span or image that gets + * inserted for the emoticon. + * @memberof emoji + * @type bool + */ + emoji.include_title = false; + + /** + * If the platform supports native emoticons, use those instead + * of the fallbacks. + * @memberof emoji + * @type bool + */ + emoji.allow_native = true; + + /** + * Set to true to use CSS sprites instead of individual images on + * platforms that support it. + * + * @memberof emoji + * @type bool + */ + emoji.use_sheet = false; + + // Keeps track of what has been initialized. + /** @private */ + emoji.inits = {}; + emoji.map = {}; + + /** + * @memberof emoji + * @param {string} str A string potentially containing ascii emoticons + * (ie. `:)`) + * + * @returns {string} A new string with all emoticons in `str` + * replaced by a representatation that's supported by the current + * environtment. + */ + emoji.replace_emoticons = function(str){ + emoji.init_emoticons(); + return str.replace(emoji.rx_emoticons, function(m, $1, $2){ + var val = emoji.map.emoticons[$2]; + return val ? $1+emoji.replacement(val, $2) : m; + }); + }; + + /** + * @memberof emoji + * @param {string} str A string potentially containing ascii emoticons + * (ie. `:)`) + * + * @returns {string} A new string with all emoticons in `str` + * replaced by their colon string representations (ie. `:smile:`) + */ + emoji.replace_emoticons_with_colons = function(str){ + emoji.init_emoticons(); + return str.replace(emoji.rx_emoticons, function(m, $1, $2){ + var val = emoji.data[emoji.map.emoticons[$2]][3][0]; + return val ? $1+':'+val+':' : m; + }); + }; + + /** + * @memberof emoji + * @param {string} str A string potentially containing colon string + * representations of emoticons (ie. `:smile:`) + * + * @returns {string} A new string with all colon string emoticons replaced + * with the appropriate representation. + */ + emoji.replace_colons = function(str){ + emoji.init_colons(); + return str.replace(emoji.rx_colons, function(m){ + var idx = m.substr(1, m.length-2); + var val = emoji.map.colons[idx]; + return val ? emoji.replacement(val, idx, ':') : m; + }); + }; + + /** + * @memberof emoji + * @param {string} str A string potentially containing unified unicode + * emoticons. (ie. 😄) + * + * @returns {string} A new string with all unicode emoticons replaced with + * the appropriate representation for the current environment. + */ + emoji.replace_unified = function(str){ + emoji.init_unified(); + return str.replace(emoji.rx_unified, function(m){ + var val = emoji.map.unified[m]; + return val ? emoji.replacement(val) : m; + }); + }; + + // Does the actual replacement of a character with the appropriate + /** @private */ + emoji.replacement = function(idx, actual, wrapper){ + wrapper = wrapper || ''; + if (emoji.colons_mode) return ':'+emoji.data[idx][3][0]+':'; + var text_name = (actual) ? wrapper+actual+wrapper : emoji.data[idx][8] || wrapper+emoji.data[idx][3][0]+wrapper; + if (emoji.text_mode) return text_name; + emoji.init_env(); + if (emoji.replace_mode == 'unified' && emoji.allow_native && emoji.data[idx][0][0]) return emoji.data[idx][0][0]; + if (emoji.replace_mode == 'softbank' && emoji.allow_native && emoji.data[idx][1]) return emoji.data[idx][1]; + if (emoji.replace_mode == 'google' && emoji.allow_native && emoji.data[idx][2]) return emoji.data[idx][2]; + var img = emoji.data[idx][9] || emoji.img_sets[emoji.img_set].path+idx+'.png'; + var title = emoji.include_title ? ' title="'+(actual || emoji.data[idx][3][0])+'"' : ''; + var text = emoji.include_text ? wrapper+(actual || emoji.data[idx][3][0])+wrapper : ''; + if (emoji.supports_css) { + var px = emoji.data[idx][4]; + var py = emoji.data[idx][5]; + if (emoji.use_sheet && px != null && py != null){ + var mul = 100 / (emoji.sheet_size - 1); + var style = 'background: url('+emoji.img_sets[emoji.img_set].sheet+');background-position:'+(mul*px)+'% '+(mul*py)+'%;background-size:'+emoji.sheet_size+'00%'; + return ''+text+''; + }else if (emoji.use_css_imgs){ + return ''+text+''; + }else{ + return ''+text+''; + } + } + return ''; + }; + + // Initializes the text emoticon data + /** @private */ + emoji.init_emoticons = function(){ + if (emoji.inits.emoticons) return; + emoji.init_colons(); // we require this for the emoticons map + emoji.inits.emoticons = 1; + + var a = []; + emoji.map.emoticons = {}; + for (var i in emoji.emoticons_data){ + // because we never see some characters in our text except as entities, we must do some replacing + var emoticon = i.replace(/\&/g, '&').replace(/\/g, '>'); + + if (!emoji.map.colons[emoji.emoticons_data[i]]) continue; + + emoji.map.emoticons[emoticon] = emoji.map.colons[emoji.emoticons_data[i]]; + a.push(emoji.escape_rx(emoticon)); + } + emoji.rx_emoticons = new RegExp(('(^|\\s)('+a.join('|')+')(?=$|[\\s|\\?\\.,!])'), 'g'); + }; + + // Initializes the colon string data + /** @private */ + emoji.init_colons = function(){ + if (emoji.inits.colons) return; + emoji.inits.colons = 1; + emoji.rx_colons = new RegExp('\:[a-zA-Z0-9-_+]+\:', 'g'); + emoji.map.colons = {}; + for (var i in emoji.data){ + for (var j=0; j":"laughing", + ":->":"laughing", + ";)":"wink", + ";-)":"wink", + ":)":"blush", + "(:":"blush", + ":-)":"blush", + "8)":"sunglasses", + ":|":"neutral_face", + ":-|":"neutral_face", + ":\\\\":"confused", + ":-\\\\":"confused", + ":\/":"confused", + ":-\/":"confused", + ":p":"stuck_out_tongue", + ":-p":"stuck_out_tongue", + ":P":"stuck_out_tongue", + ":-P":"stuck_out_tongue", + ":b":"stuck_out_tongue", + ":-b":"stuck_out_tongue", + ";p":"stuck_out_tongue_winking_eye", + ";-p":"stuck_out_tongue_winking_eye", + ";b":"stuck_out_tongue_winking_eye", + ";-b":"stuck_out_tongue_winking_eye", + ";P":"stuck_out_tongue_winking_eye", + ";-P":"stuck_out_tongue_winking_eye", + "):":"disappointed", + ":(":"disappointed", + ":-(":"disappointed", + ">:(":"angry", + ">:-(":"angry", + ":'(":"cry", + "D:":"anguished", + ":o":"open_mouth", + ":-o":"open_mouth" + }; + + if (typeof exports === 'object'){ + module.exports = emoji; + }else if (typeof define === 'function' && define.amd){ + define(function() { return emoji; }); + }else{ + this.emoji = emoji; + } + +}).call(function(){ + return this || (typeof window !== 'undefined' ? window : global); +}());