JOPE JavaScript Applications
============================
OK, so this kinda works now. This package lets you write JOPE applications in
JavaScript, w/o the need to compile any Java code. To do so, it uses the Rhino
JavaScript interpreter provided by the Mozilla project.
Since Rhino can access all Java classes you can reuse all classes from JOPE,
even other Java JAR packages. But there are a few limitations, eg you cannot
subclass Java classes in Rhino.
Hence this 'jsapp' package provides the necessary hooks and JOPE subclasses to
allow scripted applications.
Basic Structure
***************
A jsapp application currently has the following directory layout:
AppName/ (eg HelloWorld)
Application.js
Session.js
Context.js
Main.wo/
Main.html
Main.wod [optional]
Main.js [optional]
You can put in as many components as you like, but they must be 'wrapper' style
components (.wo directories). The .wod and .js are optional, the .js can contain
JS functions and variables of the component.
Running an App
**************
To run the JS application you should use the 'run' class from the jsapp package:
java org.opengroupware.jope.jsapp.run AppName/
Also check the run.sh, it contains a template of a shell script to run the app.
Be sure to include all necessary JARs in the classpath, eg js.jar, the Rhino
interpreter.
During development it makes sense to add a Defaults.properties file containing:
WOCachingEnabled=false
so that template or script changes require no restart.
Example
*******
Check samples/HelloJS for a fully scripted example application.
API
***
The API exposed towards Application, Component, Context and Session scripts its
mostly the same like the API of JOPE. If a script implements a relevant
function (eg awake()), jsapp will call it.
To call 'super' implementations (default implementations), prefix the function
with a 'super_', eg:
function shouldTakeValuesFromRequest(_rq, _ctx) {
return super_shouldTakeValuesFromRequest(_rq, _ctx);
}
Be careful, in JS you do not need to specify all parameters you get in the
function definition (eg you could leave out '_ctx'), but when you call super
you must specify everything which is required.
Tips
****
* 1) Same Namespace for Variables and Functions (aka Accessors)
Remember that JS has the same namespace for ivar and function slots, eg this
does not work in JS:
var note;
function setNote(_o) { this.note = _o; }
function note() { return this.note; }
The function overrides the ivar. In fact, the function here returns itself,
in KVC eval this will show up as:
"org.mozilla.javascript.gen.c17@2cb491"
(the compiled JS function)
* 2) Importing Java Packages
Remember that you need to import packages. Its reasonably easy:
importPackage(org.opengroupware.jope.foundation);
* 3) OGNL Interop (aka no OGNL on JavaScript components)
Remember that OGNL does not work against JS, just use JavaScript! Eg:
[TBD: we might be able to implement a proper OGNL handler?]
* 4) Writing Format classes
To create a formatter in JavaScript just use the JSFormat object. It takes a
function, eg:
var firstLast = new JSFormat(context, function(_obj) {
return _obj.firstname + " " + _obj.lastname;
});
This could be used in a WOString, eg:
* 5) Scopes
TDB: fix this documentation for the new JSCachedScriptScope stuff
Scopes are tricky, especially when you do cross-component calls. Consider this:
var newPage = pageWithName("TheOtherPage");
newPage.setTitle("Hello World");
where NewPage.js looks like this:
var title;
function setTitle(_value) {
title = _value;
}
This does NOT work properly because setTitle() won't find the 'title' variable.
The name lookup happens at the *calling* site (the one which triggers setTitle).
Hence, its better to use 'this' to target components slots in functions:
function setTitle(_value) {
this.title = _value;
}
[TBD: I'm still not 100% sure whether thats a bug in our code, eg wouldn't the
component object be the prototype or something of the setTitle function?]