Edit online

Browser Form Control

The oxy_browser built-in form control is used for providing a mechanism to integrate HTML frames or interact with SVG documents directly in the Author mode editor. It can also be used to load HTML that executes JavaScript and from that JavaScript you can access the Oxygen XML Author workspace.

The oxy_browser form control supports the following properties:

  • href - The absolute or relative location of a resource. This property is mandatory. Relative values are resolved relative to the CSS. If you have media resources relative to the XML document, you can specify their paths like this:
    oxy_browser(href, oxy_url(oxy_base-uri(), 'ex.svg')), width, 50%, height, 50%)
  • width - Specifies the width of the content area using relative (em, ex), absolute (in, cm, mm, pt, pc, px), and percentage (followed by the % character) length units.
  • height - Specifies the height of the form control area using relative (em, ex), absolute (in, cm, mm, pt, pc, px), and percentage (followed by the % character) length units.
Example: oxy_browser Form Control
object {
  content:
    oxy_browser(
      href, 'http://example.page',
      width, 600px,
      height, 400px);
}
Tip: To insert a sample of the oxy_browser form control in a CSS file (or LESS file), invoke the Content Completion Assistant by pressing Ctrl + Space and select the oxy_browser code template.

To see more detailed examples and more information about how form controls work in Oxygen XML Author, see the sample files in the following directory: [OXYGEN_INSTALL_DIR]/samples/form-controls.

Interacting with the Oxygen XML Author Workspace

The oxy_browser form control also provides the possibility of creating custom form control without having to use the Java-based API. You can use the oxy_browser form control to load HTML that executes JavaScript. In the JavaScript, you can use some predefined global variables that provide a gateway between the JavaScript and the Oxygen XML Author Java API. This allows you to perform changes in the document, open resources, and more, solely from the JavaScript.
Important: This will only work if the loaded HTML is located inside a framework or plugin directory, such as: [OXYGEN_INSTALL_DIR]/frameworks/ or [OXYGEN_INSTALL_DIR]/plugins/.
The following global variables can be used:
  • authorAccess - This object is an instance of ro.sync.ecss.extensions.api.AuthorAccess.
  • contextElement - An instance of ro.sync.ecss.extensions.api.node.AuthorNode. The form control is added over this node.
  • pluginWorkspace - An instance of ro.sync.exml.workspace.api.standalone.StandalonePluginWorkspace.
  • fcArguments - A java.util.Map implementation with the properties (name and value pairs) passed on the form control function.
  • apiHelper - A helper object for creating Java objects. It allows you to create Java objects from within the JavaScript code. These objects can then be passed to the Java methods as in the following example:
    var newAttrValue = apiHelper.newInstance(
               "ro.sync.ecss.extensions.api.node.AttrValue",
               ["normalizedValue", "rawValue", true]);
    authorAccess.getDocumentController().setAttribute(
            "counter", newAttrValue, contextElement);
    ...
    You can also specify the constructor signature:
    var newAttrValue = apiHelper.newInstance(
            "ro.sync.ecss.extensions.api.node.AttrValue
            (java.lang.String, java.lang.String, boolean)",
            ["normalizedValue", "rawValue", true]);
    authorAccess.getDocumentController().setAttribute(
            "counter", newAttrValue, contextElement);
    ...

For more information, open the form-controls.xml file in the [OXYGEN_INSTALL_DIR]/samples/form-controls directory and go to section 11.1 - Interacting with the Oxygen Workspace.

Warning: On macOS, you need to use asynchronous calls to the API, due to the following JDK bug: https://bugs.openjdk.java.net/browse/JDK-8087465. By default, the API is called synchronously, but you can change this behavior for each API object by calling two methods: sync() and async().
// By default, the methods are invoke synchronously. 
var ctrl = authorAccess.getDocumentController();
try {
  // On Mac, methods that change the document must be executed asynchronously.
  ctrl.async();
  ctrl.setAttribute("counter", newAttrValue, contextElement);
} finally {
  ctrl.sync();
}

Listening for Changes in the Document

If the form control presents some information from the document (for example, the value of an attribute), then it needs to be notified on changes in the document so that it can update that information. To do this, follow these steps:
  1. In the JavaScript, the bridgeReady() method is invoked as soon as the form control is loaded and the API bridge is installed. This is where you can add a listener:
    function bridgeReady () {
      // We declare a member function for each method of the 
      // ro.sync.ecss.extensions.api.AuthorListener interface (same function signature)
      var handler = {
        attributeChanged : function(event) {
          var node = event.getOwnerAuthorNode();
          var attrName = event.getAttributeName();
                
          if (node.equals(contextElement) && attrName === "counter") {
            init();
          }
        },
        contentDeleted : function(event) {},
        contentInserted : function(event) {}
      };
         
      // We create a proxy over an ro.sync.ecss.extensions.api.AuthorListener that will 
      // delegate its methods to the JS object's functions.
      // We assign the listener to a global variable so that we can remove it later on, 
      // on the dispose() method.
      authorDocumentListener = apiHelper.createProxyListener(
          "ro.sync.ecss.extensions.api.AuthorListener", handler);
         
      var ctrl = authorAccess.getDocumentController();
    
      // Add the proxy listener.
      ctrl.addAuthorListener(authorDocumentListener);
    }
  2. Since a listener was added on the document, it is important to remove it once the form control is not used anymore. When a form control is discarded, the dispose() JavaScript function is invoked, so if you have any cleanup to do, make sure you define a function with this name and remove any previously created listeners in it.
    /**
     * The form control will not be used anymore. Clean up.
     */
    function dispose() {
      // Dispose all added listeners.
      var ctrl = authorAccess.getDocumentController();
      ctrl.removeAuthorListener(authorDocumentListener);
    }

Debugging JavaScript Used for Custom Form Controls

If you encounter unexpected results when using the method described above, you can debug the script by using the following guidelines:
  • Calls to alert("message.to.present") or console.log("message.to.present") will be presented in the Results panel.
  • You can install the Firebug extension by executing the following script:
    {code:javascript}
    function installFB() {
      if (!document.getElementById('FirebugLite')) {
        E = document['createElement' + 'NS'] && document.documentElement.namespaceURI;
        E = E ? document['createElement' + 'NS'](E, 'script') 
              : document['createElement']('script');
        E['setAttribute']('id', 'FirebugLite');
        E['setAttribute']('src', 
                          'https://getfirebug.com/' + 'firebug-lite.js' + '#startOpened');
        E['setAttribute']('FirebugLite', '4');
        (document['getElementsByTagName']('head')[0] 
              || document['getElementsByTagName']('body')[0]).appendChild(E);
        E = new Image;
        E['setAttribute']('src', 'https://getfirebug.com/' + '#startOpened');
      }
    }
    {code}
Note: To force the Browser Form Control to reload after making changes to the JavaScript file, you need to use the Reload page action from the form control's contextual menu.

Resources

For more information about form controls, watch our video demonstration: