regex to replace strings with img - js - javascript

hey there I am currently having the issues that I want to detect strings in a text with an image.
{"a":"img1.jpg", "ab":"img2.jpg"}
my current regex is:
/(a|ab)/g
When I have a text like:
yeah abc
it replaces the "a" in the yeah with img1.jpg but it also replaces the "ab"c with "img1.jpg".
I can avoid it through switching /(ab|a)/ but this is can't be the solution, since I have a huge unsorted json list as expressions (a, ab is just for simplicity). The reason I am doing this is to replace native emojis with images.
How can I say, that it only replaces the a if there is no b following?

Sort the emoji keys in descendig order, then build your regex pattern like this:
function replaceEmojis (str) {
const emojis = {
a: { src: 'imgA.jpg', color: 'red' },
abc: { src: 'imgABC.jpg', color: 'green' },
ab: { src: 'imgAB.jpg', color: 'blue' },
bc: { src: 'imgBC.jpg', color: 'orange' }
};
const fnDescendingOrder = ([x, y]) => x > y ? -1 : +(x != y);
const keys = Object.keys(emojis).sort((x, y) =>
fnDescendingOrder(x.length == y.length ? [x, y] : [x.length, y.length])
);
const pattern = new RegExp(keys.join('|'), 'g');
const transformed = str.replace(pattern, m => {
const emoji = emojis[m];
return '<img class="' + emoji.color + '" src="' + emoji.src + '">';
});
return transformed;
};
let str = 'yeah abc ab a abca bcaba';
result.innerHTML = replaceEmojis(str);
img { width: 10px; height: 100%; }
img.red { background: red; }
img.green { background: green; }
img.blue { background: blue; }
img.orange { background: orange; }
<div id="result"></div>
You have to sort in descending order first by length, next by alphabetical order. Cause bc should be checked after abc.

Related

String between two "*" or "_" characters not getting bold or italic

Suppose there is string wrapped with two * characters (from both starting and ending). The resulting string should be converted in bold text, similarly as when the string is wrapped with two characters _, which should produce an italic string.
My code in React is the following:
import * as React from 'react';
export default function App() {
const [boldText, setBoldText] = React.useState('' as any);
const [res, setRes] = React.useState('' as any);
let speChar: any = '*_~`';
let openingTagsList: any = {
'*': '<b>',
_: '<i>',
'*_': '<b><i>',
'_*': '<b><i>',
};
let closingTagsList: any = {
'*': '</b>',
_: '</i>',
'*_': '</b></i>',
'_*': '</b></i>',
};
let openingTagsListKeys = Object?.keys(openingTagsList);
let closingTagsListKeys = Object?.keys(closingTagsList);
function strFont(e) {
let str = e.target.value;
let matchedSplChar = '';
for (let i = 0; i < str.length; i++) {
if (matchedSplChar.indexOf(str[i]) === -1) {
if (speChar.indexOf(str[i]) !== -1) matchedSplChar += str[i];
}
}
if (matchedSplChar as any) {
let FL = str[str.indexOf(matchedSplChar, 0)];
let SL = str[str.indexOf(matchedSplChar, 1)];
let startingTags;
let closingTags;
for (let key in openingTagsListKeys) {
if (matchedSplChar === openingTagsListKeys[key])
startingTags = openingTagsList[matchedSplChar];
}
for (let key in closingTagsListKeys) {
if (matchedSplChar === closingTagsListKeys[key])
closingTags = closingTagsList[matchedSplChar];
}
if (FL && SL && FL == SL) {
let replaceTags = str
.replace(FL, startingTags)
.replace(SL, closingTags);
let divTag = document.createElement('div');
divTag.innerHTML = replaceTags;
let htmlObj: any = divTag.firstChild;
if (htmlObj.innerHTML) setRes(htmlObj);
setBoldText(e.target.value);
} else {
setBoldText(e.target.value);
}
} else {
setBoldText(e.target.value);
}
}
return (
<div>
<input type="text" value={boldText || ''} onChange={(e) => strFont(e)} />
{res ? <res.tagName>{res?.innerHTML}</res.tagName> : ''}
<TextFormation />
</div>
);
}
, gives the output:
, instead of both strings being bold. How can I achieve it then?
From the above comments ...
"#KavyaPathak ... which effectively means that anything in between two * characters is going to be wrapped into , like e.g. ... foo *bar* *baz* *foo ... becoming ... foo <b>bar</b> <b> </b> <b>baz</b> <b> </b> *foo ... which renders ... "foo bar __ baz __ *foo"." – Peter Seliger
"#PeterSeliger yes" – Kavya Pathak
In case the OP's confirmation remains, the commented link to a regex and replace based approach already represents one possible solution.
Both regular expressions ...
/\*([^*]+)(?=\*)/g
/_([^_]+)(?=_)/g
... follow the same pattern.
match a control character (either * or _) ... \* respectively _ ..,
match and capture any character sequence which does not contain such a control character ... ([^*]+) respectively ([^_]+) ..,
until a positive lookahead ... (?=\*) respectively (?=_) ... confirms the presence of the next such control character (which excludes this very character from the entire match).
function getMarkupFromPseudoMarkdown(value) {
return value
.replace(/\*([^*]+)(?=\*)/g, '<b>$1</b> ')
.replace(/_([^_]+)(?=_)/g, '<i>$1</i> ')
.replace(/\n/g, '<br\/>')
.replace(/\s+/g, ' ')
.trim();
}
function displayCreatedMarkup({ currentTarget }) {
const markup = getMarkupFromPseudoMarkdown(currentTarget.value);
document.querySelector('code pre').textContent = markup;
document.querySelector('output').innerHTML = markup;
}
document
.querySelector('textarea')
.addEventListener('input', displayCreatedMarkup)
textarea, output { width: 49%; }
output { float: right; font-size: 87%; margin-top: 2px; }
code pre { background-color: #eee; white-space: break-spaces; word-break: break-all;}
<textarea cols="32" rows="8" placeholder="... put pseudo markdown here ..."></textarea>
<output></output>
<code><pre></pre></code>
And in case the OP figures that the above approach does not solve the OP's problem especially not for some bold/italic edge cases, then the OP might consider a mainly split and reduce based approach which handles such edge cases by looking up the previous (matchList[idx - 1]) and the next (matchList[idx + 1]) (control) character of a matching (neither * nor _) token.
function getMarkupFromPseudoMarkdown(value) {
return value
.split(/(\*)/)
.reduce((markup, match, idx, matchList) => {
if (match !== '*') {
if (
matchList[idx - 1] === '*' &&
matchList[idx + 1] === '*'
) {
markup = `${ markup } <b>${ match }</b> `;
} else {
markup = `${ markup }${ match }`;
}
}
return markup
})
.split(/(_)/)
.reduce((markup, match, idx, matchList) => {
if (match !== '_') {
if (
matchList[idx - 1] === '_' &&
matchList[idx + 1] === '_'
) {
markup = `${ markup } <i>${ match }</i> `;
} else {
markup = `${ markup }${ match }`;
}
}
return markup
})
.replace(/\n/g, '<br\/>')
.replace(/\s+/g, ' ')
.trim();
}
function displayCreatedMarkup({ currentTarget }) {
const markup = getMarkupFromPseudoMarkdown(currentTarget.value);
document.querySelector('code pre').textContent = markup;
document.querySelector('output').innerHTML = markup;
}
document
.querySelector('textarea')
.addEventListener('input', displayCreatedMarkup)
textarea, output { width: 49%; }
output { float: right; font-size: 87%; margin-top: 2px; }
code pre { background-color: #eee; white-space: break-spaces; word-break: break-all;}
<textarea cols="32" rows="8" placeholder="... put pseudo markdown here ..."></textarea>
<output></output>
<code><pre></pre></code>

Find key if text matches value

I'm trying to highlight only the number between the brackets in regular js. Colors are based on value (type of fruit in this scenario).
HTML
<a class="temple" href="something # URL">LARGE FRUIT (215)</a>
<a class="temple" href="something # URL">PINEAPPLE (38)</a>
<a class="temple" href="something # URL">RED APPLE (76)</a>
My Dict
var my_dict = {'BLUE':['ORANGE'], ['GRAPE']
'YELLOW':['PINEAPPLE'], ['KIWI']}
I could do them independently using but it's messy and may break the code if a tag is removed:
let Pineapple = document.querySelector('.temple')
PINEAPPLE.innerHTML = PINEAPPLE.innerHTML.replace(/\([^\)]*\)/, '<span class="red">$&</span>')
This is what I have so far:
function color(){
let fruits = document.querySelector('.temple')
for (let i = 0; i<fruits.length; i++) {
let str = fruits.innerHTML //this gives me the text I need.
My goal is to use the results (value) to find the key and ultimately color just the number.
that ?
const my_dict =
{ BLUE: [ 'ORANGE', 'GRAPE' ]
, YELLOW: [ 'PINEAPPLE', 'KIWI' ]
, RED: [ 'APPLE' ]
};
// ES5 code
const my_dict_reverse =
Object
.keys( my_dict)
.reduce( function(r,k)
{
my_dict[k].forEach( function(fruit) { r[fruit] = k } );
return r;
},{});
// my_dict_reverse = { ORANGE: 'BLUE', GRAPE: 'BLUE', PINEAPPLE: 'YELLOW', KIWI: 'YELLOW', APPLE: 'RED' }
document
.querySelectorAll('.temple')
.forEach( function(el)
{
let pos = el.textContent.search(/\([^\)]*\)/)
, fruit = el.textContent.slice(0,pos)
, val = el.textContent.slice(pos)
, colorClass = my_dict_reverse[ fruit.replace(/^\s+|\s+$/g,'')] || ''
;
el.innerHTML = fruit
+ '<span class="' + colorClass + '">'
+ val +'</span>';
});
/* ES10 code ...
const my_dict_reverse = Object.keys( my_dict).reduce((r,k)=>
{
my_dict[k].forEach(fruit=>r[fruit]=k)
return r
},{})
document.querySelectorAll('.temple').forEach(el=>
{
let [fruit, val] = el.textContent.split(/(?=\()|(?<=\))/)
, colorClass = my_dict_reverse[ fruit.trim()] ?? ''
;
el.innerHTML = `${fruit}<span class="${colorClass}">${val}</span>`
})
*/
body { background: steelblue; }
a.temple {
color : black;
float : left;
clear : both;
text-decoration : none;
}
span.RED { color : red; }
span.BLUE { color : blue; }
span.YELLOW { color : yellow; }
<a class="temple" href="something # URL">LARGE FRUIT (215)</a>
<a class="temple" href="something # URL">PINEAPPLE (38)</a>
<a class="temple" href="something # URL">APPLE (76)</a>

Json Pretty Print with syntactic coloration

I'm trying to pretty print my json data in html, and do some syntactic coloration.
But I'm having a little issue with empty values (empty list, empty string) in my code.
Here is the code :
if (!library)
var library = {};
function isInt(value) {
return !isNaN(value) && (function(x) { return (x | 0) === x; })(parseFloat(value))
};
library.json = {
replacer: function(match, pIndent, pKey, pVal, pEnd) {
var int = '<span class=json-int>';
var key = '<span class=json-key>';
var val = '<span class=json-value>';
var str = '<span class=json-string>';
var r = pIndent || '';
if (pKey)
r = r + key + pKey.replace(/[": ]/g, '') + '</span>: ';
if (pVal)
//r = r + (pVal[0] == '"'i ? str : val) + pVal + '</span>';
r = r + (isInt(pVal) ? int : str) + pVal + '</span>';
return r + (pEnd || '');
},
prettyPrint: function(obj) {
var jsonLine = /^( *)("[\w]+": )?("[^"]*"|[\w.+-]*)?([,[{])?$/mg;
return JSON.stringify(obj, null, 3)
.replace(/&/g, '&').replace(/\\"/g, '"')
.replace(/</g, '<').replace(/>/g, '>')
.replace(jsonLine, library.json.replacer);
}
};
var lint = {
"LintResult": "FAILED",
"CFN_NAG": [
{
"filename": "sam.yaml",
"file_results": {
"failure_count": 0,
"violations": []
}
}
],
"Comment": "If above CFN_NAG key has None value, check code execution log for errors/exceptions"
}
$('#lint').html(library.json.prettyPrint(lint));
//document.getElementById("json").innerHTML = JSON.stringify(data, undefined, 2);
pre {
background-color: ghostwhite;
bovrder: 1px solid silver;
padding: 10px 20px;
margin: 20px;
}
.json-key {
color: brown;
}
.json-value {
color: navy;
}
.json-string {
color: olive;
}
.json-int {
color: fuchsia;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div style="background-color:lightblue">
<h1>JSON Data:</h1>
<pre id="lint"></pre>
</div>
<p>A JSON string with 12 spaces per indentation.</p>
In the above code, the lint json variable has an empty list value for the violations item, and then this key is not print with the right color, it's just not processed.
I tried different way but I don't understand what is wrong.
You can try the code your self and will notice that the syntactic coloration doesn't work for this last item.
This might help you out:
function output(inp) {
document.body.appendChild(document.createElement('pre')).innerHTML = inp;
}
function syntaxHighlight(json) {
json = json.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
return json.replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, function (match) {
var cls = 'number';
if (/^"/.test(match)) {
if (/:$/.test(match)) {
cls = 'key';
} else {
cls = 'string';
}
} else if (/true|false/.test(match)) {
cls = 'boolean';
} else if (/null/.test(match)) {
cls = 'null';
}
return '<span class="' + cls + '">' + match + '</span>';
});
}
var obj = {
"LintResult": "FAILED",
"CFN_NAG": [
{
"filename": "sam.yaml",
"file_results": {
"failure_count": 0,
"violations": []
}
}
],
"Comment": "If above CFN_NAG key has None value, check code execution log for errors/exceptions"
};
var str = JSON.stringify(obj, undefined, 4);
output(syntaxHighlight(str));
pre {outline: 1px solid #ccc; padding: 5px; margin: 5px; background: ghostwhite }
.string { color: olive; }
.number { color: fuchsia; }
.boolean { color: navy; }
.null { color: magenta; }
.key { color: brown; }

Javascript/jQuery parse inline style as each object

I want to be able to parse inline css and have it as object in key pairs. Something like:
<div background-image: url('http://domain.com/images/image.jpg');background-size: cover;padding: 100px 0;">
{
backgroundImage : "http://domain.com/images/image.jpg",
backgroundSize: "cover",
padding: "100px 0"
}
This function works great for the most of the part. I'm having problem with background-image
it strips it completely and I end up with "url(http" instead.
function parseCss(el) {
var output = {};
if (!el || !el.attr('style')) {
return output;
}
var camelize = function camelize(str) {
return str.replace(/(?:^|[-])(\w)/g, function(a, c) {
c = a.substr(0, 1) === '-' ? c.toUpperCase() : c;
return c ? c : '';
});
}
var style = el.attr('style').split(';');
for (var i = 0; i < style.length; ++i) {
var rule = style[i].trim();
if (rule) {
var ruleParts = rule.split(':');
var key = camelize(ruleParts[0].trim());
output[key] = ruleParts[1].trim();
}
}
return output;
}
I'm pretty sure that "replace" function needs to be modified to make it work with image url
You might be able to do something like this, it would still fail for some edge cases with content. It is not running your camel case, but that is simple enough to call.
var x = document.getElementById("x");
var str = x.getAttribute("style"); //x.style.cssText;
console.log(str);
var rules = str.split(/\s*;\s*/g).reduce( function (details, val) {
if (val) {
var parts = val.match(/^([^:]+)\s*:\s*(.+)/);
details[parts[1]] = parts[2];
}
return details;
}, {});
console.log(rules);
div {
font-family: Arial;
}
<div style="color: red; background: yellow; background-image: url('http://domain.com/images/image.jpg');background-size: cover;padding: 100px 0;" id="x">test</div>
Instead of reading the the style attribute, you could iterate over the style properties. This way you avoid the problems with delimiters that are embedded in style values:
function parseCss(el) {
function camelize(key) {
return key.replace(/\-./g, function (m) {
return m[1].toUpperCase();
});
}
var output = {};
for (var a of el.style) {
output[camelize(a)] = el.style[a];
}
return output;
}
// Demo
var css = parseCss(document.querySelector('div'));
console.log(css);
<div style="background-image: url('http://domain.com/images/image.jpg');background-size: cover;padding: 100px 0;">
</div>
This does expand some consolidated styles you can have in the style attribute, such as padding, which splits into paddingLeft, paddingRight, ...etc.
With the use of some more ES6 features the above can be condensed to:
function parseCss(el) {
let camelize = key => key.replace(/\-./g, m => m[1].toUpperCase());
return Object.assign(
...Array.from(el.style, key => ({[camelize(key)]: el.style[key]})));
}
// Demo
let css = parseCss(document.querySelector('div'));
console.log(css);
<div style="background-image: url('http://domain.com/images/image.jpg');background-size: cover;padding: 100px 0;">
</div>
You can try with this, tested on few examples:
styleObj={};
style=$('div').attr('style');
rules=style.split(';');
rules = rules.filter(function(x){
return (x !== (undefined || ''));
}); // https://solidfoundationwebdev.com/blog/posts/remove-empty-elements-from-an-array-with-javascript
for (i=0;i<rules.length;i++) {
rules_arr=rules[i].split(/:(?!\/\/)/g); // split by : if not followed by //
rules_arr[1]=$.trim(rules_arr[1]).replace('url(','').replace(')','');
if(rules_arr[0].indexOf('-')>=0) {
rule=rules_arr[0].split('-');
rule=rule[0]+rule[1].charAt(0).toUpperCase()+rule[1].slice(1);
}
else {
rule=rules_arr[0];
}
styleObj[$.trim(rule)]=rules_arr[1];
}
console.log(styleObj);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div style="font-size: x-large; color: #ff9900; background-image: url('http://placehold.it/100x100');">Using inline style sheets - or is that inline styles?</div>
Demo (easier for testing of different inline styles): https://jsfiddle.net/n0n4zt3f/2/
P.S. Trimming and camel case are left... can be added, of course...

Javascript .replace() same character different outcome

I have a word : google, and I'm changing each character to a specific color, but I can't figure out how to make letters "o" different colors
This is my JS:
var text = $("#typed-strings").html().replace(/e/g, '<span class="red">e</span>').replace(/g/g, '<span class="blue">g</span>').replace(/l/g, '<span class="green">l</span>').replace(/o/g, '<span class="yellow">o</span>');
$("#typed-strings").html(text);
.red {
color: rgb(219, 50, 54);
}
.blue {
color: rgb(72, 133, 237);
}
.green {
color: rgb(60, 186, 84);
}
.yellow {
color: rgb(244, 194, 13);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="typed-strings">google</div>
maybe someone has ideas?
Thanks!
EDIT:
Thanks everyone for such a rich response, my question was edited by a moderator, perhaps that's why you've seen some changes.
I uploaded this tryout on heroku, to make things easier, but yeah, I have this long text, and every time letter "o" shows up I'd like it to either be, red or yellow, html doesn't really matter since it's purely for visualisation, but I've learned a lot from the conversations - thanks!
that's the app:
https://googlefonts.herokuapp.com
EDIT 2:
also added the non-working version within the page
As others have pointed out your issue is that you are replacing g with
<span class="...">g</span>
then replacing all l's in the last string which replaces the l in class.
Another way around this is to use a function for replace. While using a function each match is replaced in turn and any replacements you make are ignored.
Besides that you can use a separate key and boolean to track whether you have replaced the first o already or not. I added this to my example using the boolean as part of the replacement-key for the letter o to simplify things.
var replacements = {
g: '<span class="blue">g</span>',
o0: '<span class="red">o</span>',
o1: '<span class="yellow">o</span>',
l: '<span class="green">l</span>',
e: '<span class="red">e</span>'
};
var ele = $("#typed-strings");
var text = ele.html();
var firstODone = false;
text = text.replace(/[gogle]/g, function (letter) {
var key = letter;
if (key === 'o') {
key = key + (+firstODone); //Convert the boolean to an integer, 0 or 1
firstODone = true;
}
return replacements[key];
})
ele.html(text);
.red {
color: rgb(219, 50, 54);
}
.blue {
color: rgb(72, 133, 237);
}
.green {
color: rgb(60, 186, 84);
}
.yellow {
color: rgb(244, 194, 13);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="typed-strings">google</div>
If you only needed jQuery for this you can easily get rid of it, too. You can use
var ele = document.getElementById("typed-strings");
to get your element and
ele.innerHTML
to get and set your html to the element.
e.g.: ele.innerHTML = text
You might want to use the following regex:
letter + '{1}(?![^<]*\>)'
The above will replace only lowercase single letter G-s that are not inside a tag <>, here is an example:
function replaceWithSpanClass( string, letter, className ){
var regex = new RegExp( letter + '{1}(?![^<]*\>)', 'g' );
return string.replace( regex, '<span class="' + className + '">' + letter + '</span>')
}
var text = 'google';
text = replaceWithSpanClass( text, 'g', 'red' );
text = replaceWithSpanClass( text, 'o', 'blue' );
text = replaceWithSpanClass( text, 'l', 'yellow' );
text = replaceWithSpanClass( text, 'e', 'green' );
document.getElementById('result').innerHTML = text;
.yellow { color: yellow; }
.green { color: green; }
.blue { color: blue; }
.red { color: red; }
<div id="result"></div>
In this case it means that a class like yellow does not get its o replaced by <span class="blue">o</span>. That way you are sure you are only modifying outside your nodes.
<script type="text/javascript">
$(document).ready(() => {
let oldStr = $("#typed-strings").html();
let newStr = '';
for(let i = 0; i < oldStr.length; i++) {
if (oldStr[i] === 'g') {
newStr += '<span class="blue">g</span>'
} else if (oldStr[i] === 'o') {
newStr += '<span class="yellow">o</span>'
}
// Add other letters here
}
$("#typed-strings").html(newStr);
});
</script>
You can create a function that would return the color by the letter and use replace method with callback function like this:
var colors = {
g: 'blue',
o: 'yellow',
l: 'green',
e: 'red'
};
function wrapTheLetter(letter) {
var colorClass = colors[letter];
if (letter == 'o') {
// update it's color for the future
colors.o = 'grey';
}
return '<span class="' + colorClass + '">'+letter+'</span>';
}
var text = $("#typed-strings").html().replace(/\w/g, wrapTheLetter);
$("#typed-strings").html(text);

Categories