ORT-JAVASCRIPT(1)

NAME
ort-javascript - produce ort JavaScript and TypeScript classes

SYNOPSIS
ort-javascript [-t] [config...]

DESCRIPTION
The ort-javascript utility accepts ort(5) config files, defaulting to standard input, and creates JavaScript or TypeScript classes that format the JSON produced by ort-c-header(1) json_xxxx() functions. Its arguments are as follows:

-t  Emits Typescript instead of JavaScript.

config
    Configuration files in ort(5) syntax.

Output consists of a set of classes within the "ort" namespace describing structures, enumerations, and bitfields. These classes include methods to fill data from the JSON into a DOM tree by operating on elements having specific classes.

All output files are fully documented in the jsdoc format.

  Modules
All data produced by ort-javascript is contained within a namespace or module (depending upon whether -t is specified) called ort. For TypeScript output, data is namespaced. For example, let "foo" be a struct defined in the configuration.

    namespace ort {
      export interface DataCallbacks { ... }
      export interface fooData { ... }
      export class foo { }
    }

For JavaScript, data uses the module pattern.

    var ort;
    (function(ort) {
      var foo = (function() { ... }());
      ort.foo = foo;
    })(ort || (ort = {}));

  Interfaces
The concept of an interface is only applicable to -t output. Each struct in the configuration is rendered as a TypeScript interface suffixed "Data", for example, "fooData" for a structure named "foo". Each native field is mapped to a native type: string or number. Ror references, the type is the referenced interface type.

    namespace ort {
      export interface fooData {
        sometext: string;
        somenumber: number;
        areference: barData;
      }
    }

In addition to this, a "DataCallbacks" interface may be used for Customisation and contains callback functions for each field for formatting purposes.

In JavaScript output, which has no interfaces, the programmer must ensure that the variables are properly constructed.

  Constructors
Structures are output as classes containing formatting methods. (Enumerations and bitfields have static classes.) They are named as found in the configuration. Each structure has a constructor to which one passes a single object of the given interface type or an array. For TypeScript, this is the "fooData" interface. In this example, let parseData create the required interfaces.

    let input: ort.fooData = parseData();
    let foo: ort.foo = new ort.foo(input);

For JavaScript, this is a simple dictionary.

    var input = parseData();
    var foo = new ort.foo(input);

The array may be zero-length, but the object may not be null.

  Class Methods
The following methods are generated for each structure.

fillInner(e, [custom])
    Fill labelled elements within (but not including) element e, with optional call-backs custom. If e is null, does nothing.

fillInnerByClass(e, className, [custom])
    Convenience method for invoking fillInner() over all descendents of element e having class className. If e is null, does nothing. The "inner" applies to bot" e and the matched descendents.

fill(e, [custom])
    Fill labelled elements within (including) element e, with optional call-backs custom. If e is null, does nothing.

fillByClass(e, className, [custom])
    Convenience method for invoking fill() over all descendents of (and including) element e having class className. If e is null, does nothing.

fillArray(e, [custom])
    First hides (appends the "hide" class) e, then invokes fill() repeatedly over the objects (or standalone object) passed at construction time by removing, then subsequently cloning and appending, the first element of e. The fill() function is passed the new element's root and custom. If e is null or empty, does nothing except keeping e marked as hidden.

fillArrayOrShow(e, toshow, [custom])
    Convenience method around fillArray, showing (remove the "hide" class) toshow if the object or objects at construction are non-empty.

fillArrayOrHide(e, tohide, [custom])
    Convenience method around fillArray, hiding (append the "hide" class) tohide if the object or objects at construction are empty.

Applicable element labels (class names) are as follows, letting "foo" be the name of a structure in the config and "xxxx" be any of its fields.

foo-xxxx-bits-checked
    Sets or unsets the checked attribute depending upon whether the input's value attribute (if found) is covered by the object's bitmask. Only applicable to bit types.

foo-xxxx-date-text
    Replaces the contents of the element with the ISO 8601 date of the object's value. Only applicable to date and epoch types.

foo-xxxx-date-value
    Sets the value attribute to the ISO 8601 date of the object's value. Only applicable to date and epoch types.

foo-xxxx-enum-select
    All <option> descendents of the element are marked as "selected" or not depending upon whether their value matches the field value.

foo-xxxx-obj
    For structures, creates and invokes the fillInner() method on the nested structure at the given element and its descendents. This is only applicable for structure types.

foo-xxxx-text
    Replaces the contents of the element with the field value. This is not applicable to blob native types.

foo-xxxx-value
    Sets the value attribute (as in a form submission) with the field value. This is not applicable to blob native types.

foo-xxxx-value-checked
    Sets or unsets the checked attribute depending upon whether the input's value attribute matches the objects. This is not applicable to blob or structure types.

foo-has-xxxx
    Remove the “hide” class if the object is null, otherwise add the “hide” class.

foo-no-xxxx
    Add the “hide” class if the object is null, otherwise remove the “hide” class.

  Static Members
Each enumeration class contains static members with the numeric value of its items.

Each bitfield class contains two static members per item: one for the bit index, one for the generated bit mask. The former is prefixed BITI_, the latter with BITF_. Thus an item "foo" creates numeric static members BITI_foo and BITF_foo. There is always a BITI__MAX that is one greater than the largest item's value.

  Static Methods
Each enumeration corresponds to a class with field values and formatting static methods. These take advantage of the jslabel enumeration label described in ort(5).

format(e, name, value)
    Fills in all elements (not inclusive) descending from e having class name-label with the configuration label corresponding to the enumeration value value. If name is null, the element itself has its contents filled in. It value is null or not a valid enumeration value, the empty string is filled in.

If a language is specified in the root of the HTML or XML document with the "lang" attribute, it is first matched a label for that language. If there is no language, or none for that label, the default label is used. If there is no default label, an empty string is used instead.

Bit-fields behave similarly and have the same member.

format(e, name, value)
    Fills in all elements (not inclusive) descending from e having class name-label with all configuration labels with corresponding bits set in value. If name is null, the element itself has its contents filled in. It value is null, the "ort-null" class is added and the isnull label is used. If not specified, an empty string is used. If value is zero, the "ort-unset" class is added and the isunset label is used. If not specified, an empty string is used.

  Customisation
All functions accept an optional argument for providing custom per-field or per-structure callbacks. Keys in the object must consist of the structure name, followed by a dash, followed by the field name. For example, assuming a structure “client” with a field “dob” consisting of a UNIX epoch:

    var custom = { 'client-dob': formatDate };
    new ort.client(obj).fillInner(document.body, custom);

And letting a formatting function be:

    function formatDate(e, name, value) {
      var list, i;
      list = e.getElementsByClassName('client-dob-date-moment');
      for (i = 0; i < list.length; i++)
        list[i].innerHTML =
          moment.unix(value).format('DD-MM-YYYY'));
    }

This invokes the “moment.js” formatter to create dates.

The same can be applied to structures instead of to fields within structures. The keys for these are simply the structure name.

    var custom = { 'client': formatClient };
    new ort.client(obj).fillInner(document.body, custom);

The callback will then be provided the full client object.

In either case, the value for the custom key may also be an array of functions just as above. Each will be invoked in the order given, in the same way.

    var custom = { 'client': [ format1, format2 ] };

The callback function (or functions) will be invoked regardless of whether the value has been set. In the event of an unset field value, the function is passed null.

When using TypeScript, the values of the format function are typed according to the structure or field passed. For example, if the “dob” field of structure “client” is a UNIX epoch, the format function accepts a number or null.

    function formatDate(e: HTMLElement,
      name: string, v: number|null): void {
    	/* Do something... */
    }

The dictionary object is defined as follows:

    let custom: DataCallbacks = { 'client-dob': formatDate };
    new ort.client(obj).fillInner(document.body, custom);

For example, to fill in the label of an enumeration enum someenum on a field named val, provide a custom callback.

    var obj = JSON.parse(response);
    var e = document.getElementById('foo');
    var custom = {
      'foo-val': ort.someenum.format
    };
    new ort.foo(obj).fill(e, custom);

EXIT STATUS

EXAMPLES
Start with a means to contact a CGI script producing JSON data formatted by the json_xxxx() family of ort-c-header(1). This does not do any error checking.

    function init(): void
    {
      let xmh: XMLHttpRequest = new XMLHttpRequest();
      xmh.onreadystatechange = function(){
        let v: string = xmh.responseText;
        if (xmh.readyState === 4 && xmh.status === 200)
            success(v);
      };
      xmh.open('GET', 'https://your-cgi-script, true);
      xmh.send(new FormData(form));
    }

Now define success() to parse the JSON response content using the classes and methods defined in the output of ort-javascript.

    function success(resp: string): void
    {
        let obj: ort.fooData =
            <ort.fooData>JSON.parse(resp);
        new ort.foo(obj).fill(document.getElementById('place'));
    }

Lastly, use the following abbreviated HTML in which to display the contents of these objects. Let driver.js consist of the AJAX snippet and formatter and foo.s be the output of ort-javascript.

    <!DOCTYPE html>
    <html lang="en">
        <head>
          <title>Example</title>
          <script src="foo.js"></script>
          <script src="driver.js"></script>
        </head>
        <body>
            <div id="place">
                <span class="foo-xxxx-text>
                    Replaced by the "text" field.
                </span>
            </div>
        </body>
    </html>

Finally, to drive the script, cause init() to be invoked when the page has loaded. This may be in driver.js or directly in the document header.

    window.addEventListener('load', init);

SEE ALSO
ort-c-header(1), ort-c-source(1), ort(5)

BUGS
This most significant issue with JavaScript and ort-javascript is that of JSON/JavaScript incompatibility. In ort-javascript, as exported in JSON, all integers are signed and 64 bits. JavaScript (and of course TypeScript) encode integers as double precision floats, which leave than 64 bits of precision. Therefore, it's very possible to transmit valid numbers and have them be truncated by JavaScript interpreters.

There is as yet no simple way to protect against this. While some aspects (like enumeration values) may be limited to 32 bits, raw data emitted by the system is prone to truncation.