Serialize JavaScript's navigator object - javascript

I'm creating a page to help diagnose the problem our users are experiencing with our web pages (you know, asking a user "What browser are you using?" usually leads to "Internet").
This page already submits to me all the HTTP headers and now I'm trying to have JavaScript give some more informations, so I thought it would be great to have the user's navigator JavaScript object and I started looking how to serialize it so I can submit it through a form.
The problem is I'm not able to serialize the navigator object using any JSON library I know of, everyone returns an empty object (?!), so I decided to write an ad-hoc serializer.
You can find the code here:
<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.5.0/jquery.min.js" type="text/javascript"></script>
<script type="text/javascript">
function serialize (object) {
var type = typeof object;
if (object === null) {
return '"nullValue"';
}
if (type == 'string' || type === 'number' || type === 'boolean') {
return '"' + object + '"';
}
else if (type === 'function') {
return '"functionValue"';
}
else if (type === 'object') {
var output = '{';
for (var item in object) {
if (item !== 'enabledPlugin') {
output += '"' + item + '":' + serialize(object[item]) + ',';
}
}
return output.replace(/\,$/, '') + '}';
}
else if (type === 'undefined') {
return '"undefinedError"';
}
else {
return '"unknownTypeError"';
}
};
$(document).ready(function () {
$('#navigator').text(serialize(navigator));
});
</script>
<style type="text/css">
#navigator {
font-family: monospaced;
}
</style>
<title>Serialize</title>
</head>
<body>
<h1>Serialize</h1>
<p id="navigator"></p>
</body>
</html>
This code seems to work perfectly in Firefox, Opera, Chrome and Safari but (obviously) doesn't work in Internet Explorer (at least version 8.0), it complains that "Property or method not supported by the object" at line for (var item in object) {.
Do you have any hint on how to fix the code or how to reach the goal (serialize the navigator object) by other means?
Solution (v 2.0):
Replace
for (var item in object) {
if (item !== 'enabledPlugin') {
output += '"' + item + '":' + serialize(object[item]) + ',';
}
}
with
for (var item in object) {
try {
if (item !== 'enabledPlugin') {
output += '"' + item + '":' + serialize(object[item]) + ',';
}
}
catch (e) {
}
}
and it works.

Try putting it inside a new object
var _navigator = {};
for (var i in navigator) _navigator[i] = navigator[i];
And then serialize it (maybe using some JSON library if the browser doesn't have native JSON API, I use json2.js):
$('#navigator').text(JSON.stringify(_navigator));
Edit: It seems that Internet Explorer doesn't allow navigator.plugins and navigator.mimeTypes to be iterated over, so this works:
var _navigator = {};
for (var i in navigator) _navigator[i] = navigator[i];
delete _navigator.plugins;
delete _navigator.mimeTypes;
$('#navigator').text(JSON.stringify(_navigator));

JSON in accepted answer contains only top level elements. Check this out https://jsfiddle.net/j1zb7qm0/ - _navigator.connection is empty. I've wrote a small func to collect all nested properties:
function recur(obj) {
var result = {}, _tmp;
for (var i in obj) {
// enabledPlugin is too nested, also skip functions
if (i === 'enabledPlugin' || typeof obj[i] === 'function') {
continue;
} else if (typeof obj[i] === 'object') {
// get props recursively
_tmp = recur(obj[i]);
// if object is not {}
if (Object.keys(_tmp).length) {
result[i] = _tmp;
}
} else {
// string, number or boolean
result[i] = obj[i];
}
}
return result;
}
You can use it like this var _navigator = recur(navigator) or create your own wrapper. In fact you can use it to iterate over and copy any nested object.

Related

JavaScript .startWith() function not working in IE, inside angularjs project

Hi im using Angularjs for my project, There is nationality search drop down. I want to map which is typing on Input and filter it inside nationality JSON object. This part is working fine in other browsers except IE. There is console error "Object doesn't support property or method 'startsWith'". this is my code, Can i know how to add "String.prototype.startsWith" for this issue for my code.
$scope.searchNationality = function (data) {
var output = [];
if (data != "" && data != undefined) {
$scope.ShowNationalityDropDown = true;
for (var i = 0; i < $scope.nationalityList.length; i++) {
if ($scope.nationalityList[i].content.toLowerCase().startsWith(data.toLowerCase())) {
output.push($scope.nationalityList[i]);
}
}
$scope.nationalityListSearchResults = output;
} else {
$scope.ShowNationalityDropDown = false;
$scope.nationalityListSearchResults = [];
}
};
You can try changing from .startsWith to .indexOf since it is compatible with IE for lower versions. If .indexOf returns 0 then the string is in the first position of the string that calls that function, which can be usable when you are in this kind of situation that you can't use .startsWith().
const str = "Hey this is a sample string!"
console.log(str.indexOf("Hey") === 0)
console.log(str.indexOf("sample") === 0)
$scope.searchNationality = function (data) {
var thereIsData = data != "" && data != undefined;
var output = thereIsData
? $scope.nationalityList.filter(function (nationality) {
return nationality.content.toLowerCase().indexOf(data.toLowerCase())) == 0;
})
: [];
$scope.ShowNationalityDropDown = thereIsData;
}

How to copy the objects from chrome console window?

I have tried to copy the objects as text, but it show just [object object]. Before this I had tried with copy commend it was success but not now.Is that chrome issue?
What I tried?
Just Right click on the object and store as global variable from chrome console window, then next just used copy(temp6) command and tried to paste in notepad++.
It should ideally copy the object with the copy command that you wrote.
I just tried it and worked for me.
Something else that you can try to do is to stringify that object and then copy it.
Ex.
copy(JSON.stringify(temp6))
If the object already logged
Right-click on the object in console and click Store as a global
variable the output will be something like temp1
Copy and paste below code in chrome console and hit enter
(function(console){
console.save = function(data, filename){
if(!data) {
console.error('Console.save: No data')
return;
}
if(!filename) filename = 'console.json'
if(typeof data === "object"){
data = JSON.stringify(data, undefined, 4)
}
var blob = new Blob([data], {type: 'text/json'}),
e = document.createEvent('MouseEvents'),
a = document.createElement('a')
a.download = filename
a.href = window.URL.createObjectURL(blob)
a.dataset.downloadurl = ['text/json', a.download, a.href].join(':')
e.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null)
a.dispatchEvent(e)
}
})(console)
Then you can use the function for downloading,
console.save(temp1);
-If it shows Uncaught TypeError: Converting circular structure to JSON
then you need decycle JSON object and paste below code in chrome browser console and hit enter
if (typeof JSON.decycle !== "function") {
JSON.decycle = function decycle(object, replacer) {
"use strict";
var objects = new WeakMap(); // object to path mappings
return (function derez(value, path) {
var old_path;
var nu;
if (replacer !== undefined) {
value = replacer(value);
}
if (
typeof value === "object" && value !== null &&
!(value instanceof Boolean) &&
!(value instanceof Date) &&
!(value instanceof Number) &&
!(value instanceof RegExp) &&
!(value instanceof String)
) {
old_path = objects.get(value);
if (old_path !== undefined) {
return {$ref: old_path};
}
objects.set(value, path);
if (Array.isArray(value)) {
nu = [];
value.forEach(function (element, i) {
nu[i] = derez(element, path + "[" + i + "]");
});
} else {
nu = {};
Object.keys(value).forEach(function (name) {
nu[name] = derez(
value[name],
path + "[" + JSON.stringify(name) + "]"
);
});
}
return nu;
}
return value;
}(object, "$"));
};
}
if (typeof JSON.retrocycle !== "function") {
JSON.retrocycle = function retrocycle($) {
"use strict";
var px = /^\$(?:\[(?:\d+|"(?:[^\\"\u0000-\u001f]|\\([\\"\/bfnrt]|u[0-9a-zA-Z]{4}))*")\])*$/;
(function rez(value) {
if (value && typeof value === "object") {
if (Array.isArray(value)) {
value.forEach(function (element, i) {
if (typeof element === "object" && element !== null) {
var path = element.$ref;
if (typeof path === "string" && px.test(path)) {
value[i] = eval(path);
} else {
rez(element);
}
}
});
} else {
Object.keys(value).forEach(function (name) {
var item = value[name];
if (typeof item === "object" && item !== null) {
var path = item.$ref;
if (typeof path === "string" && px.test(path)) {
value[name] = eval(path);
} else {
rez(item);
}
}
});
}
}
}($));
return $;
};
}
Then finally execute code for downloading.
console.save(JSON.decycle(temp1));
You can use command in console as follows:
Let say our object is:
var object = {x:"xyz"}
Now use below command in console -
copy(JSON.stringify(object))
object is now available to clipboard.You can now use Ctrl + v to use this object.
You should check thecount object to avoid circular reference, before using copy(JSON.stringify(count)), please see here
there can be many ways to do this. One way could be to do JSON.stringify(yourObject) and then copy the output.
You can also do this without having to write any code. At least with later version of chrome.
When you right click the object you get this context:
But if you left click the line to highlight it, the right click the console line you get this context menu:
The "Save as..." option will create text file (*.log) of everything "as is" currently on the console log. So if you want to see more of the object simply expand it as far as you need.
collapsed example:
let tmpArr = []; tmpArr.push([]); tmpArr[0].push({ some: 'test'}); tmpArr[0].push({ some: 'next'}); console.log(tmpArr);
VM242:1 [Array(2)]0: (2) [{…}, {…}]length: 1[[Prototype]]: Array(0)
undefined
null
null
expanded example:
let tmpArr = []; tmpArr.push([]); tmpArr[0].push({ some: 'test'}); tmpArr[0].push({ some: 'next'}); console.log(tmpArr);
VM242:1 [Array(2)]0: Array(2)0: some: "test"[[Prototype]]: Object1: some: "next"[[Prototype]]: Objectlength: 2[[Prototype]]: Array(0)length: 1[[Prototype]]: Array(0)
undefined
null
null

JavaScript - use link parameter to create text in the H1 tag of another page

In this example I have two pages - 1 product page, and 1 conversion page.
On the product page I will have a link that points to the conversion page. On this link I would like to pass the product name via a parameter. something like this: href = conversionpage.html?productName
On the conversion page I would like use JavaScript to take the product name parameter and populate the h1 tag - so the h1 tag would be something like this < h1 >productName< /h1 >
Make sense? I have no idea how to do this.
Thank you in advance for your help. I have 100,000 + product pages this example was just to simplify the issue.
Here is what I think you want to do.
Get the URL search parameters, then take the one you need and place it in the innerHTML of the desired tag.
Loops = function(collection, fn) {
'use strict';
var i;
if ((collection.item && collection.length) || collection instanceof Array || collection instanceof Element || collection.elements || collection.jquery) {
i = collection.length;
if (i > -1) {
do {
if (collection[i] !== undefined) {
fn(i);
}
} while (--i >= 0);
}
return this;
} else {
throw new Error('"collection" (' + collection + ') is not valid. It should be an array or have an "item" method and a "length" property');
}
};
GetURLParameters = function(keys) {
'use strict';
var pair, arr, query, parameters, queryString;
if (location.search) {
query = location.search.substring(1);
parameters = query.split("&");
queryString = {};
}
function createObject(key, val, i) {
pair = parameters[i].split("=");
if (typeof queryString[pair[key]] === "undefined") {
queryString[pair[key]] = decodeURI(pair[val]);
} else if (typeof queryString[pair[key]] === "string") {
arr = [queryString[pair[key]], pair[val]];
queryString[pair[key]] = arr;
} else {
queryString[pair[key]].push(pair[val]);
}
}
if (parameters && keys === 1) {
Loops(parameters, function(i) {
createObject(1, 0, i);
});
} else if (parameters) {
Loops(parameters, function(i) {
createObject(0, 1, i);
});
}
return queryString;
};
/** \/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/ **/
var params = GetURLParameters();
console.log(params);
document.getElementById('h1').innerHTML = params['parameter-name'];
<h1 id="h1"></h1>
with a url of http://example.com?productName=Walkman
<body>
<h1 id="productName"></h1>
</body>
<script type='text/javascript'>
// start by creating a function
function loadUp(){
var str = window.location.search.replace(/(?:(\D+=))/ig, "") //get the search parameters from the url and remove everything before the "=" sign
document.getElementById('productName').innerHTML = str //assign that string to the "innerHTML" of the h1 tag that has an id of "productName"
};
window.onload = loadUp; // once the page has loaded, fire off that function
</script>
this script will do this once the document has been loaded:
<body>
<h1 id="productName">Walkman</h1>
</body>

Is there a way to manipulate included JS without eval()?

I know lots of people think "eval is evil," but I have to accomplish something and I'm having trouble figuring out how to do it without eval().
The situation is this: an external file (I have no control over it--EDIT: but it's not user-generated. It's from a trusted source! I imagine this is important) is spitting out JavaScript for me to use. This JavaScript contains some nice JSON data (which is what I need to get), but it's flanked by ordinary JavaScript statements declaring variables and calling functions and such. It looks kinda like this:
var foo = new Object();
foo['KEY'] = {Field1: 'Value1', Field2: 'Value2'};
eval('fooFunction(foo)');
If I eval() this, I can just parse foo['KEY'] and be done with it. The only way I can think to do this without eval() is with a bunch of annoying replace()ments, which hardly seems better. Am I missing some obvious way to do this? Most of the "you don't have to use eval()" alternatives I usually see assume I have complete control over everything, but in this case I have to work around this existing code.
EDIT: I should add that this code is being obtained via an AJAX call from a proxy script (cross-domain stuff), so none of the variables are accessible. If they were, I'd obviously just be able to parse foo['KEY'] and be on my merry.
SECOND EDIT: nothing conclusive yet! I'm getting dangerously close to concluding that eval() is the way to go. Can you stomach this outcome? I'm about to give in to evil(). Somebody stop me, because it's looking like the only way.
The external code better send back valid JSON. The value in your example is not valid JSON, as the keys must be wrapped with double quote.
I came up with small pure JavaScript parser, that can handle simple invalid JSON by adding double quotes by itself. It currently won't support non string values.
function ParseRawJSON(rawCode) {
var arrCandidates = [];
var lastOpenBracketIndex = -1;
for (var i = 0; i < rawCode.length; i++) {
var curChar = rawCode.charAt(i);
if (curChar === "}") {
if (lastOpenBracketIndex >= 0) {
arrCandidates.push(rawCode.substr(lastOpenBracketIndex, i - lastOpenBracketIndex + 1));
lastOpenBracketIndex = -1;
}
} else if (curChar === "{") {
lastOpenBracketIndex = i;
}
}
var arrJsonObjects = [];
for (var i = 0; i < arrCandidates.length; i++) {
var currentJSON = null;
try {
currentJSON = JSON.parse(arrCandidates[i]);
} catch (e) {
//try fixing
var fixedCandidate = TryFixJSON(arrCandidates[i]);
if (fixedCandidate) {
try {
currentJSON = JSON.parse(fixedCandidate);
} catch (e) {
currentJSON = null;
}
}
}
if (currentJSON != null) {
var keys = [];
for (var key in currentJSON)
keys.push(key);
if (keys.length > 0)
arrJsonObjects.push(currentJSON);
}
}
return arrJsonObjects;
function Trim(s, c) {
if (c instanceof Array) {
for (var i = 0; i < c.length; i++)
s = Trim(s, c[i]);
return s;
}
if (typeof c === "undefined")
c = " ";
while (s.length > 0 && s.charAt(0) === c)
s = s.substr(1, s.length - 1);
while (s.length > 0 && s.charAt(s.length - 1) === c)
s = s.substr(0, s.length - 1);
return s;
}
function TryFixJSON(strBlock) {
if (strBlock.indexOf(":") <= 0)
return false;
strBlock = strBlock.replace("{", "").replace("}", "");
var mainParts = strBlock.split(",");
for (var i = 0; i < mainParts.length; i++) {
var currentPart = Trim(mainParts[i]);
if (currentPart.indexOf(":") <= 0)
return false;
var subParts = currentPart.split(":");
if (subParts.length !== 2)
return false;
var currentKey = Trim(subParts[0], [" ", "'", "\""]);
var currentValue = Trim(subParts[1], [" ", "'", "\""]);
if (currentKey.length === 0)
return false;
subParts[0] = "\"" + currentKey + "\"";
subParts[1] = "\"" + currentValue + "\"";
mainParts[i] = subParts.join(":");
}
return "{" + mainParts.join(", ") + "}";
}
}
This will just look for anything between { and } and try to parse as JSON. No eval, in case of failure it'll just ignore the invalid block. Success? Great, it will return plain array of the valid JSON's it found.
Usage example:
var rawCode = "var foo = new Object(); { dummy here }}} function boo() {}" +
"foo['KEY'] = { \"Field1\": \"Value1\", \"Field2\": \"Value2\"}; hello {\"foo\": \"bar\"} and it's over ";
var jsonObjects = ParseRawJSON(rawCode);
for (var i = 0; i < jsonObjects.length; i++) {
for (var key in jsonObjects[i]) {
var value = jsonObjects[i][key];
//got key and value...
}
}
Live test case, using fixed version of your sample code.
A generally safer alternative to using eval is creating a new Function and passing it the string function body. That way (unless something is explicitly acessing the window object) you won't have access to the global scope and can keep it encapsulated in the function scope.
Let's say the first two lines of your example code are the JavaScript that you'd like to evaluate, if you know the name of the variable you want to retrieve as a JSON object you can just return it at the end of the created function and then call it:
var js = "var foo = {}; foo['KEY'] = {Field1: 'Value1', Field2: 'Value2'};";
var fn = new Function(js + ';return foo;');
var result = fn();
console.log(JSON.stringify(result));
This is also what MDN suggests doing in the documentation for eval:
More importantly, third party code can see the scope in which eval() was invoked, which can lead to possible attacks in ways of which the similar Function is not susceptible.
If the JSON contains just data and not functions you can use JSON.parse()
See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse for more detailed info.
As the method has been placed in to the global, then you can do
window["fooFunction"](foo)

Recursively search JSON and delete certain sub objects

I need to search a complex json object recursively, and delete the object associated with any key that starts with "_".
So far, I have:
sanitize: function(json){
for(var i in json){
if(json[i]){
if(i.substring(0,1) == "_")
delete json[i];
else
this.sanitize(json[i]);
}
}
console.log(json);
return json;
}
I exceed the maximum call stack.
Try using your own array, and also make sure the subobjects aren't circular references, and also make sure they're objects.
function sanitize(json) {
var stack = [];
var done = [];
do {
for(var x in json) {
if(x.charAt(0) === '_') {
delete json[x];
} else if(done.indexOf(json[x]) === -1 && typeof json[x] === 'object') {
stack.push(json[x]);
done.push(json[x]);
}
}
} while(json = stack.pop());
}

Categories

Resources