Coding Guide

ProtoPedia users can (1) build diagrams with code, but can also (2) code their own visual elements and diagrams, and assemble catalogs of these new elements via the catalog editor. Subsequently, such elements and diagrams are available for use in ProtoPedia's UI, where no coding is required. In addition, the core technology is openly available under the MIT Licence for use in any application (3). This document provides the information needed for all three activities.

Sections 5 - 7 describe how to work with prototype trees, the material from which all ProtoPedia elements (referred to as "items") are made. The remaining sections concern construction of and interaction with 2d shapes implemented by way of SVG.

1. Sample code

Before wading into the details, you might like to look at some code. Samples 2,3,4 illustrate how new visual elements are built.

  1. A simple diagram.
  2. Rectangle
  3. Rectangle with text
  4. Line

The following link provides access, by way of a catalog, to the code which implements many of ProtoPedia's visual elements:

Select the item of interest in the catalog, and then click the "Open Code" button.

2. Working with code

2.1 The Code Editor

The editor enables you to edit code in one window, re-run it at will, and see the results in the graphics panel immediately. If you click on the name of the source file, or on one of the dependencies, an ordinary text editor is popped on the file in question.

If you have signed in, the save as button will be active. Each signed in user is allocated a file system, where files are named according to the scheme


This is where the code editor saves files. You can open the main drawing program (also called the "structure editor") on whatever via :

2.2 Debuggers

Browser debuggers (eg Chrome's DevTools) work well with ProtoPedia. However, the usual variants at and catch errors, which is not what is wanted in the context of debugging. The variants and (accepting exactly the same GET arguments) are provided in which errors are not caught.

3. Require

Components are accessed and defined via core.require. Here is an example:

  function (circlePP,arrowPP) {
    let item ='<g/>');
    return item;

This binds the variables circlePP and arrowPP to the components defined in '/shape/circle.js', and '/shape/arcArrow.js', respectively, and then calls the function (ie the last argument to core.require). The value returned by the function is the newly built component.

4. Catalogs

Catalogs are the visual lists of items from which elements are dragged in the structure editor during the insert and swap operations. They are the principal means by which visual elements implemented in code are made available in the structure editor, and shared among the users of ProtoPedia. An initial catalog, at the filename (username)/default.catalog, is allocated for each ProtoPedia user, and additional catalogs can be added at will. The default catalog starts out empty.

In its normal state, the structure editor comes up showing a catalog that is the union of a global catalog, and your default catalog. Catalogs are partitioned into tabs, and the tab initially allocated to the items in your default catalog is "own".

In the code editor, there is an add to catalog button,which adds the current item to your default catalog. This is the easiest way to add to the catalog, but there is also a catalog editor.

Here is the catalog editor opened on the global catalog:


opens the catalog editor on your own catalog.

Each entry has the following fields:

In the structure editor, catalogs other than the defaults are invoked with the GET arguments catalog= or extension= . For example: will open the structure editor with the global catalog replaced by (exampleUser)/default.catalog (which contains the triangle shape).

will extend the global catalog with the contents of (exampleUser)/default.catalog. In this way, catalogs provide not only a way of organizing your own work, but of sharing it with other users.

5. Trees

Now to the nitty-gritty: the structure of ProtoPedia's data, and the operations which provide access to it.

All prototype trees (aka "items") are trees: each non-atomic child has a unique parent. (Formally, if parent.prop === child, and prop is an own property of parent, there is no parent2 with parent2 !== parent, and with parent2.prop2 === child for own property prop2 of parent2). The internal nodes in items inherit from prototypes core.ArrayNode for sequential, zero-based arrays, and core.ObjectNode for objects which are not arrays.;

creates a new Object, and;

a new Array.


assigns child as a child of object with name name. If child is an Object or Array, this results in setting special properties: child.__parent === object, and child.__name === 'name'.


also assigns child as a child of object, but automatically assigns its name.

For an Array,


pushes child onto the end of array, and assigns array as the parent of child. That is, array[array.length-1] === child, and if child is an Object or Array, and child.__parent === array. Also, child.__name === array.length-1. Arrays are always sequential and zero-based.


takes an "ordinary" JavaScript tree such as one expressable in JSON, and turns it into the ProtoPedia kind of tree. For example:


will produce a Object/Array tree with matching structure.

Click here for an example of tree construction and manipulation.


var root =;
var aa =;
var bb =;

root.set("a",aa); // adds aa as a child named "a" of root

// __name and __parent "glue the tree together".
bb.__parent === root;

// let's add some atomic data 
aa.x = 5;  // set is not needed for atomic data or functions
aa.f = function (x) {return x*x;}

// now for an Array
var cc =;


this yields:

        /          \
       a            b
      / \            \
     x   f            c
    /     \            \
   5    function      [ *, 5]

where * is an Object

This could also be built with

var root = core.lift({a:{x:5},b:{c:[{z:45},5]}})
root.a.f = function (x) {return x*x};

There is nothing wrong with having properties of Objects that reference nodes in the tree other than children, as in

a.xx = b; 

in the above example.Then xx is a cross-tree reference.

Restriction on names: names may include only letters, numerals, and the underbar, and may not start with a numeral.

6. Instantiation and Serialization
inode = node.instantiate();

builds an instantiation of node; a tree which has the same structure as node, but inherits primitive data and functions from node via prototypical inheritance. (Details here)

Serialization is performed by:

s = core.stringify(node);

as described here.

7. No news

Nodes are constructed with and, rather than via the use of a "new". The definition of the mk method for Object is: = function () {
  return Object.create(core.ObjectNode);

Recall that Object.create(X) creates a new object with X as prototype.

Object is introduced simply with:

core.ObjectNode = {};

and Array with:

core.ArrayNode = [];

core.ObjectNode itself serves as the prototype for instances. In the more conventional pattern, core.ObjectNode would be a function, and the prototype property of that function, not Object itself, would be the prototype for the instances generated via the new operator (note that if F = function (){}, new F() is equivalent to Object.create(F.prototype))

The function, function.prototype, new   pattern has been available in JavaScript all along, whereas the more direct Object.create was introduced more recent years (in version 1.8.5 to be exact). ProtoPedia employs the newer pattern, not so much for its intrinsic simplicity (though that's nice), but because this way of doing things has a major simplifying effect on the entire ProtoPedia code base.

You will never see a "new" anywhere in the code. The convention is that for prototype X, is the creator/initializer (which may take arguments).

8. SVG<markup>);

creates an object that inherits from the prototype svg.Element, which in turn inherits from core.ObjectNode. Its content as an SVG element is as specified by the markup. At this stage, only some of the tags are supported: clipPath, circle, g, line, linearGradient, path, polygon, polyline, rect, radialGradient, stop, text, and tspan.

Here is an example:

var circle ='<circle fill="rgb(39, 49, 151)" stroke="black" stroke-width="1" r="5" />');

Each svg.Element may have an associated coordinate system/transform. The methods getTransform, and setTransform access this, and operations such as moveto affect it. See the API section for details. As in SVG, the transforms represent coordinate systems relative to parents. The root of the visible tree is held in core.root.

An svg.Element E is displayed by construction of an associated element in the SVG Dom, and this is assigned to the __element property of E. E.__element can be manipulated directly by the primitives of the Dom such as setAttribute. This is seldom necessary, because each svg.Element has a standard list of properties (eg stroke and fill) that are transferred from the item to the Dom, by setting attributes in the Dom from the values of the item's properties of the same name. This default behavior can be extended by defining the setDomAttributes method, which, if present, will be called after the standard transfer of properties. Here is its definition for the rectangle component, which assures that the rectangle is centered on the origin in its own coordinate system (this positioning is the convention for all shapes):

item.setDomAttributes =  function (element) {

9. The Update Method

In the introductory example, interactivity is implemented "under the hood" via the ProtoPedia's graph machinery. Here is a variant which has the same behavior, ut it is implemented more directly with no dependencies on graph connections.

  item.update = function () {
    let p1=this.p1,p2 = this.p2;

This method moves circle1 and circle2 to this.p1 and this.p2, respectively, and then causes the arrows to point at the circles. Updates are triggered automatically at load time, and in any circumstance of change. By "a circumstance of change" is meant one in which thhttp:// implementation detects a potentially relevant event (eg editing properties in the right panel). Update methods, can, of course, be invoked explicitly. Each update method is responsible for triggering updates of its descendants (the automatic updater traverses the tree looking for update methods, but when such a method is found, it does not descend the tree further).

The code for the variant includes support for dragging in the method dragStep, which will be explained in the next section.

10. Defining a Visual Element

In order to support resizing, an item that is intended to play the role of a visual element appearing in a catalog should follow this rule: It should define the parameters width and height, and its update or setDomAttributes method should adjust the item's SVG content to reflect these dimensions. The figure should be centered on the local origin. Examples are




in which the SVG figures are adjusted by setDomAttribtes, and update, respectively. In cases where the width and height are always identical, the parameter dimension should be used instead of width and height, as in

11. The Graph

"Graph" here is meant in its mathematical sense: a set of vertices with edges connecting them, the Cayley D3 graph, for example. Any of the elementary items under the "shapes" tab of the standard catalog can serve as vertices. The "connectors" tab contains various kinds of edges.

ProtoPedia supplies operations for manipulating the graph structure that pertains to the vertex and edge items. The last lines of the introductory code sample illustrate one of those operations. The full list can be found in the graph section of the API.

The implementation of each kind of edge must store its ends in properties named end0, and end1

At the end of the definition of a visual element, this incantation should appear:


See for a definition of a line as a fully functioning edge: it supports controllers for dragging the ends of the line around, and also supports establishing and maintaining connections to vertices.

Just as a visual element requires some special treatment to function as an edge, the same is true if it is to function as a vertex, though in many cases, only a single line of additional code is required. This is the case for rectangles. The line which allows a rectangle to function as a vertex is:


Here is the implementation of the rectangle element. The periphery operations in question are methods that allow computation of where a ray to the center of the element will intersect its periphery. The other available primitives of this kind are:




Here is the underlying code at GitHub.

12. Diagrams

A diagram is an item which incorporates specialized definitions of dragging and other behaviors. Consider the tree, in which dragging is defined a bit differently than for vertices in a generic graph (subtrees travel around with their roots), and which has specialized menu items for adding nodes. Full documentation of all of the capabilities of diagrams is pending. At the moment, attention is confined to dragging.

The relevant code for trees is :

item.isDiagram = true;

item.dragStep = function (vertex,pos) {
 let localPos = this.toLocalCoords(pos,true);
 /* move all the descendants of vertex to the relative
    positions they had prior to the move of vertex

item.dragStart = function () {
// compute relative positions for all nodes (used in positionvertices)

and also, in the initialization of a tree we have the line:

  this.vertexP.draggableInDiagram = true;

vertexP is the prototype for nodes in the tree.

Whenever a node defined as draggableInDiagram is dragged, the dragStep method of the diagram is invoked for each increment of dragging the node, and the new position. Since the prototype vertexP of the vertices is defined as draggable, this property is inherited by the instances. If present, the dragStart method is called with the initial position at the start of the drag.

13. Controllers

Notice that when you select a circle in the Cayley diagram, a box with draggable handles appears which allows you to resize that circle or its prototype. Also, when you select one of the arrows in this diagram, handles appear that allow modification of the size of the arrow head, and the curvature of the arrow (again available for both instance and prototype). These are dubbed "controllers", and can be implemented for any item. They come in two varieties "resizer" and "custom". To implement a resizer, the method item.__setExtent(extent) , where extent is a geom:Point , should be defined, and item.__adjustable should be set to true. The input to __setExtent represents the new extent that the item should take on. If these steps are taken, a resizing box with its handles will appear in the UI whenever the item or its instances are selected. See the rectangle code for a simple example. (This is the rectangle viewed in the code editor; to see the resizer at work click "Edit Structure" in the top bar, or insert a rectangle into a network diagram.)

To define a custom control, the two methods needed are controlPoints(), and updateControlPoint(index,pos). The controlPoints method should return a core.ArrayNode of geom.Point (s). When the item is selected, yellow handles will appear at the positions returned by controlPoints (the points should be given relative to the item's own origin). Then, when the handles are dragged, updateControlPoint(index,pos) is called at each moment of dragging, with index set to the index of the point which generated the handle being dragged, and pos to its new position. It is the responsibility of updateControlPoint to update and redisplay the item as appropriate given the new handle position. The rounded rectangle provides an example - click on the yellow square to adjust the rounding (see the code here.)

If all three methods are defined and adjustable is set, as is the case for the rounded rectangle, the item will be displayed with both a resize box, and the custom handles.

14. Roles, Replacement, and the Transfer State Method

In most diagrams, the visible elements play varying roles. For example in graphs and trees, some shapes play the role of nodes/vertices, and some of edges. A role is assigned in code via:

item.role = <roleName>

When replacing an element in a diagram via "swap" or "swap prototype" in the top bar, only shapes whose role matches the role of the replaced element are presented as possibilities (via a highlight of the candidate shape as the mouse rolls over it). The catalog assigns the list of possible roles.

For any item dest, the method dest.transferState(src, own), if present, transfers state from the replaced shape (src) to its replacement (dest). The own argument indicates whether only own properties should be transfered, or whether the operation should be applied to the relevant inherited properties as well. A common defintion of transferred state for basic shapes such as circles and rectangles is:


item.transferState = function (src,own) { //own = consider only the own properties of src

ui.stdProperties is defined in the ui module as :

 const stdTransferredProperties = ['stroke','stroke-width','fill','unselectable',

A diagram might define diagram.transferElementState(dest,src,own). When a shape S is replaced by R within a diagram, this diagram.transferElementState(R,S,own) is called as well as R.transferState(S,own). As an example, transferElementState transfers information about the descendant relation in the case of tree diagrams. The own flag is set or not according to whether a prototype, or an instance is being swapped.

15. Controlling Display of Properties

When an item is selected in the structure editor, its properties and those of its prototype are displayed in the right-hand panel. If a property name begins with a double underbar (eg "__name"), it is not shown, and you can use this convention to hide properties from the user's view. But you can also employ ui.hide as exemplified by the following line from the implementation of the arrow


The subsequent line:


causes the solidHead property to be displayed with a true/false selection box. Similarly, in the implementation of the shadedCircle , the line


causes the outerFill property to be displayed with the Spectrum color picker. By default,this chooser is deployed for all fields named fill or stroke.

By default, the values of properties are editable in the property panel. But with


the given properties are presented in non-editable form.

16. MIT License

ProtoPedia aims to support open collaboration. All of the code (and other content) at ProtoPedia, including its implementation, the catalogs of elements and diagrams, and the code that you post at the site, is covered by the MIT license, which means that the code can be freely shared and modified. See our terms and conditions for details.

17. API

(Partial listing - more to come)

Calls are given in the form f(arg1:type1,arg2:type2...) where types are: boolean, number,string, Node (core.ArrayNode or core.ObjectNode) or any. When a call is described in more detail elsewhere in this document, a link is supplied.

Defaults for property values are given in parentheses just after the name of the property.

Constructor for core.ObjectNode
Constructor for core.ArrayNode
Assign vl as the child of this with name nm
Assign vl as the child of this, with an automatically assigned name.
Remove this from the tree in which it appears.
For each property p in props, set dest[p] = src[p]
core.treeProperties( nd:Node, includeLeaves:boolean)
Returns an array of the direct properties of nd which are edges of the prototype tree.
Constructor for geom.Point, with properties x and y. If x and y are omitted, 0,0 are used.
Adds point p to this (vector addition)
Scales the point by v
Creates a geom.Point from several kinds of input. If v is an array [a,b] it returns a point with coordinates {x:a,y:b}; if v is a number, a point with coordinates {x:v,y:0}, if v is a geom.Point, a copy of the point.
geom.toPoint() returns the point with coordinates {x:0,y:0}.,
Constructor for geom.Rectangle, with properties corner and extent. For example,20),,5)) has (10,20) as its upper-left corner, and (15,25) as its lower-right corner.
Constructor for geom.Transform, with properties translation, scale, and rotation. Rotation is in radians. All arguments are optional, defaulting to,0), 1, and 0, respectively.
Constructor for svg Elements. s is markup.
If an element has been hidden, change its status to "visible"
Refresh this element. Changes to the ProtoPedia data for an element (and its descendents) are transferred to the svg model. Adding an element to the ProtoPedia tree is not reflected
Returns the transform of this element.
Sets the transform of this element.
Returns the translation of the svg transform of this element.
Move this element to p. That is, set the translation of the svg transform of this element to p.
Return the scale of the transform of this element.
Set the scale of the svg transform of this element to s.
Return the bounds of the given Element (and its descendants). rt is optional. If rt is present, it should be an ancestor of this Element, and the bounds are given relative to rt's coordinate system. If rt is absent, bounds are given in the Element's own coordinate system.
connnects one end of e to v. whichEnd should be 0 or 1.
connnects one end of e to v0, and the other to v1.
moves the ends of the edge as appropriate so that it maintains its connections
updates the whole graph by updating the ends of all edges
Computes where to move the given end to, if that end is being dragged around the periphery
adds vertices and edges instantiated from the prototypes v and ed respectively, as children of n, and as indicated by data. The Cayley D3 graph is built in this way. Here is its data. The format should be self-explanatory to those familiar with JSON.
In the UI, either the prototype or the instance of the selected item can be adjusted (there is a check box for telling which). This global is set to the one being adjusted.
Properties you should set
Node.unselectable:boolean (false)
If this node is clicked, its first selectable ancestor is selected
Node.adjustable:boolean (false)
A resize box appears when this node is selected.
Node.draggable:boolean (false)
Updates and draws all of the nodes that inherit from proto. Frequently used in the form ui.updateInheritors(ui.whatToAdjust), when an edit has been made to the prototype rather instance. See the updateControlPoint method at the bottom of the implementation of the arrow.

These calls control how property values are displayed in the structure editor.

ui.hide(nd:Node,props:array of string)
ui.freeze(nd:Node,props:array of string)
Methods you define
Node.controlPoints():array of Point