Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Inject a <script> tag into each HTML page #202

Closed
betatim opened this issue May 27, 2020 · 4 comments · Fixed by #209
Closed

Inject a <script> tag into each HTML page #202

betatim opened this issue May 27, 2020 · 4 comments · Fixed by #209

Comments

@betatim
Copy link
Member

betatim commented May 27, 2020

Proposed change

Add ability to modify HTML as it is being proxied. For mybinder.org it would be cool (useful?) if we could inject something like <script src="mybinder.org/static/bundle.js"> just before the closing <body> tag of every HTML page that is proxied. This would allow us to load some JS that then injects a "binder menu". This menu would contain links to the functionality that we currently only offer for classic notebook and (partly) jupyter lab. These are buttons that let people share the binder link, open a video conference, etc. A reason to have a JS bundle that does this is that it would be independent of the particular UI (voila, RStudio, RShiny, Jupyter) and would always appear. Right now we have to maintain two ways of doing this (one for lab, one for classic) and for RStudio users get nothing.

Alternative options

Create a new package that derives from this one and implements the JS injection.

Who would use this feature?

BinderHub operators could use this to show a small custom UI element and reduce maintenance burden on them because they don't have to create one for every frontend that they want to support. In turn all users of a BinderHub would use it because it gives them access to UX elements that otherwise are hidden from them.

Suggest a solution

I would modify the handler for GET requests to check if the content type is HTML and if yes parse the HTML and inject a script tag as the last child of the body tag. The src URL for this bundle would be configured for the whole proxy.

I am not sure if we need to parse the HTML or if we could do a search and replace for </body> -> <script src="..."></body>. Parsing seems like it would be more robust but also slower??

Maybe there is a completely different way of achieving the same goal? What do people think of this?

@manics
Copy link
Member

manics commented May 27, 2020

I think it might work for traditional webpages which are static with some dynamic content, but there's a high chance of it breaking dynamic apps which are almost entirely JavaScript based. Inserting additional elements into the DOM could break assumptions that the script makes, or it could mess up the layout.

Potentially it could work for a whitelisted set of applications.

@maresb
Copy link
Contributor

maresb commented May 27, 2020

Would it make sense in the GET handler instead of writing response.body to write rewrite_response(response) where rewrite_response defaults to lambda response: response.body? That way anyone could define rewrite_response and do any injection themselves.

@yuvipanda
Copy link
Contributor

I really like @maresb's suggestion!

@maresb, would you be interested in contributing a PR?

@betatim
Copy link
Member Author

betatim commented Jun 29, 2020

I like the suggestion too. I think in the meantime I realised that for the use case of the "binder menu" we need to hook into a proxy that all pages (also jupyter notebook and lab) flow through. Otherwise those won't have the button. (Probably we'd use a side-care, jupyterhub/mybinder.org-deploy#1462 is the place to discuss that I reckon.)

However I think having something to rewrite requests in jupyter-server-proxy would be useful so 👍 to the idea.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants