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);
+}());