var emoji = <%= Dir.glob(File.expand_path("../../../public/images/*.png", __FILE__)).map{|f| File.basename(f).split(".")[0]}.inspect %>; // TODO @robin to move this whole thing to es6 Discourse.Emoji = {}; var _extendedEmoji = {}; Discourse.Dialect.registerEmoji = function(code, url) { _extendedEmoji[code] = url; }; var toSearch; var search = function(term, options) { var maxResults = (options && options["maxResults"]) || -1; toSearch = toSearch || emoji.concat(Object.keys(_extendedEmoji)); if(maxResults === 0) { return []; } var results = []; var done = function(){ return maxResults > 0 && results.length >= maxResults; } for (i=0; i < toSearch.length; i++) { if (toSearch[i].indexOf(term) === 0) { results.push(toSearch[i]); if(done()) { break; } } } if(!done()){ for (i=0; i < toSearch.length; i++) { if (toSearch[i].indexOf(term) > 0) { results.push(toSearch[i]); if(done()) { break; } } } } return results; } Discourse.Emoji.search = search; var urlFor = function(code) { var url = _extendedEmoji[code]; if (!url && emoji.indexOf(code) !== -1) { url = Discourse.getURL('/plugins/emoji/images/' + code + '.png'); } return url; } Discourse.Emoji.urlFor = urlFor; var emojiHash = {}; emoji.forEach(function(code){ emojiHash[code] = true; }); Discourse.Emoji.exists = function(code){ return !!(_extendedEmoji[code] || emojiHash[code]); } function imageFor(code) { var url = urlFor(code); if (url) { return ['img', {href: url, title: ':' + code + ':', 'class': 'emoji', alt: code}]; } } // Also support default emotions var translations = { ':)' : 'smile', ':-)' : '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', ";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 (lastChar !== ' ' && lastChar !== "\n") 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; }); } }); function escapeRegExp(s) { return s.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&'); } 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 && (lastChar !== ' ' && lastChar !== "\n")) 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]; } }); Discourse.Markdown.whiteListTag('img', 'class', 'emoji');