How to change all AJAX requests to add token header from cookies - javascript
I need to send all my XHR requests with a header named token and value coming from cookies.
I found a solution, but the code is very very long - and a bit ugly (insert a full external library - so big - just to use a simple function...)
The need to use swagger-ui. I get the token from ADFS by clicking on Authorize btn. But for all the others request (test performed using try it out btn)
Because I was not able to find a solution with the Swagger configuration, I decide to use a workaround: Change all the AJAX request on the fly.
I already tried to do it using Jquery or direct Javascript codes, but none of them were working.
Finally, I find a solution using XHook (v1.4.9 - https://github.com/jpillora/xhook)
Here is the code of my file (injected into the index page)
// XHook - v1.4.9 - https://github.com/jpillora/xhook
// Jaime Pillora <dev#jpillora.com> - MIT Copyright 2018
(function (a, b) { var c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H = [].indexOf || function (a) { for (var b = 0, c = this.length; b < c; b++)if (b in this && this[b] === a) return b; return -1 }; q = null, q = "undefined" != typeof WorkerGlobalScope && self instanceof WorkerGlobalScope ? self : "undefined" != typeof global ? global : a, x = q.document, d = "before", c = "after", o = "readyState", n = "addEventListener", m = "removeEventListener", h = "dispatchEvent", u = "XMLHttpRequest", g = "fetch", i = "FormData", p = ["load", "loadend", "loadstart"], e = ["progress", "abort", "error", "timeout"], E = "undefined" != typeof navigator && navigator.useragent ? navigator.userAgent : "", A = parseInt((/msie (\d+)/.exec(E.toLowerCase()) || [])[1]), isNaN(A) && (A = parseInt((/trident\/.*; rv:(\d+)/.exec(E.toLowerCase()) || [])[1])), (G = Array.prototype).indexOf || (G.indexOf = function (a) { var b, c, d, e; for (b = d = 0, e = this.length; d < e; b = ++d)if (c = this[b], c === a) return b; return -1 }), D = function (a, b) { return Array.prototype.slice.call(a, b) }, w = function (a) { return "returnValue" === a || "totalSize" === a || "position" === a }, z = function (a, b) { var c; for (c in a) if (a[c], !w(c)) try { b[c] = a[c] } catch (a) { } return b }, B = function (a) { return void 0 === a ? null : a }, C = function (a, b, c) { var d, e, f, g; for (e = function (a) { return function (d) { var e, f, g; e = {}; for (f in d) w(f) || (g = d[f], e[f] = g === b ? c : g); return c[h](a, e) } }, f = 0, g = a.length; f < g; f++)d = a[f], c._has(d) && (b["on" + d] = e(d)) }, y = function (a) { var b; if (x && null != x.createEventObject) return b = x.createEventObject(), b.type = a, b; try { return new Event(a) } catch (b) { return { type: a } } }, f = function (a) { var c, d, e; return d = {}, e = function (a) { return d[a] || [] }, c = {}, c[n] = function (a, c, f) { d[a] = e(a), d[a].indexOf(c) >= 0 || (f = f === b ? d[a].length : f, d[a].splice(f, 0, c)) }, c[m] = function (a, c) { var f; if (a === b) return void (d = {}); c === b && (d[a] = []), f = e(a).indexOf(c), f !== -1 && e(a).splice(f, 1) }, c[h] = function () { var b, d, f, g, h, i, j, k; for (b = D(arguments), d = b.shift(), a || (b[0] = z(b[0], y(d))), g = c["on" + d], g && g.apply(c, b), k = e(d).concat(e("*")), f = i = 0, j = k.length; i < j; f = ++i)h = k[f], h.apply(c, b) }, c._has = function (a) { return !(!d[a] && !c["on" + a]) }, a && (c.listeners = function (a) { return D(e(a)) }, c.on = c[n], c.off = c[m], c.fire = c[h], c.once = function (a, b) { var d; return d = function () { return c.off(a, d), b.apply(null, arguments) }, c.on(a, d) }, c.destroy = function () { return d = {} }), c }, F = f(!0), F.EventEmitter = f, F[d] = function (a, b) { if (a.length < 1 || a.length > 2) throw "invalid hook"; return F[n](d, a, b) }, F[c] = function (a, b) { if (a.length < 2 || a.length > 3) throw "invalid hook"; return F[n](c, a, b) }, F.enable = function () { q[u] = t, "function" == typeof r && (q[g] = r), k && (q[i] = s) }, F.disable = function () { q[u] = F[u], q[g] = F[g], k && (q[i] = k) }, v = F.headers = function (a, b) { var c, d, e, f, g, h, i, j, k; switch (null == b && (b = {}), typeof a) { case "object": d = []; for (e in a) g = a[e], f = e.toLowerCase(), d.push(f + ":\t" + g); return d.join("\n") + "\n"; case "string": for (d = a.split("\n"), i = 0, j = d.length; i < j; i++)c = d[i], /([^:]+):\s*(.+)/.test(c) && (f = null != (k = RegExp.$1) ? k.toLowerCase() : void 0, h = RegExp.$2, null == b[f] && (b[f] = h)); return b } }, k = q[i], s = function (a) { var b; this.fd = a ? new k(a) : new k, this.form = a, b = [], Object.defineProperty(this, "entries", { get: function () { var c; return c = a ? D(a.querySelectorAll("input,select")).filter(function (a) { var b; return "checkbox" !== (b = a.type) && "radio" !== b || a.checked }).map(function (a) { return [a.name, "file" === a.type ? a.files : a.value] }) : [], c.concat(b) } }), this.append = function (a) { return function () { var c; return c = D(arguments), b.push(c), a.fd.append.apply(a.fd, c) } }(this) }, k && (F[i] = k, q[i] = s), l = q[u], F[u] = l, t = q[u] = function () { var a, b, g, i, j, k, l, m, q, r, t, w, x, y, D, E, G, I, J, K, L; a = -1, I = new F[u], t = {}, y = null, l = void 0, D = void 0, w = void 0, r = function () { var b, c, d, e; if (w.status = y || I.status, y === a && A < 10 || (w.statusText = I.statusText), y !== a) { e = v(I.getAllResponseHeaders()); for (b in e) d = e[b], w.headers[b] || (c = b.toLowerCase(), w.headers[c] = d) } }, q = function () { if (I.responseType && "text" !== I.responseType) "document" === I.responseType ? (w.xml = I.responseXML, w.data = I.responseXML) : w.data = I.response; else { w.text = I.responseText, w.data = I.responseText; try { w.xml = I.responseXML } catch (a) { } } "responseURL" in I && (w.finalUrl = I.responseURL) }, G = function () { k.status = w.status, k.statusText = w.statusText }, E = function () { "text" in w && (k.responseText = w.text), "xml" in w && (k.responseXML = w.xml), "data" in w && (k.response = w.data), "finalUrl" in w && (k.responseURL = w.finalUrl) }, i = function (a) { for (; a > b && b < 4;)k[o] = ++b, 1 === b && k[h]("loadstart", {}), 2 === b && G(), 4 === b && (G(), E()), k[h]("readystatechange", {}), 4 === b && (t.async === !1 ? g() : setTimeout(g, 0)) }, g = function () { l || k[h]("load", {}), k[h]("loadend", {}), l && (k[o] = 0) }, b = 0, x = function (a) { var b, d; if (4 !== a) return void i(a); b = F.listeners(c), (d = function () { var a; return b.length ? (a = b.shift(), 2 === a.length ? (a(t, w), d()) : 3 === a.length && t.async ? a(t, w, d) : d()) : i(4) })() }, k = t.xhr = f(), I.onreadystatechange = function (a) { try { 2 === I[o] && r() } catch (a) { } 4 === I[o] && (D = !1, r(), q()), x(I[o]) }, m = function () { l = !0 }, k[n]("error", m), k[n]("timeout", m), k[n]("abort", m), k[n]("progress", function () { b < 3 ? x(3) : k[h]("readystatechange", {}) }), ("withCredentials" in I || F.addWithCredentials) && (k.withCredentials = !1), k.status = 0, L = e.concat(p); for (J = 0, K = L.length; J < K; J++)j = L[J], k["on" + j] = null; return k.open = function (a, c, d, e, f) { b = 0, l = !1, D = !1, t.headers = {}, t.headerNames = {}, t.status = 0, w = {}, w.headers = {}, t.method = a, t.url = c, t.async = d !== !1, t.user = e, t.pass = f, x(1) }, k.send = function (a) { var b, c, f, g, h, i, j, l; for (l = ["type", "timeout", "withCredentials"], i = 0, j = l.length; i < j; i++)c = l[i], f = "type" === c ? "responseType" : c, f in k && (t[c] = k[f]); t.body = a, h = function () { var a, b, d, g, h, i; for (C(e, I, k), k.upload && C(e.concat(p), I.upload, k.upload), D = !0, I.open(t.method, t.url, t.async, t.user, t.pass), h = ["type", "timeout", "withCredentials"], d = 0, g = h.length; d < g; d++)c = h[d], f = "type" === c ? "responseType" : c, c in t && (I[f] = t[c]); i = t.headers; for (a in i) b = i[a], a && I.setRequestHeader(a, b); t.body instanceof s && (t.body = t.body.fd), I.send(t.body) }, b = F.listeners(d), (g = function () { var a, c; return b.length ? (a = function (a) { if ("object" == typeof a && ("number" == typeof a.status || "number" == typeof w.status)) return z(a, w), H.call(a, "data") < 0 && (a.data = a.response || a.text), void x(4); g() }, a.head = function (a) { return z(a, w), x(2) }, a.progress = function (a) { return z(a, w), x(3) }, c = b.shift(), 1 === c.length ? a(c(t)) : 2 === c.length && t.async ? c(t, a) : a()) : h() })() }, k.abort = function () { y = a, D ? I.abort() : k[h]("abort", {}) }, k.setRequestHeader = function (a, b) { var c, d; c = null != a ? a.toLowerCase() : void 0, d = t.headerNames[c] = t.headerNames[c] || a, t.headers[d] && (b = t.headers[d] + ", " + b), t.headers[d] = b }, k.getResponseHeader = function (a) { var b; return b = null != a ? a.toLowerCase() : void 0, B(w.headers[b]) }, k.getAllResponseHeaders = function () { return B(v(w.headers)) }, I.overrideMimeType && (k.overrideMimeType = function () { return I.overrideMimeType.apply(I, arguments) }), I.upload && (k.upload = t.upload = f()), k.UNSENT = 0, k.OPENED = 1, k.HEADERS_RECEIVED = 2, k.LOADING = 3, k.DONE = 4, k.response = "", k.responseText = "", k.responseXML = null, k.readyState = 0, k.statusText = "", k }, "function" == typeof q[g] && (j = q[g], F[g] = j, r = q[g] = function (a, b) { var e, f, g; return null == b && (b = { headers: {} }), b.url = a, g = null, f = F.listeners(d), e = F.listeners(c), new Promise(function (a, c) { var d, h, i, k, l; h = function () { return b.body instanceof s && (b.body = b.body.fd), b.headers && (b.headers = new Headers(b.headers)), g || (g = new Request(b.url, b)), z(b, g) }, i = function (b) { var c; return e.length ? (c = e.shift(), 2 === c.length ? (c(h(), b), i(b)) : 3 === c.length ? c(h(), b, i) : i(b)) : a(b) }, d = function (b) { var c; if (void 0 !== b) return c = new Response(b.body || b.text, b), a(c), void i(c); k() }, k = function () { var a; return f.length ? (a = f.shift(), 1 === a.length ? d(a(b)) : 2 === a.length ? a(h(), d) : void 0) : void l() }, l = function () { return j(h()).then(function (a) { return i(a) }).catch(function (a) { return i(a), c(a) }) }, k() }) }), t.UNSENT = 0, t.OPENED = 1, t.HEADERS_RECEIVED = 2, t.LOADING = 3, t.DONE = 4, "function" == typeof define && define.amd ? define("xhook", [], function () { return F }) : "object" == typeof module && module.exports ? module.exports = { xhook: F } : q && (q.xhook = F) }).call(this, window);
// Extract the {name} value from the cookies (used to find token)
function check_cookie_name(name) {
debugger;
var match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]+)'));
if (match) {
return match[2];
}
else {
console.log('-- try to call API without authentication token ---');
return "Please login"
}
}
// Add token in header before each request
xhook.before(function (request) {
request.headers['token'] = check_cookie_name('token');
});
The actual result is good, and it works.
I expect to find a better/cleaner way to do the same thing ^^
Thank you in advance,
Regards
Nicolas
Related
What is the JS in webpacks (react-scripts) index.html doing?
I am trying to load a React component dynamically into another application (also a simple React app) but cannot get the index.js to be run. I am somewhat orienting on this article, but that's actually not the question. I stumbled upon the fact that that the generated enormous JS function that is generated to the index.html is somehow the entry point for my index.js to be called. <!doctype html> <html lang="en"> <head> <!-- head stuff here --> </head> <body><noscript>You need to enable JavaScript to run this app.</noscript> <div id="root"></div> <!-- this is the function I am talking about --> <script>!function (c) { function e(e) { for (var r, t, n = e[0], o = e[1], u = e[2], a = 0, l = []; a < n.length; a++)t = n[a], Object.prototype.hasOwnProperty.call(i, t) && i[t] && l.push(i[t][0]), i[t] = 0; for (r in o) Object.prototype.hasOwnProperty.call(o, r) && (c[r] = o[r]); for (s && s(e); l.length;)l.shift()(); return p.push.apply(p, u || []), f() } function f() { for (var e, r = 0; r < p.length; r++) { for (var t = p[r], n = !0, o = 1; o < t.length; o++) { var u = t[o]; 0 !== i[u] && (n = !1) } n && (p.splice(r--, 1), e = a(a.s = t[0])) } return e } var t = {}, i = { 1: 0 }, p = []; function a(e) { if (t[e]) return t[e].exports; var r = t[e] = { i: e, l: !1, exports: {} }; return c[e].call(r.exports, r, r.exports, a), r.l = !0, r.exports } a.m = c, a.c = t, a.d = function (e, r, t) { a.o(e, r) || Object.defineProperty(e, r, { enumerable: !0, get: t }) }, a.r = function (e) { "undefined" != typeof Symbol && Symbol.toStringTag && Object.defineProperty(e, Symbol.toStringTag, { value: "Module" }), Object.defineProperty(e, "__esModule", { value: !0 }) }, a.t = function (r, e) { if (1 & e && (r = a(r)), 8 & e) return r; if (4 & e && "object" == typeof r && r && r.__esModule) return r; var t = Object.create(null); if (a.r(t), Object.defineProperty(t, "default", { enumerable: !0, value: r }), 2 & e && "string" != typeof r) for (var n in r) a.d(t, n, function (e) { return r[e] }.bind(null, n)); return t }, a.n = function (e) { var r = e && e.__esModule ? function () { return e.default } : function () { return e }; return a.d(r, "a", r), r }, a.o = function (e, r) { return Object.prototype.hasOwnProperty.call(e, r) }, a.p = "/search/"; var r = window.webpackJsonpsearch = window.webpackJsonpsearch || [], n = r.push.bind(r); r.push = e, r = r.slice(); for (var o = 0; o < r.length; o++)e(r[o]); var s = n; f() }([])</script> <script src="/search/static/js/2.360c2576.chunk.js"></script> <script src="/search/static/js/main.f6fe58e3.chunk.js"></script> </body> </html> Currently I am only including the main.js, this is why my prototype does not work, so I would like to understand what the index.html is actually doing to recreate it in the importing system. I am not able to find any documentation to what this javascript is actually doing. Can anyone help me find some hints of what this function is doing? The project is a simple create-react-app application. No further configuration is done, but the "homepage" attribute set in package.json.
Found the answer. Actually this post got me into the right direction. The function is the inlined runtime chunk, to save some network traffic. There is an actual Webpack plugin for that that can be set by env-variable like this INLINE_RUNTIME_CHUNK=false Source This was implemented due to this reported issue. The code from reacts webpack.config.js is the following: // Inlines the webpack runtime script. This script is too small to warrant // a network request. // https://github.com/facebook/create-react-app/issues/5358 isEnvProduction && shouldInlineRuntimeChunk && new InlineChunkHtmlPlugin(HtmlWebpackPlugin, [/runtime-.+[.]js/]),
How to add/append data to multidimensional array in google script
Is it possible to add to a multidimensional array of unknown size without using a google sheets(spreadsheet) to hold the data? Looking everywhere and can't find an example for a 3 dimensional array. Here is what I want to do: var aDirTree=[]; aDirTree[0][0][0]="myName1"; aDirTree[0][0][1]="myURL1"; aDirTree[1][0][0]="myName2"; aDirTree[1][0][1]="myURL2"; //Notice we are skipping elements aDirTree[2][5][0]="myName3"; aDirTree[2][5][1]="myURL3"; Where values that are skipped are null? I'm guessing it might be some sort of push method.
In the lazier version, array can be used as a key (but it's converted to string) : var o = {} o[[1,2,3]]='a' o['4,5,6']='b' console.log(o) // { "1,2,3": "a", "4,5,6": "b" } console.log(o[[0,0,0]]) // undefined Proxy(not available in IE) can be another alternative, but it will create a lot of extra values: var handler = { get: (a, i) => i in a ? a[i] : a[i] = new Proxy([], handler) } var a = new Proxy([], handler) a[1][2][3]='a' a[4][5][6]='b' console.log(a) // [[],[[],[],[[],[],[],"a"]],[],[],[[],[],[],[],[],[[],[],[],[],[],[],"b"]]] console.log(a[0][0][0]) // [] And finally, the "real" answer: function set(a, x, y, z, v) { ((a = a[x] || (a[x] = []))[y] || (a[y] = []))[z] = v } function get(a, x, y, z, v) { return (a = a[x]) && (a = a[y]) && z in a ? a[z] : v } var a = [] set(a,1,2,3,'a') set(a,4,5,6,'b') console.log( get(a,0,0,0) ) // undefined console.log( get(a,0,0,0,'default') ) // "default" console.log( a ) // [,[,,[,,,"a"]],,,[,,,,,[,,,,,,"b"]]] Bonus: combination of all 3, but not very efficient, because the keys are converted to strings: var a = [], p = new Proxy(a, { set: (a, k, v) => ([x,y,z] = k.split(','), ((a = a[x] || (a[x] = []))[y] || (a[y] = []))[z] = v) }) p[[1,2,3]] = 'a' p[[4,5,6]] = 'b' console.log( a[[0,0,0]] ) // undefined console.log( a ) // [,[,,[,,,"a"]],,,[,,,,,[,,,,,,"b"]]]
function writeToTree(tree, first, second, third, value) { tree[first] || (tree[first] = []); tree[first][second] || (tree[first][second] = []); tree[first][second][third] || (tree[first][second][third] = []); tree[first][second][third] = value; } var aDirTree = []; writeToTree(aDirTree, 1, 55, 3, "someValue"); Or recursively, giving you arbitrary depth: function writeToTree(tree, position, value) { var insertAt = position.shift(); tree[insertAt] || (tree[insertAt] = []); if (position.length === 0) { tree[insertAt] = value; return; } writeToTree(tree[insertAt], position, value); } var aDirTree = []; writeToTree(aDirTree, [1, 55, 3], "someValue"); console.log(aDirTree);
How to use regex on string within a switch statement?
I have an if else statement to match a string with vowels and consonants which works fine. I would like to tidy it up with a switch statement however using match() does not work as a case. what am i missing? if else //returns vowels: 1, consonants: 3 function getCount(words) { var v, c; if (words === '' || words === ' ') { v=0; c=0; } else if(words.match(/[aeiou]/gi)) { v = words.match(/[aeiou]/gi).length; c = words.replace(/\s|\W/g, '').split("").length - v; } else { v = 0; c = 0; } return { vowels: v, consonants: c }; } getCount('test'); switch //returns vowels: 0, consonants: 0 function getCount(words) { var v, c; switch(words) { case words.match(/[aeiou]/gi): v = words.match(/[aeiou]/gi).length; c = words.replace(/\s|\W/g, '').split("").length - v; console.log("true"); break; case '': case ' ': v = 0; c = 0; break; default: v = 0; c = 0; } return { vowels: v, consonants: c }; } getCount('test');
// Code goes here function getCount(words) { var v, c; switch(true) { case ( words.match(/[aeiou]/gi) !=null ): v = words.match(/[aeiou]/gi).length; c = words.replace(/\s|\W/g, '').split("").length - v; console.log("true"); break; case (words==''): case (words==' '): v = 0; c = 0; break; default: v = 0; c = 0; } return { vowels: v, consonants: c }; } console.log(getCount('test'));
Your switch statement needs to evaluate an expression and compare the result to the value of each case statement. function getCount(words) { var v, c; switch(words.match(/[aeiou]/gi).length > 0) { case true: v = words.match(/[aeiou]/gi).length; c = words.replace(/\s|\W/g, '').split("").length - v; console.log("true"); break; default: v = 0; c = 0; } return { vowels: v, consonants: c }; } getCount('test');
How to sort JavaScript string array
I have the following array: var arr = ["COL10","COL5", "COL4","COL3", "COL8","COL9", "COL2","COL7", "COL1","COL6"]; console.log("After sort:"+arr.sort()); The output is: After sort:COL1,COL10,COL2,COL3,COL4,COL5,COL6,COL7,COL8,COL9 But I want it to be: After sort:COL1,COL2,COL3,COL4,COL5,COL6,COL7,COL8,COL9,COL10 How should I do this?
Use the following approach with Array.sort and String.slice functions: var arr = ["COL10","COL5","COL4","COL3","COL8","COL9","COL2","COL7","COL1","COL6"]; arr.sort(function (a,b) { return a.slice(3) - b.slice(3); }); console.log(arr);
You could split the items and sort the parts separate. var arr = ["COL10", "COL5", "COL4", "COL3", "COL8", "COL9", "COL2", "COL7", "COL1", "COL6"]; arr.sort(function (a, b) { var aa = a.split(/(\d+)/g), bb = b.split(/(\d+)/g); return aa[0].localeCompare(bb[0]) || aa[1] - bb[1]; }); console.log(arr);
Try out the alphanumerical sort from Brian Huisman: Article var arr = ["COL10", "COL5", "COL4", "COL3", "COL8", "COL9", "COL2", "COL7", "COL1", "COL6" ]; console.log("After sort:" + arr.sort(alphanum)); function alphanum(a, b) { function chunkify(t) { var tz = [], x = 0, y = -1, n = 0, i, j; while (i = (j = t.charAt(x++)).charCodeAt(0)) { var m = (i == 46 || (i >= 48 && i <= 57)); if (m !== n) { tz[++y] = ""; n = m; } tz[y] += j; } return tz; } var aa = chunkify(a); var bb = chunkify(b); for (x = 0; aa[x] && bb[x]; x++) { if (aa[x] !== bb[x]) { var c = Number(aa[x]), d = Number(bb[x]); if (c == aa[x] && d == bb[x]) { return c - d; } else return (aa[x] > bb[x]) ? 1 : -1; } } return aa.length - bb.length; }
var arr = ["COL10","COL5", "COL4","COL3", "COL8","COL9", "COL2","COL7", "COL1","COL6"]; arr.sort(function(a,b) { var a1 = parseInt(a.split('COL')[1]); var b1 = parseInt(b.split('COL')[1]); return a1 - b1; });
Crockford's .supplant with multiple level objects
/** Supplant **/ String.prototype.supplant = function(o) { return this.replace (/{([^{}]*)}/g, function (a, b) { var r = o[b]; return typeof r === 'string' || typeof r === 'number' ? r : a; } ); }; Crockford is no doubt a JavaScript Grand Wizard, but his prototype is lacking when it comes to multiple level objects. I would like this function to cover multiple level object replacement such as '{post.detailed}' could anyone help me with a revised version of supplant?
That shouldn't be too difficult. Use this replace function instead: function (a, b) { var r = o, parts = b.split("."); for (var i=0; r && i<parts.length; i++) r = r[parts[i]]; return typeof r === 'string' || typeof r === 'number' ? r : a; }
I personally hate it when people stuff their own rubbish on the native types in JavaScript. If I were to write it I would do the following... But why no love for boolean? function supplant(str, data) { return str.replace(/{([^{}]*)}/g, function (a, b) { // Split the variable into its dot notation parts var p = b.split(/\./); // The c variable becomes our cursor that will traverse the object var c = data; // Loop over the steps in the dot notation path for(var i = 0; i < p.length; ++i) { // If the key doesn't exist in the object do not process // mirrors how the function worked for bad values if(c[p[i]] == null) return a; // Move the cursor up to the next step c = c[p[i]]; } // If the data is a string or number return it otherwise do // not process, return the value it was, i.e. {x} return typeof c === 'string' || typeof c === 'number' ? c : a; }); }; It doesn't support arrays btw, you would need to do some additional stuff to support that.
#Bergi method w/ support to boolean: function (a, b) { var r = o, parts = b.split("."); for (var i=0; r && i<parts.length; i++) r = r[parts[i]]; return typeof r === 'string' || typeof r === 'number' || typeof r === 'boolean' ? r : a; } Original Crockford's Supplant method w/ support to boolean: if (!String.prototype.supplant) { String.prototype.supplant = function (o) { return this.replace(/{([^{}]*)}/g, function (a, b) { var r = o[b]; return typeof r === 'string' || typeof r === 'number' || typeof r === 'boolean' ? r : a; } ); }; } Best of luck! https://gist.github.com/fsschmitt/b48db17397499282ff8c36d73a36a8af