by Ryan Webber
The purpose of this tutorial is to demonstrate how an external file can be loaded into Isadora using the Javascript actor so that Isadora can use the data inside it.
We will look at two methods of formatting the data so that it can be read within the Javascript actor in Isadora. The first will be as Javascript code, that is executed when the file is first imported (on enter scene). The second will be as JSON (JavaScript Object Notation), which is a popular data-interchange format. We are introducing JSON in this tutorial because it will be used in up coming Javascript tutorials, and many available data sources provide access to their content in this format.
It is assumed you have basic knowledge regarding the use of Javascript in Isadora. If you do not we recommend you complete the Getting started with Javascript tutorial before proceeding with this tutorial.
This article will cover the usage of the include() function in the Javascript actor. Since the writing of this article another function has been added to the Javascript actor that is similar but works slightly differently. The read() function can be used to read a text based document into the Javascript actor, and provide the contained text as a string inside the Javascript actor.
var MyText = read('textfile.txt');
The above statement will read the text inside the 'textfile.txt' file and put this text into the variable MyText.
Below we will use include to assign multiple values from an external file and more using the include() function.
Start a new Isadora project
To begin, create an empty folder called “jsLoadData ” on your desktop in another convenient location.
Next open Isadora and save your blank Isadora file into the “jsLoadData” folder as Loader.izz.
Note: the file names here do not matter, but since we will use them for reference as we go along, we suggest using the names suggested in this tutorial.
Creating the data file
Now that we have a basic project setup, we can add a data file to the “jsLoadData” directory.
Create a text file and save it as “data.txt” to the project folder.
Under Mac OS X you can use TextEdit to create a text file; just launch TextEdit and choose Make Plain Text from the file menu. Under Windows, you can create a text file in Notepad.
If data.txt is not open, open it. Then add these two simple javascript lines to the file.
var day ="Wednesday"; var distance =300;
These two lines of text each: define a variable, and set its value. The variable 'day' is set to a string value, and the variable 'distance' is set to a numeric value.
It is important that the contents of data.txt be valid Javascript, because when it is loaded into the Javascript actor in Isadora, it is interpreted by the Javascript engine, and executed.
Load data.txt into Isadora
Ensuring the file Loader.izz is still open in Isadora, do the following steps. In Isadora, double-click the Scene Editor to open the popup-toolbox, and type 'javascript' to locate the Javascript actor.
- Click the 'Javascript' item in the list to add the Javascript actor to your empty patch.
- Double click the Javascript actor to open the code editor.
- The first line of the code in the code editor is "function main()". Move your text cursor to the position before the 'f' in 'function' and hit enter to create a blank line at the top of the code.
- Insert include("data.txt"); as the first line of text in the Javascript Editor.
- Click OK on the Javascript Editor.
If everything was entered correctly the Javascript Editor should close. If an error was made you should see an Isadora prompt open with a Javascript Error, listing the line number and error details. The Javascript Editor with remain open until the error is corrected.
Note: Either single or double quotes are allowed around the filename, however; pasting from a text editor may add unsupported characters. Try: changing the file name from “data.txt” to “dataNOT.txt” and click OK to close the Javascript Editor and see the message it produces.
Note: on Mac OS X, you may have trouble including the data file due to the quotes being of a non ASCII type in your data.txt file. If you are using TextEdit you may need to disable smart quotes, in the TextEdit preferences. If your document still raises an error when loading into Isadora, select all, and uncheck the smart quotes setting in the 'Edit -> Substitutions' menu.
The include(); function is very simple, it requires three things.
- the path to a file to load, either an absolute system path or relative path like the one shown in this example.
- it must be the FIRST LINE in the Javascript Editor (no blank lines allowed).
- the file must contain valid/executable Javascript (it can not be empty)
Remember that relative paths are in relation to the Isadora document, in this case, Loader.izz. That’s why we had to save the file before we began, so that the include statement would find our file.
Use the data in Isadora
Now with the file being included we haven't done anything with the data contained in the file. We need to reference the variable names to access them in Javascript.
Change the code in the Javascript Editor as shown below:
include("data.txt"); // load and run external file function main() // required function { // define return value as sum of two values return arguments[0] + distance; }
Click OK, and adjust the value of 'input 1' to 3. Notice the output is 303. This is great, the code has added the value of our 'distance' variable to the value of 'input 1'.
Note: Remember that input values are received in the Javascipt actor via a variable called 'arguments', and that 'arguments' is an array that can contain numerous values. Referencing 'arguments[0]' receives 'input 1'.
Many programming languages start references from zero, unlike people who generally start counting from one. This is can cause some confusion so it is important to remember that argument[0] references 'input 1' and argument[1] will reference 'input 2'.
Now double-click the Javascript actor to open the Javascript Editor again, and edit the code to match the code shown below:
include("data.txt"); // load and run external file function main() // required function { // define return value array with two values return [day,(arguments[0] + distance)]; }
We have changed the return statement. It will now return two values from the Javascript actor. The first value is the string stored in our variable 'day', and the second is the sum of the value from our 'distance' variable and from argument[0].
It can take a little while to get used to the format of the return statement, so lets look at a few samples:
- return 1 – again this will return the single numeric value of 1 as 'output 1'
- return [1] – this will return the single numeric value of 1 as 'output 1' (in this case the '1' is a single entry in an array, and Javascript allows it to be passed as a single output)
- return ["text :)", 3] – formatted much like the code we last added above, this 'return' statement will output a string to 'output 1' and will output the number 3 to 'output 2'
- return [(6 + 3), varName, cubedFunction(6)] – here the 'return' statement will return the result of the operation (6+3) to 'output 1', so the number 9 will be output. Next 'output 2' will be the contents of the variable 'varName' and 'output 3' will run a function 'cubedFunction()' which returns the value that function returns (216 assuming the function works correctly)
Hopefully these examples make the return statement format clear.
Since we changed the number of values being returned by the 'return' statement we need to adjust our Javascript actor to match. To do this, you’ll need to close the Javascript code editor by clicking OK. Change the 'outputs' input to 2.
Adjust the value of 'input 1' and notice that 'output 2' changes to match the sum of the 'input 1' value and the value of our variable 'distance'.
Why does 'output 1' display 0?
The data types required by the Javascript actor’s inputs and outputs are determined by the Javascript code you write. Isadora can’t analyze that code to automatically determine if an input or output should be a number or a string.
But all of the inputs and outputs of the Javascript actor are mutable, meaning they will dynamically change their type when you connect them to another actor. (Note that the dot at the input or output port is green instead of blue; this indicates that the property is mutable.)
So, to get ‘output 1’ to display the text output, all we need to do is to connect it to another actor that receives text as an input.
Add a Trigger Text actor to your patch.
Connect 'output 1' of the Javascript actor to the 'input' of the Trigger Text actor.
Once again adjust the value of 'input 1" of the Javascript actor to trigger your Javascript code to be executed again.
We now see the string value of our 'day' variable being output to the Trigger Text actor.
Note: it is easy to forget to mutate the outputs of our Javascript actors to allow your different data types. Try to define your 'return' data, and mutate the outputs when you start developing your code to avoid confusion.
This is a very simple example using theinclude() function to load external data, yet it does open the doors to many create possibilities.
For example, what if you need to update several text strings deep within multiple User Actors every time you run a show? Building your show file with this in mind, and loading all your text from an external file may save you hours.
Another example: perhaps you have used Isadora's TCP actors scattered throughout your show file, and you occasionally need to set new IP addresses in all these actors? Specifying those IP numbers using the Javascript actor and an external data file could be a big time saver.
Do it again with JSON
Since we have already created a project folder, we will continue using the same folder and Isadora file.
Create a new plain text file in the project folder “'jsLoadData” called “JSON.txt”.
Go back to your Isadora file Loader.izz, duplicate the scene we have been working on (right click the scene in the scene list and select 'Duplicate'.)
Open the new scene. (Make sure to save your file; you should be doing this regularly.)
We are going to use the sample JSON data available at: http://www.w3schools.com/json/
{"employees":[ {"firstName":"John", "lastName":"Doe"}, {"firstName":"Anna", "lastName":"Smith"}, {"firstName":"Peter", "lastName":"Jones"} ]}
Open the new “JSON.txt” in your favorite text editor, and add the sample JSON data from above. Notice that the array is fully enclosed in curly brackets. This is standard JavaScript Object Notation.
There is an object called “employee” that is an array of three items. Each item in that array has two named values, “firstName” and “lastName.” This demonstrates how JSON pairs property names with values (often referred to as name value pairs). This allows you to access a property’s value by its name.
For more on JSON and how it works, here’s a great page to introduce you to the basics.
However, the structure above does not fulfill one of the requirements of theinclude() function: it will not execute as Javascript when imported. We need to add a little to the beginning of the text before this will be interpreted by Isadora's Javascript engine as executable code.
Edit the “JSON.txt” file, adding var data = before the first bracket as shown below:
var data = {"employees":[ {"firstName":"John", "lastName":"Doe"}, {"firstName":"Anna", "lastName":"Smith"}, {"firstName":"Peter", "lastName":"Jones"} ]};
We have now assigned a variable to hold the Object (read more about Javascript Object Notation) and the statement is now executable by the engine.
Try: including the JSON.txt file with only that original JSON data, and see the error given by Isadora.
Why JSON?
JSON is very common. You may export JSON from a Google Speadsheet, (or many other data sources), copy it from a website, or load it dynamically. The options for creating JSON formatted data are many, so it is one of the most versatile data formats currently in use.
We have the data formatted, so will the code differ now that we are using JSON?
In this case it really won't be all that different. We will output a single value from our 'return' statement so we should change the number of outputs of our Javascript actor from 2 to 1. If nothing else was changed, we should still have one output connected to the text 'input' of the Trigger Text actor. This is perfect since all the data in our JSON sample happens to be text.
Update the code in the Javascript actor to:
include('JSON.txt'); // load and run external file function main() // required function { // define return value array with two values return [data.employees[1].firstName]; }
Change the value of 'input 1' to execute your Javascript code (the Javascript actor executes the internal javascript code anytime an input value is received). You should see 'Anna' at the 'output 1'.
The value returned by this function is specified by the following statement [data.employees[1].firstName] as follows:
- The variable 'data' contains our JSON data, which is considered an object in Javascript, and thus allows reference by dot notation.
- The first and only top-level variable defined in our JSON is a name/value pair called 'employees' which contains an array. After ‘data’, we use dot notation to reference the 'employee' variable and then array notation '[1]' to select the second entry from that array. (Without the array notation the entire array is selected.)
- The entry selected by data.employees[1] is a complete Javascript object because it starts with "{" and ends with "}", just like the main JSON object called ‘employees.’. We select 'firstName' from within this object by again using dot notation.
So, the statement [data.employees[1].firstName] gives us the value "Anna". (Remember we accessed the second array element with [1].)
If you would like more information regarding how to reference data in JSON format, read more about working with objects.
Parse JSON from a string
Now that we have our JSON data loaded into Isadora, and know how to reference data using the JSON format, we should look at one additional thing.
Often you will receive JSON data as a string (text). One common source for this type of text formatted data in Isadora is from a web API, received by the 'TCP In Watcher - Text' actor. In Isadora JSON data will be received as text if received from most of the available options:
- OSC
- TCP-IP
- As a text string from another Isadora actor.
How do we handle the data if it is in text form?
The answer is incredibly simple. It adds only one line of code. To test it we will wrap our JSON in “JSON.txt” with quotes so that the entire block is read as a single string. It should look like this:
var data = '{"employees":[{"firstName":"John","lastName":"Doe"},' +'{"firstName":"Anna","lastName":"Smith"},{"firstName":"Peter","lastName":"Jones"}]}';
Note: we need to use a differnet style of quotes to enclose the data since the data string itself includes quotes. Another option might be to escape all the quotes, but this is not as easy to maintain or read. More on Javascript strings.
Note: we have used the '+' operator to allow us to break this string into multiple line for display on this screen. It is not required. You can learn more about its use.
The extra set of single quotes around the outer "{}" set ensures the 'data' variable saves the text as a string. Notice that we have removed the line breaks since Javascript can't parse a string across multiple lines with out special characters being added to the string. The code should appear as a single long line of text in your text editor. We have also added a ";" (semicolon) to the end the statement. (Javascript is forgiving about the use of semicolons in many cases, but it is best practice to end every statement with one)
Make sure “JSON.txt” is saved with these updates. Open the Javascript Editor and close it again to force the include() to execute. If a Javascript error prompt opens, there is an error with the “JSON.txt” text.
If Isadora does not report any errors, the 'include' is parsing the text correctly.
At this point we have a variable named 'data' that equals a long string (our JSON data). We can't access the data within the string easily, and our 'return' statement will not retrieve any data.
We will add one line of code after the 'include("JSON.txt");' to correct this.
// convert data string to javascript objects data = JSON.parse(data)
Here we set our variable 'data' to the parsed version of the 'data' variable (over writting itself). To ensure that our data output changes, and that we’ll see the results, update the return statement to reference 'employees[2]'
Close the Javascript Editor. Save your Isadora file.
Change the value of 'input 1' of the Javascript actor again and we should see a new name is output.
Javascript and JSON are extremely powerful, but are also necessarily complex. We have covered a lot in this tutorial. If it is still a little confusing, please visit the links we’ve provided above for further examples. The more you use Javascript and JSON, the more comfortable you’ll get. So practice, practice, practice!
With what we have learned we can start to explore an additional layer of modularity in our Isadora patches: the freedom to make changes without editing the Isadora file, the ability to use structured data sets, and the option to pass data within Isadora as structured strings.