Connecting Javascript properties to Java object - javascript

The Problem
I am trying to feed a Java object into a script that would normally operate on the Document Object Model (DOM) of a web page. For the most part this functions as intended. However I have encountered a problem when dealing with attributes/properties of the DOM elements.
A particular property chain of interest is somediv.firstChild.href. What I can't figure out is how to get the firstChild property value dynamically. The simplest way I can think of at the moment is to use source.replaceAll("firstChild", "firstChild()"); to force the firstChild property to invoke the function firstChild() instead. However this will eventually open up a new can of worms.
The Question
How do I define an object that can be passed to a javascript function that can be operated on via the DOM?
Background
Learnings from C#
Before diving into Java I had learnt C#. In C# the concept of setters and getters is quite prevalent. If this interface method were available in Java my problem would be solved.
public string firstChild {
get { return this.getFirstChild(); }
set { this.setFirstChild(value); }
}
Current Implementation
The script is currently invoked by wrapping it in a function where I can pass in the window and document Java objects into the function's workspace.
document is a special top-level version of SpoofedDomElement (that extends it) but is functionally identical to the sample shown below. window is another object with minimal functions that handle event listeners.
Javascript (snippet) to operate on DOM
var somediv = document.createElement('div');
somediv.style.display = "block"
somediv.innerHTML="<a href='/mywork/server/test.html'>The Test Server Homepage</a>";
var linkvalue = somediv.firstChild.href;
This snippet is stored as the string theOriginalSource and used in the next section.
Java code to evaluate Javascript
String wrappedSource = "var scriptToInvoke = function(window, document){"
+ "\n" + theOriginalSource // from above
+ "\n};"
Object result = invocable.invokeFunction("scriptToInvoke", window, document);
This snippet wraps the javascript snippet so that I can pass in objects to use as window and document.
Java classes that spoof DOM elements
public class SpoofedDomElement {
public SpoofedDomElement firstChild;
public String id;
public String innerHtml;
public String href;
public SpoofedStyleProperties style = new SpoofedStyleProperties();
public String tagname;
...
}
public class SpoofedStyleProperties {
public String background = "transparent none repeat scroll 0% 0% auto padding-box border-box";
public String color = null;
public String display = "inline";
}
The above classes handle irrelevant parts of the code just fine (such as the assignment somediv.style.display = "block"). But it starts to fall apart when handling the values of firstChild or innerHtml when either value is changed.
Past Work
N.B. I include this section in all my questions to document what I have tried for future SO users who get here by Google. This might help someone reach a solution by aiding brainstorming.
Attempted Solutions
I have attempted to use a framework (HtmlUnit) to evaluate the Javascript. But I couldn't control which Javascript snippets were executed.
Potential Solutions
The following are questions that I am currently researching to find a solution. If I find anything I will report back.
Is there a way to emulate C# getter/setter behaviour in Java?
Can Javascript evaluate firstChild as a function?
Is there a way to create a wrapper within Javascript with getter/setters that can invoke my Java class's functions?
Is there an Apache Commons library for Nashorn (or similar) that isn't as heavy as the complete simulation frameworks (such as Selenium)?

It appears that you're trying to implement friendly access of script objects from Java code as well as trying to provide script-friendly API on top of java library/libraries.
For the first part [ script object access from Java ]
Apart from javax.script.Invocable interface, you can use JSObject. Nashorn exposes script objects as instances of jdk.nashorn.api.scripting.JSObject/.ScriptObjectMirror
https://docs.oracle.com/javase/8/docs/jdk/api/nashorn/jdk/nashorn/api/scripting/ScriptObjectMirror.html
For the second part [ friendlier access of Java objects from scripts ]
You can write script friendly wrappers in script itself using "JSAdapter".
Doc and Example:
https://wiki.openjdk.java.net/display/Nashorn/Nashorn+extensions#Nashornextensions-JSAdapterconstructor
If you'd prefer to do in Java, you can implement your own jdk.nashorn.api.scripting.JSObject/.AbstractJSObject.
Doc and Example:
https://wiki.openjdk.java.net/display/Nashorn/Nashorn+extensions#Nashornextensions-jsobject
Other nashorn specific script extensions may also be used to trap unknown property/method access in per object basis:
noSuchProperty hook in any script object:
https://wiki.openjdk.java.net/display/Nashorn/Nashorn+extensions#Nashornextensions-noSuchProperty
noSuchMethod hook in any script object:
https://wiki.openjdk.java.net/display/Nashorn/Nashorn+extensions#Nashornextensions-noSuchMethod
Object.bindProperties:
There script API extension can be used to bind properties of one object to another - the source object could be a Java object as well.
https://wiki.openjdk.java.net/display/Nashorn/Nashorn+extensions#Nashornextensions-Object.bindProperties
With jdk9, there is more flexible inter-language linking possible with Dynalink API [ http://openjdk.java.net/jeps/276 ]
See also:
https://blogs.oracle.com/sundararajan/entry/dynamic_linker_api_for_the
https://blogs.oracle.com/sundararajan/entry/writing_pluggable_dynalink_linker_and
https://blogs.oracle.com/sundararajan/entry/nashorn_javascript_access_to_python
There are dynalink samples "samples/dynalink" directory of Nashorn OpenJDK repository:
http://hg.openjdk.java.net/jdk9/dev/nashorn/file/4a6ee1185fc8/samples/dynalink

Related

j2v8: creating new instance of binded object

Our teams is currently under consideration of which JavaScript Engine Bridge to use. We are choosing between LiquidCore and J2V8.
My question is concerning j2v8. Supposedly, I have to create several instances of some java class in JavaScript, how can one achieve this in j2v8 using standard instantiation annotation (new ClassName())? In liquidcore you can bind some class, that extends JSFuction, with super constructor:
JSFunction(JSContext ctx, final String methodName, final Class<?extends JSObject> instanceClass)
and register property with desired class name like this:
jsBaseContext.getJsContext().property("WebSocket", this);
and then on calling:
var x = new WebSocket();
java method methodName will fire, where we'll get new instanceClass as JSValue object as parameter, already binded and ready to use in javascript.
Since no one is going to discuss this topic, i'll answer myself.
This feature is not currently considered to be added in official J2V8 release, however there is a branch on Mizumi's pull request. So, have a look, if you've faced similar issues.

GWT JSNI - Java To Javascript Back To Java Results in undefined params

I have racked my brain on this for the better part of two days. I've read through the documentation on JSNI here as well as a few different blog posts on JSNI and passing variables like this one and nothing indicates I'm doing anything wrong. Essentially what I'm trying to do is call from my GWT Client Side class to a javascript method which I'm exporting to javascript as my class loads. That method takes params from another JS method and stores them on the instance of the Java class that I passed. That seems to work. But, once I reference those methods back in my java code, they're undefined. I believe that what is happening is that my Java Class instance is getting lost somehow after the JS finishes. Here's some code to help explain the workflow...
I have a Java Class named ProfileWidgee. That class has a method to set local variables for location, lattitude, and longitude. That method name is...
public void handleTargetPicked(String mloc, String mlat, String mlng) {
loc = mloc.equalsIgnoreCase("undefined") ? "" : mloc;
lat = mlat.equalsIgnoreCase("undefined") ? "" : mlat;
lng = mlng.equalsIgnoreCase("undefined") ? "" : mlng;
Window.alert("setting on js side" + loc + lat + lng);
}
That method gets exported to the JS as a function using a JSNI method called exportMyFunction...
public static native void exportMyFunction(ProfileWidgee instance)/*-{
$wnd.handleTargetPicked = $entry(
instance.#com.n.j.client.widgees.profile.ProfileWidgee::handleTargetPicked(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;));
}-*/;
That all seems to go fine. It exports and I'm able to call the handleTargetPicked in my JS as follows...
handleTargetPicked(encodeURIComponent(place.formatted_address),
encodeURIComponent(place.geometry.location.lat()),
encodeURIComponent(place.geometry.location.lng()));
All of this seems to work and the Window.alert() displays the correct values. That leads me to believe that it has the appropriate instance of my class and that it is setting the variables appropriately. Later on though as I'm back in my Java class, I try and reference those variables and they always come back as 'undefined.'
Window.alert("reading on the java side" + pw.getLoc() + pw.getLat() + pw.getLng());
This results in 'undefined' for all three values. So my big question is ... is it possible to set a value in your Java class from the JS side and then use that value later in your class?
I just ran into a somewhat similar situation like this and happened to see your post.
I couldn't see any solutions suggested anywhere, so tried to debug it myself.
What I saw was, 'this' variable was pointing to Window rather than the object instance.
So rather than calling the method directly like e.g. handleTargetPicked(arg1, arg2), I used method.call() passing the context like e.g. handleTargetPicked.call(instance, arg1, arg2). That kind of approach solved the issue for me. Hope that helps.

How to document JavaScript objects (from UML perspective)

I am wondering (perhaps more so from a UML point of view) how others have managed to document JavaScript objects successfully.
JavaScript can be expressed as OOP but in addition to these objects, files may also contain 'lose standing' scripts, which belongs to the file itself - not the object you defined.
Would you document the file itself as a class, and the classes it contain as nested classes?
Someone adapted UML for web artifact, its called the "WAE" extension of UML.
If you work with node.js, i created a module that generate class diagram for javascript/node/html/css. Its called wavi. For javascript, function,variable are automatically recognized. You can use it for documenting your application.
https://www.npmjs.org/package/wavi
No, it wouldn't make sense to document a JavaScript file as a class (with inner classes), but the "classes" it contains may be documented with a UML class diagram. However, since there is no explicit class concept in JavaScript, people use different code patterns for defining a "class". The most commonly used code pattern is the constructor-based definition of classes as proposed on the Mozilla website. In this approach, the constructor function (say, C) represents the class. It defines a number of properties (using this) and a number of methods/functions (using C.prototype). Then, in a UML class diagram, these properties and methods can be described in the form of a class rectangle.
If you want to document your javascript objects, you can even use a MVC pattern for javascript projects. I would personally try to avoid using innerclasses and use proper relationships between classes. If you mean with innerclasses; putting multiple classes sepperated in the same js file, then; yes that's a valid option. Javascript doesn't care in which file a class is, it only thinks about the classes (unlike Java if I'm correct).
Example I wrote 8 years ago for school showing the Control class:
function Control()
{
var myView = new View(respondOnChoice);
var myMathTest = new MathTest();
var myExercise = new Exercise();
function respondOnMathTestChoice()
{
myView.emptyMainDiv();
myView.showNameAndClassChoice();
}
}
Start.js (to initiate the control):
if (window.attachEvent) //IE
{
window.attachEvent("onload", initApp);
}
if (window.addEventListener)//Firefox
{
window.addEventListener("load", initApp,false);
}
function initGame()
{
var myControl = new Control();
}
In my opinion, this is the best way to use OOP in javascript. If you program properly, you won't have to mind about anything to do with innerclasses, just relationships between classes.

Get all properties of an HTML element without running JS inside browser

I need to find all available properties of any HTML element.
Using javascript if we have to find all properties of an html element we do this
function getAllProps(objects){
props = []
for(var key in objects) {
props.push(key)
}
return props;
}
And we use above function in this way:
var btn = document.createElement("BUTTON");
document.body.appendChild(btn);
console.log(getAllProps(btn));
Above code is completely browser dependent. We need to run this code inside browser to get it running.
I want to know, is it possible to do the same using cscript.exe or any other external JS interpreter?
Thanks in Advance,
Obviously you can not do this. To get the properties of an object you need that object and the get that object you need an implementation of that object. The best thing you can do is to look into the DOM specification and read what properties should be present in a conforming implementation.
There are implementation of the DOM for JavaScript runtimes that are not browser runtimes but then you are still depending on a specific implementation. Here is one for Node.js but I have no idea how good or complete it is.

Is there a good alternative to having JavaScript code as string literals in my ASP.net code?

Working on an ASP.net web application, I've been wondering if there is a good way to avoid writing JavaScript code in string literals in my ASP.net code. See here: https://web.archive.org/web/20211020150655/https://www.4guysfromrolla.com/articles/030202-1.aspx.
In the linked example, I see code that looks like:
Private Sub Calendar1_SelectionChanged(sender As Object, e As EventArgs)
Dim strjscript as string = "<script language=""javascript"">"
strjscript &= "window.opener." & _
Httpcontext.Current.Request.Querystring("formname") & ".value = '" & _
Calendar1.SelectedDate & "';window.close();"
strjscript = strjscript & "</script" & ">" 'Don't Ask, Tool Bug
Literal1.Text = strjscript 'Set the literal control's text to the JScript code
End Sub
I'm not used to using much JavaScript. A lot of the code that I've worked with has been mostly server-side coding with T-SQL. The above code gives me a headache just looking at it. Not only is it ugly, but it shows a pattern where a malicious user could try to inject malicious code.
Is there a better way to avoid manipulating JavaScript code as string literals? Think of the ways we have to avoid manipulating T-SQL code as string literals.
Ugh, dynamically building javascript and putting it inside a literal?
Generally the only time I embed javascript in code is when I am making a custom control and want it packaged neatly (no sepatate js file to worry about), and even then I use RegisterClientScriptBlock instead of a hack like this.
Why not just have a javascript function inside the page source (or an include file) that takes two parameters (form name and selected date) and then dynamically build the function call instead of the entire script?
A common way is to use the clientscriptmanager class:
http://msdn.microsoft.com/en-us/library/z9h4dk8y.aspx
You can call the registerstartupscript method, which will add the script to the end of your page, executes when the page finishes loading but before the page's OnLoad event is raised.
The RegisterClientScriptBlock method adds the script to the top of your page. This is where you might add commonly used fnctions.
Dim script As New StringBuilder()
script.AppendFormat("window.opener.{0}", Httpcontext.Current.Request.Querystring("formname"))
script.AppendFormat(".value = '{0}';window.close();", Calendar1.SelectedDate)
Dim cs As ClientScriptManager = Page.ClientScript
cs.RegisterClientScriptBlock(Me.GetType(), "ScriptKey", script.ToString(), true)
The last parameter tells the script manager to wrap the script in <script>...</script> tags so that you don't have to.
Also, if you are adding scripts from a user control, the "ScriptKey" makes sure that the same script does not get added more than once. If you need a separate script for each control, you can dynamically generate that parameter based on the control id.
The other common method for adding links to script files on your page is RegisterClientScriptInclude
Instead of writing out the complete function, embed the function on the page or in an external file and only dynamically write out the values. For example:
<script>
<asp:Literal ID="ScriptValues" runat="server" />
</script>
<script>
function foo(bar) { ... }
</script>
Then in your code behind or wherever (sorry, I don't do VB):
var values = new StringBuilder();
values.Append("var bar = " + bar + ";");
...
ScriptValues.Text = values.ToString();
for starters the StringBuilder is far better for this than using String (it's easier to read and more performance tuned)
Dim sbuild As StringBuilder = New StringBuilder
sbuild.Append("<script language=""javascript"">")
sbuild.Append("window.opener.")
sbuild.Append(Httpcontext.Current.Request.Querystring("formname"))
sbuild.Append(".value = ")
sbuild.Append(Calendar1.SelectedDate)
sbuild.Append("';window.close();")
sbuild.Append("</script>")
Literal1.Text = sbuild.ToString
But beyond that, I would suggest trying something like the TagBuilder Class. It says it's for MVC, but I don't see why you can't use it in a Web Forms scenario as well (you'd just have to import the MVC namespace) - (though I could be wrong on this part).
There are a few things to consider in dealing with your issue.
There are several methods, including Page.RegisterClientScript, that handle some of the dirty work, by properly wrapping your JavaScript code in the proper tags, as well as placing it within the proper place in the page (inline vs. beginning/end) that will deal with some of the formatting issues.
Your code sample above is VB.Net, which is little rough working with with large amounts of text due to the requirement of having to append the &_ to every line. C# does a better job at this. The good news is that with the release of .Net 4, you no longer have to worry about doing all the line concatenations.
If you are dealing with a large amount of text that you need to embed, you could consider keeping your JavaScript in a separate text file, and read the file into your literal, or script registration. You can even do some simple string replacements if you have to have some dynamic data. The StringBuilder class is also a help, with the use of the Append and AppenLine methods(), but again it depends on how much text you're dealing with and how often you'll be needing to work with the code block in question.
Move as much as possible into a .js file.
If anything, you should only need to render simple js function calls. I try to minimize the need for these, by adding a css class and then using jquery's class selector to attach the behavior.
From the example posted by rockinthesixstring, if you want to "clean" up the visual aspect of the code, you would also write it as:
Dim sbuild As StringBuilder = New StringBuilder
With sbuild
.Append("<script language=""javascript"">")
.Append("window.opener.")
.Append(Httpcontext.Current.Request.Querystring("formname"))
.Append(".value = ")
.Append(Calendar1.SelectedDate)
.Append("';window.close();")
.Append("</script>")
End With
Literal1.Text = sbuild.ToString
However I would look into the methods Page.ClientScript
Page.ClientScript.RegisterStartupScript
or if you are using a ScriptManager
ScriptManager.RegisterStartupScript
When working with JavaScript in ASP.NET, these are the paths you should follow:
Put it in a seperate JavaScript file. More maintanable.
However, if you (for whatever reason) can't put it in a JavaScript file, put it in a static class which exposes the script as constants with placeholders for value insertion (use the # symbol so you don't have to escape characters.
Like this:
public static class JavaScriptStuff
{
public const string SpecialScriptFormat = #"window.opener.{0}.value = '{1}';window.close();"
}
Then register it using ClientScriptManager - this way you also don't need to explicity open/close the script tag (stops human error).
http://msdn.microsoft.com/en-us/library/system.web.ui.scriptmanager.aspx
string myScript = string.Format(JavaScriptStuff.SpecialScriptFormat, HttpContext.Current.Request.QueryString("formname"), Calendar1.SelectedDate);
Page.ClientScript.RegisterStartupScript(this.GetType(), "myscript", myScript, true);
You can go even further, and not expose the scripts as public properties, instead expose "getter" methods which accept the params - which adds another layer of maintainability:
public static class JavaScriptStuff
{
private const string SpecialScriptFormat = #"window.opener.{0}.value = '{1}';window.close();"
public string GetSpecialScript(string queryString, string selectedDate)
{
return string.Format(SpecialScriptFormat, queryString, selectedDate);
}
}
}
Page.ClientScript.RegisterStartupScript(this.GetType(), "myscript", JavaScriptStuff.GetSpecialScript(HttpContext.Current.Request.QueryString("formname"), Calendar1.SelectedDate), true);
HTH

Categories

Resources