As described in the Data tree chapter, data in the dizmoViewer is stored and exchanged in 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. dizmoViewer also provides 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.
To select with which main branch of the data tree you want to interact, you have to use the correct basic object ("/viewer", "/dizmos" or "/bundles").
Within each of these subtrees, there are three subtrees ("attributes", "public", "private"). You will have to decide which subtree that you want to access.
When reading and writing properties you may use any valid JavaScript data item. For example, 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, use 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 can be a JavaScript object providing flags to control the detailed behavior and result of the function called.
Here are the five options that are 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 |
fallback | Retrieve something else in case of a non-existing property |
file:true | Store files in the data tree |
timeout:5000 | Time limit in ms for the retrieval of files stored in the data tree |
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.
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 Storage for more details.
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 that you are passing a valid JSON object to the method and that you 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 you want to store a string that should not be touched, you may use the option string:true
with getProperty and setProperty to store it in the data tree.
However, always use the json:true
in the public sections of the data tree to ensure compatability between all dizmos, and compatability with external systems. string:true
should only be used in the private parts of the data tree.
If you do not specify any option, dizmoViewer by default assumes { value:true, json:true }
and to be used for the options
parameter.
setAttribute and getAttribute always take and deliver JSON objects.
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
).
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 returns the value that is 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 is deserialized using JSON.parse. On the other hand, if the string:true
flag is set, the data returned from the node is passed back as a string without any conversion. If you omit json:true
and string:true
, json:true
is used instead.
Examples:
var user = dizmo.privateStorage.getProperty("login/username");
var nodeArr = dizmo.privateStorage.getProperty("login", {nodes:true});
The variable user contains the value that was saved into the node "login/username". If the node was not written to previously, it contains the value undefined
.
The second variable, nodeArr, contains an array with the names of the subnodes of "login". These subnodes should have been created before. If no subnodes have been created yet, it contains an empty array.
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 returns "bar".
One can still not write undefined, but instead has to use the provided calldizmo.privateStorage.deleteProperty('foo'); to get rid of a property.
The function setProperty
saves the data item that is provided to the location in the data tree as specified by the path.
setProperty(path, value[, options]);
The default flag in the setProperty method is value:true
. It is also the only flag that may be used in the method. Therefore, it may as well be omitted. To create a new node, use setProperty to set any value (e.g. null
).
If the json:true
flag is set, the data written to the node is serialized, using JSON.stringify. On the other hand, if the string:true
flag is set, you have to pass a string that is 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 is stored as "no".
Paths / Node names in the data tree are case insensitive. Therefore, filenames are translated into lowercase when storing files in the data tree. Thus, filenames are treated as case insensitive. This is important when files are transferred between OS: and some OS:s have case-insensitive filenames. To avoid related problems, filenames have to be handled as case insensitive everywhere.
The order of child nodes in the data tree is undefined. That is, loading the data tree of a dizmo when booting , via dizmoLive, or when loading a setup does not guarantee a specific order of children nodes in the data tree. If the order is essential, it needs to be implemented using an index into the list of children.
The function deleteProperty
allows removing 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 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.
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. The origin of change can be from any other dizmo, by the user or by dizmoViewer. This feature 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, that is, 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. Then, your callback function will be called if the value of the node, or the value 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 is 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 is passed back to the callback function as a string without any conversion. If you omit json:true
and string:true
, then 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",
myCallbackNodes,
{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}
);
Please make sure you unsubscribe from any subscriptions your dizmo does not need anymore. dizmoViewer continues to call the specified callback function every time a node changes its value, which can produce unnecessary load on the overall system.
It is also possible to unsubscribe from a node in a callback function that was triggered based on a previously created subscription.
All subscriptions are asynchronous. Thus, when a subscription is called and immediately after the value is changed, it a subscription is not triggered. An additional (optional) callback exists 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');
});
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.
If a node to which you subscribed is deleted, the subscription is removed as well. There is still a callback, with the value first set to null
, indicating it is about to be removed.
To cancel a subscription installed before, use the unsubscribeProperty
function:
unsubscribeProperty(subscriptionId);
Above, subscriptionId
is the identifier that returned when you subscribed to the specific node.
Example:
To cancel the subscription made above, you would use
dizmo.publicStorage.unsubscribeProperty(subscrId);
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 are called again 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
.
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. The function notifies 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}
);
The code above creates a console output whenever the value of one of the nodes under the "login" node changes. It 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.
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 unique random identifier, as in the example above.
The value
parameter of the setProperty call when storing files is a 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 was stored somewhere before.
When using file:true
option, dizmoViewer tries 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 is an URI pointing to the persistent storage location of the file.
The timeout
option is the time in milliseconds until an exception is thrown if the file could not be saved until then. Its default value is 5000 ms.
setProperty
is a synchronous call when used with the default timeout. For example, if you want to store a video file that is located on a web-server, you 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.
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 frequently used data items when writing a dizmo. TThus, they have gotten 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))
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, 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. Thus, there is no need for an explicit distinction between accessing values
and nodes
. If you read from a node that has subnodes (for example "geometry") you always get the subnodes. If you read from a node that contains a value on the other hand (for example "geometry/x") you 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 contain an object with the names and values of the subnodes.
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 Viewer class.
As described above in "Subscribing to property changes", you can also subscribe to attribute changes.
To get a notification with 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 are notified if this single value changes. If, on the other hand, you subscribe to changes of an node that has subnodes, your callback is 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
);
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.
All subscriptions are asynchronous. Therefore, when a subscription is called and immediately after the value is changed, it does not trigger the subscription. An additional (optional) callback bridges 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');
});
Here too, dizmoViewer does not run your callback function if the attribute did not change because the new value is identical to the old one.
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
);
In this example, too, we separate the callback function for the sake of readability.
As with subscribeToProperty
and subscribeToAttribute
, dizmoViewer does not run your callback function if the attribute did not change.
To cancel a subscription installed before, call:
unsubscribeAttribute(subscriptionId);
Where subscriptionId
is the identifier that 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, but have a boolean property of that name. If it is set to true, subscriptions on it are allowed. If it is set to false, an error will be thrown when a user tries to subscribe to it.
The list below is based the list on mutability during a dizmo's run time (and the dizmoViewer runtime). For example, the attribute product/compiledate on the viewer never changes during a dizmo's runtime. Neither will the attribute created on a dizmo or the attribute dizmojsversion on a bundle.
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]);
To be notified whenever a new dizmo is removed from dizmoViewer, use viewer.onDizmoRemoved
to install a callback function that is executed when this happens. This function returns an subscription id that need later to unsubscribe.
Example:
var subscrId = viewer.onDizmoRemoved(myCallback[, SubscriptionRegisteredCallback]);
Use viewer.unsubscribeDizmoChanges
to unsubscribe your callback when you do no longer want to receive notifications about changes in the list of dizmos.
Example:
viewer.unsubscribeDizmoChanges(subscrId);
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 be notified whenever a new dizmo is removed from dizmoViewer, use viewer.onBundleRemoved
to install a callback function that is executed when installation occurs. This function returns a subscription id that you need later to unsubscribe.
Example:
var subscrId =viewer.onBundleRemoved(myCallback[, SubscriptionRegisteredCallback]);
Use viewer.unsubscribeBundleChanges
to unsubscribe your callback when you do no longer want to be notified about changes in the list of dizmos.
Example:
viewer.unsubscribeBundleChanges(subscrId);