Projects‎ > ‎

XForms - Support server-side JavaScript in XForms actions

NOTE: This page describes an Orbeon Forms project, not a feature which is currently part of Orbeon Forms.

Rationale

XForms actions are very powerful for relatively simple tasks, but they do not have all the properties of a modern scripting language. For more complex actions within forms, it is valuable to offer support for JavaScript and other languages.

Status

2011-01-03: Very experimental JavaScript/CoffeeScript support in the code. API is not developed yet.

Next steps

  • define simple use case to use for full demo (spell checker?)
  • implement most useful API to support use case
  • DOM access
  • local state

Proposal

The implementation uses the Rhino JavaScript engine.
The <xxf:script> action is extended to support a new runat attribute which is set to value server:

<xxf:script runat="server">
    ...
</xxf:script>

CoffeeScript can be supported with a new type attribute:

<xxf:script runat="server" type="text/coffeescript">
    ...
</xxf:script>

In this case, the XForms engine must invoke the CoffeeScript compiler and pass the result to Rhino.

API

Requirements

The script must have access to an API exposing useful aspects of the XForms runtime, including:
  • access to XForms actions such as setfocus, etc.
  • access to XForms functions such as index(), etc.
  • read/write/mutate XForms instances, possibly through a DOM API

Use from XBL

XBL includes server script (companion JavaScript file):

<xbl:script src="/xbl/orbeon/foobar/foobar-server.js" runat="server"/>

Script imports API with require keywords:

var model = require('orbeon/xforms/model')
...
var myInstance = model.instance('my-instance')
var firstName = $('first-name', myInstance)
...

Script runs in scope that is automatically available from within XBL:

<xxf:script ev:event="DOMActivate" runat="server">
    TODO: call function
</xxf:script>

TODO:

  • how does script access correct local XBL model?

Use from the top-level

[TODO: top-level must also be able to include server scripts that expose functions]

Instance access

[TODO]

Idea:
  • API to return instance (could be same as instance() / xxf:instance())
  • returns a DOM which wraps around dom4j and is able to intercept mutation
  • user will typically use jQuery or similar to navigate DOM of instance
  • an XPath API could be provided as well and/or E4X support could be tried

Controls access

  • read control value
  • write control value

Actions

[TODO]

Standard XForms actions:
  • setvalue
  • insert/delete
  • dispatch
  • insert
  • load
  • message
  • rebuild/recalculate/revalidate
  • send
  • setfocus
  • setindex
  • toggle
Extension actions:
  • show/hide
  • invalidateInstance / invalidateInstances
  • joinSubmissions
NOTE: the client-side API already features the following methods:
  • ORBEON.xforms.Document.dispatch()
  • ORBEON.xforms.Document.getValue()
  • ORBEON.xforms.Document.setValue()

Functions

Standard XForms functions:
  • event
  • index
  • instance
Extension functions:
  • MIPs
    • readonly / relevant / required / valid / type
  • case
  • index
  • instance
  • invalidBinds
  • itemset
  • pendingUploads
  • lang
  • listInstances
  • listModels
  • propertiesStartWith
  • property
  • request
    • getRemoteUser
    • getRequestAttribute
    • getRequestHeader
    • getRequestParameter
    • getRequestPath
    • getScopeAttribute
    • getSessionAttribute
    • setRequestAttribute
    • setScopeAttribute
    • setSessionAttribute
    • isUserInRole
Standard functions that we probably don't need right away if at all:
  • context
  • current
  • digest
  • hmac
  • isCardNumber
  • property
  • related to XPath limitations
    • booleanFromString
    • choose
    • countNonEmpty
    • if
    • power
    • random
  • date/time
    • daysFromDate
    • daysToDate
    • localDate
    • localDateTime
    • months
    • now
    • seconds
    • secondsFromDateTime
    • secondsToDateTime
Extension functions that we probably don't need right away if at all:
  • binding
  • callXPL
  • componentContext
  • encodeISO9075 / decodeISO9075
  • docBase64
  • attribute / element
  • evaluateAVT
  • evaluateBindProperty
  • extractDocument
  • formatMessage
  • formURLEncode
  • mutableDocument
  • repeatNodeset
  • sort
  • bind

[TODO]

Storing and retrieving state

[TODO]

Rationale

Currently, XForms actions that need state must create XForms instances and store state into those. This is often cumbersome. Actions written in JavaScript that need to keep state information should be able to use JavaScript data structures.

Format should be limited to basic types and JSON (and possibly XML) so that state can be serialized and deserialized.

Ideas

Script can store state information into models, including local XBL models. For example:

model.state.foo = < JSON or XML >

Q: Should there be a way of creating JSON XForms instances? This would allow the following scenario:
  • JS/CS calls API to send submission
  • submission returns JSON
  • JS/CS consumes JSON
Issues with this:
  • a JSON instance would be "foreign" in XML land
    • no support for XPath bindings
    • no access from XPath except through special accessor functions
Either way, the ability for JS/CS to leverage xf:submission to call services and get data back, typically in JSON format, should be supported.

[TODO]

Accessing JavaScript functions from XPath

XForms 1.2 might introduce Custom XPath Functions, which are meant to provide simple reusable XPath functions.

With JavaScript available for scripts, it would also make sense to allow writing / using such extension functions in JavaScript.

Options for defining functions (TBD):

  • Functions available in an XBL companion JavaScript file might simply be made available to XPath running within the scope of the XBL component.
  • Functions might be defined in an <xf:function> block. In this case, name and parameters are declared like for extension XPath functions using an XML syntax.
  • Functions might be defined in an <xxf:script> block. In this case, the script can simply expose functions as function foo(...)

Options for calling functions (TBD):

  • If functions are defined in an <xf:function> block, the will have a QName and can be called directly: foo:bar(item)
  • All in-scope (and maybe exported) functions could be exposed through a standard namespace prefix, e.g. js:bar(item)
  • An XPath function could be used: xxf:javascript('bar', item)

Examples

Example 1


Comments