Get variable from string - javascript

'Im trying to extract multiple variables from a string.
The string "var context = [{data: 'some data'}]; /* Some garbage comment blabla */ var foo = 3;" my code will only look for the variables context and foo.
var stringToEval = "var context = [{data: 'some data'}];/* Some garbage comment blabla */ var foo = 3;";
function evalString() {
eval(stringToEval);
if (context !== null && context !== undefined) {
// Do something
}
if (foo !== null && foo !== undefined) {
// Do something
}
}
My solution uses eval() but since I don't have control of the input this is a security issue.
Is there a way around eval()? or how can I prevent the execution of functions inside eval?

I write some code to do this, based on your string pattern, maybe it refer to you needed:
Working Demo:
https://jsfiddle.net/aw6j9upL/6/
Code:
const stringToEval = "var context = [{data: 'some data'}];/* Some garbage comment blabla */ var foo = 3;";
const contextBadJson = stringToEval.replace(/(.*)(var context = \[)(.*)(\];)(.*)/mg, '[$3]');
const fixBadJSON = (badJSON) => badJSON
// Replace ":" with "#colon#" if it's between double-quotes
.replace(/:\s*"([^"]*)"/g, function(match, p1) {
return ': "' + p1.replace(/:/g, '#colon#') + '"';
})
// Replace ":" with "#colon#" if it's between single-quotes
.replace(/:\s*'([^']*)'/g, function(match, p1) {
return ': "' + p1.replace(/:/g, '#colon#') + '"';
})
// Add double-quotes around any tokens before the remaining ":"
.replace(/(['"])?([a-z0-9A-Z_]+)(['"])?\s*:/g, '"$2": ')
// Turn "#colon#" back into ":"
.replace(/#colon#/g, ':')
;
const context = JSON.parse(fixBadJSON(contextBadJson));
const foo = stringToEval.replace(/(.*)(var foo = )(.*)(;)(.*)/mg, '$3');
console.log(context);
console.log(foo);

Related

JavaScript adding parameter to URL [duplicate]

With javascript how can I add a query string parameter to the url if not present or if it present, update the current value? I am using jquery for my client side development.
I wrote the following function which accomplishes what I want to achieve:
function updateQueryStringParameter(uri, key, value) {
var re = new RegExp("([?&])" + key + "=.*?(&|$)", "i");
var separator = uri.indexOf('?') !== -1 ? "&" : "?";
if (uri.match(re)) {
return uri.replace(re, '$1' + key + "=" + value + '$2');
}
else {
return uri + separator + key + "=" + value;
}
}
Update (2020): URLSearchParams is now supported by all modern browsers.
The URLSearchParams utility can be useful for this in combination with window.location.search. For example:
if ('URLSearchParams' in window) {
var searchParams = new URLSearchParams(window.location.search);
searchParams.set("foo", "bar");
window.location.search = searchParams.toString();
}
Now foo has been set to bar regardless of whether or not it already existed.
However, the above assignment to window.location.search will cause a page load, so if that's not desirable use the History API as follows:
if ('URLSearchParams' in window) {
var searchParams = new URLSearchParams(window.location.search)
searchParams.set("foo", "bar");
var newRelativePathQuery = window.location.pathname + '?' + searchParams.toString();
history.pushState(null, '', newRelativePathQuery);
}
Now you don't need to write your own regex or logic to handle the possible existence of query strings.
However, browser support is poor as it's currently experimental and only in use in recent versions of Chrome, Firefox, Safari, iOS Safari, Android Browser, Android Chrome and Opera. Use with a polyfill if you do decide to use it.
I have expanded the solution and combined it with another that I found to replace/update/remove the querystring parameters based on the users input and taking the urls anchor into consideration.
Not supplying a value will remove the parameter, supplying one will add/update the parameter. If no URL is supplied, it will be grabbed from window.location
function UpdateQueryString(key, value, url) {
if (!url) url = window.location.href;
var re = new RegExp("([?&])" + key + "=.*?(&|#|$)(.*)", "gi"),
hash;
if (re.test(url)) {
if (typeof value !== 'undefined' && value !== null) {
return url.replace(re, '$1' + key + "=" + value + '$2$3');
}
else {
hash = url.split('#');
url = hash[0].replace(re, '$1$3').replace(/(&|\?)$/, '');
if (typeof hash[1] !== 'undefined' && hash[1] !== null) {
url += '#' + hash[1];
}
return url;
}
}
else {
if (typeof value !== 'undefined' && value !== null) {
var separator = url.indexOf('?') !== -1 ? '&' : '?';
hash = url.split('#');
url = hash[0] + separator + key + '=' + value;
if (typeof hash[1] !== 'undefined' && hash[1] !== null) {
url += '#' + hash[1];
}
return url;
}
else {
return url;
}
}
}
Update
There was a bug when removing the first parameter in the querystring, I have reworked the regex and test to include a fix.
Second Update
As suggested by #JarónBarends - Tweak value check to check against undefined and null to allow setting 0 values
Third Update
There was a bug where removing a querystring variable directly before a hashtag would lose the hashtag symbol which has been fixed
Fourth Update
Thanks #rooby for pointing out a regex optimization in the first RegExp object.
Set initial regex to ([?&]) due to issue with using (\?|&) found by #YonatanKarni
Fifth Update
Removing declaring hash var in if/else statement
Based on #amateur's answer (and now incorporating the fix from #j_walker_dev comment), but taking into account the comment about hash tags in the url I use the following:
function updateQueryStringParameter(uri, key, value) {
var re = new RegExp("([?&])" + key + "=.*?(&|#|$)", "i");
if (uri.match(re)) {
return uri.replace(re, '$1' + key + "=" + value + '$2');
} else {
var hash = '';
if( uri.indexOf('#') !== -1 ){
hash = uri.replace(/.*#/, '#');
uri = uri.replace(/#.*/, '');
}
var separator = uri.indexOf('?') !== -1 ? "&" : "?";
return uri + separator + key + "=" + value + hash;
}
}
Edited to fix [?|&] in regex which should of course be [?&] as pointed out in the comments
Edit: Alternative version to support removing URL params as well. I have used value === undefined as the way to indicate removal. Could use value === false or even a separate input param as wanted.
function updateQueryStringParameter(uri, key, value) {
var re = new RegExp("([?&])" + key + "=.*?(&|#|$)", "i");
if( value === undefined ) {
if (uri.match(re)) {
return uri.replace(re, '$1$2').replace(/[?&]$/, '').replaceAll(/([?&])&+/g, '$1').replace(/[?&]#/, '#');
} else {
return uri;
}
} else {
if (uri.match(re)) {
return uri.replace(re, '$1' + key + "=" + value + '$2');
} else {
var hash = '';
if( uri.indexOf('#') !== -1 ){
hash = uri.replace(/.*#/, '#');
uri = uri.replace(/#.*/, '');
}
var separator = uri.indexOf('?') !== -1 ? "&" : "?";
return uri + separator + key + "=" + value + hash;
}
}
}
See it in action at https://jsfiddle.net/cdt16wex/
You can use the browser's native URL API to do this in a fairly simple way, where key and value are your parameter name and parameter value respectively.
const url = new URL(location.href);
url.searchParams.set(key, value);
history.pushState(null, '', url);
This will preserve everything about the URL and only change or add the one query param. You can also use replaceState instead of pushState if you don't want it to create a new browser history entry.
Thanks to modern javascript, node.js and browsers support, we can get out of 3rd-party library whirlpool (jquery, query-string etc.) and DRY ourselves.
Here are javascript(node.js) and typescript version for a function that adds or updates query params of given url:
Javascript
const getUriWithParam = (baseUrl, params) => {
const Url = new URL(baseUrl);
const urlParams = new URLSearchParams(Url.search);
for (const key in params) {
if (params[key] !== undefined) {
urlParams.set(key, params[key]);
}
}
Url.search = urlParams.toString();
return Url.toString();
};
console.info('expected: https://example.com/?foo=bar');
console.log(getUriWithParam("https://example.com", {foo: "bar"}));
console.info('expected: https://example.com/slug?foo=bar#hash');
console.log(getUriWithParam("https://example.com/slug#hash", {foo: "bar"}));
console.info('expected: https://example.com/?bar=baz&foo=bar');
console.log(getUriWithParam("https://example.com?bar=baz", {foo: "bar"}));
console.info('expected: https://example.com/?foo=baz&bar=baz');
console.log(getUriWithParam("https://example.com?foo=bar&bar=baz", {foo: "baz"}));
Typescript
const getUriWithParam = (
baseUrl: string,
params: Record<string, any>
): string => {
const Url = new URL(baseUrl);
const urlParams: URLSearchParams = new URLSearchParams(Url.search);
for (const key in params) {
if (params[key] !== undefined) {
urlParams.set(key, params[key]);
}
}
Url.search = urlParams.toString();
return Url.toString();
};
For React Native
URL is not implemented in React Native. So you have to install react-native-url-polyfill beforehand.
For object params
See the second solution in this answer
Here is my library to do that: https://github.com/Mikhus/jsurl
var u = new Url;
u.query.param='value'; // adds or replaces the param
alert(u)
If it's not set or want to update with a new value you can use:
window.location.search = 'param=value'; // or param=new_value
This is in simple Javascript, by the way.
EDIT
You may want to try using the jquery query-object plugin
window.location.search =
jQuery.query.set("param", 5);
I realize this question is old and has been answered to death, but here's my stab at it. I'm trying to reinvent the wheel here because I was using the currently accepted answer and the mishandling of URL fragments recently bit me in a project.
The function is below. It's quite long, but it was made to be as resilient as possible. I would love suggestions for shortening/improving it. I put together a small jsFiddle test suite for it (or other similar functions). If a function can pass every one of the tests there, I say it's probably good to go.
Update: I came across a cool function for using the DOM to parse URLs, so I incorporated that technique here. It makes the function shorter and more reliable. Props to the author of that function.
/**
* Add or update a query string parameter. If no URI is given, we use the current
* window.location.href value for the URI.
*
* Based on the DOM URL parser described here:
* http://james.padolsey.com/javascript/parsing-urls-with-the-dom/
*
* #param (string) uri Optional: The URI to add or update a parameter in
* #param (string) key The key to add or update
* #param (string) value The new value to set for key
*
* Tested on Chrome 34, Firefox 29, IE 7 and 11
*/
function update_query_string( uri, key, value ) {
// Use window URL if no query string is provided
if ( ! uri ) { uri = window.location.href; }
// Create a dummy element to parse the URI with
var a = document.createElement( 'a' ),
// match the key, optional square brackets, an equals sign or end of string, the optional value
reg_ex = new RegExp( key + '((?:\\[[^\\]]*\\])?)(=|$)(.*)' ),
// Setup some additional variables
qs,
qs_len,
key_found = false;
// Use the JS API to parse the URI
a.href = uri;
// If the URI doesn't have a query string, add it and return
if ( ! a.search ) {
a.search = '?' + key + '=' + value;
return a.href;
}
// Split the query string by ampersands
qs = a.search.replace( /^\?/, '' ).split( /&(?:amp;)?/ );
qs_len = qs.length;
// Loop through each query string part
while ( qs_len > 0 ) {
qs_len--;
// Remove empty elements to prevent double ampersands
if ( ! qs[qs_len] ) { qs.splice(qs_len, 1); continue; }
// Check if the current part matches our key
if ( reg_ex.test( qs[qs_len] ) ) {
// Replace the current value
qs[qs_len] = qs[qs_len].replace( reg_ex, key + '$1' ) + '=' + value;
key_found = true;
}
}
// If we haven't replaced any occurrences above, add the new parameter and value
if ( ! key_found ) { qs.push( key + '=' + value ); }
// Set the new query string
a.search = '?' + qs.join( '&' );
return a.href;
}
window.location.search is read/write.
However - modifying the query string will redirect the page you're on and cause a refresh from the server.
If what you're attempting to do is maintain client side state (and potentially make it bookmark-able), you'll want to modify the URL hash instead of the query string, which keeps you on the same page (window.location.hash is read/write). This is how web sites like twitter.com do this.
You'll also want the back button to work, you'll have to bind javascript events to the hash change event, a good plugin for that is http://benalman.com/projects/jquery-hashchange-plugin/
Here's my approach: The location.params() function (shown below) can be used as a getter or setter. Examples:
Given the URL is http://example.com/?foo=bar&baz#some-hash,
location.params() will return an object with all the query parameters: {foo: 'bar', baz: true}.
location.params('foo') will return 'bar'.
location.params({foo: undefined, hello: 'world', test: true}) will change the URL to http://example.com/?baz&hello=world&test#some-hash.
Here is the params() function, which can optionally be assigned to the window.location object.
location.params = function(params) {
var obj = {}, i, parts, len, key, value;
if (typeof params === 'string') {
value = location.search.match(new RegExp('[?&]' + params + '=?([^&]*)[&#$]?'));
return value ? value[1] : undefined;
}
var _params = location.search.substr(1).split('&');
for (i = 0, len = _params.length; i < len; i++) {
parts = _params[i].split('=');
if (! parts[0]) {continue;}
obj[parts[0]] = parts[1] || true;
}
if (typeof params !== 'object') {return obj;}
for (key in params) {
value = params[key];
if (typeof value === 'undefined') {
delete obj[key];
} else {
obj[key] = value;
}
}
parts = [];
for (key in obj) {
parts.push(key + (obj[key] === true ? '' : '=' + obj[key]));
}
location.search = parts.join('&');
};
I know this is quite old but i want to fires my working version in here.
function addOrUpdateUrlParam(uri, paramKey, paramVal) {
var re = new RegExp("([?&])" + paramKey + "=[^&#]*", "i");
if (re.test(uri)) {
uri = uri.replace(re, '$1' + paramKey + "=" + paramVal);
} else {
var separator = /\?/.test(uri) ? "&" : "?";
uri = uri + separator + paramKey + "=" + paramVal;
}
return uri;
}
jQuery(document).ready(function($) {
$('#paramKey,#paramValue').on('change', function() {
if ($('#paramKey').val() != "" && $('#paramValue').val() != "") {
$('#uri').val(addOrUpdateUrlParam($('#uri').val(), $('#paramKey').val(), $('#paramValue').val()));
}
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input style="width:100%" type="text" id="uri" value="http://www.example.com/text.php">
<label style="display:block;">paramKey
<input type="text" id="paramKey">
</label>
<label style="display:block;">paramValue
<input type="text" id="paramValue">
</label>
NOTE This is a modified version of #elreimundo
It's so simple with URLSearchParams, supported in all modern browsers (caniuse).
let p = new URLSearchParams();
p.set("foo", "bar");
p.set("name", "Jack & Jill?");
console.log("http://example.com/?" + p.toString());
If you want to modify the existing URL, construct the object like this: new URLSearchParams(window.location.search) and assign the string to window.location.search.
My take from here (compatible with "use strict"; does not really use jQuery):
function decodeURIParams(query) {
if (query == null)
query = window.location.search;
if (query[0] == '?')
query = query.substring(1);
var params = query.split('&');
var result = {};
for (var i = 0; i < params.length; i++) {
var param = params[i];
var pos = param.indexOf('=');
if (pos >= 0) {
var key = decodeURIComponent(param.substring(0, pos));
var val = decodeURIComponent(param.substring(pos + 1));
result[key] = val;
} else {
var key = decodeURIComponent(param);
result[key] = true;
}
}
return result;
}
function encodeURIParams(params, addQuestionMark) {
var pairs = [];
for (var key in params) if (params.hasOwnProperty(key)) {
var value = params[key];
if (value != null) /* matches null and undefined */ {
pairs.push(encodeURIComponent(key) + '=' + encodeURIComponent(value))
}
}
if (pairs.length == 0)
return '';
return (addQuestionMark ? '?' : '') + pairs.join('&');
}
//// alternative to $.extend if not using jQuery:
// function mergeObjects(destination, source) {
// for (var key in source) if (source.hasOwnProperty(key)) {
// destination[key] = source[key];
// }
// return destination;
// }
function navigateWithURIParams(newParams) {
window.location.search = encodeURIParams($.extend(decodeURIParams(), newParams), true);
}
Example usage:
// add/update parameters
navigateWithURIParams({ foo: 'bar', boz: 42 });
// remove parameter
navigateWithURIParams({ foo: null });
// submit the given form by adding/replacing URI parameters (with jQuery)
$('.filter-form').submit(function(e) {
e.preventDefault();
navigateWithURIParams(decodeURIParams($(this).serialize()));
});
Based on the answer #ellemayo gave, I came up with the following solution that allows for disabling of the hash tag if desired:
function updateQueryString(key, value, options) {
if (!options) options = {};
var url = options.url || location.href;
var re = new RegExp("([?&])" + key + "=.*?(&|#|$)(.*)", "gi"), hash;
hash = url.split('#');
url = hash[0];
if (re.test(url)) {
if (typeof value !== 'undefined' && value !== null) {
url = url.replace(re, '$1' + key + "=" + value + '$2$3');
} else {
url = url.replace(re, '$1$3').replace(/(&|\?)$/, '');
}
} else if (typeof value !== 'undefined' && value !== null) {
var separator = url.indexOf('?') !== -1 ? '&' : '?';
url = url + separator + key + '=' + value;
}
if ((typeof options.hash === 'undefined' || options.hash) &&
typeof hash[1] !== 'undefined' && hash[1] !== null)
url += '#' + hash[1];
return url;
}
Call it like this:
updateQueryString('foo', 'bar', {
url: 'http://my.example.com#hash',
hash: false
});
Results in:
http://my.example.com?foo=bar
Here is a shorter version that takes care of
query with or without a given parameter
query with multiple parameter values
query containing hash
Code:
var setQueryParameter = function(uri, key, value) {
var re = new RegExp("([?&])("+ key + "=)[^&#]*", "g");
if (uri.match(re))
return uri.replace(re, '$1$2' + value);
// need to add parameter to URI
var paramString = (uri.indexOf('?') < 0 ? "?" : "&") + key + "=" + value;
var hashIndex = uri.indexOf('#');
if (hashIndex < 0)
return uri + paramString;
else
return uri.substring(0, hashIndex) + paramString + uri.substring(hashIndex);
}
The regex description can be found here.
NOTE: This solution is based on #amateur answer, but with many improvements.
Code that appends a list of parameters to an existing url using ES6 and jQuery:
class UrlBuilder {
static appendParametersToUrl(baseUrl, listOfParams) {
if (jQuery.isEmptyObject(listOfParams)) {
return baseUrl;
}
const newParams = jQuery.param(listOfParams);
let partsWithHash = baseUrl.split('#');
let partsWithParams = partsWithHash[0].split('?');
let previousParams = '?' + ((partsWithParams.length === 2) ? partsWithParams[1] + '&' : '');
let previousHash = (partsWithHash.length === 2) ? '#' + partsWithHash[1] : '';
return partsWithParams[0] + previousParams + newParams + previousHash;
}
}
Where listOfParams is like
const listOfParams = {
'name_1': 'value_1',
'name_2': 'value_2',
'name_N': 'value_N',
};
Example of Usage:
UrlBuilder.appendParametersToUrl(urlBase, listOfParams);
Fast tests:
url = 'http://hello.world';
console.log('=> ', UrlParameters.appendParametersToUrl(url, null));
// Output: http://hello.world
url = 'http://hello.world#h1';
console.log('=> ', UrlParameters.appendParametersToUrl(url, null));
// Output: http://hello.world#h1
url = 'http://hello.world';
params = {'p1': 'v1', 'p2': 'v2'};
console.log('=> ', UrlParameters.appendParametersToUrl(url, params));
// Output: http://hello.world?p1=v1&p2=v2
url = 'http://hello.world?p0=v0';
params = {'p1': 'v1', 'p2': 'v2'};
console.log('=> ', UrlParameters.appendParametersToUrl(url, params));
// Output: http://hello.world?p0=v0&p1=v1&p2=v2
url = 'http://hello.world#h1';
params = {'p1': 'v1', 'p2': 'v2'};
console.log('=> ', UrlParameters.appendParametersToUrl(url, params));
// Output: http://hello.world?p1=v1&p2=v2#h1
url = 'http://hello.world?p0=v0#h1';
params = {'p1': 'v1', 'p2': 'v2'};
console.log('=> ', UrlParameters.appendParametersToUrl(url, params));
// Output: http://hello.world?p0=v0&p1=v1&p2=v2#h1
To give an code example for modifying window.location.search as suggested by Gal and tradyblix:
var qs = window.location.search || "?";
var param = key + "=" + value; // remember to URI encode your parameters
if (qs.length > 1) {
// more than just the question mark, so append with ampersand
qs = qs + "&";
}
qs = qs + param;
window.location.search = qs;
A different approach without using regular expressions. Supports 'hash' anchors at the end of the url as well as multiple question mark charcters (?). Should be slightly faster than the regular expression approach.
function setUrlParameter(url, key, value) {
var parts = url.split("#", 2), anchor = parts.length > 1 ? "#" + parts[1] : '';
var query = (url = parts[0]).split("?", 2);
if (query.length === 1)
return url + "?" + key + "=" + value + anchor;
for (var params = query[query.length - 1].split("&"), i = 0; i < params.length; i++)
if (params[i].toLowerCase().startsWith(key.toLowerCase() + "="))
return params[i] = key + "=" + value, query[query.length - 1] = params.join("&"), query.join("?") + anchor;
return url + "&" + key + "=" + value + anchor
}
Use this function to add, remove and modify query string parameter from URL based on jquery
/**
#param String url
#param object param {key: value} query parameter
*/
function modifyURLQuery(url, param){
var value = {};
var query = String(url).split('?');
if (query[1]) {
var part = query[1].split('&');
for (i = 0; i < part.length; i++) {
var data = part[i].split('=');
if (data[0] && data[1]) {
value[data[0]] = data[1];
}
}
}
value = $.extend(value, param);
// Remove empty value
for (i in value){
if(!value[i]){
delete value[i];
}
}
// Return url with modified parameter
if(value){
return query[0] + '?' + $.param(value);
} else {
return query[0];
}
}
Add new and modify existing parameter to url
var new_url = modifyURLQuery("http://google.com?foo=34", {foo: 50, bar: 45});
// Result: http://google.com?foo=50&bar=45
Remove existing
var new_url = modifyURLQuery("http://google.com?foo=50&bar=45", {bar: null});
// Result: http://google.com?foo=50
Here's my slightly different approach to this, written as an excercise
function addOrChangeParameters( url, params )
{
let splitParams = {};
let splitPath = (/(.*)[?](.*)/).exec(url);
if ( splitPath && splitPath[2] )
splitPath[2].split("&").forEach( k => { let d = k.split("="); splitParams[d[0]] = d[1]; } );
let newParams = Object.assign( splitParams, params );
let finalParams = Object.keys(newParams).map( (a) => a+"="+newParams[a] ).join("&");
return splitPath ? (splitPath[1] + "?" + finalParams) : (url + "?" + finalParams);
}
usage:
const url = "http://testing.com/path?empty&value1=test&id=3";
addOrChangeParameters( url, {value1:1, empty:"empty", new:0} )
"http://testing.com/path?empty=empty&value1=1&id=3&new=0"
This answer is just a small tweak of ellemayo's answer. It will automatically update the URL instead of just returning the updated string.
function _updateQueryString(key, value, url) {
if (!url) url = window.location.href;
let updated = ''
var re = new RegExp("([?&])" + key + "=.*?(&|#|$)(.*)", "gi"),
hash;
if (re.test(url)) {
if (typeof value !== 'undefined' && value !== null) {
updated = url.replace(re, '$1' + key + "=" + value + '$2$3');
}
else {
hash = url.split('#');
url = hash[0].replace(re, '$1$3').replace(/(&|\?)$/, '');
if (typeof hash[1] !== 'undefined' && hash[1] !== null) {
url += '#' + hash[1];
}
updated = url;
}
}
else {
if (typeof value !== 'undefined' && value !== null) {
var separator = url.indexOf('?') !== -1 ? '&' : '?';
hash = url.split('#');
url = hash[0] + separator + key + '=' + value;
if (typeof hash[1] !== 'undefined' && hash[1] !== null) {
url += '#' + hash[1];
}
updated = url;
}
else {
updated = url;
}
}
window.history.replaceState({ path: updated }, '', updated);
}
Java script code to find a specific query string and replace its value *
('input.letter').click(function () {
//0- prepare values
var qsTargeted = 'letter=' + this.value; //"letter=A";
var windowUrl = '';
var qskey = qsTargeted.split('=')[0];
var qsvalue = qsTargeted.split('=')[1];
//1- get row url
var originalURL = window.location.href;
//2- get query string part, and url
if (originalURL.split('?').length > 1) //qs is exists
{
windowUrl = originalURL.split('?')[0];
var qs = originalURL.split('?')[1];
//3- get list of query strings
var qsArray = qs.split('&');
var flag = false;
//4- try to find query string key
for (var i = 0; i < qsArray.length; i++) {
if (qsArray[i].split('=').length > 0) {
if (qskey == qsArray[i].split('=')[0]) {
//exists key
qsArray[i] = qskey + '=' + qsvalue;
flag = true;
break;
}
}
}
if (!flag)// //5- if exists modify,else add
{
qsArray.push(qsTargeted);
}
var finalQs = qsArray.join('&');
//6- prepare final url
window.location = windowUrl + '?' + finalQs;
}
else {
//6- prepare final url
//add query string
window.location = originalURL + '?' + qsTargeted;
}
})
});
Here's an alternative method using the inbuilt properties of the anchor HTML element:
Handles multi-valued parameters.
No risk of modifying the # fragment, or anything other than the query string itself.
May be a little easier to read? But it is longer.
var a = document.createElement('a'),
getHrefWithUpdatedQueryString = function(param, value) {
return updatedQueryString(window.location.href, param, value);
},
updatedQueryString = function(url, param, value) {
/*
A function which modifies the query string
by setting one parameter to a single value.
Any other instances of parameter will be removed/replaced.
*/
var fragment = encodeURIComponent(param) +
'=' + encodeURIComponent(value);
a.href = url;
if (a.search.length === 0) {
a.search = '?' + fragment;
} else {
var didReplace = false,
// Remove leading '?'
parts = a.search.substring(1)
// Break into pieces
.split('&'),
reassemble = [],
len = parts.length;
for (var i = 0; i < len; i++) {
var pieces = parts[i].split('=');
if (pieces[0] === param) {
if (!didReplace) {
reassemble.push('&' + fragment);
didReplace = true;
}
} else {
reassemble.push(parts[i]);
}
}
if (!didReplace) {
reassemble.push('&' + fragment);
}
a.search = reassemble.join('&');
}
return a.href;
};
if you want to set multiple parameters at once:
function updateQueryStringParameters(uri, params) {
for(key in params){
var value = params[key],
re = new RegExp("([?&])" + key + "=.*?(&|$)", "i"),
separator = uri.indexOf('?') !== -1 ? "&" : "?";
if (uri.match(re)) {
uri = uri.replace(re, '$1' + key + "=" + value + '$2');
}
else {
uri = uri + separator + key + "=" + value;
}
}
return uri;
}
same function as #amateur's
if jslint gives you an error add this after the for loop
if(params.hasOwnProperty(key))
There are a lot of awkward and unnecessarily complicated answers on this page. The highest rated one, #amateur's, is quite good, although it has a bit of unnecessary fluff in the RegExp. Here is a slightly more optimal solution with cleaner RegExp and a cleaner replace call:
function updateQueryStringParamsNoHash(uri, key, value) {
var re = new RegExp("([?&])" + key + "=[^&]*", "i");
return re.test(uri)
? uri.replace(re, '$1' + key + "=" + value)
: uri + separator + key + "=" + value
;
}
As an added bonus, if uri is not a string, you won't get errors for trying to call match or replace on something that may not implement those methods.
And if you want to handle the case of a hash (and you've already done a check for properly formatted HTML), you can leverage the existing function instead of writing a new function containing the same logic:
function updateQueryStringParams(url, key, value) {
var splitURL = url.split('#');
var hash = splitURL[1];
var uri = updateQueryStringParamsNoHash(splitURL[0]);
return hash == null ? uri : uri + '#' + hash;
}
Or you can make some slight changes to #Adam's otherwise excellent answer:
function updateQueryStringParameter(uri, key, value) {
var re = new RegExp("([?&])" + key + "=[^&#]*", "i");
if (re.test(uri)) {
return uri.replace(re, '$1' + key + "=" + value);
} else {
var matchData = uri.match(/^([^#]*)(#.*)?$/);
var separator = /\?/.test(uri) ? "&" : "?";
return matchData[0] + separator + key + "=" + value + (matchData[1] || '');
}
}
This should serve the purpose:
function updateQueryString(url, key, value) {
var arr = url.split("#");
var url = arr[0];
var fragmentId = arr[1];
var updatedQS = "";
if (url.indexOf("?") == -1) {
updatedQS = encodeURIComponent(key) + "=" + encodeURIComponent(value);
}
else {
updatedQS = addOrModifyQS(url.substring(url.indexOf("?") + 1), key, value);
}
url = url.substring(0, url.indexOf("?")) + "?" + updatedQS;
if (typeof fragmentId !== 'undefined') {
url = url + "#" + fragmentId;
}
return url;
}
function addOrModifyQS(queryStrings, key, value) {
var oldQueryStrings = queryStrings.split("&");
var newQueryStrings = new Array();
var isNewKey = true;
for (var i in oldQueryStrings) {
var currItem = oldQueryStrings[i];
var searchKey = key + "=";
if (currItem.indexOf(searchKey) != -1) {
currItem = encodeURIComponent(key) + "=" + encodeURIComponent(value);
isNewKey = false;
}
newQueryStrings.push(currItem);
}
if (isNewKey) {
newQueryStrings.push(encodeURIComponent(key) + "=" + encodeURIComponent(value));
}
return newQueryStrings.join("&");
}
By using jQuery we can do like below
var query_object = $.query_string;
query_object["KEY"] = "VALUE";
var new_url = window.location.pathname + '?'+$.param(query_object)
In variable new_url we will have new query parameters.
Reference: http://api.jquery.com/jquery.param/
I wanted something that:
Uses the browser's native URL API
Can add, update, get, or delete
Expects the query string after the hash e.g. for single page applications
function queryParam(options = {}) {
var defaults = {
method: 'set',
url: window.location.href,
key: undefined,
value: undefined,
}
for (var prop in defaults) {
options[prop] = typeof options[prop] !== 'undefined' ? options[prop] : defaults[prop]
}
const existing = (options.url.lastIndexOf('?') > options.url.lastIndexOf('#')) ? options.url.substr(options.url.lastIndexOf('?') + 1) : ''
const query = new URLSearchParams(existing)
if (options.method === 'set') {
query.set(options.key, options.value)
return `${options.url.replace(`?${existing}`, '')}?${query.toString()}`
} else if (options.method === 'get') {
const val = query.get(options.key)
let result = val === null ? val : val.toString()
return result
} else if (options.method === 'delete') {
query.delete(options.key)
let result = `${options.url.replace(`?${existing}`, '')}?${query.toString()}`
const lastChar = result.charAt(result.length - 1)
if (lastChar === '?') {
result = `${options.url.replace(`?${existing}`, '')}`
}
return result
}
}
// Usage:
let url = 'https://example.com/sandbox/#page/'
url = queryParam({
url,
method: 'set',
key: 'my-first-param',
value: 'me'
})
console.log(url)
url = queryParam({
url,
method: 'set',
key: 'my-second-param',
value: 'you'
})
console.log(url)
url = queryParam({
url,
method: 'set',
key: 'my-second-param',
value: 'whomever'
})
console.log(url)
url = queryParam({
url,
method: 'delete',
key: 'my-first-param'
})
console.log(url)
const mySecondParam = queryParam({
url,
method: 'get',
key: 'my-second-param',
})
console.log(mySecondParam)
url = queryParam({
url,
method: 'delete',
key: 'my-second-param'
})
console.log(url)
Yeah I had an issue where my querystring would overflow and duplicate, but this was due to my own sluggishness. so I played a bit and worked up some js jquery(actualy sizzle) and C# magick.
So i just realized that after the server has done with the passed values, the values doesn't matter anymore, there is no reuse, if the client wanted to do the same thing evidently it will always be a new request, even if its the same parameters being passed. And thats all clientside, so some caching/cookies etc could be cool in that regards.
JS:
$(document).ready(function () {
$('#ser').click(function () {
SerializeIT();
});
function SerializeIT() {
var baseUrl = "";
baseUrl = getBaseUrlFromBrowserUrl(window.location.toString());
var myQueryString = "";
funkyMethodChangingStuff(); //whatever else before serializing and creating the querystring
myQueryString = $('#fr2').serialize();
window.location.replace(baseUrl + "?" + myQueryString);
}
function getBaseUrlFromBrowserUrl(szurl) {
return szurl.split("?")[0];
}
function funkyMethodChangingStuff(){
//do stuff to whatever is in fr2
}
});
HTML:
<div id="fr2">
<input type="text" name="qURL" value="http://somewhere.com" />
<input type="text" name="qSPart" value="someSearchPattern" />
</div>
<button id="ser">Serialize! and go play with the server.</button>
C#:
using System.Web;
using System.Text;
using System.Collections.Specialized;
public partial class SomeCoolWebApp : System.Web.UI.Page
{
string weburl = string.Empty;
string partName = string.Empty;
protected void Page_Load(object sender, EventArgs e)
{
string loadurl = HttpContext.Current.Request.RawUrl;
string querySZ = null;
int isQuery = loadurl.IndexOf('?');
if (isQuery == -1) {
//If There Was no Query
}
else if (isQuery >= 1) {
querySZ = (isQuery < loadurl.Length - 1) ? loadurl.Substring(isQuery + 1) : string.Empty;
string[] getSingleQuery = querySZ.Split('?');
querySZ = getSingleQuery[0];
NameValueCollection qs = null;
qs = HttpUtility.ParseQueryString(querySZ);
weburl = qs["qURL"];
partName = qs["qSPart"];
//call some great method thisPageRocks(weburl,partName); or whatever.
}
}
}
Okay criticism is welcome (this was a nightly concoction so feel free to note adjustments). If this helped at all, thumb it up, Happy Coding.
No duplicates, each request as unique as you modified it, and due to how this is structured,easy to add more queries dynamicaly from wthin the dom.

React loop through object and render div with onClick function which takes argument

So I created a loop which goes through an object created by googles gecoding api. Finds certain values and than puts them into a "results list", the single elements have onClick functions. Now when I do the onClick functions with bind, they do work, when I do them with () => they don't. Maybe someone can explain to me why that doesn't work?
loop:
renderResults: function(){
var status = this.state.data.status;
var results = this.state.data.results;
var ok = (status === 'OK' ? true : false);
if (!status) {
return <div> </div>
}
if (!ok) {
return <div className="searchresults">Error, we couldn't find a match</div>
}
if (status && ok){
var size = Object.keys(results).length
console.log(this.state.data);
var validation_messages = this.state.data.results;
///* Get properties *///
var resul =[];
for (var key in validation_messages) {
console.log("####### " + key + " #######");
// skip loop i the property is from prototype
if (!validation_messages.hasOwnProperty(key)) continue;
var label1 = '';
var label2 = '';
var obj = validation_messages[key];
console.log(obj);
for (var prop2 in obj.address_components) {
if(!obj.address_components.hasOwnProperty(prop2)) continue;
var obj3 = obj.address_components[prop2];
if (obj3.types.indexOf('locality') !== -1) {
label1 = obj3.long_name;
}
if (obj3.types.indexOf('country') !== -1) {
label2 = obj3.long_name;
}
}
var lat = obj.geometry.location.lat;
var lng = obj.geometry.location.lng;
var placeid = obj.place_id;
var label3 = lat.toFixed(3) + "°N / " + lng.toFixed(3) + "°E";
console.log('label1: '+label1);
console.log('label2: '+label2);
console.log('label3: '+label3);
console.log('lat: ' + lat);
console.log('lng: ' + lng);
console.log('id: ' + placeid);
console.log(validation_messages[key].formatted_address);
resul.push(<div className="results" onClick={this.pushlabels.bind(this, label1, label2, label3)} >{label3}</div>);
}
console.log(resul);
return resul;
}
So this works:
resul.push(<div className="results" onClick={this.pushlabels.bind(this, label1, label2, label3)} >{label3}</div>);
This doesn't work:
resul.push(<div className="results" onClick={() => this.pushlabels(label1,label2,label3)} >{label3}</div>);
What do I mean with not working? If I take the version which doesn't work than I get only pushed the label1, label2, label3 from the last object in the loop.
So now I wonder why?
It has to do with variable scoop and closures, for a similar problem have a look at javascript scope problem when lambda function refers to a variable in enclosing loop
Here's a short and simple program that illustrates what happens:
function foo(first, second){
console.log(first + " : " + second)
}
var x = "x";
let bar = () => {foo("bar", x)}
let baz = foo.bind(this,"baz", x)
bar()
baz()
x = "y"
bar()
baz()
//Output:
//bar : x
//baz : x
//bar : y
//baz : x
So basically bind makes the function remember (it actually returns a new function with the parameters set) the current state of the variables. Where as a lamda looks at the variables when it's executed. That's why you only see the last three labels when you don't use bind.

Array does not work in Javascript

I tried to make a function that returns an array, the output should be "My name is Sarah Adam" but actually it does not return anything
/*global s:true*/
var m = 'My name is ';
function updateMsg(h) {
"use strict";
var el = m + h;
s = ['Adam', 'Joseph'];
return s;
}
var n1 = document.getElementById("msg");
n1.textContent = updateMsg("Sarah")[0];
you are returning s (the array) - I think you want to return the concatenated message. As in:
Updated to include variable last names
var m = 'My name is ';
function updateMsg(h, index) {
"use strict";
var el = m + h;
// array of last names
var s = ['Adam', 'Joseph'];
return el + ' ' + s[index]; // return the concatenated string instead
}
var n1 = document.getElementById("msg");
n1.textContent = updateMsg("Sarah", 0); // invoke with param
// console log (confirmation)
console.log(updateMsg("Sarah", 0));
console.log(updateMsg("Meenah", 1));
<div id="msg">
hw
</div>
You could use currying to accomplish this. Just swap your brackets [0] for parentheses (0).
var m = 'My name is ';
function updateMsg(h) {
var s = ['Adam', 'Joseph'];
return function(index) { // return a function that concatenates when given index
return m + h + " " + s[index];
};
}
var messageMaker = updateMsg("Sarah");
console.log(messageMaker(0));
console.log(messageMaker(1));
I think you want to acces one element of the list of lastnames an pass the name. I have corrected your code and do something similar to what you want :
let m = 'My name is ',
s = ['Adam', 'Joseph'],
updateMsg = (h, i) => m + h + ' ' + s[i],
n1 = document.getElementById("msg");
n1.textContent = updateMsg("Sarah", 0);
<p id="msg"></p>
Details are in the source in the comments.
SNIPPET
/*
Global variable: intro
*/
var intro = 'My name is ';
/*
Paramenter: who
*/
function updateMsg(who) {
/*
Array of 2 Strings: guess
Named it guess because that's
what I ended up doing with it
*/
var guess = [' Shadey', ' Joseph'];
/*
Concated String: `hello`
Since `intro` is global it's always accessible
`who` is the parameter with value of "Slim"
`guess[0]` = 'Shadey'
*/
var hello = intro + who + guess[0];
/*
Output of `updateMsg()` function is `hello`
which is "My name is Slim Shadey"
*/
return hello;
}
/*
Reference the `output` element as `noLogic`
*/
var noLogic = document.getElementById("msg");
/*
Set `noLogic's` text to whatever `updateMsg()`
returns
`updateMsg()` parameter is "Slim"
*/
noLogic.textContent = updateMsg("Slim");
<output id="msg"></output>
I guess the intent is to return an array with el prepended to each of the names in s. So you need to loop through the array to create the new array.
var m = 'My name is ';
function updateMsg(h) {
"use strict";
var el = m + h;
var s = ['Adam', 'Joseph'];
return s.map(function(name) {
return el + ' ' + name;
});
}
var n1 = document.getElementById("msg");
n1.textContent = updateMsg("Sarah")[0];
<div id="msg">
</div>

How to declare a dynamic local variable in Javascript

I want to create a local variable dynamically. JavaScript: Dynamically Creating Variables for Loops is not exactly what I am looking for. I dont want an array. I want to access it like a local variable.
Something like:
<script type="text/javascript">
var properties = new Object();
properties["var1"] = "value1";
properties["var2"] = "value2";
createVariables(properties);
function createVariables(properties)
{
// This function should somehow create variables in the calling function. Is there a way to do that?
}
document.write("Outside the function : " + var1 + "<br>");
document.write("Outside the function : " + var2 + "<br>");
</script>
I tried the following code.
<script type="text/javascript">
var properties = new Object();
properties["var1"] = "value1";
properties["var2"] = "value2";
createVariables(properties);
function createVariables(properties)
{
for( var variable in properties)
{
try
{
eval(variable);
eval(variable + " = " + properties[variable] + ";");
}
catch(e)
{
eval("var " + variable + " = '" + properties[variable] + "';");
}
}
document.write("Inside the function : " + var1 + "<br>");
document.write("Inside the function : " + var2 + "<br>");
}
document.write("Outside the function : " + var1 + "<br>");
document.write("Outside the function : " + var2 + "<br>");
</script>
But the generated variables are not accessible outside the createVariables().
Now, I have this solution.
<script type="text/javascript">
var properties = new Object();
properties["var1"] = "value1";
properties["var2"] = "value2";
function createVariables(properties)
{
var str = "";
for( var variable in properties)
{
str += "try{";
str += "eval('" + variable + "');";
str += "eval(\"" + variable + " = properties['" + variable + "'];\");";
str += "}";
str += "catch(e){";
str += "eval(\"var " + variable + " = properties['" + variable + "'];\");";
str += "}";
}
return str;
}
eval(createVariables(properties));
document.write("Outside the function : " + var1 + "<br>");
document.write("Outside the function : " + var2 + "<br>");
</script>
This works. But I am looking for an alternative/better solution. Is it possible to do it without eval?
EDIT: 04-July
Hi,
I tried a solution similar to what #Jonathan suggested.
<script type="text/javascript">
var startFunc = function(){
var self = this;
self.innerFunc = function innerFunc(){
var properties = new Object();
properties["var1"] = "value1";
properties["var2"] = "value2";
properties["var3"] = "value3";
function createVariables(caller, props) {
for(i in props) {
caller[i] = props[i];
}
caller.func1();
}
createVariables(self, properties);
console.log( var1 );
}
self.func1 = function func1(){
console.log( "In func 1" );
console.log( var2 );
}
innerFunc();
console.log( var3 );
}
startFunc();
</script>
This all works fine. But it is actually creating global variables instead of creating the variables in the function.
The "self" passed to the createVariables() function is window. I am not sure why it is happening. I am assigning the function scope to the self. I am not sure what is happening here. It is anyway creating global variables in this case.
If my question is not clear,
What I am after is creating local variables in the caller. The scenario is like
1) I am inside a function.
2) I invoke another function which returns me a map[This map contains name and value of a variable].
3) I want to dynamically create all the variables, if they are not already defined. If they are already defined [global/local], I want to update them.
4) Once these variables are created, I should be able to access them without any context.[Just the variable name]
<script type="text/javascript">
function mainFunc()
{
var varibalesToBeCreated = getVariables();
createVariables(varibalesToBeCreated);
alert(var1);
alert(var2);
}
function createVariables(varibalesToBeCreated)
{
// How can I implement this function,
// such that the variables are created in the caller?
// I don't want these variables this function.
}
function getVariables()
{
var properties = new Object();
properties["var1"] = "value1";
properties["var2"] = "value2";
}
mainFunc();
</script>
Depending on the scope you'd like the variables to have, this could be accomplished in a few different ways.
Global scope
To place the variables in the global scope, you could use window[varName]:
function createVariables(variables) {
for (var varName in variables) {
window[varName ] = variables[varName ];
}
}
createVariables({
'foo':'bar'
});
console.log(foo); // output: bar
Try it: http://jsfiddle.net/nLt5r/
Be advised, the global scope is a dirty, public place. Any script may read, write, or delete variables in this scope. Because of this fact, you run the risk of breaking a different script that uses the same variable names as yours, or another script breaking yours.
Function scope (using this)
To create variables in a function's scope (this.varName), you can use bind:
var variables = {
'foo':'bar'
};
var func = function () {
console.log(this.foo);
};
var boundFunc = func.bind(variables);
boundFunc(); // output: bar
Try it: http://jsfiddle.net/L4LbK/
Depending on what you do with the bound function reference, this method is slightly vulnerable to outside modification of the variables. Anything that can access boundFunc can change or refer to the value of the values by using boundFunc.varName = 'new value'; This may be to your advantage, depending on use case.
Function scope (using arguments)
You can use apply to pass an array of values as arguments:
var variables = [
'bar'
];
var func = function (foo) {
console.log('foo=', foo);
};
func.apply(null, variables);
Try it: http://jsfiddle.net/LKNqd/
As arguments are ephemeral in nature, nothing "outside" could interfere with or refer back to the values, except by modifying the variable array and re-calling the function.
Global scope as temporary
And here's a small utility function that will make temporary use of the global scope. This function is dangerous to code that also uses the global scope -- this could blast over variables that other scripts have created, use at your own risk:
var withVariables = function(func, vars) {
for (var v in vars){
this[v] = vars[v];
}
func();
for (var v in vars){
delete this[v];
}
};
// using an anonymous function
withVariables(
function () {
console.log('anonymous: ', foo);
},
{
'foo':'bar'
}
); // output: bar
// using a function reference
var myFunction =function () {
console.log('myFunction: ', foo);
};
withVariables(myFunction, {
'foo':'bar'
}); // output: bar
console.log(foo); // output: undefined
Try it: http://jsfiddle.net/X3p6k/3/
Documentation
bind on MDN - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind
apply on MDN - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply
window on MDN - https://developer.mozilla.org/en-US/docs/Web/API/Window
Here is working sample based on Chris Baker answer: Function scope (using arguments)
function myFn() {
function keyList(params) {
var str = '';
for (var key in params) {
if (params.hasOwnProperty(key)) {
str += ',' + key;
}
}
return str.length ? str.substring(1) : str;
}
function valueList(params) {
var list = [];
for (var key in params) {
if (params.hasOwnProperty(key)) {
list.push(params[key]);
}
}
return list;
}
var params = {
'var1': 'value1',
'var2': 'value2'
};
var expr = 'document.write("Inside the function : " + var1 + "<br>")'
var fn;
eval('var fn = function(' + keyList(params) + '){' + expr + '};');
fn(valueList(params));
}
myFn();
I have written short code snippet which will create both local and global variable dynamically
function createVar(name,dft){
this[name] = (typeof dft !== 'undefined')?dft:"";
}
createVar("name1","gaurav"); // it will create global variable
createVar("id");// it will create global variable
alert(name1);
alert(id);
function outer(){
var self = this;
alert(self.name1 + " inside");
}
createVar.call(outer,"name1","saurav"); // it will create local variable
outer.call(outer); // to point to local variable.
outer(); // to point to global variable
alert(name1);
hope this helps
Regards
Gaurav Khurana
The example below demonstrates how with gets a value from the object.
var obj = { a : "Hello" }
with(obj) {
alert(a) // Hello
}
But I want to notice: with is deprecated!
This answer is more or less the same as several answers above but here with a simplified sample, with and without using eval. First using eval (not recommended):
var varname = 'foo'; // pretend a user input that
var value = 42;
eval('var ' + varname + '=' + value);
And alternatively, without using eval:
var varname = prompt('Variable name:');
var value = 42;
this[varname] = value;
I hope this helps.
Source: https://www.rosettacode.org/wiki/Dynamic_variable_names#JavaScript
since you are wanting the scope of where the function is being called pass this to the function
var properties = new Object();
properties["var1"] = "value1";
properties["var2"] = "value2";
function createVariables(context) {
for(i in properties) {
context[i] = properties[i];
}
}
createVariables(this);
console.log( var1 );
Do you need something like this?
function createVariables(properties, context){
for( var variable in properties){
context[variable] = properties[variable ];
}
}
So, calling as createVariables(properties, this) will fill the current scope with values from properties.
<script type="text/javascript">
var properties = new Object();
properties["var1"] = "value1";
properties["var2"] = "value2";
createVariables(properties, this);
document.write("Outside the function : " + var1 + "<br>");
document.write("Outside the function : " + var2 + "<br>");
</script>

building a database string

I'm trying to build a database based on some arbitrary data on a website. It's complex and changes for each site so I'll spare the details. Here's basically what I'm trying to do
function level0(arg) { textarea.innerHTML += arg + ' = {'; }
function level1(arg) { textarea.innerHTML += '\n\t' + arg + ': ['; }
function level2(arg) { textarea.innerHTML += arg + ', '; }
And so on. The thing is some level1's don't have any children and I can't get the formatting right.
My three problems are as follows.
The ending commas are going to break in IE (thank you MS)
Empty level1's shouldn't be printed if they don't have any children
Closing /curly?brackets/
HERE'S A DEMO of what I have so far. Notice the ending commas, the empty sub2 which shouldn't be printed, and no closing brackets or braces
Do I need to redesign the entire thing?
Is there also a way to have this all in one function so I don't have to worry if I add another layer?
EDIT
This needs to be done in a string format, I can't build an object and then stringify it, mostly because I need to know which element I'm in the middle of adding to.
Overall it looks that you still might want to build an object, but in case you insist on not building it - here is some sample solution:
function Printer() {
var result = '',
lastLevel = null,
close = {0:'\n}', 1:']', 2:''},
delimiter = {0: ',\n', 1:',\n', 2:','};
function closeLevel(level, noDelimiter) {
if(lastLevel === null)
return;
var l = lastLevel, d = level == lastLevel;
while(l >= level) {
result += close[l] + (l == level && !noDelimiter ? delimiter[l]:'');
l--;
}
}
this.level0 = function(arg) {
closeLevel(0);
result += arg + ' = {\n';
lastLevel = 0;
};
this.level1 = function(arg) {
closeLevel(1);
result += '\t' + arg + ': [';
lastLevel = 1;
};
this.level2 = function(arg) {
closeLevel(2);
result += arg;
lastLevel = 2;
};
this.getResult = function() {
closeLevel(lastLevel, true);
return result;
}
}
var p = new Printer();
p.level0('head');
p.level1('sub1');
p.level2('item1');p.level2('item2');p.level2('item3');
p.level1('sub2');
p.level1('sub3');
p.level2('newthing');
p.level0('head2');
document.getElementById('textarea').value = p.getResult();
You could see it in action here.
I'm not sure why you're building what looks like objects with nested arrays, using string concatenation. Something like this would be much simpler, since it wouldn't require fixing trailing commas, etc:
Edit: I've updated the code to make it keep track of the last level put in.
function Db() {
var level0, level1;
var data = new Object();
this.level0 = function(arg) {
level0 = new Object();
data[arg] = level0;
}
this.level1 = function(arg) {
level1 = new Array();
level0[arg] = level1;
}
this.level2 = function(arg) {
level1.push(arg);
}
this.toString = function() {
var s = '';
for(i in data) {
s += i + '\n';
for(j in data[i]) {
if(data[i][j].length>0) {
s += '\t' + j + ': [' + data[i][j] + ']\n' ;
}
}
}
return s;
}
}
Use like this:
var db = new Db();
db.level0('head');
db.level1('sub1');
db.level2('item1');db.level2('item2');db.level2('item3');
I've tested this in the demo you linked and it works just fine.

Categories