how to add javascript file and in script tags

There’s some weird flow specifications with paypal digital goods:

HERE

further down the page, requiring you to add a js file. I rather not add this unless the user is participating in the flow, and preferably not use this at all if it can be done otherwise with Vaadin. Any thoughts?

Nobody did this with Vaadin? Is the javascript even needed to create that flow? I can’t tell, paypal documentation looks real messy to me

The javascript stuff starts from:

Is CustomLayout any help in this?

I don’t know if there is some better way, but I used the following code to inject a custom js library in Vaadin :


        String script = "try{var fileref=document.createElement('script');";
        script += "fileref.setAttribute(\"type\",\"text/javascript\");";
        script += "fileref.setAttribute(\"src\", \"" + PATH_TO_JS_SCRIPT + "\");";
        script += "document.getElementsByTagName(\"head\")[0]
.appendChild(fileref);}catch(e){alert(e);}";
        window.executeJavaScript(script);

Just replace PATH_TO_JS_SCRIPT with the path to the .js file and it will inject it in the part of the html.
Note that if you run this script during the application init or window creation time, the script will not be injected for refresh/tab close and reopen/…
I am actually using an override on Application.getWindow to handle that (and quite a lot of other things).

Hope that helps

Although it may not apply here, in general, another way to inject a js file is to override AbstractApplicationServlet.writeAjaxPage*(). This also allows loading a library before the Vaadin application starts, but gives less control about when not to load it.

That’s really good to know. Thanks.

In case somebody needs it I will put some code :
Step 1 : Create a custom Servlet
(Note : I extend ApplicationServlet but if you are using GAE maybe extending GAEApplicationServlet is better)


public class MyApplicationServlet extends ApplicationServlet {
    //This will go in the <head>
    @Override
    protected void writeAjaxPageHtmlHeader(final BufferedWriter page, String title, String themeUri,
            final HttpServletRequest request) throws IOException {
        //Don't forget to call super or nothing will work
        super.writeAjaxPageHtmlHeader(page, title, themeUri, request);

        page.write("<script type=\"text/javascript\" src=\"" + pathToJavascript + "\"></script>\n");
    }

    //This will go somewhere at the top of <body> (just after vaadin stuff)
    @Override
    protected void writeAjaxPageHtmlBodyStart(final BufferedWriter page,
            final HttpServletRequest request) throws IOException {
        //Don't forget to call super or nothing will work
        super.writeAjaxPageHtmlBodyStart(page, request);
        page.write("<script>"+myJavascript+"</script>");
    }
}

Step 2 : Update web.xml
Look for the section and replace logstorage Application servlet class name by the one you did in Step 1

Step 3 : Compile

Step 4 : Deploy

Step 5 : Enjoy

Doesn’t work. Script doesn’t gets sent to the client.
EDIT: executeJavaScript works except if javascript contains errors

My use case is:

  1. user pushes button that opens payment choices window
  2. payment button on payment window triggers the creation of an iframe and it redirects (supposedly the iframe catches this redirect)

I’ve tried to load the library and code for the trigger in 1. and 2. and it doesn’t work
The javascript by itself works (attached test).

Is there any way to inject javascript into the client?

Here’s a ready made test:

package com.example.injecttest;

import com.vaadin.Application;
import com.vaadin.ui.*;
import com.vaadin.ui.Button.ClickEvent;

public class InjecttestApplication extends Application {
	@Override
	public void init() {
		Window mainWindow = new Window("Injecttest Application");
		Label label = new Label("Hello Vaadin user");
		mainWindow.addComponent(label);
		setMainWindow(mainWindow);
		
		Button button = new Button();
		button.addListener(new Button.ClickListener() {
			
			public void buttonClick(ClickEvent event) {
				StringBuilder script = new StringBuilder();
				// @formatter:off
				script
				.append("try {")
				.append("	var script = document.createElement('script');")
				.append("	script.setAttribute('type', 'text/javascript');")
				.append("	script.setAttribute('src', 'https://www.paypalobjects.com/js/external/dg.js');")
				.append("	var content = document.createTextNode('');")
				.append("	script.appendChild(content);")
				.append("	document.body.appendChild(script);")
				.append("} catch (e) {")
				.append("	alert(e);")
				.append("}")
				.append("")
				.append("try {")
				.append("	var script = document.createElement('script');")
				.append("	var content = document.createTextNode('' + ")
				.append("		'var dg = new PAYPAL.apps.DGFlow({\n' +")
				.append("			'// the HTML ID of the form submit button which calls setEC\n' +")
				.append("			'trigger: \"pay\"\n' +")
				.append("		'});' +")
				.append("	'');")
				.append("	script.appendChild(content);")
				.append("	document.body.appendChild(script);")
				.append("} catch (e) {")
				.append("	alert(e);")
				.append("}");
				// @formatter:on
				getMainWindow().executeJavaScript(script.toString());
			}
		});
		
		getMainWindow().addComponent(button);
		
		StringBuilder script = new StringBuilder();
		// @formatter:off
		script
		.append("try {")
		.append("	var script = document.createElement('script');")
		.append("	script.setAttribute('type', 'text/javascript');")
		.append("	script.setAttribute('src', 'https://www.paypalobjects.com/js/external/dg.js');")
		.append("	var content = document.createTextNode('');")
		.append("	script.appendChild(content);")
		.append("	document.body.appendChild(script);")
		.append("} catch (e) {")
		.append("	alert(e);")
		.append("}")
		.append("")
		.append("try {")
		.append("	var script = document.createElement('script');")
		.append("	var content = document.createTextNode('' + ")
		.append("		'var dg = new PAYPAL.apps.DGFlow({\n' +")
		.append("			'// the HTML ID of the form submit button which calls setEC\n' +")
		.append("			'trigger: \"pay\"\n' +")
		.append("		'});' +")
		.append("	'');")
		.append("	script.appendChild(content);")
		.append("	document.body.appendChild(script);")
		.append("} catch (e) {")
		.append("	alert(e);")
		.append("}");
		// @formatter:on
		getMainWindow().executeJavaScript(script.toString());
	}

}

11954.html (997 Bytes)

Sorry if this is a bit off-topic, but I am always surprised when I see code like:

script += “foo”;
script += “bar”;

or

.append(“foo”)
.append(“bar”)

When you do this, the string is built at runtime, each time you call the method.
While if you do:

script = “foo” +
“bar” +

the compiler will build the string once and for all.

This is probably unimportant here, as this isn’t in a time critical section, but still it is a good habit to take.
Of course, another method, more readable/easier to maintain, is to lazily read a text file when needed.

This trigger:


StringBuilder loadTrigger = new StringBuilder();
		// @formatter:off
		loadTrigger
		.append("var head = document.getElementsByTagName('head')[0]
;")
		.append("var script = document.createElement('script');")
		.append("var content = document.createTextNode('' +")
		.append("	'function setPayTrigger() {' +")
		.append("		'var dg = new PAYPAL.apps.DGFlow({' +")
		.append("			'trigger: \"payAnchor\"' +")
		.append("		'}); alert(\"setPayTrigger()\");' +")
		.append("	'}' +")
		.append("'');")
		.append("script.appendChild(content);")
		.append("head.appendChild(script); alert(\"loadingTrigger\");");
		// @formatter:on
		mainWindow.executeJavaScript(loadTrigger.toString());

can’t target this anchor in a
subwindow
:

html body.v-generated-body div.v-window div.popupContent div.v-window-wrap div.v-window-wrap2 div.v-window-contents div div.v-verticallayout div div div div.v-label a#payAnchor

but if I move the anchor here (
main window
) it can, why?

html body.v-generated-body div#rl-3642.v-app div.v-view div.v-verticallayout div div div div.v-label a#payAnchor

The anchor added like:

Label pay = new Label(
		"<a id=\"pay\" href='https://www.sandbox.paypal.com/incontext?token=EC-3R8604095S235780N'><img src='https://www.paypal.com/en_US/i/btn/btn_dg_pay_w_paypal.gif'></a>");
pay.setContentMode(Label.CONTENT_XHTML);

The difference being only that I put it in the main application window instead of the subwindow. But it has to stay in the subwindow…
If I try to execute the needed script to target the anchor from the subwindow:

getWindow().executeJavaScript("setPayTrigger();")

I get:

Caused by: java.lang.UnsupportedOperationException: Only application level windows can execute javascript.


It only works if the anchor is in the main application window and the script is executed on the main application window.
If I move the anchor to a subwindow the target doesn’t run no more for some reason
:frowning:

I have made a test to work with below. (EDIT: also made some adjustments, now pressing the button fires the neccessary trigger setting, but still it should happen automatically somehow)

If you uncomment this:

//		 Window window = new Window();
//		 window.addComponent(pay);
//		 mainWindow.addWindow(window);

and comment this:

mainWindow.addComponent(pay);

and run the class you see the problem exactly

package com.example.jstest;

import java.io.ByteArrayInputStream;
import java.io.IOException;

import com.vaadin.Application;
import com.vaadin.terminal.ExternalResource;
import com.vaadin.ui.Button;
import com.vaadin.ui.CustomLayout;
import com.vaadin.ui.Label;
import com.vaadin.ui.VerticalLayout;
import com.vaadin.ui.Window;
import com.vaadin.ui.Button.ClickEvent;

public class JstestApplication extends Application {

    CustomLayout cl;

    @Override
    public void init() {
        final Window mainWindow = new Window("Jstest Application");
        Label label = new Label("Hello Vaadin user");
        mainWindow.addComponent(label);
        setMainWindow(mainWindow);

        StringBuilder loadScript = new StringBuilder();
        // @formatter:off
        loadScript
        .append("var head= document.getElementsByTagName('head')[0]
;")
        .append("var script= document.createElement('script');")
        .append("script.type= 'text/javascript';")
        .append("script.src= 'https://www.paypalobjects.com/js/external/dg.js';")
        .append("head.appendChild(script);alert(\"loadingScript\");");
        // @formatter:on
        mainWindow.executeJavaScript(loadScript.toString());

        StringBuilder loadTrigger = new StringBuilder();
        // @formatter:off
        loadTrigger
        .append("var head = document.getElementsByTagName('head')[0]
;")
        .append("var script = document.createElement('script');")
        .append("var content = document.createTextNode('' +")
//        .append("    'function setPayTrigger() {' +")
        .append("        'var dg = new PAYPAL.apps.DGFlow({' +")
        .append("            'trigger: \"pay\"' +")
        .append("        '}); alert(\"setPayTrigger()\");' +")
//        .append("    '}' +")
        .append("    'function setPayTrigger() {' +")
        .append("        'var dg = new PAYPAL.apps.DGFlow({' +")
        .append("            'trigger: \"pay\"' +")
        .append("        '}); alert(\"setPayTrigger()\");' +")
        .append("    '}' +")
        .append("'');")
        .append("script.appendChild(content);")
        .append("head.appendChild(script); alert(\"loadingTrigger\");");
        // @formatter:on
        mainWindow.executeJavaScript(loadTrigger.toString());

        mainWindow.executeJavaScript("setPayTrigger();");

        Button button = new Button("setPayTrigger()");
        // button.setDebugId("pay");
        button.addListener(new Button.ClickListener() {

            public void buttonClick(ClickEvent event) {
                mainWindow.executeJavaScript("setPayTrigger();");
                // mainWindow.open(new
                // ExternalResource("http://www.google.com"));
            }
        });
        mainWindow.addComponent(button);

        // note: js library sets target of anchor to 'PPDGFrame'
        // also note that the 'incontext' links work with DG, others just
        // redirect themselves outside of the DGFlow iframe
        Label pay = new Label(
        // "<a id='pay' href='https://www.paypal.com/incontext?token=EC-3R8604095S235780N'><img src='https://www.paypal.com/en_US/i/btn/btn_dg_pay_w_paypal.gif'></a>");
                "<a id='pay' href='https://www.sandbox.paypal.com/incontext?token=EC-3R8604095S235780N'><img src='https://www.paypal.com/en_US/i/btn/btn_dg_pay_w_paypal.gif'></a>");
        pay.setContentMode(Label.CONTENT_XHTML);
//        mainWindow.addComponent(pay);

         Window window = new Window();
         window.addComponent(pay);
         mainWindow.addWindow(window);

    }

}

EDIT: as a side note, if you want to go rogue, you can do something like this:

StringBuffer iframe = new StringBuffer();
// @formatter:off
			iframe.append(
					"<iframe height='550' width='420' frameborder='1' scrolling='no'")
					.append("	src='")
					.append("https://www.sandbox.paypal.com/incontext?token=")
					.append(resultValues.get("TOKEN"))
					.append("' marginheight='0' marginwidth='0'>")
					// .append("	<body topmargin=10 leftmargin=5>")
					.append("</iframe>");
			// @formatter:on
			//
			 final Label browser = new Label(iframe.toString());
			 browser.setContentMode(Label.CONTENT_XHTML);

Could it be that in your code you are running


mainWindow.executeJavaScript(...)

So you pay component works on main

While you should do


window.executeJavaScript(...)

To have that component work on current window ?

@Clerc Mathias, read at the end of my reply before the last:

Caused by: java.lang.UnsupportedOperationException:
Only application level windows can execute javascript.

@Philippe Lhoste,

Thanks for the optimisation tip. I was concerned with making it work first.
Static strings that escape the resource bundle are rare, hope I remember it next time :grin:
Even if it was a time critical section, it doesn’t quite qualify.
I made a quick test running each case one million times:
String concatenation: 30ms
StringBuilder: 1500ms

1500/1000000=0,0015ms each time a user activates this code bit
I would need 1000 users to click the buy button at the same time (I wish :grin: ) to have 1.5ms
I have SQL scripts that run in 16ms-160ms for one user click

Well, the thread is answered as far as the title goes.
I’ve managed to get past the problem with the digital goods flow triggering a hidden anchor in the main body element from my subwindow.

I saw this too…
It is a bit surprising at first, because I saw not even a message in the Firefox console. It just silently fails.

And, yes, perhaps I am just to optimization happy, but I still think it is something worth remembering. :slight_smile:

Development time optimisation leads to more time to spend optimising.
For instance I didn’t really know or care which of the above concatenations would be dynamic, and it happened that one became dynamic.
Benchmarking I noticed the gain became even less significant between compile time and run time concatenation (having two large concatenations that would happen at compile time, concatenate with 2 small strings at run time):

2344 (compile time + )
2797 (run time append)

P.S. For certain operations at run time,
Ropes
are better:

Maybe these lines help you, they did for me, I take as an example the previous post.
Scala 2.11 - Vaadin 7.2.0

 val configure = new Button()
    configure.addClickListener(new ClickListener() {
      def buttonClick(event: ClickEvent) {
        val pay = new Label("<a id='pay' href='https://www.sandbox.paypal.com/incontext?token=EC-3R8604095S235780N'><img src='https://www.paypal.com/en_US/i/btn/btn_dg_pay_w_paypal.gif'></a>",ContentMode.HTML)
        val w = new Window("test", pay)
        getUI.addWindow(w)
      }
    })

Hello,
I tried to integrate angular js application in vaadin by using custom layout like this:

   [code]

VerticalLayout mainLayout = new VerticalLayout();
mainLayout.setMargin(true);
mainLayout.setWidth(“1380px”);
setCompositionRoot(mainLayout);
Panel panel = new Panel();

    CustomLayout layout = null;
    try {
        String dynamicHtml = "<div ng-app=\"app\">..........</div>";
        layout = new CustomLayout(new ByteArrayInputStream(dynamicHtml.getBytes()));
    } catch (IOException e) {
        logger.error("could not create custom layaout", e);
    }
    panel.setContent(layout);
  mainLayout.addComponent(panel);
 importJs();

 public void importJs() {
  String PATH_TO_JS_SCRIPT = jsURL+"?tata=" + new Date();
  String script = "try{var fileref=document.createElement('script');";
  script += "fileref.setAttribute(\"type\",\"text/javascript\");";
 script += "fileref.setAttribute(\"src\", \"" + PATH_TO_JS_SCRIPT + "\");";
 script += "document.getElementsByTagName(\"head\")[0]

.appendChild(fileref);}catch(e){alert(e);}";
Page.getCurrent().getJavaScript().execute(script);
}
[/code]

I import the script js as described by Mathias.
The first time i access the page, the js file is loaded and it works.
The second time, it does’nt work.
As if the script is not imported.
I don’t see what is wrong in my code?

I use
vaadin 7.6.5
java 7

If you just need to include a js class, you could use the annotation @JavaScript({“pathToFIle.js”}) on a component or the whole application class.