-1

I've got to work a basic find string and add style eg. <button onclick="copyData(event)" class="${orders.test(match)}">${match}</button> But I want to apply the same methodology to all four regex matches below; matching the corresponding .css style.

orders:    example: 975612345678. range: 975600000000-975699999999 
customers: example: 1712345678901. range: 1700000000000-1799999999999
emails:    example: word@test.com, 01234@numbers.tk
items:     example: 32101234, 1012345678. range: 00000000-99999999, range: 1000000000-1099999999

We can see that it misses regex for emails and items: range: 00000000-99999999.

const orders = /\b9756[0-9]{8}\b/;
const customers = /\b17[0-9]{11}\b/;
const emails = ??
const items = ?? and /\b10[0-9]{8}\b/;
for (var i = 0; i < list.length; i++) {
    let text = list[i].textContent;
    // Make the regex have boundary characters to ensure that it's checking against the whole number, rather than a part. example: 975612345678. range: 975600000000-975699999999  
    const orders = /\b9756[0-9]{8}\b/; 
    list[i].innerHTML = text.replace(
        // Replace all number sequences in the text
        /\d+/g,
        // Replace by checking if the replacement text matches "const orders"(regex) to determine color
        (match) => `<button onclick="copyData(event)" class="${orders.test(match)}">${match}</button>`
    )
}
/*.true needs to be .orders */
.true {
  background-color: green;
}
.orders {
    background-color: green;
}

/* corresponding regex "orders, customers, emails and items" needs to match style */
.customers {
    background-color: red;
}
.emails {
    background-color: blue;
}
.items {
    background-color: yellow;
}

Anyone who has an solution to the problem? I have found something at Replace multiple strings with multiple other strings, but haven't found a working solution. A demo can be checked at https://jsfiddle.net/rrggrr/rt96ghw2/14/

Wiktor Stribiżew
  • 607,720
  • 39
  • 448
  • 563
RRG
  • 45
  • 1
  • 6

1 Answers1

1

Here's a refactor that should work for what you want.

I'm using multiple replacements, with the first taking care of the emails, and the second taking care of the numbers that aren't part of an email address.

It isn't necessarily the most performant code unfortunately, but it works for the input given.

Email regex from https://www.regular-expressions.info/email.html as emails are super complicated to regex.

Original snippet here:

var list = document.getElementsByClassName("message__content")
//window.copyData = copyData;
// use number manipulation to check if orders/customers/items are actually real values, as we don't need a regex for it since they are just ranges. If they are more complicated, then we could replace it with a regex
function isOrder(string) {
  return Number(string) >= 9756E8 && Number(string) < 9757E8;
}

function isCustomer(string) {
  return Number(string) >= 17E11 && Number(string) < 18E11;
}

function isItem(string) {
  return (Number(string) >= 0 && Number(string) < 1e8) || (Number(string) >= 1E9 && Number(string) < 1.1E9)
}

for (var i = 0; i < list.length; i++) {
  // Email regex from https://www.regular-expressions.info/email.html - many more complicated regexes for it on this site as well. Emails are super complicated
  const emailRegex = /\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/gi;
  let text = list[i].textContent;

  list[i].innerHTML = text.replace(emailRegex, '<button onclick="copyData(event)" class="copyData emails">$&</button>')
    .replace(
      // Replace all number sequences in the text
      // Make the regex have boundary characters to ensure that it's checking against the whole number, rather than a part. example: 975612345678. range: 975600000000-975699999999 
      // Specifically disclude anything that is an email address via composition since we look at emails already in the first replacement
      new RegExp(`\\b(?!${emailRegex.source})\\d+\\b`, 'gi'),
      // Replace by checking if the replacement text matches any of the number based checks to determine color
      (match) => {
        if (isOrder(match)) {
          return `<button onclick="copyData(event)" class="copyData orders">${match}</button>`
        } else if (isCustomer(match)) {
          return `<button onclick="copyData(event)" class="copyData customers">${match}</button>`
        } else if (isItem(match)) {
          return `<button onclick="copyData(event)" class="copyData items">${match}</button>`
        } else {
          return match;
        }
      }
    )
}



function copyData(e) {
  const textarea = document.createElement('textarea');
  textarea.textContent = e.currentTarget.innerText;
  textarea.style.position = 'fixed'; // Prevent scrolling to bottom of page in MS Edge.
  document.body.appendChild(textarea);
  textarea.select();
  document.execCommand('copy');
  document.body.removeChild(textarea);
}
for (const elem of document.querySelectorAll('.copyData')) {
  elem.addEventListener('click', copyData)
}
div {
  white-space: pre;
}

.orders {
  background-color: green;
}


/* corresponding regex "orders, customers, emails and items" needs to match style */

.customers {
  background-color: red;
}

.emails {
  background-color: blue;
}

.items {
  background-color: yellow;
}
<div class="message__content">
orders:    example: 975612345678. range: 975600000000-975699999999 
customers: example: 1712345678901. range: 1700000000000-1799999999999
emails:    example: word@test.com, 01234@numbers.tk
items:     example: 32101234, 1012345678. range: 00000000-99999999, range: 1000000000-1099999999
</div>
<div class="message__content">
GOOD:
orders:    example: 975612345678 AND 140123456789
customers: example: 1712345678901.
emails:    example: word@test.com
items:     example: 32101234 AND 1012345678

WRONG:
orders:    example: 975612345678test AND test140123456789
customers: example: 1712345678901test AND test1712345678901
emails:    example: word@test.comtest
items:     example: 32101234test AND test1012345678
</div>

Here's a refactor to allow for the numbers to not affect the emails. This uses string#split using a capturing group makes that group end up in the resulting array. Then we map over the array and any value with an odd index (index % 2 === 1) is an email from the capturing group. The other values, can then use a regex replace to perform the other matches requested.

The other change I made was to make the items group use an anchor tag to link to google, though stack snippets prevent popups, so the anchor tag will just throw an error in the console here.

var list = document.getElementsByClassName("message__content")

//window.copyData = copyData;
// use number manipulation to check if orders/customers/items are actually real values, as we don't need a regex for it since they are just ranges. If they are more complicated, then we could replace it with a regex
function isOrder(string) {
  return (Number(string) >= 9756E8 && Number(string) < 9757E8) || (Number(string) >= 14E10 && Number(string) < 15E10)
}

function isCustomer(string) {
  return Number(string) >= 17E11 && Number(string) < 18E11;
}

function isItem(string) {
  return (Number(string) >= 0 && Number(string) < 1e8) || (Number(string) >= 1E9 && Number(string) < 1.1E9)
}

for (var i = 0; i < list.length; i++) {
  // Email regex from https://www.regular-expressions.info/email.html - many more complicated regexes for it on this site as well. Emails are super complicated
  const emailRegex = /(\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b)/gi;
  // Splitting using a capturing group makes that group end up in the result
  let text = list[i].textContent;
  let splitText = text.split(emailRegex);
  list[i].innerHTML = splitText.map((text, index) => {
    if (index % 2 === 1) {
      // This is an email address
      return `<button onclick="copyData(event)" class="copyData emails">${text}</button>`
    }
    return text.replace(/\d+/g, (match) => {
      if (isOrder(match)) {
        return `<button onclick="copyData(event)" class="copyData orders">${match}</button>`
      } else if (isCustomer(match)) {
        return `<button onclick="copyData(event)" class="copyData customers">${match}</button>`
      } else if (isItem(match)) {
        return `<a href="http://google.com?q=${match}" target="_blank" rel=”noreferrer noopener”><button onclick="copyData(event)" class="button copyData items">${match}</button></a>`
      } else {
        return match;
      }
    })
  }).join('')
}



function copyData(e) {
  const textarea = document.createElement('textarea');
  textarea.textContent = e.currentTarget.innerText;
  textarea.style.position = 'fixed'; // Prevent scrolling to bottom of page in MS Edge.
  document.body.appendChild(textarea);
  textarea.select();
  document.execCommand('copy');
  document.body.removeChild(textarea);

}
for (const elem of document.querySelectorAll('.copyData')) {
  elem.addEventListener('click', copyData)

}
div {
  white-space: pre;
}

.orders {
  background-color: green;
}


/* corresponding regex "orders, customers, emails and items" needs to match style */

.customers {
  background-color: red;
}

.emails {
  background-color: blue;
}

.items {
  background-color: yellow;
}
<div class="message__content">
orders:    example: 975612345678. range: 975600000000-975699999999 
customers: example: 1712345678901. range: 1700000000000-1799999999999
emails:    example: word@test.com, 01234@numbers.tk
items:     example: 32101234, 1012345678. range: 00000000-99999999, range: 1000000000-1099999999
</div>
<div class="message__content">
GOOD:
orders:    example: 975612345678 AND 140123456789
customers: example: 1712345678901.
emails:    example: word@test.com
items:     example: 32101234 AND 1012345678

WRONG:
orders:    example: 975612345678test AND test140123456789
customers: example: 1712345678901test AND test1712345678901
emails:    example: word@test.comtest
items:     example: 32101234test AND test1012345678
</div>
Zachary Haber
  • 10,376
  • 1
  • 17
  • 31
  • Ah thats perfect! Is it also possible to make the button redirect to a certain URL. For lets say, when clicked only on button "item: 32101234" to link it to https://www.website.com/search?q=32101234 AND copy – RRG Oct 11 '20 at 17:52
  • I have only seen a small 'bug', where a word or letter is accidentally stuck to a number. For example by accidentally forgetting a space. https://jsfiddle.net/rrggrr/r8mxye5a/12/ – RRG Oct 11 '20 at 18:18
  • 1
    @RRG, I've fixed the issues you were asking about. I made the items have a link to a google search page, but it won't work on stack overflow due to permissions issues. – Zachary Haber Oct 11 '20 at 19:18
  • Ah nice thanks! small thingy. How te remove all dots in a string like 12.34.5678 --> 12345678 in items --> return (Number(string) >= 0 && Number(string) < 1e8) – RRG Oct 11 '20 at 21:07
  • and a regex to remove the spaces from customers 1712 3456 7890 1 --> 1712345678901. I think its also possible to remove all spaces and dots from all the values in hindsight. – RRG Oct 11 '20 at 21:19
  • I feel like we've gone pretty far beyond the question you've asked on here, so try and work through the remaining issues and post a more focused question (each question on SO should only focus on one question). As for removing all dots in a string, `text.replace(/\.+/g,'')` and removing all spaces: `text.replace(/ +/g,'')`, spaces and dots would use `/[ .]+/g`. Though there's more involved in adding that to everything that's already been built. – Zachary Haber Oct 12 '20 at 03:33