restore overridden window.JSON object - javascript

Some code that I don't have control over is overriding the global JSON object without checking if it's already implemented:
var JSON = {
org: "http://www.JSON.org",
copyright: "(c)2005 JSON.org",
license: "http://www.crockford.com/JSON/license.html",
stringify: function(a, g) {
...
The problem is that this version of the JSON parser is very old and has a bug, which is fouling up my attempts at serialization. (Others have had a similar problem with this implementation.)
Can I get at the browser's native implementation? I thought delete would work, but it doesn't. I suspect that's because JSON is an object and not a method in the prototype. Is there some other way to get at it?

You can create an iframe element (which will load about:blank and hence create a new context) and get a JSON object from there.
function restoreJSON() {
var f = document.createElement("iframe");
f.style.display = "none";
document.documentElement.appendChild(f);
window.JSON = f.contentWindow.JSON;
document.documentElement.removeChild(f);
}
about:blank is loaded synchronously, so no need to wait for the load event. While this isn't restoring the original JSON object, it is getting one black-box identical to it.

Since the code that you don't have control over is overriding the original before you come along in the page, you have two options:
Inject an iframe and grab the JSON off the contextWindow (as indicated in the other answer on this question at the time of this edit), or, alternately, just use the https://github.com/douglascrockford/JSON-js JSON library as your own insert. Note that using Crockford's does give cross-browser-guarantees of conformance, but the native implementations are often faster.
An alternative if you have the ability in the future to come along on the page before the offending code, is to inject something before that offending "code that helps" to grab the JSON object:
<html>
<head>
<script>
window.myJson = window.JSON;
</script>
....

Related

Javascript. How to use original JSON object (that was redefined by another developer) [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 1 year ago.
Improve this question
I have a simple JS code like:
JSON.stringify({'a':'b'});
that works ok when is run standalone on my webpage. But when my code is put inside the production environment, some extra javascript files ruin my code totally. One of those extra js files has JSON object redefinition. And now my code is using this version of JSON object I don't want to use.
How can I tell javascript to use a built-in JSON object, not a custom one?
Some clarification:
This JSON object redefinition is done inside core javascript production libraries (my code is inserted inside old CMS). It looks like core developer wanted to create his own polyfill for old browsers. I am afraid to change his code.
Thank you in advance!
You can use the old trick with an iframe to restore the global object:
// some other code breaks global JSON
window.JSON = {
stringify() { return "it's broken"; },
parse() { return "it's broken"; }
}
const obj = {foo: 1};
console.log(JSON.stringify(obj)); // it's broken
//later your code runs
(function() {
const iframe = document.createElement("iframe");
document.body.appendChild(iframe);
const origJSON = iframe.contentWindow.JSON;
document.body.removeChild(iframe);
const json = origJSON.stringify(obj);
console.log(json); // '{"foo":1}'
console.log(origJSON.parse(json)); // { foo: 1 }
//restore the global JSON
window.JSON = origJSON;
})();
//afterwards global JSON works
const json = JSON.stringify(obj);
console.log(json); // '{"foo":1}'
console.log(JSON.parse(json)); // { foo: 1 }
However, this is not a reliable way to deal with these issue. It should only serve as a temporary workaround. In general, if your environment is broken due to some script outside of your control, you cannot guarantee the environment works correctly as long as the script is there.
Here is a simple example: let us say that script A.js breaks the global JSON object. Your script is C.js where you do restore JSON with your code. However, B.js runs after A.js and before C.js and tries to use JSON. It gets a wrong value and continues to use it. However, will not function correctly. Your environment is now still broken even after you fixed it.
It is an exceptionally bad idea to tamper with global objects. If any script does that, you are better off trying to replace it or fix it than continue to use it.
By far, your best bet is to not include the file that redefines JSON, because JSON is defined in a particular way by the specification and shouldn't be overridden by something doing something different. Lots of things may well break (not just your code).
If you can't do that, you have a couple of choices:
Make sure your script is first and create a local variable private to your script and assign JSON to it, then use that local. You can even make the local JSON, like this:
;((JSON) => {
// ...your code here...
))(JSON);
If your script has to be after the other one, then put a new script before it that saves the original JSON:
const originalJSON = JSON;
Then, again, use that in your code, perhaps like this:
;((JSON) => {
// ...your code here...
))(originalJSON);

using JQuery to fetch an html document and parse it into a DOM tree

So essentially I'm trying to build my own version of GitHub's tree slider. The relevant Javascript/JQuery code is:
// handles clicking a link to move through the tree
$('#slider a').click(function() {
history.pushState({ path: this.path }, '', this.href) // change the URL in the browser using HTML5 history module
$.get(this.href, function(data) {
$('#slider').slideTo(data) // handle the page transition, preventing full page reloads
})
return false
})
// binds hitting the back button in the browser to prevent full page reloads
$(window).bind('popstate', function() {
$('#slider').slideTo(location.pathname)
}
Ok, hopefully that's understandable. Now here's my interpretation of what's going on here, followed by my problem/issue:
The callback function for the GET request when navigating through the tree is the slideTo method, and an HTML string is passed in as an argument to that function. I'm assuming that slideTo is a function defined elsewhere in the script or in a custom library, as I can't find it in the JQuery documentation. So, for my purposes, I'm trying to build my own version of this function. But the argument passed into this function, "data", is just the string of HTML returned from the GET request. However, this isn't just a snippet of HTML that I can append to a div in the document, because if I perform the same GET request (e.g. by typing the url into a web browser) I would expect to see a whole webpage and not just a piece of one.
So, within this callback function that I am defining, I would need to parse the "data" argument into a DOM so that I can extract the relevant nodes and then perform the animated transition. However, this doesn't make sense to me. It generally seems like a Bad Idea. It doesn't make sense that the client would have to parse a whole string of HTML just to access part of the DOM. GitHub claims this method is faster than a full page reload. But if my interpretation is correct, the client still has to parse a full string of HTML whether navigating through the tree by clicking (and running the callback function) or by doing full page loads such as by typing the new URL in the browser. So I'm stuck with either parsing the returned HTML string into a DOM, or ideally only fetching part of an HTML document.
Is there a way to simply load the fetched document into a Javascript or JQuery DOM object so I can easily manipulate it? or even better, is there a way to fetch only an element with an arbitrary id without doing some crazy server-side stuff (which I already tried but ended up being too spaghetti code and difficult to maintain)?
I've also already tried simply parsing the data argument into a JQuery object, but that involved a roundabout solution that only seems to work half the time, using javascript methods to strip the HTML of unwanted things, like doctype declarations and head tags:
var d = document.createElement('html');
d.innerHTML = data;
body = div.getElementsByTagName("body")[0].innerHTML;
var newDOM = $(body);
// finally I have a JQuery DOM context that I can use,
// but for some reason it doesn't always seem to work quite right
How would you approach this problem? When I write this code myself and try to make it work on my own, I feel like no matter what I do, I'm doing something horribly inefficient and hacky.
Is there a way to easily return a JQuery DOM object with a GET request? or better, just return part of a document fetched with a GET request?
Just wrap it; jQuery will parse it.
$(data) // in your callback
Imagine you want to parse a <p> tag in your normal HTML web page. You probably would use something like:
var p = $('<p>');
Right? So you have to use the same approach to parse an entire HTML document and then, navigate through the DOM tree to get the specific elements you want. Therefore, you just need to say:
$.get(this.href, function(data) {
var html = $(data);
// (...) Navigating through the DOM tree
$('#slider').slideTo( HTMLportion );
});
Notice that it also works for XML documents, so if you need to download via AJAX a XML document from the server, parse the inner information and display it on the client-side, the method is exactly the same, ok?
I hope it helps you :)
P.S: Don't ever forget to put semicolons at the end of each JavaScript sentence. Probably, if you don't put them, the engine would work but it is better to be safe and write them always!

Pure Javascript Sandbox

Is this a possible solution for a pure javascript sandbox ?
My willing is to execute an untrusted string of code without giving access to DOM, window, this, and so on but allowing the user to acces Math, JSON and other functionalities.
I've tested it on Chrome.
UPDATE:
I want to give the possibility to save on server user-defined code and make it available to other users. I'm looking for a way to deny access to the document ni order to make it safe.
function safe(code,args)
{
if (!args)
args=[];
return (function(){
for (i in window)
eval("var "+i+";");
return function(){return eval(code);}.apply(0,args);
})();
}
ff=function()
{
return 3.14;
}
console.log(safe("this;"));//Number
console.log(safe("window;"));//undefined
console.log(safe("console;"));//undefined
console.log(safe("Math;"));//MathConstructor
console.log(safe("JSON;"));//JSON
console.log(safe("Element;"));//undefined
console.log(safe("document;"));//undefined
console.log(safe("Math.cos(arguments[0]);",[3.14]));//-0.9999987317275395
console.log(safe("arguments[0]();",[ff]));//3.14
I've proposed it on an old post : https://stackoverflow.com/a/11513690/76081
Thanks!
It's unsafe. The following construction will get the global window object from inside your sandbox:
(function(){return this;})()
At which point, you can extract anything you want from it, including goodies like document.
Hat tip to T.J. Crowder for his answer on https://stackoverflow.com/a/2673780/149341 in which he described this exploit.

How to parse JSON created by WCF DataContract object

I have an object decorated with [DataContract] attribute and my WCF service is returning this as JSON like this:
{"GetCommentsByPostResult":[{"CommentCreated":"\/Date(1305736030505+0100)\/","CommentText":"Comment 1"},{"CommentCreated":"\/Date(1305736030505+0100)\/","CommentText":"Comment 2"},{"CommentCreated":"\/Date(1305736030505+0100)\/","CommentText":"Comment 2"}]});
Ive attempted to iterate through the CommentCreated with this jQuery code:
$(data).each(function ()
{
alert(this.CommentCreated);
});
But all I get is an alert box with 'undefined in' so I changed it to:
$(data).each(function () {
$(this.GetCommentsByPostResult).each(function () {
alert(this.GetCommentsByPostResult);
});
});
but that still doesnt work. What I want to do is iterate the CommentCreated and throw them to an alert box....
I'm not sure, but I don't think this would be the current element when calling each. Also, why are you wrapping the data variable with the jQuery function? jQuery collections are for DOM elements.
$.each(data.GetCommentsByPostResult, function (e) {
alert(e.CommentCreated);
});
Depending on how you are acquiring the data (a library? custom code?) you will most likely have to convert the JSON string first into an actual JavaScript object. Many browsers have built in methods for doing this, though you may need to take advantage of a 3rd party library to take advantage of those that do not have out-of-box support. I would recommend JSON-js as it follows the same syntax as those found built into some browsers.
var obj = JSON.parse(data);
Once you have this object, you can now access the data with standard JavaScript dot or bracket notation:
var comments = obj.GetCommentsByPostResult; // or...
var comments = obj['GetCommentsByPostResult'];
Those two lines are equivalent. To iterate the comments, as you are trying to do, you could try:
$.each(obj.GetCommentsByPostResult, function (e) {
alert(e.CommentCreated);
});
Also, I would recommend using console.log() instead of alert(), and a browser that supports inspection of the logged objects. This would be FireFox with the Firebug extension, or Chrome with their developer tools (Ctrl-Shift-J to activate). Not sure about the status of this type of tool in IE9, though I would not be surprised if there was an equivalent tool there as well.

How can I use jQuery 1.5.2+ on a Firefox addon?

At first I made a function that received a parameter and returned jQuery such as:
function getjQuery(window)
{
/*jquery code*/(window);
return window.jQuery;
}
But then I got an email form the review and they told me I have to use jQuery file with the original file name and completely unmodified.
I started to search for an alternative and found this solution, but there is no way it work.
jQuery object is created, but I can't find any elements. $("#id").length is always 0. With the previous method it was always found.
My current code (which doesn't work)
AddonNameSpace.jQueryAux = jQuery;
AddonNameSpace.$ = function(selector,context) {
return // correct window
new AddonNameSpace.jQueryAux.fn.init(selector,context||contentWindow);
};
AddonNameSpace.$.fn =
AddonNameSpace.$.prototype = AddonNameSpace.jQueryAux.fn;
AddonNameSpace.jQuery = AddonNameSpace.$;
The jQuery file is loading on my browser.xul overlay:
<script type="text/javascript" src="chrome://addon/content/bin/jquery-1.5.2.min.js" />
Am I loading in the right place?
How can I use jQuery to modify the content on a page (HTML) with the original jQuery file, is it even possible?
You need pass the e.originalTarget.defaultView on the second parameter on jquery..
If you don't jquery will use window.document, which is the window.document from the xul.
Use
gBrowser.addEventListener("DOMContentLoaded", function (e) {
$("#id", e.originalTarget.defaultView).length
}, true);
instead of
$("#id").length;
And, for avoid conflicts with other extensions don't use script in the xul page, use MozIJSSubScriptLoader.
Components.classes["#mozilla.org/moz/jssubscript-loader;1"]
.getService(Components.interfaces.mozIJSSubScriptLoader)
.loadSubScript("chrome://youraddon/content/jquery-1.5.2.min.js");
If you use this method, you load jquery only when you need, avoiding memory leak.
The preferred way to load it is with mozIJSSubScriptLoader so you don't collide with other's extensions. I'm not sure why you're having problems, I can use jQuery in my addon like $("#id").hide() with no additional code (although from the sidebar, now browser.xul).
Either way, this blog post provides a pretty good guide and even has an example xpi to download.

Categories