Could the leiningen plug-in figwheel or boot's counterpart be used within arbitrary webpages? I'm thinking of it as an replacement of the browser's builtin developer console.
Here is a simple scenario of how I'd imagine this workflow:
You open an arbitrary website in the browser. Beside that, you have a browser repl inside a terminal window, which is provided by one of the tools mentioned above. (I guess they both use 'weasel' for this.)
Inside the terminal one could access the current state of the weppages' DOM.
E.g: (set! (.. js/window style backgroundColor) "green"))
I guess this should not be too problematic to archive. However, I faced the following problems:
Both tools do actually just inject a bunch of JavaScript into the users's HTML page. It's basically: The users's ClojureScript compiled to JavaScript plus additional implementation of the hot-reloading mechanism via websockets. The second is just omitted when the project comes into production.
My idea was to just inject the whole bundle to another page.
I used boot for the try.
After setting up the boot's ClojureScript REPL, I opened localhost:port in a browser. It's inital source looks like this:
<!doctype html>
<html>
<head>
<title>Hello, World!</title>
</head>
<body>
<script src="js/main.js"></script>
</body>
</html>
The after main.js has been executed on page-load, many (more than 100) further javaScript tags are injected to the page. My initial idea was to just open another page now, say duckduckgo.com, and inject that one script tag to it, augmented with an absolute path to localhost.
so, at the duckduckgo.com page, inside the developer console I did this:
tag = document.createElement("script");
tag.src = "http://localhost:3000/js/main.js";
document.body.appendChild(tag);
As expected the script gets injected, and this always leads to the immediate execution of its code. I was expecting that all the other script tags get injected now automatically. Finally the webSockets should be connected to the ClojureScript repl.
However, there's the following error in the browser console: A call to document.write() from an asynchronously-loaded external script was ignored.
Indeed, many of the further script tags have been injected. But not all of them. Effectively, the socket connection is not established.
So, it looks like some script tags are injected by the mechanism I used myself (via appendChild), others should be done by document.write("<script... The later causes the problem here.
Does anybody know a way to archive this?
Related
I'm working on small .js which is going to be embedded on multiple websites, it will be loaded in a classic way - via script tag: <script src="myscript.js"></script> in sites body tag. I cannot add any more scripts to those sites.
I would like to track errors with error tracker such as Sentry, Rollup or HoneyBadger. However, all of them require being loaded with another script tag, most preferred before everything else.
Note: Those services need to load before everything else to catch errors property.
As I cannot add another script tag in the site's code, I need to execute their code inside my script, but before my actual script code.
I tried taking the content of HoneyBadger javascript library and putting it directly inside my file - it worked, however, I feel like it's terrible practice, as their code is written with modern browsers in mind, and mine supports older ones.
Is there any good way in my situation to load their .js externally?
I don't think that would work because of the way honeybadger.js v0.5 parses the script tag to get those attributes--it looks for the script tag in the dom when it's loaded.
Also, we've moved away from using the data- attributes in honeybadger.js v1.0, which was just released. In that version, you must use Honeybadger.configure to set your API key. Take a look at the new docs here:
https://docs.honeybadger.io/lib/javascript/integration/browser.html
I'd recommend going with v1.0, and using Honeybadger.configure for the configuration.
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.
Create chrome snippet like this:
window.my_property = "my_property";
Open any site
Run the snippet
I want to my snippet run before other js scripts on the website. Is it possible? Thanks in advance.
It's possible using Chrome Extensions.
You can write an extension (how to do that is beyond the scope of the answer - see some guides in the docs) that injects a Content Script with "run_at": "document_start" configuration and match patterns that fit the pages you want to inject it to.
Then, your code executes before the page is loaded - it basically consists only of the document node at that point. If you need to manipulate nodes, you're out of luck - some scripts will probably execute before the node is in the tree. If you just want to execute something like the above (modify the window object), you'll be able to do so.
Yet still, you'll need to make it a bit more complicated because of the isolated world principle. You need to use a technique to inject your code into the page.
You can do it way more easily by using a userscript engine like Tampermonkey. Again, you'll need something like
// #run-at document-start
How to write a userscript is, again, beyond the scope of this answer.
The most easy and temporary way to run chrome js Snippets before page load is to put debugger at the top of your HTML page, preferably at head or before the js code which relies on your js snippet.
<head>
<script>
debugger;
</script>
...
Now when the browser is "Paused in debugger", right click on the snippet name and Run it to execute immediately. After that you can resume debugger and continue working on the page as usual.
Please remember, this method is suitable only for testing and requires you to have control over the page (as you need to edit page HTML or JS to put debugger)
If you need to run your js snippet every-time or the page is not in your control, creating an chrome extension is currently the only solution.
I am currently rewriting a firefox extension to be used only internally at my company. I started out by moving most of the logic into an external js file loaded via a script tag within the xul overlay. (We've found it hard to get our employees to consistently upgrade so I thought I could get around that). It worked fine like this:
<overlay id="my-overlay"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
xmlns:html="http://www.w3.org/1999/xhtml">
<script type="application/x-javascript" src="https://my.company.com/path/to/jquery.min.js/>
<script type="application/x-javascript" src="https://my.company.com/path/to/toolbar/main.js"/>
<toolbox id="navigator-toolbox">
<!-- some stuff here -->
</toolbox>
But then I had the crazy idea of loading the script file dynamically so that I could use preferences to determine whether it would load from the production servers or alpha/beta servers. And that's when I failed miserably.
I've tried the following unsuccessfully:
$.ajax({ dataType: 'script', ... }) // appears to do absolutely nothing
$('overlay').appendChild('', { src: ... }) // script tag is added but not executed
document.createElementNS, etc // script tag is is added but not executed
Components.utils.import // does not accept http protocol
mozIJSSubScriptLoader // does not accept http protocol
I haven't attempted Components.utils.evalInSandbox but given its restrictions, I'm afraid it would require significant other code changes that would not be worth the slight simplification of the development cycle.
From reading thru much more mozilla documentation and bugs it appears that basically what I'm attempting to do is breaking various security concerns (I understand in principle but of course in my case, I have full control of both ends).
The thing that bothers me is that it appears to work fine as long as I hard code the script tag but only fails once I try to dynamically generate it. Should I just toss in the towel or does someone know a way to make this work?
Have you tried the brute-force approach of reading in the file using XMLHttpRequest and then just calling eval() to run it? On the face of it this seems scary from a security perspective but, as you say, using a script tag with an HTTP source URL is basically the same thing.
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?