Use a variable inside a CDATA block, in Javascript? - javascript

CDATA-blocks work great for coding large blocks of HTML, or CSS, into strings. But, I can't figure out how to use a variable-value within one.
For example, consider this JavaScript code:
var FullName = "Friedrich Hayek";
var ProfileCode = (<><![CDATA[
<div id="BigHonkingChunkO_HTML">
...Lot's o' code...
Name: $FullName$
Birth: $Birthdate$
...Lot's o' code...
... $FullName$ ...
...Lot's o' code...
</div>
]]></>).toString ();
How do I get $FullName$ to render as "Friedrich Hayek" instead of "$FullName$"?
Note that there is more than one variable and each variable can be used a few times in the CDATA block.
Alternate code sample:
var UserColorPref = "red";
var UI_CSS = (<><![CDATA[
body {
color: $UserColorPref$;
}
]]></>).toString ();
Looking to set the color attribute to red.

Is it elegant enough ?
function replaceVars(content, vars)
{
return content.replace(/\$(\w+)\$/g, function($0, $1)
{
return ($1 in vars ? vars[$1] : $0);
});
}
ProfileCode = replaceVars(ProfileCode, {"FullName" : "Friedrich Hayek"});
In case associative keys don't really matter, you can always opt to use:
sprintf or vsprintf
EDIT
What about making the 2nd parameter optional ?
function replaceVars(content, scope) {
if (!scope || typeof scope != "object") {
scope = this;
}
return content.replace(/\$(\w+)\$/g, function($0, $1) {
return ($1 in scope ? scope[$1] : $0);
});
}
// example1
var FullName = "Friedrich Hayek";
ProfileCode = replaceVars(ProfileCode);
// example2
var vars = {"FullName" : "Friedrich Hayek"};
ProfileCode = replaceVars(ProfileCode, vars);

ProfileCode=ProfileCode.replace('$FullName$',FullName);

After scouring the CDATA spec, and this "CDATA Confusion" article, it seems that CDATA sections treat everything as pure text, except for character-data entities and the section end marker (]]>). For example,
var x = $('<!DOCTYPE X[<!ENTITY foo "BAR">]><z> cc A &foo;</z>');
console.log ($(x, 'z').text() );
Yields: ]> cc A &foo;
So, there's no way to have variable substitution within a CDATA section. The best we can do is start and stop the sections, like so:
var FullName = "Friedrich Hayek";
var ProfileCode = (<><![CDATA[
<div id="BigHonkingChunkO_HTML">
...Lot's o' code...
Name: ]]></>).toString () + FullName+ (<><![CDATA[
...Lot's o' code...
</div>
]]></>).toString ();
console.log (ProfileCode);
-- which is obviously not acceptable.
Practical workaround:
It won't help anyone looking for a CDATA solution (which we now know is impossible, per the spec). But as we were just using CDATA as a method to generate a complex string, then we can clean the string up afterwards, per Ratna Dinakar's answer.
The function we ended up using is:
function sSetVarValues (sSrcStr, sReplaceList /* , Variable */)
/*--- function sSetVarValues takes a string and substitutes marked
locations with the values of the variables represented.
Conceptually, sSetVarValues() operates a little like sprintf().
Parameters:
sSrcStr -- The source string to be replaced.
sReplaceList -- A string containing a comma-separated list of variable
names expected in the raw string. For example, if
sReplaceList was "Var_A", we would expect (but not require)
that sSrcStr contained one or more "$Var_A$" substrings.
*Variable* -- A variable-length set of parameters, containing the values
of the variables specified in sReplaceList. For example,
if sReplaceList was "Var_A, Var_B, Var_C", then there better
be 3 parameters after sReplaceList in the function call.
Returns: The replaced string.
*/
{
if (!sSrcStr) return null;
if (!sReplaceList) return null;
var aReplaceList = sReplaceList.split (/,\s?/);
for (var J = aReplaceList.length-1; J >= 0; --J)
{
var zRepVar = new RegExp ('\\$' + aReplaceList[J] + '\\$', "g");
sSrcStr = sSrcStr.replace (zRepVar, arguments[J+2]);
}
return sSrcStr;
}
Sample use:
var AAA = 'first';
var BBB = 'second';
var CCC = 'third';
var Before = "1 is $AAA$, 2 is $BBB$, 3 is $CCC$";
var After = sSetVarValues (Before, "AAA, BBB, CCC", AAA, BBB, CCC);
console.log (Before);
console.log (After);
Yields:
1 is $AAA$, 2 is $BBB$, 3 is $CCC$
1 is first, 2 is second, 3 is third

Related

How transcribe remove number custom function Excel VBA function to Google Sheets Google Script (Javascript)

I need to transcribe a VBA script to Javascript but i got stuck in the java part
The main meaning is to remove numbers from a text like "texthere 123456789"
My VBA code is:
Function RemoveNum(Txt As String) As String
With CreateObject("VBScript.RegExp")
.Global = True
.Pattern = "[0-9]"
RemoveNum = .Replace(Txt, "")
End With
End Function
My Javascript attempt was:
function RemoveNumbers(RemoveNumbers) {
var RemoveNumbers;
//var str = RemoveNumbers.toString();
var str = RemoveNumbers;
str.Value.replace(/[0-9]/g, '');
}
Or even:
function rn(remvnum) {
var str = remvnum;
var n = str.toString();
var res = n.replace(/[0-9]/gmi, '');
}
What does stop me reaching the result is, the .Replace function needs to be string content otherwise will return error of undefined value, also I can't convert toString because it returns error of undefined value.
This example bellow works well as the name of the function is written in the Google Sheet cell as a custom function, but I didn't achieve the remove number desire:
function styleHyphenFormat(propertyName) {
function upperToHyphenLower(match, offset, string) {
return (offset ? '-' : '') + match.toLowerCase();
}
return propertyName.replace(/[A-Z]/g, upperToHyphenLower);
}
Does someone knows what I did wrong?
Thanks in advance.
Try removeNumbers = removeNumbers.replace(/[0-9]/g, ''); or set a new var like noNums = removeNumbers.replace(/[0-9]/g, '');
You just need to return the replaced value:
function rn(remvnum) {
//var str = remvnum;
//var n = str.toString();
var res = remvnum.toString().replace(/[0-9]/gmi, '');
return res; //Added
}
Google sheets supports regex(re2) as a built-in function unlike excel. So, You don't need macros. Even if you want a global replace, The Find and replace menu works well with regex.
=REGEXREPLACE(A2,"\d",)
or
=ARRAYFORMULA(REGEXREPLACE(A2:A5,"\d",))
\d is digit(==[0-9])

Where and why would you use tagged template literals? [duplicate]

I understand the syntax of ES6 tagged templates. What I don't see is the practical usability. When is it better than passing an object parameter, like the settings in jQuery's AJAX? $.ajax('url', { /*this guy here*/ })
Right now I only see the tricky syntax but I don't see why I would need/use it. I also found that the TypeScript team chose to implement it (in 1.5) before other important features. What is the concept behind tagged string templates?
You can use tagged templates to build APIs that are more expressive than regular function calls.
For example, I'm working on a proof-of-concept library for SQL queries on JS arrays:
let admins = sql`SELECT name, id FROM ${users}
WHERE ${user => user.roles.indexOf('admin') >= 0}`
Notice it has nothing to do with String interpolation; it uses tagged templates for readability. It would be hard to construct something that reads as intuitively with plain function calls - I guess you'd have something like this:
let admins = sql("SELECT name, id FROM $users WHERE $filter",
{ $users: users, $filter: (user) => user.roles.contains('admin') })
This example is just a fun side project, but I think it shows some of the benefits of tagged templates.
Another example, maybe more obvious, is i18n - a tagged template could insert locale-sensitive versions of your input.
See Sitepoint's explanation:
The final stage of template strings specification is about adding a custom function before the string itself to create a tagged template string.
...
For instance, here is a piece of code to block strings that try to inject custom DOM elements:
var items = [];
items.push("banana");
items.push("tomato");
items.push("light saber");
var total = "Trying to hijack your site <BR>";
var myTagFunction = function (strings,...values) {
var output = "";
for (var index = 0; index < values.length; index++) {
var valueString = values[index].toString();
if (valueString.indexOf(">") !== -1) {
// Far more complex tests can be implemented here :)
return "String analyzed and refused!";
}
output += strings[index] + values[index];
}
output += strings[index]
return output;
}
result.innerHTML = myTagFunction `You have ${items.length} item(s) in your basket for a total of $${total}`;
Tagged template strings can used for a lot of things like security, localization, creating your own domain specific language, etc.
They're useful because the function can (almost) completely define the meaning of the text inside it (almost = other than placeholders). I like to use the example of Steven Levithan's XRegExp library. It's awkward to use regular expressions defined as strings, because you have to double-escape things: Once for the string literal, and once for regex. This is one of the reasons we have regular expression literals in JavaScript.
For instance, suppose I'm doing maintenance on a site and I find this:
var isSingleUnicodeWord = /^\w+$/;
...which is meant to check if a string contains only "letters." Two problems: A) There are thousands of "word" characters across the realm of human language that \w doesn't recognize, because its definition is English-centric; and B) It includes _, which many (including the Unicode consortium) would argue is not a "letter."
Suppose in my work I've introduced XRegExp to the codebase. Since I know it supports \pL (\p for Unicode categories, and L for "letter"), I might quickly swap this in:
var isSingleUnicodeWord = XRegExp("^\pL+$"); // WRONG
Then I wonder why it didn't work, *facepalm*, and go back and escape that backslash, since it's being consumed by the string literal:
var isSingleUnicodeWord = XRegExp("^\\pL+$");
// ---------------------------------^
What a pain. Suppose I could write the actual regular expression without worrying about double-escaping?
I can: With a tagged template function. I can put this in my standard lib:
function xrex(strings, ...values) {
const raw = strings.raw;
let result = "";
for (let i = 0; i < raw.length; ++i) {
result += raw[i];
if (i < values.length) { // `values` always has one fewer entry
result += values[i];
}
}
return XRegExp(result);
}
Or alternately, this is a valid use case for reduce, and we can use destructuring in the argument list:
function xrex({raw}, ...values) {
return XRegExp(
raw.reduce(
(acc, str, index) => acc + str + (index < values.length ? values[index] : ""),
""
)
);
}
And then I can happily write:
const isSingleUnicodeWord = xrex`^\pL+$`;
Example:
// My tag function (defined once, then reused)
function xrex({raw}, ...values) {
const result = raw.reduce(
(acc, str, index) => acc + str + (index < values.length ? values[index] : ""),
""
);
console.log("Creating with:", result);
return XRegExp(result);
}
// Using it, with a couple of substitutions to prove to myself they work
let category = "L"; // L: Letter
let maybeEol = "$";
let isSingleUnicodeWord = xrex`^\p${category}+${maybeEol}`;
function test(str) {
console.log(str + ": " + isSingleUnicodeWord.test(str));
}
test("Русский"); // true
test("日本語"); // true
test("العربية"); // true
test("foo bar"); // false
test("$£"); // false
<script src="https://cdnjs.cloudflare.com/ajax/libs/xregexp/3.2.0/xregexp-all.min.js"></script>
The only thing I have to remember now is that ${...} is special because it's a placeholder. In this specific case, it's not a problem, I'm unlikely to want to apply a quantifier to the end-of-input assertion, but that's a coincidence...

Es6 nested backticks to interpolate variable's template substitutions [duplicate]

Is it possible to create a template string as a usual string,
let a = "b:${b}";
and then convert it into a template string,
let b = 10;
console.log(a.template()); // b:10
without eval, new Function and other means of dynamic code generation?
In my project I've created something like this with ES6:
String.prototype.interpolate = function(params) {
const names = Object.keys(params);
const vals = Object.values(params);
return new Function(...names, `return \`${this}\`;`)(...vals);
}
const template = 'Example text: ${text}';
const result = template.interpolate({
text: 'Foo Boo'
});
console.log(result);
As your template string must get reference to the b variable dynamically (in runtime), so the answer is: NO, it's impossible to do it without dynamic code generation.
But, with eval it's pretty simple:
let tpl = eval('`'+a+'`');
No, there is not a way to do this without dynamic code generation.
However, I have created a function which will turn a regular string into a function which can be provided with a map of values, using template strings internally.
Generate Template String Gist
/**
* Produces a function which uses template strings to do simple interpolation from objects.
*
* Usage:
* var makeMeKing = generateTemplateString('${name} is now the king of ${country}!');
*
* console.log(makeMeKing({ name: 'Bryan', country: 'Scotland'}));
* // Logs 'Bryan is now the king of Scotland!'
*/
var generateTemplateString = (function(){
var cache = {};
function generateTemplate(template){
var fn = cache[template];
if (!fn){
// Replace ${expressions} (etc) with ${map.expressions}.
var sanitized = template
.replace(/\$\{([\s]*[^;\s\{]+[\s]*)\}/g, function(_, match){
return `\$\{map.${match.trim()}\}`;
})
// Afterwards, replace anything that's not ${map.expressions}' (etc) with a blank string.
.replace(/(\$\{(?!map\.)[^}]+\})/g, '');
fn = Function('map', `return \`${sanitized}\``);
}
return fn;
}
return generateTemplate;
})();
Usage:
var kingMaker = generateTemplateString('${name} is king!');
console.log(kingMaker({name: 'Bryan'}));
// Logs 'Bryan is king!' to the console.
Hope this helps somebody. If you find a problem with the code, please be so kind as to update the Gist.
What you're asking for here:
//non working code quoted from the question
let b=10;
console.log(a.template());//b:10
is exactly equivalent (in terms of power and, er, safety) to eval: the ability to take a string containing code and execute that code; and also the ability for the executed code to see local variables in the caller's environment.
There is no way in JS for a function to see local variables in its caller, unless that function is eval(). Even Function() can't do it.
When you hear there's something called "template strings" coming to JavaScript, it's natural to assume it's a built-in template library, like Mustache. It isn't. It's mainly just string interpolation and multiline strings for JS. I think this is going to be a common misconception for a while, though. :(
There are many good solutions posted here, but none yet which utilizes the ES6 String.raw method. Here is my contriubution. It has an important limitation in that it will only accept properties from a passed in object, meaning no code execution in the template will work.
function parseStringTemplate(str, obj) {
let parts = str.split(/\$\{(?!\d)[\wæøåÆØÅ]*\}/);
let args = str.match(/[^{\}]+(?=})/g) || [];
let parameters = args.map(argument => obj[argument] || (obj[argument] === undefined ? "" : obj[argument]));
return String.raw({ raw: parts }, ...parameters);
}
let template = "Hello, ${name}! Are you ${age} years old?";
let values = { name: "John Doe", age: 18 };
parseStringTemplate(template, values);
// output: Hello, John Doe! Are you 18 years old?
Split string into non-argument textual parts. See regex.
parts: ["Hello, ", "! Are you ", " years old?"]
Split string into property names. Empty array if match fails.
args: ["name", "age"]
Map parameters from obj by property name. Solution is limited by shallow one level mapping. Undefined values are substituted with an empty string, but other falsy values are accepted.
parameters: ["John Doe", 18]
Utilize String.raw(...) and return result.
TLDR:
https://jsfiddle.net/bj89zntu/1/
Everyone seems to be worried about accessing variables. Why not just pass them? I'm sure it won't be too hard to get the variable context in the caller and pass it down. Use
ninjagecko's answer to get the props from obj.
function renderString(str,obj){
return str.replace(/\$\{(.+?)\}/g,(match,p1)=>{return index(obj,p1)})
}
Here is the full code:
function index(obj,is,value) {
if (typeof is == 'string')
is=is.split('.');
if (is.length==1 && value!==undefined)
return obj[is[0]] = value;
else if (is.length==0)
return obj;
else
return index(obj[is[0]],is.slice(1), value);
}
function renderString(str,obj){
return str.replace(/\$\{.+?\}/g,(match)=>{return index(obj,match)})
}
renderString('abc${a}asdas',{a:23,b:44}) //abc23asdas
renderString('abc${a.c}asdas',{a:{c:22,d:55},b:44}) //abc22asdas
The issue here is to have a function that has access to the variables of its caller. This is why we see direct eval being used for template processing. A possible solution would be to generate a function taking formal parameters named by a dictionary's properties, and calling it with the corresponding values in the same order. An alternative way would be to have something simple as this:
var name = "John Smith";
var message = "Hello, my name is ${name}";
console.log(new Function('return `' + message + '`;')());
And for anyone using Babel compiler we need to create closure which remembers the environment in which it was created:
console.log(new Function('name', 'return `' + message + '`;')(name));
I liked s.meijer's answer and wrote my own version based on his:
function parseTemplate(template, map, fallback) {
return template.replace(/\$\{[^}]+\}/g, (match) =>
match
.slice(2, -1)
.trim()
.split(".")
.reduce(
(searchObject, key) => searchObject[key] || fallback || match,
map
)
);
}
Similar to Daniel's answer (and s.meijer's gist) but more readable:
const regex = /\${[^{]+}/g;
export default function interpolate(template, variables, fallback) {
return template.replace(regex, (match) => {
const path = match.slice(2, -1).trim();
return getObjPath(path, variables, fallback);
});
}
//get the specified property or nested property of an object
function getObjPath(path, obj, fallback = '') {
return path.split('.').reduce((res, key) => res[key] || fallback, obj);
}
Note: This slightly improves s.meijer's original, since it won't match things like ${foo{bar} (the regex only allows non-curly brace characters inside ${ and }).
UPDATE: I was asked for an example using this, so here you go:
const replacements = {
name: 'Bob',
age: 37
}
interpolate('My name is ${name}, and I am ${age}.', replacements)
#Mateusz Moska, solution works great, but when i used it in React Native(build mode), it throws an error: Invalid character '`', though it works when i run it in debug mode.
So i wrote down my own solution using regex.
String.prototype.interpolate = function(params) {
let template = this
for (let key in params) {
template = template.replace(new RegExp('\\$\\{' + key + '\\}', 'g'), params[key])
}
return template
}
const template = 'Example text: ${text}',
result = template.interpolate({
text: 'Foo Boo'
})
console.log(result)
Demo: https://es6console.com/j31pqx1p/
NOTE: Since I don't know the root cause of an issue, i raised a ticket in react-native repo, https://github.com/facebook/react-native/issues/14107, so that once they can able to fix/guide me about the same :)
You can use the string prototype, for example
String.prototype.toTemplate=function(){
return eval('`'+this+'`');
}
//...
var a="b:${b}";
var b=10;
console.log(a.toTemplate());//b:10
But the answer of the original question is no way.
I required this method with support for Internet Explorer. It turned out the back ticks aren't supported by even IE11. Also; using eval or it's equivalent Function doesn't feel right.
For the one that notice; I also use backticks, but these ones are removed by compilers like babel. The methods suggested by other ones, depend on them on run-time. As said before; this is an issue in IE11 and lower.
So this is what I came up with:
function get(path, obj, fb = `$\{${path}}`) {
return path.split('.').reduce((res, key) => res[key] || fb, obj);
}
function parseTpl(template, map, fallback) {
return template.replace(/\$\{.+?}/g, (match) => {
const path = match.substr(2, match.length - 3).trim();
return get(path, map, fallback);
});
}
Example output:
const data = { person: { name: 'John', age: 18 } };
parseTpl('Hi ${person.name} (${person.age})', data);
// output: Hi John (18)
parseTpl('Hello ${person.name} from ${person.city}', data);
// output: Hello John from ${person.city}
parseTpl('Hello ${person.name} from ${person.city}', data, '-');
// output: Hello John from -
I currently can't comment on existing answers so I am unable to directly comment on Bryan Raynor's excellent response. Thus, this response is going to update his answer with a slight correction.
In short, his function fails to actually cache the created function, so it will always recreate, regardless of whether it's seen the template before. Here is the corrected code:
/**
* Produces a function which uses template strings to do simple interpolation from objects.
*
* Usage:
* var makeMeKing = generateTemplateString('${name} is now the king of ${country}!');
*
* console.log(makeMeKing({ name: 'Bryan', country: 'Scotland'}));
* // Logs 'Bryan is now the king of Scotland!'
*/
var generateTemplateString = (function(){
var cache = {};
function generateTemplate(template){
var fn = cache[template];
if (!fn){
// Replace ${expressions} (etc) with ${map.expressions}.
var sanitized = template
.replace(/\$\{([\s]*[^;\s\{]+[\s]*)\}/g, function(_, match){
return `\$\{map.${match.trim()}\}`;
})
// Afterwards, replace anything that's not ${map.expressions}' (etc) with a blank string.
.replace(/(\$\{(?!map\.)[^}]+\})/g, '');
fn = cache[template] = Function('map', `return \`${sanitized}\``);
}
return fn;
};
return generateTemplate;
})();
Still dynamic but seems more controlled than just using a naked eval:
const vm = require('vm')
const moment = require('moment')
let template = '### ${context.hours_worked[0].value} \n Hours worked \n #### ${Math.abs(context.hours_worked_avg_diff[0].value)}% ${fns.gt0(context.hours_worked_avg_diff[0].value, "more", "less")} than usual on ${fns.getDOW(new Date())}'
let context = {
hours_worked:[{value:10}],
hours_worked_avg_diff:[{value:10}],
}
function getDOW(now) {
return moment(now).locale('es').format('dddd')
}
function gt0(_in, tVal, fVal) {
return _in >0 ? tVal: fVal
}
function templateIt(context, template) {
const script = new vm.Script('`'+template+'`')
return script.runInNewContext({context, fns:{getDOW, gt0 }})
}
console.log(templateIt(context, template))
https://repl.it/IdVt/3
I made my own solution doing a type with a description as a function
export class Foo {
...
description?: Object;
...
}
let myFoo:Foo = {
...
description: (a,b) => `Welcome ${a}, glad to see you like the ${b} section`.
...
}
and so doing:
let myDescription = myFoo.description('Bar', 'bar');
I came up with this implementation and it works like a charm.
function interpolateTemplate(template: string, args: any): string {
return Object.entries(args).reduce(
(result, [arg, val]) => result.replace(`$\{${arg}}`, `${val}`),
template,
)
}
const template = 'This is an example: ${name}, ${age} ${email}'
console.log(interpolateTemplate(template,{name:'Med', age:'20', email:'example#abc.com'}))
You could raise an error if arg is not found in template
This solution works without ES6:
function render(template, opts) {
return new Function(
'return new Function (' + Object.keys(opts).reduce((args, arg) => args += '\'' + arg + '\',', '') + '\'return `' + template.replace(/(^|[^\\])'/g, '$1\\\'') + '`;\'' +
').apply(null, ' + JSON.stringify(Object.keys(opts).reduce((vals, key) => vals.push(opts[key]) && vals, [])) + ');'
)();
}
render("hello ${ name }", {name:'mo'}); // "hello mo"
Note: the Function constructor is always created in the global scope, which could potentially cause global variables to be overwritten by the template, e.g. render("hello ${ someGlobalVar = 'some new value' }", {name:'mo'});
You should try this tiny JS module, by Andrea Giammarchi, from github :
https://github.com/WebReflection/backtick-template
/*! (C) 2017 Andrea Giammarchi - MIT Style License */
function template(fn, $str, $object) {'use strict';
var
stringify = JSON.stringify,
hasTransformer = typeof fn === 'function',
str = hasTransformer ? $str : fn,
object = hasTransformer ? $object : $str,
i = 0, length = str.length,
strings = i < length ? [] : ['""'],
values = hasTransformer ? [] : strings,
open, close, counter
;
while (i < length) {
open = str.indexOf('${', i);
if (-1 < open) {
strings.push(stringify(str.slice(i, open)));
open += 2;
close = open;
counter = 1;
while (close < length) {
switch (str.charAt(close++)) {
case '}': counter -= 1; break;
case '{': counter += 1; break;
}
if (counter < 1) {
values.push('(' + str.slice(open, close - 1) + ')');
break;
}
}
i = close;
} else {
strings.push(stringify(str.slice(i)));
i = length;
}
}
if (hasTransformer) {
str = 'function' + (Math.random() * 1e5 | 0);
if (strings.length === values.length) strings.push('""');
strings = [
str,
'with(this)return ' + str + '([' + strings + ']' + (
values.length ? (',' + values.join(',')) : ''
) + ')'
];
} else {
strings = ['with(this)return ' + strings.join('+')];
}
return Function.apply(null, strings).apply(
object,
hasTransformer ? [fn] : []
);
}
template.asMethod = function (fn, object) {'use strict';
return typeof fn === 'function' ?
template(fn, this, object) :
template(this, fn);
};
Demo (all the following tests return true):
const info = 'template';
// just string
`some ${info}` === template('some ${info}', {info});
// passing through a transformer
transform `some ${info}` === template(transform, 'some ${info}', {info});
// using it as String method
String.prototype.template = template.asMethod;
`some ${info}` === 'some ${info}'.template({info});
transform `some ${info}` === 'some ${info}'.template(transform, {info});
Faz assim (This way):
let a = 'b:${this.b}'
let b = 10
function template(templateString, templateVars) {
return new Function('return `' + templateString + '`').call(templateVars)
}
result.textContent = template(a, {b})
<b id=result></b>
Since we're reinventing the wheel on something that would be a lovely feature in javascript.
I use eval(), which is not secure, but javascript is not secure. I readily admit that I'm not excellent with javascript, but I had a need, and I needed an answer so I made one.
I chose to stylize my variables with an # rather than an $, particularly because I want to use the multiline feature of literals without evaluating til it's ready. So variable syntax is #{OptionalObject.OptionalObjectN.VARIABLE_NAME}
I am no javascript expert, so I'd gladly take advice on improvement but...
var prsLiteral, prsRegex = /\#\{(.*?)(?!\#\{)\}/g
for(i = 0; i < myResultSet.length; i++) {
prsLiteral = rt.replace(prsRegex,function (match,varname) {
return eval(varname + "[" + i + "]");
// you could instead use return eval(varname) if you're not looping.
})
console.log(prsLiteral);
}
A very simple implementation follows
myResultSet = {totalrecords: 2,
Name: ["Bob", "Stephanie"],
Age: [37,22]};
rt = `My name is #{myResultSet.Name}, and I am #{myResultSet.Age}.`
var prsLiteral, prsRegex = /\#\{(.*?)(?!\#\{)\}/g
for(i = 0; i < myResultSet.totalrecords; i++) {
prsLiteral = rt.replace(prsRegex,function (match,varname) {
return eval(varname + "[" + i + "]");
// you could instead use return eval(varname) if you're not looping.
})
console.log(prsLiteral);
}
In my actual implementation, I choose to use #{{variable}}. One more set of braces. Absurdly unlikely to encounter that unexpectedly. The regex for that would look like /\#\{\{(.*?)(?!\#\{\{)\}\}/g
To make that easier to read
\#\{\{ # opening sequence, #{{ literally.
(.*?) # capturing the variable name
# ^ captures only until it reaches the closing sequence
(?! # negative lookahead, making sure the following
# ^ pattern is not found ahead of the current character
\#\{\{ # same as opening sequence, if you change that, change this
)
\}\} # closing sequence.
If you're not experienced with regex, a pretty safe rule is to escape every non-alphanumeric character, and don't ever needlessly escape letters as many escaped letters have special meaning to virtually all flavors of regex.
You can refer to this solution
const interpolate = (str) =>
new Function(`return \`${new String(str)}\`;`)();
const foo = 'My';
const obj = {
text: 'Hanibal Lector',
firstNum: 1,
secondNum: 2
}
const str = "${foo} name is : ${obj.text}. sum = ${obj.firstNum} + ${obj.secondNum} = ${obj.firstNum + obj.secondNum}";
console.log(interpolate(str));
I realize I am late to the game, but you could:
const a = (b) => `b:${b}`;
let b = 10;
console.log(a(b)); // b:10

Javascript splitting string using only last splitting parameter

An example of what im trying to get:
String1 - 'string.co.uk' - would return 'string' and 'co.uk'
String2 - 'random.words.string.co.uk' - would return 'string` and 'co.uk'
I currently have this:
var split= [];
var tld_part = domain_name.split(".");
var sld_parts = domain_name.split(".")[0];
tld_part = tld_part.slice(1, tld_part.length);
split.push(sld_parts);
split.push(tld_part.join("."));
With my current code, it takes the split parameter from the beginning, i want to reverse it if possible. With my current code it does this:
String1 - 'string.co.uk' - returns 'string' and 'co.uk'
String2 - 'random.words.string.co.uk' - would return 'random` and 'words.string.co.uk'
Any suggestions?
To expand upon elclanrs comment:
function getParts(str) {
var temp = str.split('.').slice(-3) // grabs the last 3 elements
return {
tld_parts : [temp[1],temp[2]].join("."),
sld_parts : temp[0]
}
}
getParts("foo.bar.baz.co.uk") would return { tld_parts : "co.uk", sld_parts : "baz" }
and
getParts("i.got.99.terms.but.a.bit.aint.one.co.uk") would return { tld_parts : "co.uk", sld_parts : "one" }
try this
var str='string.co.uk'//or 'random.words.string.co.uk'
var part = str.split('.');
var result = part[part.length - 1].toString() + '.' + part[part.length - 1].toString();
alert(result);
One way that comes to mind is the following
var tld_part = domain_name.split(".");
var name = tld_part[tld_part.length - 2];
var tld = tld_part[tld_part.length - 1] +"."+ tld_part[tld_part.length];
Depending on your use case, peforming direct splits might not be a good idea — for example, how would the above code handle .com or even just localhost? In this respect I would go down the RegExp route:
function stripSubdomains( str ){
var regs; return (regs = /([^.]+)(\.co)?(\.[^.]+)$/i.exec( str ))
? regs[1] + (regs[2]||'') + regs[3]
: str
;
};
Before the Regular Expression Police attack reprimand me for not being specific enough, a disclaimer:
The above can be tightened as a check against domain names by rather than checking for ^., to check for the specific characters allowed in a domain at that point. However, my own personal perspective on matters like these is to be more open at the point of capture, and be tougher from a filtering point at a later date... This allows you to keep an eye on what people might be trying, because you can never be 100% certain your validation isn't blocking valid requests — unless you have an army of user testers at your disposal. At the end of the day, it all depends on where this code is being used, so the above is an illustrated example only.

Javascript RegEx Mysteries (for a poor C programmer)

This is clearly a RTFM issue, but after I did so repeatedly I just can't get the damn thing to work so there are times when asking for help makes sense:
var text = "KEY:01 VAL:1.10,KEY:02 VAL:2.20,KEY:03 VAL:3.30";
var pattern = '/KEY:(\S+) VAL:([^,]+)/g';
//var pattern = '/KEY:(\S+) VAL:(.?+)(?:(?=,KEY:)|$)/g';
//var pattern = '/KEY:(\S+) VAL:(.+)$/g';
//pattern.compile(pattern);
var kv = null;
var row = 0, col = 0;
while((kv = pattern.exec(text) != null))
{
row = kv[1].charAt(0) - '0';
col = kv[1].charAt(1) - '0';
e = document.getElementById('live').rows[row].cells;
e[col].innerHTML = kv[2].slice(0, kv[2].indexOf(","));
}
kv[1] is supposed to give "01"
kv[2] is supposed to give "1.10"
...and of course kv[] should list all the values of 'text'
to fill the table called 'live'.
But I can't get to have pattern.exec() succeed in doing that.
Where is the glitch?
First, the delimiters for the RegExp should be /s, there's no need to put them in ' delimiters. i.e. to get your exec to run properly you should have:
var pattern = /KEY:(\S+) VAL:([^,]+)/g;
Second, you're assigning a boolean to kv which you don't want. The while will obviously only evaluate to true if it's not null so that's redundant. Instead you just need:
while (kv = pattern.exec(text)) {
That should get your code to work as you desire.
the syntax for pattern objects doesn't include quoting, such as:
var pattern=/KEY:(\S+) VAL:([^,]+)/g;
http://www.w3schools.com/jsref/jsref_regexp_exec.asp
It should be
var pattern = /KEY:(\S+) VAL:([^,]+)/g;
http://www.regular-expressions.info/ is a good place to start with.

Categories

Resources