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
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).
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.
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
Doesn’t work. Script doesn’t gets sent to the client.
EDIT: executeJavaScript works except if javascript contains errors
My use case is:
user pushes button that opens payment choices window
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());
}
}
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.
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
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:
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
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 ) 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.
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)
}
})
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?