作为自定义WYSIWYG编辑器的一部分,我们已被要求实现自动表情符号解析(如果已启用)。为此,我们使用正则表达式将字符组合替换为其关联的PNG文件。
以下是处理此问题的代码的相关部分(它由onkeyup
元素上的contenteditable
事件触发;我已将其修剪回相关部分):
// Parse emjoi:
this.parseEmoji = function()
{
if( ! this.settings.parseSmileys )
{
return;
}
var _self = this,
url = 'http://cdn.jsdelivr.net/emojione/assets/png/',
$html = this.$editor.html();
// Loop through:
for( var i in _self.emoji )
{
var re = new RegExp( '\\B' + _self.regexpEscape(i) + '\\B', 'g' ),
em = _self.emoji[i];
if( re.test($html) )
{
var replace = '<img class="lw-emoji" height="16" src="'+(url + em[0] + '.png')+'" alt="'+em[1]+'" />';
this.insertAtCaret( replace );
_self.$editor.html(function() { return $(this).html().replace(re, ''); });
}
}
};
这是regexpEscape()
函数:
// Escape a string so that it's RegExp safe!
this.regexpEscape = function( txt )
{
return txt.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
};
我们定义系统中使用的所有表情符号,这些表情符号由char组合本身引用,如下所示:
this.emoji = {
':)' : [ '1F642', 'Smiling face' ],
':-)' : [ '1F642', 'Smiling face' ],
':D' : [ '1F601', 'Happy face' ],
':-D' : [ '1F601', 'Happy face' ],
':\'(': [ '1F622', 'Crying face' ],
':(' : [ '1F614', 'Sad face' ],
':-(' : [ '1F614', 'Sad face' ],
':P' : [ '1F61B', 'Cheeky' ],
':-P' : [ '1F61B', 'Cheeky' ],
':/' : [ '1F615', 'Unsure face' ],
':-/' : [ '1F615', 'Unsure face' ],
'B)' : [ '1F60E', 'Too cool face' ],
'B-)' : [ '1F60E', 'Too cool face' ]
};
现在,奇怪的是,任何包含字母字符的字符组合都不会被替换,并且re.test()
函数失败。例如::)
,:-)
,:(
和:'(
都可以毫无问题地被替换。但是,:D
和B)
没有。
有人可以解释为什么alpha字符会导致RegExp内部出现问题吗?
答案 0 :(得分:1)
问题是\B
是依赖于上下文的,如果有一个单词字符开始模式,则字符必须出现在匹配的输入字符串之前。在模式结尾处的方式相同,模式结尾处的\B
将需要出现相同类型的符号。
为避免此问题,通常使用基于环视的解决方案:(?<!\w)YOUR_PATTERN(?!\w)
。但是,在JS中,不支持lookbehind。它可以解决捕获组和后续替换功能中的反向引用。
因此,要正确替换这些情况,您需要将代码部分更改为
var re = new RegExp( '(^|\\W)' + _self.regexpEscape(i) + '(?!\\w)' ),
em = _self.emoji[i]; // match the pattern when not preceded and not followed by a word character
if( re.test($html) )
{
var replace = '<img class="lw-emoji" height="16" src="'+(url + em[0] + '.png')+'" alt="'+em[1]+'" />';
this.insertAtCaret( replace );
_self.$editor.html(function() { return $(this).html().replace(re, '$1'); }); // restore the matched symbol (the one \W matched) with $1
}