This repository has been archived on 2023-03-18. You can view files and clone it, but cannot push or open issues or pull requests.
osr-discourse-src/app/assets/javascripts/discourse/lib/emoji/emoji.js.erb

266 lines
7.0 KiB
Plaintext

// TODO @robin to move this whole thing to es6
Discourse.Emoji = {};
// bump up this number to expire all emojis
Discourse.Emoji.ImageVersion = "2"
var emoji = <%= Emoji.standard.map(&:name).flatten.inspect %>;
var aliases = <%= Emoji.aliases.inspect.gsub("=>", ":") %>;
var extendedEmoji = {};
if (Discourse.Dialect) {
Discourse.Dialect.registerEmoji = function(code, url) {
code = code.toLowerCase();
extendedEmoji[code] = url;
};
}
var _unicodeReplacements;
var _unicodeRegexp;
Discourse.Dialect.setUnicodeReplacements = function(replacements) {
_unicodeReplacements = replacements;
if (replacements) {
_unicodeRegexp = new RegExp(Object.keys(replacements).join("|"), "g");
}
}
// This method is used by PrettyText to reset custom emojis in multisites
Discourse.Dialect.resetEmojis = function() {
extendedEmoji = {};
};
var customEmojiCallbacks = [];
Discourse.Emoji.addCustomEmojis = function(cb) {
customEmojiCallbacks.push(cb);
};
Discourse.Emoji.applyCustomEmojis = function() {
var self = this;
_.each(customEmojiCallbacks, function(cb) { cb.apply(self); });
};
Discourse.Emoji.list = function(){
var list = emoji.slice(0);
_.each(extendedEmoji, function(v,k){ list.push(k); });
return list;
};
var emojiHash = {};
// add all default emojis
emoji.forEach(function(code){ emojiHash[code] = true; });
// and their aliases
var aliasHash = {};
for (var name in aliases) {
aliases[name].forEach(function(alias) {
aliasHash[alias] = name;
});
}
Discourse.Emoji.unescape = function(string) {
//this can be further improved by supporting matches of emoticons that don't begin with a colon
if (Discourse.SiteSettings.enable_emoji && string.indexOf(":") >= 0) {
string = string.replace(/\B:[^\s:]+:?\B/g, function(m) {
var isEmoticon = !!Discourse.Emoji.translations[m],
emoji = isEmoticon ? Discourse.Emoji.translations[m] : m.slice(1, m.length - 1),
hasEndingColon = m.lastIndexOf(":") === m.length - 1,
url = Discourse.Emoji.urlFor(emoji);
return url && (isEmoticon || hasEndingColon) ? "<img src='" + url + "' title='" + emoji + "' alt='" + emoji + "' class='emoji'>" : m;
});
}
return string;
};
Discourse.Emoji.urlFor = urlFor = function(code) {
var url, set = Discourse.SiteSettings.emoji_set;
code = code.toLowerCase();
if(extendedEmoji.hasOwnProperty(code)) {
url = extendedEmoji[code];
}
if(!url && emojiHash.hasOwnProperty(code)) {
url = Discourse.getURL('/images/emoji/' + set + '/' + code + '.png');
}
if(url && url[0] !== 'h' && Discourse.CDN) {
url = Discourse.CDN + url;
}
if(url){
url = url + "?v=" + Discourse.Emoji.ImageVersion;
}
return url;
};
Discourse.Emoji.exists = function(code){
code = code.toLowerCase();
return !!(extendedEmoji.hasOwnProperty(code) || emojiHash.hasOwnProperty(code));
};
function imageFor(code) {
code = code.toLowerCase();
var url = urlFor(code);
if (url) {
var code = ':' + code + ':';
return ['img', { href: url, title: code, 'class': 'emoji', alt: code }];
}
}
// Also support default emotions
var translations = {
':)' : 'slight_smile',
':-)' : 'slight_smile',
':(' : 'frowning',
':-(' : 'frowning',
';)' : 'wink',
';-)' : 'wink',
':\'(' : 'cry',
':\'-(': 'cry',
':-\'(': 'cry',
':p' : 'stuck_out_tongue',
':P' : 'stuck_out_tongue',
':-P' : 'stuck_out_tongue',
':O' : 'open_mouth',
':-O' : 'open_mouth',
':D' : 'smiley',
':-D' : 'smiley',
':|' : 'expressionless',
':-|' : 'expressionless',
':/' : 'confused',
'8-)' : 'sunglasses',
";P" : 'stuck_out_tongue_winking_eye',
";-P" : 'stuck_out_tongue_winking_eye',
":$" : 'blush',
":-$" : 'blush'
};
Discourse.Emoji.translations = translations;
function checkPrev(prev) {
if (prev && prev.length) {
var lastToken = prev[prev.length-1];
if (lastToken && lastToken.charAt) {
var lastChar = lastToken.charAt(lastToken.length-1);
if (!/\W/.test(lastChar)) return false;
}
}
return true;
}
var translationsWithColon = {};
Object.keys(translations).forEach(function (t) {
if (t[0] === ':') {
translationsWithColon[t] = translations[t];
} else {
var replacement = translations[t];
Discourse.Dialect.inlineReplace(t, function (token, match, prev) {
if (!Discourse.SiteSettings.enable_emoji) { return token; }
return checkPrev(prev) ? imageFor(replacement) : token;
});
}
});
Discourse.Dialect.addPreProcessor(function(text) {
if (_unicodeReplacements) {
_unicodeRegexp.lastIndex = 0;
var m;
while ((m = _unicodeRegexp.exec(text)) !== null) {
var replacement = ":" + _unicodeReplacements[m[0]] + ":";
var before = text.charAt(m.index-1);
if (!/\B/.test(before)) {
replacement = "\u200b" + replacement;
}
text = text.replace(m[0], replacement);
}
}
return text;
});
function escapeRegExp(s) {
return s.replace(/[-/\\^$*+?.()|[\]{}]/gi, '\\$&');
}
var translationColonRegexp = new RegExp(Object.keys(translationsWithColon).map(function (t) {
return "(" + escapeRegExp(t) + ")";
}).join("|"));
Discourse.Dialect.registerInline(':', function(text, match, prev) {
if (!Discourse.SiteSettings.enable_emoji) { return; }
var endPos = text.indexOf(':', 1),
firstSpace = text.search(/\s/),
contents;
if (!checkPrev(prev)) { return; }
// If there is no trailing colon, check our translations that begin with colons
if (endPos === -1 || (firstSpace !== -1 && endPos > firstSpace)) {
translationColonRegexp.lastIndex = 0;
var m = translationColonRegexp.exec(text);
if (m && m[0] && text.indexOf(m[0]) === 0) {
// Check outer edge
var lastChar = text.charAt(m[0].length);
if (lastChar && !/\s/.test(lastChar)) return;
contents = imageFor(translationsWithColon[m[0]]);
if (contents) {
return [m[0].length, contents];
}
}
return;
}
// Simple find and replace from our array
var between = text.slice(1, endPos);
contents = imageFor(between);
if (contents) {
return [endPos+1, contents];
}
});
var toSearch;
Discourse.Emoji.search = function(term, options) {
var maxResults = (options && options["maxResults"]) || -1;
if (maxResults === 0) { return []; }
toSearch = toSearch || _.union(_.keys(emojiHash), _.keys(extendedEmoji), _.keys(aliasHash)).sort();
var i, results = [];
function addResult(term) {
var val = aliasHash[term] || term;
if (results.indexOf(val) === -1) {
results.push(val);
}
return maxResults > 0 && results.length >= maxResults;
}
var item;
for (i=0; i<toSearch.length; i++) {
item = toSearch[i];
if (item.indexOf(term) === 0 && addResult(item)) {
return results;
}
}
for (i=0; i<toSearch.length; i++) {
item = toSearch[i];
if (item.indexOf(term) === 0 && addResult(item)) {
return results;
}
}
return results;
};
Discourse.Markdown.whiteListTag('img', 'class', 'emoji');