I'm trying to mod a javascript game. It has various objects, such as sprites, tiles, items etc.
So far I have been able to compare the name value of a sprite to change the sound that is generated when you interact with it.
ie:
after('startDialog', function () { //works
if (sprite[id].name === "cat")
sounds.meow();
else if (sprite[id].name === "bully1")
sounds.punch();
else if (sprite[id].name === "bully2")
sounds.gunshot();
else
sounds.birdrobin();
});
The problem arises when I try to interact with items. The same code does not work on them...
after('onInventoryChanged', function () { //does not work
if (item[id].name === "smokes"){
sounds.lighter();
} else {
sounds.birdrobin();
}
});
The only differance between the items and sprites is, that sprites are unique, you can not have two sprites with the same name attribute. You can have as many items with the same name as you like on the other hand.
I am buffled though, as both the sprites and items objects are "generated" by the same code and I should be able to access the name variable in items as well...
function parseItem(lines, i) {
var id = getId(lines[i]);
var drwId = null;
var name = null;
//item data
item[id] = {
id : id,
drw : drwId, //drawing id
col : colorIndex,
dlg : dialogId,
name : name
};
return i;
}
function parseSprite(lines, i) {
var id = getId(lines[i]);
var drwId = null;
var name = null;
//sprite data
sprite[id] = {
id : id,
drw : drwId, //drawing id
col : colorIndex,
dlg : dialogId,
inventory : startingInventory,
name : name
};
return i;
}
Any help would be welcome, as I have been stuck here for the last few days.
If I can provide any addtional information, feel free to ask. I just don't want to clutter the question right from the start.
Edit 1:
item[id].name is used to draw the objects on the canvas, so I should be able to access it...
for (id in item) {
worldStr += "ITM " + id + "\n";
worldStr += serializeDrawing( "ITM_" + id );
if (item[id].name != null && item[id].name != undefined) {
/* NAME */
worldStr += "NAME " + item[id].name + "\n";
}
if (item[id].dlg != null) {
worldStr += "DLG " + item[id].dlg + "\n";
}
if (item[id].col != null && item[id].col != undefined && item[id].col != 2) {
/* COLOR OVERRIDE */
worldStr += "COL " + item[id].col + "\n";
}
worldStr += "\n";
}
Edit 2:
I've made a little bit of "progress". Now, onInventoryChange actually does generate a sound, but not the correct one. I have an item with a name smokes and I have an item with a name beer. The sound that is generated is in the else section even when I interact with smokes and beer...
after('onInventoryChanged', function () {
if (item.name === "smokes"){
sounds.lighter();
} else if (item.name === "beer") {
sounds.bottleCap();
} else {
sounds.birdrobin();
}
});
Thank you
I have a function that I use to pass over a table field name and its value. Depending on the name of the field, it either returns the contents as a link or it does not.
// Given a field name, check to see if its in our output. If so, return the formatted link
function createLink(field, val) {
var output = {
'ntid': 'https://web.internal/profile/' + val,
'email': 'mailTo:' + val
};
var i, key, keys = Object.keys(output);
for ( i = 0; i < keys.length; ++i ) {
key = keys[i];
if(field.toLowerCase() == key){
return ''+val+'';
}
}
return val;
}
Usage:
createLink('email', 'bob#stuff.com')
// returns bob#stuff.com
This also works for NTID. The issue I am having though is there are some field names that contain my values in the output such as Sup Email or Sup NTID and those are not transformed correctly.
Expected Result:
createLink('sup email', 'bob2#stuff2.com')
// returns bob#stuff.com
The Question:
How can I tweak my function to see if my field exists in the output array at all, even if it's not an exact match?
Change your function to
function createLink(field, val) {
var output = {
'ntid': 'https://web.internal/profile/' + val,
'email': 'mailTo:' + val
};
var i, key, keys = Object.keys(output);
for (i = 0; i < keys.length; ++i) {
key = keys[i];
if ((field.toLowerCase()).includes(key)) {
return '' + val + '';
}
}
return val;
}
console.log(createLink('sup email', 'bob2#stuff2.com') )
Notice the code if ((field.toLowerCase()).includes(key)) {
This will check for your key substring in the string
What you're implementing is the Strategy Pattern. The Strategy Pattern relies on some form of behaviour-switching depending on the inputs to the method. In your case, that switching is based on the first argument.
What you don't want to do is what your questions asks how to do. You don't want to assume every field name in your application which contains "email" or some other string is guaranteed to be an email address, handled by the same strategy.
Create a table of field names and strategies to use for the display of each of these fields; and use an "enum-ish" object as the definition of the strategies.
function create_link(field, val) {
const strategy = create_link.Field_Strategies[field];
if (typeof strategy === 'undefined') {
console.log("Using default strategy");
return val;
}
console.log("Using " + strategy);
switch (strategy) {
case create_link.Strategies.EMAIL:
return '' + val + '';
case create_link.Strategies.NTID:
return '<a href="https://web.internal/profile/' +
val + '" target="_blank">' + val + '</a>';
case create_link.Strategies.SOME_FIELD:
return '<a href="http://example.com/some/path/' +
encodeURIComponent(val) +
'" target="_blank">' + val + '</a>';
}
}
create_link.Strategies = {
EMAIL: "email strategy",
NTID: "ntid strategy",
SOME_FIELD: "somefield strategy"
};
create_link.Field_Strategies = {
"Sup email": create_link.Strategies.EMAIL,
"E-mail": create_link.Strategies.EMAIL,
"Email": create_link.Strategies.EMAIL,
"NTID": create_link.Strategies.NTID,
"Foobar baz": create_link.Strategies.SOME_FIELD
};
console.log(create_link("foo","foofoofoo"));
console.log(create_link("Sup email","supervisor#example.com"));
console.log(create_link("E-mail","foo#example.com"));
console.log(create_link("Email","bar#example.com"));
console.log(create_link("NTID","10983409509734"));
console.log(create_link("Foobar baz","Aleph null"));
You could use String.prototype.indexOf.
The indexOf() method returns the index within the calling String object of the first occurrence of the specified value...Returns -1 if the value is not found.
So your code would then look like:
// Given a field name, check to see if its in our output. If so, return the formatted link
function createLink(field, val) {
var output = {
'ntid': 'https://web.internal/profile/' + val,
'email': 'mailTo:' + val
};
var i, key, keys = Object.keys(output);
for ( i = 0; i < keys.length; ++i ) {
key = keys[i];
if(field.toLowerCase().indexOf(key) >= 0){ //CHANGE HERE
return ''+val+'';
}
}
return val;
}
var key = " ";
var myBio = {
"name":"Sathya",
"age":"23",
"position":"Soft.Engineer",
"email":
{
"email1":"sathya#gmail.com",
"email2":"sathya#knstek.com"},
};
for (key in myBio){
var y = myBio[key];
console.log(key+" : "+ y);
}
output:
name : Sathya
age : 23
position : Soft.Engineer
email : [object Object]
Required Output:
name : Sathya
age : 23
position : Soft.Engineer
email :
email1:sathya#gmail.com
email2:sathya#knstek.com
I can print this Emails separately using another loop. But I want to print with main loop only . Any ways to do this using JavaScript??
You could use a recursive function to get this done.. example
function logRecursive(object){
for (key in object){
var value=object[key];
if(typeof value === 'object'){
console.log('{');
logRecursive(value)
console.log('}');
}else{
console.log(value);
}
}
function recursion(myBio) {
for (var key in myBio) {
if (typeof(myBio[key]) == 'object') {
recursion(myBio[key]);
} else {
alert("Key: " + key + " Values: " + myBio[key]);
}
}
}
use this subroutine if you have nested json
Try this:
var myBio = {
"name" : "Sathya",
"age" : "23",
"position" : "Soft.Engineer",
"email" : {
"email1" : "sathya#gmail.com",
"email2" : "sathya#knstek.com"
},
};
function print(bio) {
for (var key in bio) {
if (typeof(bio[key]) == 'object') {
print(bio[key]);
} else {
console.log(key + ": " + bio[key]);
}
}
}
print(myBio);
Most people searching in Google and finding this question are probably looking for something like this:
console.log(JSON.stringify(object, null, 2));
This prints deeply nested JSON to the console without the annoying "[Object]".
The last two parameters are optional. The 2 says to pretty print the JSON with an indent of 2. If left blank, will output non-formatted JSON.
This does not answer this specific question, but hopefully will answer those searching for "Print nested JSON Javascript".
Try this :
var myBio = {
"name":"Sathya",
"age":"23",
"position":"Soft.Engineer",
"email": {
"email1":"sathya#gmail.com",
"email2":"sathya#knstek.com"
}
};
var result = parseJSON(myBio);
console.log(result);
function parseJSON(obj) {
var parsedData = '';
for(var i in obj) {
if (typeof obj[i] == 'object') {
parsedData += parseJSON(obj[i]);
}else {
parsedData += i +' : '+ obj[i];
}//end if
parsedData += '\n';
}//end for
return parsedData;
}//end function
I have two lists in SharePoint 2013 Online. I need to get matching values for a user-entered key (string), sort, and display both lists as one. Easy enough if I were able to use SQL to create a view. Best solution seems to be to just display both lists.
I've tried using SPD Linked Sources, but the "linked field" option never displays and the no-preview SPD is awful (what were MS thinking?). Workflow isn't feasible. List items may be edited in 'datasheet view' (client requirement). Lookups require a selection to display the related field.
I can get both lists and display them separately.
What I have:
List 1 List 2
fruit apple type rome fruit apple state washington
fruit pear type bartlett fruit pear state oregon
fruit grapes type red fruit orange state florida
What I want:
fruit apple type rome state washington
fruit grapes type red
fruit orange state florida
fruit pear type bartlett state oregon
I'm missing two things (maybe more) an array that I can use for sorting and a comparison to use for matching the fruit on both lists. The real lists may have 50-120 items (each) that need to be matched.
All items should be returned. If there's a match, the data should be in the same row. If not, blanks should display.
The code below displays via an html page where the ID for each of the table cells matches the cell references in the script below. It's not sorted and the rows don't match.
$(function() {
$.ajax({
url: "sharepointlist/_api/web/lists/GetByTitle('List1')/items",
type: "GET",
headers: { "accept": "application/json;odata=verbose"
},
}).success(function (data) {
var title = '';
var type = '';
$.each(data.d.results,
function (key, value) {
title += "Title: " + value.Title + "<br/>";
type += "Type: " + value.Type + "<br/>";
});
$("#tdtitle").html(title);
$("#tdtype").html(status);
$.ajax({
url: "sharepointlist/_api/web/lists/GetByTitle('List2')/items",
type: "GET",
headers: { "accept": "application/json;odata=verbose"
},
}).success(function (data) {
var title2 = '';
var state = '';
$.each(data.d.results,
function (key, value) {
title2 += "Title2: " + value.Title + "<br/>";
city += "State: " + value.State + "<br/>";
});
$("#tdsecond").html(title2);
$("#tdstate").html(city);
It seems you are trying to perform the "join" operation on list items returned from REST queries. If so, you could consider the following approach
function getListItems(webUrl,listTitle,selectProperties){
return $.getJSON( webUrl + "/_api/web/lists/GetByTitle('" + listTitle + "')/items?$select=" + selectProperties.join(','))
.then(function(data){
return data.value.map(function(item){
return selectProperties.reduce(function(result, key) {
result[key] = item[key];
return result;
},{});
});
});
}
function joinListItems(left, right, key) {
if(left.length == 0 || right.length == 0)
return new Error("No data was found");
var columns = Object.keys(left[0]).concat(Object.keys(right[0]));
var createRow = function(left,right){
var row = {};
columns.forEach(function(key){
row[key] = null;
});
var values = left != null ? left : right;
for(var name in values) row[name] = values[name];
return row;
};
var updateRow = function(existingRow,values){
for(var name in values) existingRow[name] = values[name];
};
return left.concat(right).reduce(function(result, current, index){
if(index < left.length){
result.rows.push(createRow(current,null));
result.keys[current[key]] = index;
}
else {
var rowIdx = result.keys[current[key]];
if(typeof rowIdx !== 'undefined'){
updateRow(result.rows[rowIdx],current);
}
else {
result.rows.push(createRow(null,current));
}
}
return result;
},{rows: [], keys: {}}).rows;
}
$.when(
// Get List1
getListItems( _spPageContextInfo.webAbsoluteUrl, "List1",['Title','Type']),
// Get List2
getListItems( _spPageContextInfo.webAbsoluteUrl, "List2",['Title','State'])
)
.then(function(items1,items2){
var key='Title';
var result = joinListItems(items1,items2,key);
result = result.sort(function(a, b){
return a.Title.localeCompare(b.Title);
});
console.log(JSON.stringify(result,null,2));
//displayResults(result);
});
//print results (from comment section)
function displayResults(items){
var title = '';
var type = '';
$.each(items, function (index, item) {
title += "Title: " + item.Title + "<br/>";
type += "Type: " + item.Type + "<br/>";
});
}
You also might find this thread helpful that specifically discusses
join operation.
Result
[
{
"Title": "fruit apple",
"Type": "type rome",
"State": "state washington"
},
{
"Title": "fruit grapes",
"Type": "type red",
"State": null
},
{
"Title": "fruit orange",
"State": "state florida",
"Type": null
},
{
"Title": "fruit pear",
"Type": "type bartlett",
"State": "state oregon"
}
]
Update for sort function
Replace:
result = result.sort(function(a, b){
return a.Title.localeCompare(b.Title);
});
with
result = result.sort(function(a, b){
if(!a.Title) a.Title = "";
if(!b.Title) b.Title = "";
return a.Title.localeCompare(b.Title);
});
I would divide task in two parts.
First of all you need to combine data retrieved from GET calls.
You may consider using promises for that in a way:
$.when(
// Get List1
$.get("sharepointlist/_api/web/lists/GetByTitle('List1')/item").then( function(data) {
return data.d.results;
}),
// Get List2
$.get("sharepointlist/_api/web/lists/GetByTitle('List2')/items").then( function(data) {
return data.d.results;
})
).then(processData);
function processData(list1, list2) {
var res = list1.concat(list2);
}
Now you need to process your data. First of all you sort your new array by Title.
newData = newData.sort(function(a, b){
return a.Title.localeCompare(b.Title);
});
Now you need to loop through sorted data and combine objects with the same Title.
res = res.reduce(function(a, b) {
var t = a.slice(-1)[0]; //it's like getting last element
if (t && t.Title === b.Title) {
if (b.State) {
t.State = b.State;
} else {
t.Type = b.Type;
}
} else {
a.push(b);
}
return a;
}, []);
Now just assign new data into DOM.
UPDATE:
The example of merging all properties while joining elements using jQuery $.extend().
res = res.reduce(function(a, b) {
var t = a.slice(-1)[0]; //it's like getting last element
if (t && t.Title === b.Title) {
$.extend(t, b);
} else {
a.push(b);
}
return a;
}, []);
PS: jQuery $.extend() ignores, properties that are null or undefined.
The link to working solution at Plunkr with hardcoded JSON files.
https://plnkr.co/edit/gYJjyT8lCCNTe6EAlSYB
This question already has answers here:
Is there an equivalent for var_dump (PHP) in Javascript?
(20 answers)
Closed 5 years ago.
I would like to see the structure of object in JavaScript (for debugging). Is there anything similar to var_dump in PHP?
Most modern browsers have a console in their developer tools, useful for this sort of debugging.
console.log(myvar);
Then you will get a nicely mapped out interface of the object/whatever in the console.
Check out the console documentation for more details.
Most common way:
console.log(object);
However I must mention JSON.stringify which is useful to dump variables in non-browser scripts:
console.log( JSON.stringify(object) );
The JSON.stringify function also supports built-in prettification as pointed out by Simon Zyx.
Example:
var obj = {x: 1, y: 2, z: 3};
console.log( JSON.stringify(obj, null, 2) ); // spacing level = 2
The above snippet will print:
{
"x": 1,
"y": 2,
"z": 3
}
On caniuse.com you can view the browsers that support natively the JSON.stringify function: http://caniuse.com/json
You can also use the Douglas Crockford library to add JSON.stringify support on old browsers: https://github.com/douglascrockford/JSON-js
Docs for JSON.stringify: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify
I hope this helps :-)
I wrote this JS function dump() to work like PHP's var_dump().
To show the contents of the variable in an alert window: dump(variable)
To show the contents of the variable in the web page: dump(variable, 'body')
To just get a string of the variable: dump(variable, 'none')
/* repeatString() returns a string which has been repeated a set number of times */
function repeatString(str, num) {
out = '';
for (var i = 0; i < num; i++) {
out += str;
}
return out;
}
/*
dump() displays the contents of a variable like var_dump() does in PHP. dump() is
better than typeof, because it can distinguish between array, null and object.
Parameters:
v: The variable
howDisplay: "none", "body", "alert" (default)
recursionLevel: Number of times the function has recursed when entering nested
objects or arrays. Each level of recursion adds extra space to the
output to indicate level. Set to 0 by default.
Return Value:
A string of the variable's contents
Limitations:
Can't pass an undefined variable to dump().
dump() can't distinguish between int and float.
dump() can't tell the original variable type of a member variable of an object.
These limitations can't be fixed because these are *features* of JS. However, dump()
*/
function dump(v, howDisplay, recursionLevel) {
howDisplay = (typeof howDisplay === 'undefined') ? "alert" : howDisplay;
recursionLevel = (typeof recursionLevel !== 'number') ? 0 : recursionLevel;
var vType = typeof v;
var out = vType;
switch (vType) {
case "number":
/* there is absolutely no way in JS to distinguish 2 from 2.0
so 'number' is the best that you can do. The following doesn't work:
var er = /^[0-9]+$/;
if (!isNaN(v) && v % 1 === 0 && er.test(3.0)) {
out = 'int';
}
*/
break;
case "boolean":
out += ": " + v;
break;
case "string":
out += "(" + v.length + '): "' + v + '"';
break;
case "object":
//check if null
if (v === null) {
out = "null";
}
//If using jQuery: if ($.isArray(v))
//If using IE: if (isArray(v))
//this should work for all browsers according to the ECMAScript standard:
else if (Object.prototype.toString.call(v) === '[object Array]') {
out = 'array(' + v.length + '): {\n';
for (var i = 0; i < v.length; i++) {
out += repeatString(' ', recursionLevel) + " [" + i + "]: " +
dump(v[i], "none", recursionLevel + 1) + "\n";
}
out += repeatString(' ', recursionLevel) + "}";
}
else {
//if object
let sContents = "{\n";
let cnt = 0;
for (var member in v) {
//No way to know the original data type of member, since JS
//always converts it to a string and no other way to parse objects.
sContents += repeatString(' ', recursionLevel) + " " + member +
": " + dump(v[member], "none", recursionLevel + 1) + "\n";
cnt++;
}
sContents += repeatString(' ', recursionLevel) + "}";
out += "(" + cnt + "): " + sContents;
}
break;
default:
out = v;
break;
}
if (howDisplay == 'body') {
var pre = document.createElement('pre');
pre.innerHTML = out;
document.body.appendChild(pre);
}
else if (howDisplay == 'alert') {
alert(out);
}
return out;
}
The var_dump equivalent in JavaScript? Simply, there isn't one.
But, that doesn't mean you're left helpless. Like some have suggested, use Firebug (or equivalent in other browsers), but unlike what others suggested, don't use console.log when you have a (slightly) better tool console.dir:
console.dir(object)
Prints an interactive listing of all properties of the object. This
looks identical to the view that you would see in the DOM tab.
As others have already mentioned, the best way to debug your variables is to use a modern browser's developer console (e.g. Chrome Developer Tools, Firefox+Firebug, Opera Dragonfly (which now disappeared in the new Chromium-based (Blink) Opera, but as developers say, "Dragonfly is not dead though we cannot give you more information yet").
But in case you need another approach, there's a really useful site called
php.js:
http://phpjs.org/
which provides "JavaScript alternatives to PHP functions" - so you can use them the similar way as you would in PHP. I will copy-paste the appropriate functions to you here, BUT be aware that these codes can get updated on the original site in case some bugs are detected, so I suggest you visiting the phpjs.org site! (Btw. I'm NOT affiliated with the site, but I find it extremely useful.)
var_dump() in JavaScript
Here is the code of the JS-alternative of var_dump():
http://phpjs.org/functions/var_dump/
it depends on the echo() function: http://phpjs.org/functions/echo/
function var_dump() {
// discuss at: http://phpjs.org/functions/var_dump/
// original by: Brett Zamir (http://brett-zamir.me)
// improved by: Zahlii
// improved by: Brett Zamir (http://brett-zamir.me)
// depends on: echo
// note: For returning a string, use var_export() with the second argument set to true
// test: skip
// example 1: var_dump(1);
// returns 1: 'int(1)'
var output = '',
pad_char = ' ',
pad_val = 4,
lgth = 0,
i = 0;
var _getFuncName = function(fn) {
var name = (/\W*function\s+([\w\$]+)\s*\(/)
.exec(fn);
if (!name) {
return '(Anonymous)';
}
return name[1];
};
var _repeat_char = function(len, pad_char) {
var str = '';
for (var i = 0; i < len; i++) {
str += pad_char;
}
return str;
};
var _getInnerVal = function(val, thick_pad) {
var ret = '';
if (val === null) {
ret = 'NULL';
} else if (typeof val === 'boolean') {
ret = 'bool(' + val + ')';
} else if (typeof val === 'string') {
ret = 'string(' + val.length + ') "' + val + '"';
} else if (typeof val === 'number') {
if (parseFloat(val) == parseInt(val, 10)) {
ret = 'int(' + val + ')';
} else {
ret = 'float(' + val + ')';
}
}
// The remaining are not PHP behavior because these values only exist in this exact form in JavaScript
else if (typeof val === 'undefined') {
ret = 'undefined';
} else if (typeof val === 'function') {
var funcLines = val.toString()
.split('\n');
ret = '';
for (var i = 0, fll = funcLines.length; i < fll; i++) {
ret += (i !== 0 ? '\n' + thick_pad : '') + funcLines[i];
}
} else if (val instanceof Date) {
ret = 'Date(' + val + ')';
} else if (val instanceof RegExp) {
ret = 'RegExp(' + val + ')';
} else if (val.nodeName) {
// Different than PHP's DOMElement
switch (val.nodeType) {
case 1:
if (typeof val.namespaceURI === 'undefined' || val.namespaceURI === 'http://www.w3.org/1999/xhtml') {
// Undefined namespace could be plain XML, but namespaceURI not widely supported
ret = 'HTMLElement("' + val.nodeName + '")';
} else {
ret = 'XML Element("' + val.nodeName + '")';
}
break;
case 2:
ret = 'ATTRIBUTE_NODE(' + val.nodeName + ')';
break;
case 3:
ret = 'TEXT_NODE(' + val.nodeValue + ')';
break;
case 4:
ret = 'CDATA_SECTION_NODE(' + val.nodeValue + ')';
break;
case 5:
ret = 'ENTITY_REFERENCE_NODE';
break;
case 6:
ret = 'ENTITY_NODE';
break;
case 7:
ret = 'PROCESSING_INSTRUCTION_NODE(' + val.nodeName + ':' + val.nodeValue + ')';
break;
case 8:
ret = 'COMMENT_NODE(' + val.nodeValue + ')';
break;
case 9:
ret = 'DOCUMENT_NODE';
break;
case 10:
ret = 'DOCUMENT_TYPE_NODE';
break;
case 11:
ret = 'DOCUMENT_FRAGMENT_NODE';
break;
case 12:
ret = 'NOTATION_NODE';
break;
}
}
return ret;
};
var _formatArray = function(obj, cur_depth, pad_val, pad_char) {
var someProp = '';
if (cur_depth > 0) {
cur_depth++;
}
var base_pad = _repeat_char(pad_val * (cur_depth - 1), pad_char);
var thick_pad = _repeat_char(pad_val * (cur_depth + 1), pad_char);
var str = '';
var val = '';
if (typeof obj === 'object' && obj !== null) {
if (obj.constructor && _getFuncName(obj.constructor) === 'PHPJS_Resource') {
return obj.var_dump();
}
lgth = 0;
for (someProp in obj) {
lgth++;
}
str += 'array(' + lgth + ') {\n';
for (var key in obj) {
var objVal = obj[key];
if (typeof objVal === 'object' && objVal !== null && !(objVal instanceof Date) && !(objVal instanceof RegExp) &&
!
objVal.nodeName) {
str += thick_pad + '[' + key + '] =>\n' + thick_pad + _formatArray(objVal, cur_depth + 1, pad_val,
pad_char);
} else {
val = _getInnerVal(objVal, thick_pad);
str += thick_pad + '[' + key + '] =>\n' + thick_pad + val + '\n';
}
}
str += base_pad + '}\n';
} else {
str = _getInnerVal(obj, thick_pad);
}
return str;
};
output = _formatArray(arguments[0], 0, pad_val, pad_char);
for (i = 1; i < arguments.length; i++) {
output += '\n' + _formatArray(arguments[i], 0, pad_val, pad_char);
}
this.echo(output);
}
print_r() in JavaScript
Here is the print_r() function:
http://phpjs.org/functions/print_r/
It depends on echo() too.
function print_r(array, return_val) {
// discuss at: http://phpjs.org/functions/print_r/
// original by: Michael White (http://getsprink.com)
// improved by: Ben Bryan
// improved by: Brett Zamir (http://brett-zamir.me)
// improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
// input by: Brett Zamir (http://brett-zamir.me)
// depends on: echo
// example 1: print_r(1, true);
// returns 1: 1
var output = '',
pad_char = ' ',
pad_val = 4,
d = this.window.document,
getFuncName = function(fn) {
var name = (/\W*function\s+([\w\$]+)\s*\(/)
.exec(fn);
if (!name) {
return '(Anonymous)';
}
return name[1];
};
repeat_char = function(len, pad_char) {
var str = '';
for (var i = 0; i < len; i++) {
str += pad_char;
}
return str;
};
formatArray = function(obj, cur_depth, pad_val, pad_char) {
if (cur_depth > 0) {
cur_depth++;
}
var base_pad = repeat_char(pad_val * cur_depth, pad_char);
var thick_pad = repeat_char(pad_val * (cur_depth + 1), pad_char);
var str = '';
if (typeof obj === 'object' && obj !== null && obj.constructor && getFuncName(obj.constructor) !==
'PHPJS_Resource') {
str += 'Array\n' + base_pad + '(\n';
for (var key in obj) {
if (Object.prototype.toString.call(obj[key]) === '[object Array]') {
str += thick_pad + '[' + key + '] => ' + formatArray(obj[key], cur_depth + 1, pad_val, pad_char);
} else {
str += thick_pad + '[' + key + '] => ' + obj[key] + '\n';
}
}
str += base_pad + ')\n';
} else if (obj === null || obj === undefined) {
str = '';
} else {
// for our "resource" class
str = obj.toString();
}
return str;
};
output = formatArray(array, 0, pad_val, pad_char);
if (return_val !== true) {
if (d.body) {
this.echo(output);
} else {
try {
// We're in XUL, so appending as plain text won't work; trigger an error out of XUL
d = XULDocument;
this.echo('<pre xmlns="http://www.w3.org/1999/xhtml" style="white-space:pre;">' + output + '</pre>');
} catch (e) {
// Outputting as plain text may work in some plain XML
this.echo(output);
}
}
return true;
}
return output;
}
var_export() in JavaScript
You may also find the var_export() alternative useful, which also depends on echo():
http://phpjs.org/functions/var_export/
function var_export(mixed_expression, bool_return) {
// discuss at: http://phpjs.org/functions/var_export/
// original by: Philip Peterson
// improved by: johnrembo
// improved by: Brett Zamir (http://brett-zamir.me)
// input by: Brian Tafoya (http://www.premasolutions.com/)
// input by: Hans Henrik (http://hanshenrik.tk/)
// bugfixed by: Brett Zamir (http://brett-zamir.me)
// bugfixed by: Brett Zamir (http://brett-zamir.me)
// depends on: echo
// example 1: var_export(null);
// returns 1: null
// example 2: var_export({0: 'Kevin', 1: 'van', 2: 'Zonneveld'}, true);
// returns 2: "array (\n 0 => 'Kevin',\n 1 => 'van',\n 2 => 'Zonneveld'\n)"
// example 3: data = 'Kevin';
// example 3: var_export(data, true);
// returns 3: "'Kevin'"
var retstr = '',
iret = '',
value,
cnt = 0,
x = [],
i = 0,
funcParts = [],
// We use the last argument (not part of PHP) to pass in
// our indentation level
idtLevel = arguments[2] || 2,
innerIndent = '',
outerIndent = '',
getFuncName = function(fn) {
var name = (/\W*function\s+([\w\$]+)\s*\(/)
.exec(fn);
if (!name) {
return '(Anonymous)';
}
return name[1];
};
_makeIndent = function(idtLevel) {
return (new Array(idtLevel + 1))
.join(' ');
};
__getType = function(inp) {
var i = 0,
match, types, cons, type = typeof inp;
if (type === 'object' && (inp && inp.constructor) &&
getFuncName(inp.constructor) === 'PHPJS_Resource') {
return 'resource';
}
if (type === 'function') {
return 'function';
}
if (type === 'object' && !inp) {
// Should this be just null?
return 'null';
}
if (type === 'object') {
if (!inp.constructor) {
return 'object';
}
cons = inp.constructor.toString();
match = cons.match(/(\w+)\(/);
if (match) {
cons = match[1].toLowerCase();
}
types = ['boolean', 'number', 'string', 'array'];
for (i = 0; i < types.length; i++) {
if (cons === types[i]) {
type = types[i];
break;
}
}
}
return type;
};
type = __getType(mixed_expression);
if (type === null) {
retstr = 'NULL';
} else if (type === 'array' || type === 'object') {
outerIndent = _makeIndent(idtLevel - 2);
innerIndent = _makeIndent(idtLevel);
for (i in mixed_expression) {
value = this.var_export(mixed_expression[i], 1, idtLevel + 2);
value = typeof value === 'string' ? value.replace(/</g, '<')
.
replace(/>/g, '>'): value;
x[cnt++] = innerIndent + i + ' => ' +
(__getType(mixed_expression[i]) === 'array' ?
'\n' : '') + value;
}
iret = x.join(',\n');
retstr = outerIndent + 'array (\n' + iret + '\n' + outerIndent + ')';
} else if (type === 'function') {
funcParts = mixed_expression.toString()
.
match(/function .*?\((.*?)\) \{([\s\S]*)\}/);
// For lambda functions, var_export() outputs such as the following:
// '\000lambda_1'. Since it will probably not be a common use to
// expect this (unhelpful) form, we'll use another PHP-exportable
// construct, create_function() (though dollar signs must be on the
// variables in JavaScript); if using instead in JavaScript and you
// are using the namespaced version, note that create_function() will
// not be available as a global
retstr = "create_function ('" + funcParts[1] + "', '" +
funcParts[2].replace(new RegExp("'", 'g'), "\\'") + "')";
} else if (type === 'resource') {
// Resources treated as null for var_export
retstr = 'NULL';
} else {
retstr = typeof mixed_expression !== 'string' ? mixed_expression :
"'" + mixed_expression.replace(/(["'])/g, '\\$1')
.
replace(/\0/g, '\\0') + "'";
}
if (!bool_return) {
this.echo(retstr);
return null;
}
return retstr;
}
echo() in JavaScript
http://phpjs.org/functions/echo/
function echo() {
// discuss at: http://phpjs.org/functions/echo/
// original by: Philip Peterson
// improved by: echo is bad
// improved by: Nate
// improved by: Brett Zamir (http://brett-zamir.me)
// improved by: Brett Zamir (http://brett-zamir.me)
// improved by: Brett Zamir (http://brett-zamir.me)
// revised by: Der Simon (http://innerdom.sourceforge.net/)
// bugfixed by: Eugene Bulkin (http://doubleaw.com/)
// bugfixed by: Brett Zamir (http://brett-zamir.me)
// bugfixed by: Brett Zamir (http://brett-zamir.me)
// bugfixed by: EdorFaus
// input by: JB
// note: If browsers start to support DOM Level 3 Load and Save (parsing/serializing),
// note: we wouldn't need any such long code (even most of the code below). See
// note: link below for a cross-browser implementation in JavaScript. HTML5 might
// note: possibly support DOMParser, but that is not presently a standard.
// note: Although innerHTML is widely used and may become standard as of HTML5, it is also not ideal for
// note: use with a temporary holder before appending to the DOM (as is our last resort below),
// note: since it may not work in an XML context
// note: Using innerHTML to directly add to the BODY is very dangerous because it will
// note: break all pre-existing references to HTMLElements.
// example 1: echo('<div><p>abc</p><p>abc</p></div>');
// returns 1: undefined
var isNode = typeof module !== 'undefined' && module.exports && typeof global !== "undefined" && {}.toString.call(
global) == '[object global]';
if (isNode) {
var args = Array.prototype.slice.call(arguments);
return console.log(args.join(' '));
}
var arg = '';
var argc = arguments.length;
var argv = arguments;
var i = 0;
var holder, win = this.window;
var d = win.document;
var ns_xhtml = 'http://www.w3.org/1999/xhtml';
// If we're in a XUL context
var ns_xul = 'http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul';
var stringToDOM = function(str, parent, ns, container) {
var extraNSs = '';
if (ns === ns_xul) {
extraNSs = ' xmlns:html="' + ns_xhtml + '"';
}
var stringContainer = '<' + container + ' xmlns="' + ns + '"' + extraNSs + '>' + str + '</' + container + '>';
var dils = win.DOMImplementationLS;
var dp = win.DOMParser;
var ax = win.ActiveXObject;
if (dils && dils.createLSInput && dils.createLSParser) {
// Follows the DOM 3 Load and Save standard, but not
// implemented in browsers at present; HTML5 is to standardize on innerHTML, but not for XML (though
// possibly will also standardize with DOMParser); in the meantime, to ensure fullest browser support, could
// attach http://svn2.assembla.com/svn/brettz9/DOMToString/DOM3.js (see http://svn2.assembla.com/svn/brettz9/DOMToString/DOM3.xhtml for a simple test file)
var lsInput = dils.createLSInput();
// If we're in XHTML, we'll try to allow the XHTML namespace to be available by default
lsInput.stringData = stringContainer;
// synchronous, no schema type
var lsParser = dils.createLSParser(1, null);
return lsParser.parse(lsInput)
.firstChild;
} else if (dp) {
// If we're in XHTML, we'll try to allow the XHTML namespace to be available by default
try {
var fc = new dp()
.parseFromString(stringContainer, 'text/xml');
if (fc && fc.documentElement && fc.documentElement.localName !== 'parsererror' && fc.documentElement.namespaceURI !==
'http://www.mozilla.org/newlayout/xml/parsererror.xml') {
return fc.documentElement.firstChild;
}
// If there's a parsing error, we just continue on
} catch (e) {
// If there's a parsing error, we just continue on
}
} else if (ax) {
// We don't bother with a holder in Explorer as it doesn't support namespaces
var axo = new ax('MSXML2.DOMDocument');
axo.loadXML(str);
return axo.documentElement;
}
/*else if (win.XMLHttpRequest) {
// Supposed to work in older Safari
var req = new win.XMLHttpRequest;
req.open('GET', 'data:application/xml;charset=utf-8,'+encodeURIComponent(str), false);
if (req.overrideMimeType) {
req.overrideMimeType('application/xml');
}
req.send(null);
return req.responseXML;
}*/
// Document fragment did not work with innerHTML, so we create a temporary element holder
// If we're in XHTML, we'll try to allow the XHTML namespace to be available by default
//if (d.createElementNS && (d.contentType && d.contentType !== 'text/html')) {
// Don't create namespaced elements if we're being served as HTML (currently only Mozilla supports this detection in true XHTML-supporting browsers, but Safari and Opera should work with the above DOMParser anyways, and IE doesn't support createElementNS anyways)
if (d.createElementNS && // Browser supports the method
(d.documentElement.namespaceURI || // We can use if the document is using a namespace
d.documentElement.nodeName.toLowerCase() !== 'html' || // We know it's not HTML4 or less, if the tag is not HTML (even if the root namespace is null)
(d.contentType && d.contentType !== 'text/html') // We know it's not regular HTML4 or less if this is Mozilla (only browser supporting the attribute) and the content type is something other than text/html; other HTML5 roots (like svg) still have a namespace
)) {
// Don't create namespaced elements if we're being served as HTML (currently only Mozilla supports this detection in true XHTML-supporting browsers, but Safari and Opera should work with the above DOMParser anyways, and IE doesn't support createElementNS anyways); last test is for the sake of being in a pure XML document
holder = d.createElementNS(ns, container);
} else {
// Document fragment did not work with innerHTML
holder = d.createElement(container);
}
holder.innerHTML = str;
while (holder.firstChild) {
parent.appendChild(holder.firstChild);
}
return false;
// throw 'Your browser does not support DOM parsing as required by echo()';
};
var ieFix = function(node) {
if (node.nodeType === 1) {
var newNode = d.createElement(node.nodeName);
var i, len;
if (node.attributes && node.attributes.length > 0) {
for (i = 0, len = node.attributes.length; i < len; i++) {
newNode.setAttribute(node.attributes[i].nodeName, node.getAttribute(node.attributes[i].nodeName));
}
}
if (node.childNodes && node.childNodes.length > 0) {
for (i = 0, len = node.childNodes.length; i < len; i++) {
newNode.appendChild(ieFix(node.childNodes[i]));
}
}
return newNode;
} else {
return d.createTextNode(node.nodeValue);
}
};
var replacer = function(s, m1, m2) {
// We assume for now that embedded variables do not have dollar sign; to add a dollar sign, you currently must use {$$var} (We might change this, however.)
// Doesn't cover all cases yet: see http://php.net/manual/en/language.types.string.php#language.types.string.syntax.double
if (m1 !== '\\') {
return m1 + eval(m2);
} else {
return s;
}
};
this.php_js = this.php_js || {};
var phpjs = this.php_js;
var ini = phpjs.ini;
var obs = phpjs.obs;
for (i = 0; i < argc; i++) {
arg = argv[i];
if (ini && ini['phpjs.echo_embedded_vars']) {
arg = arg.replace(/(.?)\{?\$(\w*?\}|\w*)/g, replacer);
}
if (!phpjs.flushing && obs && obs.length) {
// If flushing we output, but otherwise presence of a buffer means caching output
obs[obs.length - 1].buffer += arg;
continue;
}
if (d.appendChild) {
if (d.body) {
if (win.navigator.appName === 'Microsoft Internet Explorer') {
// We unfortunately cannot use feature detection, since this is an IE bug with cloneNode nodes being appended
d.body.appendChild(stringToDOM(ieFix(arg)));
} else {
var unappendedLeft = stringToDOM(arg, d.body, ns_xhtml, 'div')
.cloneNode(true); // We will not actually append the div tag (just using for providing XHTML namespace by default)
if (unappendedLeft) {
d.body.appendChild(unappendedLeft);
}
}
} else {
// We will not actually append the description tag (just using for providing XUL namespace by default)
d.documentElement.appendChild(stringToDOM(arg, d.documentElement, ns_xul, 'description'));
}
} else if (d.write) {
d.write(arg);
} else {
console.log(arg);
}
}
}
Firebug.
Then, in your javascript:
var blah = {something: 'hi', another: 'noway'};
console.debug("Here is blah: %o", blah);
Now you can look at the console, click on the statement and see what is inside blah
A nice simple solution for parsing a JSON Response to HTML.
var json_response = jQuery.parseJSON(data);
html_response += 'JSON Response:<br />';
jQuery.each(json_response, function(k, v) {
html_response += outputJSONReponse(k, v);
});
function outputJSONReponse(k, v) {
var html_response = k + ': ';
if(jQuery.isArray(v) || jQuery.isPlainObject(v)) {
jQuery.each(v, function(j, w) {
html_response += outputJSONReponse(j, w);
});
} else {
html_response += v + '<br />';
}
return html_response;
}
You could also try this function. I can't remember the original author, but all credits go to them.
Works like a charm - 100% the same as var_dump in PHP.
Check it out.
function dump(arr,level) {
var dumped_text = "";
if(!level) level = 0;
//The padding given at the beginning of the line.
var level_padding = "";
for(var j=0;j<level+1;j++) level_padding += " ";
if(typeof(arr) == 'object') { //Array/Hashes/Objects
for(var item in arr) {
var value = arr[item];
if(typeof(value) == 'object') { //If it is an array,
dumped_text += level_padding + "'" + item + "' ...\n";
dumped_text += dump(value,level+1);
} else {
dumped_text += level_padding + "'" + item + "' => \"" + value + "\"\n";
}
}
} else { //Stings/Chars/Numbers etc.
dumped_text = "===>"+arr+"<===("+typeof(arr)+")";
}
return dumped_text;
}
// Example:
var employees = [
{ id: '1', sex: 'm', city: 'Paris' },
{ id: '2', sex: 'f', city: 'London' },
{ id: '3', sex: 'f', city: 'New York' },
{ id: '4', sex: 'm', city: 'Moscow' },
{ id: '5', sex: 'm', city: 'Berlin' }
]
// Open dev console (F12) to see results:
console.log(dump(employees));
I put this forward to help anyone needing something readily practical for giving you a nice, prettified (indented) picture of a JS Node. None of the other solutions worked for me for a Node ("cyclical error" or whatever...). This walks you through the tree under the DOM Node (without using recursion) and gives you the depth, tagName (if applicable) and textContent (if applicable).
Any other details from the nodes you encounter as you walk the tree under the head node can be added as per your interest...
function printRNode( node ){
// make sort of human-readable picture of the node... a bit like PHP print_r
if( node === undefined || node === null ){
throwError( 'node was ' + typeof node );
}
let s = '';
// NB walkDOM could be made into a utility function which you could
// call with one or more callback functions as parameters...
function walkDOM( headNode ){
const stack = [ headNode ];
const depthCountDowns = [ 1 ];
while (stack.length > 0) {
const node = stack.pop();
const depth = depthCountDowns.length - 1;
// TODO non-text, non-BR nodes could show more details (attributes, properties, etc.)
const stringRep = node.nodeType === 3? 'TEXT: |' + node.nodeValue + '|' : 'tag: ' + node.tagName;
s += ' '.repeat( depth ) + stringRep + '\n';
const lastIndex = depthCountDowns.length - 1;
depthCountDowns[ lastIndex ] = depthCountDowns[ lastIndex ] - 1;
if( node.childNodes.length ){
depthCountDowns.push( node.childNodes.length );
stack.push( ... Array.from( node.childNodes ).reverse() );
}
while( depthCountDowns[ depthCountDowns.length - 1 ] === 0 ){
depthCountDowns.splice( -1 );
}
}
}
walkDOM( node );
return s;
}