Set breakpoints and debug eval'd JavaScript - javascript

I* am using client-side JS to parse XML files and generate complex JS code to eval as a result. (Generating re-usable functions that are kicked off by a runtime.) However, I need to debug the code being generated, and would like to use Chrome's built-in breakpoints, stepping, watch windows, etc.
Is there an easier way to do this than:
Dump the generated JS string to the console and/or window.
Copy the JavaScript
(optional) Run the JS through a prettifier like JSBeautifier.
Paste the JS into a file that is loaded via <script src="..."> in another web page.
* actually, a friend of mine was doing this, not me

Instead of using eval, and instead of manually copy/pasting into a separate file, you can dynamically load the JS directly into the page that generated it by using a data uri on a dynamically-created <script> element. With this approach, Chrome's Developer Tools (and Firebug) allow you to pick the data-URI as a script, turn on the Pretty Print, set breakpoints, and away you go.
var js = createMyJSCodeString();
addCode(js); // Right now! Debuggable!
// Dynamically evaluate JavaScript-as-string in the browser
function addCode(js){
var e = document.createElement('script');
e.type = 'text/javascript';
e.src = 'data:text/javascript;charset=utf-8,'+escape(js);
document.body.appendChild(e);
}

Related

Injecting Javascript code from string pretending to be an external script

The usual way of adding Javascript to a page is by adding it to the document's <body> or <head> in either static HTML or something generated server-side.
I would like to generate dynamic Javascript, and add it to the document in a dynamic fashion; that is on-demand and during run-time. For example Javascript code transmitted over a WebSocket.
One option is to create a script element, add the string with script contents to the .text property, while finally adding the script to the document, like this:
var scriptContent = 'console.log("dynamic script");';
var s = document.createElement('script');
s.text = scriptContent;
document.body.appendChild(s);
This would work, but my concern is that the script is executed with the same URL/origin as the HTML document. Whereas, if it would be a separate file, invoked with the s.src parameter, it would show up under its own URL when looking at the browser console.
Why is this at all relevant? When using the browser console to debug and inspect, it usually tells you the name of the script and the line/column of the message printed to the console. For example, when opening the console, one might several console messages, with on the right side the origin and line/column information:
<domainname>:1:1
separate_js_file.js:14:20
This is the behavior that I desire because it makes it easy to distinguish what script has written something to the console. However, if adding Javascript dynamically to the page, it all gets 'advertised' under the main document, usually printed by the domainname, like domain.com:1:1.
What I would like is to change/pretend/masquerade the origin of the script, to pretend it was an external script. This way, not all lines printed to the console would be from origin domain.com:<line>:<column> but instead would be called specifically, like module1:<line>:<column>.
Is this at all possible?
I think you can append a source map at the end of the dynamic JS.
//# sourceURL=http://example.com/path/to/your/sourcemap.map
This works when using eval statements in Firefox and in with both injected script tags and evals in chrome. Might be a way to make Firefox understand it via script tags as well.
See https://developer.mozilla.org/en-US/docs/Tools/Debugger/How_to/Use_a_source_map for the details.
See also Can't see dynamically loaded code in Chrome Developer Tools 22 for some more details.

How do I use Firebase in an external Javascript file

This is probably a very simple issue, but I've been trying to use Firebase in an external javascript file that is being used with an HTML file and can't get it to work properly. I am planning to use this file for many other similar pages, so I'd rather keep it in an external document. Specifically, my code is:
$(function() {
var head= document.getElementsByTagName('head')[0];
var script= document.createElement('script');
script.src= 'https://cdn.firebase.com/v0/firebase.js';
head.appendChild(script);
var Database = new Firebase('https://myfirebase.firebaseIO.com/');
...
but when I try to run it, it says that the Firebase object/keyword is undefined. I know that the script is being correctly appended to the HTML page because I've checked the HTML on the page after running the code.
I have also read somewhere that you might need to have a personal server to run Firebase, but frankly I don't really know what that means - in any case, I use Mac OSX and run all of my HTML and Javascript in Chrome.
Thank you very much!
The problem is that using document.createElement does not force the script to be loaded and rendered before your inclusive script is invoked (it's being invoked now). There are no guarantees by this method on when the script you include will get invoked.
Additionally, you are loading the script onDomReady by putting it inside $(function() {...}); you would want to insert it into the header immediately, not wait for the entire document to load.
The simplest answer is to just put Firebase into the head of the html page; you haven't really explained your limitations here, but I assume this isn't an option for you. If it is, KISS.
Another simple answer is to utilize jQuery, since you obviously have it available.
$.getScript('https://cdn.firebase.com/v0/firebase.js', function() {
// now I can use Firebase
});
You can also accomplish this with other methods (wait until Firebase is defined using a setInterval; utilize other script retrieval methods besides document.createElement--try googling "load scripts dynamically via javascript load order"), but I think this covers your needs sufficiently.

Validate an html document that I wrote with document.write()

Ok first off let me state that I know I should never do this under any circumstances for a real site. Ok. That's out of the way.
One of my coworkers was going off that Javascript is not a "real" programming language (his definition of "real" seems to be "it compiles"), because it depends on other languages to do its thing.
I told him I could write a website using nothing but javascript.
I am sure that this can be done, using document.write('') to get the doctype, and some script to create a dom and styles... but the problem is since the page is validated without JS, it can't show him that what the browser is looking at does in fact validate.
Anyone know of a way I can validate the actual source the browser is using instead of the javascript that initially loaded?
If you really want to demonstrate that JS is a "real" language, then you would probably be better off not using a browser as the foundation. A node.js server would allow you to generate an HTML document (using document.write if you like, but DOM is an option (and people have used client side libraries to manipulate a document in node.
Since the JS runs on the server, you can get the actual source from the browser via view-source or point the validator directly at the URI (so long as it is either public or you install a local copy of the validator)
Load the site in Firefox with Firebug installed. Fire up the "HTML" view and rightclick on the <html> node and select "copy HTML".
The closest you get using JavaScript:
var generatedHTML = document.documentElement.innerHTML;
//Retrieves everything within the (missing)HTML tags.
//The only missing parts are DOCTYPE and the <html> itself
var txt = document.createElement("textarea");
txt.style.cssText = "width:99%;height:99%;position:fixed;z-index:999;top:0;left:0";
txt.value = generatedHTML;
txt.ondblclick = function(){this.parentNode.removeChild(this)};
//Adding a simple function to easily remove the textarea once finished
document.body.appendChild(txt);
Bookmarklet (I have slightly adjusted the code to be compact):
javascript:void(function(){var t=document.createElement("textarea");t.style.cssText = "width:99%;height:99%;position:fixed;z-index:999;top:0;left:0";t.value=document.documentElement.innerHTML;txt.ondblclick=function(){t.parentNode.removeChild(t)};document.body.appendChild(t)})()
Focus the generated textarea
Manually add the DOCTYPE + <html> tags
Copy the contents of the textarea to the validator at: http://validator.w3.org/#validate-by-input

How can I inject Javascript into a site, then make sure it's cross-browser compatible?

I am doing some contract work in which I need to write small snippets of Javascript for client sites and then make sure that they work in all browsers.
I don't have access to the sites' servers themselves, so I need to be able to inject the snippets some other way.
I tried writing a proxy script in PHP that grabs the site's source code, absolutizes all the URLs, and then injects the Javascript, and that works for some client sites (and has the advantage of being easily testable in multiple browsers).
But some Javascript-heavy sites don't work with this approach -- they'll do stuff like testing the window.location to make sure it isn't spoofed, or they'll include references to files on the local server that the proxy script isn't able to filter.
As an alternative, I suppose I could use Greasemonkey to inject the Javascript ... which would let me see how the code looks in Firefox, but then how do I test in, say, IE6?
Can anyone suggest a way to do cross-browser Javascript development when you don't have access to the site on which the Javascript will eventually reside?
You can use a bookmarklet to insert your JavaScript by injecting a script tag.
The code itself would look something like this:
(function() {
var d = document,
s = d.createElement('script'),
t = d.body || d.getElementsByTagName('head')[0] || d.documentElement;
s.src = "http://path/to/your/script.js";
t.appendChild(s);
})();
Then just bookmark-ify it (search for "bookmarklet maker" and you'll find a bunch to do the work for you). You bookmark the result, visit the page you want to test on, and click your bookmark to do your testing.
For instance:
javascript:(function(){%28function%28%29%20%7Bvar%20d%20%3D%20document%2Cs%20%3D%20d.createElement%28%27script%27%29%2Ct%20%3D%20d.body%20%7C%7C%20d.getElementsByTagName%28%27head%27%29%5B0%5D%20%7C%7C%20d.documentElement%3Bs.src%20%3D%20%22http%3A//jsbin.com/ajubo4%22%3Bt.appendChild%28s%29%3B%7D%29%28%29%3B})();
If you copy that into a bookmark's URL, when you activate the bookmark on any major browser it loads a simple script from http://jsbin.com (which you can find here) that does an alert telling you how many p elements are on the page. (There are, at the moment, 23 on this page.)
One fast and simple solution, is to use Privoxy.
You could try using jQuery with the jsShell plugin from hugoware. You could then use chrome to apply jQuery to already loaded pages. jQuery should be cross browser compatible.
http://hugoware.net/projects/jsshell

GWT bookmarket or GWT as an external library

I simply want to load a GWT(Google Web Toolkit) app by adding a script tag to the DOM, however because the GWT linker uses document.write() I'm unable to find any good way of doing so. I've found some hacks for doing so on various blog posts but they all seem to fail with the latest version of GWT. Any reasonably non-invasive approach for doing this come to mind?
Clarification:
Normal way to start up a GWT app, in your host html page:
<script type="text/javascript" language="javascript" src="myapp.nocache.js"></script>
This, of course, starts up as soon as the page loads. I want to do it at a later time:
function startapp() {
var head = document.getElementsByTagName('head');
var s = document.createElement('script');
s.setAttribute('type', 'text/javascript');
s.setAttribute('src', 'myapp.nocache.js');
head[0].appendChild(s);
}
Here's what seems to work so far:
Add this to the top of your App.gwt.xml:
<!-- Cross site linker -->
<inherits name="com.google.gwt.core.Core" />
<add-linker name="xs" />
After compiling your app with the above setting, modify (or copy) the generated app.nocache.js as follows:
1) Comment the last $doc.write... statement
2) Copy this portion from the $doc.write statement you just commented out and eval it. Example:
eval('window.__gwtStatsEvent && window.__gwtStatsEvent({' + 'moduleName:"app", sessionId:window.__gwtStatsSessionId, subSystem:"startup",' + 'evtGroup: "loadExternalRefs", millis:(new Date()).getTime(),' + 'type: "end"});' + 'window.__gwtStatsEvent && window.__gwtStatsEvent({' + 'moduleName:"app", sessionId:window.__gwtStatsSessionId, subSystem:"startup",' + 'evtGroup: "moduleStartup", millis:(new Date()).getTime(),' + 'type: "moduleRequested"});');
3) Add this line right after.
document.body.appendChild(document.createElement('script')).src=base + strongName + ".cache.js";
So you're basically replacing the $doc.write with those two lines.
Now, your bookmarklet will look something like:
My App
I'm assuming you are already using the cross-domain linker and this does not resolve your problem with document.write. If not, it might be worth a look (sorry, not enough experience with it to say.)
One approach that I am fairly sure could be made to work is this:
Your bookmarklet adds a script tag to the page (as now)
This script is not GWT compiler output. It is a plain-old javascript that adds an IFrame to the page, and the src of that IFrame is pointed at an HTML page on your server that loads your GWT module.
Presumably the goal is for your GWT module to get things out of the page it was loaded into. Of course, it can't do this directly in this case because the IFrame comes from a different domain than the parent page.
In order to make this work you would have to use window.postMessage and window.addEventListener to communicate between your GWT module in the IFrame and your javascript stub in the parent (using JSNI on the GWT side.)
If you have to support older browsers, postMessage won't work - but you might be able to get away with hash manipulation - but this is probably where I'd draw a line on practicality.
Whenever a browser loads a javascript file, its also execute every line of it inorder to build the symbol tables etc.
In your case, the app loads in the browser and after the dom is loaded, your GWT module js gets loaded. At this point, the browser will try to execute every line of the GWT module javascript, possibly causing your earlier loaded DOM to go for a toss.
What exactly is your use case? If your requirement is conditionally loading the GWT module then your could try something like this:
Include this in your head:
<script src="gwtmoduleloader.js"></script>
Here, gwtmoduleloader.js is infact a servlet that will hold logic to figure out if the gwt module is to be loaded.
If the GWT module is to be loaded, the sevlet can print a
document.write('<script src="myapp.nocache.js"></script>')
or else return silently.
When browser evaluates the contents of gwtmoduleloader.js, it may find a document.write for another script (in your case the gwt module), which it will load and evaluate. This is thus a conditional load and can be achieved before the body begins loading.
Is this what you were looking for?

Categories