1

I'm adding emoticons to user input with:

function emoticons(html){
    for(var emoticon in emotes){
        for(var i = 0; i < emotes[emoticon].length; i++){
            // Escape bad characters like )
            var r = RegExp.escape(emotes[emoticon][i]);
            // Set the regex up to replace all matches
            r_escaped = new RegExp(r, "g");
            // Replace the emote with the image
            html = html.replace(r_escaped,"<img src=\""+icon_folder+"/face-"+emoticon+".png\" class=\"emoticonimg\" />");
        }
    }
    return html;
}

The problem is sometimes the user input is in <code>xxx</code> blocks. Is there a way to get the emoticons function to ignore everything inside the code blocks if they exists. As they won't always exist?

Thanks

AnApprentice
  • 108,152
  • 195
  • 629
  • 1,012

1 Answers1

1

In order to do this easily, I'd work only with text nodes (not serialised HTML) and skip code elements.

You tagged it , so there is some jQuery convenience code to ease cross browser issues with utility functions. It is very easily modified to work without jQuery, however.

var searchText = function(parentNode, regex, callback, skipElements) {

    skipElements = skipElements || ['script', 'style'];

    var node = parentNode.firstChild;

    do {

        if (node.nodeType == 1) {

            var tag = node.tagName.toLowerCase();

            if (~$.inArray(tag, skipElements)) {
                continue;
            }

            searchText.call(this, node, regex, callback);

        } else if (node.nodeType == 3) {
            while (true) {

                // Does this node have a match? If not, break and return. 
                if (!regex.test(node.data)) {
                    break;
                }

                node.data.replace(regex, function(match) {

                    var args = $.makeArray(arguments),
                        offset = args[args.length - 2],
                        newTextNode = node.splitText(offset);

                    callback.apply(window, [node].concat(args));
                    newTextNode.data = newTextNode.data.substr(match.length);
                    node = newTextNode;

                });
            }
        }
    } while (node = node.nextSibling);
};

searchText($('body')[0], /:\)/, function(node, match) {
    var img = $('<img />')[0];
    img.src = 'http://www.gravatar.com/avatar/80200e1488ab252197b7f0f51ae230ef?s=32&d=identicon&r=PG';
    img.alt = match;
    node.parentNode.insertBefore(img, node.nextSibling);
}, ['code']);

jsFiddle.

I wrote this function recently, it should do what you hope to achieve.

alex
  • 479,566
  • 201
  • 878
  • 984
  • Thanks alex, are you suggesting I make this as part of the plugin or use before calling the plugin? – AnApprentice Jan 26 '12 at 23:35
  • @Brett You can make this part of the plugin, or as a global utility function namespaced from `jQuery`. Depends on where you may find it handy, and if you want the extra dependency :) – alex Jan 26 '12 at 23:42