API Reference

loading zynii

Your app's web page will need to include something like this:

<script src="zynii/zynii.js"> </script>  

Change the path to match the actual location of the zynii.js file on your setup.

creating an app

The most important method in ZYNII is newApp(model, schema, options). You use this to create a single app object which then handles your data-model and displaying of data in the HTML. Typically you would use:

var app; //global scope

  function load() {
    app = ZYNII.newApp(model, schema, options);
  } 
CAVEAT: In principle, you can use any name you like for the app and it does not have to be global. However, the debugger expects the app to be in a global variable - app.

model

This sets up the initial values of the model. It is probably a good idea to setup a skeleton data structure using this. This is especially true if using the schema to load data from remote sources.

schema

schema is optional. It may be omitted, replaced by null or an empty object {}. If supplied, the schema consists of an object. Each key should be a path in the data model.

var schema = {
  '@local' : {persist:'local', localKey:'TEST1'},
    '@web' : {persist:'http', getURL:'service.php', loadInterval:600}
};      

The value is an object with the following properties:

.persist
local | http | file. Specifies whether data is stored in localStorage, on the server or in a file. The last option only works under nodeJS.
.localKey
key to be used for load from/save to localStorage
.pathName
pathName of file in file system (nodeJS only).
.getURL
URL used to retrieve the data
.postURL
URL used to store the data.
.loadInterval
Sets how often (in seconds) Zynii will check before loading a possibly newer version of the data from the server. If omitted or 0 loading is never done automatically.
.storeInterval
Sets how often (in seconds) Zynii will check before doing a save. If omitted or 0 save is never done automatically.

If loadInterval/storeInterval are omitted data must be loaded/stored manually using app.model.load(path)/store(path).

CAVEAT: This documentation describes the next version (0.9.9) not yet implemented!
CAVEAT: This feature should be used sparingly. There is a danger of overloading the client if there are too many automatic persistent entries. There is also a danger of overloading the server if the polling is done too often.

options

The options object is optional. It may be omitted, replaced by null or an empty object {}. If supplied, options may contain any of these properties:

.mode
blur | click. User interface behaviour. User changes take effect either when the user leaves a control (blur) or immediately on any change.
.tick
number of mS between internal updates (defaults to 250).

API

app properties/methods

newApp(model, scema, optons)
Constructor function returns a new app.
.model
Property holds the current data-model.
addFunc(name, kind, func)
Used to add your own functions to the app. Functions may be used to do custom drawing, calculation, sorting or filtering.
makeDirty(path)
.
app.bind(el)
binds element el and it's descendants to the data-model. If el is omitted document.body is used.
app.unbind(el)
Removes bindings - useful if removing HTML.
subscribe(f)
.
unsubscribe(f)
.
debug()
Returns debug data from the app.

app.addFunc(name, kind, func)

You can add your own functions to the model. These functions may be used to draw HTML, calculate values, sort or filter content. kind is a string defining the type of function. It is (currently) optional except for 'sort' and 'filter' for those kinds of functions. Add a function with:

  binder.addFunc('myFuncName', 'filter', function(..) {
      ... custom code goes here ...
    }); 

app.model properties/methods

Each app object owns it's own data-model at app.model. This supports the following methods:

.execJSON(path,value)
Updates the model changing the content at path to the new value.
.asJSON(path)
Returns a copy of the data in the model at the location given by path.
.exec(cmd, path, value)
Low-level command allows adding/updating/deleting of individual items. cmd is one of C|R|U|D. Standing for create, read, update and delete.
.load(path)
Loads data into model at path. The source of the data is preset in the schema.
.store(path)
Stores model data at path to external stroage.
.debug()
Method returns internal state of model. Used by Zynii debugger.
.setOnChange(f)
Assigns the function f to the onchange event. Gets called each time data is changed using one of the high level functions. [Should probably only be used if creating an instance of model spearately from app.]

zynii paths

Your app object owns a single data-model. This may be thought of as being something like a single JSON data structure. You get/set data in the model using zynii paths. The path is the same string you would have used in javascript code to refer to that item, but prefixed with @. So if the data-model looks like:

  { 'a': [0,1,2], 'b': {'x':0, 'y':2} }

valid paths might be @@a, @a[1] and @b.x. Zynii is slightly stricter than javascript. If the item is an element in an array you must use [n], if it's a property of an object you must use .name. So @a.1 or @b['x'] are forbidden.

Differences between Zynii and JSON

It is probably simplest to think of the data-model as being a JSON object. However there are one or two minor (but quite sensible) syntax differences. When setting values you are actually using javascript not JSON. So you are not bound by the strict syntax rules of JSON. This means you can use either ' or " for strings. And even omit them on object key names.

Also, in javascript property keys can be any string. Including strings with spaces or other non-alphanumeric characters. In Zynii you retrieve data by paths. This means you should only use key names that can be written into a path. @x.longKeyName is fine, @x.long key-name isn't.

zynii expressions

Much of the power of zynii comes from the ability to include zynii expressions in the page's HTML or a template in the page. Expressions are made up of one or more short statements contained in a data-zynii or data-zynii-xxx attribute.

An expression is made up of one or more statements separated by ;. Statements typically consist of a token (lhs) followed by = and then an expression on the rhs. The lhs is the name of some part of an element whose value will be set. This may be an attribute/property or an event handler. If the lhs and = is omitted the rhs is bound to the element's value or it's innerHTML (depends on the name of the element).

The syntax is somewhat like javascript. But note, this is not simply javascript code, instead it is a declaration of a relationship between the element and the model.

The rhs may be a simple value, e.g. a string, number or boolean value. It may be a more complex JSON style object or array. These have the same syntax as in javascript. In addition, the rhs can be a zynii path or a function defined in or added to the app. Functions can be passed arguments and these can be any valid rhs expression. So functions can be passed other functions, paths, javascript values.

Strings and paths also support a substitution facility. A path/string can include { .. } containing a valid expression. Each time the expressions is evaluated, the substitution will be evaluated first.

Notes: Although the syntax is supposed to be javascript-like. There are a few differences. Paths are text sequences prefixed with an @. For this reason @ is forbidden in function and property names. Since the expressions will be placed within an html attribute string, it helps to be able to use either form of string ' or ". So the syntax for values is more permissive than strict JSON.

Simplified grammar (BNF) for zynii expressions:

program     =  statement [ ; statement ]* [ ; ]
statement   =  ( assignment | expr ) [ : type ] 
assignment  =  token = expr
expr        =  boolean | number | string | array | object | path | function
boolean  =  false | true             // as javascript syntax
number   =  [+|-]numeric+[.numeric+][e|E[+|-]numeric+]  // as javascript syntax
string   =  " [A | S | subst]* " | ' [A | S | subst]* '
array    =  \[ [expr[ , expr]*] \]
object   =  { [ key : expr [, key : expr]*] }
key      =  simpleString | token
simpleString = " [ alpha | numeric | S ]* " | ' [alpha | numeric | S ]* '
path     =  @ [A | subst]+
function =  token ( [ expr [ , expr]*] ) 
subst    =  { expr }
A        =  alpha | numeric | . | [ | ]  // everything except whitespace and "'(),{}
type     = 'boolean' | 'number' | 'string' | 'array' | 'object' 
token    =  alpha [ alpha | numeric]*
alpha    =  A..Za..z_$
numeric  =  0..9
S        =  whitespace  // ' ' \r \n \t  

data-zynii attributes

Zynii supports a small number of HTML attributes. These are used to setup a binding between an HTML element and data in the model.

attribute type value
data-zynii statement(s) bind value, events and attributes/properties.
data-zynii-format type string|number|boolean|eN|fN
data-zynii-lookup zynii path location of array of lookup values
data-zynii-draw name name of template/function that draws a child
data-zynii-sort function one or more sort/filter functions

data-zynii

The data-path attribute holds a path string that identifies the location in the model that holds the value to be linked to this element. The format of path strings is documented in a later section.

Binds a function to the element's onclick event. For example:

  <button data-zynii-click="doIt(@x.y, 'test')" ...

every time the button is clicked the function doIt will be called. It will be passed the value at path location @x.y and the string 'test'. The called function might look like:

  function doIt(a, b) {
      // do something with a and b
      return false;
  } 

Note: inside the function, this refers to the element the handler is attached to.

data-zynii-format

HTML values are always strings and a javascript value can hold any of a number of different types. To ensure data is formatted correctly and also stored in the model in the desired type, you can specify the format. This can be one of:

boolean, string and number ensure that the value saved into the model are of those types. For numeric values you can, instead, use fN or eN. These will display the number either in fixed notation or exponential notation with N being the number of digits after the decimal point.

data-zynii-format is optional. If omitted the data is considered to be a string.

data-zynii-lookup

This attribute is typically used by a <select> element. It provides a path to a lookup array in the model. The array is a list of string values.

  ["option one","option two","option three","option four","option five"]  

data-zynii-draw

Usually simply the name of the template to be used to draw any child entries. Can also be the name of a function to be used instead.

data-zynii-sort

One or more functions that sort/filter the data. The output of each function is fed into the next.

templates

The data-model is likely to include items that have variable numbers of child items. It is useful to be able to generate HTML for these automatically.

Zynii provides the option of specifying a template for the HTML. This template is then used to generate repeating HTML for elements of an array or for properties of an object.

A template is defined in the HTML, using the HTML5 <template> tag. This needs to have a unique name specified in the id attribute.

<template id="drawRow">
  <tr>
    <td data-zynii="@@[0]" data-zynii-format="string"></td>
    <td data-zynii="@@[1]" data-zynii-format="string" contentEditable="true"></td> 
    <td data-zynii="@@[2]" data-zynii-format="number"></td> 
    <td data-zynii="@@[3]" data-zynii-format="number" contentEditable="true"></td>
  </tr> 
</template>  

You can put any HTML you like inside the template element. However, you probably shouldn't nest <template> elements. You can use any of the data-zynii-xxx attributes as in the main HTML. Within these the string @@ is a placeholder for the current path. The generated HTML will have the item's path inserted to replace the @@. The string @@@ acts as a placeholder for the current index/key of the current entry. If the data is in an array this will be a number 0 or higher. If the data is in an object this will be the key/property name.

Note: templates will often be located in the HTML - where the generated HTML will be placed. But this isn't a requirement. Templates can be placed anywhere where they will be found by bind(). They must, however, have a unique id.

UNFORTUNATELY: at the current time, the <template> tag is not fully supported in all browsers (not supported in Safari or IE). Thererfore for most situations the alternative outlined below should be used.

The alternative to using the <template> tag, is to use some other tag but with it's class set to template. However there are a number of limitations imposed by certain browsers.

You can use any tag name for the template's outer element. However it has to appropriate to the child elements you are using. If you are creating a list of li elements your template should be a ul or ol tag. If your content were table rows, it might be best to make the template a <tbody> tag.

However, some browsers will fail if the element is incompatible with the content. If the content of your template is an <li> element, you should use a <ul> or <ol> element for the template. If the content were a row, it might be best to make the template a <tbody> element. For example:

<tbody class="template" id="drawRow">
  <tr>
    <td data-zynii="@@[0]" data-zynii-format="string"></td>
    <td data-zynii="@@[1]" data-zynii-format="string" contentEditable="true"></td> 
    <td data-zynii="@@[2]" data-zynii-format="number"></td> 
    <td data-zynii="@@[3]" data-zynii-format="number" contentEditable="true"></td>
  </tr> 
</tbody>  

Once you have defined a template you need to ensure that it is called when required. This is done using the data-zynii-draw attribute. Like so:

<tbody data-zynii="@x" data-zynii-draw="drawRow">

</tbody> 

In this case, if x was an array, there would be one row in the table for each element in x. If x were an object there would be one row for each child property.

It is also possible to call a javascript function to generate the HTML rather than using a template. Instead of defining a template with a given name, add a function to the app with that name. (see addFunc() function for details.)

This is more complex than using a template but offers more options - i.e. the javascript language. One situation where this might be useful, is if the children of an array/object were not all of the same format. You might then want to generate different HTML depending on the type/structure of each child.

functions

built in functions

z.iif(flag, a, b)
Immediate if. returns a if flag is true else b.
z.alphaSort(a, b, reverse)
Sort function.
z.numericSort(a, b, reverse)
Sort function.
z.sum( args )
Sum (numerically) the passed arguments.

user defined

calculated fields

calculation functions are simple javascript functions. They should take a certain number of arguments and return the calculated result. These functions can then be used in expressions in data-zynii-xxx attributes.

draw functions

A draw function returns HTML. This can be a string, which will be automatically converted, or it can be an actual HTMLElement. Multiple nested HTML can be created, but there should only be a single HTMLElement at the top-level.

  function userDraw(path) {
    return '<label data-zynii-path="' + path + '">A: <input type="text" data-zynii-format="string" /></label>';
  } 

The top-level element should have a data-zynii attribute. You can mark a different element as being the one that holds the value using data-zynii-format or data-zynii-at.

sort functions

A sort function takes a minimum of two arguments and then returns -1,0,1 depending on the result of comparing the first two arguments. This is the same mechanism as used by Javascript's Array.sort() method:

  function customSort(a, b) {
    var result = ....
    return result; // -1, 0, -1
  } 

a and b are the child items being compared. Within the custom sort function this. refers to the parent object.

 
  function sortX(a, b, x, y, z) {
    var result = ....
    return result; // -1, 0, -1
  }  

In this second example x, y, z are the current values of the expressions provided where the sort function is called. For example:

  <ul data-zynii-path="sortX(@x, sum(@a[2],@b[2]), @z)" ... > ...  

filter functions

A filter function takes a single element and returns true if it should be included in the result. It is possible to apply several functions, in which case they are ANDed together. Only values that match all filters are returned.

Filter functions work in a similar way to Array.filter() functions in javascript. Function is called repeatedly for each element in array. The first three arguments are the item it's index in the array and the entire array. Your own arguments follow after those three. The function should then return true if the item is to be included in the results.

  function customFilter(item, index, x, ... user arguments ...) {
    var result = .... // your filter
    return result; //true|false
  } 

event functions

Event functions are of the form:

  function(e [, args ...]) {
    // your code here. 
  }   

The first argument of the function is the event object. This is the same one that is usually passed to an event handler in javascript. Inside the function this. refers to the HTML element to which the handler has been attached. This is the same as for normal javascript development.

One caveat. In Javascript it is possible to attach multiple event handlers to a single event. Zynii does NOT support this. You can only add a single event handler using a zynii expression. Also, for this reason setting an event handler via zynii and also via javascript is likely to lead to unpredictable behaviour.

transform function

ZYNII.transform(html, json); 

html should be an HTMLDocument. json is a javascript data structure. The function performs a transformation of the supplied HTML merging it with the json data. The HTML can contain all the same zynii attributes and templates. However the process is one way, changes to values in the HTML are ignored. It is intended as a way of generating report style HTML pages from the data.

Note: The transformation is actually performed on the supplied HTMLDocument. If you want to repeat the transform, you will need to reload the html or clone it each time before use. (This may change in the future!)

deploying zynii files

A basic installation of zynii consists of the following files:

zynii.js is a single javascript fie containing all of the code required to use zynii in a web application. It has no external dependencies.

debug.html is a web page/application that acts as a debugger for your zynii project. (It is itself a zynii app.) You can link your project to it during development. dump.html is a much simpler debug tool. It simply lists the data used by debug.html.

zynii source

Zynii is built from a set of source files. There are also a number of test files. You should only download these files if you are intending to contribute to Zynii development.

As well as the standard deployment files above, the source includes the following javascript files:

app.js
The app object
model.js
The data model
parser.js
Zynii expression parser
poly.js
polyfills to support IE10 and IE9.
require.js
partial emulation of node's require() function. Enables the source modules to be run in a browser.
transform.js
transform(html, json) function performs a transformation of the supplied HTML merging it with the json data. The HTML can contain all the same zynii attributes and templates. However the process is one way, changes to values in the HTML are ignored. It is intended as a way of generating report style HTML pages from the data.
utils.js
General purpose utilities used in zynii.
zAll.js
Passed to browserify to generate zynii.

These files are typically organised as node modules. They can be loaded and tested using nodeJS. zynii.js is generated by assembling These files are assembled into the final zynii.js using browserify.

Source includes the following test harness files:

app.test.js
-
model.test.js
tests for model
parser.test.js
-
require.test.js
-
transform.test.js
-
utils.test.js
-
miniJasmine.js
A simplified test framework. Similar to jasmine.
app.tester.html
html page to run tests in browser
model.tester.html
html page to run tests in browser
parser.tester.html
html page to run tests in browser
poly.tester.html
html page to run tests in browser
require.tester.html
html page to run tests in browser
transform.tester.html
html page to run tests in browser
utils.tester.html
html page to run tests in browser
testframe.html
container page runs all of the xxx.tester.html tests