1

This is a follow up question to my question about Setting the CSS of code if it contains a reserved word.

What I am trying to do: If some code has quotes or double quotes, I want to set the color of the font to red and bold. Ex. System.out.println( "Hello world" ); should set "Hello world" to red.

What's wrong: Despite my best efforts, I can't seem to get my control statements to work properly (at least I think that's the issue). It sets the first double quote and beyond to red, but when I tell it to stop when a word equals anyword" or anyword' it sets the rest of the code in the block to red.

HTML

<html>
    <body>
        <code id="java">
            public static void main(String[] args)<br>
            {
            <pre>    int i = 120; </pre><br>
            <pre>    // Displays a message in the console </pre>
            <pre>    // This is a test </pre>
            <pre>    System.out.println( "Hello Big World!" );</pre>
            }
        </code>
    </body>
</html>

CSS

.quotes
{
    font-weight: bold;
    color: #E01B1B;
}

jQuery

$(document).ready(function() {
    var code  = $("#java").html(); // Get the code
    var split = code.split(' ');   // Split up each element
    var chkQ  = 0;                 // Check for quotes
    var chkC  = 0;                 // Check until end of comment line

    // Set the CSS of reserved words, digits, strings, and comments
    for (var j = 0; j < split.length; j++) {
        // Check to see if chkQ is set to true
        if (chkQ == 1) {
            // If the element matches (anyword") or (anyword'), then set
            // flag to false and continue checking the rest of the code.
            // Else, continue setting the CSS to .quotes
            if (split[j].match(/."/) || split[j].match(/.'/)) {
                split[j] = '<span class="quotes">' + split[j] + '</span>';
                chkQ = 0;
            } else {
                split[j] = '<span class="quotes">' + split[j] + '</span>';
            }
        }
        ...
        } else if (chkQ == 0 && chkC == 0) {
            ...
            // If the element matches a ("anyword) or ('anyword)...
            } else if (split[j].match(/"./) || split[j].match(/'./)) {
                split[j] = '<span class="quotes">' + split[j] + '</span>';
                chkQ = 1;
            } ...
        }
    }
    // Join all the split up elements back together!
    $("#java").html(split.join(' '));
});

Question: Is this just simply an issue with my regex, control blocks or something completely different?

Community
  • 1
  • 1
Rob
  • 435
  • 1
  • 5
  • 25
  • 1
    Regex isn't a good idea here. You need to account for escaped characters (`\"`) and nested quotes (`"this string 'contains' nested quotes."`). You'd be better off simply looping through the string, and keeping track of the quotes with a boolean or something. – Karl Nicoll Apr 16 '12 at 22:39
  • @KarlNicoll Thank you for your help. I'm just trying to take one step at a time and nail down the basics. I will take your post into consideration. – Rob Apr 16 '12 at 22:41

4 Answers4

4

Why split the string up when you can perform a simple global regex find and replace:

<script type="text/javascript">
$(document).ready(function(){
//cache the element
   el = $('#java');
//get the HTML contained within the cached element
   code = el.html();
//return the code having executed the replace method, regex explained:
/*    
([^\w]{1}) -> look for a single character that is not an alpha character
(["']) -> then look for either a single quote or double quote
(.*?) -> then look any character, but don't be greedy
(\2) -> then look for what was found in the second group - " or '
([^\w]{1}) -> and finally look for a single character that is not an alpha character
*/
    code = code.replace(/([^\w]{1})(["'])(.*?)(\2)([^\w]{1})/gm,
//execute an anonymous callback, passing in the result for every match found
    function(match, $1, $2, $3, $4, $5, offset, original) {
//construct the replacement
        str =  $1 + '<span class="quotes">' + $2 + $3 + $4 + '</span>' + $5; 
//return the replacement
        return str; 
    });
//replace the existing HTML within the cached element
   el.html(code);
});
</script>

Edit: Just updated it to accommodate nested quotes.

trickyzter
  • 1,591
  • 13
  • 9
  • That sounds great and all but I have no idea what any of that means. I've just begun researching and using regular expressions in my code. Could you please explain what is going on after `replace(`? – Rob Apr 16 '12 at 23:52
  • I like your idea (its compact), but is there an easy way to filter out apostrophes ([since the above code fails](http://jsfiddle.net/f7wBd/14/)) should one need to? – ScottS Apr 17 '12 at 00:14
  • @ScottS Sorry for the late response, assuming a alpha character falls on either side of the apostrophe, it's relatively straight forward: http://jsfiddle.net/f7wBd/57/ or see updated code above. – trickyzter Apr 17 '12 at 13:29
  • @ScottS and Mike Dtrick Ignore prior jsfiddle link, see http://jsfiddle.net/f7wBd/61/ for updated code accommodating nested quotes. – trickyzter Apr 17 '12 at 13:50
  • @trickyzter Thank you for the explanation. The only problem is that I don't know how I would implement this block of code into my function. [Here is my demo](http://jsfiddle.net/mdeitrick/qTrcS/) that splits up each character as `split = code.split(' ');` and rejoins everything after the CSS is set... which I think may cause some problems since this example replaces the text and uses `el.html(code);`. Also, notice "Hello Big World" has a space between both parenthesis to ensure the entire line is not set to red. – Rob Apr 17 '12 at 23:30
  • @trickyzter Well, my problem really isn't solved but that's my fault for not mentioning the other parts of my code. +1 for the help too. – Rob Apr 19 '12 at 23:56
2

I don't know all your requirements, but it seems that your single quote could get a bit complicated.

I've set up a demonstration that works (updated link to include nested quotes).

I do not guarantee it is bug free. It does the replacement in two stages, first for double quotes, then for single, trying to weed out potential apostrophes (note in the code below the filters for apostrophes are based off common following letters--not sure how many you might practically need, if any).

Javascript

$(document).ready(function() {
    var code  = $("#java").html(); // Get the code
    var split = code.split('\"');  // Split up each element at the "

    // Set the CSS of reserved words, digits, strings, and comments
    for (var j = 0; j < split.length - 1; j++) {
        if (j%2 == 0) { //if first, add beginning
            split[j] = split[j] + '<span class="quotes">"';
        } else {//if second, add ending
            split[j] = split[j] + '"</span>';
        }
    }
    // Join all the split up elements back together!
    $("#java").html(split.join(""));

    code  = $("#java").html(); // Get the code
    split = code.split('\'');  // Split up each element at the '
    var openQ = 1;
    var sub1;
    var sub2;

    for (var j = 0; j < split.length - 1; j++) {
        sub1 = split[j+1].substr(0,2); //checking for a contraction of 's
        sub2 = split[j+1].substr(0,3); //checking for a contraction of 'll
        if(sub1 != "s " && sub2 != "ll ") {
          if (openQ) { //if first, add beginning
            split[j] = split[j] + '<span class="quotes">\'';
            openQ = 0;
          } else {//if second, add ending
            split[j] = split[j] + '\'</span>';
            openQ = 1;
          }
        }
        else {//add apostrophe back
            split[j] = split[j] + '\'';
        }
    }
    $("#java").html(split.join(""));
});
ScottS
  • 71,703
  • 13
  • 126
  • 146
  • Thank you for your response, this is a great answer. The only problem is `split = code.split('\'');` and `split = code.split("\"");`. If I set split to split up each element in that manner (as opposed to `split = code.split(' ');`) then it will never pick up comments, digits, or reserved words. I'll let you know if I come across anything. – Rob Apr 17 '12 at 12:21
0

Here's a pure JavaScript version:
id= id of element with quotes
classid= class to add to the quotes

function quotes(id,classid) {
    var code  = document.getElementById(id).innerHTML; 
    var split = code.split('\"'); 
    for (var j = 0; j < split.length - 1; j++) {
        if (j%2 == 0) { 
            split[j] = split[j] + '<span class='+classid+'>"';
        } else {
            split[j] = split[j] + '"</span>';
        }
    }
    document.getElementById(id).innerHTML = split.join("");
    code  = document.getElementById(id).innerHTML;
    split = code.split('\'');
    var openQ = 1;
    var sub1;
    var sub2;
    for (var j = 0; j < split.length - 1; j++) {
        sub1 = split[j+1].substr(0,2);
        sub2 = split[j+1].substr(0,3);
        if(sub1 != "s " && sub2 != "ll ") {
          if (openQ) {
            split[j] = split[j] + '<span class='+classid+'>\'';
            openQ = 0;
          } else {
            split[j] = split[j] + '\'</span>';
            openQ = 1;
          }
        }
        else {
            split[j] = split[j] + '\'';
        }
    }
    document.getElementById(id).innerHTML = split.join("");
}
StratHaxxs
  • 51
  • 1
  • 11
0

String.prototype.Text2Html = function (){
  var div = document.createElement('div');
  div.appendChild(document.createTextNode(this))
  encoded=div.innerHTML;
  div.remove();
  return encoded
}

String.prototype.colorTheQuotes = function(){
  
  var re     = /(?:<span style=|)(?:(?:"[^"]*")|(?:'[^']*'))/gm,
      text   = this.Text2Html(),
      output = text,
      tour   = 0,
      slen   = 27;

    while ((match = re.exec(text)) != null) {
      if(match[0].startsWith("<span")) continue
      output=output.slice(0,match.index+tour*slen)+'<span class="quote">'+output.slice(match.index+tour*slen,match.index+match[0].length+tour*slen)+"</span>"+output.slice(match.index+match[0].length+tour*slen);tour++
    }
  return output
}

element=document.getElementById("color")
document.addEventListener("readystatechange",(e)=>{
  element.innerHTML=element.innerText.colorTheQuotes();
 })
.quote{
  color: red;
}
<span>System.out.println( "Hello world" );</span><br>
<span id="color">System.out.println( "Hello world" );</span>
Herom123
  • 21
  • 5
  • Welcome to StackOverflow. While this code may answer the question, providing additional context regarding *how* and/or *why* it solves the problem would improve the answer's long-term value. – Sven Eberth Jul 08 '21 at 22:13