JS - function to make all lowercase except text between speech marks

Viewed 48

function reformat() {

    // assign textarea value to a variable
    var str = my_text.value;
            
    // lower case
    str = str.toLowerCase();

    // set textarea value with new value
    my_text.value = str;
    
    
    
}
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">

        <div class="container-fluid" style="padding-top:10px;">
        
            <textarea id="my_text" name="my_text" style="width:400px; height:150px;" class="form-control">
    select reject_lookup_code "Look Up Code"
         , count(*)
      from ap_interface_rejections
     WHERE reject_lookup_code NOT IN ('THIS','that')
  group by reject_lookup_code
            </textarea>

<hr />

            <button onclick="reformat()">Toggle</button>    
        
        </div> <!-- /container-fluid -->
    
    

The above code is a simple way to make all text in the textarea lowercase.

Via this question:

Make all lowercase except text between speech marks

Someone kindly confirmed how to use a regular expression in Notepad++ to make all lowercase except text between speech marks (``):

  • Find: '[^']*'|([A-Z])
  • Replace With: (?1\L$1:$0)

I am probably asking for the "moon on a stick" here - but is there a way to achieve the same result using Javascript - except for anything between speech marks (both "" and ``) to not have its case altered?

So that this:

select reject_lookup_code "Look Up Code"
     , count(*)
  from ap_interface_rejections
 WHERE reject_lookup_code NOT IN ('THIS','that')
  group by reject_lookup_code

Becomes this:

select reject_lookup_code "Look Up Code"
     , count(*)
  from ap_interface_rejections
 where reject_lookup_code not in ('THIS','that')
  group by reject_lookup_code

Thanks

2 Answers

I believe this solves your issue! Try typing into the textarea to see if you're getting the result you want.

Note the regex simply differentiates between:

  • single-quoted blocks of text not containing single-quotes
  • double-quoted blocks of text not containing double-quotes
  • backtick-quoted blocks of text not containing backticks
  • any non-single-quote, non-double-quote, non-backtick character

For each match of this regex, we simply check if the first character of the match is a quote. If it is, we leave the match alone (no case change to quoted text). If the first character is anything else, we replace it with its lowercase equivalent!

Note I couldn't exactly tell from your question if you want backtick-quoted content to remain unchanged. If you don't, simply remove the backtick-related disjunction from the regex!

let ta = document.querySelector('textarea');
let p = document.querySelector('p');

let reg =                        /['][^']+[']|["][^"]+["]|[`][^`]+[`]|[^'"`]+/g;
// Disjunction                               |           |           |
// Single quote                   [']        |           |           |
// One or more non-single-quotes     [^']+   |           |           |
// Single quote                           [']|           |           |
// Double quote                              |["]        |           |
// One or more non-double-quotes             |   [^"]+   |           |
// Double quote                              |        ["]|           |
// Backtick                                  |           |[`]        |
// One or more non-backticks                 |           |   [^`]+   |
// Backtick                                  |           |        [`]|
// Any number of non-quote characters        |           |           |[^'"`]+

ta.addEventListener('input', evt => {
  
  p.textContent = ta.value.replace(reg, match =>
    
    // Is the first character of `match` a quote?
    '\'"`'.includes(match[0])
      
      // Yes! Return quoted content unchanged.
      ? match
      
      // No! Ensure all non-quoted content is lowercase
      : match.toLowerCase()
      
  );
  
});
* { font-family: monospace; }

textarea { width: 80%; height: 80px; }

p { background-color: rgba(0, 0, 0, 0.1); white-space: pre; overflow: hidden; }

/* This simply ensures <p> elements have a height even if they're empty */
p::before { content: 'x'; position: relative; top: -100px; }
<textarea placeholder="Type stuff here!"></textarea>
<p>(Result will appear here)</p>

Consider

re = `
    ( ' (?: [^'] | \\.)* ' )   # group1 = single quoted string, with possible escapes
    |                          # OR
    ( " (?: [^"] | \\.)* " )   # group2 = double quoted string, with possible escapes
    |                          # OR
    ( [^'"]+ )                 # group3 = a sequence of non-quotes
`

compiledRe = new RegExp(
    re.replace(/\s+|#.*/g, ''),
    'g'
)

console.log(compiledRe)


text = String.raw`
    select reject_lookup_code "Look Up \"Code\"?"
         , Count(*)
    from ap_interface_rejections
    WHERE reject_lookup_code NOT IN ('THIS', 'that', 'and \'This\' One too')
    GROUP BY reject_lookup_code
`


result = text.replace(
    compiledRe,
    (_, group1, group2, group3) =>
        group1
        || group2
        || group3.toLowerCase()
)

console.log(result)

The idea is to match a strings and non-strings separately and choose between them in the replacer callback.

Related