views:

263

answers:

4

I have a string (partly HTML) where I want to replace the string :-) into bbcode :wink:. But this replacement should not happen within <pre>, but in any other tag (or even not within a tag).

For example, I want to replace

:-)<pre>:-)</pre><blockquote>:-)</blockquote>

to:

:wink:<pre>:-)</pre><blockquote>:wink:</blockquote>

I already tried it with the following RegEx, but it does not work (nothing gets replaced):

var s = ':-)<pre>:-)</pre><blockquote>:-)</blockquote>';
var regex = /:\-\)(?!(^<pre>).*<\/pre>)/g;
var r = s.replace(regex, ':wink:');

Can someone please help me? :-)

+3  A: 

You could avoid hellish regexes altogether if you use a suitable library such as jQuery, e.g.:

var excludeThese = ['pre'];

// loop over all elements on page, replacing :-) with :wink: for anything
// that is *not* a tag name in the excludeThese array

$('* not:(' + excludeThese.join(',') + ')').each(function() {
    $(this).html($(this).html().replace(/:\-\)/,':wink:'));
});
karim79
.html() returns a string. Hence .replace is a regex replace.
AnthonyWJones
I always make that error, thanks for pointing it out, fixed it. Did that *really* warrant a down-vote ?
karim79
Thanks, I already use prototype, do you know how it's done there?
acme
Probably not its own, downvote removed. However when a question is about javascript and JQuery is not specified I do find answers to that introduce JQuery unnecessarily irritating. I would hardly say that your JQuery code is much less "hellish" than a pure Regex/Javascript solution to the question asked. Hence that combined with the fact that Regex is still needed and your answer didn't actually work was enough for the downvote.
AnthonyWJones
A: 

try with var regex = /:-)(?!(^)*<\/pre>)/g;

Sadly this only works when there is no other sign besides the ":-)". For example, with ":-)<pre> :-) </pre><blockquote>:-)</blockquote>" it fails.
acme
+2  A: 

This ought to do it:-

var src = ":-)<pre>:-)</pre><blockquote>:-)</blockquote>"

var result = src.replace(/(<pre>(?:[^<](?!\/pre))*<\/pre>)|(\:\-\))/gi, fnCallback)

function fnCallback(s)
{
    if (s == ":-)") return ":wink:"
    return s;
}

alert(result);

It works because any pre element will get picked up by the first option in the regex and once consumed means that any contained :-) can't be matched since the processor will have moved beyond it.

AnthonyWJones
Downvoter, specify your reason?
AnthonyWJones
Thanks! This works, only minor fault is that the function definition has to be before the function call - but this was easy to solve ;-)
acme
Glad it solves your problem but I'm curious as to why you feel the function definition needs to preceed the call? It worked fine as is in my test harness. Javascript creates all function definitions in an execution block before executing the block. Were you thinking of function expressions?
AnthonyWJones
@Anthony what JS Test framework do you use?
Gutzofter
@Gutzofter: Its a mega-powerful, superwhizzy test harness:-
AnthonyWJones
function writeLn(s) { WScript.Echo(s); }\n alert = writeLn\n \\Your code here. ;)
AnthonyWJones
@Anthony: I copy/pasted your solution into the Firebug console to test it and there it didn't work as long as the function definition was before the function call. I have to admit I didn't test it in the real code (just implemented it then afterwards).
acme
@acme: Yes it would fail in firebug console. Console executes each line as soon as it sees it. It can't parse the whole file since there is no file just a constant stream of input. If you place the code inside (function() { ...code here... })(); it works in console.
AnthonyWJones
+1  A: 

Just thought it'd be worth offering a DOM solution:

E.g.

var div = document.createElement('div');
div.innerHTML = ":-)<pre>:-)</pre><blockquote>:-)</blockquote>";

replace(div, /:-\)/g, ":wink:", function(){

    // Custom filter function.
    // Returns false for <pre> elements.

    return this.nodeName.toLowerCase() !== 'pre';

});

div.innerHTML; // <== here's your new string!

And here's the replace function:

function replace(element, regex, replacement, filter) {

    var cur = element.firstChild;

    if (cur) do {

        if ( !filter || filter.call(cur) ) {

            if ( cur.nodeType == 1 ) {
                replace( cur, regex, replacement );
            } else {
                cur.data = cur.data.replace( regex, replacement );
            }

        }

    } while ( cur = cur.nextSibling );

}
J-P