-2

I have an input like this:

var input = "text {{ variable*100 }} text {{ variable }} text {{ variable+2 }}";

and I have a variable :

var varible = 2;

I want to replace the text inside the {{}} with the value of the variable and calculate if needed

I'm expecting the result to be :

"text 200 text 2 text 102"

I've tried this code :

var tooltip = 'text {{ damage*100 }} text {{ damage }} text {{mana-1}}';

var objMap = {
    damage: 100,
    defence: 26,
    mana: 100,
}

var replaced = tooltip.replace(/(\{{.*?\}})/g, function (m, $1) {
    var key = $1.slice(2, -2).trim();
    return objMap[key];
});

console.log(replaced)

and I got this result :

text undefined text 100 text undefined
Amine Beihaqi
  • 177
  • 2
  • 8

1 Answers1

0

It's possible to dynamically parse out the variable names from the other math done inside the brackets and then eval the result (making sure it only contains the sorts of characters permitted in a plain math expression first, so that it's safe):

var tooltip = 'text {{ damage*100 }} text {{ damage }} text {{mana-1}}';

var objMap = {
    damage: 100,
    defence: 26,
    mana: 100,
}

var replaced = tooltip.replace(/\{{(.*?)\}}/g, (_, innerText) => {
    const innerTextReplaced = innerText.replace(/[a-z]+/gi, word => objMap[word]);
    // make sure the text is now only a plain math expression
    // with no characters other than numbers and operators
    if (!innerTextReplaced.match(/^[ \d()\/*+-]+$/)) {
      throw new Error('Potentially unsafe input detected');
    }
    return eval(innerTextReplaced);
});

console.log(replaced)

If you don't want to use eval, I suppose you can use new Function, but it's unlikely to matter:

var tooltip = 'text {{ damage*100 }} text {{ damage }} text {{mana-1}}';

var objMap = {
    damage: 100,
    defence: 26,
    mana: 100,
}

var replaced = tooltip.replace(/\{{(.*?)\}}/g, (_, innerText) => {
    const innerTextReplaced = innerText.replace(/[a-z]+/gi, word => objMap[word]);
    // make sure the text is now only a plain math expression
    // with no characters other than numbers and operators
    if (!innerTextReplaced.match(/^[ \d()\/*+-]+$/)) {
      throw new Error('Potentially unsafe input detected');
    }
    return Function('return ' + innerTextReplaced)();
});

console.log(replaced)

But if at all possible, I'd much prefer a function instead of a string for the input:

var objMap = {
    damage: 100,
    defence: 26,
    mana: 100,
}

const makeTooltip = ({ damage, defence, mana }) => `text ${ damage*100 } text ${ damage } text ${mana-1}`;
console.log(makeTooltip(objMap));
CertainPerformance
  • 356,069
  • 52
  • 309
  • 320
  • Worth noting that this is incredibly insecure – Evert Mar 21 '21 at 01:20
  • thanks using `eval` solve my problem i cant use es6 literals bc the text is from an API request – Amine Beihaqi Mar 21 '21 at 01:25
  • Please read [Never use eval()!](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval#never_use_eval!) – Thomas Sablik Mar 21 '21 at 01:25
  • @ThomasSablik If the input can be sanitized - which isn't hard - I don't think there's anything wrong with it. https://stackoverflow.com/q/6479236 unless this runs in a really tight loop – CertainPerformance Mar 21 '21 at 01:30
  • Do you really expect a beginner to sanitize the input. You didn't even mention it in your answer. You gave a child a gun to play with. – Thomas Sablik Mar 21 '21 at 01:32
  • @ThomasSablik I said `(making sure it only contains the sorts of characters permitted in a plain math expression first, so that it's safe)` and also in a comment `// make sure the text is now only a plain math expression` `// with no characters other than numbers and operators` and a demonstration with `if (!innerTextReplaced.match(/^[ \d()\/*+-]+$/)) {`. – CertainPerformance Mar 21 '21 at 01:33
  • Ok sorry. You gave a child a gun to play with and said: "Be careful. Don't kill yourself" – Thomas Sablik Mar 21 '21 at 01:38
  • @ThomasSablik the input is a fetch from an API the API is safe im using the Function constructor now because I read in [Never use eval()!](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval#never_use_eval!) that `eval()` is unsafe – Amine Beihaqi Mar 21 '21 at 01:45
  • 1
    @AmineBeihaqi Both `eval` and `new Function` have the same security vulnerabilities, both of which can be remedied by making sure the input is trustworthy first, which the `innerTextReplaced.match(/^[ \d()\/*+-]+$/)` properly detects and fixes. – CertainPerformance Mar 21 '21 at 01:47
  • @AminaBeihaqi Network data and user input are never trustworthy. And that you didn't know it proves my statement. – Thomas Sablik Mar 21 '21 at 02:11