Contents

  1. Getting started
  2. Putting Jalava in your own webpage
  3. Customizing your own editor
  4. Extending Jalava
  5. Saving your diagram

Getting Started

You can download the entire package here. Once you have downloaded the zip file, unzip it to a folder of your choice. There are a number of demo web pages that you can try. All are prefixed with a 'demo' infront. For a start, you can try demo_basic.htm, which is a basic editor that allows you to draw plain rectangles and connections.

Using Jalava

The diagram editor consists of three basic parts - the Palette, the Property Page, and the Diagram. Depending on how the editor is customised, you might not have the property page, or you might not have the palette, or you might have neither - just the diagram alone.

The Palette contains components which you can place on the Diagram. Components can be Figures or Connections. To place a figure on the diagram, click on the figure in the palette and drag it onto the diagram area. To draw a connection, click on the Connection tool in the palette, then click on the Figures that you want to link up. You need to link at least one figure - ie the connection cannot be floating freely.

The Property Page provides the interface by which you can modify the properties of the figures and connections that you have on the diagram. When you click on any of the figures or connections, some properties will be displayed on the property page. You can then modify these properties to suit your liking.

Putting Jalava in your own webpage

Using Jalava in your own webpage is easy. The minimum that you need to do is initialise the Diagram and the Palette.

function initJalava(){
  Jalava.diagram = new Diagram(160, 0, "550", "600");		  
  var palette = new Palette(new PaletteFactory(), 0, 0, 150);
  palette.addItem("rect", "Processing", Palette.DRAG_TOOL, "./img/rect.gif");
  palette.addItem("connection", "Connection", Palette.CLICK_TOOL, "./img/line.gif");	      
}

// start Jalava
Jalava.start(initJalava);

Note that your initialisation code should be contained within a function. When you invoke Jalava.start, pass this callback function as an argument in start(). Once Jalava has completed its own initialisation, it will call your init routine.

Optionally, you may wish to create a Properties box as well. This box will display properties (such as color and line style) related to each item drawn in the diagram.

  Jalava.propertyPage = new PropertyPage(720, 0, 10);

This will instantiate a PropertyPage with 10 rows and position it at (720, 0). You must assign the object to Jalava.propertyPage. This will allow Jalava to reference it later to display data.

The complete example would therefore look like this

function initJalava(){
  Jalava.diagram = new Diagram(160, 0, "550", "600");		  
  var palette = new Palette(new PaletteFactory(), 0, 0, 150);
  palette.addItem("rect", "Processing", Palette.DRAG_TOOL, "./img/rect.gif");
  palette.addItem("connection", "Connection", Palette.CLICK_TOOL, "./img/line.gif");	      
  Jalava.propertyPage = new PropertyPage(720, 0, 10);
}

// start Jalava
Jalava.start(initJalava);

The full source can be found in basic.htm.

Customizing your own editor

The beauty of Jalava is that you can customise it to suit your application requirements. For instance, you may change the type of Figures drawn onto the diagram. You cah also change the behaviour of the editor, such as by disabling certain properties in the PropertyPage.

PaletteFactory

To customize the Figures that you draw onto the diagram, you need to create your own palette factory by extending the base PaletteFactory class. Jalava uses the copyPrototype method introduced by Harry Fuecks in SitePoint to do class extension. So to extend PaletteFactory, the minimum you would have to do is this:

function MyPaletteFactory() {
  MyPaletteFactory.parent.apply(this);
}
Jalava.copyPrototype(MyPaletteFactory, PaletteFactory);	

By default, the PaletteFactory will instantiate an empty GradientBlock and place it on the diagram. A GradientBlock has most common features, such as styled borders, and gradient backgrounds, and so will be sufficient for your use. If you require additional functionality, you can extend the GradientBlock class. See Extending Jalava for more information.

If you wish to use GradientBlock for your editor, then all you need to do is override the createContent() method in the PaletteFactory. This method specifies the HTML content to be placed inside the GradientBlock.

In this simple example, we will populate our GradientBlock with an icon and a label.

MyPaletteFactory.prototype.createContent = function(objId, real) {
  if (objId=="Zebra") {    
    // this is where we define our custom content
    var ele = DOM.createElement("DIV", "a");
    ele.className = "hello";
    var img = new Image();
    img.src = "./img/Zebra32.gif"
    img.style.width = "32px";
    img.style.height = "32px";
    img.style.verticalAlign = "middle";
    ele.appendChild(img);
    var span = DOM.createElement("SPAN", "mytextarea");
    span.innerHTML = "Zebra";
    span.style.marginLeft = "4px";
    if (real) {
      span.className = "editableLine";
      span.ondblclick = function(event) { Editable.startedit(event); }
    }
    ele.appendChild(span);
    return ele;
  }
  else return;
}

Now, in our initialisation code, we need to add a tool with the same id.

function initJalava(){
  Jalava.diagram = new Diagram(160, 0, "550", "600");		  
  var palette = new Palette(new MyPaletteFactory(), 0, 0, 150);
  palette.addItem("Zebra", "Zebra", Palette.DRAG_TOOL, "./img/african/Zebra16.gif");
  palette.addItem("connection", "Connection", Palette.CLICK_TOOL, "./img/line.gif");	      
  Jalava.propertyPage = new PropertyPage(720, 0, 10);
}	   

You will also have to explicitly load your new MyPaletteFactory class using the addModule() method.

Jalava.addModule("MyPaletteFactory");
Jalava.start(initJalava);

PropertyPage

You may have noticed earlier that we instantiated a PropertyPage object and placed it in our editor. The property page displays style attributes associated with each Figure - such as the background color and the border style. These attributes can be edited on the property page, allowing users to change the look of their figures dynamically.

You can customise the property page to display only certain attributes, thereby preventing the user from changing the other attributes. Or you may choose to expose additional attributes in your Figure class.

To customise the behaviour of the property page, extend the PropertyPage class and override the propertyChange() method. The method is invoked when a Figure on the diagram has changed in some way. This includes user interaction events such as "select", "deselect" and "delete".

In this example, I have disabled the Border Style and Border Width attributes for the GradientBlock, and all styles except Line Color for the DirectedConnection. Note that you need not differentiate by class - in the constructor for GradientBlock, you can pass in an optional 'type' value. This allows you to different GradientBlocks by a custom classifier.

MyPropertyPage.prototype.propertyChange = function(firer, property, value) {
  // user select a Figure	
  if (property=="select") {
  	this.table.setAttribute("id", firer.id);  	
	var className = Jalava.getClassName(firer);
	if (className=="GradientBlock" || className=="Block") {
    this.update("Backgrd Color", 
		             firer.getBackgroundColor(), 
					 this.colorPalette.createControl('Backgrd Color',
                                                     firer.getBackgroundColor(), 
                                                     PropertyPage.prototype.onChangeHandler));	  	
    this.update("Border Color", 
		             firer.getBorderColor(), 
					 this.colorPalette.createControl('Border Color',
                                                     firer.getBorderColor(), 
                                                     PropertyPage.prototype.onChangeHandler));	  		 
    this.update("Layer", 
	              firer.getLayer(),
	              this.numberInput.createControl('Layer', 
                                                 4, 
                                                 firer.getLayer(), 
                                                 PropertyPage.prototype.onChangeHandler));	
  } else if (className=="DirectedConnection") {
    this.update("Line Color", 
	              firer.color,
	              this.colorPalette.createControl('Line Color',
                                                  firer.color, 
                                                  PropertyPage.prototype.onChangeHandler));	  
    }
  }
  else if (property=="deselect" || property=="delete") {
  	this.table.setAttribute("id", "nil");
  	this.clear();
  } 
  else  {
  	this.updateStrictly(property, value);
  }
}

Extending Jalava

If the current features provided by Jalava are insufficient for your needs, you can easily extend the classes to create new functionality. Jalava makes use of the copyPrototype method introduced by Harry Fuecks in SitePoint to do class extension.

Basically, the classes of interest would be the base figure classes - namely the Connection class and the Block class. At present, the Connection class has been subclassed into DirectedConnection, which introduces line styles and arrow heads. You might want to subclass DirectedConnection further, for instance to create connections that are not limited to right-angle bends.

To extend a class, use the copyPrototype method to copy the entire prototype of the super class.

function CurvyConnection() {
  CurvyConnection.parent.apply(this);
  
  // do your initialisation code
   
}
// invoke copyPrototype method immediately after the constructor
Jalava.copyPrototype(CurvyConnection, DirectedConnection);

Assuming you want to change the way the connection is drawn, you will have to override the findPath() method.

CurvyConnection.prototype.findPath = function() {

  // write your own algorithm for finding the anchors of the connection

}

You may also need to override the redraw() method, which currently is only capable of drawing vertical and horizontal lines, using DIVs.

CurvyConnection.prototype.redraw = function() {

  // code to render the line

}

Once you have completed your class, save the code in a .js file with the same name as the class. In this example, the file would be saved as CurvyConnection.js. We now need to ask Jalava to load your class.

Jalava.addModule("CurvyConnection");

In the PaletteFactory class, you then need to specify that the factory instantiates your new CurvyConnection instead of the default DirectedConnection.

PaletteFactory.prototype.createConnectionObject = function(id) {
	return new CurvyConnection();
}	

Saving your diagram

Many users have enquired about saving their data. The good news is that this is possible with version 0.9.0. In simple terms, you just need to access two methods exposed by the Diagram class - persist() and load(). An example of how to use these two methods can be found in the jalavanow.js file (you can find this file in the download zip file). This Javascript file is used in the demonstration JalavaNow! web application.

To save your data, call the persist() method. This will serialize all your diagram data and return it as a JSON string. You can then do a HTTP POST to send this string back to your server for processing or storage.

var jsonString = Jalava.diagram.persist();

Subsequently, you will want to load the persisted data back into the Jalava editor. Call the load() method and supply the original JSON string.

Jalava.diagram.load(jsonString);

Under the hood, what the persist() method does is to delegate all the serialization to the Block and Connection objects. The Diagram class will invoke the save() method on each Block and Connection, which will return a simple object of persistable attributes. The Diagram class then makes use of a JSON library to encode the objects in JSON.

The load() method is slightly more complicated. First the JSON string is decoded to produce the objects. The loading process is then delegated to the PaletteFactory, which will instantiate a Block/Connection. Then the PaletteFactory will delegate the actual initialization to the Block/Connection, by calling its load() method. The Block/Connection will inspect the JSON object and initialize itself based on the object attributes.