Home

Notebook

Notebook was inspired by Mathematica's and Jupyter's notebook interfaces. It is a light-weight way to add executable JavaScript cells to a document written in Markdown or HTML.

In the default styling Notebook supports black File and blue Run cells and a green Result cell.

File cells

Black File cells represent editable, constant, uniquely named strings containing lines of text.

    <textarea class='nb-file' id='a'> string content </textarea>

File cells are implemented as uniquely named, editable (and selectable) text areas with black borders and a light gray label:

Leading and trailing white space is removed. The content is accessible using the load() function in a Run cell.

class='nb-file' and a unique id attribute are required; other attributes such as rows or readonly can be added.

Run cells

Blue Run cells represent editable, executable code snippets.

    <input type='text' class='nb-run' value=' JavaScript code '></input>
    <textarea class='nb-run'> JavaScript code </textarea>

Run cells are implemented as editable (and selectable) input areas with blue borders and a light blue run label:

Leading and trailing white space is removed.

class='nb-run' is required; other attributes such as rows can be added.

Once the run label is clicked the cell content is executed by a JavaScript interpreter. Output lines produced by calling the print() function or a line with a computed value if any are appended to a Result cell.

input elements are intended for one line of input. In this case pressing the return key is equivalent to clicking run.

The attribute data-result-rows can be specified for a Run cell to define the number of rows for the Result cell.

The attribute data-print-limit can be specified for a Run cell to limit the number of printed lines; the default is configured as printLimit.

Result cell

The green Result cell represents the JavaScript interpreter. It contains output from calls to the print() function or computed values.

The Result cell appears when a run label is clicked and — unless the option key is pressed during the click — the previous content of the cell is first erased. The cell is always located immediately following the Run cell where the label was clicked.

The Result cell is implemented as read-only text area with green borders and a light green label. The borders turn red if the interpreter terminates.

While the JavaScript interpreter is running, the label shows a stop symbol which can be clicked to terminate the interpreter.

If the interpreter is stopped or terminated, the label shows a close symbol which can be clicked to dispose both, the interpreter and the Result cell.

A run-away loop can be terminated by clicking the stop symbol during execution; however, a run-away loop can still crash the web page if it consumes too many resources, e.g., by printing.

load()

JavaScript code in a Run cell can use the load() function to include JavaScript code from File cells.

The function takes one or more arguments; each must be a string which is defined as the id attribute of a File cell.

Note that var and function definitions are hoisted out of File cells, but class, const, and let definitions are not:

print()

JavaScript code in a Run cell can use the print() function. This function concatenates its arguments, if any, separated by single blanks, and appends one line to the Result cell.

The number of printed lines produced by a Run cell can be limited by specifying the maximum with the attribute data-print-limit.

JavaScript evaluation

JavaScript evaluation is implemented as a Worker which is created or contacted by the Run cell and uses eval() in the onmessage() method.

If the label of a Run cell is clicked it sends a message to the Worker. The message contains the code string of the cell and a collection with the ids of all File cells as keys and the current code string in each cell as the corresponding values. The Worker cannot read from the HTML document; therefore, the load() function relies on this collection to evaluate code in File cells.

The Worker uses eval() to evaluate the Run cell's code string and replies to the cell with exactly one of the following three messages:

['exit']
['exit', 'result value']
['error', 'error mesaage']

The Worker cannot insert into the HTML document; therefore, each call to the print() function sends one message

['print', string values if any ]

which contains strings with the values to be printed, if any.

Using a Worker for JavaScript interpretation has two advantages:

  • interpretation cannot block the user interface in the browser, i.e., the stop symbol in a Result cell can be clicked and event handling will apply terminate() to the Worker to abort interpretation.

  • the global scope for eval() is the Worker, not the browser, i.e., code in the File and Run cells cannot affect the document.

Still, this code does have the privileges of the Worker, i.e., it could, for example, mischievously employ XMLHttpRequest or replace the load() and print() functions. In an attempt to protect the implementation of the onmessage() method, each time a Worker is created NB is initialized with a shallow copy of all the global methods used.

Examples

Euclid's algorithm

Compute the greatest common divisor of two positive integers x and y.

Ackerman's function

How many recursive calls before the interpreter fails?

The catch block prevents the interpreter from terminating, i.e., the Result cell does not turn red.

Too many lines

The following Run cell is specified with data-print-limit="10" to terminate the infinite printing loop.

Here the interpreter terminates, i.e., the Result cell will turn red. Run this and the previous example a few times in a row (using option-click) to see the difference in the Result cell.

Fun with conversions

There is a difference between a number and a Number object:

Zero usually is false:

Empty strings usually are false:

Boolean forces a logical value:

Infinite sequences

An infinite sequence can be represented as a function which returns an ordered pair, i.e., an object where car is a value of the sequence and cdr is the rest of the sequence:

ints(1) is the sequence of natural numbers:

The following function take() collects the first n elements of a sequence s into an array:

Note that the result of take() needs to be reversed:

The function zipWith() combines two sequences with a function:

E.g., the integers beginning with 1 can be pairwise concatenated with the integers beginning with 5:

Arrow syntax

Modern JavaScript implementations support a more concise syntax for functions. The previous examples can be expressed as follows:

The following Run cells demonstrate that the old and new functions produce the same results:

Classes

Modern JavaScript has a syntax for classes. Here is an example of objects containing a counter belonging to a class with methods to access and modify the current value.

Classes — like functions — can have names; however, in a File cell var has to be used to hoist a class definition.

Thanks to the rest parameter ...values, the method add() will add any combination of arrays and numbers to the counter:

The example takes advantage of a (questionable) aspect of arrow functions: they don't hide this. The method would require a closure if it is implemented using a function:

Getting started

Insert cells (input type='text' and textarea elements as described above) into a MarkDown document. Add the following code at the very end of the document:

    <script src='https://code.jquery.com/jquery-3.3.1.min.js'></script>
    <link rel='stylesheet' type='text/css' href='notebook.css'>
    <script type='module'>
      import { init } from 'notebook.js';
      // wrap cells below the selected element
      // and use indicated worker code
      init($('body'), 'worker.js');
    </script>

Alternatively, equivalent code can be added to the head of a HTML document.

Note that the paths above are relative to the document. notebook.js implements the Notebook module, i.e., models, views, and controllers for the cells described above, worker.js implements the JavaScript interpreter, i.e., the sandboxed runtime support for the Run cells, and notebook.css is the required stylesheet — which could be modified.

worker.js