Data Persistence

As described in the Data tree chapter, the common place to store and exchange data in dizmoViewer is the data tree. In order to access data in this tree dizmoViewer provides a few objects as well as a set of access methods to read, write and remove data nodes as well as methods to get a callback whenever a particular data item changes.

The three objects that allow you to interact with dizmoViewer are pre-instantiated objects (singletons) provided by the system for every dizmo instance.

  • viewer
    • global viewer class
    • the same for all dizmos
  • dizmo
    • the current instance of the bundle
    • storage here is tied to the current instance of the dizmo
  • bundle
    • the bundle this instance belongs to
    • storage here is shared with all instances of the bundle

Accessing properties

In order to access data items in the data tree it is important to use the correct basic object to select with which main branch of the data tree you want to interact with ("/viewer", "/dizmos" or "/bundles"). Within each of these subtrees you will have to decide which of the three subtrees provided for each object ("attributes", "public", "private") you want to access.

When reading and writing properties you may use any valid JavaScript data item like a number, a string, a boolean (true/false), an array or even an object.

To access nodes in the "public" subtree of an object use the publicStorage field in combination with the access method or the privateStorage field to access nodes in "private" data tree of the object.

To give you an idea of how this looks, here are few examples on how you would access properties (data items) in the private or public part of the data subtree of the three main objects:

var pw = viewer.privateStorage.getProperty("login/password");

dizmo.publicStorage.setProperty("stdout/topNumbers", [25, 3.14, 9, 7.222]);

bundle.privateStorage.deleteProperty("oldItem");

All property access functions (except deleteProperty) accept an optional options item as their last parameter. If specified, this is a JavaScript object providing flags to control the detailed behavior and result of the function called. Here are the five options currently available:

Option Effect
value:true Access the value(s) of the specified node
nodes:true Access the child nodes of the specified node
recursive:true Access recursively the entire subtree under the specified node
json:true (De)serialize the data passed using JSON.stringify and JSON.parse
string:true Store / retrieve the data passed as a string without touching it

Note: The two options value:true and nodes:true are currently considered to be mutually exclusive. You can only set one or the other to true.

Note: Not all options make sense for every access method. Have a look at the detailed description of each function.

Please also refer to the API documentation of dizmojs.Storage for more details.

Data Format

There are two options json:true and string:true that allow you to specify what kind of data you want to store in a property node.

Using json:true as an option to setProperty means you are passing a valid JSON object to the method and will want to retrieve it as a JSON object later using json:true with getProperty dizmoViewer will assume the data stored in the node has been stored there using the json:true option.

Although JavaScript and JSON are often considered to be fully compatible (i.e. bijective) please note that JSON does not support all native JavaScript datatypes:

«JavaScript syntax defines several native data types that are not included in the JSON standard: Date, Error, Regular Expression, Function, and undefined. These JavaScript data types must be represented by some other data format, with the programs on both ends agreeing on how to convert between the types.»

For more details on this please have a look at the Wikipedia article on JSON.

If for some reason you want to store a string that will not be touched, to store it in the data tree you may use the option string:true with getProperty and setProperty.

Note: To ensure compatibility in the public parts of the data tree between all dizmos and in the future also with external systems accessing parts of the data tree, please always use the json:true in the public sections of the data tree. string:true should therefore be used in the private parts of the data tree only!

Note: If you do not specify any option, dizmoViewer by default assumes { value:true, json:true } and to be used for the options parameter.

Note: setAttribute and getAttribute always take and deliver JSON objects.

Note: json:true and string:true are mutually exclusive. You can only set one or the other to true. These options only make sense when accessing values (value:true).

Reading properties

The function getProperty loads property data from the location in the data tree as specified by the path.

getProperty(path [, options]);

The nodes:true flag may be used in the getProperty function to get an array of the names of the children nodes of the specified node.

The value:true flag will return the value that was written into the specified node. If you omit value:true and node:true, value:true will be used.

If the json:true flag is set, the data returned from the node will be deserialized using JSON.parse. On the other hand if the string:true flag is set, the data returned from the node will be passed back as a string without any conversion. If you omit json:true and string:true, json:true will be used.

Examples:

var user = dizmo.privateStorage.getProperty("login/username");

var nodeArr = dizmo.privateStorage.getProperty("login", {nodes:true});

The variable user will contain the value that was saved into the node "login/username" or undefined, if this node was not written to previously. The second variable nodeArr will contain an array with the names of the subnodes of "login" that have been created before or an empty array if no subnodes have been created yet.

Note: new fallback option

Up to dizmoJS API 1.2, if you requested a non-existing property from storage, e.g dizmo.publicStorage.getProperty("foo"), it would return null. Starting with dizmoJS API 1.3, this has changed to undefined. The best way to support both APIs in your dizmo is to check a non-value with the following call

if (!(dizmo.publicStorage.getProperty("foo")) { 
    // initialize property }

If you want to return something else for a non-existing property, you can change it by providing a fallback parameter to the getProperty call:

dizmo.privateStorage.getProperty('foo', {fallback: 'bar'});

now if "foo" is currently not defined, it will return "bar".

One can still not write undefined, but rather has to use the provided calldizmo.privateStorage.deleteProperty(‘foo’); to get rid of a property.

Writing properties

The function setProperty saves the data item provided to the location in the data tree as specified by the path.

setProperty(path, value[, options]);

The value:true flag is currently the only one that may be used in the setProperty method. As this is the default anyway, it may as well be omitted. To create a new node just set any value (e.g. null) to it using setProperty.

If the json:true flag is set, the data written to the node will be serialized using JSON.stringify. On the other hand if the string:true flag is set, you have to pass a string that will be written any conversion unchanged. If you omit json:true and string:true, json:true will be used.

Examples:

dizmo.privateStorage.setProperty("login/keep", false);

dizmo.privateStorage.setProperty("login/keep", false, {value:true});

The result of these two calls is identical as the options parameter’s default value is {value:true}.

dizmo.privateStorage.setProperty("login/keep", "no");

dizmo.privateStorage.setProperty("login/keep", "no", {string:true});

The result of these two calls is not identical. In the first case the string passed is serialized using JSON.stringify (resulting in ""no"") while in the second example the string will just be stored as "no".

Note: case insensivity

Paths / Node names in the data tree are case insensitive. Therefore filenames (when storing files in the data tree) are also translated into lowercase. This also means that filenames will be handled in case insensitive. This is important for when files are transferred between OSs and some of them have case-insensitive filenames. To avoid this, filenames have to be handled in case insensitive everywhere.

Note: ordering of child nodes

The order of child nodes in the data tree is undefined, i.e. loading the data tree of a dizmo (when booting , via dizmoLive or when loading a setup) does not guarantee a certain order of children nodes in the data tree. If the order is important it needs to be implemented using an index into the list of children.

Deleting a property

The function deleteProperty allows to remove a node from the data tree.

Note: It is important to note that removing a node from the data tree will not only remove the node and its value but also the entire subtree attached beneath it.

deleteProperty(path);

Unlike other access functions deleteProperty does not have an options parameter as it always removes entire nodes including values and all descendants including their values.

Example:

dizmo.privateStorage.deleteProperty("login/password");

dizmo.privateStorage.deleteProperty("login");

While the first example just removes the "login/password" item in the private data tree of the dizmo object, the second removes the "login" node as well as all its children including all the values.

Subscribing to property changes

A key feature of the dizmo data tree is its capability to call back some of your code, whenever a particular property is about to be changed from any part of the code within your own dizmo, from any other dizmo, by the user or by dizmoViewer. This allows you to structure your code in unique ways and to easily communicate between dizmos without the need to poll for changes of data.

To get a notification by means of a callback whenever a property is changed, use the subscribeToProperty function. To stop a notification use the unsubscribe function with the subscriptionId you got when subscribing.

subscribeToProperty(path, callback[, options][, SubscriptionRegisteredCallback]);

The value:true flag may be used as an option of the subscribeToProperty function to get a callback whenever the value stored in this particular node is about to change. Please note that in this case you can also leave away this parameter as value:true is the default value of the options parameter.

Alternatively, the nodes:true flag may be used as an option of the subscribeToProperty function to get a callback whenever there is a change in the children nodes of the specified node, i.e. if a child node is added or removed from the list of subnodes. The call returns the current list of subnodes and the prior list of subnodes. From the difference between the two you can work out the added or removed nodes.

When listening to changes with the value:true option the recursive:true flag may be used as an additional option. In this case your callback function will be called if the value of the node or of any of its subnodes changes at any level below the node specified by path.

If the json:true flag is set, the data returned from the node will be deserialized using JSON.parse before passing it to the callback function. On the other hand if the string:true flag is set, the data returned from the node will be passed back to the callback function as a string without any conversion. If you omit json:true and string:true, json:true will be used.

Examples:

    function myCallbackVal (path, newVal, oldVal) {
        console.log("The value of node " + path + " is changing from " +
        oldVal + " to " + newVal);
    }

    var subscrId = dizmo.publicStorage.subscribeToProperty(
        "login",
        myCallbackVal
    );

    function myCallbackNodes (path, newNodes, oldNodes) {
        console.log("Node " + path);
        
        for (var i = 0; i < newNodes.length; i++) {
            console.log(newNodes[i]);
        }
    }

    var subscrId = dizmo.publicStorage.subscribeToProperty(
        "login",
        myCallbackVal,
        {nodes:true}
    );
    
    function myCallbackRecursiveVal (chArr) {
        for (var i = 0; i < chArr.length; i++) {
            console.log("The value of node "+chArr[i].path+" is changing from "+chArr[i].oldVal+" to "+chArr[i].val);
        }
    }
    
    var subscrId = dizmo.publicStorage.subscribeToProperty(
        "login",
        myCallbackRecursiveVal,
        {recursive:true,value:true}
    );

Note: Please make sure you unsubscribe any subscription your dizmo does not need anymore as dizmoViewer will continue to call the callback function specified everytime a node changes its value. This can produce unnecessary load on the overall system.

Note: Unsubscribing from a node is also possible in the callback function that got triggered based an subscription created before.

Note: All subscriptions are asynchronous. This means that when a subscription is called and immediately after the value is changed, it will not trigger the subscription. An additional (optional) callback was added to bridge that gap. It is called the moment the subscription is registered and ready to be fired. Place your code to change the value inside this callback to ensure that the subscription is triggered.

dizmo.privateStorage.subscribeToProperty('foo', function (p, v, of) { ... }, function () { // it is assured that the subscription is triggered
dizmo.privateStorage.setProperty('foo','bar'); 
});

Note: DizmoViewer is smart enough to not run your callback function if the property did not change because the new value is identical to the old one.

Note: If a node to which you subscribed is deleted, the subscription will be removed as well. There is still a callback, setting the value to null first, indicating it is about to be removed.

Unsubscribing from property changes

To cancel a subscription installed before, use the unsubscribeProperty function

    unsubscribeProperty(subscriptionId);

where subscriptionId is the identifier that was returned when you subscribed to a specific node.

Example:

To cancel the subscription made above, you would use

dizmo.publicStorage.unsubscribeProperty(subscrId);

Grouping property changes

If you don’t want to receive one callback for each property change, you can group them using beginUpdate and endUpdate calls.

dizmo.publicStorage.beginUpdate("login");

After this call, no subscriptions to the tree login will be called until you call

dizmo.publicStorage.endUpdate("login");

At that point, you’ll get one callback, containing an array with all the changes that happened between beginUpdate and endUpdate.

Organizing data hierarchically

The example below shows how you might want to organize some properties and get a notification if one of them is changed. In order to do this, organize your data in a groups, i.e. as subnodes of a common parent node:

dizmo.privateStorage.setProperty("login/username", "jdev");
dizmo.privateStorage.setProperty("login/password", "dorwssap");
dizmo.privateStorage.setProperty("login/keep", true);

Now use the function subscribeToProperty with the recursive:true and value:true options. This will notify your callback function, whenever one of the items in the group changes.

var subscriptId = dizmo.privateStorage.subscribeToProperty(
    "login",
    function(chArr) {
         console.log(chArr[0].path, ' has changed from ', chArr[0].oldVal, " to ", chArr[0].val);
    },
    {recursive:true,value:true}
);

This will create a console output whenever the value of one of the nodes under the "login" node changes. This even works if you dynamically add more subnodes to the "login" tree node later.

Note: Data trees can have as many levels and nodes as you need. You may also have multiple subscriptions to cover various parts of a data tree.

Storing files

To store files in dizmoViewer (e.g. an image in the Slideshow dizmo) use the setProperty function and set options to file:true. You may also specify an optional timeout value for the call in options.

dizmo.privateStorage.setProperty("images/t0hNX4gTK2", "http://www.mydomain.com/image.png", {file:true, timeout:1000});

Make sure the property name is unique for each file. One way to do that might be to create a random unique identifier, as in the example above.

The valueparameter of the setProperty call when storing files is an URI pointing to the source location of the file. The protocols currently supported to specify the source of the data are "http://", "https://" "file://", "data://" and "blob://". The last two allow you to save binary data that might have been produced locally instead of using data coming from a file that has been stored somewhere before.

When using file:true option, dizmoViewer will try to load the file from its source location and store it to the data tree. When using the getProperty function on such a tree node, the return value will be an URI pointing to the persistent storage location of the file.

The timeout option is the time in milliseconds until an exception will be thrown if the file could not be saved until then. Its default value is 5000ms.

setProperty is a synchronous call when used with the default timeout. If you want to store a video file that is located on a webserver for example, you will probably want this call to be executed asynchronously. In this case set the timeout value to 0 to request an asynchronous execution. Use the subscribeToProperty function to get a notification callback when saving the file in the data tree has been finished.

Accessing attributes

The attribute subtrees in the three main objects ("viewer", "dizmo" and "bundle") are an efficient and simple way to communicate with dizmoViewer as well as get and set various values and parameters with just a few access methods.

Data items in the "attributes" subtrees of the three main objects are somewhat different from general properties covered above. Their names and content are pre-defined as they are provided and maintained by dizmoViewer.

Also, attributes are data items that are used frequently when writing a dizmo. This is why they have got their own access methods which are even more compact than those for properties in the "public" and "private" subtrees. Here are a few examples of how to access data items in the "attributes" subtrees of the three main objects.

var dizmoVersion = bundle.getAttribute("version");

dizmo.setAttribute("settings/title", "My dizmo Title");

var subId = viewer.subscribeToAttribute("geometry/angle", dSpaceRotated(changedValues))

Reading attributes

The function getAttribute loads attribute data from a location in the attributes subtree as specified by the path.

getAttribute(path);

Please note that getAttribute does not have an options parameter. This is because the "attributes" subtree is static by nature. Furthermore, unlike nodes in the "private" and "public" subtrees, attribute nodes may contain a value or have subnodes instead but never both at the same time. This is why there is no need for an explicit distinction between accessing values and nodes. If you read from a node that has subnodes (like "geometry" for example) you will always get the subnodes. If you read from a node that contains a value on the other hand (like "geometry/x" for example) you will always get the value stored in this node.

Examples:

 var x = viewer.getAttribute("geometry/x");
     
 var nodes = dizmo.getAttribute("settings");

In the second example nodes will contain an object with the names and values of the subnodes.

Writing attributes

The function setAttribute writes the data provided to the location in the attribute specified by the path.

setAttribute(path);

For the same reason as given above, the setAttribute method does not have an options parameter.

For a complete list of the available viewer attributes, checkout the API documentation of the dizmojs.Viewer class.

Subscribing to attribute changes

As described above in "Subscribing to property changes", you can also subscribe to attribute changes.

To get a notification by means of a callback whenever an attribute is changed, use the subscribeToAttribute function. To stop a notification use the unsubscribeAttribute function with the subscriptionId you got when subscribing.

subscribeToAttribute(path, callback[, SubscriptionRegisteredCallback]);

Similar to the getAttributes method subscribeToAttribute does not have an options parameter. So if you subscribe to a node that contains a value, you will be notified if this single value changes. If, on the other hand you subscribe to changes of an node that has subnodes, your callback will be executed whenever one of the values of one of these subnodes changes. This is equal to the recursive option when subscribing to property values.

Example:

function myCallbackVal (path, newVal, oldVal) {
    console.log("The value of node " + path + " is changing from " +
    oldVal + " to " + newVal);
}

var subscrId = dizmo.subscribeToAttribute(
    "geometry/x",
    myCallbackVal
);

Note: Again, we are separating the callback function in the example above for the sake of readability. Of course you may use an anonymous function in the subscribeToAttribute call directly.

Note: All subscriptions are asynchronous. This means that when a subscription is called and immediately after the value is changed, it will not trigger the subscription. An additional (optional) callback was added to bridge that gap. It is called the moment the subscription is registered and ready to be fired. Place your code to change the value inside this callback to ensure that the subscription is triggered.

dizmo.subscribeToAttribute('foo', function (p, v, of) { ... }, function () { // it is assured that the subscription is triggered
dizmo.setAttribute('foo','bar'); 
});

Note: Here too, dizmoViewer will not run your callback function if the attribute did not change because the new value is identical to the old one.

Subscribing to attribute changes with a condition

To get a notification with a callback only when a condition is met, use the function `subscribeToAttributeConditional.

subscribeToAttributeConditional(attributeName, condition, callback[, SubscriptionRegisteredCallback]);

Example:

function myCallbackVal (path, newVal, oldVal) {
    console.log("The value of node " + path + " is changing from " +
    oldVal + " to " + newVal);
}

var subscrId = dizmo.subscribeToAttributeConditional(
    'state/front',
    true,
    myCallbackVal
);

Note: In this example as well, we separate the callback function for the sake of readability.

Note: As with in subscribeToProperty and subscribeToAttribute, dizmoViewer will not run your callback function if the attribute did not change.

Unsubscribing from attribute changes

To cancel a subscription installed before, just call

unsubscribeAttribute(subscriptionId);

where subscriptionId is the identifier that was returned when you subscribed to a specific node.

Example:

To cancel the subscription made above, you would use

dizmo.unsubscribeAttribute(subscrId);

Note: Some of the attributes are not subscribable, and have a property of that name, which is a boolean. If it is set to true, subscriptions on it will be allowed, if it is set to false, an error is being thrown when a user tries to subscribe to it. The list below is based the list on mutabilty during a dizmo’s run time (and the dizmoViewer runtime). For example the attribute product/compiledate on the viewer will never change during a dizmo’s runtime. Neither will the attribute created on a dizmo or the attribute dizmojsversion on a bundle.

viewer geometry height width settings username system dizmoallocatedmemory dizmousedmemory freesystemmemory opengl openglversion operatingsystem systemarchitecture systemmemory product compiledate installeddizmojsversions installedelementsversions installedthemes libraries treeversion

Using the list of dizmos

As mentioned before there is a "/dizmos" node that holds the dizmoIds of all the dizmos currently instantiated in dizmoViewer. As these Ids are only partially helpful and as this node cannot be accessed directly using getProperty or getAttribute, there are three additional calls in the API that help you to deal with the list of dizmos that are around in dizmoViewer.

Use viewer.getDizmos to get an array of the dizmo objects that are currently available in dizmoViewer. Other than the dizmoIds stored in the data tree, these objects give you full access (including all the methods of a dizmo) to the dizmo objects returned.

Example:

var dizmoObjs = viewer.getDizmos();
dizmoObjs[0].setAttribute("settings/framecolor", "#aa0000ff");

In order to get notified whenever a new dizmo is created in dizmoViewer, use viewer.onDizmoAdded to install a callback function that should be executed when this happens. This function returns an subscription id that you will need later to unsubscribe.

Example:

var subscrId = viewer.onDizmoAdded(myCallback[, SubscriptionRegisteredCallback]);

In order to get notified whenever a new dizmo is removed from dizmoViewer, use viewer.onDizmoRemoved to install a callback function that should be executed when this happens. This function returns an subscription id that you will need later to unsubscribe.

Example:

var subscrId = viewer.onDizmoRemoved(myCallback[, SubscriptionRegisteredCallback]);

Use viewer.unsubscribeDizmoChangesto unsubscribe your callback when you do not longer want to be notified about changes in the list of dizmos.

Example:

viewer.unsubscribeDizmoChanges(subscrId);

Using the list of bundles

As mentioned before there is a "/bundles" node that holds the bundleIds of all the bundles currently installed in dizmoViewer. As these Ids are only partially helpful and as this node cannot be accessed directly using getProperty or getAttribute, there are three additional calls in the API that help you to deal with the list of bundles that are available in dizmoViewer.

Use viewer.getBundles to get an array of the bundle objects that are currently available in dizmoViewer. Other than the bundleIds stored in the data tree, these objects give you full access (including all the methods of a bundle) to the bundle objects returned.

Example:

var bundleObjs = viewer.getBundles();
var category = bundleObjs[0].getAttribute("category");

To get notified whenever a new bundle is installed in dizmoViewer, use viewer.onBundleAdded to install a callback function that should be executed when this happens. This function returns an subscription id that you will need later to unsubscribe.

Example:

var subscrId = viewer.onBundleAdded(myCallback[, SubscriptionRegisteredCallback]);

To get notified whenever a new dizmo is removed from dizmoViewer, use viewer.onBundleRemoved to install a callback function that should be executed when this happens.This function returns an subscription id that you will need later to unsubscribe.

Example:

var subscrId =viewer.onBundleRemoved(myCallback[, SubscriptionRegisteredCallback]);

Use viewer.unsubscribeBundleChangesto unsubscribe your callback when you do not longer want to be notified about changes in the list of dizmos.

Example:

viewer.unsubscribeBundleChanges(subscrId);